//*************************************************************************** // // gameobj.cpp -- File contains the Game Object class code // // MechCommander 2 // //---------------------------------------------------------------------------// // Copyright (C) Microsoft Corporation. All rights reserved. // //===========================================================================// #ifndef MCLIB_H #include "mclib.h" #endif #ifndef GAMEOBJ_H #include "gameobj.h" #endif #ifndef CARNAGE_H #include "carnage.h" #endif #ifndef TEAM_H #include "team.h" #endif #ifndef OBJTYPE_H #include "objtype.h" #endif #ifndef OBJMGR_H #include "objmgr.h" #endif #ifndef DOBJNUM_H #include "dobjnum.h" #endif #ifndef MOVE_H #include "move.h" #endif #ifndef CMPONENT_H #include "cmponent.h" #endif #ifndef CONTACT_H #include "contact.h" #endif #ifndef UNITDESG_H #include "unitdesg.h" #endif #ifndef MULTPLYR_H #include "multplyr.h" #endif #ifndef MOVER_H #include "mover.h" #endif #ifndef TURRET_H #include "turret.h" #endif #ifndef GAMELOG_H #include "gamelog.h" #endif #ifndef COMNDR_H #include "comndr.h" #endif //--------------------------------------------------------------------------- extern GameLog* CombatLog; extern MissionMapPtr GameMap; extern float metersPerWorldUnit; extern float worldUnitsPerMeter; extern TeamPtr homeTeam; extern float MechClassWeights[NUM_MECH_CLASSES]; extern char* ExceptionGameMsg; char ChunkDebugMsg[5120]; unsigned long GameObject::spanMask = 0; float GameObject::blockCaptureRange = 0.0; bool GameObject::initialize = false; extern float maxVisualRange; extern long visualRangeTable[]; float applyDifficultyWeapon (float dmg, bool isPlayer); //*************************************************************************** // WEAPONFIRECHUNK class //*************************************************************************** #define WEAPONFIRECHUNK_HIT_BITS 1 #define WEAPONFIRECHUNK_WEAPON_BITS 5 #define WEAPONFIRECHUNK_TARGETTYPE_BITS 2 #define WEAPONFIRECHUNK_ENTRYQUAD_BITS 2 #define WEAPONFIRECHUNK_HITLOCATION_BITS 4 #define WEAPONFIRECHUNK_MOVERINDEX_BITS 7 #define WEAPONFIRECHUNK_CELLPOS_BITS 10 #define WEAPONFIRECHUNK_MISSILES_BITS 4 #define WEAPONFIRECHUNK_TARGETID_BITS 20 #define WEAPONFIRECHUNK_SPECIALTYPE_BITS 2 #define WEAPONFIRECHUNK_SPECIALID_BITS 8 #define WEAPONFIRECHUNK_HIT_MASK 0x00000001 #define WEAPONFIRECHUNK_WEAPON_MASK 0x0000001F #define WEAPONFIRECHUNK_TARGETTYPE_MASK 0x00000003 #define WEAPONFIRECHUNK_ENTRYQUAD_MASK 0x00000003 #define WEAPONFIRECHUNK_HITLOCATION_MASK 0x0000000F #define WEAPONFIRECHUNK_MOVERINDEX_MASK 0x0000007F #define WEAPONFIRECHUNK_TERRAINBLOCK_MASK 0x000000FF #define WEAPONFIRECHUNK_TERRAINVERTEX_MASK 0x000001FF #define WEAPONFIRECHUNK_TERRAINITEM_MASK 0x00000007 #define WEAPONFIRECHUNK_TRAIN_MASK 0x000000FF #define WEAPONFIRECHUNK_TRAINCAR_MASK 0x000000FF #define WEAPONFIRECHUNK_CELLPOS_MASK 0x000003FF #define WEAPONFIRECHUNK_MISSILES_MASK 0x0000000F #define WEAPONFIRECHUNK_TARGETID_MASK 0x000FFFFF #define WEAPONFIRECHUNK_SPECIALTYPE_MASK 0x00000003 #define WEAPONFIRECHUNK_SPECIALID_MASK 0x000000FF #define WEAPONFIRECHUNK_TARGET_MOVER 0 #define WEAPONFIRECHUNK_TARGET_TERRAIN 1 #define WEAPONFIRECHUNK_TARGET_SPECIAL 2 #define WEAPONFIRECHUNK_TARGET_LOCATION 3 #define WEAPONFIRECHUNK_SPECIAL_CAMERADRONE 0 //--------------------------------------------------------------------------- void WeaponShotInfo::init (GameObjectWatchID _attackerWID, long _masterId, float _damage, long _hitLocation, float _entryAngle) { attackerWID = _attackerWID; masterId = _masterId; damage = _damage; hitLocation = _hitLocation; entryAngle = _entryAngle; if (!MPlayer && _attackerWID) //No Multiplayer skill levels { //--------------------------------------------------- // SKill Levels -- Need Attacker Alignment and Class GameObjectPtr _attacker = ObjectManager->getByWatchID(_attackerWID); if (_attacker) { ObjectClass objClass = _attacker->getObjectClass(); long commanderId = _attacker->getCommanderId(); bool isPlayer = commanderId == Commander::home->getId(); if (!isPlayer) //Only enemy Mechs, vehicles, elementals and turrets get modified { if ((objClass == BATTLEMECH) || (objClass == GROUNDVEHICLE) || (objClass == TURRET)) { damage = applyDifficultyWeapon(_damage,isPlayer); } } else { damage = applyDifficultyWeapon(_damage,isPlayer); } } } Assert((damage >= 0.0) && (damage <= 255.0), (long)damage, " WeaponShotInfo.init: damage out of range "); if (MPlayer && MPlayer->isServer()) { damage = (float)((unsigned long)(damage * 4.0)) * 0.25; if ((entryAngle >= -45.0) && (entryAngle <= 45.0)) entryAngle = 0.0; //MECH_HIT_ARC_FRONT; else if ((entryAngle > -135.0) && (entryAngle < -45.0)) entryAngle = -90.0; //MECH_HIT_ARC_LEFT; else if ((entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 90.0; //MECH_HIT_ARC_RIGHT; else entryAngle = 180.0; //MECH_HIT_ARC_REAR; } } //--------------------------------------------------------------------------- void WeaponShotInfo::setDamage (float _damage) { damage = _damage; if (MPlayer && MPlayer->isServer()) damage = (float)((unsigned long)(damage * 4.0)) * 0.25; } //--------------------------------------------------------------------------- void WeaponShotInfo::setEntryAngle (float _entryAngle) { entryAngle = _entryAngle; if (MPlayer && MPlayer->isServer()) { if ((entryAngle >= -45.0) && (entryAngle <= 45.0)) entryAngle = 0.0; //MECH_HIT_ARC_FRONT; else if ((entryAngle > -135.0) && (entryAngle < -45.0)) entryAngle = -90.0; //MECH_HIT_ARC_LEFT; else if ((entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 90.0; //MECH_HIT_ARC_RIGHT; else entryAngle = 180.0; //MECH_HIT_ARC_REAR; } } //--------------------------------------------------------------------------- void* WeaponFireChunk::operator new (size_t ourSize) { void* result; result = systemHeap->Malloc(ourSize); return(result); } //--------------------------------------------------------------------------- void WeaponFireChunk::operator delete (void* us) { systemHeap->Free(us); } //--------------------------------------------------------------------------- void DebugWeaponFireChunk (WeaponFireChunkPtr chunk1, WeaponFireChunkPtr chunk2, GameObjectPtr attacker) { ChunkDebugMsg[0] = NULL; char outString[512]; if (attacker) { if (attacker->isMover()) { sprintf(outString, "attacker = %s (%d)\n", attacker->getName(), attacker->getPartId()); strcat(ChunkDebugMsg, outString); } else { sprintf(outString, "attacker = objClass %d (%d)\n", attacker->getObjectClass(), attacker->getPartId()); strcat(ChunkDebugMsg, outString); } } else strcat(ChunkDebugMsg, "attacker = ???\n"); if (chunk1) { strcat(ChunkDebugMsg, "\nCHUNK1\n"); GameObjectPtr target = NULL; Stuff::Vector3D targetPoint; targetPoint.Zero(); bool isTargetPoint = false; if (chunk1->targetType == WEAPONFIRECHUNK_TARGET_MOVER) target = (GameObjectPtr)MPlayer->moverRoster[chunk1->targetId]; else if (chunk1->targetType == WEAPONFIRECHUNK_TARGET_TERRAIN) target = ObjectManager->findByPartId(chunk1->targetId); else if (chunk1->targetType == WEAPONFIRECHUNK_TARGET_SPECIAL) target = ObjectManager->findByPartId(chunk1->targetId); else if (chunk1->targetType == WEAPONFIRECHUNK_TARGET_LOCATION) { targetPoint.x = (float)chunk1->targetCell[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2; targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk1->targetCell[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2; targetPoint.z = (float)land->getTerrainElevation(targetPoint); isTargetPoint = true; } if (target) { if (target->isMover()) { sprintf(outString, "target = %s (%d)\n", target->getName(), target->getPartId()); strcat(ChunkDebugMsg, outString); } else { sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId()); strcat(ChunkDebugMsg, outString); } } else if (isTargetPoint) { sprintf(outString, "target point = (%f, %f, %f)\n", targetPoint.x, targetPoint.y, targetPoint.z); strcat(ChunkDebugMsg, outString); } else { strcat(ChunkDebugMsg, "target = ???\n"); } sprintf(outString, "targetType = %d\n", chunk1->targetType); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetId = %d\n", chunk1->targetId); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialType = %d\n", chunk1->specialType); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialId = %d\n", chunk1->specialId); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetCell = [%d, %d]\n", chunk1->targetCell[0], chunk1->targetCell[1]); strcat(ChunkDebugMsg, outString); sprintf(outString, "weaponIndex = %d\n", chunk1->weaponIndex); strcat(ChunkDebugMsg, outString); sprintf(outString, "hit = %c\n", chunk1->hit ? 'T' : 'N'); strcat(ChunkDebugMsg, outString); sprintf(outString, "entryAngle = %d\n", chunk1->entryAngle); strcat(ChunkDebugMsg, outString); sprintf(outString, "numMissiles = %d\n", chunk1->numMissiles); strcat(ChunkDebugMsg, outString); sprintf(outString, "hitLocation = %d\n", chunk1->hitLocation); strcat(ChunkDebugMsg, outString); sprintf(outString, "data = %x\n", chunk1->data); strcat(ChunkDebugMsg, outString); } if (chunk2) { strcat(ChunkDebugMsg, "\nCHUNK2\n"); GameObjectPtr target = NULL; Stuff::Vector3D targetPoint; targetPoint.Zero(); bool isTargetPoint = false; if (chunk2->targetType == WEAPONFIRECHUNK_TARGET_MOVER) target = (GameObjectPtr)MPlayer->moverRoster[chunk2->targetId]; else if (chunk2->targetType == WEAPONFIRECHUNK_TARGET_TERRAIN) target = ObjectManager->findByPartId(chunk2->targetId); else if (chunk2->targetType == WEAPONFIRECHUNK_TARGET_SPECIAL) target = ObjectManager->findByPartId(chunk2->targetId); else if (chunk2->targetType == WEAPONFIRECHUNK_TARGET_LOCATION) { targetPoint.x = (float)chunk2->targetCell[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2; targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk2->targetCell[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2; targetPoint.z = (float)land->getTerrainElevation(targetPoint); isTargetPoint = true; } if (target) { if (target->isMover()) { sprintf(outString, "target = %s (%d)\n", target->getName(), target->getPartId()); strcat(ChunkDebugMsg, outString); } else { sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId()); strcat(ChunkDebugMsg, outString); } } else if (isTargetPoint) { sprintf(outString, "target point = (%f, %f, %f)\n", targetPoint.x, targetPoint.y, targetPoint.z); strcat(ChunkDebugMsg, outString); } else { strcat(ChunkDebugMsg, "target = ???\n"); } sprintf(outString, "targetType = %d\n", chunk2->targetType); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetId = %d\n", chunk2->targetId); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialType = %d\n", chunk2->specialType); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialId = %d\n", chunk2->specialId); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetCell = [%d, %d]\n", chunk2->targetCell[0], chunk1->targetCell[1]); strcat(ChunkDebugMsg, outString); sprintf(outString, "weaponIndex = %d\n", chunk2->weaponIndex); strcat(ChunkDebugMsg, outString); sprintf(outString, "hit = %c\n", chunk2->hit ? 'T' : 'N'); strcat(ChunkDebugMsg, outString); sprintf(outString, "entryAngle = %d\n", chunk2->entryAngle); strcat(ChunkDebugMsg, outString); sprintf(outString, "numMissiles = %d\n", chunk2->numMissiles); strcat(ChunkDebugMsg, outString); sprintf(outString, "hitLocation = %d\n", chunk2->hitLocation); strcat(ChunkDebugMsg, outString); sprintf(outString, "data = %x\n", chunk2->data); strcat(ChunkDebugMsg, outString); } File* debugFile = new File; debugFile->create("wfchunk.dbg"); debugFile->writeString(ChunkDebugMsg); debugFile->close(); delete debugFile; debugFile = NULL; ExceptionGameMsg = ChunkDebugMsg; } //--------------------------------------------------------------------------- #ifdef _DEBUG #define LOGWEAPONFIRECHUNKS #define WEAPONFIRELOG_SIZE 256 long NumWeaponFiresInLog = 0; long NumWeaponFiresInLogQueue = 0; GameObjectPtr WeaponFireAttackerLog[WEAPONFIRELOG_SIZE]; GameObjectPtr WeaponFireTargetLog[WEAPONFIRELOG_SIZE]; unsigned long WeaponFireDataLog[WEAPONFIRELOG_SIZE]; File* WeaponFireLog = NULL; #endif //--------------------------------------------------------------------------- #ifdef LOGWEAPONFIRECHUNKS void DumpWeaponFireLog (void) { //---------------- // Dump to file... for (long i = 0; i < NumWeaponFiresInLogQueue; i++) { GameObjectPtr attacker = WeaponFireAttackerLog[i]; GameObjectPtr target = WeaponFireTargetLog[i]; unsigned long data = WeaponFireDataLog[i]; WeaponFireChunk chunk; chunk.init(); chunk.data = data; chunk.unpack(attacker); char s[512]; char attackerName[128]; if (attacker->isMover()) sprintf(attackerName, "%s (%d)", attacker->getName(), attacker->getPartId()); else sprintf(attackerName, "objClass %d (%d)", attacker->getObjectClass(), attacker->getPartId()); char targetName[128]; if (target) if (target->isMover()) sprintf(targetName, "%s (%d)", target->getName(), target->getPartId()); else sprintf(targetName, "objClass %d (%d)", target->getObjectClass(), target->getPartId()); else sprintf(targetName, "NA/LOCATION"); sprintf(s, "attacker = %s, target = %s, data = %x, hit = %d, numMissiles = %d\n", attackerName, targetName, data, chunk.hit, chunk.numMissiles); WeaponFireLog->writeString(s); } NumWeaponFiresInLogQueue = 0; } //--------------------------------------------------------------------------- void CloseWeaponFireLog (void) { if (WeaponFireLog) { DumpWeaponFireLog(); char s[512]; sprintf(s, "\nNum Total WeaponFires = %d\n", NumWeaponFiresInLog); WeaponFireLog->writeString(s); WeaponFireLog->close(); delete WeaponFireLog; WeaponFireLog = NULL; NumWeaponFiresInLog = 0; NumWeaponFiresInLogQueue = 0; } } #endif //--------------------------------------------------------------------------- void OpenWeaponFireLog (void) { #ifdef LOGWEAPONFIRECHUNKS if (WeaponFireLog) CloseWeaponFireLog(); NumWeaponFiresInLog = 0; NumWeaponFiresInLogQueue = 0; WeaponFireLog = new File; if (!WeaponFireLog) Fatal(0, " unable to malloc WeaponFireLog "); if (WeaponFireLog->create("wf.log") != NO_ERR) Fatal(0, " unable to create WeaponFireLog "); #endif } //--------------------------------------------------------------------------- void LogWeaponFireChunk (WeaponFireChunkPtr chunk, GameObjectPtr attacker, GameObjectPtr target) { #ifdef LOGWEAPONFIRECHUNKS if (!WeaponFireLog) return; if (NumWeaponFiresInLogQueue == WEAPONFIRELOG_SIZE) DumpWeaponFireLog(); WeaponFireAttackerLog[NumWeaponFiresInLogQueue] = attacker; WeaponFireTargetLog[NumWeaponFiresInLogQueue] = target; WeaponFireDataLog[NumWeaponFiresInLogQueue] = chunk->data; NumWeaponFiresInLog++; NumWeaponFiresInLogQueue++; #endif } //--------------------------------------------------------------------------- void WeaponFireChunk::buildMoverTarget (GameObjectPtr target, long _weaponIndex, bool _hit, float _entryAngle, long _numMissiles, long _hitLocation) { targetType = WEAPONFIRECHUNK_TARGET_MOVER; targetId = ((MoverPtr)target)->getNetRosterIndex(); weaponIndex = _weaponIndex; hit = _hit; if ((_entryAngle >= -45.0) && (_entryAngle <= 45.0)) entryAngle = 0; //MECH_HIT_ARC_FRONT; else if ((_entryAngle > -135.0) && (_entryAngle < -45.0)) entryAngle = 2; //MECH_HIT_ARC_LEFT; else if ((_entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 3; //MECH_HIT_ARC_RIGHT; else entryAngle = 1; //MECH_HIT_ARC_REAR; numMissiles = _numMissiles; hitLocation = _hitLocation; Assert((targetId > -1) && (targetId < MAX_MULTIPLAYER_MOVERS), targetId, " WeaponFireChunk.buildMoverTarget: bad targetId "); Assert((weaponIndex > -1) && (weaponIndex < 32), weaponIndex, " WeaponFireChunk.buildMoverTarget: bad weaponIndex "); Assert((numMissiles > -1) && (numMissiles < 16), numMissiles, " WeaponFireChunk.buildMoverTarget: bad numMissiles "); Assert((hitLocation > -2) && (hitLocation < 12), hitLocation, " WeaponFireChunk.buildMoverTarget: bad hitLocation "); data = 0; } //--------------------------------------------------------------------------- void WeaponFireChunk::buildTerrainTarget (GameObjectPtr target, long _weaponIndex, bool _hit, long _numMissiles) { targetType = WEAPONFIRECHUNK_TARGET_TERRAIN; targetId = target->getPartId(); //target->getCellPosition(targetCell[0], targetCell[1]); weaponIndex = _weaponIndex; hit = _hit; numMissiles = _numMissiles; Assert(target->getPartId() != -1, target->getObjectClass(), " WeaponFireChunk.buildTerrainTarget: -1 partId "); data = 0; } //--------------------------------------------------------------------------- void WeaponFireChunk::buildCameraDroneTarget (GameObjectPtr target, long _weaponIndex, bool _hit, float _entryAngle, long _numMissiles) { targetType = WEAPONFIRECHUNK_TARGET_SPECIAL; targetId = target->getPartId(); targetCell[0] = 128; targetCell[1] = targetId - MIN_CAMERA_DRONE_ID; weaponIndex = _weaponIndex; hit = _hit; if ((_entryAngle >= -45.0) && (_entryAngle <= 45.0)) entryAngle = 0; //MECH_HIT_ARC_FRONT; else if ((_entryAngle > -135.0) && (_entryAngle < -45.0)) entryAngle = 2; //MECH_HIT_ARC_LEFT; else if ((_entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 3; //MECH_HIT_ARC_RIGHT; else entryAngle = 1; //MECH_HIT_ARC_REAR; numMissiles = _numMissiles; data = 0; } //--------------------------------------------------------------------------- void WeaponFireChunk::buildLocationTarget (Stuff::Vector3D location, long _weaponIndex, bool _hit, long _numMissiles) { targetType = WEAPONFIRECHUNK_TARGET_LOCATION; land->worldToCell(location, targetCell[0], targetCell[1]); weaponIndex = _weaponIndex; hit = _hit; numMissiles = _numMissiles; data = 0; } //--------------------------------------------------------------------------- #ifdef _DEBUG #define DEBUG_WEAPONFIRECHUNK #endif void WeaponFireChunk::pack (GameObjectPtr attacker) { data = 0; switch (targetType) { case WEAPONFIRECHUNK_TARGET_MOVER: //------------------------ // Mover Target... // current size is 23 bits data |= entryAngle; data <<= WEAPONFIRECHUNK_HITLOCATION_BITS; data |= (hitLocation + 2); data <<= WEAPONFIRECHUNK_MOVERINDEX_BITS; data |= targetId; if (numMissiles > 0) { //------------------------------ // Weapon is a missile weapon... data <<= WEAPONFIRECHUNK_MISSILES_BITS; data |= numMissiles; } data <<= WEAPONFIRECHUNK_HIT_BITS; break; case WEAPONFIRECHUNK_TARGET_TERRAIN: //------------------------- // Terrain Object Target... // current size is data |= (targetId - MIN_TERRAIN_PART_ID); if (numMissiles > 0) { //------------------------------ // Weapon is a missile weapon... data <<= WEAPONFIRECHUNK_MISSILES_BITS; data |= numMissiles; } data <<= WEAPONFIRECHUNK_HIT_BITS; break; case WEAPONFIRECHUNK_TARGET_SPECIAL: data |= entryAngle; data <<= WEAPONFIRECHUNK_CELLPOS_BITS; data |= targetCell[0]; data <<= WEAPONFIRECHUNK_CELLPOS_BITS; data |= targetCell[1]; if (numMissiles > 0) { //------------------------------ // Weapon is a missile weapon... data <<= WEAPONFIRECHUNK_MISSILES_BITS; data |= numMissiles; } data <<= WEAPONFIRECHUNK_HIT_BITS; break; case WEAPONFIRECHUNK_TARGET_LOCATION: //----------------------------------------- // Must be a Location Target (or a Miss)... data |= targetCell[0]; data <<= WEAPONFIRECHUNK_CELLPOS_BITS; data |= targetCell[1]; if (numMissiles > 0) { //------------------------------ // Weapon is a missile weapon... data <<= WEAPONFIRECHUNK_MISSILES_BITS; data |= numMissiles; } data <<= WEAPONFIRECHUNK_HIT_BITS; break; } if (hit) data |= 1; data <<= WEAPONFIRECHUNK_WEAPON_BITS; data |= weaponIndex; data <<= WEAPONFIRECHUNK_TARGETTYPE_BITS; data |= targetType; #ifdef DEBUG_WEAPONFIRECHUNK //------------------------- // Lots'a error checking... if ((targetType < 0) || (targetType > 3)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.pack: bad targetType %d (save wfchunk.dbg file) ", targetType); Assert(false, targetType, errMsg); } if ((weaponIndex < 0) || (weaponIndex > 31)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.pack: bad weaponIndex %d (save wfchunk.dbg file) ", weaponIndex); Assert(false, weaponIndex, errMsg); } if ((hitLocation < -1) || (hitLocation >= 12)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.pack: bad hitLocation %d (save wfchunk.dbg file) ", hitLocation); Assert(false, hitLocation, errMsg); } if ((entryAngle < 0) || (entryAngle > 3)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.pack: bad entryAngle %d (save wfchunk.dbg file) ", entryAngle); Assert(false, entryAngle, errMsg); } if ((numMissiles < 0) || (numMissiles > 15)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.pack: bad numMissiles %d (save wfchunk.dbg file) ", numMissiles); Assert(false, numMissiles, errMsg); } #endif } //--------------------------------------------------------------------------- void WeaponFireChunk::unpack (GameObjectPtr attacker) { unsigned long tempData = data; targetType = (tempData & WEAPONFIRECHUNK_TARGETTYPE_MASK); tempData >>= WEAPONFIRECHUNK_TARGETTYPE_BITS; weaponIndex = (tempData & WEAPONFIRECHUNK_WEAPON_MASK); tempData >>= WEAPONFIRECHUNK_WEAPON_BITS; hit = ((tempData & WEAPONFIRECHUNK_HIT_MASK) != 0) ? true : false; tempData >>= WEAPONFIRECHUNK_HIT_BITS; bool isMissileWeapon = false; if (attacker->isMover()) { long itemIndex = ((MoverPtr)attacker)->numOther + weaponIndex; isMissileWeapon = ((MoverPtr)attacker)->isWeaponMissile(itemIndex); } else if (attacker->getObjectClass() == TURRET) { //---------------------- // Attacker is turret... isMissileWeapon = ((Turret*)attacker)->isWeaponMissile(weaponIndex); } switch (targetType) { case WEAPONFIRECHUNK_TARGET_MOVER: //---------------- // Mover Target... if (isMissileWeapon) { numMissiles = (tempData & WEAPONFIRECHUNK_MISSILES_MASK); tempData >>= WEAPONFIRECHUNK_MISSILES_BITS; } targetId = (tempData & WEAPONFIRECHUNK_MOVERINDEX_MASK); tempData >>= WEAPONFIRECHUNK_MOVERINDEX_BITS; hitLocation = ((tempData & WEAPONFIRECHUNK_HITLOCATION_MASK) - 2); tempData >>= WEAPONFIRECHUNK_HITLOCATION_BITS; entryAngle = (tempData & WEAPONFIRECHUNK_ENTRYQUAD_MASK); break; case WEAPONFIRECHUNK_TARGET_TERRAIN: //------------------------- // Terrain Object Target... if (isMissileWeapon) { numMissiles = (tempData & WEAPONFIRECHUNK_MISSILES_MASK); tempData >>= WEAPONFIRECHUNK_MISSILES_BITS; } targetId = MIN_TERRAIN_PART_ID + (tempData & WEAPONFIRECHUNK_TARGETID_MASK); tempData >>= WEAPONFIRECHUNK_TARGETID_BITS; break; case WEAPONFIRECHUNK_TARGET_SPECIAL: //----------------------- // Special (Train, CameraDrone) Object Target... if (isMissileWeapon) { numMissiles = (tempData & WEAPONFIRECHUNK_MISSILES_MASK); tempData >>= WEAPONFIRECHUNK_MISSILES_BITS; } specialId = (tempData & WEAPONFIRECHUNK_SPECIALID_MASK); tempData >>= WEAPONFIRECHUNK_SPECIALID_BITS; specialType = (tempData & WEAPONFIRECHUNK_SPECIALTYPE_MASK); tempData >>= WEAPONFIRECHUNK_SPECIALTYPE_BITS; switch (specialType) { case WEAPONFIRECHUNK_SPECIAL_CAMERADRONE: targetId = MIN_CAMERA_DRONE_ID + specialId; break; default: Fatal(specialType, " WeaponFireChunk.unpack: bad specialType "); } entryAngle = (tempData & WEAPONFIRECHUNK_ENTRYQUAD_MASK); break; case WEAPONFIRECHUNK_TARGET_LOCATION: //----------------------------------------- // Must be a Location Target (or a Miss)... if (isMissileWeapon) { numMissiles = (tempData & WEAPONFIRECHUNK_MISSILES_MASK); tempData >>= WEAPONFIRECHUNK_MISSILES_BITS; } targetCell[1] = (tempData & WEAPONFIRECHUNK_CELLPOS_MASK); tempData >>= WEAPONFIRECHUNK_CELLPOS_BITS; targetCell[0] = (tempData & WEAPONFIRECHUNK_CELLPOS_MASK); break; } //------------------------- // Lots'a error checking... #ifdef DEBUG_WEAPONFIRECHUNK //------------------------- // Lots'a error checking... if ((targetType < 0) || (targetType > 3)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.unpack: bad targetType %d (save wfchunk.dbg file) ", targetType); Assert(false, targetType, errMsg); } if ((weaponIndex < 0) || (weaponIndex > 31)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.unpack: bad weaponIndex %d (save wfchunk.dbg file) ", weaponIndex); Assert(false, weaponIndex, errMsg); } if ((hitLocation < -1) || (hitLocation >= 12)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.unpack: bad hitLocation %d (save wfchunk.dbg file) ", hitLocation); Assert(false, hitLocation, errMsg); } if ((entryAngle < 0) || (entryAngle > 3)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.unpack: bad entryAngle %d (save wfchunk.dbg file) ", entryAngle); Assert(false, entryAngle, errMsg); } if ((numMissiles < 0) || (numMissiles > 15)) { DebugWeaponFireChunk(this, NULL, attacker); char errMsg[1024]; sprintf(errMsg, " WeaponFireChunk.unpack: bad numMissiles %d (save wfchunk.dbg file) ", numMissiles); Assert(false, numMissiles, errMsg); } if (!hit) { bool isStreakMissile = false; if (attacker->isMover()) { long itemIndex = ((MoverPtr)attacker)->numOther + weaponIndex; isStreakMissile = MasterComponent::masterList[((MoverPtr)attacker)->inventory[itemIndex].masterID].getWeaponStreak(); } else if (attacker->getObjectClass() == TURRET) { //---------------------- // Attacker is turret... isStreakMissile = ((TurretPtr)attacker)->isWeaponStreak(weaponIndex); } if (isStreakMissile) { DebugWeaponFireChunk(this, NULL, attacker); Assert(false, 0, " WeaponFireChunk.unpack: streak missile missed (save wfchunk.dbg file) "); } } GameObjectPtr target = NULL; if (targetType == 0 /*WEAPONFIRECHUNK_TARGET_MOVER*/) { target = (GameObjectPtr)MPlayer->moverRoster[targetId]; //---------------------------------------------------------------------------- // Mover targets could be NULL now, since we free them when they're destroyed. //if (target == NULL) { // DebugWeaponFireChunk (this, NULL, attacker); // Assert(false, 0, " WeaponFireChunk.unpack: NULL Mover Target (save wfchunk.dbg file) "); //} } else if (targetType == 1 /*WEAPONFIRECHUNK_TARGET_TERRAIN*/) { target = ObjectManager->findByPartId(targetId); if (target == NULL) { DebugWeaponFireChunk (this, NULL, attacker); Assert(false, 0, " WeaponFireChunk.unpack: NULL Terrain Target (save wfchunk.dbg file) "); } } else if (targetType == 2 /*WEAPONFIRECHUNK_TARGET_TRAIN*/) { target = ObjectManager->findByPartId(targetId); if (target == NULL) { DebugWeaponFireChunk (this, NULL, attacker); Assert(false, 0, " WeaponFireChunk.unpack: NULL Special Target (save wfchunk.dbg file) "); } } else if (targetType == 3 /*WEAPONFIRECHUNK_TARGET_LOCATION*/) { // Do nothing... } #endif } //--------------------------------------------------------------------------- bool WeaponFireChunk::equalTo (WeaponFireChunkPtr chunk) { if (targetType != chunk->targetType) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (targetId != chunk->targetId) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (targetCell[0] != chunk->targetCell[0]) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (targetCell[1] != chunk->targetCell[1]) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (specialType != chunk->specialType) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (specialId != chunk->specialId) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (weaponIndex != chunk->weaponIndex) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (hit != chunk->hit) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (entryAngle != chunk->entryAngle) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (numMissiles != chunk->numMissiles) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } if (hitLocation != chunk->hitLocation) { DebugWeaponFireChunk(this, chunk, NULL); return(false); } return(true); } //*************************************************************************** // WEAPONHITCHUNK class //*************************************************************************** void DebugWeaponHitChunk (WeaponHitChunkPtr chunk1, WeaponHitChunkPtr chunk2) { ChunkDebugMsg[0] = NULL; char outString[512]; if (chunk1) { strcat(ChunkDebugMsg, "\nCHUNK1\n"); GameObjectPtr target = NULL; bool isTargetPoint = false; if (chunk1->targetType == WEAPONHITCHUNK_TARGET_MOVER) target = (GameObjectPtr)MPlayer->moverRoster[chunk1->targetId]; else if (chunk1->targetType == WEAPONHITCHUNK_TARGET_TERRAIN) target = ObjectManager->findByPartId(chunk1->targetId); else if (chunk1->targetType == WEAPONHITCHUNK_TARGET_SPECIAL) target = ObjectManager->findByPartId(chunk1->targetId); else if (chunk1->targetType == WEAPONHITCHUNK_TARGET_LOCATION) isTargetPoint = true; if (target) { if (target->isMover()) { sprintf(outString, "target = %s (%d)\n", target->getName(), target->getPartId()); strcat(ChunkDebugMsg, outString); } else { sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId()); strcat(ChunkDebugMsg, outString); } } else if (isTargetPoint) strcat(ChunkDebugMsg, "target point\n"); else strcat(ChunkDebugMsg, "target = ???\n"); sprintf(outString, "targetType = %d\n", chunk1->targetType); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetId = %d\n", chunk1->targetId); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialType = %d\n", chunk1->specialType); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialId = %d\n", chunk1->specialId); strcat(ChunkDebugMsg, outString); sprintf(outString, "cause = %d\n", chunk1->cause); strcat(ChunkDebugMsg, outString); sprintf(outString, "damage = %f\n", chunk1->damage); strcat(ChunkDebugMsg, outString); sprintf(outString, "hitLocation = %d\n", chunk1->hitLocation); strcat(ChunkDebugMsg, outString); sprintf(outString, "entryAngle = %d\n", chunk1->entryAngle); strcat(ChunkDebugMsg, outString); sprintf(outString, "refit = %s\n", chunk1->refit ? "true" : "false"); strcat(ChunkDebugMsg, outString); } if (chunk2) { strcat(ChunkDebugMsg, "\nCHUNK2\n"); GameObjectPtr target = NULL; bool isTargetPoint = false; if (chunk2->targetType == WEAPONHITCHUNK_TARGET_MOVER) target = (GameObjectPtr)MPlayer->moverRoster[chunk2->targetId]; else if (chunk2->targetType == WEAPONHITCHUNK_TARGET_TERRAIN) target = ObjectManager->findByPartId(chunk2->targetId); else if (chunk2->targetType == WEAPONHITCHUNK_TARGET_SPECIAL) target = ObjectManager->findByPartId(chunk2->targetId); else if (chunk2->targetType == WEAPONHITCHUNK_TARGET_LOCATION) isTargetPoint = true; if (target) { if (target->isMover()) { sprintf(outString, "target = %s (%d)\n", target->getName(), target->getPartId()); strcat(ChunkDebugMsg, outString); } else { sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId()); strcat(ChunkDebugMsg, outString); } } else if (isTargetPoint) strcat(ChunkDebugMsg, "target point"); else strcat(ChunkDebugMsg, "target = ???\n"); sprintf(outString, "targetType = %d\n", chunk2->targetType); strcat(ChunkDebugMsg, outString); sprintf(outString, "targetId = %d\n", chunk2->targetId); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialType = %d\n", chunk2->specialType); strcat(ChunkDebugMsg, outString); sprintf(outString, "specialId = %d\n", chunk2->specialId); strcat(ChunkDebugMsg, outString); sprintf(outString, "cause = %d\n", chunk2->cause); strcat(ChunkDebugMsg, outString); sprintf(outString, "damage = %f\n", chunk2->damage); strcat(ChunkDebugMsg, outString); sprintf(outString, "hitLocation = %d\n", chunk2->hitLocation); strcat(ChunkDebugMsg, outString); sprintf(outString, "entryAngle = %d\n", chunk2->entryAngle); strcat(ChunkDebugMsg, outString); sprintf(outString, "refit = %s\n", chunk2->refit ? "true" : "false"); strcat(ChunkDebugMsg, outString); } File* debugFile = new File; debugFile->create("whchunk.dbg"); debugFile->writeString(ChunkDebugMsg); debugFile->close(); delete debugFile; debugFile = NULL; ExceptionGameMsg = ChunkDebugMsg; } //--------------------------------------------------------------------------- void* WeaponHitChunk::operator new (size_t ourSize) { void* result = systemHeap->Malloc(ourSize); return(result); } //--------------------------------------------------------------------------- void WeaponHitChunk::operator delete (void* us) { systemHeap->Free(us); } //--------------------------------------------------------------------------- void WeaponHitChunk::buildMoverTarget (GameObjectPtr target, long _cause, float _damage, long _hitLocation, float _entryAngle, bool isRefit) { targetType = WEAPONHITCHUNK_TARGET_MOVER; targetId = ((MoverPtr)target)->getNetRosterIndex(); //Assert((targetId > -1) && (targetId < MPlayer->numMovers), targetId, " WeaponHitChunk.buildMoverTarget: bad targetId "); cause = _cause; //Assert((cause damage = _damage; //Assert((damage >= ) && (damage < 1024), damage, " WeaponHitChunk.buildMoverTarget: bad damage "); hitLocation = _hitLocation; //Assert((hitLocation > -2) && (hitLocation < 12), hitLocation, " WeaponHitChunk.buildMoverTarget: bad hitLocation "); if ((_entryAngle >= -45.0) && (_entryAngle <= 45.0)) entryAngle = 0; //MECH_HIT_ARC_FRONT; else if ((_entryAngle > -135.0) && (_entryAngle < -45.0)) entryAngle = 2; //MECH_HIT_ARC_LEFT; else if ((_entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 3; //MECH_HIT_ARC_RIGHT; else entryAngle = 1; //MECH_HIT_ARC_REAR; refit = isRefit; data = 0; } //--------------------------------------------------------------------------- void WeaponHitChunk::buildTerrainTarget (GameObjectPtr target, float _damage) { targetType = WEAPONHITCHUNK_TARGET_TERRAIN; targetId = target->getPartId(); damage = _damage; data = 0; } //--------------------------------------------------------------------------- void WeaponHitChunk::buildCameraDroneTarget (GameObjectPtr target, float _damage, float _entryAngle) { targetType = WEAPONHITCHUNK_TARGET_SPECIAL; targetId = target->getPartId(); specialType = WEAPONFIRECHUNK_SPECIAL_CAMERADRONE; specialId = targetId - MIN_CAMERA_DRONE_ID; damage = _damage; //Assert((damage >= ) && (damage < 1024), damage, " WeaponHitChunk.buildMoverTarget: bad damage "); if ((_entryAngle >= -45.0) && (_entryAngle <= 45.0)) entryAngle = 0; //MECH_HIT_ARC_FRONT; else if ((_entryAngle > -135.0) && (_entryAngle < -45.0)) entryAngle = 2; //MECH_HIT_ARC_LEFT; else if ((_entryAngle > 45.0) && (_entryAngle < 135)) entryAngle = 3; //MECH_HIT_ARC_RIGHT; else entryAngle = 1; //MECH_HIT_ARC_REAR; data = 0; } //--------------------------------------------------------------------------- void WeaponHitChunk::build (GameObjectPtr target, WeaponShotInfoPtr shotInfo, bool isRefit) { if (!target) Fatal(0, " WeaponHitChunk.build: NULL target "); Assert(((float)((unsigned long)(shotInfo->damage * 4.0)) * 0.25) == shotInfo->damage, 0, " WeaponHitChunk.build: damage round error "); if (target->isMover()) { //--------------------------------------------------------------- // HACK fix for ammoExplosions without needing to save the weapon // master ID... if (shotInfo->masterId > 0) { if (MasterComponent::masterList[shotInfo->masterId].getForm() == COMPONENT_FORM_AMMO) shotInfo->masterId = -4; else shotInfo->masterId = 0; } buildMoverTarget(target, shotInfo->masterId, shotInfo->damage, shotInfo->hitLocation, shotInfo->entryAngle, isRefit); } else { switch (target->getObjectClass()) { case BUILDING: case TREEBUILDING: case TREE: case GATE: case TURRET: case BRIDGE: buildTerrainTarget(target, shotInfo->damage); break; case CAMERADRONE: buildCameraDroneTarget(target, shotInfo->damage, shotInfo->entryAngle); break; default: //Fatal(0, " WeaponHitChunk.build: bad target type "); break; } } } //--------------------------------------------------------------------------- void WeaponHitChunk::pack (void) { data = 0; switch (targetType) { case WEAPONHITCHUNK_TARGET_MOVER: //---------------- // Mover Target... if (refit) data |= 1; data <<= WEAPONHITCHUNK_ENTRYQUAD_BITS; data |= entryAngle; data <<= WEAPONHITCHUNK_HITLOCATION_BITS; data |= (hitLocation + 2); data <<= WEAPONHITCHUNK_CAUSE_BITS; data |= (cause + 7); data <<= WEAPONHITCHUNK_MOVERINDEX_BITS; data |= targetId; data <<= WEAPONHITCHUNK_DAMAGE_BITS; break; case WEAPONHITCHUNK_TARGET_TERRAIN: //------------------------- // Terrain Object Target... data |= (targetId - MIN_TERRAIN_PART_ID); data <<= WEAPONHITCHUNK_DAMAGE_BITS; break; case WEAPONHITCHUNK_TARGET_SPECIAL: data |= entryAngle; data <<= WEAPONHITCHUNK_SPECIALTYPE_BITS; data |= specialType; data <<= WEAPONHITCHUNK_SPECIALID_BITS; data |= specialId; data <<= WEAPONHITCHUNK_DAMAGE_BITS; break; default: Fatal(0, " Bad WeaponHitChunk Target Type "); } data |= (unsigned long)(damage * 4.0); data <<= WEAPONHITCHUNK_TARGETTYPE_BITS; data |= targetType; } //--------------------------------------------------------------------------- void WeaponHitChunk::unpack (void) { unsigned long tempData = data; targetType = (tempData & WEAPONHITCHUNK_TARGETTYPE_MASK); tempData >>= WEAPONHITCHUNK_TARGETTYPE_BITS; damage = (float)(tempData & WEAPONHITCHUNK_DAMAGE_MASK) * 0.25; tempData >>= WEAPONHITCHUNK_DAMAGE_BITS; Assert((damage >= 0.0) && (damage <= 255.0), 0, " WeaponHitChunk.unpack: bad damage "); switch (targetType) { case WEAPONHITCHUNK_TARGET_MOVER: //---------------- // Mover Target... targetId = (tempData & WEAPONHITCHUNK_MOVERINDEX_MASK); tempData >>= WEAPONHITCHUNK_MOVERINDEX_BITS; Assert((targetId > -1) && (targetId < MAX_MULTIPLAYER_MOVERS), targetId, " WeaponHitChunk.unpack: bad targetId "); cause = ((tempData & WEAPONHITCHUNK_CAUSE_MASK) - 7); tempData >>= WEAPONHITCHUNK_CAUSE_BITS; Assert((cause >= -7) && (cause <= 0), cause, " WeaponHitChunk.unpack: bad cause "); hitLocation = ((tempData & WEAPONHITCHUNK_HITLOCATION_MASK) - 2); tempData >>= WEAPONHITCHUNK_HITLOCATION_BITS; Assert((hitLocation > -2) && (hitLocation < 12), hitLocation, " WeaponHitChunk.unpack: bad hitLocation "); entryAngle = (tempData & WEAPONHITCHUNK_ENTRYQUAD_MASK); tempData >>= WEAPONHITCHUNK_ENTRYQUAD_BITS; refit = (tempData & WEAPONHITCHUNK_REFIT_MASK) ? true : false; break; case WEAPONHITCHUNK_TARGET_TERRAIN: //------------------------- // Terrain Object Target... targetId = MIN_TERRAIN_PART_ID + (tempData & WEAPONHITCHUNK_TARGETID_MASK); tempData >>= WEAPONHITCHUNK_TARGETID_BITS; break; case WEAPONHITCHUNK_TARGET_SPECIAL: //----------------------- // Train Object Target... specialId = (tempData & WEAPONHITCHUNK_SPECIALID_MASK); tempData >>= WEAPONHITCHUNK_SPECIALID_BITS; specialType = (tempData & WEAPONHITCHUNK_SPECIALTYPE_MASK); tempData >>= WEAPONHITCHUNK_SPECIALTYPE_BITS; switch (specialType) { case WEAPONHITCHUNK_SPECIAL_CAMERADRONE: targetId = MIN_CAMERA_DRONE_ID + specialId; break; default: Fatal(specialType, " WeaponHitChunk.unpack: bad specialType "); } entryAngle = (tempData & WEAPONHITCHUNK_ENTRYQUAD_MASK); break; default: DebugWeaponHitChunk(this, NULL); Fatal(0, " Bad WeaponHitChunk Target Type "); } } //--------------------------------------------------------------------------- bool WeaponHitChunk::equalTo (WeaponHitChunkPtr chunk) { if (targetType != chunk->targetType) { DebugWeaponHitChunk(this, chunk); return(false); } if (targetId != chunk->targetId) { DebugWeaponHitChunk(this, chunk); return(false); } if (specialType != chunk->specialType) { DebugWeaponHitChunk(this, chunk); return(false); } if (specialId != chunk->specialId) { DebugWeaponHitChunk(this, chunk); return(false); } if (cause != chunk->cause) { DebugWeaponHitChunk(this, chunk); return(false); } if (damage != chunk->damage) { DebugWeaponHitChunk(this, chunk); return(false); } if (entryAngle != chunk->entryAngle) { DebugWeaponHitChunk(this, chunk); return(false); } if (refit != chunk->refit) { DebugWeaponHitChunk(this, chunk); return(false); } if (hitLocation != chunk->hitLocation) { DebugWeaponHitChunk(this, chunk); return(false); } return(true); } //--------------------------------------------------------------------------- bool WeaponHitChunk::valid (long from) { if (refit) { if (targetType != 0) Fatal(0, " Multiplayer.handleAppWeaponHitUpdate: bad targetType for refit "); MoverPtr target = MPlayer->moverRoster[targetId]; if (!target) { if (CombatLog) CombatLog->write("WeaponHitChunk INVALID: refit has NULL target"); DebugWeaponHitChunk(this, NULL); return(false); } } else { WeaponShotInfo shotInfo; shotInfo.attackerWID = 0; shotInfo.masterId = cause; shotInfo.damage = damage; shotInfo.hitLocation = hitLocation; //shotInfo.entryAngle = entryQuadTable[entryAngle]; switch (targetType) { case WEAPONHITCHUNK_TARGET_MOVER: #ifdef _DEBUG //if (WeaponHitLog) // WeaponHitLog->writeString("==>handleAppWeaponHit\n"); #endif //FromMP = true; //if (moverRoster[chunk.targetId]) // moverRoster[chunk.targetId]->handleWeaponHit(&shotInfo); //FromMP = false; break; case WEAPONHITCHUNK_TARGET_TERRAIN: case WEAPONHITCHUNK_TARGET_SPECIAL: { GameObjectPtr target = ObjectManager->findByPartId(targetId); if (!target) { char s[512]; long r = (targetId - MIN_TERRAIN_PART_ID) / MAX_MAP_CELL_WIDTH; long c = targetId - MIN_TERRAIN_PART_ID - (MAX_MAP_CELL_WIDTH * r); sprintf(s, "WeaponHitChunk INVALID: NULL terrain target (%d), rc = %d,%d, type = %d, damage = %.2f, data=%d", targetId, r, c, targetType, damage, data); if (CombatLog) { CombatLog->write(s); CombatLog->dump(); //CombatLog->close(); } DebugWeaponHitChunk(this, NULL); return(false); } } break; default: if (CombatLog) { CombatLog->write("WeaponHitChunk INVALID: bad targetType"); CombatLog->dump(); } DebugWeaponHitChunk(this, NULL); return(false); } } return(true); } //--------------------------------------------------------------------------- // class GameObject //--------------------------------------------------------------------------- void* GameObject::operator new (size_t ourSize) { void* result = ObjectTypeManager::objectCache->Malloc(ourSize); return(result); } //--------------------------------------------------------------------------- void GameObject::operator delete (void* us) { ObjectTypeManager::objectCache->Free(us); } //--------------------------------------------------------------------------- void GameObject::init (bool create) { objectClass = GAMEOBJECT; if (initialize) { handle = 0; appearance = NULL; threatRating = 0; } partId = 0; watchID = 0; typeHandle = 0; position.Zero(); cellPositionRow = 0; cellPositionCol = 0; flags = OBJECT_FLAG_USEME | OBJECT_FLAG_AWAKE; debugFlags = 0; status = OBJECT_STATUS_NORMAL; tonnage = 0.0; d_vertexNum = -1; //team = 255; collisionFreeFromWID = 0; collisionFreeTime = 0.0; screenPos.x = 0.0f; screenPos.y = 0.0f; screenPos.z = 0.0f; screenPos.w = 0.0f; windowsVisible = 0; explDamage = 0.0; explRadius = 0.0; maxCV = 0; curCV = 0; lastFrameTime = 0.0; blipFrame = 0; numAttackers = 0; rotation = 0.0; drawFlags = 0; } //--------------------------------------------------------------------------- void GameObject::set (GameObject copy) { objectClass = copy.objectClass; handle = copy.handle; partId = copy.partId; watchID = copy.watchID; typeHandle = copy.typeHandle; position = copy.position; cellPositionRow = copy.cellPositionRow; cellPositionCol = copy.cellPositionCol; flags = copy.flags; debugFlags = copy.debugFlags; status = copy.status; tonnage = copy.tonnage; appearance = copy.appearance; d_vertexNum = copy.d_vertexNum; collisionFreeFromWID = copy.collisionFreeFromWID; collisionFreeTime = copy.collisionFreeTime; screenPos = copy.screenPos; windowsVisible = copy.windowsVisible; explRadius = copy.explRadius; explDamage = copy.explDamage; maxCV = copy.maxCV; curCV = copy.curCV; lastFrameTime = copy.lastFrameTime; blipFrame = copy.blipFrame; numAttackers = copy.numAttackers; } //--------------------------------------------------------------------------- ObjectTypePtr GameObject::getObjectType (void) { return(ObjectManager->getObjectType(typeHandle)); } //--------------------------------------------------------------------------- void GameObject::init (bool create, ObjectTypePtr _type) { typeHandle = _type->whatAmI(); #ifdef _DEBUG // id = _type->getName(); #endif objectClass = GAMEOBJECT; } //--------------------------------------------------------------------------- unsigned long GameObject::getWatchID (bool assign) { if ((watchID == 0) && assign) ObjectManager->setWatchID(this); return(watchID); } //--------------------------------------------------------------------------- void GameObject::getBlockAndVertexNumber (long &blockNum, long &vertexNum) { Assert(Terrain::worldUnitsPerVertex==128,0," Optimizations now broken "); // What is our block and vertex number? long mx = (float2long(position.x) >> 7) + Terrain::halfVerticesMapSide; long blockX = float2long(mx * Terrain::oneOverVerticesBlockSide); long my = Terrain::halfVerticesMapSide - ((float2long(position.y) >> 7) + 1); long blockY = float2long(my * Terrain::oneOverVerticesBlockSide); blockNum = blockX + (blockY * Terrain::blocksMapSide); long vertexX = mx - (blockX * Terrain::verticesBlockSide); long vertexY = my - (blockY * Terrain::verticesBlockSide); vertexNum = vertexX + (vertexY * Terrain::verticesBlockSide); } //--------------------------------------------------------------------------- long GameObject::kill (void) { //------------------------------------------------------------ //Once new MC II ObjMgr is up and running, put this back in... // DO NOT DO THIS ANYMORE. EACH OBJECT MUST HAVE ITS OWN DESTRUCTION NOW> // GOSFX Change 12/15/99 -fs Code Deleted return(OBJECT_DEAD); } //--------------------------------------------------------------------------- float GameObject::relFacingTo (Stuff::Vector3D goal, long bodyLocation) { Stuff::Vector3D facingVec = getRotationVector(); Stuff::Vector3D goalVec; goalVec.Subtract(goal, position); float angle = angle_from(facingVec, goalVec); //-------------------------------- // Get sign of relative angle. float z = (facingVec.x * goalVec.y) - (facingVec.y * goalVec.x); if (z > 0.0f) angle = -angle; return(angle); } //--------------------------------------------------------------------------- Stuff::Vector3D GameObject::relativePosition (float angle, float distance, unsigned long flags) { //-------------------------------------------------------- // Note that the angle should be -180 <= angle <= 180, and // the distance is in meters... #ifdef USE_ROTATION Stuff::Vector2DOf curPos; curPos.x = position.x; curPos.y = position.y; distance *= -worldUnitsPerMeter; Stuff::Vector2DOf shiftVect; //-------------------------------------------- // Absolute facing, based upon north facing... shiftVect.x = 0.0; shiftVect.y = 1.0; float tx = shiftVect.x; float sine = sin(angle); float cosine = cos(angle); shiftVect.x *= cosine; shiftVect.x += (shiftVect.y * sine); shiftVect.y *= cosine; shiftVect.y -= (tx * sine); //shiftVect.rotate(angle); shiftVect *= distance; Stuff::Vector2DOf relPos; relPos.x = curPos.x + shiftVect.x; relPos.y = curPos.y + shiftVect.y; Stuff::Vector2DOf start2d; Stuff::Vector2DOf goal2d; Stuff::Vector2DOf deltaVector; if (flags & RELPOS_FLAG_PASSABLE_START) { start2d = curPos; goal2d = relPos; } else { start2d = relPos; goal2d = curPos; } deltaVector.x= goal2d.x - start2d.x; deltaVector.y = goal2d.y - start2d.y; //------------------------------------------------------------- // First, we need to calc the delta vector--how much we extend // the ray everytime we check the map cell for clear placement. deltaVector.normalize(); float cellLength = Terrain::metersPerVertex / (float)MAPCELL_DIM; cellLength *= 0.5; deltaVector *= cellLength; if (deltaVector.magnitude() == 0.0) return(curPos); //------------------------------------------------- // Determine the max length the ray must be cast... float maxLength = start2d.distance_from(goal2d); //------------------------------------------------------------ // We'll start at the target, and if it's blocked, we'll move // toward our start location, looking for the first valid/open // cell... Stuff::Vector2DOf curPoint = start2d; Stuff::Vector2DOf curRay; curRay.zero(); float rayLength = 0.0; long tileR, tileC, cellR, cellC; Stuff::Vector3D curPoint3d; curPoint3d.init(curPoint.x, curPoint.y, 0.0); GameMap->worldToMapPos(curPoint3d, tileR, tileC, cellR, cellC); bool cellClear = GameMap->cellPassable(tileR, tileC, cellR, cellC); Stuff::Vector2DOf lastGoodPoint = curPoint; if (flags & RELPOS_FLAG_PASSABLE_START) while (cellClear && (rayLength < maxLength)) { lastGoodPoint = curPoint; curPoint += deltaVector; curRay = curPoint - start2d; rayLength = curRay.magnitude(); curPoint3d.init(curPoint.x, curPoint.y, 0.0); GameMap->worldToMapPos(curPoint3d, tileR, tileC, cellR, cellC); cellClear = GameMap->cellPassable(tileR, tileC, cellR, cellC); } else while (!cellClear && (rayLength < maxLength)) { lastGoodPoint = curPoint; curPoint += deltaVector; curRay = curPoint - start2d; rayLength = curRay.magnitude(); curPoint3d.init(curPoint.x, curPoint.y, 0.0); GameMap->worldToMapPos(curPoint3d, tileR, tileC, cellR, cellC); cellClear = GameMap->cellPassable(tileR, tileC, cellR, cellC); } curPoint3d.init(lastGoodPoint.x, lastGoodPoint.y, 0.0); curPoint3d.z = GameMap->getTerrainElevation(curPoint3d); return(curPoint3d); #else return(position); #endif } //--------------------------------------------------------------------------- void GameObject::setPosition (const Stuff::Vector3D& newPosition, bool calcPositions) { position = newPosition; if (calcPositions) { long newCellRow = 0; long newCellCol = 0; long tileRow = 0; long tileCol = 0; land->worldToTileCell(position, tileRow, tileCol, newCellRow, newCellCol); cellPositionRow = newCellRow + tileRow * MAPCELL_DIM; cellPositionCol = newCellCol + tileCol * MAPCELL_DIM; d_vertexNum = tileRow * Terrain::realVerticesMapSide + tileCol; } Assert((cellPositionRow >= 0) && (cellPositionRow < GameMap->getHeight()), 0, " Object moved off map "); Assert((cellPositionCol >= 0) && (cellPositionCol < GameMap->getWidth()), 0, " Object moved off map "); } //--------------------------------------------------------------------------- float GameObject::distanceFrom (Stuff::Vector3D goal) { Stuff::Vector3D result; result.x = position.x - goal.x; result.y = position.y - goal.y; result.z = 0.0; return((result.GetLength() * metersPerWorldUnit)); } //--------------------------------------------------------------------------- long GameObject::cellDistanceFrom (Stuff::Vector3D goal) { long cellRow = 0; long cellCol = 0; land->worldToCell(goal, cellRow, cellCol); long rowDelta = 0; if (cellPositionRow > cellRow) rowDelta = cellPositionRow - cellRow; else rowDelta = cellRow - cellPositionRow; long colDelta = 0; if (cellPositionCol > cellCol) colDelta = cellPositionCol - cellCol; else colDelta = cellCol - cellPositionCol; return(rowDelta > colDelta ? rowDelta : colDelta); } //--------------------------------------------------------------------------- long GameObject::cellDistanceFrom (GameObjectPtr obj) { long rowDelta = 0; if (cellPositionRow > obj->cellPositionRow) rowDelta = cellPositionRow - obj->cellPositionRow; else rowDelta = obj->cellPositionRow - cellPositionRow; long colDelta = 0; if (cellPositionCol > obj->cellPositionCol) colDelta = cellPositionCol - obj->cellPositionCol; else colDelta = obj->cellPositionCol - cellPositionCol; return(rowDelta > colDelta ? rowDelta : colDelta); } //--------------------------------------------------------------------------- long GameObject::getLineOfSightNodes (long eyeCellRow, long eyeCellCol, long* cells) { cells[0] = cellPositionRow; cells[1] = cellPositionCol; return(1); } //--------------------------------------------------------------------------- bool GameObject::lineOfSight (long cellRow, long cellCol, bool checkVisibleBits) { bool LOSclear = Team::lineOfSight(0.0f,cellPositionRow, cellPositionCol, cellRow, cellCol, getTeamId(), 15.0, checkVisibleBits); return(LOSclear); } //--------------------------------------------------------------------------- bool GameObject::lineOfSight (Stuff::Vector3D point, bool checkVisibleBits) { Stuff::Vector3D firingPosition = getLOSPosition(); Stuff::Vector3D targetPosition = point; bool LOSclear = false; if (land->IsGameSelectTerrainPosition(point)) LOSclear = Team::lineOfSight(firingPosition, targetPosition, getTeamId(), 15.0, checkVisibleBits); return(LOSclear); } //--------------------------------------------------------------------------- #ifdef LAB_ONLY extern __int64 MCTimeLOSUpdate; #endif inline bool GameObject::lineOfSight (GameObjectPtr target, float startExtRad, bool checkVisibleBits) { __int64 timeStart = GetCycles(); //If we call this without a target, we have no LOS!! // Keeps it from crashing, too. // Not sure where all of the calls Glenn makes to this are, but I'm looking! if (!target) { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return false; } Stuff::Vector3D distance; distance.Subtract(target->getPosition(),getPosition()); float dist = distance.GetApproximateLength(); //Figure out altitude above minimum terrain altitude and look up in table. float baseElevation = MapData::waterDepth; if (MapData::waterDepth < Terrain::userMin) baseElevation = Terrain::userMin; float altitude = position.z - baseElevation; float altitudeIntegerRange = (Terrain::userMax - baseElevation) * 0.00390625f; long altLevel = 0; if (altitudeIntegerRange > Stuff::SMALL) altLevel = altitude / altitudeIntegerRange; if (altLevel < 0) altLevel = 0; if (altLevel > 255) altLevel = 255; float radius = visualRangeTable[altLevel]; //Scouting specialty skill. if (isMover()) { MoverPtr mover = (MoverPtr)this; if (mover->pilot && mover->pilot->isScout()) radius += (radius * 0.2f); radius *= mover->getLOSFactor(); } if (dist > (radius * 25.0f * worldUnitsPerMeter)) { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return false; } //-------------------------------------------------------------------------- // For now, we hardcode the "height" of the firer and target (to 10 meters). // Easily changed when we add a height field to the object class... if (target->isMover()) { if (isMover() && ObjectManager->useMoverLineOfSightTable) { long index = (handle * ObjectManager->maxMovers) + target->handle; char losStatus = ObjectManager->moverLineOfSightTable[index]; if (losStatus == -1) { bool los = Team::lineOfSight(getLOSPosition(), target->getLOSPosition(), getTeamId(), target->getAppearRadius(), startExtRad, checkVisibleBits); if (los) { ObjectManager->moverLineOfSightTable[index] = 1; //Inverse is NOT always true!!!! // I can demonstrate a case!! // -fs // ObjectManager->moverLineOfSightTable[(target->handle * ObjectManager->maxMovers) + handle] = 1; } else { ObjectManager->moverLineOfSightTable[index] = 0; // ObjectManager->moverLineOfSightTable[(target->handle * ObjectManager->maxMovers) + handle] = 0; } } if (ObjectManager->moverLineOfSightTable[index]) { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return true; } else { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return false; } } if (Team::lineOfSight(getLOSPosition(), target->getLOSPosition(), getTeamId(), target->getAppearRadius(), startExtRad, checkVisibleBits)) { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return(true); } else { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return(false); } } //Try yanking these and just use the location the weapon fire is going to. // With the extent radius, like I originally did. // -fs //long lineOfSightNodes[20]; //long numNodes = target->getLineOfSightNodes(cellPositionRow, cellPositionCol, lineOfSightNodes); // for (long i = 0; i < numNodes; i++) // { // if (land->IsGameSelectTerrainPosition(getLOSPosition())) // { float elev = land->getTerrainElevation(getLOSPosition()); float localStart = getLOSPosition().z - elev; Stuff::Vector3D targetPosition = target->getLOSPosition(); if (Team::lineOfSight(getLOSPosition(), target->getLOSPosition(), getTeamId(), target->getAppearRadius(), startExtRad, checkVisibleBits)) { #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return(true); } // } // } #ifdef LAB_ONLY MCTimeLOSUpdate += (GetCycles() - timeStart); #endif return(false); } //--------------------------------------------------------------------------- void GameObject::destroy (void) { //Never need to call this. Heap destruct will get this!! // ObjectManager->removeObjectType(typeHandle); } //--------------------------------------------------------------------------- bool GameObject::onScreen (void) { //---------------------------------------------------------------------- // This function is the meat and potatoes of the object cull system. // Its job is to determine if the object is on screen or not. // It does this by transforming the position for each active camera to // its screen coords and saving them. It then checks each set of coords // to see if they are in the viewport of each camera. Returned value // is number of windows that object can be seen in. long isVisible = 0; if (eye) { Stuff::Vector3D objPosition = position; isVisible = eye->projectZ(objPosition, screenPos); } if (isVisible) { windowsVisible = turn; return(true); } return(false); } //--------------------------------------------------------------------------- float GameObject::getExtentRadius (void) { //--------------------------------------------------------------------- // Can be overridden by explosions to return an instance based value. ObjectTypePtr objType = ObjectManager->getObjectType(typeHandle); if (objType) return(objType->getExtentRadius()); return(-1.0); } //--------------------------------------------------------------------------- void GameObject::setExtentRadius (float newRadius) { //--------------------------------------------------------------------- // Can be overridden by explosions to set an instance based value. ObjectTypePtr objType = ObjectManager->getObjectType(typeHandle); if (objType) objType->setExtentRadius(newRadius); } //--------------------------------------------------------------------------- MechClass GameObject::getMechClass(void) { if (getObjectClass() != BATTLEMECH) return(MECH_CLASS_NONE); if (tonnage < MechClassWeights[MECH_CLASS_LIGHT]) return(MECH_CLASS_LIGHT); if (tonnage < MechClassWeights[MECH_CLASS_MEDIUM]) return(MECH_CLASS_MEDIUM); if (tonnage < MechClassWeights[MECH_CLASS_HEAVY]) return(MECH_CLASS_HEAVY); return(MECH_CLASS_ASSAULT); } //--------------------------------------------------------------------------- long GameObject::getCaptureBlocker (GameObjectPtr capturingMover, GameObjectPtr* blockerList) { long numBlockers = 0; TeamPtr capturingTeam = capturingMover->getTeam(); if (!capturingTeam) STOP(("GameObject.getCaptureBlocker: NULL capturingTeam")); if (distanceFrom(capturingMover->getPosition()) <= 30.0) { for (long i = 0; i < ObjectManager->getNumMovers(); i++) { MoverPtr mover = ObjectManager->getMover(i); if (capturingTeam->isEnemy(mover->getTeam())) if (!mover->isMarine() && (mover->numWeapons > 0)) if ((distanceFrom(mover->getPosition()) < blockCaptureRange) && !mover->isDisabled() && mover->getAwake()) if (blockerList) blockerList[numBlockers++] = mover; else return(1); } } else { for (long i = 0; i < ObjectManager->getNumMovers(); i++) { MoverPtr mover = ObjectManager->getMover(i); if (!mover->getTeam() || capturingTeam->isEnemy(mover->getTeam())) if (!mover->isMarine() && (mover->numWeapons > 0)) if ((distanceFrom(mover->getPosition()) < blockCaptureRange) && !mover->isDisabled() && mover->getAwake()) if (capturingMover->getTeam()->isContact(capturingMover, mover, CONTACT_CRITERIA_VISUAL_OR_SENSOR + CONTACT_CRITERIA_ENEMY + CONTACT_CRITERIA_NOT_DISABLED)) if (blockerList) blockerList[numBlockers++] = mover; else return(1); } } return(numBlockers); } //--------------------------------------------------------------------------- bool GameObject::isFriendly (GameObjectPtr obj) { TeamPtr myTeam = getTeam(); TeamPtr objTeam = obj->getTeam(); if (myTeam && objTeam) return(myTeam->isFriendly(objTeam)); return(false); } //--------------------------------------------------------------------------- bool GameObject::isEnemy (GameObjectPtr obj) { TeamPtr myTeam = getTeam(); TeamPtr objTeam = obj->getTeam(); if (myTeam && objTeam) return(myTeam->isEnemy(objTeam)); return(false); } //--------------------------------------------------------------------------- bool GameObject::isNeutral (GameObjectPtr obj) { TeamPtr myTeam = getTeam(); TeamPtr objTeam = obj->getTeam(); if (myTeam && objTeam) return(myTeam->isNeutral(objTeam)); return(false); } //--------------------------------------------------------------------------- void GameObject::CopyTo (GameObjectData *data) { if (getObjectType()) data->objectTypeNum = getObjectType()->getObjTypeNum(); else data->objectTypeNum = 0; data->objectClass = objectClass; data->handle = handle; data->partId = partId; data->watchID = watchID; data->typeHandle = typeHandle; data->position = position; data->cellPositionRow = cellPositionRow; data->cellPositionCol = cellPositionCol; data->d_vertexNum = d_vertexNum; data->flags = flags; data->debugFlags = debugFlags; data->status = status; data->tonnage = tonnage; data->rotation = rotation; if (getObjectType() && getObjectType()->getAppearanceTypeName()) { if (strlen(getObjectType()->getAppearanceTypeName()) <= 255) strcpy(data->appearanceTypeID,getObjectType()->getAppearanceTypeName()); else STOP(("Object Appearance name too long for Save. %s",getObjectType()->getAppearanceTypeName())); } else { strcpy(data->appearanceTypeID,"NONE"); } data->collisionFreeFromWID = collisionFreeFromWID; data->collisionFreeTime = collisionFreeTime; data->screenPos = screenPos; data->windowsVisible = windowsVisible; data->explRadius = explRadius; data->explDamage = explDamage; data->maxCV = maxCV; data->curCV = curCV; data->threatRating = threatRating; data->lastFrameTime = lastFrameTime; data->blipFrame = blipFrame; data->numAttackers = numAttackers; data->drawFlags = drawFlags; } //--------------------------------------------------------------------------- void GameObject::Save (PacketFilePtr file, long packetNum) { STOP(("Should never save a gameObj!!")); } //--------------------------------------------------------------------------- void GameObject::Load (GameObjectData *data) { objectClass = data->objectClass; handle = data->handle; partId = data->partId; watchID = data->watchID; position = data->position; cellPositionRow = data->cellPositionRow; cellPositionCol = data->cellPositionCol; d_vertexNum = data->d_vertexNum; flags = data->flags; debugFlags = data->debugFlags; status = data->status; tonnage = data->tonnage; rotation = data->rotation; collisionFreeFromWID = data->collisionFreeFromWID; collisionFreeTime = data->collisionFreeTime; screenPos = data->screenPos; windowsVisible = 0; //Force back to zero so I don't have to save the turn. explRadius = data->explRadius; explDamage = data->explDamage; maxCV = data->maxCV; curCV = data->curCV; threatRating = data->threatRating; lastFrameTime = data->lastFrameTime; blipFrame = data->blipFrame; numAttackers = data->numAttackers; drawFlags = data->drawFlags; } //***************************************************************************