VESSEL.CPP 109 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */
  15. /***********************************************************************************************
  16. *** 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 ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : VESSEL.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : 03/13/96 *
  26. * *
  27. * Last Update : July 31, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * VesselClass::AI -- Handles the AI processing for vessel objects. *
  32. * VesselClass::Assign_Destination -- Assign a destination for this vessel. *
  33. * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
  34. * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
  35. * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
  36. * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
  37. * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
  38. * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
  39. * VesselClass::Draw_It -- Draws the vessel. *
  40. * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
  41. * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
  42. * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
  43. * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse*
  44. * VesselClass::Init -- Initialize the vessel heap system. *
  45. * VesselClass::Mission_Retreat -- Perform the retreat mission. *
  46. * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
  47. * VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
  48. * VesselClass::Read_INI -- Read the vessel data from the INI database. *
  49. * VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
  50. * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
  51. * VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
  52. * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
  53. * VesselClass::Take_Damage -- Assign damage to the vessel. *
  54. * VesselClass::VesselClass -- Constructor for vessel class objects. *
  55. * VesselClass::What_Action -- Determines action to perform on specified cell. *
  56. * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
  57. * VesselClass::~VesselClass -- Destructor for vessel objects. *
  58. * operator delete -- Deletes a vessel's memory block. *
  59. * operator new -- Allocates a vessel object memory block. *
  60. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  61. #include "function.h"
  62. /***********************************************************************************************
  63. * VesselClass::VesselClass -- Constructor for vessel class objects. *
  64. * *
  65. * This is the normal constructor for vessel class objects. It will set up a vessel that *
  66. * is valid excepting that it won't be placed on the map. *
  67. * *
  68. * INPUT: classid -- The type of vessel this will be. *
  69. * *
  70. * house -- The owner of this vessel. *
  71. * *
  72. * OUTPUT: none *
  73. * *
  74. * WARNINGS: none *
  75. * *
  76. * HISTORY: *
  77. * 03/14/1996 JLB : Created. *
  78. *=============================================================================================*/
  79. VesselClass::VesselClass(VesselType classid, HousesType house) :
  80. DriveClass(RTTI_VESSEL, Vessels.ID(this), house),
  81. Class(VesselTypes.Ptr((int)classid)),
  82. IsToSelfRepair(false),
  83. IsSelfRepairing(false),
  84. DoorShutCountDown(0),
  85. PulseCountDown(0),
  86. SecondaryFacing(PrimaryFacing)
  87. {
  88. House->Tracking_Add(this);
  89. /*
  90. ** The ammo member is actually part of the techno class, but must be initialized
  91. ** manually here because this is where we first have access to the class pointer.
  92. */
  93. Ammo = Class->MaxAmmo;
  94. /*
  95. ** For two shooters, clear out the second shot flag -- it will be set the first time
  96. ** the object fires. For non two shooters, set the flag since it will never be cleared
  97. ** and the second shot flag tells the system that normal rearm times apply -- this is
  98. ** what is desired for non two shooters.
  99. */
  100. IsSecondShot = !Class->Is_Two_Shooter();
  101. Strength = Class->MaxStrength;
  102. /*
  103. ** The techno class cloakabilty flag is set according to the type
  104. ** class cloakability flag.
  105. */
  106. IsCloakable = Class->IsCloakable;
  107. /*
  108. ** Keep count of the number of units created.
  109. */
  110. // if (Session.Type == GAME_INTERNET) {
  111. // House->UnitTotals->Increment_Unit_Total((int)classid);
  112. // }
  113. }
  114. /***********************************************************************************************
  115. * VesselClass::~VesselClass -- Destructor for vessel objects. *
  116. * *
  117. * The destructor will destroy the vessel and ensure that it is properly removed from the *
  118. * game engine. *
  119. * *
  120. * INPUT: none *
  121. * *
  122. * OUTPUT: none *
  123. * *
  124. * WARNINGS: none *
  125. * *
  126. * HISTORY: *
  127. * 03/14/1996 JLB : Created. *
  128. *=============================================================================================*/
  129. VesselClass::~VesselClass(void)
  130. {
  131. if (GameActive && Class.Is_Valid()) {
  132. /*
  133. ** Remove this member from any team it may be associated with. This must occur at the
  134. ** top most level of the inheritance hierarchy because it may call virtual functions.
  135. */
  136. if (Team.Is_Valid()) {
  137. Team->Remove(this);
  138. Team = NULL;
  139. }
  140. House->Tracking_Remove(this);
  141. /*
  142. ** If there are any cargo members, delete them.
  143. */
  144. while (Is_Something_Attached()) {
  145. delete Detach_Object();
  146. }
  147. Limbo();
  148. }
  149. ID = -1;
  150. }
  151. /***********************************************************************************************
  152. * operator new -- Allocates a vessel object memory block. *
  153. * *
  154. * This routine is used to allocate a block of memory from the vessel heap. If there is *
  155. * no more space in the heap, then this routine will return NULL. *
  156. * *
  157. * INPUT: none *
  158. * *
  159. * OUTPUT: Returns with the pointer to the allocated block of memory. *
  160. * *
  161. * WARNINGS: This routine could return NULL. *
  162. * *
  163. * HISTORY: *
  164. * 03/14/1996 JLB : Created. *
  165. *=============================================================================================*/
  166. void * VesselClass::operator new(size_t)
  167. {
  168. void * ptr = Vessels.Alloc();
  169. if (ptr != NULL) {
  170. ((VesselClass *)ptr)->Set_Active();
  171. }
  172. return(ptr);
  173. }
  174. /***********************************************************************************************
  175. * operator delete -- Deletes a vessel's memory block. *
  176. * *
  177. * This overloaded delete operator will return the vessel's memory back to the pool of *
  178. * memory used for vessel allocation. *
  179. * *
  180. * INPUT: ptr -- Pointer to the vessel's memory block. *
  181. * *
  182. * OUTPUT: none *
  183. * *
  184. * WARNINGS: none *
  185. * *
  186. * HISTORY: *
  187. * 03/14/1996 JLB : Created. *
  188. *=============================================================================================*/
  189. void VesselClass::operator delete(void * ptr)
  190. {
  191. if (ptr != NULL) {
  192. assert(((VesselClass *)ptr)->IsActive);
  193. ((VesselClass *)ptr)->IsActive = false;
  194. }
  195. Vessels.Free((VesselClass *)ptr);
  196. }
  197. /***********************************************************************************************
  198. * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
  199. * *
  200. * This routine will return with a reference to the static class data for this vessel. *
  201. * *
  202. * INPUT: none *
  203. * *
  204. * OUTPUT: Returns with a reference to the class data structure associated with this vessel. *
  205. * *
  206. * WARNINGS: none *
  207. * *
  208. * HISTORY: *
  209. * 03/14/1996 JLB : Created. *
  210. *=============================================================================================*/
  211. ObjectTypeClass const & VesselClass::Class_Of(void) const
  212. {
  213. assert(IsActive);
  214. return(*Class);
  215. }
  216. /***********************************************************************************************
  217. * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
  218. * *
  219. * This routine is used by find path and other movement logic to determine if this *
  220. * vessel can enter the cell specified. *
  221. * *
  222. * INPUT: cell -- The cell to check this vessel against. *
  223. * *
  224. * OUTPUT: Returns with the movement restriction associated with movement into this object. *
  225. * *
  226. * WARNINGS: none *
  227. * *
  228. * HISTORY: *
  229. * 03/14/1996 JLB : Created. *
  230. *=============================================================================================*/
  231. MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const
  232. {
  233. assert(Vessels.ID(this) == ID);
  234. assert(IsActive);
  235. if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);
  236. CellClass const * cellptr = &Map[cell];
  237. /*
  238. ** Moving off the edge of the map is not allowed unless
  239. ** this is a loaner vehicle.
  240. */
  241. if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) {
  242. return(MOVE_NO);
  243. }
  244. MoveType retval = MOVE_OK;
  245. /*
  246. ** If there is blocking terrain (such as ice), then the vessel
  247. ** can't move there.
  248. */
  249. if (cellptr->Cell_Terrain() != NULL) {
  250. return(MOVE_NO);
  251. }
  252. /*
  253. ** If the cell is out and out impassable because of underlying terrain, then
  254. ** return this immutable fact.
  255. */
  256. if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) {
  257. return(MOVE_NO);
  258. }
  259. /*
  260. ** If some allied object has reserved the cell, then consider the cell
  261. ** as blocked by a moving object.
  262. */
  263. if (cellptr->Flag.Composite) {
  264. if (cellptr->Flag.Occupy.Building) {
  265. return(MOVE_NO);
  266. }
  267. TechnoClass * techno = cellptr->Cell_Techno();
  268. if (techno != NULL && techno->Is_Cloaked(this)) {
  269. return(MOVE_CLOAK);
  270. }
  271. /*
  272. ** If reserved by a vehicle, then consider this blocked terrain.
  273. */
  274. if (cellptr->Flag.Occupy.Vehicle) {
  275. retval = MOVE_MOVING_BLOCK;
  276. }
  277. }
  278. /*
  279. ** Return with the most severe reason why this cell would be impassable.
  280. */
  281. return(retval);
  282. }
  283. /***********************************************************************************************
  284. * VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
  285. * *
  286. * This routine will return with the shape number to use for the ship's body. *
  287. * *
  288. * INPUT: none *
  289. * *
  290. * OUTPUT: Returns with the shape number to use for the ship's body when drawing. *
  291. * *
  292. * WARNINGS: none *
  293. * *
  294. * HISTORY: *
  295. * 07/31/1996 JLB : Created. *
  296. *=============================================================================================*/
  297. int VesselClass::Shape_Number(void) const
  298. {
  299. /*
  300. ** For eight facing units, adjust the facing number accordingly.
  301. */
  302. FacingType facing = Dir_Facing(PrimaryFacing.Current());
  303. int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1;
  304. /*
  305. ** Special case code for transport. The north/south facing is in frame
  306. ** 0. The east/west facing is in frame 3.
  307. */
  308. if (*this == VESSEL_TRANSPORT) {
  309. shapenum = 0;
  310. }
  311. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  312. if (*this == VESSEL_CARRIER) {
  313. shapenum = 0;
  314. }
  315. #endif
  316. /*
  317. ** Door opening and closing animation stage check.
  318. */
  319. if (!Is_Door_Closed()) {
  320. shapenum = Door_Stage();
  321. }
  322. return(shapenum);
  323. }
  324. /***********************************************************************************************
  325. * VesselClass::Draw_It -- Draws the vessel. *
  326. * *
  327. * Draws the vessel on the tactical display. This routine is called by the map rendering *
  328. * process to display the vessel. *
  329. * *
  330. * INPUT: x,y -- The pixel coordinate to draw this vessel at. *
  331. * *
  332. * window-- The window to base clipping and coordinates upon when drawing. *
  333. * *
  334. * OUTPUT: none *
  335. * *
  336. * WARNINGS: none *
  337. * *
  338. * HISTORY: *
  339. * 03/14/1996 JLB : Created. *
  340. *=============================================================================================*/
  341. void VesselClass::Draw_It(int x, int y, WindowNumberType window) const
  342. {
  343. assert(Vessels.ID(this) == ID);
  344. assert(IsActive);
  345. /*
  346. ** Verify the legality of the unit class.
  347. */
  348. void const * shapefile = Get_Image_Data();
  349. if (shapefile == NULL) return;
  350. /*
  351. ** Need to know the shape name for the overlay now. ST - 8/19/2019 1:37PM
  352. */
  353. const char *turret_shape_name = NULL;
  354. /*
  355. ** If drawing of this unit is not explicitly prohibited, then proceed
  356. ** with the render process.
  357. */
  358. const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL);
  359. if (!is_hidden) {
  360. int facing = Dir_To_32(PrimaryFacing);
  361. int tfacing = Dir_To_32(SecondaryFacing);
  362. DirType rotation = DIR_N;
  363. int scale = 0x0100;
  364. /*
  365. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  366. */
  367. Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale);
  368. /*
  369. ** If there is a turret, then it must be rendered as well. This may include
  370. ** firing animation if required.
  371. */
  372. if (Class->IsTurretEquipped) {
  373. int xx = x;
  374. int yy = y;
  375. /*
  376. ** Determine which turret shape to use. This depends on if there
  377. ** is any firing animation in progress.
  378. */
  379. int shapenum = TechnoClass::BodyShape[tfacing]+32;
  380. DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16);
  381. switch (Class->Type) {
  382. case VESSEL_CA:
  383. turret_shape_name = "TURR";
  384. shapefile = Class->TurretShapes;
  385. shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
  386. Class->Turret_Adjust(turdir, xx, yy);
  387. // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM
  388. //Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
  389. Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name);
  390. xx = x;
  391. yy = y;
  392. turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16);
  393. Class->Turret_Adjust(turdir, xx, yy);
  394. break;
  395. case VESSEL_DD:
  396. turret_shape_name = "SSAM";
  397. shapefile = Class->SamShapes;
  398. shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
  399. Class->Turret_Adjust(turdir, xx, yy);
  400. break;
  401. case VESSEL_PT:
  402. turret_shape_name = "MGUN";
  403. shapefile = Class->MGunShapes;
  404. shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
  405. Class->Turret_Adjust(turdir, xx, yy);
  406. break;
  407. default:
  408. shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
  409. Class->Turret_Adjust(turdir, xx, yy);
  410. break;
  411. }
  412. /*
  413. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  414. */
  415. // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM
  416. if (turret_shape_name) {
  417. Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name);
  418. } else {
  419. Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
  420. }
  421. }
  422. }
  423. DriveClass::Draw_It(x, y, window);
  424. /*
  425. ** Patch so the transport will draw its passengers on top of itself.
  426. */
  427. if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) {
  428. TechnoClass * contact = Contact_With_Whom();
  429. assert(contact->IsActive);
  430. int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord())));
  431. int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord())));
  432. contact->Draw_It(xxx, yyy, window);
  433. contact->IsToDisplay = false;
  434. }
  435. }
  436. #ifdef CHEAT_KEYS
  437. /***********************************************************************************************
  438. * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
  439. * *
  440. * This routine will display the vessel's status information. The information is dumped to *
  441. * the monochrome monitor. *
  442. * *
  443. * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed *
  444. * to. *
  445. * *
  446. * OUTPUT: none *
  447. * *
  448. * WARNINGS: none *
  449. * *
  450. * HISTORY: *
  451. * 03/20/1996 JLB : Created. *
  452. *=============================================================================================*/
  453. void VesselClass::Debug_Dump(MonoClass * mono) const
  454. {
  455. assert(Vessels.ID(this) == ID);
  456. assert(IsActive);
  457. mono->Set_Cursor(0, 0);
  458. mono->Print(Text_String(TXT_DEBUG_SHIP));
  459. mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());
  460. mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL);
  461. mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL);
  462. DriveClass::Debug_Dump(mono);
  463. }
  464. #endif
  465. /***********************************************************************************************
  466. * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
  467. * *
  468. * This routine will fetch the overlap list for this vessel type. It takes into *
  469. * consideration any movement the vessel may be doing. *
  470. * *
  471. * INPUT: none *
  472. * *
  473. * OUTPUT: Returns with a pointer to the overlap list for this vessel. *
  474. * *
  475. * WARNINGS: none *
  476. * *
  477. * HISTORY: *
  478. * 03/20/1996 JLB : Created. *
  479. *=============================================================================================*/
  480. short const * VesselClass::Overlap_List(bool redraw) const
  481. {
  482. assert(Vessels.ID(this) == ID);
  483. assert(IsActive);
  484. #ifdef PARTIAL
  485. if (Height == 0 && redraw && Class->DimensionData != NULL) {
  486. Rect rect;
  487. int shapenum = Shape_Number();
  488. if (Class->DimensionData[shapenum].Is_Valid()) {
  489. rect = Class->DimensionData[shapenum];
  490. } else {
  491. rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum);
  492. }
  493. if (IsSelected) {
  494. rect = Union(rect, Rect(-32, 32, 64, 64));
  495. }
  496. return(Coord_Spillage_List(Coord, rect, true));
  497. }
  498. #else
  499. redraw = redraw;
  500. #endif
  501. return(Coord_Spillage_List(Coord, 56)+1);
  502. }
  503. /***********************************************************************************************
  504. * VesselClass::AI -- Handles the AI processing for vessel objects. *
  505. * *
  506. * This routine is called once for each vessel object during each main game loop. All *
  507. * normal AI processing is handled here. This includes dispatching and maintaining any *
  508. * processing that is specific to vessel objects. *
  509. * *
  510. * INPUT: none *
  511. * *
  512. * OUTPUT: none *
  513. * *
  514. * WARNINGS: none *
  515. * *
  516. * HISTORY: *
  517. * 03/20/1996 JLB : Created. *
  518. * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. *
  519. *=============================================================================================*/
  520. void VesselClass::AI(void)
  521. {
  522. assert(Vessels.ID(this) == ID);
  523. assert(IsActive);
  524. if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) {
  525. Enter_Idle_Mode();
  526. }
  527. /*
  528. ** HACK ALERT:
  529. ** If the ship finds itself in a hunt order but it has no weapons, then tell it
  530. ** to sail off the map instead.
  531. */
  532. if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) {
  533. Assign_Mission(MISSION_RETREAT);
  534. }
  535. /*
  536. ** Act on new orders if the unit is at a good position to do so.
  537. */
  538. if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) {
  539. Commence();
  540. }
  541. #ifndef CLIPDRAW
  542. if (Map.In_View(Coord_Cell(Center_Coord()))) {
  543. Mark(MARK_CHANGE);
  544. }
  545. #endif
  546. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  547. // Re-stock the ammo of any on-board helicopters on an aircraft carrier.
  548. if (*this == VESSEL_CARRIER && How_Many()) {
  549. if (!MoebiusCountDown) {
  550. MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE;
  551. ObjectClass *obj = Attached_Object();
  552. while (obj) {
  553. long bogus;
  554. ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus);
  555. obj = (obj->Next);
  556. }
  557. }
  558. }
  559. #endif
  560. /*
  561. ** Process base class AI routine. If as a result of this, the vessel gets
  562. ** destroyed, then detect this fact and bail early.
  563. */
  564. DriveClass::AI();
  565. if (!IsActive) {
  566. return;
  567. }
  568. /*
  569. ** Handle body and turret rotation.
  570. */
  571. Rotation_AI();
  572. /*
  573. ** Handle any combat processing required.
  574. */
  575. Combat_AI();
  576. /*
  577. ** Delete this unit if it finds itself off the edge of the map and it is in
  578. ** guard or other static mission mode.
  579. */
  580. if (Edge_Of_World_AI()) {
  581. return;
  582. }
  583. if (Class->Max_Passengers() > 0) {
  584. /*
  585. ** Double check that there is a passenger that is trying to load or unload.
  586. ** If not, then close the door.
  587. */
  588. if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) {
  589. LST_Close_Door();
  590. }
  591. }
  592. /*
  593. ** Don't start a new mission unless the vehicle is in the center of
  594. ** a cell (not driving) and the door (if any) is closed.
  595. */
  596. if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
  597. Commence();
  598. }
  599. /*
  600. ** Do a step of repair here, if appropriate.
  601. */
  602. Repair_AI();
  603. }
  604. /***********************************************************************************************
  605. * VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
  606. * *
  607. * This routine is called when the vessel travels one cell. It handles any processes that *
  608. * must occur on a per-cell basis. *
  609. * *
  610. * INPUT: why -- Specifies the circumstances under which this routine was called. *
  611. * *
  612. * OUTPUT: none *
  613. * *
  614. * WARNINGS: none *
  615. * *
  616. * HISTORY: *
  617. * 03/19/1996 JLB : Created. *
  618. *=============================================================================================*/
  619. void VesselClass::Per_Cell_Process(PCPType why)
  620. {
  621. assert(Vessels.ID(this) == ID);
  622. assert(IsActive);
  623. BStart(BENCH_PCP);
  624. if (why == PCP_END) {
  625. CELL cell = Coord_Cell(Coord);
  626. /*
  627. ** The unit performs looking around at this time. If the
  628. ** unit moved further than one square during the last track
  629. ** move, don't do an incremental look. Do a full look around
  630. ** instead.
  631. */
  632. Look(!IsPlanningToLook);
  633. IsPlanningToLook = false;
  634. if (IsToSelfRepair) {
  635. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  636. CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face));
  637. SmartPtr<BuildingClass> whom;
  638. whom = Map[cell].Cell_Building();
  639. if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) {
  640. // MBL 04.27.2020: Make only audible to the correct player
  641. // if (IsOwnedByPlayer) Speak(VOX_REPAIRING);
  642. if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House);
  643. IsSelfRepairing = true;
  644. IsToSelfRepair = false;
  645. break;
  646. }
  647. }
  648. }
  649. /*
  650. ** If this is a loaner unit and is is off the edge of the
  651. ** map, then it gets eliminated.
  652. */
  653. if (Edge_Of_World_AI()) {
  654. BEnd(BENCH_PCP);
  655. return;
  656. }
  657. }
  658. if (IsActive) {
  659. DriveClass::Per_Cell_Process(why);
  660. }
  661. BEnd(BENCH_PCP);
  662. }
  663. /***********************************************************************************************
  664. * VesselClass::What_Action -- Determines what action would occur if clicked on object. *
  665. * *
  666. * Use this function to determine what action would likely occur if the specified object *
  667. * were clicked on while this unit was selected as current. This function controls, not *
  668. * only the action to perform, but indirectly controls the cursor shape to use as well. *
  669. * *
  670. * INPUT: object -- The object that to check for against "this" object. *
  671. * *
  672. * OUTPUT: Returns with the default action to perform. If no clear action can be determined, *
  673. * then ACTION_NONE is returned. *
  674. * *
  675. * WARNINGS: none *
  676. * *
  677. * HISTORY: *
  678. * 04/16/1996 BWG : Created. *
  679. *=============================================================================================*/
  680. ActionType VesselClass::What_Action(ObjectClass const * object) const
  681. {
  682. assert(Vessels.ID(this) == ID);
  683. assert(IsActive);
  684. ActionType action = DriveClass::What_Action(object);
  685. if (action == ACTION_SELF) {
  686. if (Class->Max_Passengers() == 0 || !How_Many() ) {
  687. action = ACTION_NONE;
  688. } else {
  689. // check to see if the transporter can unload.
  690. bool found = 0;
  691. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  692. if (*this != VESSEL_CARRIER)
  693. #endif
  694. for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) {
  695. CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
  696. CellClass * cell = &Map[cellnum];
  697. if (Map.In_Radar(cellnum)) {
  698. if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) {
  699. continue;
  700. } else {
  701. found = true;
  702. }
  703. }
  704. }
  705. if (!found) {
  706. action = ACTION_NONE;
  707. }
  708. }
  709. }
  710. /*
  711. ** Special return to friendly repair factory action.
  712. */
  713. if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
  714. BuildingClass * building = (BuildingClass *)object;
  715. if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) {
  716. action = ACTION_ENTER;
  717. }
  718. }
  719. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  720. if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL &&
  721. (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) {
  722. action = ACTION_NOMOVE;
  723. }
  724. #endif
  725. /*
  726. ** If it doesn't know what to do with the object, then just
  727. ** say it can't move there.
  728. */
  729. if (action == ACTION_NONE) action = ACTION_NOMOVE;
  730. return(action);
  731. }
  732. /***********************************************************************************************
  733. * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
  734. * *
  735. * This routine intercepts the active click operation. It check to see if this is a self *
  736. * deployment request (MCV's have this ability). If it is, then the object is initiated *
  737. * to self deploy. In the other cases, it passes the operation down to the lower *
  738. * classes for processing. *
  739. * *
  740. * INPUT: action -- The action requested of the unit. *
  741. * *
  742. * object -- The object that the mouse pointer is over. *
  743. * *
  744. * OUTPUT: none *
  745. * *
  746. * WARNINGS: none *
  747. * *
  748. * HISTORY: *
  749. * 04/16/1996 BWG : Created. *
  750. *=============================================================================================*/
  751. void VesselClass::Active_Click_With(ActionType action, ObjectClass * object)
  752. {
  753. assert(Vessels.ID(this) == ID);
  754. assert(IsActive);
  755. // if (action != What_Action(object)) {
  756. action = What_Action(object);
  757. switch (action) {
  758. case ACTION_ENTER:
  759. action = ACTION_MOVE;
  760. // BRR 10/18/96 IsToSelfRepair = true;
  761. break;
  762. default:
  763. // action = ACTION_NONE;
  764. break;
  765. }
  766. // }
  767. // if (action == ACTION_ENTER) {
  768. // BRR 10/18/96 IsToSelfRepair = true;
  769. // action = ACTION_MOVE;
  770. // } else {
  771. // if (action != ACTION_NONE) {
  772. // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false;
  773. // }
  774. // }
  775. DriveClass::Active_Click_With(action, object);
  776. }
  777. /***********************************************************************************************
  778. * VesselClass::Active_Click_With -- Performs specified action on specified cell. *
  779. * *
  780. * This routine is called when the mouse has been clicked over a cell and this unit must *
  781. * now respond. Notice that this is merely a placeholder function that exists because there *
  782. * is another function of the same name that needs to be overloaded. C++ has scoping *
  783. * restrictions when there are two identically named functions that are overridden in *
  784. * different classes -- it handles it badly, hence the existence of this routine. *
  785. * *
  786. * INPUT: action -- The action to perform on the cell specified. *
  787. * *
  788. * cell -- The cell that the action is to be performed on. *
  789. * *
  790. * OUTPUT: none *
  791. * *
  792. * WARNINGS: none *
  793. * *
  794. * HISTORY: *
  795. * 04/16/1996 BWG : Created. *
  796. *=============================================================================================*/
  797. void VesselClass::Active_Click_With(ActionType action, CELL cell)
  798. {
  799. assert(Vessels.ID(this) == ID);
  800. assert(IsActive);
  801. // BRR 10/18/96 IsToSelfRepair = false;
  802. // if (action != ACTION_NONE) {
  803. // BRR 10/18/96 IsSelfRepairing = false;
  804. // }
  805. DriveClass::Active_Click_With(action, cell);
  806. }
  807. /***********************************************************************************************
  808. * VesselClass::Take_Damage -- Assign damage to the vessel. *
  809. * *
  810. * This routine is called to apply damage to this vessel. The amount and type of damage *
  811. * to apply is passed as parameters. This routine could end up destroying the vessel. *
  812. * *
  813. * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage *
  814. * value will be adjusted so that the actual damage applied will be *
  815. * stored into this variable for possible subsequent examination. *
  816. * *
  817. * distance -- The distance from the center of the damage to the vessel itself. *
  818. * *
  819. * warhead -- The warhead type of damage to apply. *
  820. * *
  821. * source -- The perpetrator of this damage. Knowing who was responsible allows *
  822. * retaliation logic. *
  823. * *
  824. * forced -- Is this damage forced upon the vessel by some supernatural means? *
  825. * *
  826. * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the *
  827. * general effect of the damage. Examine this return value to see if the vessel *
  828. * has been destroyed. *
  829. * *
  830. * WARNINGS: The vessel could be destroyed by the call to this routine! *
  831. * *
  832. * HISTORY: *
  833. * 05/13/1996 JLB : Created. *
  834. *=============================================================================================*/
  835. ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced)
  836. {
  837. assert(Vessels.ID(this) == ID);
  838. assert(IsActive);
  839. ResultType res = RESULT_NONE;
  840. /*
  841. ** In order for a this to be damaged, it must either be a unit
  842. ** with a crew or a sandworm.
  843. */
  844. res = FootClass::Take_Damage(damage, distance, warhead, source, forced);
  845. if (res == RESULT_DESTROYED) {
  846. Death_Announcement(source);
  847. if (Class->Explosion != ANIM_NONE) {
  848. AnimType anim = Class->Explosion;
  849. new AnimClass(anim, Coord);
  850. /*
  851. ** Very strong units that have an explosion will also rock the
  852. ** screen when they are destroyed.
  853. */
  854. if (Class->MaxStrength > 400) {
  855. int shakes = Class->MaxStrength / 150;
  856. Shake_The_Screen(shakes, Owner());
  857. if (source && Owner() != source->Owner()) {
  858. Shake_The_Screen(shakes, source->Owner());
  859. }
  860. }
  861. }
  862. /*
  863. ** Possibly have the crew member run away.
  864. */
  865. Mark(MARK_UP);
  866. while (Is_Something_Attached()) {
  867. FootClass * object = Detach_Object();
  868. /*
  869. ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure
  870. ** thing.
  871. */
  872. object->Record_The_Kill(source);
  873. delete object;
  874. }
  875. /*
  876. ** Finally, delete the vehicle.
  877. */
  878. delete this;
  879. } else {
  880. /*
  881. ** When damaged and below half strength, start smoking if
  882. ** it isn't already smoking (and it's not a submarine).
  883. */
  884. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  885. if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) {
  886. #else
  887. if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) {
  888. #endif
  889. AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
  890. if (anim != NULL) anim->Attach_To(this);
  891. }
  892. }
  893. return(res);
  894. }
  895. /***********************************************************************************************
  896. * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
  897. * *
  898. * This routine is used to determine if this vessel can fire its weapon at the target *
  899. * specified. *
  900. * *
  901. * INPUT: target -- The target candidate to determine if firing upon is valid. *
  902. * *
  903. * which -- Which weapon to use when considering the candidate as a potential *
  904. * target. *
  905. * *
  906. * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If *
  907. * it can't fire, then the enum indicates why. *
  908. * *
  909. * WARNINGS: none *
  910. * *
  911. * HISTORY: *
  912. * 05/13/1996 JLB : Created. *
  913. *=============================================================================================*/
  914. FireErrorType VesselClass::Can_Fire(TARGET target, int which) const
  915. {
  916. assert(Vessels.ID(this) == ID);
  917. assert(IsActive);
  918. DirType dir; // The facing to impart upon the projectile.
  919. int diff;
  920. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  921. if (*this == VESSEL_CARRIER) {
  922. if(!How_Many() || Arm) {
  923. return(FIRE_REARM);
  924. } else {
  925. return(FIRE_OK);
  926. }
  927. }
  928. #endif
  929. FireErrorType fire = DriveClass::Can_Fire(target, which);
  930. if(*this==VESSEL_DD) {
  931. Mono_Set_Cursor(0,0);
  932. }
  933. if (fire == FIRE_OK || fire == FIRE_CLOAKED) {
  934. WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
  935. /*
  936. ** Ensure that a torpedo will never be fired upon a non naval target.
  937. ** Unless that non-naval target is a naval building (sub pen/ship yard)
  938. */
  939. bool isseatarget = Is_Target_Vessel(target);
  940. bool isbridgetarget = false;
  941. if (weapon->Bullet->IsSubSurface) {
  942. isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges
  943. isseatarget |= isbridgetarget;
  944. }
  945. BuildingClass * bldg = ::As_Building(target);
  946. if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) {
  947. isseatarget = true;
  948. }
  949. dir = Direction(target);
  950. if (weapon->Bullet->IsSubSurface) {
  951. if (!isseatarget && Is_Target_Object(target)) {
  952. return(FIRE_CANT);
  953. }
  954. /*
  955. ** If it's a torpedo, let's check line-of-sight to make sure that
  956. ** there's only water squares between us and the target.
  957. */
  958. ObjectClass * obj = As_Object(target);
  959. COORDINATE coord = Center_Coord();
  960. if (obj != NULL) {
  961. int totaldist = ::Distance(coord, obj->Center_Coord());
  962. while (totaldist > CELL_LEPTON_W) {
  963. coord = Coord_Move(coord, dir, CELL_LEPTON_W);
  964. if (Map[coord].Land_Type() != LAND_WATER) {
  965. if (!isbridgetarget) {
  966. return(FIRE_RANGE);
  967. }
  968. }
  969. /*
  970. ** Check for friendly boats in the way.
  971. */
  972. TechnoClass * tech = Map[coord].Cell_Techno();
  973. if (tech != NULL && tech != this && House->Is_Ally(tech)) {
  974. return(FIRE_RANGE);
  975. }
  976. totaldist -= CELL_LEPTON_W;
  977. }
  978. }
  979. }
  980. /*
  981. ** Depth charges are only good against submarines.
  982. */
  983. if (weapon->Bullet->IsAntiSub) {
  984. if (!isseatarget) {
  985. return(FIRE_CANT);
  986. } else {
  987. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  988. if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) {
  989. #else
  990. if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) {
  991. #endif
  992. if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) {
  993. return(FIRE_CANT);
  994. }
  995. }
  996. }
  997. } else {
  998. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  999. if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) {
  1000. #else
  1001. if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) {
  1002. #endif
  1003. return(FIRE_CANT);
  1004. }
  1005. }
  1006. /*
  1007. ** If this unit cannot fire while moving, then bail.
  1008. */
  1009. if (!Class->IsTurretEquipped && Target_Legal(NavCom)) {
  1010. return(FIRE_MOVING);
  1011. }
  1012. /*
  1013. ** If the turret is rotating and the projectile isn't a homing type, then
  1014. ** firing must be delayed until the rotation stops.
  1015. */
  1016. if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) {
  1017. return(FIRE_ROTATING);
  1018. }
  1019. /*
  1020. ** Determine if the turret facing isn't too far off of facing the target.
  1021. */
  1022. if (Class->IsTurretEquipped) {
  1023. diff = SecondaryFacing.Difference(dir);
  1024. } else {
  1025. diff = PrimaryFacing.Difference(dir);
  1026. }
  1027. diff = ABS(diff);
  1028. if (weapon->Bullet->ROT != 0) {
  1029. diff >>= 2;
  1030. }
  1031. if (diff > 8) {
  1032. return(FIRE_FACING);
  1033. }
  1034. }
  1035. return(fire);
  1036. }
  1037. /***********************************************************************************************
  1038. * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
  1039. * *
  1040. * This routine is called to determine the coordinate that a fired projectile will *
  1041. * originate from. *
  1042. * *
  1043. * INPUT: which -- Which weapon is this query directed at? *
  1044. * *
  1045. * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. *
  1046. * *
  1047. * WARNINGS: none *
  1048. * *
  1049. * HISTORY: *
  1050. * 05/13/1996 JLB : Created. *
  1051. *=============================================================================================*/
  1052. FireDataType VesselClass::Fire_Data(int which) const
  1053. {
  1054. assert(Vessels.ID(this) == ID);
  1055. assert(IsActive);
  1056. COORDINATE coord = Center_Coord();
  1057. if (*this == VESSEL_CA) {
  1058. if (IsSecondShot) {
  1059. coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100);
  1060. } else {
  1061. coord = Coord_Move(coord, PrimaryFacing, 0x0100);
  1062. }
  1063. coord = Coord_Move(coord, DIR_N, 0x0030);
  1064. return{coord,0x0040};
  1065. }
  1066. if (*this == VESSEL_PT) {
  1067. coord = Coord_Move(coord, PrimaryFacing, 0x0080);
  1068. coord = Coord_Move(coord, DIR_N, 0x0020);
  1069. return{coord,0x0010};
  1070. }
  1071. return(DriveClass::Fire_Data(which));
  1072. }
  1073. COORDINATE VesselClass::Fire_Coord(int which) const
  1074. {
  1075. assert(Vessels.ID(this) == ID);
  1076. assert(IsActive);
  1077. COORDINATE coord = Center_Coord();
  1078. if (*this == VESSEL_CA) {
  1079. if (IsSecondShot) {
  1080. coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100);
  1081. } else {
  1082. coord = Coord_Move(coord, PrimaryFacing, 0x0100);
  1083. }
  1084. coord = Coord_Move(coord, DIR_N, 0x0030);
  1085. coord = Coord_Move(coord, Turret_Facing(), 0x0040);
  1086. return(coord);
  1087. }
  1088. if (*this == VESSEL_PT) {
  1089. coord = Coord_Move(coord, PrimaryFacing, 0x0080);
  1090. coord = Coord_Move(coord, DIR_N, 0x0020);
  1091. coord = Coord_Move(coord, Turret_Facing(), 0x0010);
  1092. return(coord);
  1093. }
  1094. return(DriveClass::Fire_Coord(which));
  1095. }
  1096. /***********************************************************************************************
  1097. * VesselClass::Init -- Initialize the vessel heap system. *
  1098. * *
  1099. * This routine is used to clear out the vessel heap. It is called whenever a scenario is *
  1100. * being initialized prior to scenario or saved game loading. *
  1101. * *
  1102. * INPUT: none *
  1103. * *
  1104. * OUTPUT: none *
  1105. * *
  1106. * WARNINGS: All vessel objects are invalid after this routine is called. *
  1107. * *
  1108. * HISTORY: *
  1109. * 05/13/1996 JLB : Created. *
  1110. *=============================================================================================*/
  1111. void VesselClass::Init(void)
  1112. {
  1113. Vessels.Free_All();
  1114. }
  1115. /***********************************************************************************************
  1116. * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel *
  1117. * *
  1118. * This routine is used by ships to determine what target they should go after. *
  1119. * *
  1120. * INPUT: threat -- The threat type that this ship should go after (as determined by the *
  1121. * team mission or general self defense principles). *
  1122. * *
  1123. * OUTPUT: Returns with the target that this ship should attack. *
  1124. * *
  1125. * WARNINGS: none *
  1126. * *
  1127. * HISTORY: *
  1128. * 05/13/1996 JLB : Created. *
  1129. *=============================================================================================*/
  1130. TARGET VesselClass::Greatest_Threat(ThreatType threat) const
  1131. {
  1132. if (*this == VESSEL_SS) {
  1133. threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA);
  1134. threat = threat | THREAT_BOATS;
  1135. //BG: get subs to attack buildings also.
  1136. threat = threat | THREAT_BUILDINGS;
  1137. threat = threat | THREAT_FACTORIES;
  1138. } else {
  1139. if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) {
  1140. if (Class->PrimaryWeapon != NULL) {
  1141. threat = threat | Class->PrimaryWeapon->Allowed_Threats();
  1142. }
  1143. if (Class->SecondaryWeapon != NULL) {
  1144. threat = threat | Class->SecondaryWeapon->Allowed_Threats();
  1145. }
  1146. // threat = threat | THREAT_GROUND | THREAT_BOATS;
  1147. }
  1148. // Cruisers can never hit infantry anyway, so take 'em out of the list
  1149. // of possible targets.
  1150. if (*this == VESSEL_CA) {
  1151. threat = (ThreatType) (threat & (~THREAT_INFANTRY));
  1152. }
  1153. }
  1154. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  1155. if (*this == VESSEL_CARRIER) {
  1156. return(TARGET_NONE);
  1157. }
  1158. #endif
  1159. return(FootClass::Greatest_Threat(threat));
  1160. }
  1161. /***********************************************************************************************
  1162. * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
  1163. * *
  1164. * This routine is called when the vessel is finished with what it is doing, but the next *
  1165. * action is not known. This routine will determine what is the appropriate course of *
  1166. * action for this vessel and then start it doing that. *
  1167. * *
  1168. * INPUT: none *
  1169. * *
  1170. * OUTPUT: none *
  1171. * *
  1172. * WARNINGS: none *
  1173. * *
  1174. * HISTORY: *
  1175. * 07/09/1996 JLB : Created. *
  1176. *=============================================================================================*/
  1177. void VesselClass::Enter_Idle_Mode(bool )
  1178. {
  1179. assert(Vessels.ID(this) == ID);
  1180. assert(IsActive);
  1181. MissionType order = MISSION_GUARD;
  1182. /*
  1183. ** A movement mission without a NavCom would be pointless to have a radio contact since
  1184. ** no radio coordination occurs on a just a simple movement mission.
  1185. */
  1186. if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) {
  1187. Transmit_Message(RADIO_OVER_OUT);
  1188. }
  1189. Handle_Navigation_List();
  1190. if (Target_Legal(NavCom)) {
  1191. order = MISSION_MOVE;
  1192. } else {
  1193. if (Class->PrimaryWeapon == NULL) {
  1194. if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) {
  1195. order = MISSION_UNLOAD;
  1196. } else {
  1197. order = MISSION_GUARD;
  1198. Assign_Target(TARGET_NONE);
  1199. Assign_Destination(TARGET_NONE);
  1200. }
  1201. } else {
  1202. if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) {
  1203. return;
  1204. }
  1205. if (House->IsHuman || Team.Is_Valid()) {
  1206. order = MISSION_GUARD;
  1207. } else {
  1208. if (House->IQ < Rule.IQGuardArea) {
  1209. order = MISSION_GUARD;
  1210. } else {
  1211. order = MISSION_GUARD_AREA;
  1212. }
  1213. }
  1214. }
  1215. }
  1216. Assign_Mission(order);
  1217. }
  1218. /***********************************************************************************************
  1219. * VesselClass::Receive_Message -- Handles receiving a radio message. *
  1220. * *
  1221. * This is the handler function for when a vessel receives a radio *
  1222. * message. Typical use of this is when a unit unloads from a lst *
  1223. * class so that clearing of the transport is successful. *
  1224. * *
  1225. * INPUT: from -- Pointer to the originator of the message. *
  1226. * *
  1227. * message -- The radio message received. *
  1228. * *
  1229. * param -- Reference to an optional parameter the might be needed to return *
  1230. * information back to the originator of the message. *
  1231. * *
  1232. * OUTPUT: Returns with the radio message response. *
  1233. * *
  1234. * WARNINGS: none *
  1235. * *
  1236. * HISTORY: *
  1237. * 05/31/1996 BWG : Created. *
  1238. *=============================================================================================*/
  1239. RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
  1240. {
  1241. assert(Vessels.ID(this) == ID);
  1242. assert(IsActive);
  1243. switch (message) {
  1244. /*
  1245. ** Asks if the passenger can load on this transport.
  1246. */
  1247. case RADIO_CAN_LOAD:
  1248. if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC);
  1249. if (How_Many() < Class->Max_Passengers()) {
  1250. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  1251. if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) {
  1252. return(RADIO_ROGER);
  1253. }
  1254. #endif
  1255. /*
  1256. ** Before saying "Sure, come on board", make sure we're adjacent to
  1257. ** the shore.
  1258. */
  1259. CELL cell;
  1260. Desired_Load_Dir(from, cell);
  1261. if(cell) {
  1262. return(RADIO_ROGER);
  1263. }
  1264. }
  1265. return(RADIO_NEGATIVE);
  1266. /*
  1267. ** This message is sent by the passenger when it determines that it has
  1268. ** entered the transport.
  1269. */
  1270. case RADIO_IM_IN:
  1271. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  1272. if(*this != VESSEL_CARRIER) {
  1273. #endif
  1274. if (How_Many() == Class->Max_Passengers()) {
  1275. LST_Close_Door();
  1276. } else {
  1277. DoorShutCountDown = TICKS_PER_SECOND;
  1278. }
  1279. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  1280. }
  1281. #endif
  1282. return(RADIO_ATTACH);
  1283. /*
  1284. ** Docking maintenance message received. Check to see if new orders should be given
  1285. ** to the impatient unit.
  1286. */
  1287. case RADIO_DOCKING:
  1288. /*
  1289. ** If this transport is moving, then always abort the docking request.
  1290. */
  1291. if (IsDriving || Target_Legal(NavCom)) {
  1292. return(RADIO_NEGATIVE);
  1293. }
  1294. /*
  1295. ** Check for the case of a docking message arriving from a unit that does not
  1296. ** have formal radio contact established. This might be a unit that is standing
  1297. ** by. If this transport is free to proceed with normal docking operation, then
  1298. ** establish formal contact now. If the transport is completely full, then break
  1299. ** off contact. In all other cases, just tell the pending unit to stand by.
  1300. */
  1301. if (Contact_With_Whom() != from) {
  1302. /*
  1303. ** Can't ever load up so tell the passenger to bug off.
  1304. */
  1305. if (How_Many() >= Class->Max_Passengers()) {
  1306. return(RADIO_NEGATIVE);
  1307. }
  1308. /*
  1309. ** Establish contact and let the loading process proceed normally.
  1310. */
  1311. if (!In_Radio_Contact()) {
  1312. param = TARGET_NONE;
  1313. Transmit_Message(RADIO_HELLO, from);
  1314. Transmit_Message(RADIO_MOVE_HERE, param);
  1315. return(RADIO_ROGER);
  1316. } else {
  1317. /*
  1318. ** This causes the potential passenger to think that all is ok and to
  1319. ** hold on for a bit.
  1320. */
  1321. return(RADIO_ROGER);
  1322. }
  1323. }
  1324. /*
  1325. **
  1326. */
  1327. if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) {
  1328. DriveClass::Receive_Message(from, message, param);
  1329. if (!IsDriving && !IsRotating) {
  1330. // if (!IsDriving && !IsRotating && !IsTethered) {
  1331. /*
  1332. ** If the potential passenger needs someplace to go, then figure out a good
  1333. ** spot and tell it to go.
  1334. */
  1335. if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
  1336. CELL cell;
  1337. DirType dir = Desired_Load_Dir(from, cell);
  1338. /*
  1339. ** If no adjacent free cells are detected, then passenger loading
  1340. ** cannot occur. Break radio contact.
  1341. */
  1342. if (cell == 0) {
  1343. Transmit_Message(RADIO_OVER_OUT, from);
  1344. } else {
  1345. param = (long)::As_Target(cell);
  1346. /*
  1347. ** If it is now facing the correct direction, then open the
  1348. ** transport doors. Close the doors if the transport is full or needs
  1349. ** to rotate.
  1350. */
  1351. if (!Is_Door_Open()) {
  1352. LST_Open_Door();
  1353. }
  1354. /*
  1355. ** Tell the potential passenger where it should go. If the passenger is
  1356. ** already at the staging location, then tell it to move onto the transport
  1357. ** directly.
  1358. */
  1359. if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
  1360. if (Is_Door_Open()) {
  1361. param = (long)As_Target();
  1362. Transmit_Message(RADIO_TETHER);
  1363. if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
  1364. Transmit_Message(RADIO_OVER_OUT, from);
  1365. } else {
  1366. Contact_With_Whom()->Unselect();
  1367. }
  1368. }
  1369. }
  1370. }
  1371. }
  1372. }
  1373. return(RADIO_ROGER);
  1374. }
  1375. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  1376. if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) {
  1377. TechnoClass::Receive_Message(from, message, param);
  1378. /*
  1379. ** Establish contact with the object if this building isn't already in contact
  1380. ** with another.
  1381. */
  1382. if (!In_Radio_Contact()) {
  1383. Transmit_Message(RADIO_HELLO, from);
  1384. }
  1385. if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
  1386. param = As_Target();
  1387. if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
  1388. Transmit_Message(RADIO_TETHER);
  1389. }
  1390. }
  1391. return(RADIO_ROGER);
  1392. }
  1393. #endif
  1394. break;
  1395. /*
  1396. ** When this message is received, it means that the other object
  1397. ** has already turned its radio off. Turn this radio off as well.
  1398. */
  1399. case RADIO_OVER_OUT:
  1400. if (Mission == MISSION_RETURN) {
  1401. Assign_Mission(MISSION_GUARD);
  1402. }
  1403. DriveClass::Receive_Message(from, message, param);
  1404. return(RADIO_ROGER);
  1405. }
  1406. return(DriveClass::Receive_Message(from, message, param));
  1407. }
  1408. /***********************************************************************************************
  1409. * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
  1410. * *
  1411. * This routine examines the unit and adjacent cells in order to find the best facing *
  1412. * for the transport and best staging cell for the potential passengers. This location is *
  1413. * modified by adjacent cell passability and direction of the potential passenger. *
  1414. * *
  1415. * INPUT: passenger -- Pointer to the potential passenger. *
  1416. * *
  1417. * moveto -- Reference to the cell number that specifies where the potential *
  1418. * passenger should move to first. *
  1419. * *
  1420. * OUTPUT: Returns with the direction the transport should face before opening the transport *
  1421. * door. *
  1422. * *
  1423. * WARNINGS: none *
  1424. * *
  1425. * HISTORY: *
  1426. * 06/01/1996 BWG : Created. *
  1427. *=============================================================================================*/
  1428. DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
  1429. {
  1430. assert(Vessels.ID(this) == ID);
  1431. assert(IsActive);
  1432. /*
  1433. ** Determine the ideal facing that provides the least resistance. This would be the direction
  1434. ** of the potential passenger or the current transport facing if it is going to unload.
  1435. */
  1436. DirType faceto;
  1437. if (passenger != NULL) {
  1438. faceto = Direction(passenger);
  1439. } else {
  1440. faceto = PrimaryFacing.Current() + DIR_S;
  1441. }
  1442. /*
  1443. ** Sweep through the adjacent cells in order to find the best candidate.
  1444. */
  1445. FacingType bestdir = FACING_N;
  1446. int bestval = -1;
  1447. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  1448. int value = 0;
  1449. CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
  1450. /*
  1451. ** Base the initial value of the potential cell according to whether the passenger is
  1452. ** allowed to enter the cell. If it can't, then give such a negative value to the
  1453. ** cell so that it is prevented from ever choosing that cell for load/unload.
  1454. */
  1455. if (passenger != NULL) {
  1456. value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128;
  1457. } else {
  1458. CellClass * cell = &Map[cellnum];
  1459. if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) {
  1460. value = -128;
  1461. } else {
  1462. if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) {
  1463. value = -128;
  1464. } else {
  1465. value = 128;
  1466. }
  1467. }
  1468. }
  1469. #if(0)
  1470. /*
  1471. ** Give more weight to the cells that require the least rotation of the transport or the
  1472. ** least roundabout movement for the potential passenger.
  1473. */
  1474. value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto);
  1475. if (face == FACING_S) {
  1476. value -= 100;
  1477. }
  1478. if (face == FACING_SW || face == FACING_SE) value += 64;
  1479. #endif
  1480. /*
  1481. ** If the value for the potential cell is greater than the last recorded potential
  1482. ** value, then record this cell as the best candidate.
  1483. */
  1484. if (bestval == -1 || value > bestval) {
  1485. bestval = value;
  1486. bestdir = face;
  1487. // } else {
  1488. // ObjectClass * obj = Map[cellnum].Cell_Occupier();
  1489. // if (obj) obj->Scatter(Coord, true);
  1490. }
  1491. }
  1492. /*
  1493. ** If a suitable direction was found, then return with the direction value.
  1494. */
  1495. moveto = 0;
  1496. if (bestval > 0) {
  1497. static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE};
  1498. moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir);
  1499. return(_desired_to_actual[bestdir]);
  1500. }
  1501. return(DIR_N);
  1502. }
  1503. /***********************************************************************************************
  1504. * VesselClass::LST_Open_Door -- Opens a LST door. *
  1505. * *
  1506. * This routine will initiate opening of the doors on the LST. *
  1507. * *
  1508. * INPUT: none *
  1509. * *
  1510. * OUTPUT: none *
  1511. * *
  1512. * WARNINGS: none *
  1513. * *
  1514. * HISTORY: *
  1515. * 06/01/1996 BWG : Created. *
  1516. *=============================================================================================*/
  1517. void VesselClass::LST_Open_Door(void)
  1518. {
  1519. assert(Vessels.ID(this) == ID);
  1520. assert(IsActive);
  1521. if (!IsDriving && !IsRotating) {
  1522. Open_Door(5, 6);
  1523. }
  1524. }
  1525. /***********************************************************************************************
  1526. * VesselClass::LST_Close_Door -- Closes a LST door. *
  1527. * *
  1528. * This routine will initiate closing of the LST door. *
  1529. * *
  1530. * INPUT: none *
  1531. * *
  1532. * OUTPUT: none *
  1533. * *
  1534. * WARNINGS: none *
  1535. * *
  1536. * HISTORY: *
  1537. * 06/01/1996 BWG : Created. *
  1538. *=============================================================================================*/
  1539. void VesselClass::LST_Close_Door(void)
  1540. {
  1541. assert(Vessels.ID(this) == ID);
  1542. assert(IsActive);
  1543. Close_Door(5, 6);
  1544. }
  1545. /***********************************************************************************************
  1546. * VesselClass::Mission_Unload -- Handles unloading cargo. *
  1547. * *
  1548. * This is the AI control sequence for when a transport desires to unload its cargo. *
  1549. * *
  1550. * INPUT: none *
  1551. * *
  1552. * OUTPUT: Returns with the delay before calling this routine again. *
  1553. * *
  1554. * WARNINGS: none *
  1555. * *
  1556. * HISTORY: *
  1557. * 06/01/1996 BWG : Created. *
  1558. *=============================================================================================*/
  1559. int VesselClass::Mission_Unload(void)
  1560. {
  1561. assert(Vessels.ID(this) == ID);
  1562. assert(IsActive);
  1563. enum {
  1564. INITIAL_CHECK,
  1565. MANEUVERING,
  1566. OPENING_DOOR,
  1567. UNLOADING,
  1568. CLOSING_DOOR
  1569. };
  1570. DirType dir;
  1571. CELL cell;
  1572. switch (Class->Type) {
  1573. case VESSEL_TRANSPORT:
  1574. switch (Status) {
  1575. case INITIAL_CHECK:
  1576. dir = Desired_Load_Dir(NULL, cell);
  1577. if (How_Many() > 0 && cell != 0) {
  1578. Do_Turn(dir);
  1579. Status = MANEUVERING;
  1580. return(1);
  1581. } else {
  1582. if (!How_Many()) { // don't break out if still carrying passengers
  1583. Assign_Mission(MISSION_GUARD);
  1584. }
  1585. }
  1586. break;
  1587. case MANEUVERING:
  1588. if (!IsRotating) {
  1589. LST_Open_Door();
  1590. if (Is_Door_Opening()) {
  1591. Status = OPENING_DOOR;
  1592. return(1);
  1593. }
  1594. }
  1595. break;
  1596. case OPENING_DOOR:
  1597. if (Is_Door_Open()) {
  1598. Status = UNLOADING;
  1599. return(1);
  1600. } else {
  1601. if (!Is_Door_Opening()) {
  1602. Status = INITIAL_CHECK;
  1603. }
  1604. }
  1605. break;
  1606. case UNLOADING:
  1607. if (How_Many()) {
  1608. /*
  1609. ** Don't do anything if still in radio contact.
  1610. */
  1611. if (In_Radio_Contact()) return(TICKS_PER_SECOND);
  1612. FootClass * passenger = Detach_Object();
  1613. if (passenger != NULL) {
  1614. DirType toface = DIR_S + PrimaryFacing;
  1615. bool placed = false;
  1616. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  1617. DirType newface = toface + Facing_Dir(face);
  1618. CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  1619. if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
  1620. ScenarioInit++;
  1621. passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface);
  1622. ScenarioInit--;
  1623. passenger->Assign_Mission(MISSION_MOVE);
  1624. passenger->Assign_Destination(::As_Target(newcell));
  1625. passenger->Commence();
  1626. Transmit_Message(RADIO_HELLO, passenger);
  1627. Transmit_Message(RADIO_TETHER, passenger);
  1628. if (passenger->What_Am_I() == RTTI_UNIT) {
  1629. ((UnitClass *)passenger)->IsToScatter = true;
  1630. }
  1631. placed = true;
  1632. break;
  1633. }
  1634. }
  1635. /*
  1636. ** If the attached unit could NOT be deployed, then re-attach
  1637. ** it and then bail out of this deploy process.
  1638. */
  1639. if (!placed) {
  1640. Attach(passenger);
  1641. /*
  1642. ** Tell everyone around the transport to scatter.
  1643. */
  1644. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  1645. CellClass * cellptr = Map[Coord].Adjacent_Cell(face);
  1646. if (cellptr && cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
  1647. cellptr->Incoming(0, true);
  1648. }
  1649. }
  1650. // Status = CLOSING_DOOR;
  1651. }
  1652. else {
  1653. passenger->Look(false);
  1654. }
  1655. }
  1656. } else {
  1657. Status = CLOSING_DOOR;
  1658. }
  1659. break;
  1660. /*
  1661. ** Close LST door in preparation for normal operation.
  1662. */
  1663. case CLOSING_DOOR:
  1664. if (Is_Door_Open()) {
  1665. LST_Close_Door();
  1666. }
  1667. if (Is_Door_Closed()) {
  1668. if (IsALoaner) {
  1669. Assign_Mission(MISSION_RETREAT);
  1670. } else {
  1671. Assign_Mission(MISSION_GUARD);
  1672. }
  1673. }
  1674. break;
  1675. }
  1676. break;
  1677. default:
  1678. break;
  1679. }
  1680. return(MissionControl[Mission].Normal_Delay());
  1681. }
  1682. /***********************************************************************************************
  1683. * VesselClass::Assign_Destination -- Assign a destination for this vessel. *
  1684. * *
  1685. * This routine is called when a destination is to be assigned to this vessel. *
  1686. * *
  1687. * INPUT: target -- The destination to assign to this vessel. *
  1688. * *
  1689. * OUTPUT: none *
  1690. * *
  1691. * WARNINGS: none *
  1692. * *
  1693. * HISTORY: *
  1694. * 07/09/1996 JLB : Created. *
  1695. *=============================================================================================*/
  1696. void VesselClass::Assign_Destination(TARGET target)
  1697. {
  1698. assert(IsActive);
  1699. /*
  1700. ** Abort early if there is anything wrong with the parameters
  1701. ** or the unit already is assigned the specified destination.
  1702. */
  1703. if (target == NavCom) return;
  1704. /*
  1705. ** Transport vehicles must tell all passengers that are about to load, that they
  1706. ** cannot proceed. This is accomplished with a radio message to this effect.
  1707. */
  1708. if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) {
  1709. long param = TARGET_NONE;
  1710. Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport.
  1711. Transmit_Message(RADIO_OVER_OUT);
  1712. if (!Is_Door_Closed()) {
  1713. LST_Close_Door();
  1714. }
  1715. }
  1716. if (!Is_Door_Closed()) {
  1717. LST_Close_Door();
  1718. }
  1719. DriveClass::Assign_Destination(target);
  1720. }
  1721. /***********************************************************************************************
  1722. * VesselClass::Pip_Count -- Fetches the number of pips to display on vessel. *
  1723. * *
  1724. * This routine is used to fetch the number of "fullness" pips to display on the vessel. *
  1725. * This will be the number of passengers on a transport. *
  1726. * *
  1727. * INPUT: none *
  1728. * *
  1729. * OUTPUT: Returns with the number of pips to draw on this unit. *
  1730. * *
  1731. * WARNINGS: none *
  1732. * *
  1733. * HISTORY: *
  1734. * 06/25/1995 JLB : Created. *
  1735. *=============================================================================================*/
  1736. int VesselClass::Pip_Count(void) const
  1737. {
  1738. if (Techno_Type_Class()->Max_Passengers() > 0) {
  1739. int passengers = 0;
  1740. ObjectClass const * object = Attached_Object();
  1741. for (int index = 0; index < Class_Of().Max_Pips(); index++) {
  1742. if (object != NULL) {
  1743. passengers++;
  1744. object = object->Next;
  1745. }
  1746. }
  1747. return passengers;
  1748. }
  1749. return 0;
  1750. }
  1751. /***********************************************************************************************
  1752. * VesselClass::Mission_Retreat -- Perform the retreat mission. *
  1753. * *
  1754. * This will cause the vessel to run away from the battlefield. It searches for an escape *
  1755. * map edge according to the reinforcement edge specified in the house. *
  1756. * *
  1757. * INPUT: none *
  1758. * *
  1759. * OUTPUT: Returns with the number of game frames to delay before this routine is called *
  1760. * again. *
  1761. * *
  1762. * WARNINGS: none *
  1763. * *
  1764. * HISTORY: *
  1765. * 07/09/1996 JLB : Created. *
  1766. *=============================================================================================*/
  1767. int VesselClass::Mission_Retreat(void)
  1768. {
  1769. assert(Vessels.ID(this) == ID);
  1770. assert(IsActive);
  1771. enum {
  1772. PICK_RETREAT_POINT,
  1773. TRAVEL
  1774. };
  1775. switch (Status) {
  1776. case PICK_RETREAT_POINT:
  1777. IsALoaner = true;
  1778. if (!Target_Legal(NavCom)) {
  1779. // CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed);
  1780. CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed);
  1781. if (Team.Is_Valid()) {
  1782. Team->Remove(this);
  1783. }
  1784. Assign_Destination(::As_Target(cell));
  1785. }
  1786. Status = TRAVEL;
  1787. return(1);
  1788. case TRAVEL:
  1789. if (!Target_Legal(NavCom)) {
  1790. Status = PICK_RETREAT_POINT;
  1791. }
  1792. break;
  1793. default:
  1794. break;
  1795. }
  1796. return(MissionControl[Mission].Normal_Delay());
  1797. }
  1798. /***********************************************************************************************
  1799. * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
  1800. * *
  1801. * Asking this question is part of the recloak process. If the answer is no, then *
  1802. * recloaking is postponed. This facilitates keeping submarines visible for longer than *
  1803. * they otherwise would be. *
  1804. * *
  1805. * INPUT: none *
  1806. * *
  1807. * OUTPUT: bool; Can this vessel recloak now? *
  1808. * *
  1809. * WARNINGS: none *
  1810. * *
  1811. * HISTORY: *
  1812. * 07/09/1996 BWG : Created. *
  1813. *=============================================================================================*/
  1814. bool VesselClass::Is_Allowed_To_Recloak(void) const
  1815. {
  1816. return(PulseCountDown == 0);
  1817. }
  1818. /***********************************************************************************************
  1819. * VesselClass::Read_INI -- Read the vessel data from the INI database. *
  1820. * *
  1821. * This will read and create all vessels specified in the INI database. This routine is *
  1822. * called when the scenario starts. *
  1823. * *
  1824. * INPUT: ini -- Reference to the INI database to read the vessel data from. *
  1825. * *
  1826. * OUTPUT: none *
  1827. * *
  1828. * WARNINGS: Vessels will be created and placed on the map by this function. *
  1829. * *
  1830. * HISTORY: *
  1831. * 07/09/1996 JLB : Created. *
  1832. *=============================================================================================*/
  1833. void VesselClass::Read_INI(CCINIClass & ini)
  1834. {
  1835. VesselClass * vessel; // Working vessel pointer.
  1836. HousesType inhouse; // Vessel house.
  1837. VesselType classid; // Vessel class.
  1838. char buf[128];
  1839. int len = ini.Entry_Count(INI_Name());
  1840. for (int index = 0; index < len; index++) {
  1841. char const * entry = ini.Get_Entry(INI_Name(), index);
  1842. ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf));
  1843. inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
  1844. if (inhouse != HOUSE_NONE) {
  1845. classid = VesselTypeClass::From_Name(strtok(NULL, ","));
  1846. if (classid != VESSEL_NONE) {
  1847. if (HouseClass::As_Pointer(inhouse) != NULL) {
  1848. vessel = new VesselClass(classid, inhouse);
  1849. if (vessel != NULL) {
  1850. /*
  1851. ** Read the raw data.
  1852. */
  1853. int strength = atoi(strtok(NULL, ",\r\n"));
  1854. CELL cell = atoi(strtok(NULL, ",\r\n"));
  1855. COORDINATE coord = Cell_Coord(cell);
  1856. DirType dir = (DirType)atoi(strtok(NULL, ",\r\n"));
  1857. MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r"));
  1858. vessel->Trigger = NULL;
  1859. TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n"));
  1860. if (tp != NULL) {
  1861. TriggerClass * tt = Find_Or_Make(tp);
  1862. if (tt != NULL) {
  1863. tt->AttachCount++;
  1864. vessel->Trigger = tt;
  1865. }
  1866. }
  1867. if (vessel->Unlimbo(coord, dir)) {
  1868. vessel->Strength = (int)vessel->Class->MaxStrength * fixed(strength, 256);
  1869. if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength;
  1870. // vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength);
  1871. if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) {
  1872. vessel->Assign_Mission(mission);
  1873. vessel->Commence();
  1874. } else {
  1875. vessel->Enter_Idle_Mode();
  1876. }
  1877. } else {
  1878. /*
  1879. ** If the vessel could not be unlimboed, then this is a catastrophic error
  1880. ** condition. Delete the vessel.
  1881. */
  1882. delete vessel;
  1883. }
  1884. }
  1885. }
  1886. }
  1887. }
  1888. }
  1889. }
  1890. /***********************************************************************************************
  1891. * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
  1892. * *
  1893. * This routine is used to add the vessel data (needed for scenario start) to the INI *
  1894. * database specified. If there was any preexisting vessel data in the database, it will *
  1895. * be cleared *
  1896. * *
  1897. * INPUT: ini -- Reference to the ini database to store the vessel data into. *
  1898. * *
  1899. * OUTPUT: none *
  1900. * *
  1901. * WARNINGS: none *
  1902. * *
  1903. * HISTORY: *
  1904. * 07/09/1996 JLB : Created. *
  1905. *=============================================================================================*/
  1906. void VesselClass::Write_INI(CCINIClass & ini)
  1907. {
  1908. /*
  1909. ** First, clear out all existing vessel data from the ini file.
  1910. */
  1911. ini.Clear(INI_Name());
  1912. /*
  1913. ** Write the vessel data out.
  1914. */
  1915. for (int index = 0; index < Vessels.Count(); index++) {
  1916. VesselClass * vessel = Vessels.Ptr(index);
  1917. if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) {
  1918. char uname[10];
  1919. char buf[128];
  1920. sprintf(uname, "%d", index);
  1921. sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
  1922. vessel->House->Class->IniName,
  1923. vessel->Class->IniName,
  1924. vessel->Health_Ratio()*256,
  1925. Coord_Cell(vessel->Coord),
  1926. vessel->PrimaryFacing.Current(),
  1927. MissionClass::Mission_Name(vessel->Mission),
  1928. vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None"
  1929. );
  1930. ini.Put_String(INI_Name(), uname, buf);
  1931. }
  1932. }
  1933. }
  1934. /***********************************************************************************************
  1935. * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
  1936. * *
  1937. * This routine is called when the vessel starts moving. It will reserve the destination *
  1938. * cell so that it won't be occupied by another vessel as this one is travelling. *
  1939. * *
  1940. * INPUT: headto -- The coordinate that will be headed to. *
  1941. * *
  1942. * OUTPUT: bool; Was the destination location successfully marked? *
  1943. * *
  1944. * WARNINGS: none *
  1945. * *
  1946. * HISTORY: *
  1947. * 07/09/1996 JLB : Created. *
  1948. *=============================================================================================*/
  1949. bool VesselClass::Start_Driver(COORDINATE & headto)
  1950. {
  1951. assert(Vessels.ID(this) == ID);
  1952. assert(IsActive);
  1953. if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver
  1954. Mark_Track(headto, MARK_DOWN);
  1955. return(true);
  1956. }
  1957. return(false);
  1958. }
  1959. /***********************************************************************************************
  1960. * VesselClass::What_Action -- Determines action to perform on specified cell. *
  1961. * *
  1962. * This routine will determine what action to perform if the mouse were clicked over the *
  1963. * cell specified. *
  1964. * *
  1965. * INPUT: cell -- The cell that the mouse might be clicked on. *
  1966. * *
  1967. * OUTPUT: Returns with the action type that this unit will perform if the mouse were *
  1968. * clicked of the cell specified. *
  1969. * *
  1970. * WARNINGS: none *
  1971. * *
  1972. * HISTORY: *
  1973. * 07/11/1996 BWG : Created. *
  1974. *=============================================================================================*/
  1975. ActionType VesselClass::What_Action(CELL cell) const
  1976. {
  1977. assert(Vessels.ID(this) == ID);
  1978. assert(IsActive);
  1979. ActionType action = DriveClass::What_Action(cell);
  1980. if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) {
  1981. return(ACTION_MOVE);
  1982. }
  1983. if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) {
  1984. return(ACTION_ATTACK);
  1985. }
  1986. return(action);
  1987. }
  1988. /***********************************************************************************************
  1989. * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
  1990. * *
  1991. * Any turret or body rotation for this vessel will be handled by this routine. *
  1992. * *
  1993. * INPUT: none *
  1994. * *
  1995. * OUTPUT: none *
  1996. * *
  1997. * WARNINGS: Only call this routine once per vessel per game logic loop. *
  1998. * *
  1999. * HISTORY: *
  2000. * 07/29/1996 JLB : Created. *
  2001. *=============================================================================================*/
  2002. void VesselClass::Rotation_AI(void)
  2003. {
  2004. if (Target_Legal(TarCom) && !IsRotating) {
  2005. DirType dir = Direction(TarCom);
  2006. if (Class->IsTurretEquipped) {
  2007. SecondaryFacing.Set_Desired(dir);
  2008. }
  2009. }
  2010. IsRotating = false;
  2011. if (Class->IsTurretEquipped) {
  2012. if (SecondaryFacing.Is_Rotating()) {
  2013. Mark(MARK_CHANGE_REDRAW);
  2014. if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) {
  2015. Mark(MARK_CHANGE_REDRAW);
  2016. }
  2017. /*
  2018. ** If no further rotation is necessary, flag that the rotation
  2019. ** has stopped.
  2020. */
  2021. IsRotating = SecondaryFacing.Is_Rotating();
  2022. }
  2023. }
  2024. }
  2025. /***********************************************************************************************
  2026. * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
  2027. * *
  2028. * This routine will process firing logic for the vessel. It includes searching for targets *
  2029. * and performing any adjustments necessary to bring the target to bear. *
  2030. * *
  2031. * INPUT: none *
  2032. * *
  2033. * OUTPUT: none *
  2034. * *
  2035. * WARNINGS: Only call this routine once per vessel per game logic loop. *
  2036. * *
  2037. * HISTORY: *
  2038. * 07/29/1996 JLB : Created. *
  2039. *=============================================================================================*/
  2040. void VesselClass::Combat_AI(void)
  2041. {
  2042. if (Target_Legal(TarCom) && Is_Weapon_Equipped()) {
  2043. /*
  2044. ** Determine which weapon can fire. First check for the primary weapon. If that weapon
  2045. ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the
  2046. ** failure code returned is that from the primary weapon.
  2047. */
  2048. int primary = What_Weapon_Should_I_Use(TarCom);
  2049. FireErrorType ok = Can_Fire(TarCom, primary);
  2050. switch (ok) {
  2051. case FIRE_OK:
  2052. Fire_At(TarCom, primary);
  2053. break;
  2054. case FIRE_FACING:
  2055. if (Class->IsTurretEquipped) {
  2056. SecondaryFacing.Set_Desired(Direction(TarCom));
  2057. } else {
  2058. if (!PrimaryFacing.Is_Rotating()) {
  2059. PrimaryFacing.Set_Desired(Direction(TarCom));
  2060. }
  2061. }
  2062. break;
  2063. case FIRE_CLOAKED:
  2064. Mark(MARK_OVERLAP_UP);
  2065. IsFiring = false;
  2066. Mark(MARK_OVERLAP_DOWN);
  2067. Do_Uncloak();
  2068. break;
  2069. }
  2070. }
  2071. }
  2072. /***********************************************************************************************
  2073. * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
  2074. * *
  2075. * In addition to detecting the edge of world case, this routine will delete the vessel *
  2076. * if it occurs. *
  2077. * *
  2078. * INPUT: none *
  2079. * *
  2080. * OUTPUT: bool; Was the vessel deleted by this routine? *
  2081. * *
  2082. * WARNINGS: Be sure to examine the return value and if true, abort any further processing *
  2083. * for this vessel since it has been deleted. This routine should be called once *
  2084. * per vessel per game logic loop. *
  2085. * *
  2086. * HISTORY: *
  2087. * 07/29/1996 JLB : Created. *
  2088. *=============================================================================================*/
  2089. bool VesselClass::Edge_Of_World_AI(void)
  2090. {
  2091. if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) {
  2092. if (Team.Is_Valid()) Team->IsLeaveMap = true;
  2093. Stun();
  2094. delete this;
  2095. return(true);
  2096. }
  2097. return(false);
  2098. }
  2099. /***********************************************************************************************
  2100. * VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
  2101. * *
  2102. * When a vessel repairs, it does so 'by itself' and not under direct control of another *
  2103. * object. This self repair logic is processed here. Upon repair completion of money *
  2104. * exhuastion, the repair process will terminate. *
  2105. * *
  2106. * INPUT: none *
  2107. * *
  2108. * OUTPUT: none *
  2109. * *
  2110. * WARNINGS: Call this routine only once per vessel per game logic loop. *
  2111. * *
  2112. * HISTORY: *
  2113. * 07/29/1996 BWG : Created. *
  2114. *=============================================================================================*/
  2115. void VesselClass::Repair_AI(void)
  2116. {
  2117. if (IsSelfRepairing) {
  2118. if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) {
  2119. Mark(MARK_CHANGE);
  2120. int cost = Class->Repair_Cost();
  2121. int step = Class->Repair_Step();
  2122. if (House->Available_Money() >= cost) {
  2123. House->Spend_Money(cost);
  2124. Strength += step;
  2125. if (Strength >= Class->MaxStrength) {
  2126. Strength = Class->MaxStrength;
  2127. IsSelfRepairing = IsToSelfRepair = false;
  2128. // MBL 04.27.2020: Make only audible to the correct player
  2129. // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED);
  2130. if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House);
  2131. }
  2132. }
  2133. }
  2134. }
  2135. }
  2136. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  2137. /***********************************************************************************************
  2138. * VesselClass::Fire_At -- Try to fire upon the target specified. *
  2139. * *
  2140. * This routine is the auto-fire logic for the ship. It will check *
  2141. * to see if we're an aircraft carrier, and if so, launch one of our *
  2142. * aircraft. If we're not an aircraft carrier, it lets the higher-level *
  2143. * Fire_At logic take over. *
  2144. * *
  2145. * INPUT: target -- The target to fire upon. *
  2146. * *
  2147. * which -- Which weapon to use when firing. 0=primary, 1=secondary. *
  2148. * *
  2149. * OUTPUT: bool; Did firing occur? *
  2150. * *
  2151. * WARNINGS: none *
  2152. * *
  2153. * HISTORY: *
  2154. * 04/26/1994 JLB : Created. *
  2155. *=============================================================================================*/
  2156. BulletClass * VesselClass::Fire_At(TARGET target, int which)
  2157. {
  2158. //PG assert(Units.ID(this) == ID);
  2159. assert(IsActive);
  2160. if (*this == VESSEL_CARRIER) {
  2161. Arm = CarrierLaunchDelay;
  2162. FootClass * passenger = Detach_Object();
  2163. if (passenger != NULL) {
  2164. ScenarioInit++;
  2165. passenger->Unlimbo(Center_Coord());
  2166. ScenarioInit--;
  2167. passenger->Assign_Mission(MISSION_ATTACK);
  2168. passenger->Assign_Target(TarCom);
  2169. passenger->Commence();
  2170. // If we've launched our last aircraft, discontinue attacking.
  2171. if (!How_Many()) Assign_Target(TARGET_NONE);
  2172. }
  2173. } else {
  2174. return DriveClass::Fire_At(target, which);
  2175. }
  2176. return (NULL);
  2177. }
  2178. #endif