VESSEL.CPP 105 KB

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