//--------------------------------------------------------------------------- // // ObjType.cpp -- File contains the Basic Game Object Type code // //---------------------------------------------------------------------------// // Copyright (C) Microsoft Corporation. All rights reserved. // //===========================================================================// //--------------------------------------------------------------------------- // Include Files #ifndef MCLIB_H #include "mclib.h" #endif #ifndef OBJTYPE_H #include "objtype.h" #endif #ifndef DOBJNUM_H #include "dobjnum.h" #endif #ifndef GAMEOBJ_H #include "gameobj.h" #endif #ifndef MECH_H #include "mech.h" #endif #ifndef GVEHICL_H #include "gvehicl.h" #endif #ifndef BLDNG_H #include "bldng.h" #endif #ifndef TERROBJ_H #include "terrobj.h" #endif #ifndef WEAPONBOLT_H #include "weaponbolt.h" #endif #ifndef CARNAGE_H #include "carnage.h" #endif #ifndef TURRET_H #include "turret.h" #endif #ifndef GATE_H #include "gate.h" #endif #ifndef ARTLRY_H #include "artlry.h" #endif #ifndef WEAPONFX_H #include "weaponfx.h" #endif //--------------------------------------------------------------------------- PacketFilePtr ObjectTypeManager::objectFile = NULL; UserHeapPtr ObjectTypeManager::objectTypeCache = NULL; UserHeapPtr ObjectTypeManager::objectCache = NULL; long ObjectTypeManager::bridgeTypeHandle = 0xFFFFFFFF; long ObjectTypeManager::forestTypeHandle = 0xFFFFFFFF; long ObjectTypeManager::wallHeavyTypeHandle = 0xFFFFFFFF; long ObjectTypeManager::wallMediumTypeHandle = 0xFFFFFFFF; long ObjectTypeManager::wallLightTypeHandle = 0xFFFFFFFF; unsigned long NextIdNumber = 0x30000001; //Big number to indicate //This object is NOT a mission //part. Trust Me. -ffs //*************************************************************************** //* OBJECTTYPE class //*************************************************************************** void* ObjectType::operator new (size_t ourSize) { void* result = ObjectTypeManager::objectTypeCache->Malloc(ourSize); return(result); } //--------------------------------------------------------------------------- void ObjectType::operator delete (void* us) { if (!((ObjectTypePtr)us)->inUse()) { ObjectTypeManager::objectTypeCache->Free(us); } } //--------------------------------------------------------------------------- GameObjectPtr ObjectType::createInstance (void) { GameObjectPtr result = new GameObject; result->init(true, this); //result->setIdNumber(NextIdNumber++); return(result); } //--------------------------------------------------------------------------- void ObjectType::destroy (void) { // Nothing at the moment! ObjectTypeManager::objectTypeCache->Free(appearName); appearName = NULL; } //--------------------------------------------------------------------------- long ObjectType::init (FilePtr objFile, unsigned long fileSize) { return(NO_ERR); } //--------------------------------------------------------------------------- long ObjectType::init (FitIniFilePtr objFile) { long result = 0; result = objFile->seekBlock("ObjectType"); if (result != NO_ERR) return(result); numUsers = 0; //-------------------------------------------------------------------- // Read in the rest of the Object type Data here. This includes // exposions, collision extents, etc. char apprName[512]; result = objFile->readIdString("AppearanceName",apprName,511); if (result == NO_ERR) { appearName = (char *)ObjectTypeManager::objectTypeCache->Malloc(strlen(apprName)+1); strcpy(appearName,apprName); } result = objFile->readIdLong("ExplosionObject",explosionObject); if (result != NO_ERR) return(result); result = objFile->readIdLong("DestroyedObject",destroyedObject); if (result != NO_ERR) return(result); result = objFile->readIdFloat("ExtentRadius",extentRadius); if (result != NO_ERR) extentRadius = 0.0f; result = objFile->readIdLong("IconNumber", iconNumber); if (result != NO_ERR) iconNumber = -1; keepMe = true; //Never cache out anymore! teamId = -1; //Everything starts out Neutral now. return(NO_ERR); } //--------------------------------------------------------------------------- void ObjectType::createExplosion (Stuff::Vector3D& position, float dmg, float rad) { long effectId = weaponEffects->GetEffectObjNum(explosionObject); if (explosionObject >= 0) { if (rad == 0.0) rad = getExtentRadius(); ObjectManager->createExplosion(effectId, NULL, position, dmg, rad); } } //--------------------------------------------------------------------------- bool ObjectType::handleCollision (GameObjectPtr collidee, GameObjectPtr collider) { //--------------------------------------------- // The default reaction of any object in the world // is to simply explode. This just returns TRUE // to facilitate this behaviour. return(true); } //--------------------------------------------------------------------------- bool ObjectType::handleDestruction (GameObjectPtr collidee, GameObjectPtr collider) { //--------------------------------------------- // The default reaction of any object in the world // is to simply explode. This routine will create // the associated explosions, debris, smoke, fire, etc. // and then return TRUE, to indicate that the object // has, in fact, gone south. The object is NOT // removed from the object list here!! Please don't // do it here either since it will screw up much code. Stuff::Vector3D pos = collidee->getPosition(); createExplosion(pos); return(true); } //*************************************************************************** //* GAMEOBJECT TYPE MANAGER class //*************************************************************************** long ObjectTypeManager::init (char* objectFileName, long objectTypeCacheSize, long objectCacheSize, long maxObjectTypes) { FullPathFileName objectName; objectName.init(objectPath,objectFileName,".pak"); objectFile = new PacketFile; if (!objectFile) return NO_RAM_FOR_OBJECT_TYPE_FILE; long result = objectFile->open(objectName); if (result != NO_ERR) return(result); objectTypeCache = new UserHeap; if (!objectTypeCache) return NO_RAM_FOR_OBJECT_TYPE_CACHE; result = objectTypeCache->init(objectTypeCacheSize,"ObjectTypeHeap"); if (result != NO_ERR) return(result); objectCache = new UserHeap; if (!objectCache) return NO_RAM_FOR_OBJECT_CACHE; result = objectCache->init(objectCacheSize,"ObjectHeap"); if (result != NO_ERR) return(result); numObjectTypes = maxObjectTypes; table = (ObjectTypePtr*)objectTypeCache->Malloc(sizeof(ObjectTypePtr) * numObjectTypes); if (!table) Fatal(0, " ObjectTypeManager.init: unable to create table "); for (long i = 0; i < numObjectTypes; i++) table[i] = NULL; //--------------------------------------------------------------------------- // Since MC1 handled all of these MiscTerrainObjectTypes with one ObjectType, // we set aside 5 separate typeHandles so they can be more logically // managed. If we never have to load MC1/MCX missions, we can simply init // the objectType packfile with 5 sep. types to begin with and avoid this // hack-like "fix"... bridgeTypeHandle = numObjectTypes - 5; forestTypeHandle = numObjectTypes - 4; wallHeavyTypeHandle = numObjectTypes - 3; wallMediumTypeHandle = numObjectTypes - 2; wallLightTypeHandle = numObjectTypes - 1; return(NO_ERR); } //--------------------------------------------------------------------------- void ObjectTypeManager::destroy(void) { if (table) { objectTypeCache->Free(table); table = NULL; numObjectTypes = 0; } if (objectFile) { objectFile->close(); delete objectFile; objectFile = NULL; } if (objectTypeCache) { delete objectTypeCache; objectTypeCache = NULL; } if (objectCache) { delete objectCache; objectCache = NULL; } } //--------------------------------------------------------------------------- ObjectTypePtr ObjectTypeManager::load (ObjectTypeNumber objTypeNum, bool noCacheOut, bool forceLoad) { //----------------------------------------------------------------------- // NOTE: This function attempts to load the objectType into the table. If // the object type is ALREADY loaded, it simply returns NULL (indicating // it's already been loaded, so no problem). Otherwise, it returns the // newly loaded object type. What I'm say'n here is--this function // CANNOT fatal out, because any calling function will want to know // if the object type had to be loaded or not. However, it will fatal // if it's unable to load the object type from the packet file. //-------------------------------------------------------- // If we are going to disk to get the object, be sure the // frame length knows to force itself into load mode. dynamicFrameTiming = false; if ((objTypeNum < 0) || (objTypeNum >= numObjectTypes)) Fatal(objTypeNum, " ObjectTypeManager.load: bad objTypeNum "); if (objTypeNum == 0) //First Object always NULL! return NULL; if (!forceLoad && get(objTypeNum, false)) return(NULL); bool isMiscTerrObj = false; long objectTypeNum = -1; ObjectTypePtr objType = NULL; if ((objTypeNum == bridgeTypeHandle) || (objTypeNum == forestTypeHandle) || (objTypeNum == wallHeavyTypeHandle) || (objTypeNum == wallMediumTypeHandle) || (objTypeNum == wallLightTypeHandle)) { //---------------------------------------------------------- // MiscTerrainObject "hack" to maintain MC1 compatibility... objectTypeNum = TERRAINOBJECT_TYPE; isMiscTerrObj = true; } else if (objectFile->seekPacket(objTypeNum) == NO_ERR) { if (objTypeNum == 268) { gosASSERT(objTypeNum == 268); } //-------------------------------------------------------- // All new code here. This will ask the objectType it is // loading what kind of objectType it is and create it // based on that instead of objTypeNum. FitIniFile objTypeFile; long result = objTypeFile.open(objectFile,objectFile->getPacketSize()); if (result != NO_ERR) Fatal(objTypeNum, " ObjectTypeManager.load: can't create object "); result = objTypeFile.seekBlock("ObjectClass"); if (result != NO_ERR) Fatal(objTypeNum, " ObjectTypeManager.load: can't create object "); result = objTypeFile.readIdLong("ObjectTypeNum",objectTypeNum); if (result != NO_ERR) Fatal(objTypeNum, " ObjectTypeManager.load: can't create object "); objTypeFile.close(); objectFile->seekPacket(objTypeNum); } else Fatal(objTypeNum, " ObjectTypeManager.load: can't create object "); //----------------------------------------------------- // Now that we know what type it is, let's create it... switch (objectTypeNum) { case CRAPPY_OBJECT: //---------------------------------------------- // In theory we can't ever get here. // Because we did our jobs correctly!! Fatal(CANT_LOAD_INVALID_OBJECT, " ObjectTypeManager.load: can't create object "); break; case BATTLEMECH_TYPE: { objType = new BattleMechType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Mech type "); } break; case VEHICLE_TYPE: { objType = new GroundVehicleType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Vehicle type "); } break; case TREEBUILDING_TYPE: case BUILDING_TYPE: { objType = new BuildingType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Building type "); } break; case TREE_TYPE: { objType = new TerrainObjectType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init TerrainObject:Tree type "); } break; case TERRAINOBJECT_TYPE: { objType = new TerrainObjectType; objType->setObjTypeNum(objTypeNum); if (isMiscTerrObj) ((TerrainObjectTypePtr)objType)->initMiscTerrObj(objTypeNum); else if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init TerrainObject type "); } break; case WEAPONBOLT_TYPE: { objType = new WeaponBoltType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init WeaponBolt type "); } break; case TURRET_TYPE: { objType = new TurretType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Turret type "); } break; case EXPLOSION_TYPE: { objType = new ExplosionType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Explosion type "); } break; case FIRE_TYPE: { objType = new FireType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Fire type "); } break; case GATE_TYPE: { objType = new GateType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Gate type "); } break; case ARTILLERY_TYPE: { objType = new ArtilleryType; objType->setObjTypeNum(objTypeNum); if (objType->init(objectFile,objectFile->getPacketSize()) != NO_ERR) Fatal(objectTypeNum, " ObjectTypeManager.load: unable to init Artillery type "); } break; default: return(NULL); //Fatal(OBJECT_TYPE_NUMBER_UNDEFINED, " ObjectTypeManager.load: undefined objType "); } if (noCacheOut) { //--------------------------------------------------- // Do NOT EVER cache this object type out. FXs, etc. // This means I'm preloading it!! objType->makeLovable(); if (objType->getExplosionObject() > 0) load(objType->getExplosionObject()); } table[objTypeNum] = objType; return(objType); } //--------------------------------------------------------------------------- void ObjectTypeManager::remove (long objTypeNum) { if ((objTypeNum <= 0) || (objTypeNum >= numObjectTypes)) Fatal(objTypeNum, " ObjectTypeManager.remove: bad objTypeNum "); if (table[objTypeNum]) { table[objTypeNum]->removeUser(); if (!table[objTypeNum]->inUse() && !table[objTypeNum]->lovable()) { delete table[objTypeNum]; table[objTypeNum] = NULL; } } } //--------------------------------------------------------------------------- void ObjectTypeManager::remove (ObjectTypePtr objTypePtr) { remove(objTypePtr->whatAmI()); } //--------------------------------------------------------------------------- ObjectTypePtr ObjectTypeManager::get (ObjectTypeNumber objTypeNum, bool loadIt) { if ((objTypeNum < 0) || (objTypeNum >= numObjectTypes)) Fatal(objTypeNum, " ObjectTypeManager.find: bad objTypeNum "); //--------------------------- // If not, cache it in now... if (!table[objTypeNum] && loadIt) { if (!load(objTypeNum, true, true)) { // Fatal(objTypeNum, " ObjectTypeManager.get: unable to load object type "); } } return(table[objTypeNum]); } //--------------------------------------------------------------------------- GameObjectPtr ObjectTypeManager::create (ObjectTypeNumber objTypeNum) { if ((objTypeNum < 0) || (objTypeNum >= numObjectTypes)) Fatal(objTypeNum, " ObjectTypeManager.get: bad objTypeNum "); //---------------------------------------------------------- // First, check if the objectType has already been loaded... ObjectTypePtr objType = get(objTypeNum); //--------------------------- // If not, cache it in now... if (!objType) objType = load(objTypeNum); //-------------------------------------------------------------------- // By now, it should be cached in (otherwise, some error has occured). // Now, create an instance of it and send it back (after we increment // the number of gameobjects of this type)... if (objType) { GameObjectPtr obj = objType->createInstance(); if (!obj) Fatal(objTypeNum, " ObjectTypeManager.create: unable to create instance "); objType->addUser(); return(obj); } Fatal(objTypeNum, " ObjectTypeManager.create: unable to load object type "); return(NULL); } //***************************************************************************