turret.cpp 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343
  1. //***************************************************************************
  2. //
  3. // turret.cpp -- File contains the Turret Object code
  4. //
  5. // MechCommander 2
  6. //
  7. //---------------------------------------------------------------------------//
  8. // Copyright (C) Microsoft Corporation. All rights reserved. //
  9. //===========================================================================//
  10. #ifndef MCLIB_H
  11. #include "mclib.h"
  12. #endif
  13. #ifndef TURRET_H
  14. #include "turret.h"
  15. #endif
  16. #ifndef GAMESOUND_H
  17. #include "gamesound.h"
  18. #endif
  19. #ifndef SOUNDS_H
  20. #include "sounds.h"
  21. #endif
  22. #ifndef MOVE_H
  23. #include "move.h"
  24. #endif
  25. #ifndef COLLSN_H
  26. #include "collsn.h"
  27. #endif
  28. #ifndef MECH_H
  29. #include "mech.h"
  30. #endif
  31. #ifndef GVEHICL_H
  32. #include "gvehicl.h"
  33. #endif
  34. #ifndef CARNAGE_H
  35. #include "carnage.h"
  36. #endif
  37. #ifndef TEAM_H
  38. #include "team.h"
  39. #endif
  40. #ifndef WEAPONFX_H
  41. #include "weaponfx.h"
  42. #endif
  43. #ifndef WEAPONBOLT_H
  44. #include "weaponbolt.h"
  45. #endif
  46. #ifndef MULTPLYR_H
  47. #include "multplyr.h"
  48. #endif
  49. #ifndef MISSION_H
  50. #include "mission.h"
  51. #endif
  52. #ifndef GAMELOG_H
  53. #include "gamelog.h"
  54. #endif
  55. #include "..\resource.h"
  56. //***************************************************************************
  57. //extern float worldUnitsPerMeter;
  58. //extern bool useSound;
  59. //extern BOOL useOldProject;
  60. //extern BOOL drawExtents;
  61. extern float WeaponRange[NUM_FIRERANGES];
  62. extern float WeaponRanges[NUM_WEAPON_RANGE_TYPES][2];
  63. extern float WeaponFireModifiers[NUM_WEAPONFIRE_MODIFIERS];
  64. extern float MaxVisualRadius;
  65. //extern long ClusterSizeSRM;
  66. //extern long ClusterSizeLRM;
  67. //extern float MaxVisualRadius;
  68. //extern float MetersPerCell;
  69. extern GameLog* CombatLog;
  70. extern void DebugWeaponFireChunk (WeaponFireChunkPtr chunk1, WeaponFireChunkPtr chunk2, GameObjectPtr attacker);
  71. extern void LogWeaponFireChunk (WeaponFireChunkPtr chunk, GameObjectPtr attacker, GameObjectPtr target);
  72. //long cLoadString (HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax );
  73. bool Turret::turretsEnabled[MAX_TEAMS] = {true, true, true, true, true, true, true, true};
  74. #define MAX_TURRET_PITCH (-45.0f)
  75. inline float agsqrt( float _a, float _b )
  76. {
  77. return sqrt(_a*_a + _b*_b);
  78. }
  79. //***************************************************************************
  80. // TURRET TYPE class
  81. //***************************************************************************
  82. GameObjectPtr TurretType::createInstance (void) {
  83. TurretPtr newTurret = new Turret;
  84. if (!newTurret)
  85. return(NULL);
  86. newTurret->init(true, this);
  87. return(newTurret);
  88. }
  89. //---------------------------------------------------------------------------
  90. void TurretType::destroy (void)
  91. {
  92. ObjectType::destroy();
  93. }
  94. //---------------------------------------------------------------------------
  95. long TurretType::init (FilePtr objFile, unsigned long fileSize) {
  96. long result = 0;
  97. FitIniFile bldgFile;
  98. result = bldgFile.open(objFile, fileSize);
  99. if (result != NO_ERR)
  100. return(result);
  101. //------------------------------------------------------------------
  102. // Read in the data needed for the TreeBuilding
  103. result = bldgFile.seekBlock("TurretData");
  104. if (result != NO_ERR)
  105. return(result);
  106. result = bldgFile.readIdFloat("LOSFactor",LOSFactor);
  107. if (result != NO_ERR)
  108. LOSFactor = 1.0f;
  109. unsigned long dmgLevel;
  110. result = bldgFile.readIdULong("DmgLevel",dmgLevel);
  111. if (result != NO_ERR)
  112. return(result);
  113. damageLevel = (float)dmgLevel;
  114. bldgFile.readIdULong("BlownEffectId",blownEffectId);
  115. if (result != NO_ERR)
  116. blownEffectId = -1;
  117. bldgFile.readIdULong("NormalEffectId",normalEffectId);
  118. if (result != NO_ERR)
  119. normalEffectId = -1;
  120. bldgFile.readIdULong("DamageEffectId",damageEffectId);
  121. if (result != NO_ERR)
  122. damageEffectId = -1;
  123. result = bldgFile.readIdFloat("ExplosionRadius",explRad);
  124. if (result != NO_ERR)
  125. explRad = 0.0;
  126. result = bldgFile.readIdFloat("ExplosionDamage",explDmg);
  127. if (result != NO_ERR)
  128. explDmg = 0.0;
  129. result = bldgFile.readIdFloat("Tonnage", baseTonnage);
  130. if (result != NO_ERR)
  131. baseTonnage = 20;
  132. result = bldgFile.readIdFloat("AttackRadius", engageRadius);
  133. if (result != NO_ERR)
  134. return result;
  135. result = bldgFile.readIdFloat("MaxTurretYawRate", turretYawRate);
  136. if (result != NO_ERR)
  137. return result;
  138. result = bldgFile.readIdLong("WeaponType", weaponMasterId[0]);
  139. if (result != NO_ERR)
  140. return(result);
  141. //SPOTLIGHTS!!!!
  142. if (weaponMasterId[0] != -1)
  143. punch = MasterComponent::masterList[weaponMasterId[0]].getCV();
  144. else
  145. punch = 0.0f;
  146. result = bldgFile.readIdLong("WeaponType1", weaponMasterId[1]);
  147. if (result != NO_ERR)
  148. weaponMasterId[1] = -1;
  149. else
  150. punch += MasterComponent::masterList[weaponMasterId[1]].getCV();
  151. result = bldgFile.readIdLong("WeaponType2", weaponMasterId[2]);
  152. if (result != NO_ERR)
  153. weaponMasterId[2] = -1;
  154. else
  155. punch += MasterComponent::masterList[weaponMasterId[2]].getCV();
  156. result = bldgFile.readIdLong("WeaponType3", weaponMasterId[3]);
  157. if (result != NO_ERR)
  158. weaponMasterId[3] = -1;
  159. else
  160. punch += MasterComponent::masterList[weaponMasterId[3]].getCV();
  161. result = bldgFile.readIdLong("PilotSkill", pilotSkill);
  162. if (result != NO_ERR)
  163. return(result);
  164. result = bldgFile.readIdFloat("LittleExtent",littleExtent);
  165. if (result != NO_ERR)
  166. littleExtent = 20.0;
  167. result = bldgFile.readIdLong ("BuildingName", turretTypeName);
  168. if (result != NO_ERR)
  169. turretTypeName = IDS_TRTOBJ_NAME;
  170. result = bldgFile.readIdLong( "BuildingDescription", buildingDescriptionID );
  171. if ( result != NO_ERR )
  172. buildingDescriptionID = -1;
  173. //------------------------------------------------------------------
  174. // Initialize the base object Type from the current file.
  175. result = ObjectType::init(&bldgFile);
  176. return(result);
  177. }
  178. //---------------------------------------------------------------------------
  179. bool TurretType::handleCollision (GameObjectPtr collidee, GameObjectPtr collider) {
  180. if (MPlayer && !MPlayer->isServer())
  181. return(true);
  182. //-------------------------------------------------------
  183. // The handleCollision routine checks the current target
  184. // against the thing collided with and compares distance.
  185. // The closest of the two becomes the new target.
  186. TurretPtr turret = (TurretPtr)collidee;
  187. GameObjectPtr target = ObjectManager->getByWatchID(turret->targetWID);
  188. float targetRange = 10000000.0;
  189. if (target) {
  190. Stuff::Vector3D targetRangeV3;
  191. targetRangeV3.Subtract(turret->getPosition(), target->getPosition());
  192. targetRange = targetRangeV3.x * targetRangeV3.x + targetRangeV3.y * targetRangeV3.y; //().magnitude();
  193. }
  194. //-------------------------------------
  195. // IMPORTANT MULTIPLAYER NOTE!!!!!!!!!!
  196. //-------------------------------------
  197. // Turrets may only target movers and
  198. // camera drones. NOTHING ELSE!
  199. // Anything else would cause BIG
  200. // BADNESS!
  201. //-------------------------------------
  202. // Turrets can check for neutral. Turrets must also check for allied team member in GAME
  203. // THIS IS NOT DONE YET!
  204. if ((turret->getFlag(OBJECT_FLAG_POP_NEUTRALS) && collidee->isNeutral(collider)) || collidee->isEnemy(collider))
  205. {
  206. //----------------------------------------------
  207. // Not on my side. Shoot her!!!
  208. switch (collider->getObjectClass())
  209. {
  210. case BATTLEMECH:
  211. case GROUNDVEHICLE:
  212. case ELEMENTAL:
  213. {
  214. if (collider->isDisabled() || collider->isDestroyed())
  215. {
  216. //--------------------------------------
  217. // Don't shoot, its dead!!!
  218. }
  219. else
  220. {
  221. Stuff::Vector3D colliderRangeV3;
  222. colliderRangeV3.Subtract(turret->getPosition(), collider->getPosition());
  223. float colliderRange = colliderRangeV3.x * colliderRangeV3.x + colliderRangeV3.y * colliderRangeV3.y; //().magnitude();
  224. if (colliderRange < targetRange)
  225. turret->targetWID = collider->getWatchID();
  226. }
  227. }
  228. break;
  229. case CAMERADRONE:
  230. {
  231. if (collider->isDestroyed())
  232. {
  233. //--------------------------------------
  234. // Don't shoot, its dead!!!
  235. }
  236. else
  237. {
  238. Stuff::Vector3D colliderRangeV3;
  239. colliderRangeV3.Subtract(turret->getPosition(), collider->getPosition());
  240. float colliderRange = colliderRangeV3.x * colliderRangeV3.x + colliderRangeV3.y * colliderRangeV3.y; //().magnitude();
  241. if (colliderRange < targetRange)
  242. turret->targetWID = collider->getWatchID();
  243. }
  244. }
  245. break;
  246. }
  247. }
  248. return(true);
  249. }
  250. //---------------------------------------------------------------------------
  251. bool TurretType::handleDestruction (GameObjectPtr collidee, GameObjectPtr collider) {
  252. return(false);
  253. }
  254. //---------------------------------------------------------------------------
  255. // class Turret
  256. //---------------------------------------------------------------------------
  257. void Turret::init (bool create) {
  258. setFlag(OBJECT_FLAG_JUSTCREATED, true);
  259. setFlag(OBJECT_FLAG_CAN_FIRE, true);
  260. setFlag(OBJECT_FLAG_ONFIRE, false);
  261. setFlag(OBJECT_FLAG_CHECKED_ONSCREEN, false);
  262. appearance = NULL;
  263. vertexNumber = 0;
  264. blockNumber = 0;
  265. idleWait = RandomNumber(10);
  266. didReveal = 0;
  267. turretRotation = 0.0;
  268. targetWID = 0;
  269. pointLight = NULL;
  270. maxRange = 0.0f;
  271. netRosterIndex = -1;
  272. numWeaponFireChunks[0] = 0;
  273. numWeaponFireChunks[1] = 0;
  274. parentId = 0xffffffff;
  275. parent = 0;
  276. minRange = 0.0;
  277. maxRange = 0.0;
  278. numFunctionalWeapons = 0;
  279. }
  280. //---------------------------------------------------------------------------
  281. long Turret::setTeamId (long _teamId, bool setup) {
  282. if (MPlayer)
  283. {
  284. teamId = _teamId;
  285. }
  286. else
  287. {
  288. if (_teamId == 2) //Allies
  289. teamId = 0; //Same as PlayerTeam.
  290. else if (_teamId > 2)
  291. teamId = 1; //Not ally. ENEMY!!
  292. else
  293. teamId = _teamId; //Otherwise we were set to either -1, 0 or 1.
  294. }
  295. targetWID = 0;
  296. static unsigned long highLight[8] = {0x00007f00, 0x007f0000,
  297. 0x0000007f, 0x0000007f,
  298. 0x0000007f, 0x0000007f,
  299. 0x0000007f, 0x0000007f};
  300. if ((turn > 10) && (teamId > -1) && (teamId < 8))
  301. appearance->flashBuilding(5.0,0.5,highLight[teamId]);
  302. return(NO_ERR);
  303. }
  304. //---------------------------------------------------------------------------
  305. TeamPtr Turret::getTeam (void) {
  306. if (teamId == -1)
  307. return(NULL);
  308. return(Team::teams[teamId]);
  309. }
  310. //---------------------------------------------------------------------------
  311. bool Turret::isFriendly (TeamPtr team) {
  312. if (teamId > -1)
  313. return(Team::relations[teamId][team->getId()] == RELATION_FRIENDLY);
  314. return(false);
  315. }
  316. //---------------------------------------------------------------------------
  317. bool Turret::isEnemy (TeamPtr team) {
  318. if (teamId > -1)
  319. return(Team::relations[teamId][team->getId()] == RELATION_ENEMY);
  320. return(false);
  321. }
  322. //---------------------------------------------------------------------------
  323. bool Turret::isNeutral (TeamPtr team) {
  324. if (teamId > -1)
  325. return(Team::relations[teamId][team->getId()] == RELATION_NEUTRAL);
  326. return(true);
  327. }
  328. //---------------------------------------------------------------------------
  329. void Turret::setDamage (float newDamage)
  330. {
  331. damage = newDamage;
  332. TurretTypePtr type = (TurretTypePtr)getObjectType();
  333. if (damage >= type->getDamageLevel())
  334. {
  335. setStatus(OBJECT_STATUS_DESTROYED);
  336. appearance->setObjStatus(OBJECT_STATUS_DESTROYED);
  337. }
  338. }
  339. //---------------------------------------------------------------------------
  340. long Turret::updateAnimations (void)
  341. {
  342. long canFire = 0;
  343. //---------------------------------------------
  344. // Animate the turret popping up!
  345. long animState = appearance->getCurrentGestureId();
  346. if (getAwake() && targetWID)
  347. {
  348. switch (animState)
  349. {
  350. case -1: //NOT UPDATED YET. SWITCH TO ZERO
  351. if (!appearance->getInTransition())
  352. {
  353. appearance->setGesture(0);
  354. }
  355. break;
  356. case 2: //Just Animating. Do NOTHING!
  357. canFire = 1;
  358. break;
  359. case 0: //Not triggered yet. Switch to 1
  360. appearance->setGesture(1);
  361. break;
  362. case 1: //triggered, when fully open switch to 2
  363. if (!appearance->getInTransition())
  364. {
  365. appearance->setGesture(2);
  366. canFire = 1;
  367. }
  368. break;
  369. case 3: //Closing. Wait until closed and then switch to 1.
  370. if (!appearance->getInTransition())
  371. {
  372. appearance->setGesture(1);
  373. }
  374. break;
  375. }
  376. }
  377. else if (!getAwake() || !targetWID)
  378. {
  379. switch (animState)
  380. {
  381. case -1: //NOT UPDATED YET. SWITCH TO ZERO
  382. if (!appearance->getInTransition())
  383. {
  384. appearance->setGesture(0);
  385. }
  386. break;
  387. case 2: //Just Animating. Wait until one loop done, then trigger closing
  388. appearance->setGesture(3);
  389. break;
  390. case 0: //Not triggered yet. DO NOTHING!
  391. break;
  392. case 1: //triggered, when fully open switch to 3 to close it.
  393. if (!appearance->getInTransition())
  394. {
  395. appearance->setGesture(3);
  396. }
  397. break;
  398. case 3: //Closing to closed. When Closed, DO NOTHING
  399. if (!appearance->getInTransition())
  400. {
  401. appearance->setGesture(0);
  402. }
  403. break;
  404. }
  405. }
  406. return canFire;
  407. }
  408. //---------------------------------------------------------------------------
  409. long Turret::update (void)
  410. {
  411. if (getFlag(OBJECT_FLAG_JUSTCREATED))
  412. {
  413. setFlag(OBJECT_FLAG_JUSTCREATED, false);
  414. //Since we are idling, pick a new random position to point to.
  415. if (RollDice(50))
  416. idlePosition.x = position.x + RandomNumber(500);
  417. else
  418. idlePosition.x = position.x - RandomNumber(500);
  419. if (RollDice(50))
  420. idlePosition.y = position.y + RandomNumber(500);
  421. else
  422. idlePosition.y = position.y - RandomNumber(500);
  423. idlePosition.z = position.z;
  424. oldPosition = idlePosition;
  425. //-----------------------------------------------------
  426. // Check if ParentId is NOT 0xffffffff.
  427. // if not, find parent in ObjMgr and get its pointer.
  428. if ((parentId != 0xffffffff) && (parentId != 0))
  429. {
  430. parent = ObjectManager->findByCellPosition((parentId>>16),(parentId & 0x0000ffff))->getWatchID();
  431. ObjectManager->getByWatchID(parent)->setFlag(OBJECT_FLAG_CAPTURABLE, true);
  432. gosASSERT(parent != 0);
  433. }
  434. threatRating = (short)((TurretType*)getObjectType())->punch;
  435. calcFireRanges();
  436. }
  437. //--------------------------------------
  438. // Turret is Dead. React Appropriately
  439. // Just do not call any of the fire routines
  440. if (getFlag(OBJECT_FLAG_DESTROYED))
  441. {
  442. setFlag(OBJECT_FLAG_TANGIBLE, false);
  443. if (pointLight)
  444. {
  445. eye->removeWorldLight(lightId,pointLight);
  446. free(pointLight);
  447. pointLight = NULL;
  448. }
  449. appearance->setObjectParameters(position, rotation, drawFlags, teamId,Team::getRelation(teamId, Team::home->getId()));
  450. appearance->setMoverParameters(turretRotation,0.0f,0.0f);
  451. bool inView = appearance->recalcBounds();
  452. if (inView || !didReveal || (turn < 4))
  453. {
  454. appearance->setInView(true);
  455. appearance->update();
  456. windowsVisible = turn;
  457. didReveal = true;
  458. }
  459. return(true); //NEVER RETURN ANYTHING BUT TRUE!!!!!!!!!!!!!!!!
  460. }
  461. if (!turretsEnabled[getTeamId()]) {
  462. targetWID = 0;
  463. }
  464. //--------------------------------------
  465. // In case the target has been purged...
  466. if (targetWID && !ObjectManager->getByWatchID(targetWID))
  467. targetWID = 0;
  468. float ourExtentRadius = getExtentRadius();
  469. if (targetWID && (!MPlayer || MPlayer->isServer()))
  470. {
  471. GameObjectPtr attackTarget = ObjectManager->getByWatchID(targetWID);
  472. if (attackTarget)
  473. {
  474. Stuff::Vector3D targetRangeV3;
  475. targetRangeV3.Subtract(attackTarget->getPosition(), position);
  476. float targetRange = targetRangeV3.x * targetRangeV3.x + targetRangeV3.y * targetRangeV3.y; //().magnitude();
  477. if (attackTarget->isDisabled() ||
  478. attackTarget->isDestroyed() ||
  479. GameObject::isFriendly(attackTarget) ||
  480. (targetRange > (ourExtentRadius * ourExtentRadius)))
  481. targetWID = 0;
  482. }
  483. else
  484. targetWID = 0;
  485. }
  486. bool active = getAwake();
  487. //-----------------------------------------------------------------------------------
  488. // Old linkage code set awake in ABL. MUST still support this and new linkage code!
  489. if (active && parent &&
  490. (ObjectManager->getByWatchID(parent)->isDestroyed() ||
  491. ObjectManager->getByWatchID(parent)->isDisabled() ||
  492. !ObjectManager->getByWatchID(parent)->getAwake()))
  493. {
  494. ObjectType* parentClass = ObjectManager->getByWatchID(parent)->getObjectType();
  495. if ( active ) // play this sound regardless of wether its a turret control or generator
  496. {
  497. soundSystem->playBettySample( BETTY_GENERATOR_DESTROYED );
  498. }
  499. active = false;
  500. setAwake(false);
  501. appearance->startActivity(TURRET_POWER_DOWN_EFFECT,false);
  502. }
  503. //-----------------------------------------------------------------
  504. // Must also check if parent alive and on same side now internally
  505. if (active && parent &&
  506. !ObjectManager->getByWatchID(parent)->isDestroyed() &&
  507. !ObjectManager->getByWatchID(parent)->isDisabled() &&
  508. (ObjectManager->getByWatchID(parent)->getTeamId() != getTeamId()))
  509. {
  510. // if building recaptured play a sound
  511. if ((ObjectManager->getByWatchID(parent)->getTeamId() != Team::home->getId()) && (turn > 5) && (getTeamId() != -1))
  512. soundSystem->playBettySample(BETTY_BUILDING_RECAPTURED);
  513. setTeamId(ObjectManager->getByWatchID(parent)->getTeam()->getId(),false);
  514. }
  515. if ( parent &&
  516. (!ObjectManager->getByWatchID(parent)->isDisabled()) &&
  517. ObjectManager->getByWatchID(parent)->getTargeted() )
  518. {
  519. setTargeted( true );
  520. }
  521. //Must reveal every frame now for REAL LOS!!
  522. if ((turn > 1) && active && getTeam())
  523. {
  524. if (turretsEnabled[getTeamId()])
  525. {
  526. TurretTypePtr turretType = (TurretTypePtr)ObjectManager->getObjectType(typeHandle);
  527. getTeam()->markSeen(position,turretType->LOSFactor);
  528. }
  529. }
  530. float turretFacing = 0.0f;
  531. float turretTurnRate = 0.0;
  532. if (active && targetWID)
  533. {
  534. GameObjectPtr target = ObjectManager->getByWatchID(targetWID);
  535. //----------------------------------
  536. // Rotate Turret to point at target
  537. if (target)
  538. turretFacing = relFacingTo(target->getPosition());
  539. //--------------------------------------------------------------------
  540. // Complicated, but let me 'splain. No there is too much legacy code.
  541. // Let me sum up. We need to know what direction to rotate the turret.
  542. // That direction is the shortest distance between where we are and
  543. // where we are going. For the record, WE HAVE NEVER DONE THIS CORRECTLY!
  544. if ((turretFacing < -10.0) || (turretFacing > 10.0)) {
  545. //-----------------------------------------------
  546. // We can and will shift facing to destination...
  547. float maxRate = ((TurretTypePtr)getObjectType())->turretYawRate;
  548. if (turretFacing < 0.0)
  549. {
  550. turretTurnRate = maxRate*frameLength;
  551. if (turretTurnRate > fabs(turretFacing))
  552. turretTurnRate = fabs(turretFacing);
  553. }
  554. else
  555. {
  556. turretTurnRate = -maxRate*frameLength;
  557. if (turretTurnRate < -turretFacing)
  558. turretTurnRate = -turretFacing;
  559. }
  560. setFlag(OBJECT_FLAG_FACING_TARGET,false);
  561. }
  562. else
  563. {
  564. setFlag(OBJECT_FLAG_FACING_TARGET,true);
  565. }
  566. turretRotation += turretTurnRate;
  567. }
  568. else if (active && !targetWID)
  569. {
  570. //---------------------------------------------------------------
  571. // Create an Idle animation here for the turrets and Spotlights.
  572. // Just slowly rotate turret for now.
  573. turretFacing = relFacingTo(idlePosition);
  574. if ((turretFacing < -10.0) || (turretFacing > 10.0)) {
  575. //-----------------------------------------------
  576. // We can and will shift facing to destination...
  577. float maxRate = ((TurretTypePtr)getObjectType())->turretYawRate;
  578. if (turretFacing < 0.0)
  579. {
  580. turretTurnRate = maxRate*frameLength;
  581. if (turretTurnRate > fabs(turretFacing))
  582. turretTurnRate = fabs(turretFacing);
  583. }
  584. else
  585. {
  586. turretTurnRate = -maxRate*frameLength;
  587. if (turretTurnRate < -turretFacing)
  588. turretTurnRate = -turretFacing;
  589. }
  590. setFlag(OBJECT_FLAG_FACING_TARGET,false);
  591. }
  592. else
  593. {
  594. setFlag(OBJECT_FLAG_FACING_TARGET,true);
  595. idleWait += frameLength;
  596. if (idleWait > 10.0f)
  597. {
  598. //Since we are idling, pick a new random position to point to.
  599. if (RollDice(50))
  600. idlePosition.x = position.x + RandomNumber(ourExtentRadius);
  601. else
  602. idlePosition.x = position.x - RandomNumber(ourExtentRadius);
  603. if (RollDice(50))
  604. idlePosition.y = position.y + RandomNumber(ourExtentRadius);
  605. else
  606. idlePosition.y = position.y - RandomNumber(ourExtentRadius);
  607. idlePosition.z = position.z;
  608. idleWait = 0.0f;
  609. }
  610. }
  611. turretRotation += turretTurnRate;
  612. }
  613. else if (!active && !getFlag(OBJECT_FLAG_VEHICLE_APPR))
  614. {
  615. }
  616. if (turretRotation > 360.0f)
  617. turretRotation -= 360.0f;
  618. if (turretRotation < -360.0f)
  619. turretRotation += 360.0f;
  620. Stuff::Vector3D distance;
  621. distance.Subtract(oldPosition,position);
  622. float spotDistance = distance.GetApproximateLength();
  623. float turretPitch = 0.0f;
  624. if (((TurretTypePtr)getObjectType())->weaponMasterId[0] == -1)
  625. {
  626. turretPitch = (1.0f - (spotDistance / (ourExtentRadius))) * MAX_TURRET_PITCH;
  627. if (turretPitch > 0.0f)
  628. turretPitch = 0.0f;
  629. }
  630. //--------------------------------------------------------------------------
  631. // Make the turrets sad if their parent was destroyed, disabled or asleep.
  632. if (!active)
  633. {
  634. turretPitch = -35.0f;
  635. }
  636. //-------------------------------------------
  637. // Handle power out.
  638. // Parent is down OR we are destroyed.
  639. if (!active || (getStatus() == OBJECT_STATUS_DESTROYED))
  640. appearance->setLightsOut(true);
  641. appearance->setObjectParameters(position, rotation, drawFlags, teamId,Team::getRelation(teamId, Team::home->getId()));
  642. appearance->setMoverParameters(turretRotation,turretPitch,0.0f);
  643. bool inView = appearance->recalcBounds();
  644. long canFire = updateAnimations();
  645. if (inView || !didReveal || (turn < 4))
  646. {
  647. appearance->setInView(true);
  648. appearance->update();
  649. windowsVisible = turn;
  650. didReveal = true;
  651. }
  652. //-------------------------------------
  653. // Are we a spotlight?
  654. if (((TurretTypePtr)getObjectType())->weaponMasterId[0] == -1)
  655. {
  656. //---------------------------------
  657. // Yes we are. Check if night.
  658. // If night and no spotlight yet, create one.
  659. // Move it with turret's rotation below.
  660. //
  661. // For E3, have the spotlights create the light immediately or we run out of light sources!
  662. if (active && eye->getIsNight() && (pointLight == NULL) && (getStatus() != OBJECT_STATUS_DESTROYED))
  663. {
  664. pointLight = (TG_LightPtr)malloc(sizeof(TG_Light));
  665. pointLight->init(TG_LIGHT_SPOT);
  666. lightId = eye->addWorldLight(pointLight);
  667. pointLight->SetaRGB(0xffffffee);
  668. pointLight->SetIntensity(0.25f);
  669. pointLight->SetFalloffDistances(50.0f, 450.0f);
  670. }
  671. if ((!active || !eye->getIsNight()) && pointLight)
  672. {
  673. eye->removeWorldLight(lightId,pointLight);
  674. free(pointLight);
  675. pointLight = NULL;
  676. }
  677. }
  678. if (pointLight)
  679. {
  680. //----------------------------------------------
  681. // Must move light if spotlight is MOVING!
  682. // Find Direction of movement subtracting target or idle position from oldPosition.
  683. // find distance from that vector.
  684. // move distance proportional to rotation this frame divided by rotation left to hit target.
  685. // Store newly derived position as oldPosition.
  686. Stuff::Point3D xlatPos;
  687. Stuff::Point3D ourPosition;
  688. if (turretTurnRate == 0.0f)
  689. {
  690. //We are on Target. Light is at same position as thing we are illuminating.
  691. GameObjectPtr target = ObjectManager->getByWatchID(targetWID);
  692. if (target)
  693. oldPosition = target->getPosition();
  694. else if (idleWait != 0.0f)
  695. oldPosition = idlePosition;
  696. oldPosition.z = position.z;
  697. }
  698. else
  699. {
  700. Stuff::Vector3D Dir;
  701. GameObjectPtr target = ObjectManager->getByWatchID(targetWID);
  702. if (target)
  703. {
  704. Dir.Subtract(target->getPosition(),oldPosition);
  705. float dist = Dir.GetLength();
  706. if (dist > Stuff::SMALL)
  707. {
  708. Dir.Normalize(Dir);
  709. dist *= fabs(turretTurnRate) / fabs(turretFacing);
  710. Dir *= dist;
  711. oldPosition += Dir;
  712. }
  713. else
  714. oldPosition = target->getPosition();
  715. }
  716. else
  717. {
  718. Dir.Subtract(idlePosition,oldPosition);
  719. float dist = Dir.GetApproximateLength();
  720. if (dist > Stuff::SMALL)
  721. {
  722. Dir.Normalize(Dir);
  723. dist *= fabs(turretTurnRate) / fabs(turretFacing);
  724. Dir *= dist;
  725. oldPosition += Dir;
  726. }
  727. else
  728. oldPosition = idlePosition;
  729. }
  730. }
  731. ourPosition = oldPosition;
  732. xlatPos.x = -ourPosition.x;
  733. xlatPos.y = ourPosition.z;
  734. xlatPos.z = ourPosition.y;
  735. pointLight->direction = xlatPos;
  736. pointLight->spotDir.x = -position.x;
  737. pointLight->spotDir.y = position.z;
  738. pointLight->spotDir.z = position.y;
  739. pointLight->maxSpotLength = ourExtentRadius * 1.5f;
  740. Stuff::LinearMatrix4D lightToWorldMatrix;
  741. lightToWorldMatrix.BuildTranslation(xlatPos);
  742. lightToWorldMatrix.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  743. pointLight->SetLightToWorld(&lightToWorldMatrix);
  744. pointLight->SetPosition(&ourPosition);
  745. }
  746. setFlag(OBJECT_FLAG_CAN_FIRE, true);
  747. for (long i=0;i<MAX_TURRET_WEAPONS;i++)
  748. {
  749. if (!getFlag(OBJECT_FLAG_DESTROYED) && targetWID && isWeaponReady(i) && active && canFire)
  750. {
  751. GameObjectPtr target = ObjectManager->getByWatchID(targetWID);
  752. if (target && (!MPlayer || MPlayer->isServer()))
  753. fireWeapon(target,i);
  754. }
  755. }
  756. if (numWeaponFireChunks[CHUNK_RECEIVE] > 0)
  757. updateWeaponFireChunks(CHUNK_RECEIVE);
  758. return(1);
  759. }
  760. //---------------------------------------------------------------------------
  761. float Turret::relFacingTo (Stuff::Vector3D goal, long bodyLocation)
  762. {
  763. Stuff::Vector3D facingVec;
  764. facingVec.x = 0.0f;
  765. facingVec.y = -1.0f;
  766. facingVec.z = 0.0f;
  767. Rotate(facingVec, -turretRotation);
  768. Stuff::Vector3D goalVec;
  769. goalVec.Subtract(goal, position);
  770. float angle = angle_from(facingVec, goalVec);
  771. //--------------------------------
  772. // Get sign of relative angle.
  773. float z = (facingVec.x * goalVec.y) - (facingVec.y * goalVec.x);
  774. if (z > 0.0f)
  775. angle = -angle;
  776. return(angle);
  777. }
  778. //---------------------------------------------------------------------------
  779. bool Turret::isWeaponReady (long weaponId)
  780. {
  781. if (((TurretTypePtr)getObjectType())->weaponMasterId[weaponId] == -1)
  782. return false;
  783. if ((readyTime[weaponId] > scenarioTime) || !getFlag(OBJECT_FLAG_CAN_FIRE) || !getFlag(OBJECT_FLAG_FACING_TARGET))
  784. return(false);
  785. return(true);
  786. }
  787. //---------------------------------------------------------------------------
  788. bool Turret::isWeaponMissile (long weaponId)
  789. {
  790. if (((TurretTypePtr)getObjectType())->weaponMasterId[weaponId] == -1)
  791. return false;
  792. return(MasterComponent::masterList[((TurretTypePtr)getObjectType())->weaponMasterId[weaponId]].getForm() == COMPONENT_FORM_WEAPON_MISSILE);
  793. }
  794. //---------------------------------------------------------------------------
  795. bool Turret::isWeaponStreak (long weaponId)
  796. {
  797. if (((TurretTypePtr)getObjectType())->weaponMasterId[weaponId] == -1)
  798. return false;
  799. return(MasterComponent::masterList[((TurretTypePtr)getObjectType())->weaponMasterId[weaponId]].getWeaponStreak());
  800. }
  801. //---------------------------------------------------------------------------
  802. float Turret::calcAttackChance (GameObjectPtr target, long* range, long weaponId)
  803. {
  804. if (((TurretTypePtr)getObjectType())->weaponMasterId[weaponId] == -1)
  805. return 0.0f;
  806. //-------------------------------------------------------------
  807. // First, let's find out what kind of object we're targeting...
  808. Stuff::Vector3D targetPosition(0.0f,0.0f,0.0f);
  809. BattleMechPtr mech = NULL;
  810. GroundVehiclePtr vehicle = NULL;
  811. if (target)
  812. {
  813. long targetObjectClass = target->getObjectClass();
  814. switch (targetObjectClass)
  815. {
  816. case BATTLEMECH:
  817. mech = (BattleMechPtr)target;
  818. break;
  819. case GROUNDVEHICLE:
  820. vehicle = (GroundVehiclePtr)target;
  821. break;
  822. }
  823. targetPosition = target->getPosition();
  824. }
  825. float attackChance = ((TurretTypePtr)getObjectType())->pilotSkill;
  826. //----------------------
  827. // General fire range...
  828. float distanceToTarget = distanceFrom(targetPosition);
  829. if (range)
  830. {
  831. if (distanceToTarget <= WeaponRange[FIRERANGE_SHORT])
  832. *range = FIRERANGE_SHORT;
  833. else if (distanceToTarget <= WeaponRange[FIRERANGE_MEDIUM])
  834. *range = FIRERANGE_MEDIUM;
  835. else
  836. *range = FIRERANGE_LONG;
  837. }
  838. //----------------------
  839. // Range (Per Weapon)...
  840. long weaponMasterId = ((TurretTypePtr)getObjectType())->weaponMasterId[weaponId];
  841. float weaponMinRange = WeaponRanges[MasterComponent::masterList[weaponMasterId].getWeaponRange()][0];
  842. float weaponMaxRange = WeaponRanges[MasterComponent::masterList[weaponMasterId].getWeaponRange()][1];
  843. if (distanceToTarget <= weaponMinRange)
  844. return(-1.0);
  845. else if (distanceToTarget > weaponMaxRange)
  846. return(-1.0);
  847. //-------------------
  848. // Target movement...
  849. if (target)
  850. {
  851. Stuff::Vector3D targetVelocity;
  852. targetVelocity = target->getVelocity();
  853. float targetSpeed = targetVelocity.x * targetVelocity.x + targetVelocity.y * targetVelocity.y;
  854. if (targetSpeed == 0.0)
  855. attackChance += 50.0;
  856. }
  857. //Mech specialist modifiers
  858. if (mech && mech->getPilot())
  859. {
  860. if ((mech->tonnage < 40) && mech->getPilot()->isLightMechSpecialist())
  861. attackChance -= 30.0f;
  862. else if ((mech->tonnage >= 40) && (mech->tonnage < 60) && mech->getPilot()->isMediumMechSpecialist())
  863. attackChance -= 20.0f;
  864. else if ((mech->tonnage >= 60) && (mech->tonnage < 80) && mech->getPilot()->isHevayMechSpecialist())
  865. attackChance -= 10.0f;
  866. else if ((mech->tonnage >= 80) && (mech->tonnage < 100) && mech->getPilot()->isAssaultMechSpecialist())
  867. attackChance -= 10.0f;
  868. if (attackChance < 5.0f)
  869. attackChance = 5.0f;
  870. }
  871. return(attackChance);
  872. }
  873. //------------------------------------------------------------------------------------------
  874. void Turret::recordWeaponFireTime (long weaponId) {
  875. lastFireTime[weaponId] = scenarioTime;
  876. }
  877. //------------------------------------------------------------------------------------------
  878. void Turret::startWeaponRecycle (long weaponId)
  879. {
  880. readyTime[weaponId] = scenarioTime + MasterComponent::masterList[((TurretTypePtr)getObjectType())->weaponMasterId[weaponId]].getWeaponRecycleTime();
  881. }
  882. //---------------------------------------------------------------------------
  883. long Turret::clearWeaponFireChunks (long which) {
  884. long numChunks = numWeaponFireChunks[which];
  885. numWeaponFireChunks[which] = 0;
  886. return(numChunks);
  887. }
  888. //---------------------------------------------------------------------------
  889. long Turret::addWeaponFireChunk (long which, WeaponFireChunkPtr chunk) {
  890. if (numWeaponFireChunks[which] == MAX_WEAPONFIRE_CHUNKS)
  891. Fatal(0, " Turret::addWeaponFireChunk--Too many weaponfire chunks ");
  892. chunk->pack(this);
  893. weaponFireChunks[which][numWeaponFireChunks[which]++] = chunk->data;
  894. return(numWeaponFireChunks[which]);
  895. }
  896. //---------------------------------------------------------------------------
  897. long Turret::addWeaponFireChunks (long which, unsigned long* packedChunkBuffer, long numChunks) {
  898. if ((numWeaponFireChunks[which] + numChunks) >= MAX_WEAPONFIRE_CHUNKS)
  899. Fatal(0, " Turret::addWeaponFireChunks--Too many weaponfire chunks ");
  900. #if 0
  901. memcpy(&weaponFireChunks[which][numWeaponFireChunks[which]], packedChunkBuffer, 4 * numChunks);
  902. numWeaponFireChunks[which] += numChunks;
  903. #else
  904. for (long i = 0; i < numChunks; i++) {
  905. weaponFireChunks[which][numWeaponFireChunks[which]++] = packedChunkBuffer[i];
  906. //---------------
  907. // FOR TESTING...
  908. WeaponFireChunk chunk;
  909. chunk.init();
  910. chunk.data = packedChunkBuffer[i];
  911. chunk.unpack(this);
  912. }
  913. #endif
  914. return(numWeaponFireChunks[which]);
  915. }
  916. //---------------------------------------------------------------------------
  917. long Turret::grabWeaponFireChunks (long which, unsigned long* packedChunkBuffer) {
  918. if (numWeaponFireChunks[which] > 0)
  919. for (long i = 0; i < numWeaponFireChunks[which]; i++)
  920. packedChunkBuffer[i] = weaponFireChunks[which][i];
  921. //memcpy(packedChunkBuffer, weaponFireChunks[which], 4 * numWeaponFireChunks[which]);
  922. return(numWeaponFireChunks[which]);
  923. }
  924. //---------------------------------------------------------------------------
  925. long Turret::updateWeaponFireChunks (long which)
  926. {
  927. for (long i = 0; i < numWeaponFireChunks[which]; i++)
  928. {
  929. WeaponFireChunk chunk;
  930. chunk.init();
  931. chunk.data = weaponFireChunks[which][i];
  932. chunk.unpack(this);
  933. static float entryQuadTable[4] = {0.0, 180.0, -90.0, 90.0};
  934. if (chunk.targetType == 0)
  935. {
  936. GameObjectPtr target = (GameObjectPtr)MPlayer->moverRoster[chunk.targetId];
  937. //----------------------------------------------------------------------------
  938. // Mover targets could be NULL now, since we free them when they're destroyed.
  939. if (target)
  940. handleWeaponFire(chunk.weaponIndex,
  941. target,
  942. NULL,
  943. chunk.hit,
  944. entryQuadTable[chunk.entryAngle],
  945. chunk.numMissiles,
  946. chunk.hitLocation);
  947. }
  948. else if (chunk.targetType == 1)
  949. {
  950. GameObjectPtr target = ObjectManager->findByPartId(chunk.targetId);
  951. if (target == NULL)
  952. {
  953. DebugWeaponFireChunk (&chunk, NULL, this);
  954. Assert(FALSE, 0, " Turret.updateWeaponFireChunks: NULL Terrain Target (save wfchunk.dbg file) ");
  955. }
  956. handleWeaponFire(chunk.weaponIndex,
  957. target,
  958. NULL,
  959. chunk.hit,
  960. entryQuadTable[chunk.entryAngle],
  961. chunk.numMissiles,
  962. chunk.hitLocation);
  963. }
  964. else if (chunk.targetType == 2)
  965. {
  966. GameObjectPtr target = ObjectManager->findByPartId(chunk.targetId);
  967. if (target == NULL)
  968. {
  969. DebugWeaponFireChunk (&chunk, NULL, this);
  970. Assert(FALSE, 0, " Turret.updateWeaponFireChunks: NULL Special Target (save wfchunk.dbg file) ");
  971. }
  972. handleWeaponFire(chunk.weaponIndex,
  973. target,
  974. NULL,
  975. chunk.hit,
  976. entryQuadTable[chunk.entryAngle],
  977. chunk.numMissiles,
  978. chunk.hitLocation);
  979. }
  980. else if (chunk.targetType == 3)
  981. {
  982. Stuff::Vector3D targetPoint;
  983. targetPoint.x = (float)chunk.targetCell[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2;
  984. targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk.targetCell[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2;
  985. targetPoint.z = (float)0;
  986. handleWeaponFire(chunk.weaponIndex, NULL, &targetPoint, chunk.hit, 0.0, 0, 0);
  987. }
  988. else
  989. Fatal(0, " Mover.updateWeaponFireChunk: bad targetType ");
  990. }
  991. numWeaponFireChunks[which] = 0;
  992. return(NO_ERR);
  993. }
  994. //---------------------------------------------------------------------------
  995. Stuff::Vector3D Turret::getPositionFromHS (long nodeId)
  996. {
  997. //-----------------------------------------------------
  998. // Need to get this working with SITE code when done!
  999. Stuff::Vector3D nodePos = appearance->getHitNode();
  1000. if ((nodePos == position) || (nodeId != -1))
  1001. nodePos = appearance->getWeaponNodePosition(0);
  1002. return(nodePos);
  1003. }
  1004. //---------------------------------------------------------------------------
  1005. Stuff::Vector3D Turret::getLOSPosition (void)
  1006. {
  1007. Stuff::Vector3D nodePos = appearance->getWeaponNodePosition(0);
  1008. Stuff::Vector3D losPos = position;
  1009. losPos.z = nodePos.z;
  1010. return(losPos);
  1011. }
  1012. //---------------------------------------------------------------------------
  1013. void Turret::printFireWeaponDebugInfo (GameObjectPtr target, Stuff::Vector3D* targetPoint, long chance, long roll, WeaponShotInfo* shotInfo) {
  1014. if (!CombatLog)
  1015. return;
  1016. static char* locationStrings [] = {
  1017. "head",
  1018. "center torso",
  1019. "left torso",
  1020. "right torso",
  1021. "left arm",
  1022. "right arm",
  1023. "left leg",
  1024. "right leg",
  1025. "rear center torso",
  1026. "rear left torso",
  1027. "rear right torso"
  1028. };
  1029. if (roll < chance) {
  1030. if (target) {
  1031. char* targetName = target->getName();
  1032. char s[1024];
  1033. sprintf(s, "[%.2f] turret.fireWeapon HIT: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), getName(), target->getPartId(), targetName ? targetName : "unknown");
  1034. CombatLog->write(s);
  1035. sprintf(s, " chance = %03d, roll = %03d", chance, roll);
  1036. CombatLog->write(s);
  1037. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  1038. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  1039. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  1040. shotInfo->damage,
  1041. shotInfo->entryAngle);
  1042. CombatLog->write(s);
  1043. CombatLog->write(" ");
  1044. }
  1045. else if (targetPoint) {
  1046. }
  1047. }
  1048. else {
  1049. if (target) {
  1050. char* targetName = target->getName();
  1051. char s[1024];
  1052. sprintf(s, "[%.2f] turret.fireWeapon MISS: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), getName(), target->getPartId(), targetName ? targetName : "unknown");
  1053. CombatLog->write(s);
  1054. sprintf(s, " chance = %03d, roll = %03d", chance, roll);
  1055. CombatLog->write(s);
  1056. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  1057. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  1058. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  1059. shotInfo->damage,
  1060. shotInfo->entryAngle);
  1061. CombatLog->write(s);
  1062. CombatLog->write(" ");
  1063. }
  1064. else if (targetPoint) {
  1065. }
  1066. }
  1067. }
  1068. //----------------------------------------------------------------------------
  1069. void Turret::printHandleWeaponHitDebugInfo (WeaponShotInfo* shotInfo) {
  1070. if (!CombatLog)
  1071. return;
  1072. static char* locationStrings [] = {
  1073. "head",
  1074. "center torso",
  1075. "left torso",
  1076. "right torso",
  1077. "left arm",
  1078. "right arm",
  1079. "left leg",
  1080. "right leg",
  1081. "rear center torso",
  1082. "rear left torso",
  1083. "rear right torso"
  1084. };
  1085. char s[1024];
  1086. char statusStr[50];
  1087. if (isDestroyed())
  1088. sprintf(statusStr, " DESTROYED:");
  1089. //else if (Team::noPain[getTeamId()])
  1090. // sprintf(statusStr, " NO PAIN:");
  1091. else
  1092. sprintf(statusStr, ":");
  1093. sprintf(s, "[%.2f] turret.handleWeaponHit%s (%05d)%s ", scenarioTime, statusStr, getPartId(), getName());
  1094. CombatLog->write(s);
  1095. GameObjectPtr attacker = ObjectManager->getByWatchID(shotInfo->attackerWID);
  1096. if (attacker)
  1097. sprintf(s, " attacker = (%05d)%s", attacker->getPartId(), attacker->getName());
  1098. else
  1099. sprintf(s, " attacker = (%05)<unknown WID>", shotInfo->attackerWID);
  1100. CombatLog->write(s);
  1101. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  1102. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  1103. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  1104. shotInfo->damage,
  1105. shotInfo->entryAngle);
  1106. CombatLog->write(s);
  1107. CombatLog->write(" ");
  1108. }
  1109. //----------------------------------------------------------------------------
  1110. void Turret::fireWeapon (GameObjectPtr target, long weaponId) {
  1111. if (!target)
  1112. return;
  1113. long weaponMasterId = ((TurretTypePtr)getObjectType())->weaponMasterId[weaponId];
  1114. //--------------------------------------------
  1115. // Check and make sure we have line of fire...
  1116. bool canSeeTarget = false;
  1117. currentWeaponNode = appearance->getWeaponNode(0);
  1118. if (currentWeaponNode != -1)
  1119. {
  1120. Stuff::Vector3D pos = appearance->getWeaponNodePosition(currentWeaponNode);
  1121. appearance->setWeaponNodeUsed(currentWeaponNode);
  1122. //Check if this is an indirect Fire weapon.
  1123. bool indirectFireWeapon = false;
  1124. //--------------------------------------------------------------------
  1125. for (long i=0;i<20;i++)
  1126. {
  1127. if (Mover::IndirectFireWeapons[i] == ((TurretTypePtr)getObjectType())->weaponMasterId[weaponId])
  1128. {
  1129. indirectFireWeapon = true;
  1130. break;
  1131. }
  1132. }
  1133. bool LOS = false;
  1134. LOS = lineOfSight(target,getAppearRadius());
  1135. if (!LOS && indirectFireWeapon)
  1136. LOS = getTeam()->teamLineOfSight(target->getPosition(),target->getAppearRadius());
  1137. canSeeTarget = LOS;
  1138. }
  1139. if (target && !canSeeTarget)
  1140. return;
  1141. float entryAngle = 0.0;
  1142. long aimLocation = -1;
  1143. if (target)
  1144. entryAngle = target->relFacingTo(position);
  1145. bool isStreakMissile = MasterComponent::masterList[weaponMasterId].getWeaponStreak();
  1146. long range;
  1147. long attackChance = (float)calcAttackChance(target,&range,weaponId);
  1148. if (attackChance == -1.0)
  1149. return;
  1150. long hitRoll = RandomNumber(100);
  1151. long hitLocation = -2;
  1152. MechWarriorPtr targetPilot = NULL;
  1153. if (target && target->isMover()) {
  1154. targetPilot = ((MoverPtr)target)->getPilot();
  1155. targetPilot->updateAttackerStatus(partId, scenarioTime);
  1156. }
  1157. //-----------------------
  1158. // Weapon must recycle...
  1159. startWeaponRecycle(weaponId);
  1160. if (hitRoll < attackChance) {
  1161. //------------------------------------------------
  1162. // Weapon fires, so record current scenarioTime...
  1163. recordWeaponFireTime(weaponId);
  1164. //------------
  1165. // Attack hit.
  1166. if (MasterComponent::masterList[weaponMasterId].getForm() == COMPONENT_FORM_WEAPON_MISSILE) {
  1167. //---------------------------------------------------------
  1168. // It's a missile weapon. We need to determine how many hit
  1169. // (and missed) the target, and in how many clusters...
  1170. long missileAmount = MasterComponent::masterList[weaponMasterId].getWeaponAmmoAmount();
  1171. long numMissiles;
  1172. if (isStreakMissile)
  1173. numMissiles = missileAmount;
  1174. else {
  1175. numMissiles = ((float)missileAmount / 2.0) + 0.5;
  1176. if (numMissiles < 1)
  1177. numMissiles = 1;
  1178. if (numMissiles > missileAmount)
  1179. numMissiles = missileAmount;
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Is the missile short-range(SRM) or long-range(LRM)? SRMs are handled with
  1183. // each missile spawning a separate damage calc. LRMs are clustered, 5 missiles
  1184. // to a cluster (with the remainder a final, separate cluster), where each
  1185. // cluster spawns a separate damage calc...
  1186. // NO MORE CLUSTERS!
  1187. //-----------------------------------------------
  1188. // a MissileGen Object is ALL of the clusters.
  1189. // Don't make a thousand of them or the game implodes!
  1190. //numClusters = 1;
  1191. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1192. //----------------------------------------------------
  1193. // Need to know which hotspot this comes from.
  1194. // Also need to know which hotspot this is going to.
  1195. unsigned long sourceHotSpot = currentWeaponNode;
  1196. unsigned long targetHotSpot = 0;
  1197. WeaponShotInfo curShotInfo;
  1198. if (numMissiles > 0) {
  1199. //-------------------------------------------------------------------
  1200. // This code will mess up if the object is not a BULLET!!!!!!!!!!!
  1201. WeaponBoltPtr weaponFX = ObjectManager->createWeaponBolt(effectType);
  1202. if (!weaponFX)
  1203. Fatal(-1," couldnt create weapon FX ");
  1204. if (target) {
  1205. if (aimLocation == -1)
  1206. hitLocation = target->calcHitLocation(this, weaponMasterId, ATTACKSOURCE_WEAPONFIRE, ATTACK_TO_DESTROY);
  1207. if (target->getObjectClass() == BATTLEMECH)
  1208. targetHotSpot = ((BattleMechPtr)target)->body[hitLocation].hotSpotNumber;
  1209. }
  1210. else
  1211. hitLocation = -1;
  1212. Assert(hitLocation != -2, 0, " Turret.FireWeapon: Bad Hit Location ");
  1213. //--------------------------------------
  1214. curShotInfo.init(this->getWatchID(),
  1215. weaponMasterId,
  1216. numMissiles * MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1217. hitLocation,
  1218. entryAngle);
  1219. //-------------------------------------------------------------------------
  1220. // If I'm in a multiplayer game and I'm the server, record this turret fire
  1221. // so it may be broadcast to all clients...
  1222. if (MPlayer && MPlayer->isServer()) {
  1223. WeaponFireChunk chunk;
  1224. chunk.init();
  1225. if (target) {
  1226. if (target->isMover())
  1227. chunk.buildMoverTarget(target,
  1228. weaponId,
  1229. true,
  1230. entryAngle,
  1231. numMissiles,
  1232. hitLocation);
  1233. else
  1234. chunk.buildTerrainTarget(target,
  1235. weaponId,
  1236. true,
  1237. numMissiles);
  1238. //GameObjectPtr tempObj = ObjectManager->findByPartId(chunk.targetId);
  1239. chunk.pack(this);
  1240. WeaponFireChunk chunk2;
  1241. chunk2.init();
  1242. chunk2.data = chunk.data;
  1243. chunk2.unpack(this);
  1244. if (!chunk.equalTo(&chunk2))
  1245. Fatal(0, " Turret.fireWeapon: Bad WeaponFireChunk (save wfchunk.dbg file now) ");
  1246. addWeaponFireChunk(CHUNK_SEND, &chunk);
  1247. LogWeaponFireChunk(&chunk, this, target);
  1248. }
  1249. }
  1250. if (target) {
  1251. weaponFX->connect(this, target, &curShotInfo, sourceHotSpot, targetHotSpot);
  1252. printFireWeaponDebugInfo(target, NULL, attackChance, hitRoll, &curShotInfo);
  1253. }
  1254. }
  1255. }
  1256. else
  1257. {
  1258. //----------------------------------------------------
  1259. // Non-missile weapon, so just one weapon hit spawn...
  1260. // For now, always use a laser effect...
  1261. if (target)
  1262. {
  1263. if (aimLocation == -1)
  1264. hitLocation = target->calcHitLocation(this, weaponMasterId, ATTACKSOURCE_WEAPONFIRE, ATTACK_TO_DESTROY);
  1265. }
  1266. else
  1267. hitLocation = -1;
  1268. Assert(hitLocation != -2, 0, " Turret.FireWeapon: Bad Hit Location 2 ");
  1269. WeaponShotInfo shotInfo;
  1270. shotInfo.init(this->getWatchID(),
  1271. weaponMasterId,
  1272. MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1273. hitLocation,
  1274. entryAngle);
  1275. //-------------------------------------------------------------------------
  1276. // If I'm in a multiplayer game and I'm the server, record this turret fire
  1277. // so it may be broadcast to all clients...
  1278. if (MPlayer && MPlayer->isServer()) {
  1279. WeaponFireChunk chunk;
  1280. chunk.init();
  1281. if (target) {
  1282. if (target->isMover())
  1283. chunk.buildMoverTarget(target,
  1284. weaponId,
  1285. true,
  1286. entryAngle,
  1287. 0,
  1288. hitLocation);
  1289. else
  1290. chunk.buildTerrainTarget(target,
  1291. weaponId,
  1292. true,
  1293. 0);
  1294. //GameObjectPtr tempObj = ObjectManager->findByPartId(chunk.targetId);
  1295. chunk.pack(this);
  1296. WeaponFireChunk chunk2;
  1297. chunk2.init();
  1298. chunk2.data = chunk.data;
  1299. chunk2.unpack(this);
  1300. if (!chunk.equalTo(&chunk2))
  1301. Fatal(0, " Turret.fireWeapon: Bad WeaponFireChunk (save wfchunk.dbg file now) ");
  1302. addWeaponFireChunk(CHUNK_SEND, &chunk);
  1303. LogWeaponFireChunk(&chunk, this, target);
  1304. }
  1305. }
  1306. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1307. WeaponBoltPtr weaponFX = ObjectManager->createWeaponBolt(effectType);
  1308. if (!weaponFX)
  1309. Fatal(-1," couldnt create weapon FX ");
  1310. unsigned long sourceHotSpot = currentWeaponNode;
  1311. unsigned long targetHotSpot = 0;
  1312. if (target) {
  1313. if (target->getObjectClass() == BATTLEMECH)
  1314. targetHotSpot = ((BattleMechPtr)target)->body[hitLocation].hotSpotNumber;
  1315. weaponFX->connect(this, target, &shotInfo, sourceHotSpot, targetHotSpot);
  1316. printFireWeaponDebugInfo(target, NULL, attackChance, hitRoll, &shotInfo);
  1317. }
  1318. }
  1319. }
  1320. else
  1321. {
  1322. if (!isStreakMissile)
  1323. {
  1324. //------------------------------------------------
  1325. // Weapon fires, so record current scenarioTime...
  1326. recordWeaponFireTime(weaponId);
  1327. //----------------------------------------------------
  1328. // Miss, so check for possible miss resolution here...
  1329. if (MasterComponent::masterList[weaponMasterId].getForm() == COMPONENT_FORM_WEAPON_MISSILE)
  1330. {
  1331. //---------------------------------------------------------
  1332. // It's a missile weapon. We need to determine how many hit
  1333. // (and missed) the target, and in how many clusters...
  1334. long missileAmount = MasterComponent::masterList[weaponMasterId].getWeaponAmmoAmount();
  1335. long numMissiles = ((float)missileAmount / 2.0) + 0.5;
  1336. if (numMissiles < 1)
  1337. numMissiles = 1;
  1338. if (numMissiles > missileAmount)
  1339. numMissiles = missileAmount;
  1340. //-----------------------------------------------------------------------------
  1341. // Is the missile short-range(SRM) or long-range(LRM)? SRMs are handled with
  1342. // each missile spawning a separate damage calc. LRMs are clustered, 5 missiles
  1343. // to a cluster (with the remainder a final, separate cluster), where each
  1344. // cluster spawns a separate damage calc...
  1345. if (numMissiles > 0)
  1346. {
  1347. //-----------------------------------------------
  1348. // a MissileGen Object is ALL of the clusters.
  1349. // Don't make a thousand of them or the game implodes!
  1350. //numClusters = 1;
  1351. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1352. //-------------------------------------------------------------------
  1353. // This code will mess up if the object is not a BULLET!!!!!!!!!!!
  1354. WeaponBoltPtr weaponFX = ObjectManager->createWeaponBolt(effectType);
  1355. if (!weaponFX)
  1356. Fatal(-1," couldnt create weapon FX ");
  1357. unsigned long sourceHotSpot = currentWeaponNode;
  1358. WeaponShotInfo curShotInfo;
  1359. curShotInfo.init(this->getWatchID(),
  1360. weaponMasterId,
  1361. numMissiles * MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1362. -1,
  1363. entryAngle);
  1364. //-------------------------------------------------------------------------------------------
  1365. // If we missed, pick sights away from the target and check LOS to each one. If all are
  1366. // invisible, just hit the target with zero points of damage.
  1367. float missRadius = target ? 25.0 : 5.0;
  1368. Stuff::Vector3D positionOffset = target->getPosition();
  1369. positionOffset.x += missRadius;
  1370. positionOffset.z = land->getTerrainElevation(positionOffset);
  1371. bool canSeeHit = lineOfSight(positionOffset,true);
  1372. if (!canSeeHit)
  1373. {
  1374. positionOffset.x -= (missRadius * 2.0f);
  1375. positionOffset.z = land->getTerrainElevation(positionOffset);
  1376. canSeeHit = lineOfSight(positionOffset,true);
  1377. if (!canSeeHit)
  1378. {
  1379. positionOffset.x += missRadius;
  1380. positionOffset.y += missRadius;
  1381. positionOffset.z = land->getTerrainElevation(positionOffset);
  1382. canSeeHit = lineOfSight(positionOffset,true);
  1383. if (!canSeeHit)
  1384. {
  1385. positionOffset.y -= (missRadius * 2.0f);
  1386. positionOffset.z = land->getTerrainElevation(positionOffset);
  1387. canSeeHit = lineOfSight(positionOffset,true);
  1388. if (!canSeeHit)
  1389. {
  1390. //OK, no miss location is visible. Hit the target with ZERO damage!!
  1391. curShotInfo.init(this->getWatchID(),
  1392. weaponMasterId,
  1393. 0.0f,
  1394. -1,
  1395. entryAngle);
  1396. }
  1397. }
  1398. }
  1399. }
  1400. //-------------------------------------------------------------------------
  1401. // If I'm in a multiplayer game and I'm the server, record this weapon fire
  1402. // so it may be broadcast to all clients...
  1403. if (MPlayer && MPlayer->isServer())
  1404. {
  1405. WeaponFireChunk chunk;
  1406. chunk.init();
  1407. chunk.buildLocationTarget(positionOffset, weaponId, FALSE, numMissiles);
  1408. chunk.pack(this);
  1409. WeaponFireChunk chunk2;
  1410. chunk2.init();
  1411. chunk2.data = chunk.data;
  1412. chunk2.unpack(this);
  1413. if (!chunk.equalTo(&chunk2))
  1414. Fatal(0, " Turret.fireWeapon: Bad WeaponFireChunk (save wfchunk.dbg file now) ");
  1415. addWeaponFireChunk(CHUNK_SEND, &chunk);
  1416. LogWeaponFireChunk(&chunk, this, target);
  1417. }
  1418. if (canSeeHit) //miss location is in LOS. Hit the ground
  1419. weaponFX->connect(this,positionOffset,&curShotInfo,sourceHotSpot);
  1420. else //Miss location is NOT in LOS. Hit Target with ZERO damage!!!
  1421. weaponFX->connect(this,target,&curShotInfo,sourceHotSpot);
  1422. printFireWeaponDebugInfo(target, &positionOffset, attackChance, hitRoll, &curShotInfo);
  1423. }
  1424. }
  1425. else
  1426. {
  1427. //----------------------------------------------------
  1428. // Non-missile weapon, so just one weapon hit spawn...
  1429. // For now, always use a laser effect...
  1430. WeaponShotInfo shotInfo;
  1431. shotInfo.init(this->getWatchID(),
  1432. weaponMasterId,
  1433. MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1434. -1,
  1435. entryAngle);
  1436. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1437. WeaponBoltPtr weaponFX = ObjectManager->createWeaponBolt(effectType);
  1438. if (!weaponFX)
  1439. Fatal(-1," couldnt create weapon FX ");
  1440. //-------------------------------------------------------------------------------------------
  1441. // If we missed, pick sights away from the target and check LOS to each one. If all are
  1442. // invisible, just hit the target with zero points of damage.
  1443. float missRadius = target ? 25.0 : 5.0;
  1444. Stuff::Vector3D positionOffset = target->getPosition();
  1445. positionOffset.x += missRadius;
  1446. positionOffset.z = land->getTerrainElevation(positionOffset);
  1447. bool canSeeHit = lineOfSight(positionOffset,true);
  1448. if (!canSeeHit)
  1449. {
  1450. positionOffset.x -= (missRadius * 2.0f);
  1451. positionOffset.z = land->getTerrainElevation(positionOffset);
  1452. canSeeHit = lineOfSight(positionOffset,true);
  1453. if (!canSeeHit)
  1454. {
  1455. positionOffset.x += missRadius;
  1456. positionOffset.y += missRadius;
  1457. positionOffset.z = land->getTerrainElevation(positionOffset);
  1458. canSeeHit = lineOfSight(positionOffset,true);
  1459. if (!canSeeHit)
  1460. {
  1461. positionOffset.y -= (missRadius * 2.0f);
  1462. positionOffset.z = land->getTerrainElevation(positionOffset);
  1463. canSeeHit = lineOfSight(positionOffset,true);
  1464. if (!canSeeHit)
  1465. {
  1466. //OK, no miss location is visible. Hit the target with ZERO damage!!
  1467. shotInfo.init(this->getWatchID(),
  1468. weaponMasterId,
  1469. 0.0f,
  1470. -1,
  1471. entryAngle);
  1472. }
  1473. }
  1474. }
  1475. }
  1476. //-------------------------------------------------------------------------
  1477. // If I'm in a multiplayer game and I'm the server, record this weapon fire
  1478. // so it may be broadcast to all clients...
  1479. if (MPlayer && MPlayer->isServer()) {
  1480. WeaponFireChunk chunk;
  1481. chunk.init();
  1482. chunk.buildLocationTarget(positionOffset, weaponId, FALSE, 0);
  1483. chunk.pack(this);
  1484. WeaponFireChunk chunk2;
  1485. chunk2.init();
  1486. chunk2.data = chunk.data;
  1487. chunk2.unpack(this);
  1488. if (!chunk.equalTo(&chunk2))
  1489. Fatal(0, " Turret.fireWeapon: Bad WeaponFireChunk (save wfchunk.dbg file now) ");
  1490. addWeaponFireChunk(CHUNK_SEND, &chunk);
  1491. LogWeaponFireChunk(&chunk, this, target);
  1492. }
  1493. unsigned long sourceHotSpot = currentWeaponNode;
  1494. if (canSeeHit) //miss location is in LOS. Hit the ground
  1495. weaponFX->connect(this,positionOffset,&shotInfo,sourceHotSpot);
  1496. else //Miss location is NOT in LOS. Hit Target with ZERO damage!!!
  1497. weaponFX->connect(this,target,&shotInfo,sourceHotSpot);
  1498. printFireWeaponDebugInfo(target, &positionOffset, attackChance, hitRoll, &shotInfo);
  1499. }
  1500. }
  1501. }
  1502. //-------------------------------------------------------------------
  1503. // Trigger the WEAPON TARGET event. For now, this assumes the target
  1504. // KNOWS we were targeting him. Of course, the target wouldn't always
  1505. // be aware of this, would they?
  1506. if (targetPilot)
  1507. targetPilot->triggerAlarm(PILOT_ALARM_TARGET_OF_WEAPONFIRE, getWatchID());
  1508. }
  1509. //---------------------------------------------------------------------------
  1510. long Turret::handleWeaponFire (long weaponIndex,
  1511. GameObjectPtr target,
  1512. Stuff::Vector3D* targetPoint,
  1513. bool hit,
  1514. float entryAngle,
  1515. long numMissiles,
  1516. long hitLocation) {
  1517. long weaponMasterId = ((TurretTypePtr)getObjectType())->weaponMasterId[weaponIndex];
  1518. bool isStreakMissile = MasterComponent::masterList[weaponMasterId].getWeaponStreak();
  1519. //-----------------------
  1520. // Weapon must recycle...
  1521. startWeaponRecycle(weaponIndex);
  1522. WeaponBoltPtr weaponFX = NULL;
  1523. if (hit) {
  1524. //------------
  1525. // Attack hit.
  1526. if (MasterComponent::masterList[weaponMasterId].getForm() == COMPONENT_FORM_WEAPON_MISSILE) {
  1527. //-----------------------------------------------------------------------------
  1528. // Is the missile short-range(SRM) or long-range(LRM)? SRMs are handled with
  1529. // each missile spawning a separate damage calc. LRMs are clustered, 5 missiles
  1530. // to a cluster (with the remainder a final, separate cluster), where each
  1531. // cluster spawns a separate damage calc...
  1532. //-----------------------------------------------
  1533. // a MissileGen Object is ALL of the clusters.
  1534. // Don't make a thousand of them or the game implodes!
  1535. //numClusters = 1;
  1536. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1537. if (numMissiles > 0) {
  1538. //-------------------------------------------------------------------
  1539. // This code will mess up if the object is not a BULLET!!!!!!!!!!!
  1540. weaponFX = ObjectManager->createWeaponBolt(effectType);
  1541. if (!weaponFX)
  1542. Fatal(-1," couldnt create weapon FX ");
  1543. Assert(hitLocation != -2, 0, " Turret.handleWeaponFire: Bad Hit Location ");
  1544. unsigned long targetHotSpot = 0;
  1545. if (target && (target->getObjectClass() == BATTLEMECH))
  1546. targetHotSpot = ((BattleMechPtr)target)->body[hitLocation].hotSpotNumber;
  1547. WeaponShotInfo curShotInfo;
  1548. curShotInfo.init(this->getWatchID(),
  1549. weaponMasterId,
  1550. (numMissiles * MasterComponent::masterList[weaponMasterId].getWeaponDamage()),
  1551. hitLocation,
  1552. entryAngle);
  1553. if (target)
  1554. weaponFX->connect(this, target, &curShotInfo, 0, targetHotSpot);
  1555. else
  1556. weaponFX->connect(this, *targetPoint, &curShotInfo);
  1557. }
  1558. }
  1559. else
  1560. {
  1561. //----------------------------------------------------
  1562. // Non-missile weapon, so just one weapon hit spawn...
  1563. // For now, always use a laser effect...
  1564. WeaponShotInfo shotInfo;
  1565. shotInfo.init(this->getWatchID(),
  1566. weaponMasterId,
  1567. MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1568. hitLocation,
  1569. entryAngle);
  1570. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1571. weaponFX = ObjectManager->createWeaponBolt(effectType);
  1572. if (!weaponFX)
  1573. Fatal(-1," couldnt create weapon FX ");
  1574. if (target) {
  1575. unsigned long targetHotSpot = 0;
  1576. if (target->getObjectClass() == BATTLEMECH)
  1577. targetHotSpot = ((BattleMechPtr)target)->body[hitLocation].hotSpotNumber;
  1578. weaponFX->connect(this, target, &shotInfo, 0, targetHotSpot);
  1579. }
  1580. else {
  1581. //--------------------------------
  1582. // Hit the target point/terrain...
  1583. weaponFX->connect(this, *targetPoint, &shotInfo);
  1584. }
  1585. }
  1586. }
  1587. else
  1588. {
  1589. if (!isStreakMissile) {
  1590. //----------------------------------------------------
  1591. // Miss, so check for possible miss resolution here...
  1592. if (MasterComponent::masterList[weaponMasterId].getForm() == COMPONENT_FORM_WEAPON_MISSILE) {
  1593. //-----------------------------------------------------------------------------
  1594. // Is the missile short-range(SRM) or long-range(LRM)? SRMs are handled with
  1595. // each missile spawning a separate damage calc. LRMs are clustered, 5 missiles
  1596. // to a cluster (with the remainder a final, separate cluster), where each
  1597. // cluster spawns a separate damage calc...
  1598. if (numMissiles > 0) {
  1599. //-----------------------------------------------
  1600. // a MissileGen Object is ALL of the clusters.
  1601. // Don't make a thousand of them or the game implodes!
  1602. //numClusters = 1;
  1603. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1604. //-------------------------------------------------------------------
  1605. // This code will mess up if the object is not a BULLET!!!!!!!!!!!
  1606. weaponFX = ObjectManager->createWeaponBolt(effectType);
  1607. if (!weaponFX)
  1608. Fatal(-1," couldnt create weapon FX ");
  1609. WeaponShotInfo curShotInfo;
  1610. curShotInfo.init(this->getWatchID(),
  1611. weaponMasterId,
  1612. numMissiles * MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1613. -1,
  1614. entryAngle);
  1615. //-------------------------------------------------------------------------------------------
  1616. // If we missed, push the weapon target position away from the target by twice extent Radius
  1617. float missRadius = target ? 25.0 : 5.0;
  1618. Stuff::Vector3D positionOffset;
  1619. positionOffset.x = missRadius;
  1620. positionOffset.y = missRadius;
  1621. positionOffset.z = 0.0;
  1622. //------------------------------------------------------------
  1623. // This is OK because a miss can miss to any screen location
  1624. // during network play and it is still valid.
  1625. // NOT true: Stray shots could trigger mines!
  1626. positionOffset.x = RandomNumber(positionOffset.x * 2) - positionOffset.x;
  1627. positionOffset.y = RandomNumber(positionOffset.y * 2) - positionOffset.y;
  1628. positionOffset.z = RandomNumber(positionOffset.z * 2) - positionOffset.z;
  1629. positionOffset += (target ? target->getPosition() : *targetPoint);
  1630. weaponFX->connect(this, positionOffset, &curShotInfo);
  1631. }
  1632. }
  1633. else
  1634. {
  1635. //----------------------------------------------------
  1636. // Non-missile weapon, so just one weapon hit spawn...
  1637. // For now, always use a laser effect...
  1638. WeaponShotInfo shotInfo;
  1639. shotInfo.init(this->getWatchID(),
  1640. weaponMasterId,
  1641. MasterComponent::masterList[weaponMasterId].getWeaponDamage(),
  1642. -1,
  1643. entryAngle);
  1644. unsigned char effectType = MasterComponent::masterList[weaponMasterId].getWeaponSpecialEffect();
  1645. weaponFX = ObjectManager->createWeaponBolt(effectType);
  1646. if (!weaponFX)
  1647. Fatal(-1," couldnt create weapon FX ");
  1648. //-------------------------------------------------------------------------------------------
  1649. // If we missed, push the weapon target position away from the target by twice extent Radius
  1650. float missRadius = target ? 25.0 : 5.0;
  1651. Stuff::Vector3D positionOffset;
  1652. positionOffset.x = missRadius;
  1653. positionOffset.y = missRadius;
  1654. positionOffset.z = 0.0;
  1655. positionOffset.x = RandomNumber(positionOffset.x * 2) - positionOffset.x;
  1656. positionOffset.y = RandomNumber(positionOffset.y * 2) - positionOffset.y;
  1657. positionOffset.z = RandomNumber(positionOffset.z * 2) - positionOffset.z;
  1658. positionOffset += (target ? target->getPosition() : *targetPoint);
  1659. weaponFX->connect(this, positionOffset, &shotInfo);
  1660. }
  1661. }
  1662. }
  1663. //-------------------------------------------------------------------
  1664. // Trigger the WEAPON TARGET event. For now, this assumes the target
  1665. // KNOWS we were targeting him. Of course, the target wouldn't always
  1666. // be aware of this, would they?
  1667. MechWarriorPtr targetPilot = NULL;
  1668. if (target && target->isMover()) {
  1669. targetPilot = ((MoverPtr)target)->getPilot();
  1670. targetPilot->updateAttackerStatus(partId, scenarioTime);
  1671. targetPilot->triggerAlarm(PILOT_ALARM_TARGET_OF_WEAPONFIRE, getWatchID());
  1672. }
  1673. return(NO_ERR);
  1674. }
  1675. //---------------------------------------------------------------------------
  1676. void Turret::lightOnFire (float timeToBurn) {
  1677. }
  1678. //---------------------------------------------------------------------------
  1679. void Turret::renderShadows (void)
  1680. {
  1681. if (appearance->hasAnimationData(0))
  1682. return; //No shadows on Popup Turrets. Period.
  1683. if (appearance->canBeSeen())
  1684. {
  1685. appearance->renderShadows();
  1686. }
  1687. setSelected(false); //ALWAYS reset the selected flags. GUI needs this to work!
  1688. setTargeted( false ); //ALWAYS do it here, too! Otherwise things may draw FUNNY!
  1689. }
  1690. //---------------------------------------------------------------------------
  1691. void Turret::render (void)
  1692. {
  1693. if (appearance->canBeSeen())
  1694. {
  1695. if (drawFlags & DRAW_BARS )
  1696. {
  1697. TurretTypePtr type = (TurretTypePtr)getObjectType();
  1698. float barStatus = 1.0;
  1699. float totalDmgLvl = type->getDamageLevel();
  1700. if (totalDmgLvl > 0.0)
  1701. barStatus -= getDamage() / totalDmgLvl;
  1702. if (barStatus < 0.0)
  1703. barStatus = 0.0;
  1704. DWORD color = 0xff7f7f7f;
  1705. if ((teamId > -1) && (teamId < 8)) {
  1706. if (getTeam()->isFriendly(Team::home))
  1707. color = SB_GREEN;
  1708. else if (getTeam()->isEnemy(Team::home))
  1709. color = SB_RED;
  1710. else
  1711. color = SB_BLUE;
  1712. }
  1713. appearance->setBarColor(color);
  1714. appearance->setBarStatus(barStatus);
  1715. }
  1716. if (((TurretTypePtr)getObjectType())->turretTypeName < IDS_MC2_STRING_START)
  1717. {
  1718. appearance->setObjectNameId(((TurretTypePtr)getObjectType())->turretTypeName + IDS_MC2_STRING_START);
  1719. }
  1720. else
  1721. {
  1722. appearance->setObjectNameId(((TurretTypePtr)getObjectType())->turretTypeName);
  1723. }
  1724. windowsVisible = turn;
  1725. appearance->setVisibility(true,true);
  1726. appearance->render();
  1727. }
  1728. setSelected(false); //ALWAYS reset the selected flags. GUI needs this to work!
  1729. setTargeted( false ); //ALWAYS do it here, too! Otherwise things may draw FUNNY!
  1730. }
  1731. //---------------------------------------------------------------------------
  1732. void Turret::destroy (void)
  1733. {
  1734. if (appearance)
  1735. {
  1736. delete appearance;
  1737. appearance = NULL;
  1738. }
  1739. }
  1740. //---------------------------------------------------------------------------
  1741. void Turret::init (bool create, ObjectTypePtr _type) {
  1742. //-------------------------------------------
  1743. // Initialize the Building Appearance here.
  1744. GameObject::init(true, _type);
  1745. setFlag(OBJECT_FLAG_JUSTCREATED, true);
  1746. //-------------------------------------------------------------
  1747. // The appearance is initialized here using data from the type
  1748. // Need an MLR appearance class
  1749. char *appearName = _type->getAppearanceTypeName();
  1750. //--------------------------------------------------------------
  1751. // New code!!!
  1752. // We need to append the sprite type to the appearance num now.
  1753. // The MechEdit tool does not assume a sprite type, nor should it.
  1754. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  1755. long appearanceType = (BLDG_TYPE << 24);
  1756. AppearanceTypePtr buildingAppearanceType = NULL;
  1757. if (!appearName)
  1758. {
  1759. //------------------------------------------------------
  1760. // LOAD a dummy appearance until real ones are available
  1761. // for this building!
  1762. appearanceType = (BLDG_TYPE << 24);
  1763. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TESTBLDG");
  1764. }
  1765. else
  1766. {
  1767. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearName);
  1768. if (!buildingAppearanceType)
  1769. {
  1770. char msg[1024];
  1771. sprintf(msg,"No Building Appearance Named %s",appearName);
  1772. Fatal(0,msg);
  1773. }
  1774. }
  1775. appearance = new BldgAppearance;
  1776. gosASSERT(appearance != NULL);
  1777. //--------------------------------------------------------------
  1778. // The only appearance type for buildings is MLR_APPEARANCE.
  1779. gosASSERT(buildingAppearanceType->getAppearanceClass() == BLDG_TYPE);
  1780. appearance->init((BldgAppearanceType*)buildingAppearanceType, (GameObjectPtr)this);
  1781. objectClass = TURRET;
  1782. setFlag(OBJECT_FLAG_NEVER_REVEALED, true);
  1783. teamId = -1;
  1784. readyTime[0] = readyTime[1] = readyTime[2] = readyTime[3] = 0.0;
  1785. TurretTypePtr type = (TurretTypePtr)getObjectType();
  1786. if (type->engageRadius != 0.0)
  1787. type->setExtentRadius(type->engageRadius);
  1788. if (type->getExtentRadius() > 0.0)
  1789. setFlag(OBJECT_FLAG_TANGIBLE, true);
  1790. tonnage = type->baseTonnage;
  1791. explDamage = type->explDmg;
  1792. explRadius = type->explRad;
  1793. setFlag(OBJECT_FLAG_CAPTURABLE, false);
  1794. setFlag(OBJECT_FLAG_POP_NEUTRALS, false);
  1795. }
  1796. //---------------------------------------------------------------------------
  1797. long Turret::handleWeaponHit (WeaponShotInfoPtr shotInfo, bool addMultiplayChunk) {
  1798. if (!shotInfo)
  1799. return(NO_ERR);
  1800. if (addMultiplayChunk)
  1801. MPlayer->addWeaponHitChunk(this, shotInfo);
  1802. printHandleWeaponHitDebugInfo(shotInfo);
  1803. damage = getDamage() + shotInfo->damage;
  1804. TurretTypePtr type = (TurretTypePtr)getObjectType();
  1805. if (!isDestroyed() && (damage >= type->getDamageLevel()))
  1806. {
  1807. setFlag(OBJECT_FLAG_DESTROYED, true);
  1808. //------------------------------------------------------
  1809. // Turret is dead. You may no longer collide with it.
  1810. setFlag(OBJECT_FLAG_TANGIBLE, false);
  1811. setStatus(OBJECT_STATUS_DESTROYED);
  1812. appearance->setObjStatus(OBJECT_STATUS_DESTROYED);
  1813. //-----------------------------------------------------
  1814. // Now, blow the building up using its type->explosion
  1815. // (this won't do anything if a building type doesn't have an explosion)
  1816. ObjectManager->createExplosion(TURRET_EXPLOSION_ID,NULL,position,explDamage,explRadius);
  1817. if (CombatLog) {
  1818. char s[1024];
  1819. sprintf(s, "[%.2f] turret.destroyed: (%05d)%s", scenarioTime, getPartId(), getName());
  1820. CombatLog->write(s);
  1821. CombatLog->write(" ");
  1822. }
  1823. }
  1824. return(NO_ERR);
  1825. }
  1826. //---------------------------------------------------------------------------
  1827. long Turret::calcFireRanges (void) {
  1828. //---------------------------
  1829. // Calc min and max ranges...
  1830. maxRange = 0;
  1831. minRange = 1000000.0;
  1832. numFunctionalWeapons = 0;
  1833. for (long curWeapon = 0; curWeapon < MAX_TURRET_WEAPONS; curWeapon++) {
  1834. long weaponMasterId = ((TurretTypePtr)getObjectType())->weaponMasterId[curWeapon];
  1835. if (weaponMasterId != -1) {
  1836. long range = (long)MasterComponent::masterList[weaponMasterId].getWeaponRange();
  1837. float minWeaponRange = WeaponRanges[range][0];
  1838. float maxWeaponRange = WeaponRanges[range][1];
  1839. if (maxWeaponRange > maxRange)
  1840. maxRange = maxWeaponRange;
  1841. if (minWeaponRange < minRange)
  1842. minRange = minWeaponRange;
  1843. numFunctionalWeapons++;
  1844. }
  1845. }
  1846. return(0);
  1847. }
  1848. //---------------------------------------------------------------------------
  1849. bool Turret::isLinked (void)
  1850. {
  1851. return (parent != 0);
  1852. }
  1853. //---------------------------------------------------------------------------
  1854. GameObjectPtr Turret::getParent (void)
  1855. {
  1856. return (ObjectManager->getByWatchID(parent));
  1857. }
  1858. //---------------------------------------------------------------------------
  1859. void Turret::setParentId (DWORD pId)
  1860. {
  1861. parentId = pId;
  1862. }
  1863. //---------------------------------------------------------------------------
  1864. void Turret::updateDebugWindow (GameDebugWindow* debugWindow) {
  1865. debugWindow->clear();
  1866. char s[128];
  1867. //-------------------------------------------------------
  1868. // For now, show the floating help text if we have one...
  1869. if (((ObjectAppearance*)appearance)->objectNameId != -1) {
  1870. char myName[255];
  1871. cLoadString(((ObjectAppearance*)appearance)->objectNameId, myName, 254);
  1872. debugWindow->print(myName);
  1873. }
  1874. else
  1875. debugWindow->print("<no name>");
  1876. sprintf(s, "team: %d, partID: %d, objT: %d", getTeamId(), getPartId(), getObjectType()->whatAmI());
  1877. debugWindow->print(s);
  1878. GameObjectPtr target = ObjectManager->getByWatchID(targetWID);
  1879. sprintf(s, "target: %d", target ? target->getPartId() : 0);
  1880. debugWindow->print(s);
  1881. sprintf(s, "damage: %.2f/%.2f", getDamage(), getDamageLevel());
  1882. debugWindow->print(s);
  1883. }
  1884. //***************************************************************************
  1885. void Turret::Save (PacketFilePtr file, long packetNum)
  1886. {
  1887. TurretData data;
  1888. CopyTo(&data);
  1889. //PacketNum incremented in ObjectManager!!
  1890. file->writePacket(packetNum,(MemoryPtr)&data,sizeof(TurretData),STORAGE_TYPE_ZLIB);
  1891. }
  1892. //***************************************************************************
  1893. void Turret::CopyTo (TurretData *data)
  1894. {
  1895. data->teamId = teamId;
  1896. data->turretRotation = turretRotation;
  1897. data->didReveal = didReveal;
  1898. data->targetWID = targetWID;
  1899. memcpy(data->readyTime,readyTime,sizeof(float) * MAX_TURRET_WEAPONS);
  1900. memcpy(data->lastFireTime,lastFireTime,sizeof(float) * MAX_TURRET_WEAPONS);
  1901. data->minRange = minRange;
  1902. data->maxRange = maxRange;
  1903. data->numFunctionalWeapons = numFunctionalWeapons;
  1904. data->idleWait = idleWait;
  1905. data->idlePosition = idlePosition;
  1906. data->oldPosition = oldPosition;
  1907. data->parentId = parentId;
  1908. data->parent = parent;
  1909. data->currentWeaponNode = currentWeaponNode;
  1910. TerrainObject::CopyTo(dynamic_cast<TerrainObjectData *>(data));
  1911. }
  1912. //---------------------------------------------------------------------------
  1913. void Turret::Load (TurretData *data)
  1914. {
  1915. TerrainObject::Load(dynamic_cast<TerrainObjectData *>(data));
  1916. teamId = data->teamId;
  1917. turretRotation = data->turretRotation;
  1918. //Always FALSE on a load because we need to recalc the matrices to insure good LOS!!
  1919. didReveal = false;
  1920. targetWID = data->targetWID;
  1921. memcpy(readyTime,data->readyTime,sizeof(float) * MAX_TURRET_WEAPONS);
  1922. memcpy(lastFireTime,data->lastFireTime,sizeof(float) * MAX_TURRET_WEAPONS);
  1923. minRange = data->minRange;
  1924. maxRange = data->maxRange;
  1925. numFunctionalWeapons = data->numFunctionalWeapons;
  1926. idleWait = data->idleWait;
  1927. idlePosition = data->idlePosition;
  1928. oldPosition = data->oldPosition;
  1929. parentId = data->parentId;
  1930. parent = data->parent;
  1931. currentWeaponNode = data->currentWeaponNode;
  1932. }
  1933. //***************************************************************************