UNIT.CPP 219 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/UNIT.CPP 1 3/03/97 10:26a Joe_bostic $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : UNIT.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : September 10, 1993 *
  26. * *
  27. * Last Update : November 3, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * Recoil_Adjust -- Adjust pixel values in direction specified. *
  32. * UnitClass::AI -- AI processing for the unit. *
  33. * UnitClass::APC_Close_Door -- Closes an APC door. *
  34. * UnitClass::APC_Open_Door -- Opens an APC door. *
  35. * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
  36. * UnitClass::Active_Click_With -- Performs specified action on specified cell. *
  37. * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. *
  38. * UnitClass::Assign_Destination -- Assign a destination to a unit. *
  39. * UnitClass::Blocking_Object -- Determines how a object blocks a unit *
  40. * UnitClass::Can_Enter_Cell -- Determines cell entry legality. *
  41. * UnitClass::Can_Fire -- Determines if turret can fire upon target. *
  42. * UnitClass::Click_With -- Handles player map clicking while this unit is selected. *
  43. * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. *
  44. * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. *
  45. * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. *
  46. * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
  47. * UnitClass::Draw_It -- Draws a unit object. *
  48. * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. *
  49. * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
  50. * UnitClass::Fire_Direction -- Determines the direction of firing. *
  51. * UnitClass::Firing_AI -- Handle firing logic for this unit. *
  52. * UnitClass::Flag_Attach -- Attaches a house flag to this unit. *
  53. * UnitClass::Flag_Remove -- Removes the house flag from this unit. *
  54. * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. *
  55. * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. *
  56. * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. *
  57. * UnitClass::Harvesting -- Harvests tiberium at the current location. *
  58. * UnitClass::Init -- Clears all units for scenario preparation. *
  59. * UnitClass::Limbo -- Limbo this unit. *
  60. * UnitClass::Mission_Guard -- Special guard mission override processor. *
  61. * UnitClass::Mission_Guard_Area -- Guard area logic for units. *
  62. * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. *
  63. * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. *
  64. * UnitClass::Mission_Move -- Handles special move mission overrides. *
  65. * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. *
  66. * UnitClass::Mission_Unload -- Handles unloading cargo. *
  67. * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
  68. * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. *
  69. * UnitClass::Overlap_List -- Determines overlap list for units. *
  70. * UnitClass::Overrun_Square -- Handles vehicle overrun of a cell. *
  71. * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. *
  72. * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. *
  73. * UnitClass::Random_Animate -- Handles random idle animation for the unit. *
  74. * UnitClass::Read_INI -- Reads units from scenario INI file. *
  75. * UnitClass::Receive_Message -- Handles receiving a radio message. *
  76. * UnitClass::Reload_AI -- Perform reload logic for this unit. *
  77. * UnitClass::Rotation_AI -- Process any turret or body rotation. *
  78. * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. *
  79. * UnitClass::Set_Speed -- Initiate unit movement physics. *
  80. * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. *
  81. * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. *
  82. * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. *
  83. * UnitClass::Start_Driver -- Starts driving and reserves destination cell. *
  84. * UnitClass::Take_Damage -- Inflicts damage points on a unit. *
  85. * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. *
  86. * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
  87. * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. *
  88. * UnitClass::UnitClass -- Constructor for units. *
  89. * UnitClass::Unlimbo -- Removes unit from stasis. *
  90. * UnitClass::What_Action -- Determines action to perform on specified cell. *
  91. * UnitClass::What_Action -- Determines what action would occur if clicked on object. *
  92. * UnitClass::Write_INI -- Store the units to the INI database. *
  93. * UnitClass::delete -- Deletion operator for units. *
  94. * UnitClass::new -- Allocate a unit slot and adjust access arrays. *
  95. * UnitClass::~UnitClass -- Destructor for unit objects. *
  96. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  97. #include "function.h"
  98. #include "COORDA.h"
  99. extern void Logic_Switch_Player_Context(ObjectClass *object);
  100. extern void Logic_Switch_Player_Context(HouseClass *object);
  101. extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
  102. extern bool Is_Legacy_Render_Enabled(void);
  103. static int _GapShroudXTable[]={
  104. -1, 0, 1,
  105. -2,-1, 0, 1, 2,
  106. -2,-1, 0, 1, 2,
  107. -2,-1, 0, 1, 2,
  108. -2,-1, 0, 1, 2,
  109. -2,-1, 0, 1, 2,
  110. -1, 0, 1
  111. };
  112. static int _GapShroudYTable[]={
  113. -3,-3,-3,
  114. -2,-2,-2,-2,-2,
  115. -1,-1,-1,-1,-1,
  116. 0, 0, 0, 0, 0,
  117. 1, 1, 1, 1, 1,
  118. 2, 2, 2, 2, 2,
  119. 3, 3, 3
  120. };
  121. /***********************************************************************************************
  122. * Recoil_Adjust -- Adjust pixel values in direction specified. *
  123. * *
  124. * This is a helper routine that modifies the pixel coordinates provided according to the *
  125. * direction specified. The effect is the simulate recoil effects by moving an object 'back'*
  126. * one pixel. Since the pixels moved depend on facing, this routine handles the pixel *
  127. * adjustment quickly. *
  128. * *
  129. * INPUT: dir -- The direction to base the recoil on. *
  130. * *
  131. * x,y -- References to the pixel coordinates that will be adjusted. *
  132. * *
  133. * OUTPUT: none *
  134. * *
  135. * WARNINGS: none *
  136. * *
  137. * HISTORY: *
  138. * 05/08/1995 JLB : Created. *
  139. *=============================================================================================*/
  140. void Recoil_Adjust(DirType dir, int &x, int &y)
  141. {
  142. static struct {
  143. signed char X,Y;
  144. } _adjust[32] = {
  145. {0, 1}, // N
  146. {0, 1},
  147. {0, 1},
  148. {-1, 1},
  149. {-1, 1}, // NE
  150. {-1, 1},
  151. {-1, 0},
  152. {-1, 0},
  153. {-1, 0}, // E
  154. {-1, 0},
  155. {-1, -1},
  156. {-1, -1},
  157. {-1, -1}, // SE
  158. {-1, -1},
  159. {-1, -1},
  160. {0, -1},
  161. {0, -1}, // S
  162. {0, -1},
  163. {0, -1},
  164. {1, -1},
  165. {1, -1}, // SW
  166. {1, -1},
  167. {1, 0},
  168. {1, 0},
  169. {1, 0}, // W
  170. {1, 0},
  171. {1, 1},
  172. {1, 1},
  173. {1, 1}, // NW
  174. {1, 1},
  175. {0, 1},
  176. {0, 1}
  177. };
  178. int index = Dir_To_32(dir);
  179. x += _adjust[index].X;
  180. y += _adjust[index].Y;
  181. }
  182. /***********************************************************************************************
  183. * UnitClass::new -- Allocate a unit slot and adjust access arrays. *
  184. * *
  185. * This routine will allocate a unit from the available unit pool and *
  186. * fixup all the access lists to match. It will allocate a unit slot *
  187. * from within the range allowed for the specified unit type. If no *
  188. * slot was found, then it will fail. *
  189. * *
  190. * INPUT: none *
  191. * *
  192. * OUTPUT: Returns with a pointer to the allocated unit. *
  193. * *
  194. * WARNINGS: none *
  195. * *
  196. * HISTORY: *
  197. * 04/11/1994 JLB : Created. *
  198. * 04/21/1994 JLB : Converted to operator new. *
  199. *=============================================================================================*/
  200. void * UnitClass::operator new(size_t)
  201. {
  202. void * ptr = Units.Alloc();
  203. if (ptr != NULL) {
  204. ((UnitClass *)ptr)->Set_Active();
  205. }
  206. return(ptr);
  207. }
  208. /***********************************************************************************************
  209. * UnitClass::delete -- Deletion operator for units. *
  210. * *
  211. * This removes the unit from the local allocation system. Since this *
  212. * is a fixed block of memory, not much has to be done to delete the *
  213. * unit. Merely marking it as inactive is enough. *
  214. * *
  215. * INPUT: ptr -- Pointer to the unit to delete. *
  216. * *
  217. * OUTPUT: none *
  218. * *
  219. * WARNINGS: none *
  220. * *
  221. * HISTORY: *
  222. * 04/21/1994 JLB : Created. *
  223. *=============================================================================================*/
  224. void UnitClass::operator delete(void * ptr)
  225. {
  226. if (ptr != NULL) {
  227. ((UnitClass *)ptr)->IsActive = false;
  228. }
  229. Units.Free((UnitClass *)ptr);
  230. }
  231. /***********************************************************************************************
  232. * UnitClass::~UnitClass -- Destructor for unit objects. *
  233. * *
  234. * This destructor will lower the unit count for the owning house as well as inform any *
  235. * other units in communication, that this unit is about to leave reality. *
  236. * *
  237. * INPUT: none *
  238. * *
  239. * OUTPUT: none *
  240. * *
  241. * WARNINGS: none *
  242. * *
  243. * HISTORY: *
  244. * 08/15/1994 JLB : Created. *
  245. *=============================================================================================*/
  246. UnitClass::~UnitClass(void)
  247. {
  248. if (GameActive && Class.Is_Valid()) {
  249. /*
  250. ** Remove this member from any team it may be associated with. This must occur at the
  251. ** top most level of the inheritance hierarchy because it may call virtual functions.
  252. */
  253. if (Team.Is_Valid()) {
  254. Team->Remove(this);
  255. Team = NULL;
  256. }
  257. House->Tracking_Remove(this);
  258. /*
  259. ** If there are any cargo members, delete them.
  260. */
  261. while (Is_Something_Attached()) {
  262. delete Detach_Object();
  263. }
  264. Limbo();
  265. }
  266. ID = -1;
  267. }
  268. /***********************************************************************************************
  269. * UnitClass::UnitClass -- Constructor for units. *
  270. * *
  271. * This constructor for units will initialize the unit into the game *
  272. * system. It will be placed in all necessary tracking lists. The initial condition will *
  273. * be in a state of limbo. *
  274. * *
  275. * INPUT: classid -- The type of unit to create. *
  276. * *
  277. * house -- The house owner of this unit. *
  278. * *
  279. * OUTPUT: none *
  280. * *
  281. * WARNINGS: none *
  282. * *
  283. * HISTORY: *
  284. * 04/21/1994 JLB : Created. *
  285. *=============================================================================================*/
  286. UnitClass::UnitClass(UnitType classid, HousesType house) :
  287. DriveClass(RTTI_UNIT, Units.ID(this), house),
  288. Class(UnitTypes.Ptr((int)classid)),
  289. Flagged(HOUSE_NONE),
  290. IsDumping(false),
  291. Gems(0),
  292. Gold(0),
  293. Tiberium(0),
  294. IsToScatter(false),
  295. ShroudBits(0xFFFFFFFFUL),
  296. ShroudCenter(0),
  297. Reload(0),
  298. SecondaryFacing(PrimaryFacing),
  299. TiberiumUnloadRefinery(TARGET_NONE)
  300. {
  301. Reload = 0;
  302. House->Tracking_Add(this);
  303. Ammo = Class->MaxAmmo;
  304. IsCloakable = Class->IsCloakable;
  305. if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3));
  306. /*
  307. ** For two shooters, clear out the second shot flag -- it will be set the first time
  308. ** the object fires. For non two shooters, set the flag since it will never be cleared
  309. ** and the second shot flag tells the system that normal rearm times apply -- this is
  310. ** what is desired for non two shooters.
  311. */
  312. IsSecondShot = !Class->Is_Two_Shooter();
  313. Strength = Class->MaxStrength;
  314. /*
  315. ** Keep count of the number of units created.
  316. */
  317. // if (Session.Type == GAME_INTERNET) {
  318. // House->UnitTotals->Increment_Unit_Total((int)classid);
  319. // }
  320. }
  321. #ifdef CHEAT_KEYS
  322. /***********************************************************************************************
  323. * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. *
  324. * *
  325. * This displays the current status of the unit class to the mono monitor. By this display *
  326. * bugs may be tracked down or prevented. *
  327. * *
  328. * INPUT: none *
  329. * *
  330. * OUTPUT: none *
  331. * *
  332. * WARNINGS: none *
  333. * *
  334. * HISTORY: *
  335. * 06/02/1994 JLB : Created. *
  336. *=============================================================================================*/
  337. void UnitClass::Debug_Dump(MonoClass * mono) const
  338. {
  339. assert(Units.ID(this) == ID);
  340. assert(IsActive);
  341. mono->Set_Cursor(0, 0);
  342. mono->Print(Text_String(TXT_DEBUG_VEHICLE));
  343. mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());
  344. mono->Set_Cursor(1, 11);mono->Printf("%03", Gems);
  345. mono->Set_Cursor(7, 11);mono->Printf("%03", Gold);
  346. mono->Fill_Attrib(66, 13, 12, 1, IsDumping ? MonoClass::INVERSE : MonoClass::NORMAL);
  347. DriveClass::Debug_Dump(mono);
  348. }
  349. #endif
  350. /***********************************************************************************************
  351. * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. *
  352. * *
  353. * This routine is used by the rendering system in order to sort the *
  354. * game objects in a back to front order. This is now the correct *
  355. * overlap effect is achieved. *
  356. * *
  357. * INPUT: none *
  358. * *
  359. * OUTPUT: Returns with a coordinate value that can be used for sorting. *
  360. * *
  361. * WARNINGS: none *
  362. * *
  363. * HISTORY: *
  364. * 05/17/1994 JLB : Created. *
  365. *=============================================================================================*/
  366. COORDINATE UnitClass::Sort_Y(void) const
  367. {
  368. assert(Units.ID(this) == ID);
  369. assert(IsActive);
  370. return(Coord_Add(Coord, 0x00800000L));
  371. }
  372. /***********************************************************************************************
  373. * UnitClass::AI -- AI processing for the unit. *
  374. * *
  375. * This routine will perform the AI processing necessary for the unit. These are non- *
  376. * graphic related operations. *
  377. * *
  378. * INPUT: none *
  379. * *
  380. * OUTPUT: none *
  381. * *
  382. * WARNINGS: none *
  383. * *
  384. * HISTORY: *
  385. * 05/31/1994 JLB : Created. *
  386. *=============================================================================================*/
  387. void UnitClass::AI(void)
  388. {
  389. assert(Units.ID(this) == ID);
  390. assert(IsActive);
  391. /*
  392. ** Act on new orders if the unit is at a good position to do so.
  393. */
  394. if (Height == 0 && !IsDumping && !IsDriving && Is_Door_Closed() /*&& Mission != MISSION_UNLOAD*/) {
  395. // if (MissionQueue == MISSION_NONE) Enter_Idle_Mode();
  396. Commence();
  397. }
  398. DriveClass::AI();
  399. if (!IsActive || Height > 0) {
  400. return;
  401. }
  402. /*
  403. ** Hack check to ensure that a harvester won't harvest if it is not harvesting.
  404. */
  405. if (Mission != MISSION_HARVEST) {
  406. IsHarvesting = false;
  407. }
  408. /*
  409. ** Handle combat logic for this unit. It will determine if it has a target and
  410. ** if so, if conditions are favorable for firing. When conditions permit, the
  411. ** unit will fire upon its target.
  412. */
  413. Firing_AI();
  414. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  415. if (!IsActive) {
  416. return;
  417. }
  418. #endif
  419. /*
  420. ** Turret rotation processing. Handles rotating radar dish
  421. ** as well as conventional turrets if present. If no turret present, but
  422. ** it decides that the body should face its target, then body rotation
  423. ** would occur by this process as well.
  424. */
  425. Rotation_AI();
  426. /*
  427. ** Scatter units off buildings in guard modes.
  428. */
  429. if (!IsTethered && !IsFiring && !IsDriving && !IsRotating && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord].Cell_Building() != NULL) {
  430. Scatter(0, true, true);
  431. }
  432. /*
  433. ** Delete this unit if it finds itself off the edge of the map and it is in
  434. ** guard or other static mission mode.
  435. */
  436. if (Edge_Of_World_AI()) {
  437. return;
  438. }
  439. /*
  440. ** Units will reload every so often if they are under the burden of
  441. ** being required to reload between shots.
  442. */
  443. Reload_AI();
  444. /*
  445. ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS
  446. ** mission that they can follow. Passenger loading is merely a part of their normal operation.
  447. */
  448. if (Class->Max_Passengers() > 0) {
  449. /*
  450. ** Double check that there is a passenger that is trying to load or unload.
  451. ** If not, then close the door.
  452. */
  453. if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) {
  454. APC_Close_Door();
  455. }
  456. }
  457. /*
  458. ** Don't start a new mission unless the vehicle is in the center of
  459. ** a cell (not driving) and the door (if any) is closed.
  460. */
  461. if (!IsDumping && !IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
  462. Commence();
  463. }
  464. /*
  465. ** A cloaked object that is carrying the flag will always shimmer.
  466. */
  467. if (Cloak == CLOAKED && Flagged != HOUSE_NONE) {
  468. Do_Shimmer();
  469. }
  470. /*
  471. ** Mobile gap generators regenerate their gap every so often (just in case).
  472. */
  473. if (Class->IsGapper && !IsDriving && (Frame % TICKS_PER_SECOND) == 0) {
  474. Shroud_Regen();
  475. }
  476. }
  477. /***********************************************************************************************
  478. * UnitClass::Rotation_AI -- Process any turret or body rotation. *
  479. * *
  480. * This routine will handle the rotation logic for the unit's turret (if it has one) as *
  481. * well as its normal body shape. *
  482. * *
  483. * INPUT: none *
  484. * *
  485. * OUTPUT: none *
  486. * *
  487. * WARNINGS: none *
  488. * *
  489. * HISTORY: *
  490. * 07/30/1996 JLB : Created. *
  491. *=============================================================================================*/
  492. void UnitClass::Rotation_AI(void)
  493. {
  494. if (Target_Legal(TarCom) && !IsRotating) {
  495. DirType dir = Direction(TarCom);
  496. if (Class->IsTurretEquipped) {
  497. SecondaryFacing.Set_Desired(dir);
  498. } else {
  499. /*
  500. ** Non turret equipped vehicles will rotate their body to face the target only
  501. ** if the vehicle isn't currently moving or facing the correct direction. This
  502. ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the
  503. ** target, since they aren't maneuverable enough.
  504. */
  505. if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) {
  506. PrimaryFacing.Set_Desired(dir);
  507. }
  508. }
  509. }
  510. if (Class->IsRadarEquipped) {
  511. Mark(MARK_CHANGE_REDRAW);
  512. SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8));
  513. Mark(MARK_CHANGE_REDRAW);
  514. } else {
  515. IsRotating = false;
  516. if (Class->IsTurretEquipped) {
  517. if (IsTurretLockedDown) {
  518. SecondaryFacing.Set_Desired(PrimaryFacing.Current());
  519. }
  520. if (SecondaryFacing.Is_Rotating()) {
  521. Mark(MARK_CHANGE_REDRAW);
  522. if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) {
  523. Mark(MARK_CHANGE_REDRAW);
  524. }
  525. /*
  526. ** If no further rotation is necessary, flag that the rotation
  527. ** has stopped.
  528. */
  529. if (!Class->IsRadarEquipped) {
  530. IsRotating = SecondaryFacing.Is_Rotating();
  531. }
  532. } else {
  533. if (!IsTurretLockedDown && !Target_Legal(TarCom)) {
  534. if (!Target_Legal(NavCom)) {
  535. SecondaryFacing.Set_Desired(PrimaryFacing.Current());
  536. } else {
  537. SecondaryFacing.Set_Desired(Direction(NavCom));
  538. }
  539. }
  540. }
  541. }
  542. }
  543. }
  544. /***********************************************************************************************
  545. * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. *
  546. * *
  547. * When a unit leaves the map it will be eliminated. This routine checks for this case *
  548. * and eliminates the unit accordingly. *
  549. * *
  550. * INPUT: none *
  551. * *
  552. * OUTPUT: bool; Was the unit eliminated by this routine? *
  553. * *
  554. * WARNINGS: Be sure to check for the return value and if 'true' abort any further processing*
  555. * of the unit since it is dead. Only call this routine once per unit per *
  556. * game logic loop. *
  557. * *
  558. * HISTORY: *
  559. * 07/30/1996 JLB : Created. *
  560. *=============================================================================================*/
  561. bool UnitClass::Edge_Of_World_AI(void)
  562. {
  563. if (Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) {
  564. if (Team.Is_Valid()) Team->IsLeaveMap = true;
  565. Stun();
  566. delete this;
  567. return(true);
  568. }
  569. return(false);
  570. }
  571. /***********************************************************************************************
  572. * UnitClass::Reload_AI -- Perform reload logic for this unit. *
  573. * *
  574. * Some units require special reload logic. The V2 rocket launcher in particular. Perform *
  575. * this reload logic with this routine. *
  576. * *
  577. * INPUT: none *
  578. * *
  579. * OUTPUT: none *
  580. * *
  581. * WARNINGS: Only call this routine once per unit per game logic loop. *
  582. * *
  583. * HISTORY: *
  584. * 07/30/1996 JLB : Created. *
  585. *=============================================================================================*/
  586. void UnitClass::Reload_AI(void)
  587. {
  588. if (*this == UNIT_V2_LAUNCHER && Ammo < Class->MaxAmmo) {
  589. if (IsDriving) {
  590. Reload = Reload + 1;
  591. } else {
  592. if (Reload == 0) {
  593. Ammo++;
  594. if (Ammo < Class->MaxAmmo) {
  595. Reload = TICKS_PER_SECOND*30;
  596. }
  597. Mark(MARK_CHANGE);
  598. }
  599. }
  600. }
  601. }
  602. /***********************************************************************************************
  603. * UnitClass::Firing_AI -- Handle firing logic for this unit. *
  604. * *
  605. * This routine wil check for and perform any firing logic required of this unit. *
  606. * *
  607. * INPUT: none *
  608. * *
  609. * OUTPUT: none *
  610. * *
  611. * WARNINGS: This should be called only once per unit per game logic loop. *
  612. * *
  613. * HISTORY: *
  614. * 07/30/1996 JLB : Created. *
  615. *=============================================================================================*/
  616. void UnitClass::Firing_AI(void)
  617. {
  618. if (Target_Legal(TarCom) && Class->PrimaryWeapon != NULL) {
  619. /*
  620. ** Determine which weapon can fire. First check for the primary weapon. If that weapon
  621. ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the
  622. ** failure code returned is that from the primary weapon.
  623. */
  624. int primary = What_Weapon_Should_I_Use(TarCom);
  625. FireErrorType ok = Can_Fire(TarCom, primary);
  626. switch (ok) {
  627. case FIRE_OK:
  628. if (!((UnitClass *)this)->Class->IsFireAnim) {
  629. Mark(MARK_OVERLAP_UP);
  630. IsFiring = false;
  631. Mark(MARK_OVERLAP_DOWN);
  632. }
  633. Fire_At(TarCom, primary);
  634. break;
  635. case FIRE_FACING:
  636. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  637. if (Class->IsLockTurret || Class->Type == UNIT_DEMOTRUCK) {
  638. #else
  639. if (Class->IsLockTurret) {
  640. #endif
  641. if (!Target_Legal(NavCom) && !IsDriving) {
  642. PrimaryFacing.Set_Desired(Direction(TarCom));
  643. SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
  644. }
  645. } else {
  646. SecondaryFacing.Set_Desired(Direction(TarCom));
  647. }
  648. break;
  649. case FIRE_CLOAKED:
  650. Mark(MARK_OVERLAP_UP);
  651. IsFiring = false;
  652. Mark(MARK_OVERLAP_DOWN);
  653. Do_Uncloak();
  654. break;
  655. }
  656. }
  657. }
  658. /***********************************************************************************************
  659. * UnitClass::Receive_Message -- Handles receiving a radio message. *
  660. * *
  661. * This is the handler function for when a unit receives a radio *
  662. * message. Typical use of this is when a unit unloads from a hover *
  663. * class so that clearing of the transport is successful. *
  664. * *
  665. * INPUT: from -- Pointer to the originator of the message. *
  666. * *
  667. * message -- The radio message received. *
  668. * *
  669. * param -- Reference to an optional parameter the might be needed to return *
  670. * information back to the originator of the message. *
  671. * *
  672. * OUTPUT: Returns with the radio message response. *
  673. * *
  674. * WARNINGS: none *
  675. * *
  676. * HISTORY: *
  677. * 05/22/1994 JLB : Created. *
  678. *=============================================================================================*/
  679. RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
  680. {
  681. assert(Units.ID(this) == ID);
  682. assert(IsActive);
  683. switch (message) {
  684. /*
  685. ** Checks to see if this object is in need of service depot processing.
  686. */
  687. case RADIO_NEED_REPAIR:
  688. if (!IsDriving && !Target_Legal(NavCom) && (Health_Ratio() >= 1 && (*this != UNIT_MINELAYER || Ammo >= Class->MaxAmmo))) return(RADIO_NEGATIVE);
  689. break;
  690. // return(RADIO_ROGER);
  691. /*
  692. ** Asks if the passenger can load on this transport.
  693. */
  694. case RADIO_CAN_LOAD:
  695. if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC);
  696. if (How_Many() < Class->Max_Passengers()) {
  697. return(RADIO_ROGER);
  698. }
  699. return(RADIO_NEGATIVE);
  700. /*
  701. ** The refinery has told this harvester that it should begin the backup procedure
  702. ** so that proper unloading may take place.
  703. */
  704. case RADIO_BACKUP_NOW:
  705. DriveClass::Receive_Message(from, message, param);
  706. if (!IsRotating && PrimaryFacing != DIR_W) {
  707. Do_Turn(DIR_W);
  708. } else {
  709. if (!IsDriving) {
  710. TechnoClass * whom = Contact_With_Whom();
  711. if (IsTethered && whom != NULL) {
  712. if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) {
  713. if (Transmit_Message(RADIO_IM_IN, whom) == RADIO_ROGER) {
  714. Transmit_Message(RADIO_UNLOADED, whom);
  715. }
  716. }
  717. }
  718. }
  719. }
  720. return(RADIO_ROGER);
  721. /*
  722. ** This message is sent by the passenger when it determines that it has
  723. ** entered the transport.
  724. */
  725. case RADIO_IM_IN:
  726. if (How_Many() == Class->Max_Passengers()) {
  727. APC_Close_Door();
  728. }
  729. return(RADIO_ATTACH);
  730. /*
  731. ** Docking maintenance message received. Check to see if new orders should be given
  732. ** to the impatient unit.
  733. */
  734. case RADIO_DOCKING:
  735. /*
  736. ** If this transport is moving, then always abort the docking request.
  737. */
  738. if (IsDriving || Target_Legal(NavCom)) {
  739. return(RADIO_NEGATIVE);
  740. }
  741. /*
  742. ** Check for the case of a docking message arriving from a unit that does not
  743. ** have formal radio contact established. This might be a unit that is standing
  744. ** by. If this transport is free to proceed with normal docking operation, then
  745. ** establish formal contact now. If the transport is completely full, then break
  746. ** off contact. In all other cases, just tell the pending unit to stand by.
  747. */
  748. if (Contact_With_Whom() != from) {
  749. /*
  750. ** Can't ever load up so tell the passenger to bug off.
  751. */
  752. if (How_Many() >= Class->Max_Passengers()) {
  753. return(RADIO_NEGATIVE);
  754. }
  755. /*
  756. ** Establish contact and let the loading process proceed normally.
  757. */
  758. if (!In_Radio_Contact()) {
  759. Transmit_Message(RADIO_HELLO, from);
  760. } else {
  761. /*
  762. ** This causes the potential passenger to think that all is ok and to
  763. ** hold on for a bit.
  764. */
  765. return(RADIO_ROGER);
  766. }
  767. }
  768. if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) {
  769. DriveClass::Receive_Message(from, message, param);
  770. if (!IsDriving && !IsRotating && !IsTethered) {
  771. /*
  772. ** If the potential passenger needs someplace to go, then figure out a good
  773. ** spot and tell it to go.
  774. */
  775. if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
  776. CELL cell;
  777. DirType dir = Desired_Load_Dir(from, cell);
  778. /*
  779. ** If no adjacent free cells are detected, then passenger loading
  780. ** cannot occur. Break radio contact.
  781. */
  782. if (cell == 0) {
  783. Transmit_Message(RADIO_OVER_OUT, from);
  784. } else {
  785. param = (long)::As_Target(cell);
  786. Do_Turn(dir);
  787. /*
  788. ** If it is now facing the correct direction, then open the
  789. ** transport doors. Close the doors if the transport is or needs
  790. ** to rotate.
  791. */
  792. #ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  793. if (*this == UNIT_APC || *this == UNIT_PHASE) {
  794. #else
  795. if (*this == UNIT_APC) {
  796. #endif
  797. if (IsRotating) {
  798. if (!Is_Door_Closed()) {
  799. APC_Close_Door();
  800. }
  801. } else {
  802. if (!Is_Door_Open()) {
  803. APC_Open_Door();
  804. }
  805. }
  806. }
  807. /*
  808. ** Tell the potential passenger where it should go. If the passenger is
  809. ** already at the staging location, then tell it to move onto the transport
  810. ** directly.
  811. */
  812. if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
  813. #ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  814. if ( (*this != UNIT_APC && *this != UNIT_PHASE) || Is_Door_Open()) {
  815. #else
  816. if (*this != UNIT_APC || Is_Door_Open()) {
  817. #endif
  818. param = (long)As_Target();
  819. Transmit_Message(RADIO_TETHER);
  820. if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
  821. Transmit_Message(RADIO_OVER_OUT, from);
  822. } else {
  823. Contact_With_Whom()->Unselect();
  824. }
  825. }
  826. }
  827. }
  828. }
  829. }
  830. return(RADIO_ROGER);
  831. }
  832. break;
  833. /*
  834. ** Something bad has happened to the object in contact with. Abort any coordinated
  835. ** activity with this object. Basically, ... run away! Run away!
  836. */
  837. case RADIO_RUN_AWAY:
  838. if (Class->IsToHarvest && In_Radio_Contact() && Mission == MISSION_ENTER) {
  839. TechnoClass * contact = Contact_With_Whom();
  840. if (contact->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)contact) == STRUCT_REFINERY) {
  841. // Slight hack; set a target so the harvest mission knows to skip to finding home state
  842. Assign_Mission(MISSION_HARVEST);
  843. TarCom = As_Target();
  844. return(RADIO_ROGER);
  845. }
  846. }
  847. return(DriveClass::Receive_Message(from, message, param));
  848. /*
  849. ** When this message is received, it means that the other object
  850. ** has already turned its radio off. Turn this radio off as well.
  851. */
  852. case RADIO_OVER_OUT:
  853. if (Mission == MISSION_RETURN) {
  854. Assign_Mission(MISSION_GUARD);
  855. }
  856. DriveClass::Receive_Message(from, message, param);
  857. return(RADIO_ROGER);
  858. }
  859. return(DriveClass::Receive_Message(from, message, param));
  860. }
  861. /***********************************************************************************************
  862. * UnitClass::Unlimbo -- Removes unit from stasis. *
  863. * *
  864. * This routine will place a unit into the game and out of its limbo *
  865. * state. This occurs whenever a unit is unloaded from a transport. *
  866. * *
  867. * INPUT: coord -- The coordinate to make the unit appear. *
  868. * *
  869. * dir -- The initial facing to impart upon the unit. *
  870. * *
  871. * OUTPUT: bool; Was the unit unlimboed successfully? If the desired *
  872. * coordinate is illegal, then this might very well return *
  873. * false. *
  874. * *
  875. * WARNINGS: none *
  876. * *
  877. * HISTORY: *
  878. * 05/22/1994 JLB : Created. *
  879. *=============================================================================================*/
  880. bool UnitClass::Unlimbo(COORDINATE coord, DirType dir)
  881. {
  882. assert(Units.ID(this) == ID);
  883. assert(IsActive);
  884. /*
  885. ** All units must start out facing one of the 8 major directions.
  886. */
  887. dir = Facing_Dir(Dir_Facing(dir));
  888. if (DriveClass::Unlimbo(coord, dir)) {
  889. SecondaryFacing = dir;
  890. /*
  891. ** Ensure that the owning house knows about the
  892. ** new object.
  893. */
  894. House->UScan |= (1L << Class->Type);
  895. House->ActiveUScan |= (1L << Class->Type);
  896. /*
  897. ** If it starts off the edge of the map, then it already starts cloaked.
  898. */
  899. if (IsCloakable && !IsLocked) Cloak = CLOAKED;
  900. /*
  901. ** Units default to no special animation.
  902. */
  903. Set_Rate(0);
  904. Set_Stage(0);
  905. return(true);
  906. }
  907. return(false);
  908. }
  909. /***********************************************************************************************
  910. * UnitClass::Take_Damage -- Inflicts damage points on a unit. *
  911. * *
  912. * This routine will inflict the specified number of damage points on *
  913. * the given unit. If the unit is destroyed, then this routine will *
  914. * remove the unit cleanly from the game. The return value indicates *
  915. * whether the unit was destroyed. This will allow appropriate death *
  916. * animation or whatever. *
  917. * *
  918. * INPUT: damage-- The number of damage points to inflict. *
  919. * *
  920. * distance -- The distance from the damage center point to the object's center point.*
  921. * *
  922. * warhead--The type of damage to inflict. *
  923. * *
  924. * source -- Who is responsible for this damage? *
  925. * *
  926. * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to *
  927. * RESULT_DESTROYED. *
  928. * *
  929. * WARNINGS: none *
  930. * *
  931. * HISTORY: *
  932. * 05/30/1991 JLB : Created. *
  933. * 07/12/1991 JLB : Script initiated by unit destruction. *
  934. * 04/15/1994 JLB : Converted to member function. *
  935. * 04/16/1994 JLB : Warhead modifier. *
  936. * 06/03/1994 JLB : Added the source of the damage target value. *
  937. * 06/20/1994 JLB : Source is a base class pointer. *
  938. * 11/22/1994 JLB : Shares base damage handler for techno objects. *
  939. * 06/30/1995 JLB : Lasers do maximum damage against gunboat. *
  940. * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. *
  941. *=============================================================================================*/
  942. ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced)
  943. {
  944. assert(Units.ID(this) == ID);
  945. assert(IsActive);
  946. ResultType res = RESULT_NONE;
  947. /*
  948. ** Remember if this object was selected. If it was and it gets destroyed and it has
  949. ** passengers that pop out, then the passengers will inherit the select state.
  950. */
  951. //bool select = (IsSelected && House->IsPlayerControl);
  952. bool select = (Is_Selected_By_Player() );//&& House->IsPlayerControl);
  953. /*
  954. ** In order for a this to be damaged, it must either be a unit
  955. ** with a crew or a sandworm.
  956. */
  957. res = DriveClass::Take_Damage(damage, distance, warhead, source, forced);
  958. if (res == RESULT_DESTROYED) {
  959. Death_Announcement(source);
  960. Shroud_Regen(); // remove the shroud if it's a gap generator
  961. if (Class->Explosion != ANIM_NONE) {
  962. AnimType anim = Class->Explosion;
  963. /*
  964. ** SSM launchers will really explode big if they are carrying
  965. ** missiles at the time of the explosion.
  966. */
  967. if (*this == UNIT_V2_LAUNCHER && Ammo) {
  968. anim = ANIM_NAPALM3;
  969. }
  970. new AnimClass(anim, Coord);
  971. /*
  972. ** Harvesters explode with a force equal to the amount of
  973. ** Tiberium they are carrying.
  974. */
  975. if (Tiberium > 0 && Rule.IsExplosiveHarvester) {
  976. Wide_Area_Damage(Coord, CELL_LEPTON_W + CELL_LEPTON_W/2, Credit_Load()+Class->MaxStrength, this, WARHEAD_HE);
  977. }
  978. /*
  979. ** Very strong units that have an explosion will also rock the
  980. ** screen when they are destroyed.
  981. */
  982. if (Class->MaxStrength > 400) {
  983. Shake_The_Screen(3, Owner());
  984. if (source && Owner() != source->Owner()) {
  985. Shake_The_Screen(3, source->Owner());
  986. }
  987. }
  988. }
  989. /*
  990. ** Possibly have the crew member run away.
  991. */
  992. CELL cell = Coord_Cell(Center_Coord());
  993. Mark(MARK_UP);
  994. if (Class->IsCrew && Class->Max_Passengers() == 0) {
  995. if (Percent_Chance(50)) {
  996. InfantryClass * i = 0;
  997. if (Class->PrimaryWeapon == NULL) {
  998. i = new InfantryClass(INFANTRY_C1, House->Class->House);
  999. if (i != NULL) i->IsTechnician = true;
  1000. } else {
  1001. i = new InfantryClass(INFANTRY_E1, House->Class->House);
  1002. }
  1003. if (i != NULL) {
  1004. if (i->Unlimbo(Coord, DIR_N)) {
  1005. i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2);
  1006. i->Scatter(0, true);
  1007. if (!House->IsHuman) {
  1008. i->Assign_Mission(MISSION_HUNT);
  1009. } else {
  1010. i->Assign_Mission(MISSION_GUARD);
  1011. }
  1012. if (select) i->Select();
  1013. } else {
  1014. delete i;
  1015. }
  1016. }
  1017. }
  1018. } else {
  1019. while (Is_Something_Attached()) {
  1020. FootClass * object = Detach_Object();
  1021. if (object == NULL) break; // How can this happen?
  1022. /*
  1023. ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure
  1024. ** thing.
  1025. */
  1026. if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) {
  1027. object->Look(false);
  1028. object->Scatter(0, true);
  1029. if (select) object->Select();
  1030. } else {
  1031. object->Record_The_Kill(source);
  1032. delete object;
  1033. }
  1034. }
  1035. }
  1036. /*
  1037. ** If this is a truck, there is a possibility that a crate will drop out
  1038. ** if the scenario so indicates and there is room.
  1039. */
  1040. if (Scen.IsTruckCrate && *this == UNIT_TRUCK) {
  1041. cell = Nearby_Location();
  1042. if (cell != 0) {
  1043. new OverlayClass(OVERLAY_WOOD_CRATE, cell);
  1044. }
  1045. }
  1046. if (*this == UNIT_MCV) {
  1047. if (House) {
  1048. House->Check_Pertinent_Structures();
  1049. }
  1050. }
  1051. /*
  1052. ** Finally, delete the vehicle.
  1053. */
  1054. delete this;
  1055. } else {
  1056. /*
  1057. ** When damaged and below half strength, start smoking if
  1058. ** it isn't already smoking.
  1059. */
  1060. if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached) {
  1061. #ifdef FIXIT_ANTS
  1062. if (*this != UNIT_ANT1 && *this != UNIT_ANT2 && *this != UNIT_ANT3) {
  1063. #endif
  1064. AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
  1065. if (anim) anim->Attach_To(this);
  1066. #ifdef FIXIT_ANTS
  1067. }
  1068. #endif
  1069. }
  1070. /*
  1071. ** Try to crush anyone that fires on this unit if possible. The harvester
  1072. ** typically is the only one that will qualify here.
  1073. */
  1074. if (!Team.Is_Valid() && source != NULL && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Rule.IsAutoCrush)) {
  1075. /*
  1076. ** Try to crush the attacker if it can be crushed by this unit and this unit is
  1077. ** not equipped with a flame type weapon. If this unit has a weapon and the target
  1078. ** is not very close, then fire on it instead. In easy mode, they never run over the
  1079. ** player. In hard mode, they always do. In normal mode, they only overrun past
  1080. ** mission #8.
  1081. */
  1082. if (Should_Crush_It(source)) {
  1083. Assign_Destination(source->As_Target());
  1084. Assign_Mission(MISSION_MOVE);
  1085. } else {
  1086. /*
  1087. ** Try to return to base if possible.
  1088. */
  1089. if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() <= Rule.ConditionYellow) {
  1090. /*
  1091. ** Find nearby refinery and head to it?
  1092. */
  1093. BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false);
  1094. /*
  1095. ** Since the refinery said it was ok to load, establish radio
  1096. ** contact with the refinery and then await docking orders.
  1097. */
  1098. if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
  1099. Assign_Mission(MISSION_ENTER);
  1100. }
  1101. }
  1102. }
  1103. }
  1104. /*
  1105. ** Computer controlled harvester will radio for help if they are attacked.
  1106. */
  1107. if (*this == UNIT_HARVESTER && !House->IsHuman && source) {
  1108. Base_Is_Attacked(source);
  1109. }
  1110. }
  1111. return(res);
  1112. }
  1113. /***********************************************************************************************
  1114. * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible*
  1115. * *
  1116. * This routine intercepts the active click operation. It check to see if this is a self *
  1117. * deployment request (MCV's have this ability). If it is, then the object is initiated *
  1118. * to self deploy. In the other cases, it passes the operation down to the lower *
  1119. * classes for processing. *
  1120. * *
  1121. * INPUT: action -- The action requested of the unit. *
  1122. * *
  1123. * object -- The object that the mouse pointer is over. *
  1124. * *
  1125. * OUTPUT: none *
  1126. * *
  1127. * WARNINGS: none *
  1128. * *
  1129. * HISTORY: *
  1130. * 03/10/1995 JLB : Created. *
  1131. *=============================================================================================*/
  1132. void UnitClass::Active_Click_With(ActionType action, ObjectClass * object)
  1133. {
  1134. assert(Units.ID(this) == ID);
  1135. assert(IsActive);
  1136. if (action != What_Action(object)) {
  1137. action = What_Action(object);
  1138. switch (action) {
  1139. case ACTION_SABOTAGE:
  1140. case ACTION_CAPTURE:
  1141. action = ACTION_ATTACK;
  1142. break;
  1143. case ACTION_ENTER:
  1144. action = ACTION_MOVE;
  1145. break;
  1146. default:
  1147. break;
  1148. }
  1149. }
  1150. /*
  1151. ** Short circuit out if trying to tell a unit to "nomove" to itself. This bypass of the
  1152. ** normal active click with logic prevents any disturbance to the vehicle's state. Without
  1153. ** this bypass, a unit on a repair bay would stop repairing because it would break radio
  1154. ** contact.
  1155. */
  1156. if (object == this && action == ACTION_NOMOVE) {
  1157. return;
  1158. }
  1159. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1160. if (*this == UNIT_MAD && (IsDumping || Gold)) {
  1161. } else {
  1162. DriveClass::Active_Click_With(action, object);
  1163. }
  1164. #else
  1165. DriveClass::Active_Click_With(action, object);
  1166. #endif
  1167. }
  1168. /***********************************************************************************************
  1169. * UnitClass::Active_Click_With -- Performs specified action on specified cell. *
  1170. * *
  1171. * This routine is called when the mouse has been clicked over a cell and this unit must *
  1172. * now respond. Notice that this is merely a placeholder function that exists because there *
  1173. * is another function of the same name that needs to be overloaded. C++ has scoping *
  1174. * restrictions when there are two identically named functions that are overridden in *
  1175. * different classes -- it handles it badly, hence the existence of this routine. *
  1176. * *
  1177. * INPUT: action -- The action to perform on the cell specified. *
  1178. * *
  1179. * cell -- The cell that the action is to be performed on. *
  1180. * *
  1181. * OUTPUT: none *
  1182. * *
  1183. * WARNINGS: none *
  1184. * *
  1185. * HISTORY: *
  1186. * 09/21/1995 JLB : Created. *
  1187. *=============================================================================================*/
  1188. void UnitClass::Active_Click_With(ActionType action, CELL cell)
  1189. {
  1190. assert(Units.ID(this) == ID);
  1191. assert(IsActive);
  1192. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1193. if (*this == UNIT_MAD && (IsDumping || Gold)) {
  1194. } else {
  1195. DriveClass::Active_Click_With(action, cell);
  1196. }
  1197. #else
  1198. DriveClass::Active_Click_With(action, cell);
  1199. #endif
  1200. }
  1201. void UnitClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
  1202. {
  1203. assert(Units.ID(this) == ID);
  1204. assert(IsActive);
  1205. if (mission == MISSION_HARVEST) {
  1206. ArchiveTarget = TARGET_NONE;
  1207. } else if (mission == MISSION_ENTER) {
  1208. BuildingClass* building = As_Building(destination);
  1209. if (building != NULL && *building == STRUCT_REFINERY && building->In_Radio_Contact()) {
  1210. building->Transmit_Message(RADIO_OVER_OUT);
  1211. }
  1212. }
  1213. DriveClass::Player_Assign_Mission(mission, target, destination);
  1214. }
  1215. /***********************************************************************************************
  1216. * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
  1217. * *
  1218. * This routine is called when the unit completes one mission but does not have a clear *
  1219. * follow up mission to perform. In such a case, the unit should enter a default idle *
  1220. * state. This idle state varies depending on what the current internal computer *
  1221. * settings of the unit is as well as what kind of unit it is. *
  1222. * *
  1223. * INPUT: initial -- Is this called when the unit just leaves a factory or is initially *
  1224. * or is initially placed on the map? *
  1225. * *
  1226. * OUTPUT: none *
  1227. * *
  1228. * WARNINGS: none *
  1229. * *
  1230. * HISTORY: *
  1231. * 05/31/1994 JLB : Created. *
  1232. * 06/03/1994 JLB : Fixed to handle non-combat vehicles. *
  1233. * 06/18/1995 JLB : Allows a harvester to stop harvesting. *
  1234. *=============================================================================================*/
  1235. void UnitClass::Enter_Idle_Mode(bool initial)
  1236. {
  1237. assert(Units.ID(this) == ID);
  1238. assert(IsActive);
  1239. MissionType order = MISSION_GUARD;
  1240. if (IsToScatter) {
  1241. IsToScatter = false;
  1242. Scatter(0, true);
  1243. }
  1244. /*
  1245. ** A movement mission without a NavCom would be pointless to have a radio contact since
  1246. ** no radio coordination occurs on a just a simple movement mission.
  1247. */
  1248. if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) {
  1249. Transmit_Message(RADIO_OVER_OUT);
  1250. }
  1251. Handle_Navigation_List();
  1252. if (Target_Legal(NavCom)) {
  1253. order = MISSION_MOVE;
  1254. } else {
  1255. if (!Is_Weapon_Equipped()) {
  1256. if (Class->IsToHarvest) {
  1257. if (!In_Radio_Contact() && Mission != MISSION_HARVEST && MissionQueue != MISSION_HARVEST) {
  1258. if (initial || !House->IsHuman || Map[Coord].Land_Type() == LAND_TIBERIUM) {
  1259. order = MISSION_HARVEST;
  1260. } else {
  1261. order = MISSION_GUARD;
  1262. }
  1263. Assign_Target(TARGET_NONE);
  1264. Assign_Destination(TARGET_NONE);
  1265. } else {
  1266. return;
  1267. }
  1268. } else {
  1269. if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team.Is_Valid()) {
  1270. order = MISSION_UNLOAD;
  1271. } else {
  1272. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1273. if(*this == UNIT_MAD && Mission == MISSION_UNLOAD) {
  1274. order = MISSION_UNLOAD;
  1275. } else {
  1276. #endif
  1277. order = MISSION_GUARD;
  1278. Assign_Target(TARGET_NONE);
  1279. Assign_Destination(TARGET_NONE);
  1280. }
  1281. }
  1282. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1283. }
  1284. #endif
  1285. } else {
  1286. if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) {
  1287. return;
  1288. }
  1289. if (House->IQ < Rule.IQGuardArea || Team.Is_Valid()) {
  1290. order = MISSION_GUARD;
  1291. } else {
  1292. order = MISSION_GUARD_AREA;
  1293. }
  1294. }
  1295. }
  1296. Assign_Mission(order);
  1297. }
  1298. /***********************************************************************************************
  1299. * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. *
  1300. * *
  1301. * This routine is used by the MCV to find a clear spot to deploy. If a clear spot *
  1302. * is found, then the MCV will assign that location to its navigation computer. This only *
  1303. * occurs if the MCV isn't already heading toward a spot. *
  1304. * *
  1305. * INPUT: none *
  1306. * *
  1307. * OUTPUT: bool; Is the located at a spot where it can deploy? *
  1308. * *
  1309. * WARNINGS: none *
  1310. * *
  1311. * HISTORY: *
  1312. * 06/27/1994 JLB : Created. *
  1313. *=============================================================================================*/
  1314. bool UnitClass::Goto_Clear_Spot(void)
  1315. {
  1316. assert(Units.ID(this) == ID);
  1317. assert(IsActive);
  1318. Mark(MARK_UP);
  1319. if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Adjacent_Cell(Coord_Cell(Center_Coord()), FACING_NW))) {
  1320. Mark(MARK_DOWN);
  1321. return(true);
  1322. }
  1323. if (!Target_Legal(NavCom)) {
  1324. /*
  1325. ** This scan table is skewed to north scanning only. This should
  1326. ** probably be converted to a more flexible method.
  1327. */
  1328. static int _offsets[] = {
  1329. -MAP_CELL_W*1,
  1330. -MAP_CELL_W*2,
  1331. -(MAP_CELL_W*2)+1,
  1332. -(MAP_CELL_W*2)-1,
  1333. -MAP_CELL_W*3,
  1334. -(MAP_CELL_W*3)+1,
  1335. -(MAP_CELL_W*3)-1,
  1336. -(MAP_CELL_W*3)+2,
  1337. -(MAP_CELL_W*3)-2,
  1338. -MAP_CELL_W*4,
  1339. -(MAP_CELL_W*4)+1,
  1340. -(MAP_CELL_W*4)-1,
  1341. -(MAP_CELL_W*4)+2,
  1342. -(MAP_CELL_W*4)-2,
  1343. //BG: Added south scanning
  1344. MAP_CELL_W*1,
  1345. MAP_CELL_W*2,
  1346. (MAP_CELL_W*2)+1,
  1347. (MAP_CELL_W*2)-1,
  1348. MAP_CELL_W*3,
  1349. (MAP_CELL_W*3)+1,
  1350. (MAP_CELL_W*3)-1,
  1351. (MAP_CELL_W*3)+2,
  1352. (MAP_CELL_W*3)-2,
  1353. MAP_CELL_W*4,
  1354. (MAP_CELL_W*4)+1,
  1355. (MAP_CELL_W*4)-1,
  1356. (MAP_CELL_W*4)+2,
  1357. (MAP_CELL_W*4)-2,
  1358. //BG: Added some token east/west scanning
  1359. -1,-2,-3,-4,
  1360. 1, 2, 3, 4,
  1361. 0
  1362. };
  1363. int * ptr;
  1364. ptr = &_offsets[0];
  1365. while (*ptr) {
  1366. CELL cell = Coord_Cell(Coord)+*ptr++;
  1367. CELL check_cell = Adjacent_Cell(cell, FACING_NW);
  1368. if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(check_cell)) {
  1369. Assign_Destination(::As_Target(cell));
  1370. break;
  1371. }
  1372. }
  1373. }
  1374. Mark(MARK_DOWN);
  1375. /*
  1376. ** If we couldn't find a destination to go to, let's try random movement
  1377. ** to see if that brings us to a better spot.
  1378. */
  1379. if(!Target_Legal(NavCom) && !House->IsHuman) {
  1380. Scatter(0);
  1381. }
  1382. return(false);
  1383. }
  1384. /***********************************************************************************************
  1385. * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. *
  1386. * *
  1387. * Certain units have the ability to deploy into a building. When this routine is called *
  1388. * for one of those units, it will attempt to deploy at its current location. If the unit *
  1389. * is in motion to a destination or it isn't one of the special units that can deploy or *
  1390. * it isn't allowed to deploy at this location for some reason it won't deploy. In all *
  1391. * other cases, it will begin to deploy and once it begins only a player abort action will *
  1392. * stop it. *
  1393. * *
  1394. * INPUT: none *
  1395. * *
  1396. * OUTPUT: bool; Was deployment begun? *
  1397. * *
  1398. * WARNINGS: none *
  1399. * *
  1400. * HISTORY: *
  1401. * 06/18/1994 JLB : Created. *
  1402. *=============================================================================================*/
  1403. bool UnitClass::Try_To_Deploy(void)
  1404. {
  1405. assert(Units.ID(this) == ID);
  1406. assert(IsActive);
  1407. if (!Target_Legal(NavCom) && !IsRotating) {
  1408. if (*this == UNIT_MCV) {
  1409. /*
  1410. ** Determine if it is legal to deploy at this location. If not, tell the
  1411. ** player.
  1412. */
  1413. Mark(MARK_UP);
  1414. CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW));
  1415. if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) {
  1416. if (PlayerPtr == House) {
  1417. Speak(VOX_DEPLOY);
  1418. }
  1419. if (!House->IsHuman) {
  1420. BuildingTypeClass::As_Reference(STRUCT_CONST).Flush_For_Placement(cell, House);
  1421. }
  1422. Mark(MARK_DOWN);
  1423. IsDeploying = false;
  1424. return(false);
  1425. }
  1426. Mark(MARK_DOWN);
  1427. /*
  1428. ** If the unit is not facing the correct direction, then start it rotating
  1429. ** toward the right facing, but still flag it as if it had deployed. This is
  1430. ** because it will deploy as soon as it reaches the correct facing.
  1431. */
  1432. if (PrimaryFacing.Current() != DIR_SW) {
  1433. Do_Turn(DIR_SW);
  1434. // PrimaryFacing.Set_Desired(DIR_SW);
  1435. IsDeploying = true;
  1436. return(true);
  1437. }
  1438. /*
  1439. ** Since the unit is already facing the correct direction, actually do the
  1440. ** deploy logic. If for some reason this cannot occur, then don't delete the
  1441. ** unit, just mark it as not deploying.
  1442. */
  1443. Mark(MARK_UP);
  1444. BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House);
  1445. if (building != NULL) {
  1446. if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) {
  1447. /*
  1448. ** Play the buildup sound for the player if this is the players
  1449. ** MCV.
  1450. */
  1451. if (building->House == PlayerPtr) {
  1452. Sound_Effect(VOC_PLACE_BUILDING_DOWN, Center_Coord());
  1453. } else {
  1454. building->IsToRebuild = true;
  1455. building->IsToRepair = true;
  1456. }
  1457. /*
  1458. ** Always reveal the construction yard to the player that owned the
  1459. ** mobile construction vehicle.
  1460. */
  1461. building->Revealed(House);
  1462. /*
  1463. ** When the MCV deploys, always consider production to have started
  1464. ** for the owning house. This ensures that in multiplay, computer
  1465. ** opponents will begin construction as soon as they start their
  1466. ** base.
  1467. */
  1468. House->IsStarted = true;
  1469. /*
  1470. ** Force the newly placed construction yard to be in the same strength
  1471. ** ratio as the MCV that deployed into it.
  1472. */
  1473. building->Strength = Health_Ratio() * (int)building->Class->MaxStrength;
  1474. /*
  1475. ** Force the MCV to drop any flag it was carrying. This will also set
  1476. ** the owner house's flag home cell (since the house's FlagHome is
  1477. ** presumably 0 at this point).
  1478. */
  1479. Stun();
  1480. /*
  1481. ** If this MCV was teleported here, clear the gray flag so
  1482. ** the screen will go back to color.
  1483. */
  1484. if (IsMoebius && !Scen.IsFadingColor) {
  1485. Scen.IsFadingBW = false;
  1486. Scen.IsFadingColor = true;
  1487. Scen.FadeTimer = GRAYFADETIME;
  1488. }
  1489. delete this;
  1490. return(true);
  1491. } else {
  1492. /*
  1493. ** Could not deploy the construction yard at this location! Just revert
  1494. ** back to normal "just sitting there" mode and await further instructions.
  1495. */
  1496. delete building;
  1497. }
  1498. }
  1499. Mark(MARK_DOWN);
  1500. IsDeploying = false;
  1501. }
  1502. }
  1503. return(false);
  1504. }
  1505. /***********************************************************************************************
  1506. * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. *
  1507. * *
  1508. * This routine will perform the operations necessary that occur when a unit is at the *
  1509. * center of a cell. These operations could entail deploying into a construction yard, *
  1510. * radioing a transport unit, and looking around for the enemy. *
  1511. * *
  1512. * INPUT: why -- Specifies the circumstances under which this routine was called. *
  1513. * *
  1514. * OUTPUT: none *
  1515. * *
  1516. * WARNINGS: none *
  1517. * *
  1518. * HISTORY: *
  1519. * 06/18/1994 JLB : Created. *
  1520. * 06/17/1995 JLB : Handles case when building says "NO!" *
  1521. * 06/30/1995 JLB : Gunboats head back and forth now. *
  1522. *=============================================================================================*/
  1523. void UnitClass::Per_Cell_Process(PCPType why)
  1524. {
  1525. assert(Units.ID(this) == ID);
  1526. assert(IsActive);
  1527. CELL cell = Coord_Cell(Coord);
  1528. HousesType house;
  1529. if (why == PCP_END || why == PCP_ROTATION) {
  1530. /*
  1531. ** Check to see if this is merely the end of a rotation for the MCV as it is
  1532. ** preparing to deploy. In this case, it should begin its deploy process.
  1533. */
  1534. if (IsDeploying) {
  1535. Try_To_Deploy();
  1536. if (!IsActive) return; // Unit no longer exists -- bail.
  1537. }
  1538. }
  1539. BStart(BENCH_PCP);
  1540. if (why == PCP_END) {
  1541. /*
  1542. ** If this is a unit that is driving onto a building then the unit must enter
  1543. ** the building as the final step.
  1544. */
  1545. TechnoClass * whom = Contact_With_Whom();
  1546. if (IsTethered && whom != NULL) {
  1547. if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) {
  1548. if (whom == Map[CELL(cell-MAP_CELL_W)].Cell_Building()) {
  1549. switch (Transmit_Message(RADIO_IM_IN, whom)) {
  1550. case RADIO_ROGER:
  1551. break;
  1552. case RADIO_ATTACH:
  1553. break;
  1554. default:
  1555. Scatter(0, true);
  1556. break;
  1557. }
  1558. }
  1559. }
  1560. }
  1561. /*
  1562. ** Unit entering a transport vehicle will break radio contact
  1563. ** and attach itself to the transporter.
  1564. */
  1565. TechnoClass * techno = Contact_With_Whom();
  1566. if (Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) {
  1567. if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) {
  1568. Limbo();
  1569. techno->Attach(this);
  1570. }
  1571. BEnd(BENCH_PCP);
  1572. return;
  1573. }
  1574. /*
  1575. ** When breaking away from a transport object or building, possibly
  1576. ** scatter or otherwise begin normal unit operations.
  1577. */
  1578. if (IsTethered && (Mission != MISSION_ENTER ||
  1579. (As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom))
  1580. ) &&
  1581. Mission != MISSION_UNLOAD) {
  1582. /*
  1583. ** Special hack check to make sure that even though it has moved one
  1584. ** cell, if it is still on the building (e.g., service depot), have
  1585. ** it scatter again.
  1586. */
  1587. if (Map[Coord].Cell_Building() != NULL && !Target_Legal(NavCom)) {
  1588. Scatter(0, true, true);
  1589. } else {
  1590. TechnoClass * contact = Contact_With_Whom();
  1591. if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) {
  1592. if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)contact) != STRUCT_REPAIR) {
  1593. Assign_Mission(MISSION_HARVEST);
  1594. } else if (!Target_Legal(NavCom)) {
  1595. Scatter(0, true);
  1596. } else {
  1597. /*
  1598. ** Special case hack to allow automatic transition to loading
  1599. ** onto a transport (or other situation) if the destination
  1600. ** so indicates.
  1601. */
  1602. TechnoClass * techno = As_Techno(NavCom);
  1603. if (techno != NULL) {
  1604. Transmit_Message(RADIO_DOCKING, techno);
  1605. }
  1606. }
  1607. } else {
  1608. if (*this == UNIT_HARVESTER) {
  1609. if (Target_Legal(ArchiveTarget)) {
  1610. Assign_Mission(MISSION_HARVEST);
  1611. Assign_Destination(ArchiveTarget);
  1612. ArchiveTarget = TARGET_NONE;
  1613. } else {
  1614. /*
  1615. ** Since there is no place to go, move away to clear
  1616. ** the pad for another harvester.
  1617. */
  1618. if (!Target_Legal(NavCom)) {
  1619. Scatter(0, true);
  1620. }
  1621. }
  1622. }
  1623. }
  1624. }
  1625. }
  1626. /*
  1627. ** If this is a loaner unit and is is off the edge of the
  1628. ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that
  1629. ** it is probably carrying an incoming reinforcement and it should not be eliminated.
  1630. */
  1631. if (Edge_Of_World_AI()) {
  1632. BEnd(BENCH_PCP);
  1633. return;
  1634. }
  1635. /*
  1636. ** The unit performs looking around at this time. If the
  1637. ** unit moved further than one square during the last track
  1638. ** move, don't do an incremental look. Do a full look around
  1639. ** instead.
  1640. */
  1641. if (IsPlanningToLook) {
  1642. IsPlanningToLook = false;
  1643. Look(false);
  1644. } else {
  1645. Look(true);
  1646. }
  1647. /*
  1648. ** If this is a mobile gap generator, restore the shroud where appropriate
  1649. ** and re-shroud around us.
  1650. */
  1651. if (Class->IsGapper && !House->IsPlayerControl) {
  1652. Shroud_Regen();
  1653. }
  1654. /*
  1655. ** Act on new orders if the unit is at a good position to do so.
  1656. */
  1657. if (!IsDumping) {
  1658. Commence();
  1659. }
  1660. /*
  1661. ** Certain units require some setup time after they come to a halt.
  1662. */
  1663. if (!Target_Legal(NavCom) && Path[0] == FACING_NONE) {
  1664. if (Class->IsNoFireWhileMoving) {
  1665. Arm = Rearm_Delay(true)/4;
  1666. }
  1667. }
  1668. /*
  1669. ** If there is a house flag here, then this unit just might pick it up.
  1670. */
  1671. if (Flagged == HOUSE_NONE) {
  1672. if (Map[cell].IsFlagged && !House->Is_Ally(Map[cell].Owner)) {
  1673. HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this);
  1674. }
  1675. }
  1676. /*
  1677. ** If this is the unit's own flag-home-cell and the unit is carrying
  1678. ** a flag, destroy the house of the flag the unit is carrying.
  1679. */
  1680. if (Flagged != HOUSE_NONE) {
  1681. /*
  1682. ** If this vehicle is carrying your flag, then it will reveal the
  1683. ** map for you as well as itself. This gives you and opportunity to
  1684. ** attack the unit.
  1685. */
  1686. if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) {
  1687. Map.Sight_From(Coord_Cell(Coord), Class->SightRange, House, true);
  1688. }
  1689. /*
  1690. ** If the flag reaches the home cell for the player, then the flag's
  1691. ** owner will be destroyed.
  1692. */
  1693. if (cell == HouseClass::As_Pointer(Owner())->FlagHome) {
  1694. house = Flagged; // Flag_Remove will clear 'Flagged', so save it
  1695. HouseClass::As_Pointer(house)->Flag_Remove(As_Target(), true);
  1696. HouseClass::As_Pointer(house)->Flag_To_Die();
  1697. }
  1698. }
  1699. /*
  1700. ** If entering a cell with a land mine in it, blow up the mine.
  1701. */
  1702. BuildingClass * bldng = Map[cell].Cell_Building();
  1703. if (bldng != NULL && (*bldng == STRUCT_AVMINE || *bldng == STRUCT_APMINE) && !bldng->House->Is_Ally(this)) {
  1704. /*
  1705. ** Special case: if it's a land mine deployer, and it ran over the
  1706. ** type of mine it deploys (only possible if it just dropped it
  1707. ** down) then ignore the mine.
  1708. */
  1709. if (*this != UNIT_MINELAYER || bldng->House != House) {
  1710. COORDINATE blcoord = bldng->Center_Coord();
  1711. new AnimClass(ANIM_MINE_EXP1, blcoord);
  1712. // new AnimClass(Combat_Anim(Rule.AVMineDamage, WARHEAD_HE, Map[cell].Land_Type()), blcoord);
  1713. /*
  1714. ** Vehicles blow up both mines, but they only take significant damage from AV mines.
  1715. */
  1716. if (*bldng == STRUCT_AVMINE) {
  1717. int damage = Rule.AVMineDamage;
  1718. Take_Damage(damage, 0, WARHEAD_HE);
  1719. } else {
  1720. int damage = 10;
  1721. Take_Damage(damage, 0, WARHEAD_HE);
  1722. }
  1723. delete bldng;
  1724. if (!IsActive) {
  1725. BEnd(BENCH_PCP);
  1726. return;
  1727. }
  1728. }
  1729. }
  1730. /*
  1731. ** If after all is said and done, the unit finishes its move on an impassable cell, then
  1732. ** it must presume that it is in the case of a unit driving onto a bridge that blows up
  1733. ** before the unit completes it's move. In such a case the unit should have been destroyed
  1734. ** anyway, so blow it up now.
  1735. */
  1736. LandType land = Map[Coord].Land_Type();
  1737. if (!IsDriving && IsMovingOntoBridge && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) {
  1738. new AnimClass(Combat_Anim(Strength, WARHEAD_AP, land), Coord);
  1739. int damage = Strength;
  1740. Take_Damage(damage, 0, WARHEAD_AP, NULL, true);
  1741. return;
  1742. }
  1743. }
  1744. /*
  1745. ** Destroy any crushable wall that is driven over by a tracked vehicle.
  1746. */
  1747. CellClass * cellptr = &Map[cell];
  1748. if (Class->IsCrusher && cellptr->Overlay != OVERLAY_NONE) {
  1749. // if (Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) {
  1750. OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  1751. if (optr->IsCrushable) {
  1752. if (optr->Type == OVERLAY_SANDBAG_WALL) {
  1753. Sound_Effect(VOC_SANDBAG, Center_Coord());
  1754. } else {
  1755. Sound_Effect(VOC_WALLKILL2, Center_Coord());
  1756. }
  1757. cellptr->Reduce_Wall(-1);
  1758. }
  1759. }
  1760. /*
  1761. ** Check to see if crushing of any unfortunate infantry is warranted.
  1762. */
  1763. Overrun_Square(Coord_Cell(Coord), false);
  1764. if (!IsActive) {
  1765. BEnd(BENCH_PCP);
  1766. return;
  1767. }
  1768. DriveClass::Per_Cell_Process(why);
  1769. BEnd(BENCH_PCP);
  1770. }
  1771. /***********************************************************************************************
  1772. * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. *
  1773. * *
  1774. * This routine will calculate the shape number for this unit. The shape number is used *
  1775. * for the body of the unit. *
  1776. * *
  1777. * INPUT: none *
  1778. * *
  1779. * OUTPUT: Returns with the shape number to be used for this unit. *
  1780. * *
  1781. * WARNINGS: none *
  1782. * *
  1783. * HISTORY: *
  1784. * 07/29/1996 JLB : Created. *
  1785. *=============================================================================================*/
  1786. int UnitClass::Shape_Number(void) const
  1787. {
  1788. assert(Units.ID(this) == ID);
  1789. assert(IsActive);
  1790. int shapenum; // Working shape number.
  1791. int facing = Dir_To_32(PrimaryFacing);
  1792. int tfacing = Dir_To_32(SecondaryFacing);
  1793. DirType rotation = DIR_N;
  1794. #ifdef FIXIT_ANTS
  1795. /*
  1796. ** This handles the ant case.
  1797. */
  1798. if (Class->Rotation == 8) {
  1799. /*
  1800. ** The starting frame is based on the facing of the unit.
  1801. */
  1802. shapenum = ((UnitClass::BodyShape[facing]+2)/4) & 0x07;
  1803. /*
  1804. ** If the unit is driving, then it has an animation adjustment to the frame number.
  1805. */
  1806. if (IsDriving) {
  1807. shapenum = 8 + (shapenum * 8) + ((::Frame+ID)/2) % 8;
  1808. } else {
  1809. /*
  1810. ** If in combat, then do combat anims.
  1811. */
  1812. if (Arm > 0) {
  1813. shapenum = 8 + 64 + (shapenum * 4) + ((::Frame+ID)/2) % 4;
  1814. }
  1815. }
  1816. } else {
  1817. #endif
  1818. /*
  1819. ** Fetch the harvesting animation stage as appropriate.
  1820. */
  1821. if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) {
  1822. // static char _hstage[] = {0, 1, 2, 3, 4, 5, 6, 7, 0};
  1823. unsigned stage = Fetch_Stage();
  1824. if (stage >= ARRAY_SIZE(Class->Harvester_Load_List)) stage = ARRAY_SIZE(Class->Harvester_Load_List)-1;
  1825. shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*Class->Harvester_Load_Count)+Class->Harvester_Load_List[stage];
  1826. } else {
  1827. /*
  1828. ** If the harvester's dumping a load of ore, show that animation
  1829. */
  1830. if (IsDumping) {
  1831. unsigned stage = Fetch_Stage();
  1832. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1833. if (*this == UNIT_MAD) {
  1834. if (stage >= 8) {
  1835. stage = 7;
  1836. }
  1837. shapenum = 32 + stage + (UnitClass::BodyShape[facing]/4)*8;
  1838. } else {
  1839. if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1;
  1840. shapenum = Class->Harvester_Dump_List[stage]+96;
  1841. }
  1842. #else
  1843. if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1;
  1844. shapenum = Class->Harvester_Dump_List[stage]+96;
  1845. #endif
  1846. } else {
  1847. shapenum = UnitClass::BodyShape[facing];
  1848. if (Class->IsAnimating) {
  1849. shapenum = Fetch_Stage();
  1850. }
  1851. /*
  1852. ** Door opening and closing animation must be handled carefully. There are only
  1853. ** certain directions where this door animation will work.
  1854. */
  1855. if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) {
  1856. if (PrimaryFacing == DIR_NE) {
  1857. shapenum = 32;
  1858. } else {
  1859. if (PrimaryFacing == DIR_NW) {
  1860. shapenum = 35;
  1861. }
  1862. }
  1863. shapenum += Door_Stage();
  1864. }
  1865. }
  1866. }
  1867. #ifdef FIXIT_ANTS
  1868. }
  1869. #endif
  1870. /*
  1871. ** The body of the V2 launcher indicates whether it is loaded with a missile
  1872. ** or not.
  1873. */
  1874. if (*this == UNIT_V2_LAUNCHER) {
  1875. if (Ammo == 0) shapenum += 32;
  1876. }
  1877. return(shapenum);
  1878. }
  1879. /***********************************************************************************************
  1880. * UnitClass::Draw_It -- Draws a unit object. *
  1881. * *
  1882. * This routine is the one that actually draws a unit object. It displays the unit *
  1883. * according to its current state flags and centered at the location specified. *
  1884. * *
  1885. * INPUT: x,y -- The X and Y coordinate of where to draw the unit. *
  1886. * *
  1887. * window -- The clipping window to use. *
  1888. * *
  1889. * OUTPUT: none *
  1890. * *
  1891. * WARNINGS: none *
  1892. * *
  1893. * HISTORY: *
  1894. * 06/20/1994 JLB : Created. *
  1895. * 06/27/1994 JLB : Takes a window parameter. *
  1896. * 08/15/1994 JLB : Removed infantry support. *
  1897. * 01/07/1995 JLB : Harvester animation support. *
  1898. * 07/08/1995 JLB : Uses general purpose draw routine. *
  1899. *=============================================================================================*/
  1900. void UnitClass::Draw_It(int x, int y, WindowNumberType window) const
  1901. {
  1902. assert(Units.ID(this) == ID);
  1903. assert(IsActive);
  1904. int shapenum; // Working shape number.
  1905. void const * shapefile; // Working shape file pointer.
  1906. int facing = Dir_To_32(PrimaryFacing);
  1907. int tfacing = Dir_To_32(SecondaryFacing);
  1908. DirType rotation = DIR_N;
  1909. int scale = 0x0100;
  1910. /*
  1911. ** Verify the legality of the unit class.
  1912. */
  1913. shapefile = Get_Image_Data();
  1914. if (shapefile == NULL) return;
  1915. /*
  1916. ** If drawing of this unit is not explicitly prohibited, then proceed
  1917. ** with the render process.
  1918. */
  1919. const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL);
  1920. if (!is_hidden) {
  1921. shapenum = Shape_Number();
  1922. /*
  1923. ** The artillery unit should have its entire body recoil when it fires.
  1924. */
  1925. if (*this == UNIT_ARTY && IsInRecoilState) {
  1926. Recoil_Adjust(PrimaryFacing.Current(), x, y);
  1927. }
  1928. /*
  1929. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  1930. */
  1931. Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale);
  1932. /*
  1933. ** If there is a rotating radar dish, draw it now.
  1934. */
  1935. if (Class->IsRadarEquipped) {
  1936. if (*this == UNIT_MGG) {
  1937. int x2 = x, y2 = y;
  1938. shapenum = 32 + (Frame & 7);
  1939. Class->Turret_Adjust(PrimaryFacing, x2, y2);
  1940. Techno_Draw_Object(shapefile, shapenum, x2, y2, window);
  1941. } else {
  1942. //#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  1943. // if (*this == UNIT_PHASE) {
  1944. // shapenum = 38 + (Frame & 7);
  1945. // } else {
  1946. // shapenum = 32 + (Frame % 32);
  1947. // }
  1948. //#else
  1949. shapenum = 32 + (Frame % 32);
  1950. //#endif
  1951. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1952. if (*this == UNIT_TESLATANK) {
  1953. Techno_Draw_Object(shapefile, shapenum, x, y, window);
  1954. } else {
  1955. Techno_Draw_Object(shapefile, shapenum, x, y-5, window);
  1956. }
  1957. #else
  1958. Techno_Draw_Object(shapefile, shapenum, x, y-5, window);
  1959. #endif
  1960. }
  1961. }
  1962. /*
  1963. ** If there is a turret, then it must be rendered as well. This may include
  1964. ** firing animation if required.
  1965. */
  1966. if (/*!Class->IsChunkyShape &&*/ Class->IsTurretEquipped) {
  1967. int xx = x;
  1968. int yy = y;
  1969. /*
  1970. ** Determine which turret shape to use. This depends on if there
  1971. ** is any firing animation in progress.
  1972. */
  1973. shapenum = TechnoClass::BodyShape[tfacing]+32;
  1974. #ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  1975. if (*this == UNIT_PHASE) {
  1976. shapenum += 6;
  1977. }
  1978. #endif
  1979. /*
  1980. ** A recoiling turret moves "backward" one pixel.
  1981. */
  1982. if (IsInRecoilState) {
  1983. Recoil_Adjust(SecondaryFacing, xx, yy);
  1984. }
  1985. Class->Turret_Adjust(PrimaryFacing, xx, yy);
  1986. /*
  1987. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  1988. */
  1989. Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
  1990. }
  1991. }
  1992. /*
  1993. ** If this unit is carrying the flag, then draw that on top of everything else.
  1994. */
  1995. if (Flagged != HOUSE_NONE) {
  1996. shapefile = MFCD::Retrieve("FLAGFLY.SHP");
  1997. int flag_x = x + (ICON_PIXEL_W / 2) - 2;
  1998. int flag_y = y + (3 * ICON_PIXEL_H / 4) - Get_Build_Frame_Height(shapefile);
  1999. CC_Draw_Shape(this, "FLAGFLY", shapefile, Frame % 14, flag_x, flag_y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged);
  2000. }
  2001. DriveClass::Draw_It(x, y, window);
  2002. }
  2003. /***********************************************************************************************
  2004. * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. *
  2005. * *
  2006. * This routine is used to move a harvester to a place where it can load up with *
  2007. * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets *
  2008. * the navigation computer toward the nearest Tiberium and lets the unit head there *
  2009. * automatically. *
  2010. * *
  2011. * INPUT: center -- Reference to the center of the radius scan. *
  2012. * *
  2013. * x,y -- Relative offset from the center cell to perform the check upon. *
  2014. * *
  2015. * OUTPUT: int; Amount of Tiberium at this location. *
  2016. * *
  2017. * WARNINGS: none *
  2018. * *
  2019. * HISTORY: *
  2020. * 07/18/1994 JLB : Created. *
  2021. *=============================================================================================*/
  2022. int UnitClass::Tiberium_Check(CELL & center, int x, int y)
  2023. {
  2024. assert(Units.ID(this) == ID);
  2025. assert(IsActive);
  2026. /*
  2027. ** If the specified offset from the origin will cause it
  2028. ** to spill past the map edge, then abort this cell check.
  2029. */
  2030. if (Cell_X(center)+x < Map.MapCellX) return(0);
  2031. if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(0);
  2032. if (Cell_Y(center)+y < Map.MapCellY) return(0);
  2033. if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(0);
  2034. center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y);
  2035. if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].Is_Mapped(PlayerPtr)))) {
  2036. if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(0);
  2037. if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) {
  2038. int value = 0;
  2039. switch (Map[center].Overlay) {
  2040. case OVERLAY_GOLD1:
  2041. case OVERLAY_GOLD2:
  2042. case OVERLAY_GOLD3:
  2043. case OVERLAY_GOLD4:
  2044. value = Rule.GoldValue;
  2045. break;
  2046. case OVERLAY_GEMS1:
  2047. case OVERLAY_GEMS2:
  2048. case OVERLAY_GEMS3:
  2049. case OVERLAY_GEMS4:
  2050. value = Rule.GemValue*4;
  2051. break;
  2052. }
  2053. return((Map[center].OverlayData+1)*value);
  2054. }
  2055. }
  2056. return(0);
  2057. }
  2058. /***********************************************************************************************
  2059. * UnitClass::Goto_Tiberium -- Searches for and heads toward tiberium. *
  2060. * *
  2061. * This routine will cause the unit to search for and head toward nearby Tiberium. When *
  2062. * the Tiberium is reached, then this routine should not be called again until such time *
  2063. * as additional harvesting is required. When this routine returns false, then it should *
  2064. * be called again until such time as it returns true. *
  2065. * *
  2066. * INPUT: rad = size of ring to search *
  2067. * *
  2068. * OUTPUT: Has the unit reached Tiberium and harvesting should begin? *
  2069. * *
  2070. * WARNINGS: none *
  2071. * *
  2072. * HISTORY: *
  2073. * 09/22/1995 JLB : Created. *
  2074. *=============================================================================================*/
  2075. bool UnitClass::Goto_Tiberium(int rad)
  2076. {
  2077. assert(Units.ID(this) == ID);
  2078. assert(IsActive);
  2079. if (!Target_Legal(NavCom)) {
  2080. CELL center = Coord_Cell(Center_Coord());
  2081. if (Map[center].Land_Type() == LAND_TIBERIUM) {
  2082. return(true);
  2083. } else {
  2084. /*
  2085. ** Perform a ring search outward from the center.
  2086. */
  2087. for (int radius = 1; radius < rad; radius++) {
  2088. CELL cell = center;
  2089. CELL bestcell = 0;
  2090. int tiberium = 0;
  2091. int besttiberium = 0;
  2092. for (int x = -radius; x <= radius; x++) {
  2093. /*
  2094. ** Randomize the corners.
  2095. */
  2096. int corner[2];
  2097. int corners[4][2] = {
  2098. {x, -radius},
  2099. {x, +radius},
  2100. {-radius, x},
  2101. {+radius, x}
  2102. };
  2103. for (int i = 0; i < 3; i++) {
  2104. int j = i + rand() / (RAND_MAX / (4 - i) + 1);
  2105. memcpy(&corner, &corners[j], sizeof(corner));
  2106. memcpy(&corners[j], &corners[i], sizeof(corner));
  2107. memcpy(&corners[i], corner, sizeof(corner));
  2108. }
  2109. cell = center;
  2110. tiberium = Tiberium_Check(cell, corners[0][0], corners[0][1]);
  2111. if (tiberium > besttiberium) {
  2112. bestcell = cell;
  2113. besttiberium = tiberium;
  2114. }
  2115. cell = center;
  2116. tiberium = Tiberium_Check(cell, corners[1][0], corners[1][1]);
  2117. if (tiberium > besttiberium) {
  2118. bestcell = cell;
  2119. besttiberium = tiberium;
  2120. }
  2121. cell = center;
  2122. tiberium = Tiberium_Check(cell, corners[2][0], corners[2][1]);
  2123. if (tiberium > besttiberium) {
  2124. bestcell = cell;
  2125. besttiberium = tiberium;
  2126. }
  2127. cell = center;
  2128. tiberium = Tiberium_Check(cell, corners[3][0], corners[3][1]);
  2129. if (tiberium > besttiberium) {
  2130. bestcell = cell;
  2131. besttiberium = tiberium;
  2132. }
  2133. }
  2134. if (bestcell) {
  2135. Assign_Destination(::As_Target(bestcell));
  2136. return(false);
  2137. }
  2138. }
  2139. }
  2140. }
  2141. return(false);
  2142. }
  2143. /***********************************************************************************************
  2144. * UnitClass::Harvesting -- Harvests tiberium at the current location. *
  2145. * *
  2146. * This routine is used to by the harvester to harvest Tiberium at the current location. *
  2147. * When harvesting is complete, this routine will return true. *
  2148. * *
  2149. * INPUT: none *
  2150. * *
  2151. * OUTPUT: bool; Is harvesting complete? *
  2152. * *
  2153. * WARNINGS: none *
  2154. * *
  2155. * HISTORY: *
  2156. * 07/18/1994 JLB : Created. *
  2157. *=============================================================================================*/
  2158. bool UnitClass::Harvesting(void)
  2159. {
  2160. assert(Units.ID(this) == ID);
  2161. assert(IsActive);
  2162. CELL cell = Coord_Cell(Coord);
  2163. CellClass * ptr = &Map[cell];
  2164. /*
  2165. ** Keep waiting if still heading toward a spot to harvest.
  2166. */
  2167. if (Target_Legal(NavCom)) return(true);
  2168. if (Tiberium_Load() < 1 && ptr->Land_Type() == LAND_TIBERIUM) {
  2169. /*
  2170. ** Lift some Tiberium from the ground. Try to lift a complete
  2171. ** "level" of Tiberium. A level happens to be 6 steps. If there
  2172. ** is a partial level, then lift that instead. Never lift more
  2173. ** than the harvester can carry.
  2174. */
  2175. // int reducer = (ptr->OverlayData % 6) + 1;
  2176. int reducer = 1;
  2177. OverlayType overlay = ptr->Overlay;
  2178. reducer = ptr->Reduce_Tiberium(min(reducer, Rule.BailCount-Tiberium));
  2179. Tiberium += reducer;
  2180. switch (overlay) {
  2181. case OVERLAY_GOLD1:
  2182. case OVERLAY_GOLD2:
  2183. case OVERLAY_GOLD3:
  2184. case OVERLAY_GOLD4:
  2185. Gold += reducer;
  2186. break;
  2187. case OVERLAY_GEMS1:
  2188. case OVERLAY_GEMS2:
  2189. case OVERLAY_GEMS3:
  2190. case OVERLAY_GEMS4:
  2191. Gems += reducer;
  2192. if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
  2193. if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
  2194. if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
  2195. break;
  2196. default:
  2197. break;
  2198. }
  2199. Set_Stage(0);
  2200. Set_Rate(Rule.OreDumpRate);
  2201. } else {
  2202. /*
  2203. ** If the harvester is stopped on a non Tiberium field and the harvester
  2204. ** isn't loaded with Tiberium, then no further action can be performed
  2205. ** by this logic routine. Bail with a failure and thus cause a branch to
  2206. ** a better suited logic processor.
  2207. */
  2208. Set_Stage(0);
  2209. Set_Rate(0);
  2210. return(false);
  2211. }
  2212. return(true);
  2213. }
  2214. /***********************************************************************************************
  2215. * UnitClass::Mission_Unload -- Handles unloading cargo. *
  2216. * *
  2217. * This is the AI control sequence for when a transport desires to unload its cargo and *
  2218. * then exit the map. *
  2219. * *
  2220. * INPUT: none *
  2221. * *
  2222. * OUTPUT: Returns with the delay before calling this routine again. *
  2223. * *
  2224. * WARNINGS: none *
  2225. * *
  2226. * HISTORY: *
  2227. * 07/18/1994 JLB : Created. *
  2228. *=============================================================================================*/
  2229. int UnitClass::Mission_Unload(void)
  2230. {
  2231. assert(Units.ID(this) == ID);
  2232. assert(IsActive);
  2233. enum {
  2234. INITIAL_CHECK,
  2235. MANEUVERING,
  2236. OPENING_DOOR,
  2237. UNLOADING,
  2238. CLOSING_DOOR
  2239. };
  2240. DirType dir;
  2241. CELL cell;
  2242. switch (Class->Type) {
  2243. case UNIT_HARVESTER:
  2244. if (PrimaryFacing != DIR_W) {
  2245. if (!IsRotating) {
  2246. Do_Turn(DIR_W);
  2247. }
  2248. return(5);
  2249. }
  2250. if (!IsDumping) {
  2251. IsDumping = true;
  2252. Set_Stage(0);
  2253. Set_Rate(Rule.OreDumpRate);
  2254. break;
  2255. }
  2256. if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Dump_List)-1) break;
  2257. IsDumping = false;
  2258. if (Tiberium) {
  2259. Tiberium = 0;
  2260. int credits = Credit_Load();
  2261. House->Harvested(credits);
  2262. Tiberium = Gold = Gems = 0;
  2263. }
  2264. Transmit_Message(RADIO_OVER_OUT);
  2265. Assign_Mission(MISSION_HARVEST);
  2266. break;
  2267. case UNIT_TRUCK:
  2268. switch (Status) {
  2269. case INITIAL_CHECK:
  2270. dir = Desired_Load_Dir(NULL, cell);
  2271. if (How_Many() && cell != 0) {
  2272. Do_Turn(dir);
  2273. Status = MANEUVERING;
  2274. return(1);
  2275. } else {
  2276. Assign_Mission(MISSION_GUARD);
  2277. }
  2278. break;
  2279. case MANEUVERING:
  2280. if (!IsRotating) {
  2281. Status = UNLOADING;
  2282. return(1);
  2283. }
  2284. break;
  2285. case UNLOADING:
  2286. if (How_Many()) {
  2287. FootClass * passenger = Detach_Object();
  2288. if (passenger != NULL) {
  2289. DirType toface = DIR_S + PrimaryFacing;
  2290. bool placed = false;
  2291. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  2292. DirType newface = toface + Facing_Dir(face);
  2293. CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  2294. if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
  2295. ScenarioInit++;
  2296. passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface);
  2297. ScenarioInit--;
  2298. passenger->Assign_Mission(MISSION_MOVE);
  2299. passenger->Assign_Destination(::As_Target(newcell));
  2300. placed = true;
  2301. break;
  2302. }
  2303. }
  2304. /*
  2305. ** If the attached unit could NOT be deployed, then re-attach
  2306. ** it and then bail out of this deploy process.
  2307. */
  2308. if (!placed) {
  2309. Attach(passenger);
  2310. Status = CLOSING_DOOR;
  2311. }
  2312. else {
  2313. passenger->Look(false);
  2314. }
  2315. }
  2316. } else {
  2317. Status = CLOSING_DOOR;
  2318. }
  2319. break;
  2320. /*
  2321. ** Close APC door in preparation for normal operation.
  2322. */
  2323. case CLOSING_DOOR:
  2324. Assign_Mission(MISSION_GUARD);
  2325. break;
  2326. }
  2327. break;
  2328. case UNIT_APC:
  2329. #ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  2330. case UNIT_PHASE:
  2331. #endif
  2332. switch (Status) {
  2333. case INITIAL_CHECK:
  2334. dir = Desired_Load_Dir(NULL, cell);
  2335. if (How_Many() && cell != 0) {
  2336. Do_Turn(dir);
  2337. Status = MANEUVERING;
  2338. return(1);
  2339. } else {
  2340. Assign_Mission(MISSION_GUARD);
  2341. }
  2342. break;
  2343. case MANEUVERING:
  2344. if (!IsRotating) {
  2345. APC_Open_Door();
  2346. if (Is_Door_Opening()) {
  2347. Status = OPENING_DOOR;
  2348. return(1);
  2349. }
  2350. }
  2351. break;
  2352. case OPENING_DOOR:
  2353. if (Is_Door_Open()) {
  2354. Status = UNLOADING;
  2355. return(1);
  2356. } else {
  2357. if (!Is_Door_Opening()) {
  2358. Status = INITIAL_CHECK;
  2359. }
  2360. }
  2361. break;
  2362. case UNLOADING:
  2363. if (How_Many()) {
  2364. FootClass * passenger = Detach_Object();
  2365. if (passenger != NULL) {
  2366. DirType toface = DIR_S + PrimaryFacing;
  2367. bool placed = false;
  2368. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  2369. DirType newface = toface + Facing_Dir(face);
  2370. CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  2371. if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
  2372. ScenarioInit++;
  2373. passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface);
  2374. ScenarioInit--;
  2375. passenger->Assign_Mission(MISSION_MOVE);
  2376. passenger->Assign_Destination(::As_Target(newcell));
  2377. placed = true;
  2378. break;
  2379. }
  2380. }
  2381. /*
  2382. ** If the attached unit could NOT be deployed, then re-attach
  2383. ** it and then bail out of this deploy process.
  2384. */
  2385. if (!placed) {
  2386. Attach(passenger);
  2387. Status = CLOSING_DOOR;
  2388. }
  2389. else {
  2390. passenger->Look(false);
  2391. }
  2392. }
  2393. } else {
  2394. Status = CLOSING_DOOR;
  2395. }
  2396. break;
  2397. /*
  2398. ** Close APC door in preparation for normal operation.
  2399. */
  2400. case CLOSING_DOOR:
  2401. if (Is_Door_Open()) {
  2402. APC_Close_Door();
  2403. }
  2404. if (Is_Door_Closed()) {
  2405. Assign_Mission(MISSION_GUARD);
  2406. }
  2407. break;
  2408. }
  2409. break;
  2410. case UNIT_MCV:
  2411. switch (Status) {
  2412. case 0:
  2413. Path[0] = FACING_NONE;
  2414. Status = 1;
  2415. break;
  2416. case 1:
  2417. if (!IsDriving) {
  2418. Try_To_Deploy();
  2419. if (IsActive) {
  2420. if (IsDeploying) {
  2421. Status = 2;
  2422. } else {
  2423. if (!House->IsHuman && Session.Type != GAME_NORMAL) {
  2424. Assign_Mission(MISSION_HUNT);
  2425. } else {
  2426. Assign_Mission(MISSION_GUARD);
  2427. }
  2428. }
  2429. }
  2430. }
  2431. break;
  2432. case 2:
  2433. if (!IsDeploying) {
  2434. Assign_Mission(MISSION_GUARD);
  2435. }
  2436. break;
  2437. }
  2438. return(1);
  2439. case UNIT_MINELAYER:
  2440. switch (Status) {
  2441. case INITIAL_CHECK:
  2442. dir = DIR_NE;
  2443. if (Ammo > 0) {
  2444. Do_Turn(dir);
  2445. Status = MANEUVERING;
  2446. return(1);
  2447. } else {
  2448. Assign_Mission(MISSION_GUARD);
  2449. }
  2450. break;
  2451. case MANEUVERING:
  2452. if (!IsRotating) {
  2453. APC_Open_Door();
  2454. if (Is_Door_Opening()) {
  2455. Status = OPENING_DOOR;
  2456. return(1);
  2457. }
  2458. }
  2459. break;
  2460. case OPENING_DOOR:
  2461. if (Is_Door_Open()) {
  2462. Status = UNLOADING;
  2463. return(1);
  2464. } else {
  2465. if (!Is_Door_Opening()) {
  2466. Status = INITIAL_CHECK;
  2467. }
  2468. }
  2469. break;
  2470. case UNLOADING:
  2471. if (Ammo > 0) {
  2472. if (!Map[Center_Coord()].Cell_Building()) {
  2473. Mark(MARK_UP);
  2474. BuildingClass * building = new BuildingClass((House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE || House->ActLike == HOUSE_BAD) ? STRUCT_APMINE : STRUCT_AVMINE, House->Class->House);
  2475. if (building != NULL) {
  2476. ScenarioInit = true;
  2477. if (building->Unlimbo(Coord)) {
  2478. Sound_Effect(VOC_MINELAY1, Coord);
  2479. ScenarioInit = false;
  2480. building->Revealed(House);
  2481. Ammo--;
  2482. }
  2483. ScenarioInit = false;
  2484. }
  2485. Status = CLOSING_DOOR;
  2486. Mark(MARK_DOWN);
  2487. } else {
  2488. Status = CLOSING_DOOR;
  2489. }
  2490. } else {
  2491. Status = CLOSING_DOOR;
  2492. }
  2493. break;
  2494. /*
  2495. ** Close APC door in preparation for normal operation.
  2496. */
  2497. case CLOSING_DOOR:
  2498. if (Is_Door_Open()) {
  2499. APC_Close_Door();
  2500. }
  2501. if (Is_Door_Closed()) {
  2502. Assign_Mission(MISSION_GUARD);
  2503. }
  2504. break;
  2505. }
  2506. break;
  2507. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2508. case UNIT_MAD:
  2509. if (!Gems && !IsDumping) {
  2510. Gems = 1;
  2511. Gold = 0;
  2512. Arm = QuakeDelay * House->ROFBias;
  2513. #ifdef ENGLISH
  2514. Speak(VOX_MADTANK_DEPLOYED); // this voice only exists in English
  2515. #else
  2516. Sound_Effect(VOC_BUZZY1,Center_Coord());
  2517. #endif
  2518. Set_Stage(0);
  2519. Set_Rate(Rule.OreDumpRate*2);
  2520. IsDumping = true;
  2521. #if 1
  2522. InfantryClass *crew = new InfantryClass(INFANTRY_C1, House->Class->House);
  2523. if (crew != NULL) crew->IsTechnician = true;
  2524. if (crew != NULL) {
  2525. DirType toface = DIR_S + PrimaryFacing;
  2526. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  2527. DirType newface = toface + Facing_Dir(face);
  2528. CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  2529. if (crew->Can_Enter_Cell(newcell) == MOVE_OK) {
  2530. ScenarioInit++;
  2531. crew->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface);
  2532. ScenarioInit--;
  2533. crew->Assign_Mission(MISSION_MOVE);
  2534. crew->Assign_Destination(::As_Target(newcell));
  2535. break;
  2536. }
  2537. }
  2538. }
  2539. #endif
  2540. }
  2541. if ( (Arm && !Gold) || IronCurtainCountDown) {
  2542. Set_Stage(Fetch_Stage() & 1);
  2543. return(1);
  2544. }
  2545. if (!Gold) {
  2546. Sound_Effect(VOC_MAD_CHARGE, Center_Coord());
  2547. Set_Stage(0);
  2548. Gold = 1;
  2549. return(1);
  2550. }
  2551. if (Fetch_Stage() < 7) {
  2552. return(1);
  2553. }
  2554. IsDumping = false;
  2555. Sound_Effect(VOC_MAD_EXPLODE, Center_Coord());
  2556. Strength = 1; // assure destruction
  2557. PendingTimeQuake = true; // trigger a time quake
  2558. TimeQuakeCenter = ::As_Target(Center_Coord());
  2559. break;
  2560. case UNIT_CHRONOTANK:
  2561. if (IsOwnedByPlayer) {
  2562. Map.IsTargettingMode = SPC_CHRONO2;
  2563. HouseClass* old_player_ptr = PlayerPtr;
  2564. Logic_Switch_Player_Context(this);
  2565. Unselect_All();
  2566. On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode);
  2567. Logic_Switch_Player_Context(old_player_ptr);
  2568. }
  2569. House->UnitToTeleport = As_Target();
  2570. Assign_Mission(MISSION_GUARD);
  2571. break;
  2572. #endif
  2573. default:
  2574. break;
  2575. }
  2576. return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
  2577. }
  2578. /***********************************************************************************************
  2579. * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. *
  2580. * *
  2581. * This is the AI process used by harvesters when they are doing their harvesting action. *
  2582. * This entails searching for nearby Tiberium, heading there, harvesting, and then *
  2583. * returning to a refinery for unloading. *
  2584. * *
  2585. * INPUT: none *
  2586. * *
  2587. * OUTPUT: Returns with the delay before calling this routine again. *
  2588. * *
  2589. * WARNINGS: none *
  2590. * *
  2591. * HISTORY: *
  2592. * 07/18/1994 JLB : Created. *
  2593. * 06/21/1995 JLB : Force guard mode if no Tiberium found. *
  2594. * 09/28/1995 JLB : Aborts harvesting if there are no more refineries. *
  2595. *=============================================================================================*/
  2596. int UnitClass::Mission_Harvest(void)
  2597. {
  2598. assert(Units.ID(this) == ID);
  2599. assert(IsActive);
  2600. enum {
  2601. LOOKING,
  2602. HARVESTING,
  2603. FINDHOME,
  2604. HEADINGHOME,
  2605. GOINGTOIDLE,
  2606. };
  2607. /*
  2608. ** A non-harvesting type unit will just sit still if it is given the harvest mission. This
  2609. ** allows combat units to act "brain dead".
  2610. */
  2611. if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30);
  2612. /*
  2613. ** If there are no more refineries, then drop into guard mode.
  2614. */
  2615. if (!(House->ActiveBScan & STRUCTF_REFINERY)) {
  2616. Assign_Mission(MISSION_GUARD);
  2617. return(1);
  2618. }
  2619. switch (Status) {
  2620. /*
  2621. ** Go and find a Tiberium field to harvest.
  2622. */
  2623. case LOOKING:
  2624. /*
  2625. ** Slightly hacky; if TarCom is set then skip to finding home state.
  2626. */
  2627. if (Target_Legal(TarCom)) {
  2628. Assign_Target(TARGET_NONE);
  2629. Status = FINDHOME;
  2630. return(1);
  2631. }
  2632. /*
  2633. ** Look for ore where we last found some - mine the same patch
  2634. */
  2635. if (Target_Legal(ArchiveTarget)) {
  2636. Assign_Destination(ArchiveTarget);
  2637. ArchiveTarget = 0;
  2638. }
  2639. IsHarvesting = false;
  2640. if (Goto_Tiberium(Rule.TiberiumLongScan / CELL_LEPTON_W)) {
  2641. IsHarvesting = true;
  2642. Set_Rate(2);
  2643. Set_Stage(0);
  2644. Status = HARVESTING;
  2645. ArchiveTarget = ::As_Target(Coord_Cell(Coord));
  2646. return(1);
  2647. } else {
  2648. /*
  2649. ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then
  2650. ** force it to go into guard mode. This will prevent the harvester from repeatedly
  2651. ** searching for Tiberium.
  2652. */
  2653. if (!Target_Legal(NavCom)) {
  2654. /*
  2655. ** If the archive target is legal, then head there since it is presumed
  2656. ** that the archive target points to the last place it harvested at. This might
  2657. ** solve the case where the harvester gets stuck and can't find Tiberium just because
  2658. ** it is greater than 32 squares away.
  2659. */
  2660. if (Target_Legal(ArchiveTarget)) {
  2661. Assign_Destination(ArchiveTarget);
  2662. } else {
  2663. Status = GOINGTOIDLE;
  2664. IsUseless = true;
  2665. House->IsTiberiumShort = true;
  2666. return(TICKS_PER_SECOND*7);
  2667. }
  2668. } else {
  2669. IsUseless = false;
  2670. }
  2671. }
  2672. break;
  2673. /*
  2674. ** Harvest at current location until full or Tiberium exhausted.
  2675. */
  2676. case HARVESTING:
  2677. // if (Fetch_Stage() > ARRAY_SIZE(Class->Harvester_Load_List)) {
  2678. // Set_Stage(0);
  2679. // }
  2680. if (Fetch_Rate() == 0) {
  2681. Set_Stage(0);
  2682. Set_Rate(Rule.OreDumpRate);
  2683. }
  2684. if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Load_List)) return(1);
  2685. if (!Harvesting()) {
  2686. IsHarvesting = false;
  2687. if (Tiberium_Load() == 1) {
  2688. Status = FINDHOME;
  2689. } else {
  2690. if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom)) {
  2691. ArchiveTarget = TARGET_NONE;
  2692. Status = FINDHOME;
  2693. } else {
  2694. Status = HARVESTING;
  2695. IsHarvesting = true;
  2696. }
  2697. }
  2698. return(1);
  2699. } else if (!Target_Legal(NavCom) && ArchiveTarget == TARGET_NONE) {
  2700. ArchiveTarget = ::As_Target(Coord_Cell(Coord));
  2701. }
  2702. return(1);
  2703. // return(TICKS_PER_SECOND*Rule.OreDumpRate);
  2704. /*
  2705. ** Find and head to refinery.
  2706. */
  2707. case FINDHOME:
  2708. if (!Target_Legal(NavCom)) {
  2709. /*
  2710. ** Find best refinery.
  2711. */
  2712. BuildingClass * nearest = Find_Best_Refinery();
  2713. /*
  2714. ** Since the refinery said it was ok to load, establish radio
  2715. ** contact with the refinery and then await docking orders.
  2716. */
  2717. if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
  2718. Status = HEADINGHOME;
  2719. if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
  2720. Speak(VOX_NEED_MO_CAPACITY);
  2721. }
  2722. } else {
  2723. ScenarioInit++;
  2724. nearest = Find_Best_Refinery();
  2725. ScenarioInit--;
  2726. if (nearest != NULL) {
  2727. Assign_Destination(::As_Target(Nearby_Location(nearest)));
  2728. }
  2729. }
  2730. }
  2731. break;
  2732. /*
  2733. ** In communication with refinery so that it will successfully dock and
  2734. ** unload. If, for some reason, radio contact was lost, then hunt for
  2735. ** another refinery to unload at.
  2736. */
  2737. case HEADINGHOME:
  2738. Assign_Mission(MISSION_ENTER);
  2739. return(1);
  2740. /*
  2741. ** The harvester has nothing to do. There is no Tiberium nearby and
  2742. ** no where to go.
  2743. */
  2744. case GOINGTOIDLE:
  2745. if (IsUseless) {
  2746. if (House->ActiveBScan & STRUCTF_REPAIR) {
  2747. Assign_Mission(MISSION_REPAIR);
  2748. } else {
  2749. Assign_Mission(MISSION_HUNT);
  2750. }
  2751. }
  2752. Assign_Mission(MISSION_GUARD);
  2753. break;
  2754. }
  2755. return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
  2756. }
  2757. /***********************************************************************************************
  2758. * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. *
  2759. * *
  2760. * Computer controlled units must be intelligent enough to find enemies as well as to *
  2761. * attack them. This AI process will handle both the simple attack process as well as the *
  2762. * scanning for enemy units to attack. *
  2763. * *
  2764. * INPUT: none *
  2765. * *
  2766. * OUTPUT: Returns with the delay before calling this routine again. *
  2767. * *
  2768. * WARNINGS: none *
  2769. * *
  2770. * HISTORY: *
  2771. * 07/18/1994 JLB : Created. *
  2772. *=============================================================================================*/
  2773. int UnitClass::Mission_Hunt(void)
  2774. {
  2775. assert(Units.ID(this) == ID);
  2776. assert(IsActive);
  2777. if (*this == UNIT_MCV) {
  2778. enum {
  2779. FIND_SPOT,
  2780. WAITING
  2781. };
  2782. switch (Status) {
  2783. /*
  2784. ** This stage handles locating a convenient spot, rotating to face the correct
  2785. ** direction and then commencing the deployment operation.
  2786. */
  2787. case FIND_SPOT:
  2788. if (Goto_Clear_Spot()) {
  2789. if (Try_To_Deploy()) {
  2790. Status = WAITING;
  2791. }
  2792. }
  2793. break;
  2794. /*
  2795. ** This stage watchdogs the deployment operation and if for some reason, the deployment
  2796. ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for
  2797. ** a convenient spot to deploy.
  2798. */
  2799. case WAITING:
  2800. if (!IsDeploying) {
  2801. Status = FIND_SPOT;
  2802. }
  2803. break;
  2804. }
  2805. } else {
  2806. return(DriveClass::Mission_Hunt());
  2807. }
  2808. return(MissionControl[Mission].Normal_Delay()+Random_Pick(0, 2));
  2809. }
  2810. /***********************************************************************************************
  2811. * UnitClass::Overlap_List -- Determines overlap list for units. *
  2812. * *
  2813. * The unit overlap list is used to keep track of which cells are to *
  2814. * be marked for redraw. This is critical in order to keep the units *
  2815. * displayed correctly. *
  2816. * *
  2817. * INPUT: none *
  2818. * *
  2819. * OUTPUT: Returns with the overlap list pointer for the unit at its *
  2820. * present position. *
  2821. * *
  2822. * WARNINGS: none *
  2823. * *
  2824. * HISTORY: *
  2825. * 05/26/1994 JLB : Created. *
  2826. * 06/19/1994 JLB : Uses Coord_Spillable_List function. *
  2827. *=============================================================================================*/
  2828. short const * UnitClass::Overlap_List(bool redraw) const
  2829. {
  2830. assert(Units.ID(this) == ID);
  2831. assert(IsActive);
  2832. #ifdef PARTIAL
  2833. if (Height == 0 && redraw && Class->DimensionData != NULL) {
  2834. Rect rect;
  2835. int shapenum = Shape_Number();
  2836. if (Class->DimensionData[shapenum].Is_Valid()) {
  2837. rect = Class->DimensionData[shapenum];
  2838. } else {
  2839. rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum);
  2840. }
  2841. if (Is_Selected_By_Player()) {
  2842. rect = Union(rect, Rect(-15, -15, 30, 30));
  2843. }
  2844. if (Class->IsTurretEquipped || Class->IsRadarEquipped) {
  2845. rect = Union(rect, Rect(-15, -15, 30, 30));
  2846. }
  2847. return(Coord_Spillage_List(Coord, rect, true));
  2848. }
  2849. #else
  2850. redraw = redraw;
  2851. #endif
  2852. int size = ICON_PIXEL_W;
  2853. if (redraw && (Is_Selected_By_Player() || IsFiring)) {
  2854. size += 24;
  2855. }
  2856. if (Class->IsGigundo || IsAnimAttached) {
  2857. size = ICON_PIXEL_W*2;
  2858. }
  2859. return(Coord_Spillage_List(Coord, size)+1);
  2860. }
  2861. /***********************************************************************************************
  2862. * UnitClass::Can_Enter_Cell -- Determines cell entry legality. *
  2863. * *
  2864. * Use this routine to determine if the unit can enter the cell *
  2865. * specified and given the direction of entry specified. Typically, *
  2866. * this is used when determining unit travel path. *
  2867. * *
  2868. * INPUT: cell -- The cell to examine. *
  2869. * *
  2870. * facing -- The facing that the unit would enter the specified *
  2871. * cell. If this value is -1, then don't consider *
  2872. * facing when performing the check. *
  2873. * *
  2874. * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is *
  2875. * allowed. *
  2876. * *
  2877. * WARNINGS: none *
  2878. * *
  2879. * HISTORY: *
  2880. * 09/07/1992 JLB : Created. *
  2881. * 04/16/1994 JLB : Converted to member function. *
  2882. * 07/04/1995 JLB : Allowed to drive on building trying to enter it. *
  2883. *=============================================================================================*/
  2884. MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const
  2885. {
  2886. assert(Units.ID(this) == ID);
  2887. assert(IsActive);
  2888. bool cancrush = false;
  2889. CellClass const * cellptr = &Map[cell];
  2890. if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);
  2891. /*
  2892. ** Moving off the edge of the map is not allowed unless
  2893. ** this is a loaner vehicle.
  2894. */
  2895. if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map() && IsLocked) {
  2896. return(MOVE_NO);
  2897. }
  2898. MoveType retval = MOVE_OK;
  2899. /*
  2900. ** Certain vehicles can drive over walls. Check for this case and
  2901. ** and return the appropriate flag. Other units treat walls as impassable.
  2902. */
  2903. if (cellptr->Overlay != OVERLAY_NONE) {
  2904. OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  2905. if (optr->IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) {
  2906. return(MOVE_NO);
  2907. }
  2908. if (optr->IsWall) {
  2909. /*
  2910. ** If the blocking wall is crushable (and not owned by this player or one of this players
  2911. ** allies, then record that it is crushable and let the normal logic take over. The end
  2912. ** result should cause this unit to consider the cell passable.
  2913. */
  2914. if (optr->IsCrushable && Class->IsCrusher) {
  2915. cancrush = !House->Is_Ally(cellptr->Owner);
  2916. }
  2917. if (!cancrush && Is_Weapon_Equipped()) {
  2918. WarheadTypeClass const * whead = Class->PrimaryWeapon->WarheadPtr;
  2919. if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) {
  2920. // if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) {
  2921. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  2922. // } else {
  2923. // return(MOVE_NO);
  2924. // }
  2925. } else {
  2926. return(MOVE_NO);
  2927. }
  2928. }
  2929. }
  2930. }
  2931. /*
  2932. ** Loop through all of the objects in the square setting a bit
  2933. ** for how they affect movement.
  2934. */
  2935. bool crushable = false;
  2936. ObjectClass * obj = cellptr->Cell_Occupier();
  2937. while (obj != NULL) {
  2938. if (obj != this) {
  2939. /*
  2940. ** If object is a land mine, allow movement if possible.
  2941. */
  2942. if (obj->What_Am_I() == RTTI_BUILDING && (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House))) {
  2943. if ((*(BuildingClass *)obj) == STRUCT_APMINE) return(MOVE_OK);
  2944. if ((*(BuildingClass *)obj) == STRUCT_AVMINE) return(MOVE_OK);
  2945. }
  2946. /*
  2947. ** Always allow entry if trying to move on a cell with
  2948. ** authorization from the occupier.
  2949. */
  2950. if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) {
  2951. return(MOVE_OK);
  2952. }
  2953. /*
  2954. ** Special check to allow entry into the sea transport this vehicle
  2955. ** is trying to enter.
  2956. */
  2957. if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) {
  2958. return(MOVE_OK);
  2959. }
  2960. /*
  2961. ** Guard area should not allow the guarding unit to enter the cell with the
  2962. ** guarded unit.
  2963. */
  2964. if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target()) {
  2965. return(MOVE_NO);
  2966. }
  2967. bool is_moving = obj->Is_Foot() &&
  2968. (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving);
  2969. // (((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving);
  2970. // (((FootClass *)obj)->IsRotating || ((FootClass *)obj)->IsDriving);
  2971. // (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->IsDriving);
  2972. if (House->Is_Ally(obj)) {
  2973. if (is_moving) {
  2974. int face = Dir_Facing(PrimaryFacing);
  2975. int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4;
  2976. if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) {
  2977. return(MOVE_NO);
  2978. }
  2979. if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK;
  2980. } else {
  2981. if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO);
  2982. /*
  2983. ** If the blocking object is not in the same zone, then it certainly
  2984. ** isn't a temporary block, it is a permanent one.
  2985. */
  2986. if (Map[Coord].Zones[Class->MZone] != cellptr->Zones[Class->MZone]) return(MOVE_NO);
  2987. if (retval < MOVE_TEMP) retval = MOVE_TEMP;
  2988. }
  2989. } else {
  2990. /*
  2991. ** Cloaked enemy objects are not considered if this is a Find_Path()
  2992. ** call.
  2993. */
  2994. if (!obj->Is_Techno() || !((TechnoClass *)obj)->Is_Cloaked(this)) {
  2995. /*
  2996. ** If this unit can crush infantry, and there is an enemy infantry in the
  2997. ** cell, don't consider the cell impassible. This is true even if the unit
  2998. ** doesn't contain a legitimate weapon.
  2999. */
  3000. bool crusher = Class->IsCrusher;
  3001. if (!crusher || !obj->Class_Of().IsCrushable) {
  3002. /*
  3003. ** Any non-allied blockage is considered impassable if the unit
  3004. ** is not equipped with a weapon.
  3005. */
  3006. if (Class->PrimaryWeapon == NULL) return(MOVE_NO);
  3007. /*
  3008. ** Some kinds of terrain are considered destroyable if the unit is equipped
  3009. ** with the weapon that can destroy it. Otherwise, the terrain is considered
  3010. ** impassable.
  3011. */
  3012. switch (obj->What_Am_I()) {
  3013. case RTTI_TERRAIN:
  3014. #ifdef TOFIX
  3015. if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD &&
  3016. Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) {
  3017. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  3018. } else {
  3019. return(MOVE_NO);
  3020. }
  3021. break;
  3022. #else
  3023. return(MOVE_NO);
  3024. #endif
  3025. default:
  3026. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  3027. break;
  3028. }
  3029. } else {
  3030. crushable = true;
  3031. }
  3032. } else {
  3033. if (retval < MOVE_CLOAK) retval = MOVE_CLOAK;
  3034. }
  3035. }
  3036. }
  3037. /*
  3038. ** Move to next object in chain.
  3039. */
  3040. obj = obj->Next;
  3041. }
  3042. /*
  3043. ** If the cell is out and out impassable because of underlying terrain, then
  3044. ** return this immutable fact.
  3045. */
  3046. if (!cancrush && retval != MOVE_DESTROYABLE && Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) {
  3047. return(MOVE_NO);
  3048. }
  3049. /*
  3050. ** If some allied object has reserved the cell, then consider the cell
  3051. ** as blocked by a moving object.
  3052. */
  3053. if (retval == MOVE_OK && !crushable && (cellptr->Flag.Composite & 0x3F) != 0) {
  3054. /*
  3055. ** If reserved by a vehicle, then consider this blocked terrain.
  3056. */
  3057. if (cellptr->Flag.Occupy.Vehicle) {
  3058. retval = MOVE_MOVING_BLOCK;
  3059. } else {
  3060. if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) {
  3061. retval = MOVE_MOVING_BLOCK;
  3062. } else {
  3063. /*
  3064. ** Enemy infantry have reserved the cell. If this unit can crush
  3065. ** infantry, consider the cell passable. If not, then consider the
  3066. ** cell destroyable if it has a weapon. If neither case applies, then
  3067. ** this vehicle should avoid the cell altogether.
  3068. */
  3069. if (!Class->IsCrusher) {
  3070. if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsAntiGround) {
  3071. retval = MOVE_DESTROYABLE;
  3072. } else {
  3073. return(MOVE_NO);
  3074. }
  3075. }
  3076. }
  3077. }
  3078. }
  3079. /*
  3080. ** If its ok to move into the cell because we can crush whats in the cell, then
  3081. ** make sure no one else is already moving into the cell to crush something.
  3082. */
  3083. if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) {
  3084. /*
  3085. ** However, if the cell is occupied by a crushable vehicle, then we can
  3086. ** never be sure if some other friendly vehicle is also trying to crush
  3087. ** the cell at the same time. In the case of a crushable vehicle in the
  3088. ** cell, then allow entry.
  3089. */
  3090. if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) {
  3091. return(MOVE_MOVING_BLOCK);
  3092. }
  3093. }
  3094. /*
  3095. ** Return with the most severe reason why this cell would be impassable.
  3096. */
  3097. return(retval);
  3098. }
  3099. /***********************************************************************************************
  3100. * UnitClass::Init -- Clears all units for scenario preparation. *
  3101. * *
  3102. * This routine will zero out the unit list and unit objects. This routine is typically *
  3103. * used in preparation for a new scenario load. All units are guaranteed to be eliminated *
  3104. * by this routine. *
  3105. * *
  3106. * INPUT: none *
  3107. * *
  3108. * OUTPUT: none *
  3109. * *
  3110. * WARNINGS: none *
  3111. * *
  3112. * HISTORY: *
  3113. * 08/15/1994 JLB : Created. *
  3114. *=============================================================================================*/
  3115. void UnitClass::Init(void)
  3116. {
  3117. Units.Free_All();
  3118. }
  3119. /***********************************************************************************************
  3120. * UnitClass::Start_Driver -- Starts driving and reserves destination cell. *
  3121. * *
  3122. * This routine will start the vehicle moving by marking the destination cell as *
  3123. * reserved. Cells must be reserved in this fashion or else they might become occupied as *
  3124. * the vehicle is proceeding toward it. *
  3125. * *
  3126. * INPUT: headto -- The location where the vehicle will be heading. *
  3127. * *
  3128. * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell *
  3129. * being occupied. *
  3130. * *
  3131. * WARNINGS: none *
  3132. * *
  3133. * HISTORY: *
  3134. * 12/22/1994 JLB : Created. *
  3135. *=============================================================================================*/
  3136. bool UnitClass::Start_Driver(COORDINATE & headto)
  3137. {
  3138. assert(Units.ID(this) == ID);
  3139. assert(IsActive);
  3140. if (DriveClass::Start_Driver(headto) && IsActive) {//BG IsActive can be cleared by Start_Driver
  3141. Mark_Track(headto, MARK_DOWN);
  3142. return(true);
  3143. }
  3144. return(false);
  3145. }
  3146. /***********************************************************************************************
  3147. * UnitClass::What_Action -- Determines what action would occur if clicked on object. *
  3148. * *
  3149. * Use this function to determine what action would likely occur if the specified object *
  3150. * were clicked on while this unit was selected as current. This function controls, not *
  3151. * only the action to perform, but indirectly controls the cursor shape to use as well. *
  3152. * *
  3153. * INPUT: object -- The object that to check for against "this" object. *
  3154. * *
  3155. * OUTPUT: Returns with the default action to perform. If no clear action can be determined, *
  3156. * then ACTION_NONE is returned. *
  3157. * *
  3158. * WARNINGS: none *
  3159. * *
  3160. * HISTORY: *
  3161. * 01/11/1995 JLB : Created. *
  3162. *=============================================================================================*/
  3163. ActionType UnitClass::What_Action(ObjectClass const * object) const
  3164. {
  3165. assert(Units.ID(this) == ID);
  3166. assert(IsActive);
  3167. ActionType action = DriveClass::What_Action(object);
  3168. /*
  3169. ** Allow units to move onto land mines.
  3170. */
  3171. if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING) {
  3172. StructType blah = *((BuildingClass *)object);
  3173. if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE);
  3174. }
  3175. /*
  3176. ** If the unit doesn't have a weapon, but can crush the object, then consider
  3177. ** the object as a movable location.
  3178. */
  3179. if (action == ACTION_ATTACK && !Can_Player_Fire()) {
  3180. if (Class->IsCrusher && object->Class_Of().IsCrushable) {
  3181. action = ACTION_MOVE;
  3182. } else {
  3183. action = ACTION_SELECT;
  3184. }
  3185. }
  3186. /*
  3187. ** Don't allow special deploy action unless there is something to deploy.
  3188. */
  3189. if (action == ACTION_SELF) {
  3190. if (*this == UNIT_MCV) {
  3191. /*
  3192. ** The MCV will get the no-deploy cursor if it couldn't
  3193. ** deploy at its current location.
  3194. */
  3195. ((ObjectClass &)(*this)).Mark(MARK_UP);
  3196. if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) {
  3197. action = ACTION_NO_DEPLOY;
  3198. }
  3199. ((ObjectClass &)(*this)).Mark(MARK_DOWN);
  3200. } else {
  3201. /*
  3202. ** The mine layer can "deploy" its mines if it currently isn't
  3203. ** sitting on top of a mine and it still has mines available.
  3204. */
  3205. if (*this == UNIT_MINELAYER) {
  3206. if (!Ammo || Map[Center_Coord()].Cell_Building() || (Map[Center_Coord()].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Map[Center_Coord()].Smudge).IsBib)) {
  3207. action = ACTION_NO_DEPLOY;
  3208. }
  3209. } else {
  3210. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3211. if (*this == UNIT_CHRONOTANK || *this == UNIT_MAD) {
  3212. if (*this == UNIT_CHRONOTANK) {
  3213. // If the chrono tank's counter is still charging up, don't allow deploy. Or,
  3214. // if it's a player-controlled chrono tank, and the player's currently trying
  3215. // to teleport a different unit, don't allow teleporting this unit.
  3216. if(MoebiusCountDown || (IsOwnedByPlayer && House->UnitToTeleport && Map.IsTargettingMode == SPC_CHRONO2)) {
  3217. action = ACTION_NO_DEPLOY;
  3218. }
  3219. }
  3220. } else {
  3221. #endif
  3222. /*
  3223. ** All other units can "deploy" their passengers if they in-fact have
  3224. ** passengers and are a transport vehicle. Otherwise, they cannot
  3225. ** perform any self action.
  3226. */
  3227. if (Class->Max_Passengers() > 0) {
  3228. if (How_Many() == 0) {
  3229. action = ACTION_NO_DEPLOY;
  3230. }
  3231. } else {
  3232. action = ACTION_NONE;
  3233. }
  3234. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3235. }
  3236. #endif
  3237. }
  3238. }
  3239. }
  3240. /*
  3241. ** Special return to friendly refinery action.
  3242. */
  3243. bool is_player_controlled = (Session.Type == GAME_NORMAL)
  3244. ? (House->IsPlayerControl && object->Owner() != HOUSE_NONE && HouseClass::As_Pointer(object->Owner())->IsPlayerControl)
  3245. : (Is_Owned_By_Player() && House->Class->House == object->Owner());
  3246. if (is_player_controlled && object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) {
  3247. action = ACTION_ENTER;
  3248. }
  3249. /*
  3250. ** Special return to friendly repair factory action.
  3251. */
  3252. if (is_player_controlled && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
  3253. BuildingClass * building = (BuildingClass *)object;
  3254. if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
  3255. action = ACTION_MOVE;
  3256. }
  3257. }
  3258. /*
  3259. ** Check to see if it can enter a transporter.
  3260. */
  3261. if (
  3262. House->Is_Ally(object) &&
  3263. House->IsPlayerControl && object->Is_Techno() && object->What_Am_I() == RTTI_VESSEL) {
  3264. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  3265. if( *(VesselClass *)object != VESSEL_CARRIER) {
  3266. #endif
  3267. switch (((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) {
  3268. case RADIO_ROGER:
  3269. action = ACTION_ENTER;
  3270. break;
  3271. case RADIO_NEGATIVE:
  3272. action = ACTION_NO_ENTER;
  3273. break;
  3274. default:
  3275. action = ACTION_NONE;
  3276. break;
  3277. }
  3278. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  3279. }
  3280. #endif
  3281. }
  3282. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3283. if (*this == UNIT_MAD && (IsDumping || Gold)) {
  3284. action = ACTION_NONE;
  3285. }
  3286. #endif
  3287. /*
  3288. ** If it doesn't know what to do with the object, then just
  3289. ** say it can't move there.
  3290. */
  3291. if (action == ACTION_NONE) action = ACTION_NOMOVE;
  3292. return(action);
  3293. }
  3294. /***********************************************************************************************
  3295. * UnitClass::What_Action -- Determines action to perform on specified cell. *
  3296. * *
  3297. * This routine will determine what action to perform if the mouse were clicked over the *
  3298. * cell specified. At the unit level, only the harvester is checked for. The lower *
  3299. * classes determine the regular action response. *
  3300. * *
  3301. * INPUT: cell -- The cell that the mouse might be clicked on. *
  3302. * *
  3303. * OUTPUT: Returns with the action type that this unit will perform if the mouse were *
  3304. * clicked of the cell specified. *
  3305. * *
  3306. * WARNINGS: none *
  3307. * *
  3308. * HISTORY: *
  3309. * 09/21/1995 JLB : Created. *
  3310. *=============================================================================================*/
  3311. ActionType UnitClass::What_Action(CELL cell) const
  3312. {
  3313. assert(Units.ID(this) == ID);
  3314. assert(IsActive);
  3315. ActionType action = DriveClass::What_Action(cell);
  3316. if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) {
  3317. return(ACTION_HARVEST);
  3318. }
  3319. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3320. if (*this == UNIT_MAD && (IsDumping || Gold)) {
  3321. action = ACTION_NOMOVE;
  3322. }
  3323. #endif
  3324. return(action);
  3325. }
  3326. /***********************************************************************************************
  3327. * UnitClass::Exit_Repair -- Drive the unit off the repair facility. *
  3328. * *
  3329. * INPUT: none *
  3330. * *
  3331. * OUTPUT: none *
  3332. * *
  3333. * WARNINGS: none *
  3334. * *
  3335. * HISTORY: *
  3336. * 04/03/1995 BWG : Created. *
  3337. *=============================================================================================*/
  3338. #define XYCELL(x, y) (y*MAP_CELL_W+x)
  3339. void UnitClass::Exit_Repair(void)
  3340. {
  3341. assert(Units.ID(this) == ID);
  3342. assert(IsActive);
  3343. int i;
  3344. CELL cell;
  3345. bool found = false;
  3346. static short const ExitRepair[] = {
  3347. XYCELL(0, -2),
  3348. XYCELL(1, -1),
  3349. XYCELL(2, 0),
  3350. XYCELL(1, 1),
  3351. XYCELL(0, 2),
  3352. XYCELL(-1, 1),
  3353. XYCELL(-2, 0),
  3354. XYCELL(-1, -1)
  3355. };
  3356. cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())];
  3357. if (Can_Enter_Cell(cell) == MOVE_OK) found = true;
  3358. if (!found) for (i=0; i<8; i++) {
  3359. cell = Coord_Cell(Coord) + ExitRepair[i];
  3360. if (Can_Enter_Cell(cell) == MOVE_OK) {
  3361. found = true;
  3362. break;
  3363. }
  3364. }
  3365. if (found) {
  3366. // DirType dir = Direction(cell);
  3367. Assign_Mission(MISSION_MOVE);
  3368. Assign_Destination(::As_Target(cell));
  3369. }
  3370. }
  3371. /***********************************************************************************************
  3372. * UnitClass::Mission_Guard -- Special guard mission override processor. *
  3373. * *
  3374. * Handles the guard mission for the unit. If the IQ is high enough and the unit is *
  3375. * a harvester, it will begin to harvest automatically. An MCV might autodeploy. *
  3376. * *
  3377. * INPUT: none *
  3378. * *
  3379. * OUTPUT: Returns the time delay before this command is executed again. *
  3380. * *
  3381. * WARNINGS: none *
  3382. * *
  3383. * HISTORY: *
  3384. * 05/08/1995 JLB : Created. *
  3385. * 05/08/1995 JLB : Fixes gunboat problems. *
  3386. *=============================================================================================*/
  3387. int UnitClass::Mission_Guard(void)
  3388. {
  3389. assert(Units.ID(this) == ID);
  3390. assert(IsActive);
  3391. if (/*House->IsBaseBuilding &&*/ !House->IsHuman && Class->IsToHarvest && House->Get_Quantity(STRUCT_REFINERY) > 0 && !House->IsTiberiumShort) {
  3392. Assign_Mission(MISSION_HARVEST);
  3393. return(1);
  3394. // return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
  3395. }
  3396. if (*this == UNIT_MCV && House->IsBaseBuilding) {
  3397. Assign_Mission(MISSION_UNLOAD);
  3398. return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
  3399. }
  3400. return(DriveClass::Mission_Guard());
  3401. }
  3402. /***********************************************************************************************
  3403. * UnitClass::Mission_Move -- Handles special move mission overrides. *
  3404. * *
  3405. * This routine intercepts the normal move mission and if a gunboat is being processed, *
  3406. * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission *
  3407. * regardless of what the player did. *
  3408. * *
  3409. * INPUT: none *
  3410. * *
  3411. * OUTPUT: Returns the number of ticks before this routine should be called again. *
  3412. * *
  3413. * WARNINGS: none *
  3414. * *
  3415. * HISTORY: *
  3416. * 05/09/1995 JLB : Created. *
  3417. * 09/28/1995 JLB : Harvester stick in guard mode if no more refineries. *
  3418. *=============================================================================================*/
  3419. int UnitClass::Mission_Move(void)
  3420. {
  3421. assert(Units.ID(this) == ID);
  3422. assert(IsActive);
  3423. IsHarvesting = false;
  3424. /*
  3425. ** Always make sure that that transport door is closed if the vehicle is moving.
  3426. */
  3427. if (!Is_Door_Closed()) {
  3428. APC_Close_Door();
  3429. }
  3430. return(DriveClass::Mission_Move());
  3431. }
  3432. int UnitClass::Mission_Enter(void)
  3433. {
  3434. assert(Units.ID(this) == ID);
  3435. assert(IsActive);
  3436. if (Class->IsToHarvest) {
  3437. TechnoClass * contact = Contact_With_Whom();
  3438. if (contact == NULL) {
  3439. contact = As_Techno(ArchiveTarget);
  3440. }
  3441. if (contact != NULL &&
  3442. contact->What_Am_I() == RTTI_BUILDING &&
  3443. *((BuildingClass*)contact) == STRUCT_REFINERY) {
  3444. TiberiumUnloadRefinery = contact->As_Target();
  3445. }
  3446. }
  3447. return(DriveClass::Mission_Enter());
  3448. }
  3449. /***********************************************************************************************
  3450. * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
  3451. * *
  3452. * This routine examines the unit and adjacent cells in order to find the best facing *
  3453. * for the transport and best staging cell for the potential passengers. This location is *
  3454. * modified by adjacent cell passability and direction of the potential passenger. *
  3455. * *
  3456. * INPUT: passenger -- Pointer to the potential passenger. *
  3457. * *
  3458. * moveto -- Reference to the cell number that specifies where the potential *
  3459. * passenger should move to first. *
  3460. * *
  3461. * OUTPUT: Returns with the direction the transport should face before opening the transport *
  3462. * door. *
  3463. * *
  3464. * WARNINGS: none *
  3465. * *
  3466. * HISTORY: *
  3467. * 05/23/1995 JLB : Created. *
  3468. *=============================================================================================*/
  3469. DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
  3470. {
  3471. assert(Units.ID(this) == ID);
  3472. assert(IsActive);
  3473. /*
  3474. ** Determine the ideal facing that provides the least resistance. This would be the direction
  3475. ** of the potential passenger or the current transport facing if it is going to unload.
  3476. */
  3477. DirType faceto;
  3478. if (passenger != NULL) {
  3479. faceto = Direction(passenger);
  3480. } else {
  3481. faceto = PrimaryFacing.Current() + DIR_S;
  3482. }
  3483. /*
  3484. ** Sweep through the adjacent cells in order to find the best candidate.
  3485. */
  3486. FacingType bestdir = FACING_N;
  3487. int bestval = -1;
  3488. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  3489. int value = 0;
  3490. CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
  3491. /*
  3492. ** Base the initial value of the potential cell according to whether the passenger is
  3493. ** allowed to enter the cell. If it can't, then give such a negative value to the
  3494. ** cell so that it is prevented from ever choosing that cell for load/unload.
  3495. */
  3496. if (passenger != NULL) {
  3497. value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128;
  3498. } else {
  3499. CellClass * cell = &Map[cellnum];
  3500. 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) {
  3501. value = -128;
  3502. } else {
  3503. if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) {
  3504. value = -128;
  3505. } else {
  3506. value = 128;
  3507. }
  3508. }
  3509. }
  3510. /*
  3511. ** Give more weight to the cells that require the least rotation of the transport or the
  3512. ** least roundabout movement for the potential passenger.
  3513. */
  3514. value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto);
  3515. if (face == FACING_S) {
  3516. value -= 100;
  3517. }
  3518. if (face == FACING_SW || face == FACING_SE) value += 64;
  3519. /*
  3520. ** If the value for the potential cell is greater than the last recorded potential
  3521. ** value, then record this cell as the best candidate.
  3522. */
  3523. if (bestval == -1 || value > bestval) {
  3524. bestval = value;
  3525. bestdir = face;
  3526. }
  3527. }
  3528. /*
  3529. ** If a suitable direction was found, then return with the direction value.
  3530. */
  3531. moveto = 0;
  3532. if (bestval > 0) {
  3533. static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE};
  3534. moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir);
  3535. return(_desired_to_actual[bestdir]);
  3536. }
  3537. return(DIR_S);
  3538. }
  3539. /***********************************************************************************************
  3540. * UnitClass::Flag_Attach -- Attaches a house flag to this unit. *
  3541. * *
  3542. * This routine will attach a house flag to this unit. *
  3543. * *
  3544. * INPUT: house -- The house that is having its flag attached to it. *
  3545. * *
  3546. * OUTPUT: Was the house flag successfully attached to this unit? *
  3547. * *
  3548. * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure *
  3549. * of this routine. *
  3550. * *
  3551. * HISTORY: *
  3552. * 05/23/1995 JLB : Created. *
  3553. *=============================================================================================*/
  3554. bool UnitClass::Flag_Attach(HousesType house)
  3555. {
  3556. assert(Units.ID(this) == ID);
  3557. assert(IsActive);
  3558. if (house != HOUSE_NONE && Flagged == HOUSE_NONE) {
  3559. Flagged = house;
  3560. Mark(MARK_CHANGE);
  3561. return(true);
  3562. }
  3563. return(false);
  3564. }
  3565. /***********************************************************************************************
  3566. * UnitClass::Flag_Remove -- Removes the house flag from this unit. *
  3567. * *
  3568. * This routine will remove the house flag that is attached to this unit. *
  3569. * *
  3570. * INPUT: none *
  3571. * *
  3572. * OUTPUT: Was the flag successfully removed? *
  3573. * *
  3574. * WARNINGS: This routine doesn't put the flag into a new location. That operation must *
  3575. * be performed or else the house flag will cease to exist. *
  3576. * *
  3577. * HISTORY: *
  3578. * 05/23/1995 JLB : Created. *
  3579. *=============================================================================================*/
  3580. bool UnitClass::Flag_Remove(void)
  3581. {
  3582. assert(Units.ID(this) == ID);
  3583. assert(IsActive);
  3584. if (Flagged != HOUSE_NONE) {
  3585. Flagged = HOUSE_NONE;
  3586. Mark(MARK_CHANGE);
  3587. return(true);
  3588. }
  3589. return(false);
  3590. }
  3591. /***********************************************************************************************
  3592. * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. *
  3593. * *
  3594. * This routine is used to fetch the number of "fullness" pips to display on the unit. *
  3595. * This will either be the number of passengers or the percentage full (in 1/5ths) of *
  3596. * a harvester. *
  3597. * *
  3598. * INPUT: none *
  3599. * *
  3600. * OUTPUT: Returns with the number of pips to draw on this unit. *
  3601. * *
  3602. * WARNINGS: none *
  3603. * *
  3604. * HISTORY: *
  3605. * 06/25/1995 JLB : Created. *
  3606. *=============================================================================================*/
  3607. int UnitClass::Pip_Count(void) const
  3608. {
  3609. assert(Units.ID(this) == ID);
  3610. assert(IsActive);
  3611. if (Class->Max_Passengers() > 0) {
  3612. return(How_Many());
  3613. }
  3614. if (*this == UNIT_MINELAYER) {
  3615. int retval = 0;
  3616. if (Ammo > 0) {
  3617. retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo);
  3618. if (!retval) retval = 1;
  3619. }
  3620. return(retval);
  3621. }
  3622. if (*this == UNIT_HARVESTER) {
  3623. return((Gold + Gems) / 4);
  3624. }
  3625. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3626. if (*this == UNIT_CHRONOTANK) {
  3627. int fulldur = ChronoTankDuration * TICKS_PER_MINUTE;
  3628. return( (fulldur - MoebiusCountDown) / (fulldur / 5));
  3629. }
  3630. #endif
  3631. return(0);
  3632. }
  3633. /***********************************************************************************************
  3634. * UnitClass::APC_Close_Door -- Closes an APC door. *
  3635. * *
  3636. * This routine will initiate closing of the APC door. *
  3637. * *
  3638. * INPUT: none *
  3639. * *
  3640. * OUTPUT: none *
  3641. * *
  3642. * WARNINGS: none *
  3643. * *
  3644. * HISTORY: *
  3645. * 06/25/1995 JLB : Created. *
  3646. *=============================================================================================*/
  3647. void UnitClass::APC_Close_Door(void)
  3648. {
  3649. assert(Units.ID(this) == ID);
  3650. assert(IsActive);
  3651. Close_Door(10, 2);
  3652. }
  3653. /***********************************************************************************************
  3654. * UnitClass::APC_Open_Door -- Opens an APC door. *
  3655. * *
  3656. * This routine will initiate opening of the APC door. *
  3657. * *
  3658. * INPUT: none *
  3659. * *
  3660. * OUTPUT: none *
  3661. * *
  3662. * WARNINGS: none *
  3663. * *
  3664. * HISTORY: *
  3665. * 06/25/1995 JLB : Created. *
  3666. *=============================================================================================*/
  3667. void UnitClass::APC_Open_Door(void)
  3668. {
  3669. assert(Units.ID(this) == ID);
  3670. assert(IsActive);
  3671. if (!IsDriving && !IsRotating) {
  3672. if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) {
  3673. Open_Door(10, 2);
  3674. } else {
  3675. Open_Door(1, 2);
  3676. }
  3677. }
  3678. }
  3679. /***********************************************************************************************
  3680. * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. *
  3681. * *
  3682. * When a unit is destroyed, a crew member might be generated. This routine will return *
  3683. * with the infantry type to produce for this unit. This routine will be called for every *
  3684. * survivor that is generated. *
  3685. * *
  3686. * INPUT: none *
  3687. * *
  3688. * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. *
  3689. * *
  3690. * WARNINGS: none *
  3691. * *
  3692. * HISTORY: *
  3693. * 08/13/1995 JLB : Created. *
  3694. *=============================================================================================*/
  3695. InfantryType UnitClass::Crew_Type(void) const
  3696. {
  3697. assert(Units.ID(this) == ID);
  3698. assert(IsActive);
  3699. if (Class->PrimaryWeapon == NULL) {
  3700. if (Percent_Chance(50)) {
  3701. return(INFANTRY_C1);
  3702. } else {
  3703. return(INFANTRY_C7);
  3704. }
  3705. }
  3706. return(DriveClass::Crew_Type());
  3707. }
  3708. /***********************************************************************************************
  3709. * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. *
  3710. * *
  3711. * This mission handler will look for a repair facility. If one is found then contact *
  3712. * is established and then the normal Mission_Enter logic is performed. The repair facility *
  3713. * will take over the actual repair coordination process. *
  3714. * *
  3715. * INPUT: none *
  3716. * *
  3717. * OUTPUT: Returns the number of game frames to delay before calling this routine again. *
  3718. * *
  3719. * WARNINGS: none *
  3720. * *
  3721. * HISTORY: *
  3722. * 10/02/1995 JLB : Created. *
  3723. *=============================================================================================*/
  3724. int UnitClass::Mission_Repair(void)
  3725. {
  3726. assert(Units.ID(this) == ID);
  3727. assert(IsActive);
  3728. BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, true);
  3729. IsHarvesting = false;
  3730. /*
  3731. ** If there is no available repair facility, then check to see if there
  3732. ** are any repair facilities at all. If not, then enter this unit
  3733. ** into idle state.
  3734. */
  3735. if (nearest == NULL) {
  3736. if (!(House->ActiveBScan & STRUCTF_REFINERY)) {
  3737. Enter_Idle_Mode();
  3738. }
  3739. } else {
  3740. /*
  3741. ** Try to establish radio contact with the repair facility. If contact
  3742. ** was established, then proceed with normal enter mission, which handles
  3743. ** the repair process.
  3744. */
  3745. if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
  3746. Assign_Mission(MISSION_ENTER);
  3747. return(1);
  3748. }
  3749. }
  3750. /*
  3751. ** If no action could be performed at this time, then wait
  3752. ** around for a bit before trying again.
  3753. */
  3754. return(MissionControl[Mission].Normal_Delay());
  3755. }
  3756. /***********************************************************************************************
  3757. * UnitClass::Fire_Direction -- Determines the direction of firing. *
  3758. * *
  3759. * This routine will return with the facing that a projectile will travel if it was *
  3760. * fired at this instant. The facing should match the turret facing for those units *
  3761. * equipped with a turret. If the unit doesn't have a turret, then it will be the facing *
  3762. * of the body. *
  3763. * *
  3764. * INPUT: none *
  3765. * *
  3766. * OUTPUT: Returns with the default firing direction for a projectile. *
  3767. * *
  3768. * WARNINGS: none *
  3769. * *
  3770. * HISTORY: *
  3771. * 06/25/1995 JLB : Created. *
  3772. *=============================================================================================*/
  3773. DirType UnitClass::Fire_Direction(void) const
  3774. {
  3775. assert(Units.ID(this) == ID);
  3776. assert(IsActive);
  3777. if (Class->IsTurretEquipped) {
  3778. if (*this == UNIT_V2_LAUNCHER) {
  3779. int diff1 = SecondaryFacing.Difference(DIR_E);
  3780. int diff2 = SecondaryFacing.Difference(DIR_W);
  3781. diff1 = ABS(diff1);
  3782. diff2 = ABS(diff2);
  3783. int diff = min(diff1, diff2);
  3784. int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff);
  3785. if (SecondaryFacing.Difference(DIR_N) < 0) {
  3786. return(DirType)(SecondaryFacing - (DirType)adj);
  3787. } else {
  3788. return(DirType)(SecondaryFacing + (DirType)adj);
  3789. }
  3790. }
  3791. return(SecondaryFacing.Current());
  3792. }
  3793. return(DriveClass::Fire_Direction());
  3794. }
  3795. /***********************************************************************************************
  3796. * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. *
  3797. * *
  3798. * This virtual routine is used to determine if the vehicle is allowed *
  3799. * to start moving. It is typically called when the vehicle desires *
  3800. * to move but needs confirmation from the turret logic before *
  3801. * proceeding. This happens when dealing with a vehicle that must have *
  3802. * its turret face the same direction as the body before the vehicle *
  3803. * may begin movement. *
  3804. * *
  3805. * INPUT: dir -- The facing the unit wants to travel in. *
  3806. * *
  3807. * OUTPUT: bool; Can the unit begin forward movement now? *
  3808. * *
  3809. * WARNINGS: none *
  3810. * *
  3811. * HISTORY: *
  3812. * 05/12/1994 JLB : Created. *
  3813. *=============================================================================================*/
  3814. bool UnitClass::Ok_To_Move(DirType dir) const
  3815. {
  3816. assert(Units.ID(this) == ID);
  3817. assert(IsActive);
  3818. if (Class->IsLockTurret) {
  3819. if (IsRotating) {
  3820. return(false);
  3821. } else {
  3822. if (SecondaryFacing.Difference(dir)) {
  3823. ((UnitClass *)this)->SecondaryFacing.Set_Desired(dir);
  3824. return(false);
  3825. }
  3826. }
  3827. }
  3828. return(true);
  3829. }
  3830. /***********************************************************************************************
  3831. * UnitClass::Can_Fire -- Determines if turret can fire upon target. *
  3832. * *
  3833. * This routine determines if the turret can fire upon the target *
  3834. * specified. *
  3835. * *
  3836. * INPUT: target -- The target to fire upon. *
  3837. * *
  3838. * which -- Which weapon to use to determine legality to fire. 0=primary, *
  3839. * 1=secondary. *
  3840. * *
  3841. * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. *
  3842. * *
  3843. * WARNINGS: none *
  3844. * *
  3845. * HISTORY: *
  3846. * 04/26/1994 JLB : Created. *
  3847. * 06/01/1994 JLB : Returns reason why it can't fire. *
  3848. *=============================================================================================*/
  3849. FireErrorType UnitClass::Can_Fire(TARGET target, int which) const
  3850. {
  3851. assert(Units.ID(this) == ID);
  3852. assert(IsActive);
  3853. DirType dir; // The facing to impart upon the projectile.
  3854. int diff;
  3855. FireErrorType fire = DriveClass::Can_Fire(target, which);
  3856. if (fire == FIRE_OK) {
  3857. WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
  3858. /*
  3859. ** If this unit cannot fire while moving, then bail.
  3860. */
  3861. if ((Class->IsNoFireWhileMoving /*!Class->IsTurretEquipped || Class->IsLockTurret*/) && Target_Legal(NavCom)) {
  3862. return(FIRE_MOVING);
  3863. }
  3864. /*
  3865. ** If the turret is rotating and the projectile isn't a homing type, then
  3866. ** firing must be delayed until the rotation stops.
  3867. */
  3868. if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) {
  3869. return(FIRE_ROTATING);
  3870. }
  3871. dir = Direction(target);
  3872. /*
  3873. ** Determine if the turret facing isn't too far off of facing the target.
  3874. */
  3875. if (Class->IsTurretEquipped) {
  3876. diff = SecondaryFacing.Difference(dir);
  3877. } else {
  3878. diff = PrimaryFacing.Difference(dir);
  3879. }
  3880. diff = ABS(diff);
  3881. if (weapon->Bullet->ROT != 0) {
  3882. diff >>= 2;
  3883. }
  3884. if (diff < 8) {
  3885. return(DriveClass::Can_Fire(target, which));
  3886. }
  3887. return(FIRE_FACING);
  3888. }
  3889. return(fire);
  3890. }
  3891. /***********************************************************************************************
  3892. * UnitClass::Fire_At -- Try to fire upon the target specified. *
  3893. * *
  3894. * This routine is the auto-fire logic for the turret. It will check *
  3895. * to see if firing is technically legal given the specified target. *
  3896. * If it is legal to fire, it does so. It is safe to call this routine *
  3897. * every game tick. *
  3898. * *
  3899. * INPUT: target -- The target to fire upon. *
  3900. * *
  3901. * which -- Which weapon to use when firing. 0=primary, 1=secondary. *
  3902. * *
  3903. * OUTPUT: bool; Did firing occur? *
  3904. * *
  3905. * WARNINGS: none *
  3906. * *
  3907. * HISTORY: *
  3908. * 04/26/1994 JLB : Created. *
  3909. *=============================================================================================*/
  3910. BulletClass * UnitClass::Fire_At(TARGET target, int which)
  3911. {
  3912. assert(Units.ID(this) == ID);
  3913. assert(IsActive);
  3914. BulletClass * bullet = NULL;
  3915. WeaponTypeClass const * weap = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
  3916. if (weap == NULL) return(NULL);
  3917. if (Can_Fire(target, which) == FIRE_OK) {
  3918. bullet = DriveClass::Fire_At(target, which);
  3919. if (bullet != NULL) {
  3920. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3921. if(Class->Type == UNIT_DEMOTRUCK && IsActive) delete this;
  3922. #endif
  3923. /*
  3924. ** Possible reload timer set.
  3925. */
  3926. if ((*this == UNIT_V2_LAUNCHER) && Reload == 0) {
  3927. Reload = TICKS_PER_SECOND * 30;
  3928. }
  3929. }
  3930. }
  3931. return(bullet);
  3932. }
  3933. /***********************************************************************************************
  3934. * UnitClass::Class_Of -- Fetches a reference to the class type for this object. *
  3935. * *
  3936. * This routine will fetch a reference to the TypeClass of this object. *
  3937. * *
  3938. * INPUT: none *
  3939. * *
  3940. * OUTPUT: Returns with reference to the type class of this object. *
  3941. * *
  3942. * WARNINGS: none *
  3943. * *
  3944. * HISTORY: *
  3945. * 07/29/1995 JLB : Created. *
  3946. *=============================================================================================*/
  3947. ObjectTypeClass const & UnitClass::Class_Of(void) const
  3948. {
  3949. assert(Units.ID(this) == ID);
  3950. assert(IsActive);
  3951. return(*Class);
  3952. }
  3953. /***********************************************************************************************
  3954. * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
  3955. * *
  3956. * Use this routine to determine what the Tiberium load is (as a fixed point percentage). *
  3957. * *
  3958. * INPUT: none *
  3959. * *
  3960. * OUTPUT: Returns with the current "fullness" rating for the object. *
  3961. * *
  3962. * WARNINGS: none *
  3963. * *
  3964. * HISTORY: *
  3965. * 03/17/1995 JLB : Created. *
  3966. *=============================================================================================*/
  3967. fixed UnitClass::Tiberium_Load(void) const
  3968. {
  3969. assert(IsActive);
  3970. if (*this == UNIT_HARVESTER) {
  3971. return(fixed(Tiberium, Rule.BailCount));
  3972. }
  3973. return(0);
  3974. }
  3975. BuildingClass* UnitClass::Find_Best_Refinery(void) const
  3976. {
  3977. /*
  3978. ** Remember our last refinery and prefer that one, if still available and valid.
  3979. */
  3980. if (Target_Legal(TiberiumUnloadRefinery)) {
  3981. BuildingClass * refinery = As_Building(TiberiumUnloadRefinery);
  3982. if (refinery != NULL &&
  3983. refinery->House == House &&
  3984. !refinery->IsInLimbo &&
  3985. refinery->Mission != MISSION_DECONSTRUCTION &&
  3986. *refinery == STRUCT_REFINERY &&
  3987. Map[refinery->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) {
  3988. return refinery;
  3989. } else {
  3990. TiberiumUnloadRefinery = TARGET_NONE;
  3991. }
  3992. }
  3993. /*
  3994. ** Find nearby refinery and head to it?
  3995. */
  3996. return Find_Docking_Bay(STRUCT_REFINERY, false);
  3997. }
  3998. /***********************************************************************************************
  3999. * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
  4000. * *
  4001. * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple *
  4002. * calls to this routine are needed in order to fully offload all Tiberium. *
  4003. * *
  4004. * INPUT: none *
  4005. * *
  4006. * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,*
  4007. * then this indicates that all Tiberium has been offloaded. *
  4008. * *
  4009. * WARNINGS: none *
  4010. * *
  4011. * HISTORY: *
  4012. * 07/19/1995 JLB : Created. *
  4013. *=============================================================================================*/
  4014. int UnitClass::Offload_Tiberium_Bail(void)
  4015. {
  4016. assert(IsActive);
  4017. if (Tiberium) {
  4018. Tiberium--;
  4019. // MBL 05.15.2020: Note, if the below code is ever reeanbled for some ready, make sure to see fix in
  4020. // Tiberian Dawn's DriveClass::Offload_Tiberium_Bail() for AI players
  4021. #ifdef TOFIX
  4022. if (House->IsHuman) {
  4023. return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT);
  4024. }
  4025. return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT);
  4026. #endif
  4027. }
  4028. return(0);
  4029. }
  4030. /***********************************************************************************************
  4031. * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. *
  4032. * *
  4033. * This routine will check to see if the target is infantry and it can be overrun. It will *
  4034. * try to overrun the infantry rather than attack it. This only applies to computer *
  4035. * controlled vehicles. If it isn't the infantry overrun case, then it falls into the *
  4036. * base class for normal (complex) approach algorithm. *
  4037. * *
  4038. * INPUT: none *
  4039. * *
  4040. * OUTPUT: none *
  4041. * *
  4042. * WARNINGS: none *
  4043. * *
  4044. * HISTORY: *
  4045. * 03/17/1995 JLB : Created. *
  4046. * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. *
  4047. *=============================================================================================*/
  4048. void UnitClass::Approach_Target(void)
  4049. {
  4050. assert(IsActive);
  4051. /*
  4052. ** Only if there is a legal target should the approach check occur.
  4053. */
  4054. if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) {
  4055. /*
  4056. ** Special case:
  4057. ** If this is for a unit that can crush infantry, and the target is
  4058. ** infantry, AND the infantry is pretty darn close, then just try
  4059. ** to drive over the infantry instead of firing on it.
  4060. */
  4061. TechnoClass * target = As_Techno(TarCom);
  4062. if (Class->IsCrusher && Distance(TarCom) < Rule.CrushDistance && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) {
  4063. Assign_Destination(TarCom);
  4064. return;
  4065. }
  4066. }
  4067. /*
  4068. ** In the other cases, uses the more complex "get to just within weapon range"
  4069. ** algorithm.
  4070. */
  4071. DriveClass::Approach_Target();
  4072. }
  4073. /***********************************************************************************************
  4074. * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. *
  4075. * *
  4076. * This routine is called when a vehicle enters a square or when it is about to enter a *
  4077. * square (controlled by parameter). When a vehicle that can crush infantry enters a *
  4078. * cell that contains infantry, then the infantry will be destroyed (regardless of *
  4079. * affiliation). When a vehicle threatens to overrun a square, all occupying infantry *
  4080. * will attempt to get out of the way. *
  4081. * *
  4082. * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. *
  4083. * *
  4084. * threaten -- Don't kill, but just threaten to enter the cell. *
  4085. * *
  4086. * OUTPUT: none *
  4087. * *
  4088. * WARNINGS: none *
  4089. * *
  4090. * HISTORY: *
  4091. * 01/19/1995 JLB : Created. *
  4092. *=============================================================================================*/
  4093. void UnitClass::Overrun_Square(CELL cell, bool threaten)
  4094. {
  4095. assert(IsActive);
  4096. CellClass * cellptr = &Map[cell];
  4097. if (Class->IsCrusher) {
  4098. if (threaten) {
  4099. /*
  4100. ** If the cell contains infantry, then they will panic when a vehicle tries
  4101. ** drive over them. Have the infantry run away instead.
  4102. */
  4103. if (cellptr->Flag.Composite & 0x1F) {
  4104. /*
  4105. ** Scattering is controlled by the game difficulty level.
  4106. */
  4107. cellptr->Incoming(0, true);
  4108. }
  4109. } else {
  4110. ObjectClass * object = cellptr->Cell_Occupier();
  4111. int crushed = false;
  4112. while (object != NULL) {
  4113. if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < CELL_LEPTON_W/2) {
  4114. #ifdef OBSOLETE
  4115. /*
  4116. ** If we're running over infantry, let's see if the infantry we're
  4117. ** squashing is a thief trying to capture us. If so, let him succeed.
  4118. */
  4119. if (object->What_Am_I() == RTTI_INFANTRY && *((InfantryClass *)object) == INFANTRY_THIEF && ((InfantryClass *)object)->NavCom == As_Target()) {
  4120. ObjectClass * next = object->Next;
  4121. IsOwnedByPlayer = ((InfantryClass *)object)->IsOwnedByPlayer;
  4122. House = ((InfantryClass *)object)->House;
  4123. delete object;
  4124. object = next;
  4125. } else {
  4126. #endif
  4127. ObjectClass * next = object->Next;
  4128. crushed = true;
  4129. /*
  4130. ** Record credit for the kill(s)
  4131. */
  4132. Sound_Effect(VOC_SQUISH, Coord);
  4133. if (object->Height == 0) {
  4134. AnimClass* anim = new AnimClass(ANIM_CORPSE1, object->Center_Coord());
  4135. if (anim != NULL) {
  4136. anim->Set_Owner(object->Owner());
  4137. }
  4138. }
  4139. object->Record_The_Kill(this);
  4140. object->Mark(MARK_UP);
  4141. object->Limbo();
  4142. delete object;
  4143. //new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord));
  4144. object = next;
  4145. #ifdef OBSOLETE
  4146. }
  4147. #endif
  4148. } else {
  4149. object = object->Next;
  4150. }
  4151. }
  4152. if (crushed) Do_Uncloak();
  4153. }
  4154. }
  4155. }
  4156. /***********************************************************************************************
  4157. * UnitClass::Assign_Destination -- Assign a destination to a unit. *
  4158. * *
  4159. * This will assign the specified destination to the unit. It is presumed that doing is *
  4160. * is all that is needed in order to cause the unit to move to the specified destination. *
  4161. * *
  4162. * INPUT: target -- The target (location) to move to. *
  4163. * *
  4164. * OUTPUT: none *
  4165. * *
  4166. * WARNINGS: none *
  4167. * *
  4168. * HISTORY: *
  4169. * 07/09/1996 JLB : Created. *
  4170. *=============================================================================================*/
  4171. void UnitClass::Assign_Destination(TARGET target)
  4172. {
  4173. assert(IsActive);
  4174. /*
  4175. ** Abort early if there is anything wrong with the parameters
  4176. ** or the unit already is assigned the specified destination.
  4177. */
  4178. if (target == NavCom) return;
  4179. /*
  4180. ** Transport vehicles must tell all passengers that are about to load, that they
  4181. ** cannot proceed. This is accomplished with a radio message to this effect.
  4182. */
  4183. if (In_Radio_Contact() && Class->Max_Passengers() > 0 && Contact_With_Whom()->Is_Infantry()) {
  4184. Transmit_Message(RADIO_OVER_OUT);
  4185. }
  4186. BuildingClass * b = As_Building(target);
  4187. /*
  4188. ** Handle entry logic here.
  4189. */
  4190. if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) {
  4191. /*
  4192. ** If not already in radio contact (presumed with the transport), then
  4193. ** either try to establish contact if allowed, or just move close and
  4194. ** wait until radio contact can be established.
  4195. */
  4196. if (!In_Radio_Contact()) {
  4197. if (b != NULL) {
  4198. /*
  4199. ** Determine if the transport is already in radio contact. If so, then just move
  4200. ** toward the transport and try to establish contact at a later time.
  4201. */
  4202. if (b->In_Radio_Contact()) {
  4203. // TCTCTC -- call for an update from the transport to get a good rendezvous position.
  4204. ArchiveTarget = target;
  4205. /*
  4206. ** HACK ALERT: The repair bay is counting on the assignment of the NavCom by this routine.
  4207. ** The refinery must NOT have the navcom assigned by this routine.
  4208. */
  4209. if (*b != STRUCT_REPAIR) {
  4210. target = TARGET_NONE;
  4211. }
  4212. } else {
  4213. if (Transmit_Message(RADIO_DOCKING, b) != RADIO_ROGER) {
  4214. Transmit_Message(RADIO_OVER_OUT);
  4215. if (*b == STRUCT_REPAIR) {
  4216. ArchiveTarget = target;
  4217. }
  4218. }
  4219. if (*b != STRUCT_REPAIR) {
  4220. ArchiveTarget = target;
  4221. target = TARGET_NONE;
  4222. }
  4223. }
  4224. } else {
  4225. TechnoClass * techno = As_Techno(target);
  4226. if (techno != NULL) {
  4227. /*
  4228. ** Determine if the transport is already in radio contact. If so, then just move
  4229. ** toward the transport and try to establish contact at a later time.
  4230. */
  4231. if (techno->In_Radio_Contact()) {
  4232. // TCTCTC -- call for an update from the transport to get a good rendezvous position.
  4233. ArchiveTarget = target;
  4234. } else {
  4235. if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) {
  4236. if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) {
  4237. Transmit_Message(RADIO_OVER_OUT);
  4238. } else {
  4239. //BG: keep retransmitted navcom from radio-move-here.
  4240. return;
  4241. }
  4242. }
  4243. }
  4244. }
  4245. }
  4246. } else {
  4247. Path[0] = FACING_NONE;
  4248. }
  4249. } else {
  4250. Path[0] = FACING_NONE;
  4251. }
  4252. /*
  4253. ** If the player clicked on a friendly repair facility and the repair
  4254. ** facility is currently not involved with some other unit (radio or unloading).
  4255. */
  4256. if (b != NULL && *b == STRUCT_REPAIR) {
  4257. if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this) ) {
  4258. // if (target != NULL) {
  4259. ArchiveTarget = target;
  4260. // }
  4261. // target = TARGET_NONE;
  4262. } else {
  4263. /*
  4264. ** Establish radio contact protocol. If the facility responds correctly,
  4265. ** then remain in radio contact and proceed toward the desired destination.
  4266. */
  4267. if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) {
  4268. /*
  4269. ** Last check to make sure that the loading square is free from permanent
  4270. ** occupation (such as a building).
  4271. */
  4272. CELL cell = (CELL)(Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1));
  4273. if (Ground[Map[cell].Land_Type()].Cost[Techno_Type_Class()->Speed] > 0) {
  4274. if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) {
  4275. FootClass::Assign_Destination(target);
  4276. Path[0] = FACING_NONE;
  4277. return;
  4278. }
  4279. /*
  4280. ** Failure to establish a docking relationship with the refinery.
  4281. ** Bail & await further instructions.
  4282. */
  4283. Transmit_Message(RADIO_OVER_OUT);
  4284. }
  4285. }
  4286. }
  4287. }
  4288. DriveClass::Assign_Destination(target);
  4289. }
  4290. /***********************************************************************************************
  4291. * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. *
  4292. * *
  4293. * This routine will search the map looking for a good target to attack. It takes into *
  4294. * consideration the type of weapon it is equipped with. *
  4295. * *
  4296. * INPUT: threat -- The threat type to search for. *
  4297. * *
  4298. * OUTPUT: Returns with a target value of the target that this unit should pursue. If there *
  4299. * is no suitable target, then TARGET_NONE is returned. *
  4300. * *
  4301. * WARNINGS: none *
  4302. * *
  4303. * HISTORY: *
  4304. * 07/09/1996 JLB : Created. *
  4305. *=============================================================================================*/
  4306. TARGET UnitClass::Greatest_Threat(ThreatType threat) const
  4307. {
  4308. assert(IsActive);
  4309. if (Class->PrimaryWeapon != NULL) {
  4310. threat = threat | Class->PrimaryWeapon->Allowed_Threats();
  4311. }
  4312. if (Class->SecondaryWeapon != NULL) {
  4313. threat = threat | Class->SecondaryWeapon->Allowed_Threats();
  4314. }
  4315. #ifdef OBSOLETE
  4316. if (House->IsHuman) {
  4317. threat = threat & ~THREAT_BUILDINGS;
  4318. }
  4319. #endif
  4320. return(FootClass::Greatest_Threat(threat));
  4321. }
  4322. /***********************************************************************************************
  4323. * UnitClass::Read_INI -- Reads units from scenario INI file. *
  4324. * *
  4325. * This routine is used to read all the starting units from the *
  4326. * scenario control INI file. The units are created and placed on the *
  4327. * map by this routine. *
  4328. * *
  4329. * INI entry format: *
  4330. * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername *
  4331. * *
  4332. * INPUT: buffer -- Pointer to the loaded scenario INI file. *
  4333. * *
  4334. * OUTPUT: none *
  4335. * *
  4336. * WARNINGS: none *
  4337. * *
  4338. * HISTORY: *
  4339. * 05/24/1994 JLB : Created. *
  4340. *=============================================================================================*/
  4341. void UnitClass::Read_INI(CCINIClass & ini)
  4342. {
  4343. UnitClass * unit; // Working unit pointer.
  4344. HousesType inhouse; // Unit house.
  4345. UnitType classid; // Unit class.
  4346. char buf[128];
  4347. int len = ini.Entry_Count(INI_Name());
  4348. for (int index = 0; index < len; index++) {
  4349. char const * entry = ini.Get_Entry(INI_Name(), index);
  4350. ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf));
  4351. inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
  4352. if (inhouse != HOUSE_NONE) {
  4353. classid = UnitTypeClass::From_Name(strtok(NULL, ","));
  4354. if (classid != UNIT_NONE) {
  4355. if (HouseClass::As_Pointer(inhouse) != NULL) {
  4356. unit = new UnitClass(classid, inhouse);
  4357. if (unit != NULL) {
  4358. /*
  4359. ** Read the raw data.
  4360. */
  4361. int strength = atoi(strtok(NULL, ",\r\n"));
  4362. CELL cell = atoi(strtok(NULL, ",\r\n"));
  4363. COORDINATE coord = Cell_Coord(cell);
  4364. DirType dir = (DirType)atoi(strtok(NULL, ",\r\n"));
  4365. MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r"));
  4366. unit->Trigger = NULL;
  4367. TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL,",\r\n"));
  4368. if (tp != NULL) {
  4369. TriggerClass * tt = Find_Or_Make(tp);
  4370. if (tt != NULL) {
  4371. tt->AttachCount++;
  4372. unit->Trigger = tt;
  4373. }
  4374. }
  4375. if (unit->Unlimbo(coord, dir)) {
  4376. unit->Strength = (int)unit->Class->MaxStrength * fixed(strength, 256);
  4377. if (unit->Strength > unit->Class->MaxStrength-3) unit->Strength = unit->Class->MaxStrength;
  4378. if (Session.Type == GAME_NORMAL || unit->House->IsHuman) {
  4379. unit->Assign_Mission(mission);
  4380. unit->Commence();
  4381. } else {
  4382. unit->Enter_Idle_Mode();
  4383. }
  4384. } else {
  4385. /*
  4386. ** If the unit could not be unlimboed, then this is a catastrophic error
  4387. ** condition. Delete the unit.
  4388. */
  4389. delete unit;
  4390. }
  4391. }
  4392. }
  4393. }
  4394. }
  4395. }
  4396. }
  4397. /***********************************************************************************************
  4398. * UnitClass::Write_INI -- Store the units to the INI database. *
  4399. * *
  4400. * This routine will store all the unit data to the INI database. *
  4401. * *
  4402. * INPUT: ini -- Reference to the INI database object to store to. *
  4403. * *
  4404. * OUTPUT: none *
  4405. * *
  4406. * WARNINGS: none *
  4407. * *
  4408. * HISTORY: *
  4409. * 07/03/1996 JLB : Created. *
  4410. *=============================================================================================*/
  4411. void UnitClass::Write_INI(CCINIClass & ini)
  4412. {
  4413. /*
  4414. ** First, clear out all existing unit data from the ini file.
  4415. */
  4416. ini.Clear(INI_Name());
  4417. /*
  4418. ** Write the unit data out.
  4419. */
  4420. for (int index = 0; index < Units.Count(); index++) {
  4421. UnitClass * unit = Units.Ptr(index);
  4422. if (unit != NULL && !unit->IsInLimbo && unit->IsActive) {
  4423. char uname[10];
  4424. char buf[128];
  4425. sprintf(uname, "%d", index);
  4426. sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
  4427. unit->House->Class->IniName,
  4428. unit->Class->IniName,
  4429. unit->Health_Ratio()*256,
  4430. Coord_Cell(unit->Coord),
  4431. unit->PrimaryFacing.Current(),
  4432. MissionClass::Mission_Name(unit->Mission),
  4433. unit->Trigger.Is_Valid() ? unit->Trigger->Class->IniName : "None"
  4434. );
  4435. ini.Put_String(INI_Name(), uname, buf);
  4436. }
  4437. }
  4438. }
  4439. /***********************************************************************************************
  4440. * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. *
  4441. * *
  4442. * This will determine the value of the cargo carried (limited to considering only gold *
  4443. * and gems) and return that value. Use this to determine how 'valuable' a harvester is. *
  4444. * *
  4445. * INPUT: none *
  4446. * *
  4447. * OUTPUT: Returns with the credit value of the cargo load of this unit (harvester). *
  4448. * *
  4449. * WARNINGS: none *
  4450. * *
  4451. * HISTORY: *
  4452. * 07/29/1996 JLB : Created. *
  4453. *=============================================================================================*/
  4454. int UnitClass::Credit_Load(void) const
  4455. {
  4456. return((Gold * Rule.GoldValue) + (Gems * Rule.GemValue));
  4457. }
  4458. /***********************************************************************************************
  4459. * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. *
  4460. * *
  4461. * Call this routine to determine if this unit should crush the object specified. The *
  4462. * test for crushable action depends on proximity and ability of the unit. If a unit *
  4463. * should crush the object, then it should be given a movement order to enter the cell *
  4464. * where the object is located. *
  4465. * *
  4466. * INPUT: it -- The object to see if it should be crushed. *
  4467. * *
  4468. * OUTPUT: bool; Should "it" be crushed by this unit? *
  4469. * *
  4470. * WARNINGS: none *
  4471. * *
  4472. * HISTORY: *
  4473. * 07/29/1996 JLB : Created. *
  4474. *=============================================================================================*/
  4475. bool UnitClass::Should_Crush_It(TechnoClass const * it) const
  4476. {
  4477. assert(IsActive);
  4478. /*
  4479. ** If this unit cannot crush anything or the candidate object cannot be crushed,
  4480. ** then it obviously should not try to crush it -- return negative answer.
  4481. */
  4482. if (!Class->IsCrusher || it == NULL || !it->Techno_Type_Class()->IsCrushable) return(false);
  4483. /*
  4484. ** Objects that are far away should really be fired upon rather than crushed.
  4485. */
  4486. if (Distance(it) > Rule.CrushDistance) return(false);
  4487. /*
  4488. ** Human controlled units don't automatically crush. Neither do computer controlled ones
  4489. ** if they are at difficult setting.
  4490. */
  4491. if (House->IsHuman || House->Difficulty == DIFF_HARD) return(false);
  4492. /*
  4493. ** If the weapon this unit is equipped with is very good against crushable objects then
  4494. ** fire the weapon instead. It is presumed that a wood destroying weapon is good against
  4495. ** most crushable object types (infantry).
  4496. */
  4497. if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) return(false);
  4498. /*
  4499. ** If the house IQ indicates that crushing should not be allowed, then don't
  4500. ** suggest that crushing be done.
  4501. */
  4502. if (House->IQ < Rule.IQCrush) return(false);
  4503. /*
  4504. ** Don't allow crushing of spies by computer-controlled vehicles.
  4505. */
  4506. if (it->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)it == INFANTRY_SPY) {
  4507. return(false);
  4508. }
  4509. return(true);
  4510. }
  4511. /***********************************************************************************************
  4512. * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. *
  4513. * *
  4514. * This scatter logic will actually look for a nearby location rather than an adjacent *
  4515. * free location. This is necessary because sometimes a unit is required to scatter more *
  4516. * than one cell. A vehicle on a service depot is a prime example. *
  4517. * *
  4518. * INPUT: threat -- The coordinate that a potential threat resides. If this is a non *
  4519. * threat related scatter, then this parameter will be zero. *
  4520. * *
  4521. * forced -- Should the scatter be performed even if it would be otherwise *
  4522. * inconvenient? *
  4523. * *
  4524. * nokidding-- Should the scatter be performed even if it would otherwise be *
  4525. * illegal? *
  4526. * *
  4527. * OUTPUT: none *
  4528. * *
  4529. * WARNINGS: none *
  4530. * *
  4531. * HISTORY: *
  4532. * 10/02/1996 JLB : Created. *
  4533. *=============================================================================================*/
  4534. void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
  4535. {
  4536. assert(IsActive);
  4537. if (Mission == MISSION_SLEEP || Mission == MISSION_STICKY || Mission == MISSION_UNLOAD) return;
  4538. /*
  4539. ** Certain missions prevent scattering regardless of whether it would be
  4540. ** a good idea or not.
  4541. */
  4542. if (!MissionControl[Mission].IsScatter && !forced) return;
  4543. if (PrimaryFacing.Is_Rotating()) return;
  4544. // if (IsRotating) return;
  4545. if (Target_Legal(NavCom) && !nokidding) return;
  4546. if (threat == 0) {
  4547. Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord), Class->Speed)));
  4548. } else {
  4549. DriveClass::Scatter(threat, forced, nokidding);
  4550. }
  4551. }
  4552. /***********************************************************************************************
  4553. * UnitClass::Limbo -- Limbo this unit. *
  4554. * *
  4555. * This will cause the unit to go into a limbo state. If it was carrying a flag, then *
  4556. * the flag will be dropped where the unit is at. *
  4557. * *
  4558. * INPUT: none *
  4559. * *
  4560. * OUTPUT: bool; Was this unit limboed? *
  4561. * *
  4562. * WARNINGS: none *
  4563. * *
  4564. * HISTORY: *
  4565. * 10/08/1996 JLB : Created. *
  4566. *=============================================================================================*/
  4567. bool UnitClass::Limbo(void)
  4568. {
  4569. if (DriveClass::Limbo()) {
  4570. if (Flagged != HOUSE_NONE) {
  4571. HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord));
  4572. Flagged = HOUSE_NONE;
  4573. }
  4574. return(true);
  4575. }
  4576. return(false);
  4577. }
  4578. /***********************************************************************************************
  4579. * UnitClass::Apply_Temporary_Jamming_Shroud -- Apply a temporary gap generator shroud effect *
  4580. * *
  4581. * This is intended as a temporary effect that is active only during export of the *
  4582. * shroud data *
  4583. * *
  4584. * INPUT: House to apply effect for *
  4585. * *
  4586. * OUTPUT: Bitmask of cells that effect was applied to *
  4587. * *
  4588. * WARNINGS: none *
  4589. * *
  4590. * HISTORY: *
  4591. * 8/19/2020 12:13PM ST : Created. *
  4592. *=============================================================================================*/
  4593. unsigned int UnitClass::Apply_Temporary_Jamming_Shroud(HouseClass *house_to_apply_for)
  4594. {
  4595. unsigned int shroud_bits_applied = 0;
  4596. if (!IsActive || !Strength) {
  4597. return shroud_bits_applied;
  4598. }
  4599. if (!Class->IsGapper) {
  4600. return shroud_bits_applied;
  4601. }
  4602. CELL shroud_center = Coord_Cell(Center_Coord());
  4603. int centerx = Cell_X(shroud_center);
  4604. int centery = Cell_Y(shroud_center);
  4605. CELL trycell;
  4606. for (int index = 0; index < 31; index++) {
  4607. shroud_bits_applied <<= 1;
  4608. trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
  4609. if (Map[trycell].Is_Mapped(house_to_apply_for)) {
  4610. Map.Jam_Cell(trycell, House);
  4611. shroud_bits_applied |= 1;
  4612. }
  4613. }
  4614. if (shroud_bits_applied) {
  4615. Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, house_to_apply_for);
  4616. }
  4617. return shroud_bits_applied;
  4618. }
  4619. /***********************************************************************************************
  4620. * UnitClass::Unapply_Temporary_Jamming_Shroud -- Remove temporary gap generator shroud effect *
  4621. * *
  4622. * Remove gap effect added by Apply_Temporary_Jamming_Shroud *
  4623. * *
  4624. * INPUT: House to unapply effect for *
  4625. * Bitmask of cells that effect was applied to *
  4626. * *
  4627. * OUTPUT: *
  4628. * *
  4629. * WARNINGS: none *
  4630. * *
  4631. * HISTORY: *
  4632. * 8/19/2020 12:16PM ST : Created. *
  4633. *=============================================================================================*/
  4634. void UnitClass::Unapply_Temporary_Jamming_Shroud(HouseClass *house_to_unapply_for, unsigned int shroud_bits_applied)
  4635. {
  4636. if (!IsActive || !Strength) {
  4637. return;
  4638. }
  4639. if (!Class->IsGapper) {
  4640. return;
  4641. }
  4642. CELL shroud_center = Coord_Cell(Center_Coord());
  4643. int centerx = Cell_X(shroud_center);
  4644. int centery = Cell_Y(shroud_center);
  4645. CELL trycell;
  4646. for (int index = 30; index >= 0 && shroud_bits_applied; index--) {
  4647. if (shroud_bits_applied & 1) {
  4648. trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
  4649. Map.UnJam_Cell(trycell, House);
  4650. Map.Map_Cell(trycell, house_to_unapply_for);
  4651. }
  4652. shroud_bits_applied >>= 1;
  4653. }
  4654. }
  4655. /*
  4656. ** Updated for client/server multiplayer - ST 8/12/2019 11:46AM
  4657. */
  4658. void UnitClass::Shroud_Regen(void)
  4659. {
  4660. if (Class->IsGapper/*KO && !House->IsPlayerControl*/) {
  4661. int index;
  4662. int centerx, centery;
  4663. CELL trycell;
  4664. if (Session.Type != GAME_GLYPHX_MULTIPLAYER || Is_Legacy_Render_Enabled()) {
  4665. // Only restore under the shroud if it's a valid field.
  4666. if (ShroudBits != (unsigned)-1L) {
  4667. centerx = Cell_X(ShroudCenter);
  4668. centery = Cell_Y(ShroudCenter);
  4669. for (index = 30; index >= 0 && ShroudBits; index--) {
  4670. if (ShroudBits & 1) {
  4671. trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
  4672. #if(0)
  4673. Map.Map_Cell(trycell, PlayerPtr);
  4674. #else
  4675. Map.UnJam_Cell(trycell, House);
  4676. Map.Map_Cell(trycell, House);
  4677. #endif
  4678. }
  4679. ShroudBits >>= 1;
  4680. }
  4681. }
  4682. if(IsActive && Strength) {
  4683. // Now shroud around the new center
  4684. ShroudBits = 0L;
  4685. ShroudCenter = Coord_Cell(Center_Coord());
  4686. centerx = Cell_X(ShroudCenter);
  4687. centery = Cell_Y(ShroudCenter);
  4688. for (index = 0; index < 31; index++) {
  4689. ShroudBits <<= 1;
  4690. trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
  4691. if (Map[trycell].Is_Mapped(House)) {
  4692. Map.Jam_Cell(trycell, House);
  4693. ShroudBits |= 1;
  4694. }
  4695. }
  4696. }
  4697. }
  4698. /*
  4699. ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM
  4700. */
  4701. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  4702. if (House->IsPlayerControl) {
  4703. Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, PlayerPtr);
  4704. }
  4705. } else {
  4706. if (Is_Legacy_Render_Enabled()) {
  4707. for (int i = 0; i < Session.Players.Count(); i++) {
  4708. HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
  4709. if (player_house->IsHuman && player_house != House) {
  4710. Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house);
  4711. }
  4712. }
  4713. }
  4714. }
  4715. }
  4716. }
  4717. /***********************************************************************************************
  4718. * UnitClass::Mission_Guard_Area -- Guard area logic for units. *
  4719. * *
  4720. * This logic is similar to normal guard area except that APCs owned by the computer will *
  4721. * try to load up with nearby infantry. This will give the computer some fake intelligence *
  4722. * when playing in skirmish mode. *
  4723. * *
  4724. * INPUT: none *
  4725. * *
  4726. * OUTPUT: Returns with the delay to use before calling this routine again. *
  4727. * *
  4728. * WARNINGS: none *
  4729. * *
  4730. * HISTORY: *
  4731. * 11/03/1996 JLB : Created. *
  4732. *=============================================================================================*/
  4733. int UnitClass::Mission_Guard_Area(void)
  4734. {
  4735. assert(IsActive);
  4736. /*
  4737. ** Check to see if this is an APC that is largely empty and not otherwise doing anything.
  4738. ** Such an APC should load up with infantry.
  4739. */
  4740. if (Session.Type != GAME_NORMAL &&
  4741. #ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
  4742. (*this == UNIT_APC || *this == UNIT_PHASE ) &&
  4743. #else
  4744. *this == UNIT_APC &&
  4745. #endif
  4746. !Target_Legal(TarCom) &&
  4747. !In_Radio_Contact() &&
  4748. House->Which_Zone(this) != ZONE_NONE &&
  4749. !House->IsHuman) {
  4750. int needed = Class->Max_Passengers() - How_Many();
  4751. for (int index = 0; index < Infantry.Count(); index++) {
  4752. if (needed == 0) break;
  4753. InfantryClass * infantry = Infantry.Ptr(index);
  4754. if (infantry != NULL &&
  4755. infantry->IsActive &&
  4756. !infantry->IsInLimbo &&
  4757. infantry->Strength > 0 &&
  4758. infantry->House == House &&
  4759. !Target_Legal(infantry->TarCom) &&
  4760. !Target_Legal(infantry->NavCom) &&
  4761. Distance(infantry) < 7 * CELL_LEPTON_W &&
  4762. (infantry->Mission == MISSION_GUARD || infantry->Mission == MISSION_GUARD_AREA)) {
  4763. infantry->Assign_Mission(MISSION_ENTER);
  4764. infantry->ArchiveTarget = As_Target();
  4765. needed--;
  4766. }
  4767. }
  4768. }
  4769. return(DriveClass::Mission_Guard_Area());
  4770. }