sakurabossgameobj.cpp 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/sakurabossgameobj.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/16/02 6:01p $*
  29. * *
  30. * $Revision:: 35 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "sakurabossgameobj.h"
  36. #include "wwhack.h"
  37. #include "simpledefinitionfactory.h"
  38. #include "persistfactory.h"
  39. #include "combatchunkid.h"
  40. #include "debug.h"
  41. #include "animcontrol.h"
  42. #include "crandom.h"
  43. #include "weapons.h"
  44. #include "rendobj.h"
  45. #include "wwaudio.h"
  46. #include "audiblesound.h"
  47. #include "explosion.h"
  48. #include "phys.h"
  49. #include "rbody.h"
  50. #include "pscene.h"
  51. #include "soldier.h"
  52. #include "waypath.h"
  53. #include "waypoint.h"
  54. #include "pathfind.h"
  55. #include "pathmgr.h"
  56. #include "string_ids.h"
  57. #include "translateobj.h"
  58. #include "translatedb.h"
  59. #include "wwprofile.h"
  60. #include "conversationmgr.h"
  61. #include "activeconversation.h"
  62. #include "conversation.h"
  63. #include "combat.h"
  64. DECLARE_FORCE_LINK (SakuraBoss)
  65. //////////////////////////////////////////////////////////////////////////
  66. // General constants
  67. //////////////////////////////////////////////////////////////////////////
  68. static const char * LEFT_ROCKET_BONE = "BN_MISSILE_WNGL";
  69. static const char * RIGHT_ROCKET_BONE = "BN_MISSILE_WNGR";
  70. static const char * ROCKET_DOOR_ANIMATION = "V_COMMANCHE.A_COMM_MISL";
  71. static const char * GATLING_MUZZLE = "MUZZLEA0";
  72. static const char * LEFT_ROCKET_MUZZLE = "MUZZLEB01";
  73. static const char * RIGHT_ROCKET_MUZZLE = "MUZZLEB0";
  74. static const char * LEFT_ROCKET_MESH = "V_COM_MISSILEL";
  75. static const char * RIGHT_ROCKET_MESH = "V_COM_MISSILER";
  76. static float ROCKETS_IN_FRAME_NUM = 0.0F;
  77. static float ROCKETS_OUT_FRAME_NUM = 10.0F;
  78. //////////////////////////////////////////////////////////////////////////
  79. // Waypath IDs
  80. //////////////////////////////////////////////////////////////////////////
  81. const int WID_LONG_STRAFING_RUN = 1700009;
  82. const int WID_LONG_STRAFING_RUN2 = 1700016;
  83. const int WID_TO_LIFT_AREA_SOUTH = 1700018;
  84. const int WID_TO_LIFT_AREA_NORTH = 1700019;
  85. const int WID_TIBFIELD_TO_VSTRAFE = 1700004;
  86. const int WID_LSTRAF2_TO_VSTRAFE = 1700017;
  87. const int WID_VSTRAFE_TO_TIBFIELD = 1700006;
  88. const int WID_LSTRAFE_TO_TIBFIELD = 1700002;
  89. const int WID_REFINERY_TO_TIBFIELD = 1700012;
  90. const int WID_LSTRAFE_TO_REFINERY = 1700010;
  91. const int WID_VSTRAFE_TO_REFINERY = 1700014;
  92. const int WID_PPLANT_TO_REFINERY = 1700007;
  93. const int WID_VSTRAFE_TO_PPLANT = 1700015;
  94. const int WID_REFINERY_TO_PPLANT = 1700008;
  95. const int WID_REFINERY_TO_LSTRAFE2 = 1700003;
  96. const int WID_VSTRAFE_TO_LSTRAFE2 = 1700005;
  97. const int WID_REFINERY_TO_LSTRAFE = 1700011;
  98. const int WID_VSTRAFE_TO_LSTRAFE = 1700013;
  99. //////////////////////////////////////////////////////////////////////////
  100. // Weapon and state enumerations
  101. //////////////////////////////////////////////////////////////////////////
  102. enum
  103. {
  104. WEAPON_GATLING_GUN = 0x01,
  105. WEAPON_ROCKETS_LEFT = 0x02,
  106. WEAPON_ROCKETS_RIGHT = 0x04,
  107. WEAPON_ALL = WEAPON_GATLING_GUN | WEAPON_ROCKETS_LEFT | WEAPON_ROCKETS_RIGHT,
  108. };
  109. enum
  110. {
  111. ROCKET_STATE_OPENING = 1,
  112. ROCKET_STATE_OPEN,
  113. ROCKET_STATE_CLOSING,
  114. ROCKET_STATE_CLOSED,
  115. ROCKET_STATE_FIRING
  116. };
  117. enum
  118. {
  119. GATLING_STATE_NORMAL = 1,
  120. GATLING_STATE_REVVING_UP,
  121. GATLING_STATE_FIRING
  122. };
  123. enum
  124. {
  125. SAKURA_STATE_NORMAL = 1,
  126. SAKURA_STATE_TAUNTING,
  127. };
  128. enum
  129. {
  130. VEHICLE_STATE_HOVERING = 1,
  131. VEHICLE_STATE_MOVING,
  132. };
  133. enum
  134. {
  135. STATE_CIRCLE_POWER_PLANT = 1,
  136. STATE_CIRCLE_REFINERY,
  137. STATE_ATTACK_LIFT_AREA,
  138. STATE_LONG_STRAFING_RUN,
  139. STATE_LAND_IN_TIBERIUM_FIELD,
  140. STATE_VALLEY_STRAFE,
  141. STATE_LONG_STRAFING_RUN2,
  142. STATE_IN_TRANSITION
  143. };
  144. //////////////////////////////////////////////////////////////////////////
  145. // Weapon constants
  146. //////////////////////////////////////////////////////////////////////////
  147. enum
  148. {
  149. ROCKET_RIGHT = 0,
  150. ROCKET_LEFT = 1
  151. };
  152. //////////////////////////////////////////////////////////////////////////
  153. // Taunt constants
  154. //////////////////////////////////////////////////////////////////////////
  155. const char *TAUNT_IDS[6] =
  156. {
  157. "IDS_SAKURA_BOSS_TAUNT1",
  158. "IDS_SAKURA_BOSS_TAUNT2",
  159. "IDS_SAKURA_BOSS_TAUNT3",
  160. "IDS_SAKURA_BOSS_TAUNT4",
  161. "IDS_SAKURA_BOSS_TAUNT5",
  162. "IDS_SAKURA_BOSS_TAUNT6"
  163. };
  164. //////////////////////////////////////////////////////////////////////////
  165. // Save/load constants
  166. //////////////////////////////////////////////////////////////////////////
  167. enum
  168. {
  169. CHUNKID_DEF_PARENT = 0x09070458,
  170. CHUNKID_DEF_VARIABLES,
  171. CHUNKID_DEF_ROCKET_DEFENSEOBJ_DEF,
  172. VARID_DEF_GATLING_DEF_ID = 1,
  173. VARID_DEF_ROCKET_DEF_ID,
  174. VARID_DEF_GATLING_REV_SOUND_ID,
  175. VARID_DEF_ROCKET_DOOR_SOUND_ID,
  176. VARID_DEF_ROCKET_DESTROYED_EXPLOSION_ID
  177. };
  178. enum
  179. {
  180. CHUNKID_PARENT = 0x09070459,
  181. CHUNKID_VARIABLES,
  182. CHUNKID_ROCKETL_DEFENSE_OBJ,
  183. CHUNKID_ROCKETR_DEFENSE_OBJ,
  184. CHUNKID_LAST_DAMAGER,
  185. CHUNKID_CURR_TARGET,
  186. CHUNKID_PILOT,
  187. CHUNKID_PATH,
  188. VARID_AVAILABLE_WEAPONS = 1,
  189. VARID_OVERALL_STATE,
  190. VARID_NEXT_OVERALL_STATE,
  191. VARID_FACE_TARGET_IN_TRANSITION,
  192. VARID_ROCKET_LAUNCHER_STATE,
  193. VARID_GATTLING_GUN_STATE,
  194. VARID_SAKURA_STATE,
  195. VARID_VEHICLE_STATE,
  196. VARID_MOVE_TO_LOCATION,
  197. VARID_GATTLING_GUN_STATE_TIME_LEFT,
  198. VARID_SAKURA_TAUNT_TIME_LEFT,
  199. VARID_ROCKET_LAUNCHER_STATE_TIME_LEFT,
  200. VARID_OVERALL_STATE_TIME_LEFT,
  201. VARID_TARGET_TIME_LEFT,
  202. VARID_CURRENT_HEALTH,
  203. VARID_TARGET_ANGLE,
  204. VARID_TARGET_POS,
  205. VARID_IS_ATTACKING,
  206. VARID_CURRENT_MUZZLE_TM,
  207. VARID_CURRENT_MUZZLE_INDEX,
  208. VARID_TILT_ANGLE,
  209. VARID_CHOPPER_TILT_BONE_INDEX
  210. };
  211. //////////////////////////////////////////////////////////////////////////
  212. // Factories
  213. //////////////////////////////////////////////////////////////////////////
  214. SimplePersistFactoryClass<SakuraBossGameObjDef, CHUNKID_GAME_OBJECT_DEF_SAKURA_BOSS> _SakuraBossGameObjDefPersistFactory;
  215. SimplePersistFactoryClass<SakuraBossGameObj, CHUNKID_GAME_OBJECT_SAKURA_BOSS> _SakuraBossGameObjPersistFactory;
  216. DECLARE_DEFINITION_FACTORY(SakuraBossGameObjDef, CLASSID_GAME_OBJECT_DEF_SAKURA_BOSS, "Sakura Boss") _SakuraBossGameObjDefDefFactory;
  217. //////////////////////////////////////////////////////////////////////////
  218. //
  219. // SakuraBossGameObjDef
  220. //
  221. //////////////////////////////////////////////////////////////////////////
  222. SakuraBossGameObjDef::SakuraBossGameObjDef (void) :
  223. GattlingGunDefID (0),
  224. RocketLauncherDefID (0),
  225. GattlingGunRevSoundDefID (0),
  226. RocketDestroyedExplosionID (0),
  227. RocketDoorOpenSoundID (0)
  228. {
  229. PARAM_SEPARATOR (SakuraBossGameObjDef, "Rocket Launcher Defense Settings");
  230. DEFENSEOBJECTDEF_EDITABLE_PARAMS (SakuraBossGameObjDef, RocketsDefense);
  231. PARAM_SEPARATOR (SakuraBossGameObjDef, "");
  232. PARAM_SEPARATOR (SakuraBossGameObjDef, "Weapons");
  233. EDITABLE_PARAM (SakuraBossGameObjDef, ParameterClass::TYPE_WEAPONOBJDEFINITIONID, RocketLauncherDefID);
  234. EDITABLE_PARAM (SakuraBossGameObjDef, ParameterClass::TYPE_WEAPONOBJDEFINITIONID, GattlingGunDefID);
  235. EDITABLE_PARAM (SakuraBossGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, GattlingGunRevSoundDefID);
  236. EDITABLE_PARAM (SakuraBossGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, RocketDoorOpenSoundID);
  237. EDITABLE_PARAM (SakuraBossGameObjDef, ParameterClass::TYPE_EXPLOSIONDEFINITIONID, RocketDestroyedExplosionID);
  238. PARAM_SEPARATOR (SakuraBossGameObjDef, "");
  239. return ;
  240. }
  241. //////////////////////////////////////////////////////////////////////////
  242. //
  243. // ~SakuraBossGameObjDef
  244. //
  245. //////////////////////////////////////////////////////////////////////////
  246. SakuraBossGameObjDef::~SakuraBossGameObjDef (void)
  247. {
  248. return ;
  249. }
  250. //////////////////////////////////////////////////////////////////////////
  251. //
  252. // Get_Class_ID
  253. //
  254. //////////////////////////////////////////////////////////////////////////
  255. uint32
  256. SakuraBossGameObjDef::Get_Class_ID (void) const
  257. {
  258. return CLASSID_GAME_OBJECT_DEF_SAKURA_BOSS;
  259. }
  260. //////////////////////////////////////////////////////////////////////////
  261. //
  262. // Create
  263. //
  264. //////////////////////////////////////////////////////////////////////////
  265. PersistClass *
  266. SakuraBossGameObjDef::Create (void) const
  267. {
  268. SakuraBossGameObj *obj = new SakuraBossGameObj;
  269. obj->Init(*this);
  270. return obj;
  271. }
  272. //////////////////////////////////////////////////////////////////////////
  273. //
  274. // Save
  275. //
  276. //////////////////////////////////////////////////////////////////////////
  277. bool
  278. SakuraBossGameObjDef::Save (ChunkSaveClass &csave)
  279. {
  280. csave.Begin_Chunk (CHUNKID_DEF_PARENT);
  281. VehicleGameObjDef::Save (csave);
  282. csave.End_Chunk ();
  283. csave.Begin_Chunk (CHUNKID_DEF_VARIABLES);
  284. Save_Variables (csave);
  285. csave.End_Chunk();
  286. csave.Begin_Chunk (CHUNKID_DEF_ROCKET_DEFENSEOBJ_DEF);
  287. RocketsDefense.Save (csave);
  288. csave.End_Chunk();
  289. return true;
  290. }
  291. //////////////////////////////////////////////////////////////////////////
  292. //
  293. // Load
  294. //
  295. //////////////////////////////////////////////////////////////////////////
  296. bool
  297. SakuraBossGameObjDef::Load (ChunkLoadClass &cload)
  298. {
  299. while (cload.Open_Chunk ()) {
  300. switch(cload.Cur_Chunk_ID ()) {
  301. case CHUNKID_DEF_PARENT:
  302. VehicleGameObjDef::Load (cload);
  303. break;
  304. case CHUNKID_DEF_VARIABLES:
  305. Load_Variables (cload);
  306. break;
  307. case CHUNKID_DEF_ROCKET_DEFENSEOBJ_DEF:
  308. RocketsDefense.Load (cload);
  309. break;
  310. default:
  311. Debug_Say (("Unrecognized SakuraBossGameObjDef chunkID\n"));
  312. break;
  313. }
  314. cload.Close_Chunk ();
  315. }
  316. return true;
  317. }
  318. ///////////////////////////////////////////////////////////////////////////
  319. //
  320. // Save_Variables
  321. //
  322. ///////////////////////////////////////////////////////////////////////////
  323. void
  324. SakuraBossGameObjDef::Save_Variables (ChunkSaveClass &csave)
  325. {
  326. WRITE_MICRO_CHUNK (csave, VARID_DEF_GATLING_DEF_ID, GattlingGunDefID);
  327. WRITE_MICRO_CHUNK (csave, VARID_DEF_ROCKET_DEF_ID, RocketLauncherDefID);
  328. WRITE_MICRO_CHUNK (csave, VARID_DEF_GATLING_REV_SOUND_ID, GattlingGunRevSoundDefID);
  329. WRITE_MICRO_CHUNK (csave, VARID_DEF_ROCKET_DOOR_SOUND_ID, RocketDoorOpenSoundID);
  330. WRITE_MICRO_CHUNK (csave, VARID_DEF_ROCKET_DESTROYED_EXPLOSION_ID, RocketDestroyedExplosionID);
  331. return ;
  332. }
  333. ///////////////////////////////////////////////////////////////////////////
  334. //
  335. // Load_Variables
  336. //
  337. ///////////////////////////////////////////////////////////////////////////
  338. void
  339. SakuraBossGameObjDef::Load_Variables (ChunkLoadClass &cload)
  340. {
  341. while (cload.Open_Micro_Chunk ()) {
  342. switch (cload.Cur_Micro_Chunk_ID ()) {
  343. READ_MICRO_CHUNK (cload, VARID_DEF_GATLING_DEF_ID, GattlingGunDefID);
  344. READ_MICRO_CHUNK (cload, VARID_DEF_ROCKET_DEF_ID, RocketLauncherDefID);
  345. READ_MICRO_CHUNK (cload, VARID_DEF_GATLING_REV_SOUND_ID, GattlingGunRevSoundDefID);
  346. READ_MICRO_CHUNK (cload, VARID_DEF_ROCKET_DOOR_SOUND_ID, RocketDoorOpenSoundID);
  347. READ_MICRO_CHUNK (cload, VARID_DEF_ROCKET_DESTROYED_EXPLOSION_ID, RocketDestroyedExplosionID);
  348. default:
  349. Debug_Say (("Unrecognized SakuraBossGameObjDef Variable chunkID %d\n", cload.Cur_Micro_Chunk_ID ()));
  350. break;
  351. }
  352. cload.Close_Micro_Chunk ();
  353. }
  354. return ;
  355. }
  356. ///////////////////////////////////////////////////////////////////////////
  357. //
  358. // Get_Factory
  359. //
  360. ///////////////////////////////////////////////////////////////////////////
  361. const PersistFactoryClass &
  362. SakuraBossGameObjDef::Get_Factory (void) const
  363. {
  364. return _SakuraBossGameObjDefPersistFactory;
  365. }
  366. /*
  367. **
  368. ** Start of SakuraBossGameObj
  369. **
  370. */
  371. ///////////////////////////////////////////////////////////////////////////
  372. //
  373. // SakuraBossGameObj
  374. //
  375. ///////////////////////////////////////////////////////////////////////////
  376. SakuraBossGameObj::SakuraBossGameObj (void) :
  377. GattlingGun (NULL),
  378. RockerLauncherLeft (NULL),
  379. RockerLauncherRight (NULL),
  380. AvailableWeapons (WEAPON_ALL),
  381. OverallState (STATE_ATTACK_LIFT_AREA),
  382. NextOverallState (STATE_ATTACK_LIFT_AREA),
  383. RocketLauncherState (ROCKET_STATE_CLOSED),
  384. GattlingGunState (GATLING_STATE_NORMAL),
  385. SakuraState (SAKURA_STATE_NORMAL),
  386. VehicleState (VEHICLE_STATE_HOVERING),
  387. GattlingGunStateTimeLeft (0),
  388. SakuraTauntTimeLeft (0),
  389. CurrentMuzzleIndex (0),
  390. RocketLauncherStateTimeLeft (0),
  391. OverallStateTimeLeft (10.0F),
  392. TargetTimeLeft (0),
  393. FaceTargetInTransition (false),
  394. CurrentHealth (1.0F),
  395. TargetAngle (0),
  396. IsAttacking (false),
  397. Path (NULL),
  398. ChopperTiltBoneIndex (0),
  399. TiltAngle (0),
  400. AvailableTaunts (0xFFFFFF)
  401. {
  402. Path = new PathClass;
  403. Shuffle_Taunt_List ();
  404. TauntList[0] = 1;//IDS_SAKURA_BOSS_TAUNT1;
  405. TauntList[1] = 2;//IDS_SAKURA_BOSS_TAUNT2;
  406. TauntList[2] = 3;//IDS_SAKURA_BOSS_TAUNT3;
  407. TauntList[3] = 4;//IDS_SAKURA_BOSS_TAUNT4;
  408. TauntList[4] = 5;//IDS_SAKURA_BOSS_TAUNT5;
  409. TauntList[5] = 6;//IDS_SAKURA_BOSS_TAUNT6;
  410. return ;
  411. }
  412. ///////////////////////////////////////////////////////////////////////////
  413. //
  414. // ~SakuraBossGameObj
  415. //
  416. ///////////////////////////////////////////////////////////////////////////
  417. SakuraBossGameObj::~SakuraBossGameObj (void)
  418. {
  419. REF_PTR_RELEASE (Path);
  420. return ;
  421. }
  422. ///////////////////////////////////////////////////////////////////////////
  423. //
  424. // Get_Factory
  425. //
  426. ///////////////////////////////////////////////////////////////////////////
  427. const PersistFactoryClass &
  428. SakuraBossGameObj::Get_Factory (void) const
  429. {
  430. return _SakuraBossGameObjPersistFactory;
  431. }
  432. ////////////////////////////////////////////////////////////////
  433. //
  434. // Init
  435. //
  436. ////////////////////////////////////////////////////////////////
  437. void SakuraBossGameObj::Init( void )
  438. {
  439. Init( Get_Definition() );
  440. }
  441. ///////////////////////////////////////////////////////////////////////////
  442. //
  443. // Init
  444. //
  445. ///////////////////////////////////////////////////////////////////////////
  446. void
  447. SakuraBossGameObj::Init (const SakuraBossGameObjDef &definition)
  448. {
  449. VehicleGameObj::Init (definition);
  450. //
  451. // Initialize the defense for the left and right rocket launchers
  452. //
  453. // Pass a NULL for the owner so we don't think we have killed Sakura too early
  454. LeftRocketDefenseObject.Init (definition.RocketsDefense, NULL);
  455. RightRocketDefenseObject.Init (definition.RocketsDefense, NULL);
  456. //
  457. // Lookup the weapon definitions
  458. //
  459. const WeaponDefinitionClass *gatling_gun_def = WeaponManager::Find_Weapon_Definition (definition.GattlingGunDefID);
  460. const WeaponDefinitionClass *rocket_launcher_def = WeaponManager::Find_Weapon_Definition (definition.RocketLauncherDefID);
  461. //
  462. // Create the gatling gun
  463. //
  464. if (gatling_gun_def != NULL) {
  465. GattlingGun = new WeaponClass (gatling_gun_def);
  466. GattlingGun->Set_Owner (this);
  467. GattlingGun->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  468. }
  469. //
  470. // Create the rocket launchers
  471. //
  472. if (rocket_launcher_def != NULL) {
  473. RockerLauncherLeft = new WeaponClass (rocket_launcher_def);
  474. RockerLauncherRight = new WeaponClass (rocket_launcher_def);
  475. RockerLauncherLeft->Set_Owner (this);
  476. RockerLauncherRight->Set_Owner (this);
  477. RockerLauncherLeft->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  478. RockerLauncherRight->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  479. }
  480. //
  481. // Make sure we have an animation controller
  482. //
  483. if (Get_Anim_Control () == NULL) {
  484. Set_Anim_Control (new SimpleAnimControlClass);
  485. Get_Anim_Control ()->Set_Model (Peek_Model ());
  486. }
  487. //
  488. // Make sure the engine is on
  489. //
  490. Enable_Engine (true);
  491. Pilot.Initialize (this);
  492. Pilot.Set_Arrived_Dist (1.0F);
  493. Pilot.Set_Aggressivness (1.0F);
  494. Pilot.Set_Is_Exact_Z_Important (true);
  495. //
  496. // Lookup the chopper-tilting bone index
  497. //
  498. ChopperTiltBoneIndex = Peek_Model ()->Get_Bone_Index ("V_CHOPPER");
  499. return ;
  500. }
  501. ///////////////////////////////////////////////////////////////////////////
  502. //
  503. // Get_Definition
  504. //
  505. ///////////////////////////////////////////////////////////////////////////
  506. const SakuraBossGameObjDef &
  507. SakuraBossGameObj::Get_Definition (void) const
  508. {
  509. return (const SakuraBossGameObjDef &)BaseGameObj::Get_Definition ();
  510. }
  511. ///////////////////////////////////////////////////////////////////////////
  512. //
  513. // Save
  514. //
  515. ///////////////////////////////////////////////////////////////////////////
  516. bool
  517. SakuraBossGameObj::Save (ChunkSaveClass & csave)
  518. {
  519. csave.Begin_Chunk (CHUNKID_PARENT);
  520. VehicleGameObj::Save (csave);
  521. csave.End_Chunk ();
  522. csave.Begin_Chunk (CHUNKID_ROCKETL_DEFENSE_OBJ);
  523. LeftRocketDefenseObject.Save (csave);
  524. csave.End_Chunk ();
  525. csave.Begin_Chunk (CHUNKID_ROCKETR_DEFENSE_OBJ);
  526. RightRocketDefenseObject.Save (csave);
  527. csave.End_Chunk ();
  528. csave.Begin_Chunk (CHUNKID_LAST_DAMAGER);
  529. LastDamager.Save (csave);
  530. csave.End_Chunk ();
  531. csave.Begin_Chunk (CHUNKID_CURR_TARGET);
  532. CurrentTarget.Save (csave);
  533. csave.End_Chunk ();
  534. csave.Begin_Chunk (CHUNKID_PILOT);
  535. Pilot.Save (csave);
  536. csave.End_Chunk ();
  537. csave.Begin_Chunk (CHUNKID_VARIABLES);
  538. Save_Variables (csave);
  539. csave.End_Chunk ();
  540. if (Path != NULL) {
  541. csave.Begin_Chunk (CHUNKID_PATH);
  542. Path->Save (csave);
  543. csave.End_Chunk();
  544. }
  545. return true;
  546. }
  547. ///////////////////////////////////////////////////////////////////////////
  548. //
  549. // Load
  550. //
  551. ///////////////////////////////////////////////////////////////////////////
  552. bool
  553. SakuraBossGameObj::Load (ChunkLoadClass &cload)
  554. {
  555. while (cload.Open_Chunk ()) {
  556. switch(cload.Cur_Chunk_ID ()) {
  557. case CHUNKID_PARENT:
  558. VehicleGameObj::Load (cload);
  559. break;
  560. case CHUNKID_ROCKETL_DEFENSE_OBJ:
  561. LeftRocketDefenseObject.Load (cload);
  562. break;
  563. case CHUNKID_ROCKETR_DEFENSE_OBJ:
  564. RightRocketDefenseObject.Load (cload);
  565. break;
  566. case CHUNKID_LAST_DAMAGER:
  567. LastDamager.Load (cload);
  568. break;
  569. case CHUNKID_CURR_TARGET:
  570. CurrentTarget.Load (cload);
  571. break;
  572. case CHUNKID_PILOT:
  573. Pilot.Load (cload);
  574. break;
  575. case CHUNKID_PATH:
  576. {
  577. Path = new PathClass;
  578. Path->Load (cload);
  579. }
  580. break;
  581. case CHUNKID_VARIABLES:
  582. Load_Variables (cload);
  583. break;
  584. default:
  585. Debug_Say (("Unrecognized SakuraBossGameObj chunk ID\n"));
  586. break;
  587. }
  588. cload.Close_Chunk ();
  589. }
  590. SaveLoadSystemClass::Register_Post_Load_Callback (this);
  591. return true;
  592. }
  593. ///////////////////////////////////////////////////////////////////////////
  594. //
  595. // On_Post_Load
  596. //
  597. ///////////////////////////////////////////////////////////////////////////
  598. void
  599. SakuraBossGameObj::On_Post_Load (void)
  600. {
  601. VehicleGameObj::On_Post_Load ();
  602. //
  603. // Initialize the defense for the left and right rocket launchers
  604. //
  605. // Pass a NULL for the owner so we don't think we have killed Sakura too early
  606. //LeftRocketDefenseObject.Init (Get_Definition ().RocketsDefense, NULL);
  607. //RightRocketDefenseObject.Init (Get_Definition ().RocketsDefense, NULL);
  608. //
  609. // Lookup the weapon definitions
  610. //
  611. const WeaponDefinitionClass *gatling_gun_def = WeaponManager::Find_Weapon_Definition (Get_Definition ().GattlingGunDefID);
  612. const WeaponDefinitionClass *rocket_launcher_def = WeaponManager::Find_Weapon_Definition (Get_Definition ().RocketLauncherDefID);
  613. //
  614. // Create the gatling gun
  615. //
  616. if (gatling_gun_def != NULL) {
  617. GattlingGun = new WeaponClass (gatling_gun_def);
  618. GattlingGun->Set_Owner (this);
  619. GattlingGun->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  620. }
  621. //
  622. // Create the rocket launchers
  623. //
  624. if (rocket_launcher_def != NULL) {
  625. RockerLauncherLeft = new WeaponClass (rocket_launcher_def);
  626. RockerLauncherRight = new WeaponClass (rocket_launcher_def);
  627. RockerLauncherLeft->Set_Owner (this);
  628. RockerLauncherRight->Set_Owner (this);
  629. RockerLauncherLeft->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  630. RockerLauncherRight->Init_Muzzle_Flash (Peek_Physical_Object ()->Peek_Model ());
  631. }
  632. //
  633. // Make sure the engine is on
  634. //
  635. //Enable_Engine (true);
  636. //Pilot.Initialize (this);
  637. //Pilot.Set_Arrived_Dist (1.0F);
  638. //Pilot.Set_Aggressivness (1.0F);
  639. //
  640. // Lookup the chopper-tilting bone index
  641. //
  642. ChopperTiltBoneIndex = Peek_Model ()->Get_Bone_Index ("V_CHOPPER");
  643. return ;
  644. }
  645. ///////////////////////////////////////////////////////////////////////////
  646. //
  647. // Save_Variables
  648. //
  649. ///////////////////////////////////////////////////////////////////////////
  650. void
  651. SakuraBossGameObj::Save_Variables (ChunkSaveClass &csave)
  652. {
  653. WRITE_MICRO_CHUNK (csave, VARID_AVAILABLE_WEAPONS, AvailableWeapons);
  654. WRITE_MICRO_CHUNK (csave, VARID_OVERALL_STATE, OverallState);
  655. WRITE_MICRO_CHUNK (csave, VARID_NEXT_OVERALL_STATE, NextOverallState);
  656. WRITE_MICRO_CHUNK (csave, VARID_FACE_TARGET_IN_TRANSITION, FaceTargetInTransition);
  657. WRITE_MICRO_CHUNK (csave, VARID_ROCKET_LAUNCHER_STATE, RocketLauncherState);
  658. WRITE_MICRO_CHUNK (csave, VARID_GATTLING_GUN_STATE, GattlingGunState);
  659. WRITE_MICRO_CHUNK (csave, VARID_SAKURA_STATE, SakuraState);
  660. WRITE_MICRO_CHUNK (csave, VARID_VEHICLE_STATE, VehicleState);
  661. WRITE_MICRO_CHUNK (csave, VARID_MOVE_TO_LOCATION, MoveToLocation);
  662. WRITE_MICRO_CHUNK (csave, VARID_GATTLING_GUN_STATE_TIME_LEFT, GattlingGunStateTimeLeft);
  663. WRITE_MICRO_CHUNK (csave, VARID_SAKURA_TAUNT_TIME_LEFT, SakuraTauntTimeLeft);
  664. WRITE_MICRO_CHUNK (csave, VARID_ROCKET_LAUNCHER_STATE_TIME_LEFT, RocketLauncherStateTimeLeft);
  665. WRITE_MICRO_CHUNK (csave, VARID_OVERALL_STATE_TIME_LEFT, OverallStateTimeLeft);
  666. WRITE_MICRO_CHUNK (csave, VARID_TARGET_TIME_LEFT, TargetTimeLeft);
  667. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_HEALTH, CurrentHealth);
  668. WRITE_MICRO_CHUNK (csave, VARID_TARGET_ANGLE, TargetAngle);
  669. WRITE_MICRO_CHUNK (csave, VARID_TARGET_POS, TargetPos);
  670. WRITE_MICRO_CHUNK (csave, VARID_IS_ATTACKING, IsAttacking);
  671. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_MUZZLE_TM, CurrentMuzzleTM);
  672. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_MUZZLE_INDEX, CurrentMuzzleIndex);
  673. WRITE_MICRO_CHUNK (csave, VARID_TILT_ANGLE, TiltAngle);
  674. WRITE_MICRO_CHUNK (csave, VARID_CHOPPER_TILT_BONE_INDEX, ChopperTiltBoneIndex);
  675. return ;
  676. }
  677. ///////////////////////////////////////////////////////////////////////////
  678. //
  679. // Load_Variables
  680. //
  681. ///////////////////////////////////////////////////////////////////////////
  682. void
  683. SakuraBossGameObj::Load_Variables (ChunkLoadClass &cload)
  684. {
  685. while (cload.Open_Micro_Chunk ()) {
  686. switch (cload.Cur_Micro_Chunk_ID ()) {
  687. READ_MICRO_CHUNK (cload, VARID_AVAILABLE_WEAPONS, AvailableWeapons);
  688. READ_MICRO_CHUNK (cload, VARID_OVERALL_STATE, OverallState);
  689. READ_MICRO_CHUNK (cload, VARID_NEXT_OVERALL_STATE, NextOverallState);
  690. READ_MICRO_CHUNK (cload, VARID_FACE_TARGET_IN_TRANSITION, FaceTargetInTransition);
  691. READ_MICRO_CHUNK (cload, VARID_ROCKET_LAUNCHER_STATE, RocketLauncherState);
  692. READ_MICRO_CHUNK (cload, VARID_GATTLING_GUN_STATE, GattlingGunState);
  693. READ_MICRO_CHUNK (cload, VARID_SAKURA_STATE, SakuraState);
  694. READ_MICRO_CHUNK (cload, VARID_VEHICLE_STATE, VehicleState);
  695. READ_MICRO_CHUNK (cload, VARID_MOVE_TO_LOCATION, MoveToLocation);
  696. READ_MICRO_CHUNK (cload, VARID_GATTLING_GUN_STATE_TIME_LEFT, GattlingGunStateTimeLeft);
  697. READ_MICRO_CHUNK (cload, VARID_SAKURA_TAUNT_TIME_LEFT, SakuraTauntTimeLeft);
  698. READ_MICRO_CHUNK (cload, VARID_ROCKET_LAUNCHER_STATE_TIME_LEFT, RocketLauncherStateTimeLeft);
  699. READ_MICRO_CHUNK (cload, VARID_OVERALL_STATE_TIME_LEFT, OverallStateTimeLeft);
  700. READ_MICRO_CHUNK (cload, VARID_TARGET_TIME_LEFT, TargetTimeLeft);
  701. READ_MICRO_CHUNK (cload, VARID_CURRENT_HEALTH, CurrentHealth);
  702. READ_MICRO_CHUNK (cload, VARID_TARGET_ANGLE, TargetAngle);
  703. READ_MICRO_CHUNK (cload, VARID_TARGET_POS, TargetPos);
  704. READ_MICRO_CHUNK (cload, VARID_IS_ATTACKING, IsAttacking);
  705. READ_MICRO_CHUNK (cload, VARID_CURRENT_MUZZLE_TM, CurrentMuzzleTM);
  706. READ_MICRO_CHUNK (cload, VARID_CURRENT_MUZZLE_INDEX, CurrentMuzzleIndex);
  707. READ_MICRO_CHUNK (cload, VARID_TILT_ANGLE, TiltAngle);
  708. READ_MICRO_CHUNK (cload, VARID_CHOPPER_TILT_BONE_INDEX, ChopperTiltBoneIndex);
  709. default:
  710. Debug_Say (("Unrecognized SakuraBossGameObj Variable chunkID %d\n", cload.Cur_Micro_Chunk_ID ()));
  711. break;
  712. }
  713. cload.Close_Micro_Chunk ();
  714. }
  715. return ;
  716. }
  717. ///////////////////////////////////////////////////////////////////////////
  718. //
  719. // Apply_Control
  720. //
  721. ///////////////////////////////////////////////////////////////////////////
  722. void
  723. SakuraBossGameObj::Apply_Control (void)
  724. {
  725. VehicleGameObj::Apply_Control ();
  726. return ;
  727. }
  728. ///////////////////////////////////////////////////////////////////////////
  729. //
  730. // Update_Decision_Data
  731. //
  732. ///////////////////////////////////////////////////////////////////////////
  733. void
  734. SakuraBossGameObj::Update_Decision_Data (void)
  735. {
  736. //
  737. // Update our current health
  738. //
  739. DefenseObjectClass *defense_object = Get_Defense_Object ();
  740. if (defense_object != NULL) {
  741. CurrentHealth = defense_object->Get_Health () / defense_object->Get_Health_Max ();
  742. }
  743. //
  744. // Determine if any of our weapons are currently attacking the player
  745. //
  746. IsAttacking = false;
  747. if ( RocketLauncherState != ROCKET_STATE_CLOSED ||
  748. GattlingGunState != GATLING_STATE_NORMAL)
  749. {
  750. IsAttacking = true;
  751. }
  752. //
  753. // Update information about our current target
  754. //
  755. TargetAngle = 0;
  756. TargetPos.Set (0, 0, 0);
  757. if (CurrentTarget != NULL) {
  758. //
  759. // Get the target's position
  760. //
  761. CurrentTarget.Get_Ptr ()->Get_Position (&TargetPos);
  762. //
  763. // Get the target's position relative to ours
  764. //
  765. Vector3 obj_space_target;
  766. Matrix3D::Inverse_Transform_Vector (Get_Transform (), TargetPos, &obj_space_target);
  767. //
  768. // What angle is the target off from our current facing?
  769. //
  770. TargetAngle = WWMath::Atan2 (obj_space_target.Y, obj_space_target.X);
  771. TargetAngle = WWMath::Wrap (TargetAngle, DEG_TO_RADF (-180), DEG_TO_RADF (180));
  772. }
  773. return ;
  774. }
  775. ///////////////////////////////////////////////////////////////////////////
  776. //
  777. // Think
  778. //
  779. ///////////////////////////////////////////////////////////////////////////
  780. void
  781. SakuraBossGameObj::Think (void)
  782. {
  783. Pilot.Think ();
  784. VehicleGameObj::Think ();
  785. WWPROFILE( "Sakura Think" );
  786. //
  787. // Find a target if necessary
  788. //
  789. Update_Target ();
  790. //
  791. // Update our 'snapshot' of the world's (and our) state
  792. //
  793. Update_Decision_Data ();
  794. //
  795. // Update the individual parts that make-up the boss
  796. //
  797. Update_Rocket_State ();
  798. Update_Gattling_Gun_State ();
  799. Update_Sakura_State ();
  800. Update_Vehicle_State ();
  801. Update_Tilt ();
  802. //
  803. // Now, look at the bigger picture and determine
  804. // what each part should be doing
  805. //
  806. Decide_New_Overall_State ();
  807. Update_Overall_State ();
  808. //
  809. // Update the weapons
  810. //
  811. if (RockerLauncherLeft != NULL) {
  812. CurrentMuzzleIndex = ROCKET_LEFT;
  813. RockerLauncherLeft->Update ();
  814. RockerLauncherLeft->Set_Total_Rounds (1000);
  815. }
  816. if (RockerLauncherRight != NULL) {
  817. CurrentMuzzleIndex = ROCKET_RIGHT;
  818. RockerLauncherRight->Update ();
  819. RockerLauncherRight->Set_Total_Rounds (1000);
  820. }
  821. if (GattlingGun != NULL) {
  822. GattlingGun->Update ();
  823. GattlingGun->Set_Total_Rounds (1000);
  824. }
  825. return ;
  826. }
  827. ///////////////////////////////////////////////////////////////////////////
  828. //
  829. // Request_Overall_State
  830. //
  831. ///////////////////////////////////////////////////////////////////////////
  832. void
  833. SakuraBossGameObj::Request_Overall_State (int new_state)
  834. {
  835. if (OverallState != STATE_IN_TRANSITION && OverallState != new_state) {
  836. switch (new_state)
  837. {
  838. case STATE_CIRCLE_POWER_PLANT:
  839. if ( OverallState == STATE_CIRCLE_REFINERY ||
  840. OverallState == STATE_ATTACK_LIFT_AREA)
  841. {
  842. Do_Waypath (WID_REFINERY_TO_PPLANT);
  843. Set_Overall_State (STATE_IN_TRANSITION);
  844. } else if ( OverallState == STATE_LONG_STRAFING_RUN ||
  845. OverallState == STATE_LAND_IN_TIBERIUM_FIELD ||
  846. OverallState == STATE_LONG_STRAFING_RUN2)
  847. {
  848. Set_Overall_State (STATE_CIRCLE_POWER_PLANT);
  849. } else if (OverallState == STATE_VALLEY_STRAFE) {
  850. Do_Waypath (WID_VSTRAFE_TO_PPLANT);
  851. Set_Overall_State (STATE_IN_TRANSITION);
  852. }
  853. break;
  854. case STATE_CIRCLE_REFINERY:
  855. if ( OverallState == STATE_CIRCLE_POWER_PLANT ||
  856. OverallState == STATE_LAND_IN_TIBERIUM_FIELD ||
  857. OverallState == STATE_LONG_STRAFING_RUN2)
  858. {
  859. Do_Waypath (WID_PPLANT_TO_REFINERY);
  860. Set_Overall_State (STATE_IN_TRANSITION);
  861. } else if (OverallState == STATE_ATTACK_LIFT_AREA) {
  862. Set_Overall_State (STATE_CIRCLE_REFINERY);
  863. } else if (OverallState == STATE_VALLEY_STRAFE) {
  864. Do_Waypath (WID_VSTRAFE_TO_REFINERY);
  865. Set_Overall_State (STATE_IN_TRANSITION);
  866. } else if (OverallState == STATE_LONG_STRAFING_RUN) {
  867. Do_Waypath (WID_LSTRAFE_TO_REFINERY);
  868. Set_Overall_State (STATE_IN_TRANSITION);
  869. }
  870. break;
  871. case STATE_LAND_IN_TIBERIUM_FIELD:
  872. if ( OverallState == STATE_CIRCLE_REFINERY ||
  873. OverallState == STATE_ATTACK_LIFT_AREA) {
  874. Do_Waypath (WID_REFINERY_TO_TIBFIELD);
  875. Set_Overall_State (STATE_IN_TRANSITION);
  876. } else if ( OverallState == STATE_CIRCLE_POWER_PLANT ||
  877. OverallState == STATE_LONG_STRAFING_RUN2)
  878. {
  879. Set_Overall_State (STATE_LAND_IN_TIBERIUM_FIELD);
  880. } else if (OverallState == STATE_LONG_STRAFING_RUN) {
  881. Do_Waypath (WID_LSTRAFE_TO_TIBFIELD);
  882. Set_Overall_State (STATE_IN_TRANSITION);
  883. } else if (OverallState == STATE_VALLEY_STRAFE) {
  884. Do_Waypath (WID_VSTRAFE_TO_TIBFIELD);
  885. Set_Overall_State (STATE_IN_TRANSITION);
  886. }
  887. break;
  888. case STATE_VALLEY_STRAFE:
  889. if ( OverallState == STATE_CIRCLE_REFINERY ||
  890. OverallState == STATE_CIRCLE_POWER_PLANT ||
  891. OverallState == STATE_LONG_STRAFING_RUN)
  892. {
  893. Set_Overall_State (STATE_VALLEY_STRAFE);
  894. } else if (OverallState == STATE_ATTACK_LIFT_AREA) {
  895. Set_Overall_State (STATE_VALLEY_STRAFE);
  896. } else if (OverallState == STATE_LONG_STRAFING_RUN2) {
  897. Do_Waypath (WID_LSTRAF2_TO_VSTRAFE);
  898. Set_Overall_State (STATE_IN_TRANSITION);
  899. } else if (OverallState == STATE_LAND_IN_TIBERIUM_FIELD) {
  900. Do_Waypath (WID_TIBFIELD_TO_VSTRAFE);
  901. Set_Overall_State (STATE_IN_TRANSITION);
  902. }
  903. break;
  904. case STATE_ATTACK_LIFT_AREA:
  905. if ( OverallState == STATE_CIRCLE_REFINERY ||
  906. OverallState == STATE_LONG_STRAFING_RUN ||
  907. OverallState == STATE_VALLEY_STRAFE)
  908. {
  909. Do_Waypath (WID_TO_LIFT_AREA_SOUTH);
  910. Set_Overall_State (STATE_IN_TRANSITION);
  911. } else if ( OverallState == STATE_CIRCLE_POWER_PLANT ||
  912. OverallState == STATE_LONG_STRAFING_RUN2 ||
  913. OverallState == STATE_LAND_IN_TIBERIUM_FIELD)
  914. {
  915. Do_Waypath (WID_TO_LIFT_AREA_NORTH);
  916. Set_Overall_State (STATE_IN_TRANSITION);
  917. }
  918. break;
  919. case STATE_LONG_STRAFING_RUN2:
  920. if ( OverallState == STATE_CIRCLE_REFINERY ||
  921. OverallState == STATE_ATTACK_LIFT_AREA) {
  922. Do_Waypath (WID_REFINERY_TO_LSTRAFE2);
  923. Set_Overall_State (STATE_IN_TRANSITION);
  924. } else if (OverallState == STATE_VALLEY_STRAFE) {
  925. Do_Waypath (WID_VSTRAFE_TO_LSTRAFE2);
  926. Set_Overall_State (STATE_IN_TRANSITION);
  927. } else if ( OverallState == STATE_CIRCLE_POWER_PLANT ||
  928. OverallState == STATE_LAND_IN_TIBERIUM_FIELD ||
  929. OverallState == STATE_LONG_STRAFING_RUN)
  930. {
  931. Set_Overall_State (STATE_LONG_STRAFING_RUN2);
  932. }
  933. break;
  934. case STATE_LONG_STRAFING_RUN:
  935. if ( OverallState == STATE_CIRCLE_REFINERY ||
  936. OverallState == STATE_ATTACK_LIFT_AREA)
  937. {
  938. Do_Waypath (WID_REFINERY_TO_LSTRAFE);
  939. Set_Overall_State (STATE_IN_TRANSITION);
  940. } else if (OverallState == STATE_VALLEY_STRAFE) {
  941. Do_Waypath (WID_VSTRAFE_TO_LSTRAFE);
  942. Set_Overall_State (STATE_IN_TRANSITION);
  943. } else if ( OverallState == STATE_CIRCLE_POWER_PLANT ||
  944. OverallState == STATE_LAND_IN_TIBERIUM_FIELD ||
  945. OverallState == STATE_LONG_STRAFING_RUN2)
  946. {
  947. Set_Overall_State (STATE_LONG_STRAFING_RUN);
  948. }
  949. break;
  950. }
  951. NextOverallState = new_state;
  952. }
  953. return ;
  954. }
  955. ///////////////////////////////////////////////////////////////////////////
  956. //
  957. // Set_Overall_State
  958. //
  959. ///////////////////////////////////////////////////////////////////////////
  960. void
  961. SakuraBossGameObj::Set_Overall_State (int new_state)
  962. {
  963. if (OverallState != new_state) {
  964. switch (new_state)
  965. {
  966. case STATE_CIRCLE_POWER_PLANT:
  967. Pilot.Set_Destination (Vector3 (-63.34F, 21.26F, 26.0F));
  968. Pilot.Set_Mode (PilotClass::MODE_CIRCLE_POINT);
  969. Pilot.Set_Circle_Bounds (DEG_TO_RADF (100), DEG_TO_RADF (225));
  970. Pilot.Set_Circle_Dist (30);
  971. break;
  972. case STATE_CIRCLE_REFINERY:
  973. {
  974. Vector3 destination (-166.0F, 40.13F, 42.23F);
  975. Pilot.Set_Destination (destination);
  976. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  977. destination += Vector3 (0, -10, 0);
  978. Pilot.Set_Target (&destination);
  979. }
  980. break;
  981. case STATE_ATTACK_LIFT_AREA:
  982. {
  983. Vector3 destination (-161.78F, 79.03F, 18.88F);
  984. Pilot.Set_Destination (destination);
  985. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  986. destination.X += 10;
  987. Pilot.Set_Target (&destination);
  988. }
  989. break;
  990. case STATE_LONG_STRAFING_RUN2:
  991. Pilot.Set_Max_Speed (100.0F);
  992. Do_Waypath (WID_LONG_STRAFING_RUN2);
  993. break;
  994. case STATE_LONG_STRAFING_RUN:
  995. Pilot.Set_Max_Speed (100.0F);
  996. Do_Waypath (WID_LONG_STRAFING_RUN);
  997. break;
  998. case STATE_VALLEY_STRAFE:
  999. {
  1000. Vector3 destination (-84.33F, 77.99F, 26.77F);
  1001. Pilot.Set_Destination (destination);
  1002. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  1003. }
  1004. break;
  1005. case STATE_LAND_IN_TIBERIUM_FIELD:
  1006. {
  1007. Vector3 destination (-107.0F, -10.0F, 15.0F);
  1008. Pilot.Set_Destination (destination);
  1009. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  1010. }
  1011. break;
  1012. case STATE_IN_TRANSITION:
  1013. FaceTargetInTransition = false;
  1014. break;
  1015. }
  1016. OverallState = new_state;
  1017. //
  1018. // Force a new state change in a little while
  1019. //
  1020. OverallStateTimeLeft = WWMath::Random_Float (5.0F, 10.0F);
  1021. }
  1022. return ;
  1023. }
  1024. ///////////////////////////////////////////////////////////////////////////
  1025. //
  1026. // Should_Change_Overall_State
  1027. //
  1028. ///////////////////////////////////////////////////////////////////////////
  1029. bool
  1030. SakuraBossGameObj::Should_Change_Overall_State (void)
  1031. {
  1032. if (OverallState == STATE_IN_TRANSITION || VehicleState == VEHICLE_STATE_MOVING) {
  1033. return false;
  1034. }
  1035. bool retval = false;
  1036. //
  1037. // We need to change states if our timer has told us to...
  1038. //
  1039. OverallStateTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1040. if (OverallStateTimeLeft <= 0) {
  1041. retval = true;
  1042. } else if (CurrentTarget != NULL) {
  1043. switch (OverallState)
  1044. {
  1045. case STATE_CIRCLE_POWER_PLANT:
  1046. if (TargetPos.Z < 12.0F) {
  1047. retval = true;
  1048. }
  1049. break;
  1050. case STATE_CIRCLE_REFINERY:
  1051. if (TargetPos.X > -91.0F || TargetPos.Y > 69.0F) {
  1052. retval = true;
  1053. }
  1054. break;
  1055. case STATE_ATTACK_LIFT_AREA:
  1056. if (TargetPos.X > -100.0F || TargetPos.Y < 50.0F) {
  1057. retval = true;
  1058. }
  1059. break;
  1060. case STATE_LAND_IN_TIBERIUM_FIELD:
  1061. if (TargetPos.X > -119.0F && TargetPos.X < -95.0F && TargetPos.Y < 0.0F) {
  1062. retval = true;
  1063. }
  1064. break;
  1065. case STATE_VALLEY_STRAFE:
  1066. if (TargetPos.Y < 20.0F && (TargetPos.X < -138.0F || TargetPos.X > -78.0F)) {
  1067. retval = true;
  1068. }
  1069. break;
  1070. case STATE_LONG_STRAFING_RUN:
  1071. if (TargetPos.X > -100.0F && TargetPos.Y > 35.0F) {
  1072. retval = true;
  1073. }
  1074. break;
  1075. case STATE_LONG_STRAFING_RUN2:
  1076. if (TargetPos.X > -80.0F || TargetPos.Y < 10.0F) {
  1077. retval = true;
  1078. }
  1079. break;
  1080. }
  1081. }
  1082. return retval;
  1083. }
  1084. ///////////////////////////////////////////////////////////////////////////
  1085. //
  1086. // Decide_New_Overall_State
  1087. //
  1088. ///////////////////////////////////////////////////////////////////////////
  1089. void
  1090. SakuraBossGameObj::Decide_New_Overall_State (void)
  1091. {
  1092. //
  1093. // Do we need to change state now?
  1094. //
  1095. if (Should_Change_Overall_State ()) {
  1096. //
  1097. // If we are going to change states, then stop attacking the target
  1098. //
  1099. Stop_Attacking ();
  1100. //
  1101. // If we've been injured enough, bump up the long strafing run possibility
  1102. //
  1103. if (CurrentHealth < 0.45F && FreeRandom.Get_Int (4) == 1) {
  1104. //
  1105. // Flip a coin and do a strafing run
  1106. //
  1107. if (FreeRandom.Get_Int (2) == 0) {
  1108. Request_Overall_State (STATE_LONG_STRAFING_RUN);
  1109. } else {
  1110. Request_Overall_State (STATE_LONG_STRAFING_RUN2);
  1111. }
  1112. } else {
  1113. //
  1114. // Based on this position determine which state we want to go to...
  1115. //
  1116. if ( TargetPos.X < -104.0F &&
  1117. TargetPos.Y > 69.0F && TargetPos.Y < 85.8F)
  1118. {
  1119. Request_Overall_State (STATE_ATTACK_LIFT_AREA);
  1120. } else if ( OverallState != STATE_CIRCLE_REFINERY &&
  1121. TargetPos.X < -125.0F && TargetPos.Y > -19.0F) {
  1122. Request_Overall_State (STATE_CIRCLE_REFINERY);
  1123. } else if ( OverallState != STATE_CIRCLE_POWER_PLANT &&
  1124. TargetPos.X > -84.0F && TargetPos.Z > 11.0F) {
  1125. Request_Overall_State (STATE_CIRCLE_POWER_PLANT);
  1126. } else if ( OverallState != STATE_LAND_IN_TIBERIUM_FIELD &&
  1127. TargetPos.Y < 39.0F) {
  1128. Request_Overall_State (STATE_LAND_IN_TIBERIUM_FIELD);
  1129. } else if (OverallState != STATE_VALLEY_STRAFE) {
  1130. Request_Overall_State (STATE_VALLEY_STRAFE);
  1131. } else if (FreeRandom.Get_Int (5) == 1) {
  1132. //
  1133. // Flip a coin and do a strafing run
  1134. //
  1135. if (FreeRandom.Get_Int (2) == 0) {
  1136. Request_Overall_State (STATE_LONG_STRAFING_RUN);
  1137. } else {
  1138. Request_Overall_State (STATE_LONG_STRAFING_RUN2);
  1139. }
  1140. }
  1141. }
  1142. OverallStateTimeLeft = WWMath::Random_Float (5.0F, 10.0F);
  1143. }
  1144. return ;
  1145. }
  1146. ///////////////////////////////////////////////////////////////////////////
  1147. //
  1148. // Update_Overall_State
  1149. //
  1150. ///////////////////////////////////////////////////////////////////////////
  1151. void
  1152. SakuraBossGameObj::Update_Overall_State (void)
  1153. {
  1154. switch (OverallState)
  1155. {
  1156. case STATE_CIRCLE_POWER_PLANT:
  1157. if (CurrentTarget != NULL) {
  1158. Pilot.Set_Circle_Pos (TargetPos);
  1159. } else {
  1160. Pilot.Set_Target (NULL);
  1161. }
  1162. //
  1163. // Use the gattling guns on the player
  1164. //
  1165. Attack_Target (0, 1);
  1166. break;
  1167. case STATE_CIRCLE_REFINERY:
  1168. if (CurrentTarget != NULL) {
  1169. Vector3 destination (-166.0F, 40.13F, 42.23F);
  1170. bool target_player = true;
  1171. //
  1172. // Should the commanche 'face' the player or look straight ahead
  1173. //
  1174. if (TargetPos.Y < 21.0F && TargetPos.X < -151.0F) {
  1175. target_player = false;
  1176. }
  1177. //
  1178. // Have the commanche strafe by the player
  1179. //
  1180. if (TargetPos.X > -140.0F && TargetPos.Y > 34.0F) {
  1181. destination.X += 10.0F;
  1182. destination.Y = TargetPos.Y + 1.0F;
  1183. destination.Y = min (destination.Y, 78.0F);
  1184. destination.Z -= 20.0F;
  1185. } else {
  1186. if (target_player || TargetPos.X > -151.0F) {
  1187. destination.X += 20.0F;
  1188. destination.Z -= 20.0F;
  1189. }
  1190. //
  1191. // Let the commanche move backwards (if necessary)
  1192. //
  1193. if (TargetPos.Y > -6.0F) {
  1194. destination.Y = TargetPos.Y + 46.0F;
  1195. destination.Y = min (destination.Y, 78.0F);
  1196. }
  1197. }
  1198. //
  1199. // Don't let the commanche dip below the player's level
  1200. //
  1201. destination.Z = max (TargetPos.Z + 10.0F, destination.Z);
  1202. Pilot.Set_Destination (destination);
  1203. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  1204. if (target_player) {
  1205. Pilot.Set_Target (&TargetPos);
  1206. } else {
  1207. Vector3 position;
  1208. Get_Position (&position);
  1209. position += Vector3 (0, -10, 0);
  1210. Pilot.Set_Target (&position);
  1211. }
  1212. //
  1213. // Start attacking the player if we are within 4 meters of
  1214. // our expected destination
  1215. //
  1216. Vector3 curr_position;
  1217. Get_Position (&curr_position);
  1218. if ((destination - curr_position).Length2 () < 16) {
  1219. //
  1220. // Use the rockets to attack the player
  1221. //
  1222. Attack_Target (1, 0);
  1223. }
  1224. } else {
  1225. Pilot.Set_Target (NULL);
  1226. }
  1227. break;
  1228. case STATE_ATTACK_LIFT_AREA:
  1229. {
  1230. Vector3 destination (-161.78F, 79.03F, 18.88F);
  1231. destination.Z = max (TargetPos.Z + 7.0F, 18.88F);
  1232. Pilot.Set_Destination (destination);
  1233. destination.X += 10;
  1234. Pilot.Set_Target (&destination);
  1235. //
  1236. // Make sure we are attacking the player
  1237. //
  1238. if (IsAttacking == false) {
  1239. //
  1240. // Attack with the gattling gun if the player is close,
  1241. // otherwise attack with the rockets
  1242. //
  1243. if (TargetPos.X > -130.0F) {
  1244. Attack_Target (1, 0);
  1245. } else {
  1246. Attack_Target (0, 1);
  1247. }
  1248. }
  1249. }
  1250. break;
  1251. case STATE_LONG_STRAFING_RUN2:
  1252. case STATE_LONG_STRAFING_RUN:
  1253. {
  1254. bool should_fire = false;
  1255. //
  1256. // Determine if we should start firing or not...
  1257. // We should fire on the target if they are in front of us
  1258. //
  1259. if (CurrentTarget != NULL) {
  1260. //
  1261. // Transform the target into our coordinate system. Is the target in front of us?
  1262. //
  1263. if (TargetAngle > DEG_TO_RADF (-75) && TargetAngle < DEG_TO_RADF (75)) {
  1264. Vector3 curr_position;
  1265. Get_Position (&curr_position);
  1266. //
  1267. // Are we inside the valley? Note: We don't want to fire if we have
  1268. // no chance of hitting the target
  1269. //
  1270. if ( curr_position.Y >= -40.0F && curr_position.Y <= 140.0F &&
  1271. curr_position.X >= -178.0F)
  1272. {
  1273. should_fire = true;
  1274. }
  1275. }
  1276. } else {
  1277. Pilot.Set_Target (NULL);
  1278. }
  1279. //
  1280. // Turn the gattling guns on/off
  1281. //
  1282. if (should_fire) {
  1283. Set_Gattling_Gun_State (GATLING_STATE_FIRING);
  1284. } else {
  1285. Set_Gattling_Gun_State (GATLING_STATE_NORMAL);
  1286. }
  1287. //
  1288. // Restore the vehicle's speed when we've finished moving
  1289. //
  1290. if (VehicleState != VEHICLE_STATE_MOVING) {
  1291. //
  1292. // Target the player
  1293. //
  1294. Pilot.Set_Target (&TargetPos);
  1295. //
  1296. // Restore the speed
  1297. //
  1298. Pilot.Set_Max_Speed (20.0F);
  1299. }
  1300. }
  1301. break;
  1302. case STATE_VALLEY_STRAFE:
  1303. if (CurrentTarget != NULL) {
  1304. Vector3 destination (-84.33F, 77.99F, 26.77F);
  1305. if (TargetPos.Y < 42.0F) {
  1306. //
  1307. // Simply match the player's position along our strafe path
  1308. //
  1309. destination.X = WWMath::Clamp (TargetPos.X, -143.0F, -84.33F);
  1310. } else {
  1311. //
  1312. // Try to stay targetted on the player by strafing along the path
  1313. //
  1314. if (TargetPos.X > -113.0F) {
  1315. destination.X = WWMath::Clamp (TargetPos.X - 35.0F, -143.0F, -84.33F);
  1316. } else {
  1317. destination.X = WWMath::Clamp (TargetPos.X + 35.0F, -143.0F, -84.33F);
  1318. }
  1319. }
  1320. Pilot.Set_Destination (destination);
  1321. Pilot.Set_Mode (PilotClass::MODE_HOVER);
  1322. Pilot.Set_Target (&TargetPos);
  1323. //
  1324. // Start attacking the player if we are within 4 meters of
  1325. // our expected destination
  1326. //
  1327. Vector3 curr_position;
  1328. Get_Position (&curr_position);
  1329. if ((destination - curr_position).Length2 () < 16) {
  1330. //
  1331. // Make sure we are attacking the player (with either weapon)
  1332. //
  1333. Attack_Target (1, 1);
  1334. }
  1335. } else {
  1336. Pilot.Set_Target (NULL);
  1337. }
  1338. break;
  1339. case STATE_LAND_IN_TIBERIUM_FIELD:
  1340. {
  1341. if (CurrentTarget != NULL) {
  1342. Pilot.Set_Target (&TargetPos);
  1343. } else {
  1344. Pilot.Set_Target (NULL);
  1345. }
  1346. //
  1347. // Start attacking the player if we are within 4 meters of
  1348. // our expected destination
  1349. //
  1350. Vector3 curr_position;
  1351. Vector3 destination = Pilot.Get_Destination ();
  1352. Get_Position (&curr_position);
  1353. if ((destination - curr_position).Length2 () < 16) {
  1354. //
  1355. // Attack the player with either weapon
  1356. //
  1357. Attack_Target (1, 1);
  1358. }
  1359. }
  1360. break;
  1361. case STATE_IN_TRANSITION:
  1362. //
  1363. // If we've finished moving, then we are done with the transition
  1364. // and are ready to move on to the new state.
  1365. //
  1366. if (VehicleState != VEHICLE_STATE_MOVING) {
  1367. Set_Overall_State (NextOverallState);
  1368. } else if (FaceTargetInTransition) {
  1369. if (CurrentTarget != NULL) {
  1370. Pilot.Set_Target (&TargetPos);
  1371. } else {
  1372. Pilot.Set_Target (NULL);
  1373. }
  1374. }
  1375. break;
  1376. }
  1377. return ;
  1378. }
  1379. ///////////////////////////////////////////////////////////////////////////
  1380. //
  1381. // Update_Tilt
  1382. //
  1383. ///////////////////////////////////////////////////////////////////////////
  1384. void
  1385. SakuraBossGameObj::Update_Tilt (void)
  1386. {
  1387. const float TILT_RATE = DEG_TO_RADF (25.0F);
  1388. //
  1389. // Lookup the model we are going to tilt
  1390. //
  1391. RenderObjClass *model = Peek_Model ();
  1392. WWASSERT (model != NULL);
  1393. if (ChopperTiltBoneIndex > 0) {
  1394. //
  1395. // Determine what the maximum tilt allowed is...
  1396. //
  1397. float max_tilt = DEG_TO_RADF (35.0F);
  1398. if (OverallState == STATE_LAND_IN_TIBERIUM_FIELD) {
  1399. max_tilt = DEG_TO_RADF (10.0F);
  1400. }
  1401. //
  1402. // Decide if we should be in a tilted or normal state
  1403. //
  1404. if (OverallState != STATE_IN_TRANSITION) {
  1405. //
  1406. // Determine how far we need to tile to look straight at the player
  1407. //
  1408. float target_angle = DEG_TO_RADF (25.0F);
  1409. if (CurrentTarget != NULL) {
  1410. //
  1411. // Get the target's position relative to ours
  1412. //
  1413. Matrix3D untilted_tm (1);
  1414. untilted_tm.Rotate_Z (Get_Transform ().Get_Z_Rotation ());
  1415. untilted_tm.Set_Translation (Get_Transform ().Get_Translation ());
  1416. Vector3 obj_space_target;
  1417. Matrix3D::Inverse_Transform_Vector (untilted_tm, TargetPos, &obj_space_target);
  1418. //
  1419. // How far do we need to tilt to be facing the player?
  1420. //
  1421. target_angle = -WWMath::Atan2 (obj_space_target.Z, obj_space_target.X);
  1422. target_angle = WWMath::Clamp (target_angle, -max_tilt, max_tilt);
  1423. }
  1424. //
  1425. // Increate our tilt percent
  1426. //
  1427. if (TiltAngle < target_angle) {
  1428. TiltAngle += TimeManager::Get_Frame_Seconds () * TILT_RATE;
  1429. TiltAngle = min (TiltAngle, target_angle);
  1430. } else {
  1431. TiltAngle -= TimeManager::Get_Frame_Seconds () * TILT_RATE;
  1432. TiltAngle = max (TiltAngle, target_angle);
  1433. }
  1434. //
  1435. // Check to see if we should start the tilting process
  1436. //
  1437. if (!model->Is_Bone_Captured (ChopperTiltBoneIndex)) {
  1438. model->Capture_Bone (ChopperTiltBoneIndex);
  1439. TiltAngle = 0;
  1440. }
  1441. } else {
  1442. //
  1443. // Decrease our tilt percent
  1444. //
  1445. TiltAngle -= (TimeManager::Get_Frame_Seconds () * TILT_RATE);
  1446. TiltAngle = max (TiltAngle, 0.0F);
  1447. //
  1448. // Check to see if we should release our control of the bone
  1449. //
  1450. if (TiltAngle == 0 && model->Is_Bone_Captured (ChopperTiltBoneIndex)) {
  1451. model->Release_Bone (ChopperTiltBoneIndex);
  1452. }
  1453. }
  1454. //
  1455. // Make sure our percent is clamped between 0 and 1
  1456. //
  1457. //TiltPercent = WWMath::Clamp (TiltPercent, 0, 1.0F);
  1458. //
  1459. // Do we need to tilt the bone?
  1460. //
  1461. if (model->Is_Bone_Captured (ChopperTiltBoneIndex)) {
  1462. //
  1463. // Tilt the model up to 25 degrees
  1464. //
  1465. Matrix3D bone_tm (1);
  1466. bone_tm.Rotate_Y (TiltAngle);
  1467. model->Control_Bone (ChopperTiltBoneIndex, bone_tm);
  1468. }
  1469. }
  1470. return ;
  1471. }
  1472. ///////////////////////////////////////////////////////////////////////////
  1473. //
  1474. // Attack_Target
  1475. //
  1476. ///////////////////////////////////////////////////////////////////////////
  1477. void
  1478. SakuraBossGameObj::Attack_Target
  1479. (
  1480. float rocket_priority,
  1481. float gattling_priority
  1482. )
  1483. {
  1484. //
  1485. // Don't attack if we don't have a target, or we are already attacking
  1486. //
  1487. if (CurrentTarget == NULL || IsAttacking || SakuraState != SAKURA_STATE_NORMAL) {
  1488. return ;
  1489. }
  1490. //
  1491. // There's a 1 in 6 chance that Sakura will taunt the player
  1492. // instead of attacking
  1493. //
  1494. if (FreeRandom.Get_Int (4) == 2) {
  1495. Set_Sakura_State (SAKURA_STATE_TAUNTING);
  1496. } else {
  1497. //
  1498. // If both rockets are gone, then decrease its priority so
  1499. // we won't choose them
  1500. //
  1501. if ( ((AvailableWeapons & WEAPON_ROCKETS_LEFT) == 0) &&
  1502. ((AvailableWeapons & WEAPON_ROCKETS_RIGHT) == 0))
  1503. {
  1504. rocket_priority = gattling_priority - 1.0F;
  1505. }
  1506. //
  1507. // Decide which weapon to use
  1508. //
  1509. if (rocket_priority > gattling_priority) {
  1510. Set_Rocket_State (ROCKET_STATE_OPENING);
  1511. } else if (gattling_priority > rocket_priority) {
  1512. Set_Gattling_Gun_State (GATLING_STATE_REVVING_UP);
  1513. } else {
  1514. //
  1515. // Priorities are equal, so toss a coin and pick a weapon
  1516. //
  1517. if (FreeRandom.Get_Int (2) == 1) {
  1518. Set_Rocket_State (ROCKET_STATE_OPENING);
  1519. } else {
  1520. Set_Gattling_Gun_State (GATLING_STATE_REVVING_UP);
  1521. }
  1522. }
  1523. }
  1524. return ;
  1525. }
  1526. ///////////////////////////////////////////////////////////////////////////
  1527. //
  1528. // Stop_Attacking
  1529. //
  1530. ///////////////////////////////////////////////////////////////////////////
  1531. void
  1532. SakuraBossGameObj::Stop_Attacking (void)
  1533. {
  1534. //
  1535. // Force the rockets closed
  1536. //
  1537. if (RocketLauncherState != ROCKET_STATE_CLOSED) {
  1538. Set_Rocket_State (ROCKET_STATE_CLOSING);
  1539. }
  1540. //
  1541. // Force the gattling gun to stop
  1542. //
  1543. if (GattlingGunState != GATLING_STATE_NORMAL) {
  1544. Set_Gattling_Gun_State (GATLING_STATE_NORMAL);
  1545. }
  1546. return ;
  1547. }
  1548. ///////////////////////////////////////////////////////////////////////////
  1549. //
  1550. // Set_Vehicle_State
  1551. //
  1552. ///////////////////////////////////////////////////////////////////////////
  1553. void
  1554. SakuraBossGameObj::Set_Vehicle_State (int new_state)
  1555. {
  1556. if (VehicleState != new_state) {
  1557. switch (new_state)
  1558. {
  1559. case VEHICLE_STATE_HOVERING:
  1560. case VEHICLE_STATE_MOVING:
  1561. break;
  1562. }
  1563. VehicleState = new_state;
  1564. }
  1565. return ;
  1566. }
  1567. ///////////////////////////////////////////////////////////////////////////
  1568. //
  1569. // Update_Vehicle_State
  1570. //
  1571. ///////////////////////////////////////////////////////////////////////////
  1572. void
  1573. SakuraBossGameObj::Update_Vehicle_State (void)
  1574. {
  1575. //
  1576. // If we have a current target, then check to make sure
  1577. // we are pointing all our weapons at that target
  1578. //
  1579. if (TargetAngle > DEG_TO_RADF (-90) && TargetAngle < DEG_TO_RADF (90)) {
  1580. Set_Targeting (TargetPos, true);
  1581. } else {
  1582. Vector3 target_pos = Get_Transform () * Vector3 (100.0F, 0, 0) ;
  1583. Set_Targeting (target_pos, true);
  1584. }
  1585. switch (VehicleState)
  1586. {
  1587. case VEHICLE_STATE_HOVERING:
  1588. break;
  1589. case VEHICLE_STATE_MOVING:
  1590. {
  1591. //
  1592. // Get the next point on the path
  1593. //
  1594. Vector3 curr_pos;
  1595. Vector3 next_pos;
  1596. Get_Position (&curr_pos);
  1597. const AABoxClass &bounding_box = Peek_Model ()->Get_Bounding_Box ();
  1598. Vector3 center_to_tip_vector;
  1599. Matrix3D::Rotate_Vector (Get_Transform (), Vector3 (bounding_box.Extent.X / 2.0F, 0, 0), &center_to_tip_vector);
  1600. Vector3 tip_curr_pos = curr_pos + center_to_tip_vector;
  1601. tip_curr_pos.Z = curr_pos.Z;
  1602. Path->Evaluate_Next_Point (tip_curr_pos, next_pos);
  1603. PathClass::STATE_DESC result = Path->Get_State ();
  1604. //
  1605. // Check to see if we need to continue along the path of
  1606. // if we are finished
  1607. //
  1608. if (Pilot.Get_Mode () == PilotClass::MODE_HOVER) {
  1609. Set_Vehicle_State (VEHICLE_STATE_HOVERING);
  1610. } else if (result < PathClass::FIRST_ERROR) {
  1611. Pilot.Set_Next_Point (next_pos);
  1612. }
  1613. }
  1614. break;
  1615. }
  1616. return ;
  1617. }
  1618. ///////////////////////////////////////////////////////////////////////////
  1619. //
  1620. // Set_Sakura_State
  1621. //
  1622. ///////////////////////////////////////////////////////////////////////////
  1623. void
  1624. SakuraBossGameObj::Set_Sakura_State (int new_state)
  1625. {
  1626. if (SakuraState != new_state) {
  1627. switch (new_state)
  1628. {
  1629. case SAKURA_STATE_NORMAL:
  1630. break;
  1631. case SAKURA_STATE_TAUNTING:
  1632. {
  1633. //
  1634. // Find the first available taunt
  1635. //
  1636. int taunt_id = 0;
  1637. for (int index = 0; index < MAX_TAUNTS; index ++) {
  1638. if (TauntList[index] != 0) {
  1639. taunt_id = TauntList[index] - 1;
  1640. TauntList[index] = 0;
  1641. break;
  1642. }
  1643. }
  1644. //
  1645. // Reshuffle the taunts if necessary
  1646. //
  1647. if (index >= (MAX_TAUNTS - 1)) {
  1648. Shuffle_Taunt_List ();
  1649. }
  1650. //
  1651. // Default to a 2 second taunt deault
  1652. //
  1653. SakuraTauntTimeLeft = 2.0F;
  1654. //
  1655. // Lookup the conversation for this taunt
  1656. //
  1657. ConversationClass *conv = ConversationMgrClass::Find_Conversation (TAUNT_IDS[taunt_id]);
  1658. if (conv != NULL) {
  1659. ActiveConversationClass *taunt_conversation = ConversationMgrClass::Create_New_Conversation (conv);
  1660. if (taunt_conversation != NULL) {
  1661. //
  1662. // Add Sakura and the player to the list
  1663. //
  1664. taunt_conversation->Add_Orator (NULL);
  1665. OratorClass *orator = taunt_conversation->Add_Orator (COMBAT_STAR);
  1666. orator->Set_Flag (OratorClass::FLAG_DONT_MOVE, true);
  1667. orator->Set_Flag (OratorClass::FLAG_DONT_TURN_HEAD, false);
  1668. orator->Set_Flag (OratorClass::FLAG_DONT_FACE, true);
  1669. SakuraTauntTimeLeft = taunt_conversation->Get_Conversation_Time ();
  1670. //
  1671. // Start the conversation
  1672. //
  1673. taunt_conversation->Start_Conversation ();
  1674. REF_PTR_RELEASE (taunt_conversation);
  1675. }
  1676. REF_PTR_RELEASE (conv);
  1677. }
  1678. }
  1679. break;
  1680. }
  1681. SakuraState = new_state;
  1682. }
  1683. return ;
  1684. }
  1685. ///////////////////////////////////////////////////////////////////////////
  1686. //
  1687. // Shuffle_Taunt_List
  1688. //
  1689. ///////////////////////////////////////////////////////////////////////////
  1690. void
  1691. SakuraBossGameObj::Shuffle_Taunt_List (void)
  1692. {
  1693. ::memset (TauntList, 0, sizeof (TauntList));
  1694. //
  1695. // Grab an entry from the taunt id list
  1696. //
  1697. for (int index = 0; index < MAX_TAUNTS; index ++) {
  1698. //
  1699. // Choose a random list entry to plug this taunt ID into
  1700. //
  1701. int list_index = 0;
  1702. do
  1703. {
  1704. list_index = FreeRandom.Get_Int ( MAX_TAUNTS );
  1705. } while (TauntList[list_index] != 0);
  1706. //
  1707. // Stick this taunt ID into the list
  1708. //
  1709. TauntList[list_index] = index + 1;
  1710. }
  1711. return ;
  1712. }
  1713. ///////////////////////////////////////////////////////////////////////////
  1714. //
  1715. // Update_Sakura_State
  1716. //
  1717. ///////////////////////////////////////////////////////////////////////////
  1718. void
  1719. SakuraBossGameObj::Update_Sakura_State (void)
  1720. {
  1721. switch (SakuraState)
  1722. {
  1723. case SAKURA_STATE_NORMAL:
  1724. break;
  1725. case SAKURA_STATE_TAUNTING:
  1726. SakuraTauntTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1727. if (SakuraTauntTimeLeft <= 0) {
  1728. Set_Sakura_State (SAKURA_STATE_NORMAL);
  1729. }
  1730. break;
  1731. }
  1732. return ;
  1733. }
  1734. ///////////////////////////////////////////////////////////////////////////
  1735. //
  1736. // Set_Gattling_Gun_State
  1737. //
  1738. ///////////////////////////////////////////////////////////////////////////
  1739. void
  1740. SakuraBossGameObj::Set_Gattling_Gun_State (int new_state)
  1741. {
  1742. if (GattlingGunState != new_state) {
  1743. switch (new_state)
  1744. {
  1745. case GATLING_STATE_NORMAL:
  1746. GattlingGun->Set_Primary_Triggered (false);
  1747. break;
  1748. case GATLING_STATE_REVVING_UP:
  1749. {
  1750. AudibleSoundClass *sound = WWAudioClass::Get_Instance()->Create_Sound (Get_Definition().GattlingGunRevSoundDefID);
  1751. if (sound != NULL) {
  1752. //
  1753. // We will continue to 'rev' the gatling gun until the sound ends
  1754. //
  1755. GattlingGunStateTimeLeft = (sound->Get_Duration () / 1000.0F);
  1756. //
  1757. // Attach the sound to the gatling gun's muzzle, and add it to the world
  1758. //
  1759. sound->Attach_To_Object (Peek_Model (), GATLING_MUZZLE);
  1760. sound->Add_To_Scene (true);
  1761. REF_PTR_RELEASE (sound);
  1762. } else {
  1763. GattlingGunStateTimeLeft = 1.0F;
  1764. }
  1765. }
  1766. break;
  1767. case GATLING_STATE_FIRING:
  1768. //
  1769. // Pick a random amount of time to fire at the player
  1770. //
  1771. GattlingGunStateTimeLeft = WWMath::Random_Float (5.0F, 10.0F);
  1772. if (GattlingGun != NULL) {
  1773. //
  1774. // Start firing the gatling gun at the player
  1775. //
  1776. GattlingGun->Set_Primary_Triggered (true);
  1777. }
  1778. break;
  1779. }
  1780. GattlingGunState = new_state;
  1781. }
  1782. return ;
  1783. }
  1784. ///////////////////////////////////////////////////////////////////////////
  1785. //
  1786. // Update_Gattling_Gun_State
  1787. //
  1788. ///////////////////////////////////////////////////////////////////////////
  1789. void
  1790. SakuraBossGameObj::Update_Gattling_Gun_State (void)
  1791. {
  1792. switch ( GattlingGunState )
  1793. {
  1794. case GATLING_STATE_REVVING_UP:
  1795. GattlingGunStateTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1796. if (GattlingGunStateTimeLeft <= 0) {
  1797. //
  1798. // We've finished revving up the gatling gun, so now
  1799. // start firing at the player
  1800. //
  1801. Set_Gattling_Gun_State (GATLING_STATE_FIRING);
  1802. }
  1803. break;
  1804. case GATLING_STATE_NORMAL:
  1805. break;
  1806. case GATLING_STATE_FIRING:
  1807. //
  1808. // Target the player
  1809. //
  1810. ScriptableGameObj *target = CurrentTarget.Get_Ptr ();
  1811. if (target != NULL) {
  1812. Set_Targeting (TargetPos, true);
  1813. }
  1814. //
  1815. // Stop firing when we've shot at the player for a few seconds
  1816. //
  1817. GattlingGunStateTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1818. if (GattlingGunStateTimeLeft <= 0) {
  1819. Set_Gattling_Gun_State (GATLING_STATE_NORMAL);
  1820. }
  1821. break;
  1822. }
  1823. return ;
  1824. }
  1825. ///////////////////////////////////////////////////////////////////////////
  1826. //
  1827. // Set_Rocket_State
  1828. //
  1829. ///////////////////////////////////////////////////////////////////////////
  1830. void
  1831. SakuraBossGameObj::Set_Rocket_State (int new_state)
  1832. {
  1833. if (RocketLauncherState != new_state) {
  1834. switch (new_state)
  1835. {
  1836. case ROCKET_STATE_OPENING:
  1837. {
  1838. //
  1839. // Play the door open sound on the left rocket launcher
  1840. //
  1841. if (AvailableWeapons & WEAPON_ROCKETS_LEFT) {
  1842. Matrix3D left_tm = Peek_Model ()->Get_Bone_Transform (LEFT_ROCKET_MUZZLE);
  1843. WWAudioClass::Get_Instance()->Create_Instant_Sound (Get_Definition ().RocketDoorOpenSoundID, left_tm);
  1844. }
  1845. //
  1846. // Play the door open sound on the right rocket launcher
  1847. //
  1848. if (AvailableWeapons & WEAPON_ROCKETS_RIGHT) {
  1849. Matrix3D right_tm = Peek_Model ()->Get_Bone_Transform (RIGHT_ROCKET_MUZZLE);
  1850. WWAudioClass::Get_Instance()->Create_Instant_Sound (Get_Definition ().RocketDoorOpenSoundID, right_tm);
  1851. }
  1852. //
  1853. // Open the rocket doors
  1854. //
  1855. Open_Rocket_Launchers (true);
  1856. }
  1857. break;
  1858. case ROCKET_STATE_CLOSING:
  1859. {
  1860. //
  1861. // Close the rocket doors
  1862. //
  1863. Open_Rocket_Launchers (false);
  1864. //
  1865. // Stop firing
  1866. //
  1867. if (RockerLauncherLeft != NULL && RockerLauncherRight != NULL) {
  1868. RockerLauncherLeft->Set_Primary_Triggered (false);
  1869. RockerLauncherRight->Set_Primary_Triggered (false);
  1870. }
  1871. }
  1872. break;
  1873. case ROCKET_STATE_FIRING:
  1874. {
  1875. //
  1876. // Pick a random amount of time to fire at the player
  1877. //
  1878. RocketLauncherStateTimeLeft = WWMath::Random_Float (2.0F, 6.0F);
  1879. if (RockerLauncherLeft != NULL && RockerLauncherRight != NULL) {
  1880. RockerLauncherLeft->Set_Target (TargetPos);
  1881. RockerLauncherRight->Set_Target (TargetPos);
  1882. //
  1883. // Start launching rockets at the player
  1884. //
  1885. if (AvailableWeapons & WEAPON_ROCKETS_LEFT) {
  1886. RockerLauncherLeft->Set_Primary_Triggered (true);
  1887. }
  1888. if (AvailableWeapons & WEAPON_ROCKETS_RIGHT) {
  1889. RockerLauncherRight->Set_Primary_Triggered (true);
  1890. }
  1891. }
  1892. }
  1893. break;
  1894. case ROCKET_STATE_OPEN:
  1895. RocketLauncherStateTimeLeft = 2.0F;
  1896. break;
  1897. case ROCKET_STATE_CLOSED:
  1898. break;
  1899. }
  1900. RocketLauncherState = new_state;
  1901. }
  1902. return ;
  1903. }
  1904. ///////////////////////////////////////////////////////////////////////////
  1905. //
  1906. // Update_Rocket_State
  1907. //
  1908. ///////////////////////////////////////////////////////////////////////////
  1909. void
  1910. SakuraBossGameObj::Update_Rocket_State (void)
  1911. {
  1912. switch ( RocketLauncherState )
  1913. {
  1914. case ROCKET_STATE_OPENING:
  1915. {
  1916. //
  1917. // Are the rocket launcher doors completely open?
  1918. //
  1919. AnimControlClass *anim_control = Get_Anim_Control ();
  1920. if (anim_control != NULL && anim_control->Get_Current_Frame () == ROCKETS_OUT_FRAME_NUM) {
  1921. Set_Rocket_State (ROCKET_STATE_OPEN);
  1922. }
  1923. }
  1924. break;
  1925. case ROCKET_STATE_CLOSING:
  1926. {
  1927. //
  1928. // Are the rocket launcher doors completely closed?
  1929. //
  1930. AnimControlClass *anim_control = Get_Anim_Control ();
  1931. if (anim_control != NULL && anim_control->Get_Current_Frame () == ROCKETS_IN_FRAME_NUM) {
  1932. Set_Rocket_State (ROCKET_STATE_CLOSED);
  1933. }
  1934. }
  1935. break;
  1936. case ROCKET_STATE_FIRING:
  1937. {
  1938. RockerLauncherLeft->Set_Target (TargetPos);
  1939. RockerLauncherRight->Set_Target (TargetPos);
  1940. RocketLauncherStateTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1941. if (RocketLauncherStateTimeLeft <= 0) {
  1942. //
  1943. // Stop firing
  1944. //
  1945. RockerLauncherLeft->Set_Primary_Triggered (false);
  1946. RockerLauncherRight->Set_Primary_Triggered (false);
  1947. //
  1948. // Close the rocket doors
  1949. //
  1950. Set_Rocket_State (ROCKET_STATE_CLOSING);
  1951. }
  1952. }
  1953. break;
  1954. case ROCKET_STATE_OPEN:
  1955. {
  1956. RocketLauncherStateTimeLeft -= TimeManager::Get_Frame_Seconds ();
  1957. if (RocketLauncherStateTimeLeft <= 0) {
  1958. //
  1959. // Commence firing
  1960. //
  1961. Set_Rocket_State (ROCKET_STATE_FIRING);
  1962. }
  1963. }
  1964. break;
  1965. case ROCKET_STATE_CLOSED:
  1966. break;
  1967. }
  1968. return ;
  1969. }
  1970. ///////////////////////////////////////////////////////////////////////////
  1971. //
  1972. // Apply_Damage
  1973. //
  1974. ///////////////////////////////////////////////////////////////////////////
  1975. void
  1976. SakuraBossGameObj::Apply_Damage_Extended
  1977. (
  1978. const OffenseObjectClass & damager,
  1979. float scale,
  1980. const Vector3 & direction,
  1981. const char * collision_box_name
  1982. )
  1983. {
  1984. if ( !CombatManager::I_Am_Server() ) {
  1985. return;
  1986. }
  1987. //
  1988. // If rockets are out, and the player shot the rocket, then
  1989. // apply the damage to the rocket (not the whole vehicle)
  1990. //
  1991. bool damaged_rockets = false;
  1992. if (RocketLauncherState != ROCKET_STATE_CLOSED && collision_box_name != NULL) {
  1993. if (::strstr (collision_box_name, LEFT_ROCKET_MESH) != NULL) {
  1994. damaged_rockets = true;
  1995. //
  1996. // Apply the damage to the left rocket launcher
  1997. //
  1998. float health_left = LeftRocketDefenseObject.Apply_Damage (damager, scale);
  1999. if (health_left <= 0) {
  2000. Blow_Off_Weapon (WEAPON_ROCKETS_LEFT);
  2001. }
  2002. } else if (::strstr (collision_box_name, RIGHT_ROCKET_MESH) != NULL) {
  2003. damaged_rockets = true;
  2004. //
  2005. // Apply the damage to the right rocket launcher
  2006. //
  2007. float health_left = RightRocketDefenseObject.Apply_Damage (damager, scale);
  2008. if (health_left <= 0) {
  2009. Blow_Off_Weapon (WEAPON_ROCKETS_RIGHT);
  2010. }
  2011. }
  2012. }
  2013. //
  2014. // If the rockets weren't specifically targetted, then let the damage occur normally
  2015. //
  2016. if (damaged_rockets == false) {
  2017. VehicleGameObj::Apply_Damage_Extended (damager, scale, direction, collision_box_name);
  2018. }
  2019. //
  2020. // Remember who was last shooting at us
  2021. //
  2022. LastDamager = damager.Get_Owner ();
  2023. CurrentTarget = LastDamager;
  2024. return ;
  2025. }
  2026. ///////////////////////////////////////////////////////////////////////////
  2027. //
  2028. // Blow_Off_Weapon
  2029. //
  2030. ///////////////////////////////////////////////////////////////////////////
  2031. void
  2032. SakuraBossGameObj::Blow_Off_Weapon (int weapon_id)
  2033. {
  2034. Matrix3D tm (1);
  2035. //
  2036. // Determine where to create the explosion at
  2037. //
  2038. if (weapon_id == WEAPON_ROCKETS_LEFT) {
  2039. tm = Peek_Model ()->Get_Bone_Transform (LEFT_ROCKET_MUZZLE);
  2040. } else {
  2041. tm = Peek_Model ()->Get_Bone_Transform (RIGHT_ROCKET_MUZZLE);
  2042. }
  2043. //
  2044. // Create an explosion
  2045. //
  2046. ExplosionManager::Create_Explosion_At (Get_Definition ().RocketDestroyedExplosionID, tm, NULL);
  2047. //
  2048. // Find the rocket launcher model
  2049. //
  2050. RenderObjClass *weapon_model = NULL;
  2051. if (weapon_id == WEAPON_ROCKETS_LEFT) {
  2052. weapon_model = Peek_Model ()->Get_Sub_Object_By_Name (LEFT_ROCKET_MESH);
  2053. } else {
  2054. weapon_model = Peek_Model ()->Get_Sub_Object_By_Name (RIGHT_ROCKET_MESH);
  2055. }
  2056. //
  2057. // Remove the destroyed rocket launcher model from the apache
  2058. //
  2059. if (weapon_model != NULL) {
  2060. Peek_Model ()->Remove_Sub_Object (weapon_model);
  2061. REF_PTR_RELEASE (weapon_model);
  2062. }
  2063. //
  2064. // 'Shake' the vehicle
  2065. //
  2066. PhysClass *phys_obj = Peek_Physical_Object ();
  2067. if (phys_obj != NULL && phys_obj->As_RigidBodyClass () != NULL) {
  2068. RigidBodyClass *rigid_body = phys_obj->As_RigidBodyClass ();
  2069. Vector3 center = Get_Transform ().Get_Translation ();
  2070. Vector3 y_axis = Get_Transform ().Get_Y_Vector ();
  2071. //
  2072. // Calculate a position 1 meter to the left and right of the
  2073. // apache's center of mass
  2074. //
  2075. Vector3 left_imp_pos = center + y_axis;
  2076. Vector3 right_imp_pos = center - y_axis;
  2077. //
  2078. // Determine how much impulse to apply on each side
  2079. //
  2080. Vector3 left_imp (0, 0, rigid_body->Get_Mass () * 10);
  2081. Vector3 right_imp (0, 0, -rigid_body->Get_Mass () * 10);
  2082. if (weapon_id == WEAPON_ROCKETS_RIGHT) {
  2083. left_imp.Z = -left_imp.Z;
  2084. right_imp.Z = -right_imp.Z;
  2085. }
  2086. //
  2087. // Make the vehicle 'react' to the explosion
  2088. //
  2089. rigid_body->Apply_Impulse (left_imp, left_imp_pos);
  2090. rigid_body->Apply_Impulse (right_imp, right_imp_pos);
  2091. }
  2092. //
  2093. // Make sure the weapon stops firing
  2094. //
  2095. if (weapon_id == WEAPON_ROCKETS_LEFT) {
  2096. RockerLauncherLeft->Set_Primary_Triggered (false);
  2097. } else {
  2098. RockerLauncherRight->Set_Primary_Triggered (false);
  2099. }
  2100. //
  2101. // Remove the weapon from the list of available weapons
  2102. //
  2103. AvailableWeapons &= ~weapon_id;
  2104. return ;
  2105. }
  2106. ///////////////////////////////////////////////////////////////////////////
  2107. //
  2108. // Open_Rocket_Launchers
  2109. //
  2110. ///////////////////////////////////////////////////////////////////////////
  2111. void
  2112. SakuraBossGameObj::Open_Rocket_Launchers (bool onoff)
  2113. {
  2114. AnimControlClass *anim_control = Get_Anim_Control ();
  2115. if (anim_control != NULL) {
  2116. //
  2117. // Set the rocket-door animation
  2118. //
  2119. anim_control->Set_Animation (ROCKET_DOOR_ANIMATION);
  2120. anim_control->Set_Mode (ANIM_MODE_TARGET);
  2121. anim_control->Set_Target_Frame (onoff ? ROCKETS_OUT_FRAME_NUM : ROCKETS_IN_FRAME_NUM);
  2122. }
  2123. return ;
  2124. }
  2125. ///////////////////////////////////////////////////////////////////////////
  2126. //
  2127. // Are_Rocker_Launchers_Ready
  2128. //
  2129. ///////////////////////////////////////////////////////////////////////////
  2130. bool
  2131. SakuraBossGameObj::Are_Rocker_Launchers_Ready (void)
  2132. {
  2133. bool retval = false;
  2134. //
  2135. // Rocket launchers are ready if the animation has been played to end
  2136. //
  2137. AnimControlClass *anim_control = Get_Anim_Control ();
  2138. if (anim_control != NULL) {
  2139. retval = (anim_control->Get_Current_Frame () == ROCKETS_OUT_FRAME_NUM);
  2140. }
  2141. return retval;
  2142. }
  2143. ///////////////////////////////////////////////////////////////////////////
  2144. //
  2145. // Move_To_Location
  2146. //
  2147. ///////////////////////////////////////////////////////////////////////////
  2148. void
  2149. SakuraBossGameObj::Move_To_Location (const Vector3 &pos, float speed)
  2150. {
  2151. MoveToLocation = pos;
  2152. Set_Vehicle_State (VEHICLE_STATE_MOVING);
  2153. //
  2154. // Create an action that will be responsible for moving the vehicle
  2155. // to this location.
  2156. //
  2157. Pilot.Set_Mode (PilotClass::MODE_FLY_TO_POINT);
  2158. Pilot.Set_Destination (pos);
  2159. return ;
  2160. }
  2161. ///////////////////////////////////////////////////////////////////////////
  2162. //
  2163. // Get_Muzzle
  2164. //
  2165. ///////////////////////////////////////////////////////////////////////////
  2166. const Matrix3D &
  2167. SakuraBossGameObj::Get_Muzzle (int /*index*/)
  2168. {
  2169. CurrentMuzzleTM = Get_Transform ();
  2170. RenderObjClass *model = Peek_Model ();
  2171. if (model != NULL) {
  2172. if (GattlingGunState == GATLING_STATE_FIRING) {
  2173. //
  2174. // Simply return the muzzle bone of the gatling gun
  2175. //
  2176. CurrentMuzzleTM = model->Get_Bone_Transform (GATLING_MUZZLE);
  2177. } else {
  2178. //
  2179. // Toggle rocket launchers.
  2180. //
  2181. // Note: This is done because we use a separate
  2182. // weapon for each rocket launcher. However, the weapon asks
  2183. // us for a muzzle without telling us which weapon it is, so
  2184. // to simulate attacking from both sides simultaneously we alternate
  2185. // between the two muzzles.
  2186. //
  2187. //CurrentMuzzleIndex = !CurrentMuzzleIndex;
  2188. //
  2189. // Make sure we aren't attacking from a side that's been blown off.
  2190. //
  2191. if ((AvailableWeapons & WEAPON_ROCKETS_LEFT) == 0) {
  2192. CurrentMuzzleIndex = ROCKET_RIGHT;
  2193. } else if ((AvailableWeapons & WEAPON_ROCKETS_RIGHT) == 0) {
  2194. CurrentMuzzleIndex = ROCKET_LEFT;
  2195. }
  2196. if (CurrentMuzzleIndex == ROCKET_RIGHT) {
  2197. CurrentMuzzleTM = model->Get_Bone_Transform (RIGHT_ROCKET_MUZZLE);
  2198. } else {
  2199. CurrentMuzzleTM = model->Get_Bone_Transform (LEFT_ROCKET_MUZZLE);
  2200. }
  2201. }
  2202. }
  2203. return CurrentMuzzleTM;
  2204. }
  2205. ///////////////////////////////////////////////////////////////////////////
  2206. //
  2207. // Update_Target
  2208. //
  2209. ///////////////////////////////////////////////////////////////////////////
  2210. void
  2211. SakuraBossGameObj::Update_Target (void)
  2212. {
  2213. //
  2214. // Check to see if its time to choose a new target yet
  2215. //
  2216. TargetTimeLeft -= TimeManager::Get_Frame_Seconds ();
  2217. if (TargetTimeLeft <= 0) {
  2218. //
  2219. // Choose either the closest commando or the last person to damage us
  2220. //
  2221. if (FreeRandom.Get_Int (2) == 0 && LastDamager != NULL) {
  2222. CurrentTarget = LastDamager;
  2223. } else {
  2224. CurrentTarget.Set_Ptr (Find_Closest_Human_Player ());
  2225. }
  2226. //
  2227. // Wait a random amount of time before we choose a new target
  2228. //
  2229. TargetTimeLeft = WWMath::Random_Float (5.0F, 20.0F);
  2230. } else if (CurrentTarget == NULL) {
  2231. TargetTimeLeft = TargetTimeLeft * 0.25F;
  2232. }
  2233. return ;
  2234. }
  2235. ///////////////////////////////////////////////////////////////////////////
  2236. //
  2237. // Find_Closest_Human_Player
  2238. //
  2239. ///////////////////////////////////////////////////////////////////////////
  2240. SoldierGameObj *
  2241. SakuraBossGameObj::Find_Closest_Human_Player (void)
  2242. {
  2243. SoldierGameObj *closest_human_player = NULL;
  2244. float closest_distance = 9999.0F;
  2245. //
  2246. // Get our current position
  2247. //
  2248. Vector3 sakura_position;
  2249. Get_Position (&sakura_position);
  2250. AABoxClass valley_box;
  2251. valley_box.Center.Set (-105.0F, 35.0F, 20.0F);
  2252. valley_box.Extent.Set (75.0F, 70.5F, 14.0F);
  2253. //
  2254. // Collect the dynamic physics objects in the valley
  2255. //
  2256. NonRefPhysListClass objs_in_valley;
  2257. PhysicsSceneClass::Get_Instance ()->Collect_Objects (valley_box, false, true, &objs_in_valley);
  2258. //
  2259. // Loop over all the objects
  2260. //
  2261. NonRefPhysListIterator it (&objs_in_valley);
  2262. for (it.First (); !it.Is_Done (); it.Next ()) {
  2263. PhysicalGameObj *gameobj = NULL;
  2264. //
  2265. // Get the game object from this physics object
  2266. //
  2267. if (it.Peek_Obj()->Get_Observer () != NULL) {
  2268. gameobj = ((CombatPhysObserverClass *)it.Peek_Obj()->Get_Observer())->As_PhysicalGameObj();
  2269. }
  2270. //
  2271. // Check to see if this is a commando game object
  2272. //
  2273. if (gameobj != NULL) {
  2274. SoldierGameObj *soldier = gameobj->As_SoldierGameObj ();
  2275. if (soldier != NULL && soldier->Is_Human_Controlled()) {
  2276. Vector3 position;
  2277. soldier->Get_Position (&position);
  2278. //
  2279. // Is this the closest commando we've found so far?
  2280. //
  2281. float distance = (position - sakura_position).Length ();
  2282. if (distance < closest_distance) {
  2283. closest_human_player = soldier;
  2284. closest_distance = distance;
  2285. }
  2286. }
  2287. }
  2288. }
  2289. return closest_human_player;
  2290. }
  2291. ///////////////////////////////////////////////////////////////////////////
  2292. //
  2293. // Do_Waypath
  2294. //
  2295. ///////////////////////////////////////////////////////////////////////////
  2296. void
  2297. SakuraBossGameObj::Do_Waypath (int waypath_id, int start_id, int end_id)
  2298. {
  2299. WaypathClass *waypath = PathfindClass::Get_Instance()->Find_Waypath (waypath_id);
  2300. if (waypath != NULL) {
  2301. //
  2302. // Initialize our pathfind object with the waypath
  2303. //
  2304. PathObjectClass path_obj;
  2305. path_obj.Initialize (*Peek_Physical_Object ());
  2306. path_obj.Set_Max_Speed (Get_Max_Speed ());
  2307. path_obj.Set_Turn_Radius (Get_Turn_Radius ());
  2308. path_obj.Set_Flag (PathObjectClass::IS_VEHICLE, true);
  2309. Path->Set_Path_Object (path_obj);
  2310. Path->Set_Traversal_Type (PathClass::SPLINE);
  2311. Path->Set_Movement_Radius (5.0F);
  2312. Path->Initialize (waypath, start_id, end_id);
  2313. Path->Set_Movement_Directions (PathClass::MOVE_X | PathClass::MOVE_Y | PathClass::MOVE_Z);
  2314. //
  2315. // Find the starting and ending waypoints
  2316. //
  2317. int count = waypath->Get_Point_Count ();
  2318. int start_index = 0;
  2319. int end_index = count - 1;
  2320. for (int index = 0; index < count; index ++) {
  2321. if (waypath->Get_Point (index)->Get_ID () == start_id) {
  2322. start_index = index;
  2323. }
  2324. if (waypath->Get_Point (index)->Get_ID () == end_id) {
  2325. end_index = index;
  2326. }
  2327. }
  2328. //
  2329. // Get the world space locations of the starting and ending waypoints
  2330. //
  2331. Vector3 start_pos = waypath->Get_Point (start_index)->Get_Position ();
  2332. Vector3 end_pos = waypath->Get_Point (end_index)->Get_Position ();
  2333. //
  2334. // Initialize the pilot code with the new flight path
  2335. //
  2336. Pilot.Set_Mode (PilotClass::MODE_FLY_TO_POINT);
  2337. Pilot.Set_Next_Point (start_pos);
  2338. Pilot.Set_Destination (end_pos);
  2339. Pilot.Set_Arrived_Dist (1.0F);
  2340. Pilot.Set_Hover_Dist (10.0F);
  2341. Set_Vehicle_State (VEHICLE_STATE_MOVING);
  2342. }
  2343. return ;
  2344. }