| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */
- /***********************************************************************************************
- *** 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 ***
- ***********************************************************************************************
- * *
- * Project Name : Command & Conquer *
- * *
- * File Name : VESSEL.CPP *
- * *
- * Programmer : Joe L. Bostic *
- * *
- * Start Date : 03/13/96 *
- * *
- * Last Update : July 31, 1996 [JLB] *
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * VesselClass::AI -- Handles the AI processing for vessel objects. *
- * VesselClass::Assign_Destination -- Assign a destination for this vessel. *
- * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
- * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
- * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
- * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
- * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
- * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
- * VesselClass::Draw_It -- Draws the vessel. *
- * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
- * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
- * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
- * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse*
- * VesselClass::Init -- Initialize the vessel heap system. *
- * VesselClass::Mission_Retreat -- Perform the retreat mission. *
- * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
- * VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
- * VesselClass::Read_INI -- Read the vessel data from the INI database. *
- * VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
- * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
- * VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
- * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
- * VesselClass::Take_Damage -- Assign damage to the vessel. *
- * VesselClass::VesselClass -- Constructor for vessel class objects. *
- * VesselClass::What_Action -- Determines action to perform on specified cell. *
- * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
- * VesselClass::~VesselClass -- Destructor for vessel objects. *
- * operator delete -- Deletes a vessel's memory block. *
- * operator new -- Allocates a vessel object memory block. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "function.h"
- /***********************************************************************************************
- * VesselClass::VesselClass -- Constructor for vessel class objects. *
- * *
- * This is the normal constructor for vessel class objects. It will set up a vessel that *
- * is valid excepting that it won't be placed on the map. *
- * *
- * INPUT: classid -- The type of vessel this will be. *
- * *
- * house -- The owner of this vessel. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- VesselClass::VesselClass(VesselType classid, HousesType house) :
- DriveClass(RTTI_VESSEL, Vessels.ID(this), house),
- Class(VesselTypes.Ptr((int)classid)),
- IsToSelfRepair(false),
- IsSelfRepairing(false),
- DoorShutCountDown(0),
- PulseCountDown(0),
- SecondaryFacing(PrimaryFacing)
- {
- House->Tracking_Add(this);
- /*
- ** The ammo member is actually part of the techno class, but must be initialized
- ** manually here because this is where we first have access to the class pointer.
- */
- Ammo = Class->MaxAmmo;
- /*
- ** For two shooters, clear out the second shot flag -- it will be set the first time
- ** the object fires. For non two shooters, set the flag since it will never be cleared
- ** and the second shot flag tells the system that normal rearm times apply -- this is
- ** what is desired for non two shooters.
- */
- IsSecondShot = !Class->Is_Two_Shooter();
- Strength = Class->MaxStrength;
- /*
- ** The techno class cloakabilty flag is set according to the type
- ** class cloakability flag.
- */
- IsCloakable = Class->IsCloakable;
- /*
- ** Keep count of the number of units created.
- */
- // if (Session.Type == GAME_INTERNET) {
- // House->UnitTotals->Increment_Unit_Total((int)classid);
- // }
- }
- /***********************************************************************************************
- * VesselClass::~VesselClass -- Destructor for vessel objects. *
- * *
- * The destructor will destroy the vessel and ensure that it is properly removed from the *
- * game engine. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- VesselClass::~VesselClass(void)
- {
- if (GameActive && Class.Is_Valid()) {
- /*
- ** Remove this member from any team it may be associated with. This must occur at the
- ** top most level of the inheritance hierarchy because it may call virtual functions.
- */
- if (Team.Is_Valid()) {
- Team->Remove(this);
- Team = NULL;
- }
- House->Tracking_Remove(this);
- /*
- ** If there are any cargo members, delete them.
- */
- while (Is_Something_Attached()) {
- delete Detach_Object();
- }
- Limbo();
- }
- ID = -1;
- }
- /***********************************************************************************************
- * operator new -- Allocates a vessel object memory block. *
- * *
- * This routine is used to allocate a block of memory from the vessel heap. If there is *
- * no more space in the heap, then this routine will return NULL. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the pointer to the allocated block of memory. *
- * *
- * WARNINGS: This routine could return NULL. *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- void * VesselClass::operator new(size_t)
- {
- void * ptr = Vessels.Alloc();
- if (ptr != NULL) {
- ((VesselClass *)ptr)->IsActive = true;
- }
- return(ptr);
- }
- /***********************************************************************************************
- * operator delete -- Deletes a vessel's memory block. *
- * *
- * This overloaded delete operator will return the vessel's memory back to the pool of *
- * memory used for vessel allocation. *
- * *
- * INPUT: ptr -- Pointer to the vessel's memory block. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::operator delete(void * ptr)
- {
- if (ptr != NULL) {
- assert(((VesselClass *)ptr)->IsActive);
- ((VesselClass *)ptr)->IsActive = false;
- }
- Vessels.Free((VesselClass *)ptr);
- }
- /***********************************************************************************************
- * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
- * *
- * This routine will return with a reference to the static class data for this vessel. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with a reference to the class data structure associated with this vessel. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- ObjectTypeClass const & VesselClass::Class_Of(void) const
- {
- assert(IsActive);
- return(*Class);
- }
- /***********************************************************************************************
- * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
- * *
- * This routine is used by find path and other movement logic to determine if this *
- * vessel can enter the cell specified. *
- * *
- * INPUT: cell -- The cell to check this vessel against. *
- * *
- * OUTPUT: Returns with the movement restriction associated with movement into this object. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);
- CellClass const * cellptr = &Map[cell];
- /*
- ** Moving off the edge of the map is not allowed unless
- ** this is a loaner vehicle.
- */
- if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) {
- return(MOVE_NO);
- }
- MoveType retval = MOVE_OK;
- /*
- ** If there is blocking terrain (such as ice), then the vessel
- ** can't move there.
- */
- if (cellptr->Cell_Terrain() != NULL) {
- return(MOVE_NO);
- }
- /*
- ** If the cell is out and out impassable because of underlying terrain, then
- ** return this immutable fact.
- */
- if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) {
- return(MOVE_NO);
- }
- /*
- ** If some allied object has reserved the cell, then consider the cell
- ** as blocked by a moving object.
- */
- if (cellptr->Flag.Composite) {
- if (cellptr->Flag.Occupy.Building) {
- return(MOVE_NO);
- }
- TechnoClass * techno = cellptr->Cell_Techno();
- if (techno != NULL && techno->Cloak == CLOAKED && !House->Is_Ally(techno)) {
- return(MOVE_CLOAK);
- }
- /*
- ** If reserved by a vehicle, then consider this blocked terrain.
- */
- if (cellptr->Flag.Occupy.Vehicle) {
- retval = MOVE_MOVING_BLOCK;
- }
- }
- /*
- ** Return with the most severe reason why this cell would be impassable.
- */
- return(retval);
- }
- /***********************************************************************************************
- * VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
- * *
- * This routine will return with the shape number to use for the ship's body. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the shape number to use for the ship's body when drawing. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/31/1996 JLB : Created. *
- *=============================================================================================*/
- int VesselClass::Shape_Number(void) const
- {
- /*
- ** For eight facing units, adjust the facing number accordingly.
- */
- FacingType facing = Dir_Facing(PrimaryFacing.Current());
- int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1;
- /*
- ** Special case code for transport. The north/south facing is in frame
- ** 0. The east/west facing is in frame 3.
- */
- if (*this == VESSEL_TRANSPORT) {
- shapenum = 0;
- }
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if (*this == VESSEL_CARRIER) {
- shapenum = 0;
- }
- #endif
- /*
- ** Door opening and closing animation stage check.
- */
- if (!Is_Door_Closed()) {
- shapenum = Door_Stage();
- }
- return(shapenum);
- }
- /***********************************************************************************************
- * VesselClass::Draw_It -- Draws the vessel. *
- * *
- * Draws the vessel on the tactical display. This routine is called by the map rendering *
- * process to display the vessel. *
- * *
- * INPUT: x,y -- The pixel coordinate to draw this vessel at. *
- * *
- * window-- The window to base clipping and coordinates upon when drawing. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/14/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Draw_It(int x, int y, WindowNumberType window) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- /*
- ** Verify the legality of the unit class.
- */
- void const * shapefile = Get_Image_Data();
- if (shapefile == NULL) return;
- /*
- ** If drawing of this unit is not explicitly prohibited, then proceed
- ** with the render process.
- */
- if (Visual_Character() != VISUAL_HIDDEN) {
- int facing = Dir_To_32(PrimaryFacing);
- int tfacing = Dir_To_32(SecondaryFacing);
- DirType rotation = DIR_N;
- int scale = 0x0100;
- /*
- ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
- */
- Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale);
- /*
- ** If there is a turret, then it must be rendered as well. This may include
- ** firing animation if required.
- */
- if (Class->IsTurretEquipped) {
- int xx = x;
- int yy = y;
- /*
- ** Determine which turret shape to use. This depends on if there
- ** is any firing animation in progress.
- */
- int shapenum = TechnoClass::BodyShape[tfacing]+32;
- DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16);
- switch (Class->Type) {
- case VESSEL_CA:
- shapefile = Class->TurretShapes;
- shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
- Class->Turret_Adjust(turdir, xx, yy);
- Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
- xx = x;
- yy = y;
- turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16);
- Class->Turret_Adjust(turdir, xx, yy);
- break;
- case VESSEL_DD:
- shapefile = Class->SamShapes;
- shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
- Class->Turret_Adjust(turdir, xx, yy);
- break;
- case VESSEL_PT:
- shapefile = Class->MGunShapes;
- shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
- Class->Turret_Adjust(turdir, xx, yy);
- break;
- default:
- shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
- Class->Turret_Adjust(turdir, xx, yy);
- break;
- }
- /*
- ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
- */
- Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
- }
- }
- DriveClass::Draw_It(x, y, window);
- /*
- ** Patch so the transport will draw its passengers on top of itself.
- */
- if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) {
- assert(Contact_With_Whom()->IsActive);
- Contact_With_Whom()->Render(true);
- }
- }
- #ifdef CHEAT_KEYS
- /***********************************************************************************************
- * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
- * *
- * This routine will display the vessel's status information. The information is dumped to *
- * the monochrome monitor. *
- * *
- * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed *
- * to. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/20/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Debug_Dump(MonoClass * mono) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- mono->Set_Cursor(0, 0);
- mono->Print(Text_String(TXT_DEBUG_SHIP));
- mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());
- mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL);
- DriveClass::Debug_Dump(mono);
- }
- #endif
- /***********************************************************************************************
- * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
- * *
- * This routine will fetch the overlap list for this vessel type. It takes into *
- * consideration any movement the vessel may be doing. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with a pointer to the overlap list for this vessel. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/20/1996 JLB : Created. *
- *=============================================================================================*/
- short const * VesselClass::Overlap_List(bool redraw) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- #ifdef PARTIAL
- if (Height == 0 && redraw && Class->DimensionData != NULL) {
- Rect rect;
- int shapenum = Shape_Number();
- if (Class->DimensionData[shapenum].Is_Valid()) {
- rect = Class->DimensionData[shapenum];
- } else {
- rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum);
- }
- if (IsSelected) {
- rect = Union(rect, Rect(-32, 32, 64, 64));
- }
- return(Coord_Spillage_List(Coord, rect, true));
- }
- #else
- redraw = redraw;
- #endif
- return(Coord_Spillage_List(Coord, 56)+1);
- }
- /***********************************************************************************************
- * VesselClass::AI -- Handles the AI processing for vessel objects. *
- * *
- * This routine is called once for each vessel object during each main game loop. All *
- * normal AI processing is handled here. This includes dispatching and maintaining any *
- * processing that is specific to vessel objects. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/20/1996 JLB : Created. *
- * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. *
- *=============================================================================================*/
- void VesselClass::AI(void)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) {
- Enter_Idle_Mode();
- }
- /*
- ** HACK ALERT:
- ** If the ship finds itself in a hunt order but it has no weapons, then tell it
- ** to sail off the map instead.
- */
- if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) {
- Assign_Mission(MISSION_RETREAT);
- }
- /*
- ** Act on new orders if the unit is at a good position to do so.
- */
- if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) {
- Commence();
- }
- #ifndef CLIPDRAW
- if (Map.In_View(Coord_Cell(Center_Coord())) && Visual_Character() != VISUAL_HIDDEN && Visual_Character() != VISUAL_NORMAL) {
- Mark(MARK_CHANGE);
- }
- #endif
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- // Re-stock the ammo of any on-board helicopters on an aircraft carrier.
- if (*this == VESSEL_CARRIER && How_Many()) {
- if (!MoebiusCountDown) {
- MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE;
- ObjectClass *obj = Attached_Object();
- while (obj) {
- long bogus;
- ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus);
- obj = (obj->Next);
- }
- }
- }
- #endif
- /*
- ** Process base class AI routine. If as a result of this, the vessel gets
- ** destroyed, then detect this fact and bail early.
- */
- DriveClass::AI();
- if (!IsActive) {
- return;
- }
- /*
- ** Handle body and turret rotation.
- */
- Rotation_AI();
- /*
- ** Handle any combat processing required.
- */
- Combat_AI();
- /*
- ** Delete this unit if it finds itself off the edge of the map and it is in
- ** guard or other static mission mode.
- */
- if (Edge_Of_World_AI()) {
- return;
- }
- if (Class->Max_Passengers() > 0) {
- /*
- ** Double check that there is a passenger that is trying to load or unload.
- ** If not, then close the door.
- */
- if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) {
- LST_Close_Door();
- }
- }
- /*
- ** Don't start a new mission unless the vehicle is in the center of
- ** a cell (not driving) and the door (if any) is closed.
- */
- if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
- Commence();
- }
- /*
- ** Do a step of repair here, if appropriate.
- */
- Repair_AI();
- }
- /***********************************************************************************************
- * VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
- * *
- * This routine is called when the vessel travels one cell. It handles any processes that *
- * must occur on a per-cell basis. *
- * *
- * INPUT: why -- Specifies the circumstances under which this routine was called. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/19/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Per_Cell_Process(PCPType why)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- BStart(BENCH_PCP);
- if (why == PCP_END) {
- CELL cell = Coord_Cell(Coord);
- /*
- ** The unit performs looking around at this time. If the
- ** unit moved further than one square during the last track
- ** move, don't do an incremental look. Do a full look around
- ** instead.
- */
- Look(!IsPlanningToLook);
- IsPlanningToLook = false;
- if (IsToSelfRepair) {
- for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
- CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face));
- SmartPtr<BuildingClass> whom;
- whom = Map[cell].Cell_Building();
- if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) {
- if (IsOwnedByPlayer) Speak(VOX_REPAIRING);
- IsSelfRepairing = true;
- IsToSelfRepair = false;
- break;
- }
- }
- }
- /*
- ** If this is a loaner unit and is is off the edge of the
- ** map, then it gets eliminated.
- */
- if (Edge_Of_World_AI()) {
- BEnd(BENCH_PCP);
- return;
- }
- }
- if (IsActive) {
- DriveClass::Per_Cell_Process(why);
- }
- BEnd(BENCH_PCP);
- }
- /***********************************************************************************************
- * VesselClass::What_Action -- Determines what action would occur if clicked on object. *
- * *
- * Use this function to determine what action would likely occur if the specified object *
- * were clicked on while this unit was selected as current. This function controls, not *
- * only the action to perform, but indirectly controls the cursor shape to use as well. *
- * *
- * INPUT: object -- The object that to check for against "this" object. *
- * *
- * OUTPUT: Returns with the default action to perform. If no clear action can be determined, *
- * then ACTION_NONE is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 04/16/1996 BWG : Created. *
- *=============================================================================================*/
- ActionType VesselClass::What_Action(ObjectClass const * object) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- ActionType action = DriveClass::What_Action(object);
- if (action == ACTION_SELF) {
- if (Class->Max_Passengers() == 0 || !How_Many() ) {
- action = ACTION_NONE;
- } else {
- // check to see if the transporter can unload.
- bool found = 0;
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if (*this != VESSEL_CARRIER)
- #endif
- for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) {
- CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
- CellClass * cell = &Map[cellnum];
- 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) {
- continue;
- } else {
- found = true;
- }
- }
- if (!found) {
- action = ACTION_NONE;
- }
- }
- }
- /*
- ** Special return to friendly repair factory action.
- */
- if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
- BuildingClass * building = (BuildingClass *)object;
- if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) {
- action = ACTION_ENTER;
- }
- }
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL &&
- (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) {
- action = ACTION_NOMOVE;
- }
- #endif
- /*
- ** If it doesn't know what to do with the object, then just
- ** say it can't move there.
- */
- if (action == ACTION_NONE) action = ACTION_NOMOVE;
- return(action);
- }
- /***********************************************************************************************
- * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
- * *
- * This routine intercepts the active click operation. It check to see if this is a self *
- * deployment request (MCV's have this ability). If it is, then the object is initiated *
- * to self deploy. In the other cases, it passes the operation down to the lower *
- * classes for processing. *
- * *
- * INPUT: action -- The action requested of the unit. *
- * *
- * object -- The object that the mouse pointer is over. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 04/16/1996 BWG : Created. *
- *=============================================================================================*/
- void VesselClass::Active_Click_With(ActionType action, ObjectClass * object)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- // if (action != What_Action(object)) {
- action = What_Action(object);
- switch (action) {
- case ACTION_ENTER:
- action = ACTION_MOVE;
- // BRR 10/18/96 IsToSelfRepair = true;
- break;
- default:
- // action = ACTION_NONE;
- break;
- }
- // }
- // if (action == ACTION_ENTER) {
- // BRR 10/18/96 IsToSelfRepair = true;
- // action = ACTION_MOVE;
- // } else {
- // if (action != ACTION_NONE) {
- // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false;
- // }
- // }
- DriveClass::Active_Click_With(action, object);
- }
- /***********************************************************************************************
- * VesselClass::Active_Click_With -- Performs specified action on specified cell. *
- * *
- * This routine is called when the mouse has been clicked over a cell and this unit must *
- * now respond. Notice that this is merely a placeholder function that exists because there *
- * is another function of the same name that needs to be overloaded. C++ has scoping *
- * restrictions when there are two identically named functions that are overridden in *
- * different classes -- it handles it badly, hence the existence of this routine. *
- * *
- * INPUT: action -- The action to perform on the cell specified. *
- * *
- * cell -- The cell that the action is to be performed on. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 04/16/1996 BWG : Created. *
- *=============================================================================================*/
- void VesselClass::Active_Click_With(ActionType action, CELL cell)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- // BRR 10/18/96 IsToSelfRepair = false;
- // if (action != ACTION_NONE) {
- // BRR 10/18/96 IsSelfRepairing = false;
- // }
- DriveClass::Active_Click_With(action, cell);
- }
- /***********************************************************************************************
- * VesselClass::Take_Damage -- Assign damage to the vessel. *
- * *
- * This routine is called to apply damage to this vessel. The amount and type of damage *
- * to apply is passed as parameters. This routine could end up destroying the vessel. *
- * *
- * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage *
- * value will be adjusted so that the actual damage applied will be *
- * stored into this variable for possible subsequent examination. *
- * *
- * distance -- The distance from the center of the damage to the vessel itself. *
- * *
- * warhead -- The warhead type of damage to apply. *
- * *
- * source -- The perpetrator of this damage. Knowing who was responsible allows *
- * retaliation logic. *
- * *
- * forced -- Is this damage forced upon the vessel by some supernatural means? *
- * *
- * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the *
- * general effect of the damage. Examine this return value to see if the vessel *
- * has been destroyed. *
- * *
- * WARNINGS: The vessel could be destroyed by the call to this routine! *
- * *
- * HISTORY: *
- * 05/13/1996 JLB : Created. *
- *=============================================================================================*/
- ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- ResultType res = RESULT_NONE;
- /*
- ** In order for a this to be damaged, it must either be a unit
- ** with a crew or a sandworm.
- */
- res = FootClass::Take_Damage(damage, distance, warhead, source, forced);
- if (res == RESULT_DESTROYED) {
- Death_Announcement(source);
- if (Class->Explosion != ANIM_NONE) {
- AnimType anim = Class->Explosion;
- new AnimClass(anim, Coord);
- /*
- ** Very strong units that have an explosion will also rock the
- ** screen when they are destroyed.
- */
- if (Class->MaxStrength > 400) {
- Shake_The_Screen(Class->MaxStrength / 150);
- }
- }
- /*
- ** Possibly have the crew member run away.
- */
- Mark(MARK_UP);
- while (Is_Something_Attached()) {
- FootClass * object = Detach_Object();
- /*
- ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure
- ** thing.
- */
- object->Record_The_Kill(source);
- delete object;
- }
- /*
- ** Finally, delete the vehicle.
- */
- delete this;
- } else {
- /*
- ** When damaged and below half strength, start smoking if
- ** it isn't already smoking (and it's not a submarine).
- */
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) {
- #else
- if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) {
- #endif
- AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
- if (anim != NULL) anim->Attach_To(this);
- }
- }
- return(res);
- }
- /***********************************************************************************************
- * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
- * *
- * This routine is used to determine if this vessel can fire its weapon at the target *
- * specified. *
- * *
- * INPUT: target -- The target candidate to determine if firing upon is valid. *
- * *
- * which -- Which weapon to use when considering the candidate as a potential *
- * target. *
- * *
- * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If *
- * it can't fire, then the enum indicates why. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/13/1996 JLB : Created. *
- *=============================================================================================*/
- FireErrorType VesselClass::Can_Fire(TARGET target, int which) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- DirType dir; // The facing to impart upon the projectile.
- int diff;
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if (*this == VESSEL_CARRIER) {
- if(!How_Many() || Arm) {
- return(FIRE_REARM);
- } else {
- return(FIRE_OK);
- }
- }
- #endif
- FireErrorType fire = DriveClass::Can_Fire(target, which);
- if(*this==VESSEL_DD) {
- Mono_Set_Cursor(0,0);
- }
- if (fire == FIRE_OK || fire == FIRE_CLOAKED) {
- WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
- /*
- ** Ensure that a torpedo will never be fired upon a non naval target.
- ** Unless that non-naval target is a naval building (sub pen/ship yard)
- */
- bool isseatarget = Is_Target_Vessel(target);
- bool isbridgetarget = false;
- if (weapon->Bullet->IsSubSurface) {
- isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges
- isseatarget |= isbridgetarget;
- }
- BuildingClass * bldg = ::As_Building(target);
- if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) {
- isseatarget = true;
- }
- dir = Direction(target);
- if (weapon->Bullet->IsSubSurface) {
- if (!isseatarget && Is_Target_Object(target)) {
- return(FIRE_CANT);
- }
- /*
- ** If it's a torpedo, let's check line-of-sight to make sure that
- ** there's only water squares between us and the target.
- */
- ObjectClass * obj = As_Object(target);
- COORDINATE coord = Center_Coord();
- if (obj != NULL) {
- int totaldist = ::Distance(coord, obj->Center_Coord());
- while (totaldist > CELL_LEPTON_W) {
- coord = Coord_Move(coord, dir, CELL_LEPTON_W);
- if (Map[coord].Land_Type() != LAND_WATER) {
- if (!isbridgetarget) {
- return(FIRE_RANGE);
- }
- }
- /*
- ** Check for friendly boats in the way.
- */
- TechnoClass * tech = Map[coord].Cell_Techno();
- if (tech != NULL && tech != this && House->Is_Ally(tech)) {
- return(FIRE_RANGE);
- }
- totaldist -= CELL_LEPTON_W;
- }
- }
- }
- /*
- ** Depth charges are only good against submarines.
- */
- if (weapon->Bullet->IsAntiSub) {
- if (!isseatarget) {
- return(FIRE_CANT);
- } else {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) {
- #else
- if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) {
- #endif
- if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) {
- return(FIRE_CANT);
- }
- }
- }
- } else {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) {
- #else
- if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) {
- #endif
- return(FIRE_CANT);
- }
- }
- /*
- ** If this unit cannot fire while moving, then bail.
- */
- if (!Class->IsTurretEquipped && Target_Legal(NavCom)) {
- return(FIRE_MOVING);
- }
- /*
- ** If the turret is rotating and the projectile isn't a homing type, then
- ** firing must be delayed until the rotation stops.
- */
- if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) {
- return(FIRE_ROTATING);
- }
- /*
- ** Determine if the turret facing isn't too far off of facing the target.
- */
- if (Class->IsTurretEquipped) {
- diff = SecondaryFacing.Difference(dir);
- } else {
- diff = PrimaryFacing.Difference(dir);
- }
- diff = ABS(diff);
- if (weapon->Bullet->ROT != 0) {
- diff >>= 2;
- }
- if (diff > 8) {
- return(FIRE_FACING);
- }
- }
- return(fire);
- }
- /***********************************************************************************************
- * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
- * *
- * This routine is called to determine the coordinate that a fired projectile will *
- * originate from. *
- * *
- * INPUT: which -- Which weapon is this query directed at? *
- * *
- * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/13/1996 JLB : Created. *
- *=============================================================================================*/
- COORDINATE VesselClass::Fire_Coord(int which) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- COORDINATE coord = Center_Coord();
- if (*this == VESSEL_CA) {
- if (IsSecondShot) {
- coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100);
- } else {
- coord = Coord_Move(coord, PrimaryFacing, 0x0100);
- }
- coord = Coord_Move(coord, DIR_N, 0x0030);
- coord = Coord_Move(coord, Turret_Facing(), 0x0040);
- return(coord);
- }
- if (*this == VESSEL_PT) {
- coord = Coord_Move(coord, PrimaryFacing, 0x0080);
- coord = Coord_Move(coord, DIR_N, 0x0020);
- coord = Coord_Move(coord, Turret_Facing(), 0x0010);
- return(coord);
- }
- return(DriveClass::Fire_Coord(which));
- }
- /***********************************************************************************************
- * VesselClass::Init -- Initialize the vessel heap system. *
- * *
- * This routine is used to clear out the vessel heap. It is called whenever a scenario is *
- * being initialized prior to scenario or saved game loading. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: All vessel objects are invalid after this routine is called. *
- * *
- * HISTORY: *
- * 05/13/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Init(void)
- {
- Vessels.Free_All();
- }
- /***********************************************************************************************
- * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel *
- * *
- * This routine is used by ships to determine what target they should go after. *
- * *
- * INPUT: threat -- The threat type that this ship should go after (as determined by the *
- * team mission or general self defense principles). *
- * *
- * OUTPUT: Returns with the target that this ship should attack. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/13/1996 JLB : Created. *
- *=============================================================================================*/
- TARGET VesselClass::Greatest_Threat(ThreatType threat) const
- {
- if (*this == VESSEL_SS) {
- threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA);
- threat = threat | THREAT_BOATS;
- //BG: get subs to attack buildings also.
- threat = threat | THREAT_BUILDINGS;
- threat = threat | THREAT_FACTORIES;
- } else {
- if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) {
- if (Class->PrimaryWeapon != NULL) {
- threat = threat | Class->PrimaryWeapon->Allowed_Threats();
- }
- if (Class->SecondaryWeapon != NULL) {
- threat = threat | Class->SecondaryWeapon->Allowed_Threats();
- }
- // threat = threat | THREAT_GROUND | THREAT_BOATS;
- }
- // Cruisers can never hit infantry anyway, so take 'em out of the list
- // of possible targets.
- if (*this == VESSEL_CA) {
- threat = (ThreatType) (threat & (~THREAT_INFANTRY));
- }
- }
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if (*this == VESSEL_CARRIER) {
- return(TARGET_NONE);
- }
- #endif
- return(FootClass::Greatest_Threat(threat));
- }
- /***********************************************************************************************
- * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
- * *
- * This routine is called when the vessel is finished with what it is doing, but the next *
- * action is not known. This routine will determine what is the appropriate course of *
- * action for this vessel and then start it doing that. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Enter_Idle_Mode(bool )
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- MissionType order = MISSION_GUARD;
- /*
- ** A movement mission without a NavCom would be pointless to have a radio contact since
- ** no radio coordination occurs on a just a simple movement mission.
- */
- if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) {
- Transmit_Message(RADIO_OVER_OUT);
- }
- Handle_Navigation_List();
- if (Target_Legal(NavCom)) {
- order = MISSION_MOVE;
- } else {
- if (Class->PrimaryWeapon == NULL) {
- if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) {
- order = MISSION_UNLOAD;
- } else {
- order = MISSION_GUARD;
- Assign_Target(TARGET_NONE);
- Assign_Destination(TARGET_NONE);
- }
- } else {
- if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) {
- return;
- }
- if (House->IsHuman || Team.Is_Valid()) {
- order = MISSION_GUARD;
- } else {
- if (House->IQ < Rule.IQGuardArea) {
- order = MISSION_GUARD;
- } else {
- order = MISSION_GUARD_AREA;
- }
- }
- }
- }
- Assign_Mission(order);
- }
- /***********************************************************************************************
- * VesselClass::Receive_Message -- Handles receiving a radio message. *
- * *
- * This is the handler function for when a vessel receives a radio *
- * message. Typical use of this is when a unit unloads from a lst *
- * class so that clearing of the transport is successful. *
- * *
- * INPUT: from -- Pointer to the originator of the message. *
- * *
- * message -- The radio message received. *
- * *
- * param -- Reference to an optional parameter the might be needed to return *
- * information back to the originator of the message. *
- * *
- * OUTPUT: Returns with the radio message response. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/31/1996 BWG : Created. *
- *=============================================================================================*/
- RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- switch (message) {
- /*
- ** Asks if the passenger can load on this transport.
- */
- case RADIO_CAN_LOAD:
- if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC);
- if (How_Many() < Class->Max_Passengers()) {
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) {
- return(RADIO_ROGER);
- }
- #endif
- /*
- ** Before saying "Sure, come on board", make sure we're adjacent to
- ** the shore.
- */
- CELL cell;
- Desired_Load_Dir(from, cell);
- if(cell) {
- return(RADIO_ROGER);
- }
- }
- return(RADIO_NEGATIVE);
- /*
- ** This message is sent by the passenger when it determines that it has
- ** entered the transport.
- */
- case RADIO_IM_IN:
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if(*this != VESSEL_CARRIER) {
- #endif
- if (How_Many() == Class->Max_Passengers()) {
- LST_Close_Door();
- } else {
- DoorShutCountDown = TICKS_PER_SECOND;
- }
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- }
- #endif
- return(RADIO_ATTACH);
- /*
- ** Docking maintenance message received. Check to see if new orders should be given
- ** to the impatient unit.
- */
- case RADIO_DOCKING:
- /*
- ** If this transport is moving, then always abort the docking request.
- */
- if (IsDriving || Target_Legal(NavCom)) {
- return(RADIO_NEGATIVE);
- }
- /*
- ** Check for the case of a docking message arriving from a unit that does not
- ** have formal radio contact established. This might be a unit that is standing
- ** by. If this transport is free to proceed with normal docking operation, then
- ** establish formal contact now. If the transport is completely full, then break
- ** off contact. In all other cases, just tell the pending unit to stand by.
- */
- if (Contact_With_Whom() != from) {
- /*
- ** Can't ever load up so tell the passenger to bug off.
- */
- if (How_Many() >= Class->Max_Passengers()) {
- return(RADIO_NEGATIVE);
- }
- /*
- ** Establish contact and let the loading process proceed normally.
- */
- if (!In_Radio_Contact()) {
- param = TARGET_NONE;
- Transmit_Message(RADIO_HELLO, from);
- Transmit_Message(RADIO_MOVE_HERE, param);
- return(RADIO_ROGER);
- } else {
- /*
- ** This causes the potential passenger to think that all is ok and to
- ** hold on for a bit.
- */
- return(RADIO_ROGER);
- }
- }
- /*
- **
- */
- if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) {
- DriveClass::Receive_Message(from, message, param);
- if (!IsDriving && !IsRotating) {
- // if (!IsDriving && !IsRotating && !IsTethered) {
- /*
- ** If the potential passenger needs someplace to go, then figure out a good
- ** spot and tell it to go.
- */
- if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
- CELL cell;
- DirType dir = Desired_Load_Dir(from, cell);
- /*
- ** If no adjacent free cells are detected, then passenger loading
- ** cannot occur. Break radio contact.
- */
- if (cell == 0) {
- Transmit_Message(RADIO_OVER_OUT, from);
- } else {
- param = (long)::As_Target(cell);
- /*
- ** If it is now facing the correct direction, then open the
- ** transport doors. Close the doors if the transport is full or needs
- ** to rotate.
- */
- if (!Is_Door_Open()) {
- LST_Open_Door();
- }
- /*
- ** Tell the potential passenger where it should go. If the passenger is
- ** already at the staging location, then tell it to move onto the transport
- ** directly.
- */
- if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
- if (Is_Door_Open()) {
- param = (long)As_Target();
- Transmit_Message(RADIO_TETHER);
- if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
- Transmit_Message(RADIO_OVER_OUT, from);
- } else {
- Contact_With_Whom()->Unselect();
- }
- }
- }
- }
- }
- }
- return(RADIO_ROGER);
- }
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) {
- TechnoClass::Receive_Message(from, message, param);
- /*
- ** Establish contact with the object if this building isn't already in contact
- ** with another.
- */
- if (!In_Radio_Contact()) {
- Transmit_Message(RADIO_HELLO, from);
- }
- if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
- param = As_Target();
- if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
- Transmit_Message(RADIO_TETHER);
- }
- }
- return(RADIO_ROGER);
- }
- #endif
- break;
- /*
- ** When this message is received, it means that the other object
- ** has already turned its radio off. Turn this radio off as well.
- */
- case RADIO_OVER_OUT:
- if (Mission == MISSION_RETURN) {
- Assign_Mission(MISSION_GUARD);
- }
- DriveClass::Receive_Message(from, message, param);
- return(RADIO_ROGER);
- }
- return(DriveClass::Receive_Message(from, message, param));
- }
- /***********************************************************************************************
- * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
- * *
- * This routine examines the unit and adjacent cells in order to find the best facing *
- * for the transport and best staging cell for the potential passengers. This location is *
- * modified by adjacent cell passability and direction of the potential passenger. *
- * *
- * INPUT: passenger -- Pointer to the potential passenger. *
- * *
- * moveto -- Reference to the cell number that specifies where the potential *
- * passenger should move to first. *
- * *
- * OUTPUT: Returns with the direction the transport should face before opening the transport *
- * door. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/01/1996 BWG : Created. *
- *=============================================================================================*/
- DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- /*
- ** Determine the ideal facing that provides the least resistance. This would be the direction
- ** of the potential passenger or the current transport facing if it is going to unload.
- */
- DirType faceto;
- if (passenger != NULL) {
- faceto = Direction(passenger);
- } else {
- faceto = PrimaryFacing.Current() + DIR_S;
- }
- /*
- ** Sweep through the adjacent cells in order to find the best candidate.
- */
- FacingType bestdir = FACING_N;
- int bestval = -1;
- for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
- int value = 0;
- CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
- /*
- ** Base the initial value of the potential cell according to whether the passenger is
- ** allowed to enter the cell. If it can't, then give such a negative value to the
- ** cell so that it is prevented from ever choosing that cell for load/unload.
- */
- if (passenger != NULL) {
- value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128;
- } else {
- CellClass * cell = &Map[cellnum];
- 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) {
- value = -128;
- } else {
- if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) {
- value = -128;
- } else {
- value = 128;
- }
- }
- }
- #if(0)
- /*
- ** Give more weight to the cells that require the least rotation of the transport or the
- ** least roundabout movement for the potential passenger.
- */
- value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto);
- if (face == FACING_S) {
- value -= 100;
- }
- if (face == FACING_SW || face == FACING_SE) value += 64;
- #endif
- /*
- ** If the value for the potential cell is greater than the last recorded potential
- ** value, then record this cell as the best candidate.
- */
- if (bestval == -1 || value > bestval) {
- bestval = value;
- bestdir = face;
- // } else {
- // ObjectClass * obj = Map[cellnum].Cell_Occupier();
- // if (obj) obj->Scatter(Coord, true);
- }
- }
- /*
- ** If a suitable direction was found, then return with the direction value.
- */
- moveto = 0;
- if (bestval > 0) {
- static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE};
- moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir);
- return(_desired_to_actual[bestdir]);
- }
- return(DIR_N);
- }
- /***********************************************************************************************
- * VesselClass::LST_Open_Door -- Opens a LST door. *
- * *
- * This routine will initiate opening of the doors on the LST. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/01/1996 BWG : Created. *
- *=============================================================================================*/
- void VesselClass::LST_Open_Door(void)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- if (!IsDriving && !IsRotating) {
- Open_Door(5, 6);
- }
- }
- /***********************************************************************************************
- * VesselClass::LST_Close_Door -- Closes a LST door. *
- * *
- * This routine will initiate closing of the LST door. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/01/1996 BWG : Created. *
- *=============================================================================================*/
- void VesselClass::LST_Close_Door(void)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- Close_Door(5, 6);
- }
- /***********************************************************************************************
- * VesselClass::Mission_Unload -- Handles unloading cargo. *
- * *
- * This is the AI control sequence for when a transport desires to unload its cargo. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the delay before calling this routine again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/01/1996 BWG : Created. *
- *=============================================================================================*/
- int VesselClass::Mission_Unload(void)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- enum {
- INITIAL_CHECK,
- MANEUVERING,
- OPENING_DOOR,
- UNLOADING,
- CLOSING_DOOR
- };
- DirType dir;
- CELL cell;
- switch (Class->Type) {
- case VESSEL_TRANSPORT:
- switch (Status) {
- case INITIAL_CHECK:
- dir = Desired_Load_Dir(NULL, cell);
- if (How_Many() > 0 && cell != 0) {
- Do_Turn(dir);
- Status = MANEUVERING;
- return(1);
- } else {
- if (!How_Many()) { // don't break out if still carrying passengers
- Assign_Mission(MISSION_GUARD);
- }
- }
- break;
- case MANEUVERING:
- if (!IsRotating) {
- LST_Open_Door();
- if (Is_Door_Opening()) {
- Status = OPENING_DOOR;
- return(1);
- }
- }
- break;
- case OPENING_DOOR:
- if (Is_Door_Open()) {
- Status = UNLOADING;
- return(1);
- } else {
- if (!Is_Door_Opening()) {
- Status = INITIAL_CHECK;
- }
- }
- break;
- case UNLOADING:
- if (How_Many()) {
- /*
- ** Don't do anything if still in radio contact.
- */
- if (In_Radio_Contact()) return(TICKS_PER_SECOND);
- FootClass * passenger = Detach_Object();
- if (passenger != NULL) {
- DirType toface = DIR_S + PrimaryFacing;
- bool placed = false;
- for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
- DirType newface = toface + Facing_Dir(face);
- CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
- if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
- ScenarioInit++;
- passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface);
- ScenarioInit--;
- passenger->Assign_Mission(MISSION_MOVE);
- passenger->Assign_Destination(::As_Target(newcell));
- passenger->Commence();
- Transmit_Message(RADIO_HELLO, passenger);
- Transmit_Message(RADIO_TETHER, passenger);
- if (passenger->What_Am_I() == RTTI_UNIT) {
- ((UnitClass *)passenger)->IsToScatter = true;
- }
- placed = true;
- break;
- }
- }
- /*
- ** If the attached unit could NOT be deployed, then re-attach
- ** it and then bail out of this deploy process.
- */
- if (!placed) {
- Attach(passenger);
- /*
- ** Tell everyone around the transport to scatter.
- */
- for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
- CellClass * cellptr = &Map[Coord].Adjacent_Cell(face);
- if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
- cellptr->Incoming(0, true);
- }
- }
- // Status = CLOSING_DOOR;
- }
- }
- } else {
- Status = CLOSING_DOOR;
- }
- break;
- /*
- ** Close LST door in preparation for normal operation.
- */
- case CLOSING_DOOR:
- if (Is_Door_Open()) {
- LST_Close_Door();
- }
- if (Is_Door_Closed()) {
- if (IsALoaner) {
- Assign_Mission(MISSION_RETREAT);
- } else {
- Assign_Mission(MISSION_GUARD);
- }
- }
- break;
- }
- break;
- default:
- break;
- }
- return(MissionControl[Mission].Normal_Delay());
- }
- /***********************************************************************************************
- * VesselClass::Assign_Destination -- Assign a destination for this vessel. *
- * *
- * This routine is called when a destination is to be assigned to this vessel. *
- * *
- * INPUT: target -- The destination to assign to this vessel. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Assign_Destination(TARGET target)
- {
- assert(IsActive);
- /*
- ** Abort early if there is anything wrong with the parameters
- ** or the unit already is assigned the specified destination.
- */
- if (target == NavCom) return;
- /*
- ** Transport vehicles must tell all passengers that are about to load, that they
- ** cannot proceed. This is accomplished with a radio message to this effect.
- */
- if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) {
- long param = TARGET_NONE;
- Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport.
- Transmit_Message(RADIO_OVER_OUT);
- if (!Is_Door_Closed()) {
- LST_Close_Door();
- }
- }
- if (!Is_Door_Closed()) {
- LST_Close_Door();
- }
- DriveClass::Assign_Destination(target);
- }
- /***********************************************************************************************
- * VesselClass::Mission_Retreat -- Perform the retreat mission. *
- * *
- * This will cause the vessel to run away from the battlefield. It searches for an escape *
- * map edge according to the reinforcement edge specified in the house. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of game frames to delay before this routine is called *
- * again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- int VesselClass::Mission_Retreat(void)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- enum {
- PICK_RETREAT_POINT,
- TRAVEL
- };
- switch (Status) {
- case PICK_RETREAT_POINT:
- IsALoaner = true;
- if (!Target_Legal(NavCom)) {
- // CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed);
- CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed);
- if (Team.Is_Valid()) {
- Team->Remove(this);
- }
- Assign_Destination(::As_Target(cell));
- }
- Status = TRAVEL;
- return(1);
- case TRAVEL:
- if (!Target_Legal(NavCom)) {
- Status = PICK_RETREAT_POINT;
- }
- break;
- default:
- break;
- }
- return(MissionControl[Mission].Normal_Delay());
- }
- /***********************************************************************************************
- * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
- * *
- * Asking this question is part of the recloak process. If the answer is no, then *
- * recloaking is postponed. This facilitates keeping submarines visible for longer than *
- * they otherwise would be. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: bool; Can this vessel recloak now? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 BWG : Created. *
- *=============================================================================================*/
- bool VesselClass::Is_Allowed_To_Recloak(void) const
- {
- return(PulseCountDown == 0);
- }
- /***********************************************************************************************
- * VesselClass::Read_INI -- Read the vessel data from the INI database. *
- * *
- * This will read and create all vessels specified in the INI database. This routine is *
- * called when the scenario starts. *
- * *
- * INPUT: ini -- Reference to the INI database to read the vessel data from. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Vessels will be created and placed on the map by this function. *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Read_INI(CCINIClass & ini)
- {
- VesselClass * vessel; // Working vessel pointer.
- HousesType inhouse; // Vessel house.
- VesselType classid; // Vessel class.
- char buf[128];
- int len = ini.Entry_Count(INI_Name());
- for (int index = 0; index < len; index++) {
- char const * entry = ini.Get_Entry(INI_Name(), index);
- ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf));
- inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
- if (inhouse != HOUSE_NONE) {
- classid = VesselTypeClass::From_Name(strtok(NULL, ","));
- if (classid != VESSEL_NONE) {
- vessel = new VesselClass(classid, inhouse);
- if (vessel != NULL) {
- /*
- ** Read the raw data.
- */
- int strength = atoi(strtok(NULL, ",\r\n"));
- CELL cell = atoi(strtok(NULL, ",\r\n"));
- COORDINATE coord = Cell_Coord(cell);
- DirType dir = (DirType)atoi(strtok(NULL, ",\r\n"));
- MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r"));
- vessel->Trigger = NULL;
- TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n"));
- if (tp != NULL) {
- TriggerClass * tt = Find_Or_Make(tp);
- if (tt != NULL) {
- tt->AttachCount++;
- vessel->Trigger = tt;
- }
- }
- if (vessel->Unlimbo(coord, dir)) {
- vessel->Strength = vessel->Class->MaxStrength * fixed(strength, 256);
- if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength;
- // vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength);
- if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) {
- vessel->Assign_Mission(mission);
- vessel->Commence();
- } else {
- vessel->Enter_Idle_Mode();
- }
- } else {
- /*
- ** If the vessel could not be unlimboed, then this is a catastrophic error
- ** condition. Delete the vessel.
- */
- delete vessel;
- }
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
- * *
- * This routine is used to add the vessel data (needed for scenario start) to the INI *
- * database specified. If there was any preexisting vessel data in the database, it will *
- * be cleared *
- * *
- * INPUT: ini -- Reference to the ini database to store the vessel data into. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Write_INI(CCINIClass & ini)
- {
- /*
- ** First, clear out all existing vessel data from the ini file.
- */
- ini.Clear(INI_Name());
- /*
- ** Write the vessel data out.
- */
- for (int index = 0; index < Vessels.Count(); index++) {
- VesselClass * vessel = Vessels.Ptr(index);
- if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) {
- char uname[10];
- char buf[128];
- sprintf(uname, "%d", index);
- sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
- vessel->House->Class->IniName,
- vessel->Class->IniName,
- vessel->Health_Ratio()*256,
- Coord_Cell(vessel->Coord),
- vessel->PrimaryFacing.Current(),
- MissionClass::Mission_Name(vessel->Mission),
- vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None"
- );
- ini.Put_String(INI_Name(), uname, buf);
- }
- }
- }
- /***********************************************************************************************
- * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
- * *
- * This routine is called when the vessel starts moving. It will reserve the destination *
- * cell so that it won't be occupied by another vessel as this one is travelling. *
- * *
- * INPUT: headto -- The coordinate that will be headed to. *
- * *
- * OUTPUT: bool; Was the destination location successfully marked? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- bool VesselClass::Start_Driver(COORDINATE & headto)
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver
- Mark_Track(headto, MARK_DOWN);
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * VesselClass::What_Action -- Determines action to perform on specified cell. *
- * *
- * This routine will determine what action to perform if the mouse were clicked over the *
- * cell specified. *
- * *
- * INPUT: cell -- The cell that the mouse might be clicked on. *
- * *
- * OUTPUT: Returns with the action type that this unit will perform if the mouse were *
- * clicked of the cell specified. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/11/1996 BWG : Created. *
- *=============================================================================================*/
- ActionType VesselClass::What_Action(CELL cell) const
- {
- assert(Vessels.ID(this) == ID);
- assert(IsActive);
- ActionType action = DriveClass::What_Action(cell);
- if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) {
- return(ACTION_MOVE);
- }
- if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) {
- return(ACTION_ATTACK);
- }
- return(action);
- }
- /***********************************************************************************************
- * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
- * *
- * Any turret or body rotation for this vessel will be handled by this routine. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Only call this routine once per vessel per game logic loop. *
- * *
- * HISTORY: *
- * 07/29/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Rotation_AI(void)
- {
- if (Target_Legal(TarCom) && !IsRotating) {
- DirType dir = Direction(TarCom);
- if (Class->IsTurretEquipped) {
- SecondaryFacing.Set_Desired(dir);
- }
- }
- IsRotating = false;
- if (Class->IsTurretEquipped) {
- if (SecondaryFacing.Is_Rotating()) {
- Mark(MARK_CHANGE_REDRAW);
- if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) {
- Mark(MARK_CHANGE_REDRAW);
- }
- /*
- ** If no further rotation is necessary, flag that the rotation
- ** has stopped.
- */
- IsRotating = SecondaryFacing.Is_Rotating();
- }
- }
- }
- /***********************************************************************************************
- * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
- * *
- * This routine will process firing logic for the vessel. It includes searching for targets *
- * and performing any adjustments necessary to bring the target to bear. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Only call this routine once per vessel per game logic loop. *
- * *
- * HISTORY: *
- * 07/29/1996 JLB : Created. *
- *=============================================================================================*/
- void VesselClass::Combat_AI(void)
- {
- if (Target_Legal(TarCom) && Is_Weapon_Equipped()) {
- /*
- ** Determine which weapon can fire. First check for the primary weapon. If that weapon
- ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the
- ** failure code returned is that from the primary weapon.
- */
- int primary = What_Weapon_Should_I_Use(TarCom);
- FireErrorType ok = Can_Fire(TarCom, primary);
- switch (ok) {
- case FIRE_OK:
- Fire_At(TarCom, primary);
- break;
- case FIRE_FACING:
- if (Class->IsTurretEquipped) {
- SecondaryFacing.Set_Desired(Direction(TarCom));
- } else {
- if (!PrimaryFacing.Is_Rotating()) {
- PrimaryFacing.Set_Desired(Direction(TarCom));
- }
- }
- break;
- case FIRE_CLOAKED:
- Mark(MARK_OVERLAP_UP);
- IsFiring = false;
- Mark(MARK_OVERLAP_DOWN);
- Do_Uncloak();
- break;
- }
- }
- }
- /***********************************************************************************************
- * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
- * *
- * In addition to detecting the edge of world case, this routine will delete the vessel *
- * if it occurs. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: bool; Was the vessel deleted by this routine? *
- * *
- * WARNINGS: Be sure to examine the return value and if true, abort any further processing *
- * for this vessel since it has been deleted. This routine should be called once *
- * per vessel per game logic loop. *
- * *
- * HISTORY: *
- * 07/29/1996 JLB : Created. *
- *=============================================================================================*/
- bool VesselClass::Edge_Of_World_AI(void)
- {
- if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) {
- if (Team.Is_Valid()) Team->IsLeaveMap = true;
- Stun();
- delete this;
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
- * *
- * When a vessel repairs, it does so 'by itself' and not under direct control of another *
- * object. This self repair logic is processed here. Upon repair completion of money *
- * exhuastion, the repair process will terminate. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Call this routine only once per vessel per game logic loop. *
- * *
- * HISTORY: *
- * 07/29/1996 BWG : Created. *
- *=============================================================================================*/
- void VesselClass::Repair_AI(void)
- {
- if (IsSelfRepairing) {
- if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) {
- Mark(MARK_CHANGE);
- int cost = Class->Repair_Cost();
- int step = Class->Repair_Step();
- if (House->Available_Money() >= cost) {
- House->Spend_Money(cost);
- Strength += step;
- if (Strength >= Class->MaxStrength) {
- Strength = Class->MaxStrength;
- IsSelfRepairing = IsToSelfRepair = false;
- if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED);
- }
- }
- }
- }
- }
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- /***********************************************************************************************
- * VesselClass::Fire_At -- Try to fire upon the target specified. *
- * *
- * This routine is the auto-fire logic for the ship. It will check *
- * to see if we're an aircraft carrier, and if so, launch one of our *
- * aircraft. If we're not an aircraft carrier, it lets the higher-level *
- * Fire_At logic take over. *
- * *
- * INPUT: target -- The target to fire upon. *
- * *
- * which -- Which weapon to use when firing. 0=primary, 1=secondary. *
- * *
- * OUTPUT: bool; Did firing occur? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 04/26/1994 JLB : Created. *
- *=============================================================================================*/
- BulletClass * VesselClass::Fire_At(TARGET target, int which)
- {
- assert(Units.ID(this) == ID);
- assert(IsActive);
- if (*this == VESSEL_CARRIER) {
- Arm = CarrierLaunchDelay;
- FootClass * passenger = Detach_Object();
- if (passenger != NULL) {
- ScenarioInit++;
- passenger->Unlimbo(Center_Coord());
- ScenarioInit--;
- passenger->Assign_Mission(MISSION_ATTACK);
- passenger->Assign_Target(TarCom);
- passenger->Commence();
- // If we've launched our last aircraft, discontinue attacking.
- if (!How_Many()) Assign_Target(TARGET_NONE);
- }
- } else {
- return DriveClass::Fire_At(target, which);
- }
- return (NULL);
- }
- #endif
|