HOUSE.CPP 336 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232
  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/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */
  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 : HOUSE.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : May 21, 1994 *
  26. * *
  27. * Last Update : November 4, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * HouseClass::AI -- Process house logic. *
  32. * HouseClass::AI_Aircraft -- Determines what aircraft to build next. *
  33. * HouseClass::AI_Attack -- Handles offensive attack logic. *
  34. * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. *
  35. * HouseClass::AI_Building -- Determines what building to build. *
  36. * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. *
  37. * HouseClass::AI_Infantry -- Determines the infantry unit to build. *
  38. * HouseClass::AI_Money_Check -- Handles money production logic. *
  39. * HouseClass::AI_Power_Check -- Handle the power situation. *
  40. * HouseClass::AI_Unit -- Determines what unit to build next. *
  41. * HouseClass::Abandon_Production -- Abandons production of item type specified. *
  42. * HouseClass::Active_Add -- Add an object to active duty for this house. *
  43. * HouseClass::Active_Remove -- Remove this object from active duty for this house. *
  44. * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. *
  45. * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. *
  46. * HouseClass::Adjust_Power -- Adjust the power value of the house. *
  47. * HouseClass::Adjust_Threat -- Adjust threat for the region specified. *
  48. * HouseClass::As_Pointer -- Converts a house number into a house object pointer. *
  49. * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. *
  50. * HouseClass::Attacked -- Lets player know if base is under attack. *
  51. * HouseClass::Available_Money -- Fetches the total credit worth of the house. *
  52. * HouseClass::Begin_Production -- Starts production of the specified object type. *
  53. * HouseClass::Blowup_All -- blows up everything *
  54. * HouseClass::Can_Build -- General purpose build legality checker. *
  55. * HouseClass::Clobber_All -- removes all objects for this house *
  56. * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. *
  57. * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. *
  58. * HouseClass::Detach -- Removes specified object from house tracking systems. *
  59. * HouseClass::Do_All_To_Hunt -- Send all units to hunt. *
  60. * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. *
  61. * HouseClass::Expert_AI -- Handles expert AI processing. *
  62. * HouseClass::Factory_Count -- Fetches the number of factories for specified type. *
  63. * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. *
  64. * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. *
  65. * HouseClass::Find_Build_Location -- Finds a suitable building location. *
  66. * HouseClass::Find_Building -- Finds a building of specified type. *
  67. * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. *
  68. * HouseClass::Find_Juicy_Target -- Finds a suitable field target. *
  69. * HouseClass::Fire_Sale -- Cause all buildings to be sold. *
  70. * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). *
  71. * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. *
  72. * HouseClass::Flag_Remove -- Removes the flag from the specified target. *
  73. * HouseClass::Flag_To_Die -- Flags the house to blow up soon. *
  74. * HouseClass::Flag_To_Lose -- Flags the house to die soon. *
  75. * HouseClass::Flag_To_Win -- Flags the house to win soon. *
  76. * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. *
  77. * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. *
  78. * HouseClass::Harvested -- Adds Tiberium to the harvest storage. *
  79. * HouseClass::HouseClass -- Constructor for a house object. *
  80. * HouseClass::Init -- init's in preparation for new scenario *
  81. * HouseClass::Init_Data -- Initializes the multiplayer color data. *
  82. * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. *
  83. * HouseClass::Is_Ally -- Checks to see if the object is an ally. *
  84. * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
  85. * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? *
  86. * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. *
  87. * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated *
  88. * HouseClass::Make_Ally -- Make the specified house an ally. *
  89. * HouseClass::Make_Enemy -- Make an enemy of the house specified. *
  90. * HouseClass::Manual_Place -- Inform display system of building placement mode. *
  91. * HouseClass::One_Time -- Handles one time initialization of the house array. *
  92. * HouseClass::Place_Object -- Places the object (building) at location specified. *
  93. * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. *
  94. * HouseClass::Power_Fraction -- Fetches the current power output rating. *
  95. * HouseClass::Production_Begun -- Records that production has begun. *
  96. * HouseClass::Read_INI -- Reads house specific data from INI. *
  97. * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. *
  98. * HouseClass::Recalc_Center -- Recalculates the center point of the base. *
  99. * HouseClass::Refund_Money -- Refunds money to back to the house. *
  100. * HouseClass::Remap_Table -- Fetches the remap table for this house object. *
  101. * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. *
  102. * HouseClass::Set_Factory -- Assign specified factory to house tracking. *
  103. * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. *
  104. * HouseClass::Special_Weapon_AI -- Fires special weapon. *
  105. * HouseClass::Spend_Money -- Removes money from the house. *
  106. * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. *
  107. * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. *
  108. * HouseClass::Suggested_New_Team -- Determine what team should be created. *
  109. * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. *
  110. * HouseClass::Suspend_Production -- Temporarily puts production on hold. *
  111. * HouseClass::Tally_Score -- Fills in the score system for this round *
  112. * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. *
  113. * HouseClass::Tracking_Add -- Informs house of new inventory item. *
  114. * HouseClass::Tracking_Remove -- Remove object from house tracking system. *
  115. * HouseClass::Where_To_Go -- Determines where the object should go and wait. *
  116. * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. *
  117. * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. *
  118. * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. *
  119. * HouseClass::Write_INI -- Writes the house data to the INI database. *
  120. * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. *
  121. * HouseClass::delete -- Deallocator function for a house object. *
  122. * HouseClass::new -- Allocator for a house class. *
  123. * HouseClass::operator HousesType -- Conversion to HousesType operator. *
  124. * HouseClass::~HouseClass -- Default destructor for a house object. *
  125. * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. *
  126. * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. *
  127. * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. *
  128. * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. *
  129. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  130. #include "function.h"
  131. #include "vortex.h"
  132. //#include "WolDebug.h"
  133. /*
  134. ** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM
  135. */
  136. #include "SidebarGlyphx.h"
  137. TFixedIHeapClass<HouseClass::BuildChoiceClass> HouseClass::BuildChoice;
  138. int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Save(Pipe &) const
  139. {
  140. return(true);
  141. }
  142. int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Load(Straw &)
  143. {
  144. return(0);
  145. }
  146. void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Code_Pointers(void)
  147. {
  148. }
  149. void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Decode_Pointers(void)
  150. {
  151. }
  152. extern bool RedrawOptionsMenu;
  153. /***********************************************************************************************
  154. * HouseClass::operator HousesType -- Conversion to HousesType operator. *
  155. * *
  156. * This operator will automatically convert from a houses class object into the HousesType *
  157. * enumerated value. *
  158. * *
  159. * INPUT: none *
  160. * *
  161. * OUTPUT: Returns with the object's HousesType value. *
  162. * *
  163. * WARNINGS: none *
  164. * *
  165. * HISTORY: *
  166. * 01/23/1995 JLB : Created. *
  167. *=============================================================================================*/
  168. HouseClass::operator HousesType(void) const
  169. {
  170. assert(Houses.ID(this) == ID);
  171. return(Class->House);
  172. }
  173. /***********************************************************************************************
  174. * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. *
  175. * *
  176. * This will calculate the current tiberium (gold) load as a ratio of the maximum storage *
  177. * capacity. *
  178. * *
  179. * INPUT: none *
  180. * *
  181. * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. *
  182. * *
  183. * WARNINGS: none *
  184. * *
  185. * HISTORY: *
  186. * 07/31/1996 JLB : Created. *
  187. *=============================================================================================*/
  188. fixed HouseClass::Tiberium_Fraction(void) const
  189. {
  190. if (Tiberium == 0) {
  191. return(0);
  192. }
  193. return(fixed(Tiberium, Capacity));
  194. }
  195. /***********************************************************************************************
  196. * HouseClass::As_Pointer -- Converts a house number into a house object pointer. *
  197. * *
  198. * Use this routine to convert a house number into the house pointer that it represents. *
  199. * A simple index into the Houses template array is not sufficient, since the array order *
  200. * is arbitrary. An actual scan through the house object is required in order to find the *
  201. * house object desired. *
  202. * *
  203. * INPUT: house -- The house type number to look up. *
  204. * *
  205. * OUTPUT: Returns with a pointer to the house object that the house number represents. *
  206. * *
  207. * WARNINGS: none *
  208. * *
  209. * HISTORY: *
  210. * 01/23/1995 JLB : Created. *
  211. *=============================================================================================*/
  212. HouseClass * HouseClass::As_Pointer(HousesType house)
  213. {
  214. if (house != HOUSE_NONE) {
  215. for (int index = 0; index < Houses.Count(); index++) {
  216. if (Houses.Ptr(index)->Class->House == house) {
  217. return(Houses.Ptr(index));
  218. }
  219. }
  220. }
  221. return(0);
  222. }
  223. /***********************************************************************************************
  224. * HouseClass::One_Time -- Handles one time initialization of the house array. *
  225. * *
  226. * This basically calls the constructor for each of the houses in the game. All other *
  227. * data specific to the house is initialized when the scenario is loaded. *
  228. * *
  229. * INPUT: none *
  230. * *
  231. * OUTPUT: none *
  232. * *
  233. * WARNINGS: Only call this ONCE at the beginning of the game. *
  234. * *
  235. * HISTORY: *
  236. * 12/09/1994 JLB : Created. *
  237. *=============================================================================================*/
  238. void HouseClass::One_Time(void)
  239. {
  240. BuildChoice.Set_Heap(STRUCT_COUNT);
  241. }
  242. /***********************************************************************************************
  243. * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. *
  244. * *
  245. * The handicap rating will affect combat, movement, and production for the house. It can *
  246. * either make it more or less difficult for the house (controlled by the handicap value). *
  247. * *
  248. * INPUT: handicap -- The handicap value to assign to this house. The default value for *
  249. * a house is DIFF_NORMAL. *
  250. * *
  251. * OUTPUT: Returns with the old handicap value. *
  252. * *
  253. * WARNINGS: none *
  254. * *
  255. * HISTORY: *
  256. * 07/09/1996 JLB : Created. *
  257. * 10/22/1996 JLB : Uses act like value for multiplay only. *
  258. *=============================================================================================*/
  259. DiffType HouseClass::Assign_Handicap(DiffType handicap)
  260. {
  261. DiffType old = Difficulty;
  262. Difficulty = handicap;
  263. if (Session.Type != GAME_NORMAL) {
  264. HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike);
  265. FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias;
  266. GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias;
  267. AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias;
  268. ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias;
  269. ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias;
  270. CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias;
  271. RepairDelay = Rule.Diff[handicap].RepairDelay;
  272. BuildDelay = Rule.Diff[handicap].BuildDelay;
  273. BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias;
  274. } else {
  275. FirepowerBias = Rule.Diff[handicap].FirepowerBias;
  276. GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias;
  277. AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias;
  278. ArmorBias = Rule.Diff[handicap].ArmorBias;
  279. ROFBias = Rule.Diff[handicap].ROFBias;
  280. CostBias = Rule.Diff[handicap].CostBias;
  281. RepairDelay = Rule.Diff[handicap].RepairDelay;
  282. BuildDelay = Rule.Diff[handicap].BuildDelay;
  283. BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias;
  284. }
  285. return(old);
  286. }
  287. #ifdef CHEAT_KEYS
  288. void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const
  289. {
  290. mono->Set_Cursor(x, y);
  291. mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense);
  292. }
  293. /***********************************************************************************************
  294. * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. *
  295. * *
  296. * This utility function will output the current status of the house class to the mono *
  297. * screen. Through this information bugs may be fixed or detected. *
  298. * *
  299. * INPUT: none *
  300. * *
  301. * OUTPUT: none *
  302. * *
  303. * WARNINGS: none *
  304. * *
  305. * HISTORY: *
  306. * 05/31/1994 JLB : Created. *
  307. *=============================================================================================*/
  308. void HouseClass::Debug_Dump(MonoClass * mono) const
  309. {
  310. mono->Set_Cursor(0, 0);
  311. mono->Print(Text_String(TXT_DEBUG_HOUSE));
  312. mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name());
  313. mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name());
  314. mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel);
  315. mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty);
  316. mono->Set_Cursor(52, 1);mono->Printf("%2d", State);
  317. mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage);
  318. mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ);
  319. mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer);
  320. mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan);
  321. mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name());
  322. mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft);
  323. mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits);
  324. mono->Set_Cursor(37, 3);mono->Printf("%5d", Power);
  325. mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied);
  326. mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal);
  327. mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime);
  328. mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime);
  329. mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan);
  330. mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name());
  331. mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings);
  332. mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium);
  333. mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain);
  334. mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]);
  335. mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime);
  336. mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime);
  337. mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan);
  338. mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name());
  339. mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits);
  340. mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits);
  341. mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost);
  342. mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies);
  343. mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack);
  344. mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan);
  345. mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name());
  346. mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry);
  347. mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity);
  348. mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost);
  349. mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W);
  350. mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer);
  351. mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan);
  352. mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name());
  353. mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels);
  354. mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center));
  355. mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime);
  356. for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) {
  357. mono->Set_Cursor(1+index, 15);
  358. if (Scen.GlobalFlags[index] != 0) {
  359. mono->Print("1");
  360. } else {
  361. mono->Print("0");
  362. }
  363. if (index >= 24) break;
  364. }
  365. if (Enemy != HOUSE_NONE) {
  366. char const * name = "";
  367. name = HouseClass::As_Pointer(Enemy)->Name();
  368. mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name());
  369. }
  370. Print_Zone_Stats(27, 11, ZONE_NORTH, mono);
  371. Print_Zone_Stats(27, 13, ZONE_CORE, mono);
  372. Print_Zone_Stats(27, 15, ZONE_SOUTH, mono);
  373. Print_Zone_Stats(1, 13, ZONE_WEST, mono);
  374. Print_Zone_Stats(53, 13, ZONE_EAST, mono);
  375. mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL);
  376. mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL);
  377. mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL);
  378. mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL);
  379. mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL);
  380. mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL);
  381. mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL);
  382. mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL);
  383. mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL);
  384. mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL);
  385. mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL);
  386. mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL);
  387. mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL);
  388. mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL);
  389. mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL);
  390. mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL);
  391. mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL);
  392. mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL);
  393. mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL);
  394. mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL);
  395. mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL);
  396. mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL);
  397. }
  398. #endif
  399. /***********************************************************************************************
  400. * HouseClass::new -- Allocator for a house class. *
  401. * *
  402. * This is the allocator for a house class. Since there can be only *
  403. * one of each type of house, this is allocator has restricted *
  404. * functionality. Any attempt to allocate a house structure for a *
  405. * house that already exists, just returns a pointer to the previously *
  406. * allocated house. *
  407. * *
  408. * INPUT: house -- The house to allocate a class object for. *
  409. * *
  410. * OUTPUT: Returns with a pointer to the allocated class object. *
  411. * *
  412. * WARNINGS: none *
  413. * *
  414. * HISTORY: *
  415. * 05/22/1994 JLB : Created. *
  416. *=============================================================================================*/
  417. void * HouseClass::operator new(size_t)
  418. {
  419. void * ptr = Houses.Allocate();
  420. if (ptr) {
  421. ((HouseClass *)ptr)->IsActive = true;
  422. }
  423. return(ptr);
  424. }
  425. /***********************************************************************************************
  426. * HouseClass::delete -- Deallocator function for a house object. *
  427. * *
  428. * This function marks the house object as "deallocated". Such a *
  429. * house object is available for reallocation later. *
  430. * *
  431. * INPUT: ptr -- Pointer to the house object to deallocate. *
  432. * *
  433. * OUTPUT: none *
  434. * *
  435. * WARNINGS: none *
  436. * *
  437. * HISTORY: *
  438. * 05/22/1994 JLB : Created. *
  439. *=============================================================================================*/
  440. void HouseClass::operator delete(void * ptr)
  441. {
  442. if (ptr) {
  443. ((HouseClass *)ptr)->IsActive = false;
  444. }
  445. Houses.Free((HouseClass *)ptr);
  446. }
  447. /***********************************************************************************************
  448. * HouseClass::HouseClass -- Constructor for a house object. *
  449. * *
  450. * This function is the constructor and it marks the house object *
  451. * as being allocated. *
  452. * *
  453. * INPUT: none *
  454. * *
  455. * OUTPUT: none *
  456. * *
  457. * WARNINGS: none *
  458. * *
  459. * HISTORY: *
  460. * 05/22/1994 JLB : Created. *
  461. *=============================================================================================*/
  462. #define VOX_NOT_READY VOX_NONE
  463. HouseClass::HouseClass(HousesType house) :
  464. RTTI(RTTI_HOUSE),
  465. ID(Houses.ID(this)),
  466. Class(HouseTypes.Ptr(house)),
  467. Difficulty(Scen.CDifficulty),
  468. FirepowerBias(1),
  469. GroundspeedBias(1),
  470. AirspeedBias(1),
  471. ArmorBias(1),
  472. ROFBias(1),
  473. CostBias(1),
  474. BuildSpeedBias(1),
  475. RepairDelay(0),
  476. BuildDelay(0),
  477. ActLike(Class->House),
  478. IsHuman(false),
  479. WasHuman(false),
  480. IsPlayerControl(false),
  481. IsStarted(false),
  482. IsAlerted(false),
  483. IsBaseBuilding(false),
  484. IsDiscovered(false),
  485. IsMaxedOut(false),
  486. IsDefeated(false),
  487. IsToDie(false),
  488. IsToLose(false),
  489. IsToWin(false),
  490. IsCivEvacuated(false),
  491. IsRecalcNeeded(true),
  492. IsVisionary(false),
  493. IsTiberiumShort(false),
  494. IsSpied(false),
  495. IsThieved(false),
  496. IsGPSActive(false),
  497. IsBuiltSomething(false),
  498. IsResigner(false),
  499. IsGiverUpper(false),
  500. IsParanoid(false),
  501. IsToLook(true),
  502. IsQueuedMovementToggle(false),
  503. DidRepair(false),
  504. IQ(Control.IQ),
  505. State(STATE_BUILDUP),
  506. JustBuiltStructure(STRUCT_NONE),
  507. JustBuiltInfantry(INFANTRY_NONE),
  508. JustBuiltUnit(UNIT_NONE),
  509. JustBuiltAircraft(AIRCRAFT_NONE),
  510. JustBuiltVessel(VESSEL_NONE),
  511. Blockage(0),
  512. RepairTimer(0),
  513. AlertTime(0),
  514. BorrowedTime(0),
  515. BScan(0),
  516. ActiveBScan(0),
  517. OldBScan(0),
  518. UScan(0),
  519. ActiveUScan(0),
  520. OldUScan(0),
  521. IScan(0),
  522. ActiveIScan(0),
  523. OldIScan(0),
  524. AScan(0),
  525. ActiveAScan(0),
  526. OldAScan(0),
  527. VScan(0),
  528. ActiveVScan(0),
  529. OldVScan(0),
  530. CreditsSpent(0),
  531. HarvestedCredits(0),
  532. StolenBuildingsCredits(0),
  533. CurUnits(0),
  534. CurBuildings(0),
  535. CurInfantry(0),
  536. CurVessels(0),
  537. CurAircraft(0),
  538. Tiberium(0),
  539. Credits(0),
  540. Capacity(0),
  541. AircraftTotals(NULL),
  542. InfantryTotals(NULL),
  543. UnitTotals(NULL),
  544. BuildingTotals(NULL),
  545. VesselTotals(NULL),
  546. DestroyedAircraft(NULL),
  547. DestroyedInfantry(NULL),
  548. DestroyedUnits(NULL),
  549. DestroyedBuildings(NULL),
  550. DestroyedVessels(NULL),
  551. CapturedBuildings(NULL),
  552. TotalCrates(NULL),
  553. AircraftFactories(0),
  554. InfantryFactories(0),
  555. UnitFactories(0),
  556. BuildingFactories(0),
  557. VesselFactories(0),
  558. Power(0),
  559. Drain(0),
  560. AircraftFactory(-1),
  561. InfantryFactory(-1),
  562. UnitFactory(-1),
  563. BuildingFactory(-1),
  564. VesselFactory(-1),
  565. Radar(RADAR_NONE),
  566. FlagLocation(TARGET_NONE),
  567. FlagHome(0),
  568. UnitsLost(0),
  569. BuildingsLost(0),
  570. WhoLastHurtMe(house),
  571. StartLocationOverride(-1),
  572. Center(0),
  573. Radius(0),
  574. LATime(0),
  575. LAType(RTTI_NONE),
  576. LAZone(ZONE_NONE),
  577. LAEnemy(HOUSE_NONE),
  578. ToCapture(TARGET_NONE),
  579. RadarSpied(0),
  580. PointTotal(0),
  581. PreferredTarget(QUARRY_ANYTHING),
  582. ScreenShakeTime(0),
  583. Attack(0),
  584. Enemy(HOUSE_NONE),
  585. AITimer(0),
  586. UnitToTeleport(0),
  587. BuildStructure(STRUCT_NONE),
  588. BuildUnit(UNIT_NONE),
  589. BuildInfantry(INFANTRY_NONE),
  590. BuildAircraft(AIRCRAFT_NONE),
  591. BuildVessel(VESSEL_NONE),
  592. NukeDest(0),
  593. Allies(0),
  594. DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay),
  595. TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay),
  596. TriggerTime(0),
  597. SpeakAttackDelay(1),
  598. SpeakPowerDelay(1),
  599. SpeakMoneyDelay(1),
  600. SpeakMaxedDelay(1),
  601. RemapColor(Class->RemapColor),
  602. DebugUnlockBuildables(false)
  603. {
  604. /*
  605. ** Explicit in-place construction of the super weapons is
  606. ** required here because the default constructor for super
  607. ** weapons must serve as a no-initialization constructor (save/load reasons).
  608. */
  609. new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
  610. new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY);
  611. new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
  612. new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY);
  613. new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY);
  614. new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY);
  615. new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
  616. new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
  617. memset(UnitsKilled, '\0', sizeof(UnitsKilled));
  618. memset(BuildingsKilled, '\0', sizeof(BuildingsKilled));
  619. memset(BQuantity, '\0', sizeof(BQuantity));
  620. memset(UQuantity, '\0', sizeof(UQuantity));
  621. memset(IQuantity, '\0', sizeof(IQuantity));
  622. memset(AQuantity, '\0', sizeof(AQuantity));
  623. memset(VQuantity, '\0', sizeof(VQuantity));
  624. strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name.
  625. HouseTriggers[house].Clear();
  626. memset((void *)&Regions[0], 0x00, sizeof(Regions));
  627. Make_Ally(house);
  628. Assign_Handicap(Scen.CDifficulty);
  629. /*
  630. ** Set the time of the first AI attack.
  631. */
  632. Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
  633. Init_Unit_Trackers();
  634. }
  635. /***********************************************************************************************
  636. * HouseClass::~HouseClass -- House class destructor *
  637. * *
  638. * *
  639. * *
  640. * INPUT: Nothing *
  641. * *
  642. * OUTPUT: Nothing *
  643. * *
  644. * WARNINGS: None *
  645. * *
  646. * HISTORY: *
  647. * 8/6/96 4:48PM ST : Created *
  648. *=============================================================================================*/
  649. HouseClass::~HouseClass (void)
  650. {
  651. Class = 0;
  652. Free_Unit_Trackers();
  653. }
  654. /***********************************************************************************************
  655. * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. *
  656. * *
  657. * This is the default constructor that initializes all the values to their default *
  658. * settings. *
  659. * *
  660. * INPUT: none *
  661. * *
  662. * OUTPUT: none *
  663. * *
  664. * WARNINGS: none *
  665. * *
  666. * HISTORY: *
  667. * 07/31/1996 JLB : Created. *
  668. *=============================================================================================*/
  669. HouseStaticClass::HouseStaticClass(void) :
  670. IQ(0),
  671. TechLevel(1),
  672. Allies(0),
  673. MaxUnit(Rule.UnitMax/6),
  674. MaxBuilding(Rule.BuildingMax/6),
  675. MaxInfantry(Rule.InfantryMax/6),
  676. MaxVessel(Rule.VesselMax/6),
  677. MaxAircraft(Rule.UnitMax/6),
  678. InitialCredits(0),
  679. Edge(SOURCE_NORTH)
  680. {
  681. }
  682. /***********************************************************************************************
  683. * HouseClass::Can_Build -- General purpose build legality checker. *
  684. * *
  685. * This routine is called when it needs to be determined if the specified object type can *
  686. * be built by this house. Production and sidebar maintenance use this routine heavily. *
  687. * *
  688. * INPUT: type -- Pointer to the type of object that legality is to be checked for. *
  689. * *
  690. * house -- This is the house to check for legality against. Note that this might *
  691. * not be 'this' house since the check could be from a captured factory. *
  692. * Captured factories build what the original owner of them could build. *
  693. * *
  694. * OUTPUT: Can the specified object be built? *
  695. * *
  696. * WARNINGS: none *
  697. * *
  698. * HISTORY: *
  699. * 07/04/1995 JLB : Created. *
  700. * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. *
  701. * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. *
  702. * 11/04/1996 JLB : Computer uses prerequisite record. *
  703. *=============================================================================================*/
  704. bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const
  705. {
  706. assert(Houses.ID(this) == ID);
  707. assert(type != NULL);
  708. /*
  709. ** An object with a prohibited tech level availability will never be allowed, regardless
  710. ** of who requests it.
  711. */
  712. if (((TechnoTypeClass const *)type)->Level == -1) return(false);
  713. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  714. /*
  715. ** If this is a CounterStrike II-only unit, and we're playing a multiplayer
  716. ** game in 'downshifted' mode against CounterStrike or Red Alert, then
  717. ** don't allow building this unit.
  718. */
  719. if (!NewUnitsEnabled) {
  720. switch(type->What_Am_I()) {
  721. case RTTI_INFANTRYTYPE:
  722. if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT)
  723. return(false);
  724. break;
  725. case RTTI_UNITTYPE:
  726. if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT)
  727. return(false);
  728. break;
  729. case RTTI_VESSELTYPE:
  730. if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT)
  731. return(false);
  732. break;
  733. default:
  734. break;
  735. }
  736. }
  737. #endif
  738. /*
  739. ** The computer can always build everything.
  740. */
  741. if (!IsHuman && Session.Type == GAME_NORMAL) return(true);
  742. /*
  743. ** Special hack to get certain objects to exist for both sides in the game.
  744. */
  745. int own = type->Get_Ownable();
  746. /*
  747. ** Check to see if this owner can build the object type specified.
  748. */
  749. if (((1L << house) & own) == 0) {
  750. return(false);
  751. }
  752. /*
  753. ** Perform some equivalency fixups for the building existence flags.
  754. */
  755. long flags = ActiveBScan;
  756. /*
  757. ** The computer records prerequisite buildings because it can't relay on the
  758. ** sidebar to keep track of this information.
  759. */
  760. if (!IsHuman) {
  761. flags = OldBScan;
  762. }
  763. int pre = ((TechnoTypeClass const *)type)->Prerequisite;
  764. /*
  765. ** Advanced power also serves as a prerequisite for normal power.
  766. */
  767. if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER;
  768. /*
  769. ** Either tech center counts as a prerequisite.
  770. */
  771. if (Session.Type != GAME_NORMAL) {
  772. if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH;
  773. }
  774. int level = Control.TechLevel;
  775. #ifdef CHEAT_KEYS
  776. if (Debug_Cheat) {
  777. level = 98;
  778. pre = 0;
  779. }
  780. #endif
  781. // ST - 8/23/2019 4:53PM
  782. if (DebugUnlockBuildables) {
  783. level = 98;
  784. pre = 0;
  785. }
  786. /*
  787. ** See if the prerequisite requirements have been met.
  788. */
  789. return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= (unsigned)level);
  790. }
  791. /***************************************************************************
  792. * HouseClass::Init -- init's in preparation for new scenario *
  793. * *
  794. * INPUT: *
  795. * none. *
  796. * *
  797. * OUTPUT: *
  798. * none. *
  799. * *
  800. * WARNINGS: *
  801. * none. *
  802. * *
  803. * HISTORY: *
  804. * 12/07/1994 BR : Created. *
  805. * 12/17/1994 JLB : Resets tracker bits. *
  806. *=========================================================================*/
  807. void HouseClass::Init(void)
  808. {
  809. Houses.Free_All();
  810. for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
  811. HouseTriggers[index].Clear();
  812. }
  813. }
  814. // Object selection list is switched with player context for GlyphX. ST - 8/7/2019 10:11AM
  815. extern void Logic_Switch_Player_Context(HouseClass *house);
  816. extern bool MPSuperWeaponDisable;
  817. /***********************************************************************************************
  818. * HouseClass::AI -- Process house logic. *
  819. * *
  820. * This handles the AI for the house object. It should be called once per house per game *
  821. * tick. It processes all house global tasks such as low power damage accumulation and *
  822. * house specific trigger events. *
  823. * *
  824. * INPUT: none *
  825. * *
  826. * OUTPUT: none *
  827. * *
  828. * WARNINGS: none *
  829. * *
  830. * HISTORY: *
  831. * 12/27/1994 JLB : Created. *
  832. * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. *
  833. *=============================================================================================*/
  834. extern void Recalculate_Placement_Distances();
  835. void HouseClass::AI(void)
  836. {
  837. assert(Houses.ID(this) == ID);
  838. // Set PlayerPtr to be this house. ST - 8/7/2019 10:12AM
  839. Logic_Switch_Player_Context(this);
  840. /*
  841. ** If base building has been turned on by a trigger, then force the house to begin
  842. ** production and team creation as well. This is also true if the IQ is high enough to
  843. ** being base building.
  844. */
  845. if (!IsHuman && (IsBaseBuilding || IQ >= Rule.IQProduction)) {
  846. IsBaseBuilding = true;
  847. IsStarted = true;
  848. IsAlerted = true;
  849. }
  850. /*
  851. ** Check to see if the house wins.
  852. */
  853. if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) {
  854. IsToWin = false;
  855. if (this == PlayerPtr) {
  856. PlayerWins = true;
  857. } else {
  858. PlayerLoses = true;
  859. }
  860. }
  861. /*
  862. ** Check to see if the house loses.
  863. */
  864. if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) {
  865. IsToLose = false;
  866. if (this == PlayerPtr) {
  867. PlayerLoses = true;
  868. } else {
  869. PlayerWins = true;
  870. }
  871. }
  872. /*
  873. ** Check to see if all objects of this house should be blown up.
  874. */
  875. if (IsToDie && BorrowedTime == 0) {
  876. IsToDie = false;
  877. Blowup_All();
  878. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  879. MPlayer_Defeated();
  880. }
  881. }
  882. /*
  883. ** Double check power values to correct illegal conditions. It is possible to
  884. ** get a power output of negative (one usually) as a result of damage sustained
  885. ** and the fixed point fractional math involved with power adjustments. If the
  886. ** power rating drops below zero, then make it zero.
  887. */
  888. Power = max(Power, 0);
  889. Drain = max(Drain, 0);
  890. /*
  891. ** If the base has been alerted to the enemy and should be attacking, then
  892. ** see if the attack timer has expired. If it has, then create the attack
  893. ** teams.
  894. */
  895. if (IsAlerted && AlertTime == 0) {
  896. /*
  897. ** Adjusted to reduce maximum number of teams created.
  898. */
  899. int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1));
  900. for (int index = 0; index < maxteams; index++) {
  901. TeamTypeClass const * ttype = Suggested_New_Team(true);
  902. if (ttype != NULL) {
  903. ScenarioInit++;
  904. ttype->Create_One_Of();
  905. ScenarioInit--;
  906. }
  907. }
  908. AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
  909. // int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2);
  910. // int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2);
  911. // AlertTime = Random_Pick(mintime, maxtime);
  912. }
  913. /*
  914. ** If this house's flag waypoint is a valid cell, see if there's
  915. ** someone sitting on it. If so, make the scatter.
  916. */
  917. if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) {
  918. TechnoClass * techno = Map[FlagHome].Cell_Techno();
  919. if (techno != NULL) {
  920. bool moving = false;
  921. if (techno->Is_Foot()) {
  922. if (Target_Legal(((FootClass *)techno)->NavCom)) {
  923. moving = true;
  924. }
  925. }
  926. if (!moving) {
  927. techno->Scatter(0, true, true);
  928. }
  929. }
  930. }
  931. /*
  932. ** Create teams for this house if necessary.
  933. ** (Use the same timer for some extra capture-the-flag logic.)
  934. */
  935. if (!IsAlerted && !TeamTime) {
  936. TeamTypeClass const * ttype = Suggested_New_Team(false);
  937. if (ttype) {
  938. ttype->Create_One_Of();
  939. }
  940. TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE;
  941. }
  942. /*
  943. ** If there is insufficient power, then all buildings that are above
  944. ** half strength take a little bit of damage.
  945. */
  946. if (DamageTime == 0) {
  947. /*
  948. ** When the power is below required, then the buildings will take damage over
  949. ** time.
  950. */
  951. if (Power_Fraction() < 1) {
  952. for (int index = 0; index < Buildings.Count(); index++) {
  953. BuildingClass & b = *Buildings.Ptr(index);
  954. if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) {
  955. // BG: Only damage buildings that require power, to keep the
  956. // land mines from blowing up under low-power conditions
  957. if (b.Class->Drain) {
  958. int damage = 1;
  959. b.Take_Damage(damage, 0, WARHEAD_AP, 0);
  960. }
  961. }
  962. }
  963. }
  964. DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay;
  965. }
  966. /*
  967. ** If there are no more buildings to sell, then automatically cancel the
  968. ** sell mode.
  969. */
  970. if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) {
  971. Map.Sell_Mode_Control(0);
  972. }
  973. /*
  974. ** Various base conditions may be announced to the player. Typically, this would be
  975. ** low tiberium capacity or low power.
  976. */
  977. if (PlayerPtr == this) {
  978. if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) {
  979. Speak(VOX_NEED_MO_MONEY);
  980. Map.Flash_Money();
  981. SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
  982. int text_id = TXT_INSUFFICIENT_FUNDS;
  983. char const * text = Text_String(TXT_INSUFFICIENT_FUNDS);
  984. if (text != NULL) {
  985. Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
  986. }
  987. }
  988. if (SpeakMaxedDelay == 0 && IsMaxedOut) {
  989. IsMaxedOut = false;
  990. if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
  991. Speak(VOX_NEED_MO_CAPACITY);
  992. SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
  993. }
  994. }
  995. if (SpeakPowerDelay == 0 && Power_Fraction() < 1) {
  996. if (ActiveBScan & STRUCTF_CONST) {
  997. Speak(VOX_LOW_POWER);
  998. SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
  999. Map.Flash_Power();
  1000. int text_id = -1;
  1001. char const * text = NULL;
  1002. if (BQuantity[STRUCT_AAGUN] > 0) {
  1003. text = Text_String(TXT_POWER_AAGUN);
  1004. text_id = TXT_POWER_AAGUN;
  1005. }
  1006. if (BQuantity[STRUCT_TESLA] > 0) {
  1007. text = Text_String(TXT_POWER_TESLA);
  1008. text_id = TXT_POWER_TESLA;
  1009. }
  1010. if (text == NULL) {
  1011. text = Text_String(TXT_LOW_POWER);
  1012. text_id = TXT_LOW_POWER;
  1013. }
  1014. if (text != NULL) {
  1015. Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
  1016. }
  1017. }
  1018. }
  1019. }
  1020. /*
  1021. ** If there is a flag associated with this house, then mark it to be
  1022. ** redrawn.
  1023. */
  1024. if (Target_Legal(FlagLocation)) {
  1025. UnitClass * unit = As_Unit(FlagLocation);
  1026. if (unit) {
  1027. unit->Mark(MARK_CHANGE);
  1028. } else {
  1029. CELL cell = As_Cell(FlagLocation);
  1030. Map[cell].Flag_Update();
  1031. Map[cell].Redraw_Objects();
  1032. }
  1033. }
  1034. bool is_time = false;
  1035. /*
  1036. ** Triggers are only checked every so often. If the trigger timer has expired,
  1037. ** then set the trigger processing flag.
  1038. */
  1039. if (TriggerTime == 0 || IsBuiltSomething) {
  1040. is_time = true;
  1041. TriggerTime = TICKS_PER_MINUTE/10;
  1042. IsBuiltSomething = false;
  1043. }
  1044. /*
  1045. ** Process any super weapon logic required.
  1046. */
  1047. if (Session.Type != GAME_GLYPHX_MULTIPLAYER || !MPSuperWeaponDisable) {
  1048. Super_Weapon_Handler();
  1049. }
  1050. #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse.
  1051. if( (Session.Type != GAME_NORMAL || !IsHuman) && Scen.AutoSonarTimer == 0 )
  1052. {
  1053. // If house has nothing but subs left, do an automatic sonar pulse to reveal them.
  1054. if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw
  1055. {
  1056. int iCount = 0;
  1057. int i;
  1058. for( i = 0; i != STRUCT_COUNT-3; ++i )
  1059. {
  1060. iCount += BQuantity[ i ];
  1061. }
  1062. if( !iCount )
  1063. {
  1064. for( i = 0; i != UNIT_RA_COUNT-3; ++i )
  1065. {
  1066. iCount += UQuantity[ i ];
  1067. }
  1068. if( !iCount )
  1069. {
  1070. // ajw - Found bug - house's civilians are not removed from IQuantity when they die.
  1071. // Workaround...
  1072. for( i = 0; i <= INFANTRY_DOG; ++i )
  1073. {
  1074. iCount += IQuantity[ i ];
  1075. }
  1076. if( !iCount )
  1077. {
  1078. for( i = 0; i != AIRCRAFT_COUNT; ++i )
  1079. {
  1080. iCount += AQuantity[ i ];
  1081. }
  1082. if( !iCount )
  1083. {
  1084. for( i = 0; i != VESSEL_RA_COUNT; ++i )
  1085. {
  1086. if( i != VESSEL_SS )
  1087. iCount += VQuantity[ i ];
  1088. }
  1089. if( !iCount )
  1090. {
  1091. // Do the ping.
  1092. for (int index = 0; index < Vessels.Count(); index++) {
  1093. VesselClass * sub = Vessels.Ptr(index);
  1094. if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) {
  1095. sub->PulseCountDown = 15 * TICKS_PER_SECOND;
  1096. sub->Do_Uncloak();
  1097. }
  1098. }
  1099. bAutoSonarPulse = true;
  1100. }
  1101. }
  1102. }
  1103. }
  1104. }
  1105. }
  1106. }
  1107. #endif
  1108. if (Session.Type != GAME_NORMAL) {
  1109. Check_Pertinent_Structures();
  1110. }
  1111. /*
  1112. ** Special win/lose check for multiplayer games; by-passes the
  1113. ** trigger system. We must wait for non-zero frame, because init
  1114. ** may not properly set IScan etc for each house; you have to go
  1115. ** through each object's AI before it will be properly set.
  1116. */
  1117. if (Session.Type != GAME_NORMAL && !IsDefeated &&
  1118. !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) {
  1119. MPlayer_Defeated();
  1120. }
  1121. /*
  1122. ** Try to spring all events attached to this house. The triggers will check
  1123. ** for themselves if they actually need to be sprung or not.
  1124. */
  1125. for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) {
  1126. if (HouseTriggers[Class->House][index]->Spring() && index > 0) {
  1127. index--;
  1128. continue;
  1129. }
  1130. }
  1131. /*
  1132. ** If a radar facility is not present, but the radar is active, then turn the radar off.
  1133. ** The radar also is turned off when the power gets below 100% capacity.
  1134. */
  1135. if (PlayerPtr == this) {
  1136. bool jammed = true;
  1137. /*
  1138. ** Find if there are any radar facilities, and if they're jammed or not
  1139. */
  1140. if (IsGPSActive) {
  1141. jammed = false;
  1142. } else {
  1143. for (int index = 0; index < Buildings.Count(); index++) {
  1144. BuildingClass * building = Buildings.Ptr(index);
  1145. #ifdef FIXIT_RADAR_JAMMED
  1146. if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) {
  1147. #else
  1148. if (building && building->House == PlayerPtr) {
  1149. #endif
  1150. if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) {
  1151. if (!building->IsJammed) {
  1152. jammed = false;
  1153. break;
  1154. }
  1155. }
  1156. }
  1157. }
  1158. }
  1159. Map.Set_Jammed(this, jammed);
  1160. // Need to add in here where we activate it when only GPS is active.
  1161. if (Map.Is_Radar_Active()) {
  1162. if (ActiveBScan & STRUCTF_RADAR) {
  1163. if (Power_Fraction() < 1 && !IsGPSActive) {
  1164. Map.Radar_Activate(0);
  1165. }
  1166. } else {
  1167. if (!IsGPSActive) {
  1168. Map.Radar_Activate(0);
  1169. }
  1170. }
  1171. } else {
  1172. if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) {
  1173. if (Power_Fraction() >= 1 || IsGPSActive) {
  1174. Map.Radar_Activate(1);
  1175. }
  1176. } else {
  1177. if (Map.Is_Radar_Existing()) {
  1178. Map.Radar_Activate(4);
  1179. }
  1180. }
  1181. }
  1182. if (!IsGPSActive && !(ActiveBScan & STRUCTF_RADAR)) {
  1183. Radar = RADAR_NONE;
  1184. } else {
  1185. Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF;
  1186. }
  1187. }
  1188. VisibleCredits.AI(false, this, true);
  1189. /*
  1190. ** Perform any expert system AI processing.
  1191. */
  1192. if (IsBaseBuilding && AITimer == 0) {
  1193. AITimer = Expert_AI();
  1194. }
  1195. if (!IsBaseBuilding && State == STATE_ENDGAME) {
  1196. Fire_Sale();
  1197. Do_All_To_Hunt();
  1198. }
  1199. AI_Building();
  1200. AI_Unit();
  1201. AI_Vessel();
  1202. AI_Infantry();
  1203. AI_Aircraft();
  1204. /*
  1205. ** If the production possibilities need to be recalculated, then do so now. This must
  1206. ** occur after the scan bits have been properly updated.
  1207. */
  1208. if (PlayerPtr == this && IsRecalcNeeded) {
  1209. IsRecalcNeeded = false;
  1210. Map.Recalc();
  1211. /*
  1212. ** This placement might affect any prerequisite requirements for construction
  1213. ** lists. Update the buildable options accordingly.
  1214. */
  1215. for (int index = 0; index < Buildings.Count(); index++) {
  1216. BuildingClass * building = Buildings.Ptr(index);
  1217. if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) {
  1218. if (PlayerPtr == building->House) {
  1219. building->Update_Buildables();
  1220. }
  1221. }
  1222. }
  1223. Recalculate_Placement_Distances();
  1224. Check_Pertinent_Structures();
  1225. }
  1226. /*
  1227. ** See if it's time to re-set the can-repair flag
  1228. */
  1229. if (DidRepair && RepairTimer == 0) {
  1230. DidRepair = false;
  1231. }
  1232. if (this == PlayerPtr && IsToLook) {
  1233. IsToLook = false;
  1234. Map.All_To_Look(PlayerPtr);
  1235. }
  1236. }
  1237. /***********************************************************************************************
  1238. * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. *
  1239. * *
  1240. * This handles any super weapons assigned to this house. It also performs any necessary *
  1241. * maintenance that the super weapons require. *
  1242. * *
  1243. * INPUT: none *
  1244. * *
  1245. * OUTPUT: none *
  1246. * *
  1247. * WARNINGS: none *
  1248. * *
  1249. * HISTORY: *
  1250. * 09/17/1996 JLB : Created. *
  1251. *=============================================================================================*/
  1252. void HouseClass::Super_Weapon_Handler(void)
  1253. {
  1254. /*
  1255. ** Perform all super weapon AI processing. This just checks to see if
  1256. ** the graphic needs changing for the special weapon and updates the
  1257. ** sidebar as necessary.
  1258. */
  1259. for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) {
  1260. SuperClass * super = &SuperWeapon[special];
  1261. if (super->Is_Present()) {
  1262. /*
  1263. ** Perform any charge-up logic for the super weapon. If the super
  1264. ** weapon is owned by the player and a graphic change is detected, then
  1265. ** flag the sidebar to be redrawn so the player will see the change.
  1266. */
  1267. if (super->AI(this == PlayerPtr)) {
  1268. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1269. }
  1270. /*
  1271. ** Repeating super weapons that require power will be suspended if there
  1272. ** is insufficient power available.
  1273. */
  1274. if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) {
  1275. super->Suspend(Power_Fraction() < 1);
  1276. }
  1277. }
  1278. }
  1279. /*
  1280. ** Check to see if they have launched the GPS, but subsequently lost their
  1281. ** tech center. If so, remove the GPS, and shroud the map.
  1282. */
  1283. if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) {
  1284. IsGPSActive = false;
  1285. /*
  1286. ** Updated for client/server multiplayer. ST - 8/12/2019 11:32AM
  1287. */
  1288. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  1289. if (IsPlayerControl) {
  1290. Map.Shroud_The_Map(PlayerPtr);
  1291. }
  1292. } else {
  1293. if (IsHuman) {
  1294. Map.Shroud_The_Map(this);
  1295. }
  1296. }
  1297. }
  1298. /*
  1299. ** Check to see if the GPS Satellite should be removed from the sidebar
  1300. ** because of outside circumstances. The advanced technology facility
  1301. ** being destroyed is a good example of this. Having fired the satellite
  1302. ** is another good example, because it's a one-shot item.
  1303. */
  1304. if (SuperWeapon[SPC_GPS].Is_Present()) {
  1305. if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) {
  1306. /*
  1307. ** Remove the missile capability when there is no advanced tech facility.
  1308. */
  1309. if (SuperWeapon[SPC_GPS].Remove()) {
  1310. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1311. IsRecalcNeeded = true;
  1312. }
  1313. } else {
  1314. /*
  1315. ** Auto-fire the GPS satellite if it's charged up.
  1316. */
  1317. if (SuperWeapon[SPC_GPS].Is_Ready()) {
  1318. SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr);
  1319. if (SuperWeapon[SPC_GPS].Remove()) {
  1320. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1321. }
  1322. IsRecalcNeeded = true;
  1323. for (int index = 0; index < Buildings.Count(); index++) {
  1324. BuildingClass * bldg = Buildings.Ptr(index);
  1325. if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) {
  1326. bldg->HasFired = true;
  1327. bldg->Assign_Mission(MISSION_MISSILE);
  1328. break;
  1329. }
  1330. }
  1331. }
  1332. }
  1333. } else {
  1334. /*
  1335. ** If there is no GPS satellite present, but there is a GPS satellite
  1336. ** facility available, then make the GPS satellite available as well.
  1337. */
  1338. if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 &&
  1339. !IsGPSActive &&
  1340. Control.TechLevel >= Rule.GPSTechLevel &&
  1341. (IsHuman || IQ >= Rule.IQSuperWeapons)) {
  1342. bool canfire = false;
  1343. for (int index = 0; index < Buildings.Count(); index++) {
  1344. BuildingClass * bldg = Buildings.Ptr(index);
  1345. if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) {
  1346. if (!bldg->HasFired) {
  1347. canfire = true;
  1348. break;
  1349. }
  1350. }
  1351. }
  1352. if (canfire) {
  1353. SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
  1354. /*
  1355. ** Flag the sidebar to be redrawn if necessary.
  1356. */
  1357. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1358. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1359. if (IsHuman) {
  1360. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_GPS, this);
  1361. }
  1362. } else {
  1363. if (this == PlayerPtr) {
  1364. Map.Add(RTTI_SPECIAL, SPC_GPS);
  1365. Map.Column[1].Flag_To_Redraw();
  1366. }
  1367. }
  1368. }
  1369. }
  1370. }
  1371. /*
  1372. ** Check to see if the chronosphere should be removed from the sidebar
  1373. ** because of outside circumstances. The chronosphere facility
  1374. ** being destroyed is a good example of this.
  1375. */
  1376. if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) {
  1377. if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) {
  1378. /*
  1379. ** Remove the chronosphere when there is no chronosphere facility.
  1380. ** Note that this will not remove the one time created chronosphere.
  1381. */
  1382. if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) {
  1383. if (this == PlayerPtr) {
  1384. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1385. if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) {
  1386. if (Map.IsTargettingMode == SPC_CHRONO2) {
  1387. TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport);
  1388. if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
  1389. } else {
  1390. Map.IsTargettingMode = SPC_NONE;
  1391. }
  1392. } else {
  1393. Map.IsTargettingMode = SPC_NONE;
  1394. }
  1395. }
  1396. #else
  1397. if (Map.IsTargettingMode == SPC_CHRONOSPHERE ||
  1398. Map.IsTargettingMode == SPC_CHRONO2) {
  1399. Map.IsTargettingMode = SPC_NONE;
  1400. }
  1401. #endif
  1402. Map.Column[1].Flag_To_Redraw();
  1403. }
  1404. IsRecalcNeeded = true;
  1405. }
  1406. }
  1407. } else {
  1408. /*
  1409. ** If there is no chronosphere present, but there is a chronosphere
  1410. ** facility available, then make the chronosphere available as well.
  1411. */
  1412. if ((ActiveBScan & STRUCTF_CHRONOSPHERE) &&
  1413. // (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) &&
  1414. (unsigned)Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level &&
  1415. // Control.TechLevel >= Rule.ChronoTechLevel &&
  1416. (IsHuman || IQ >= Rule.IQSuperWeapons)) {
  1417. SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
  1418. /*
  1419. ** Flag the sidebar to be redrawn if necessary.
  1420. */
  1421. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1422. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1423. if (IsHuman) {
  1424. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_CHRONOSPHERE, this);
  1425. }
  1426. } else {
  1427. if (this == PlayerPtr) {
  1428. Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE);
  1429. Map.Column[1].Flag_To_Redraw();
  1430. }
  1431. }
  1432. }
  1433. }
  1434. /*
  1435. ** Check to see if the iron curtain should be removed from the sidebar
  1436. ** because of outside circumstances. The iron curtain facility
  1437. ** being destroyed is a good example of this.
  1438. */
  1439. if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) {
  1440. if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) {
  1441. /*
  1442. ** Remove the iron curtain when there is no iron curtain facility.
  1443. ** Note that this will not remove the one time created iron curtain.
  1444. */
  1445. if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) {
  1446. if (this == PlayerPtr) {
  1447. if (Map.IsTargettingMode == SPC_IRON_CURTAIN) {
  1448. Map.IsTargettingMode = SPC_NONE;
  1449. }
  1450. Map.Column[1].Flag_To_Redraw();
  1451. }
  1452. IsRecalcNeeded = true;
  1453. }
  1454. }
  1455. } else {
  1456. /*
  1457. ** If there is no iron curtain present, but there is an iron curtain
  1458. ** facility available, then make the iron curtain available as well.
  1459. */
  1460. if ((ActiveBScan & STRUCTF_IRON_CURTAIN) &&
  1461. (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) &&
  1462. (IsHuman || IQ >= Rule.IQSuperWeapons)) {
  1463. SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
  1464. /*
  1465. ** Flag the sidebar to be redrawn if necessary.
  1466. */
  1467. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1468. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1469. if (IsHuman) {
  1470. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_IRON_CURTAIN, this);
  1471. }
  1472. } else {
  1473. if (this == PlayerPtr) {
  1474. Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN);
  1475. Map.Column[1].Flag_To_Redraw();
  1476. }
  1477. }
  1478. }
  1479. }
  1480. /*
  1481. ** Check to see if the sonar pulse should be removed from the sidebar
  1482. ** because of outside circumstances. The spied-upon enemy sub pen
  1483. ** being destroyed is a good example of this.
  1484. */
  1485. if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) {
  1486. int usspy = 1 << (Class->House);
  1487. bool present = false;
  1488. bool powered = false;
  1489. for (int q = 0; q < Buildings.Count() && !powered; q++) {
  1490. BuildingClass * bldg = Buildings.Ptr(q);
  1491. if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->Spied_By() & usspy) ) {
  1492. present = true;
  1493. powered = !(bldg->House->Power_Fraction() < 1);
  1494. }
  1495. }
  1496. if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) {
  1497. /*
  1498. ** Remove the sonar pulse when there is no spied-upon enemy sub pen.
  1499. ** Note that this will not remove the one time created sonar pulse.
  1500. */
  1501. if (SuperWeapon[SPC_SONAR_PULSE].Remove()) {
  1502. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1503. IsRecalcNeeded = true;
  1504. }
  1505. }
  1506. }
  1507. /*
  1508. ** Check to see if the nuclear weapon should be removed from the sidebar
  1509. ** because of outside circumstances. The missile silos
  1510. ** being destroyed is a good example of this.
  1511. */
  1512. if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) {
  1513. if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) {
  1514. /*
  1515. ** Remove the nuke when there is no missile silo.
  1516. ** Note that this will not remove the one time created nuke.
  1517. */
  1518. if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) {
  1519. if (this == PlayerPtr) {
  1520. if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) {
  1521. Map.IsTargettingMode = SPC_NONE;
  1522. }
  1523. Map.Column[1].Flag_To_Redraw();
  1524. }
  1525. IsRecalcNeeded = true;
  1526. }
  1527. } else {
  1528. /*
  1529. ** Allow the computer to fire the nuclear weapon when the weapon is
  1530. ** ready and the owner is the computer.
  1531. */
  1532. if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) {
  1533. Special_Weapon_AI(SPC_NUCLEAR_BOMB);
  1534. }
  1535. }
  1536. } else {
  1537. /*
  1538. ** If there is no nuclear missile present, but there is a missile
  1539. ** silo available, then make the missile available as well.
  1540. */
  1541. if ((ActiveBScan & STRUCTF_MSLO) &&
  1542. ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) &&
  1543. (IsHuman || IQ >= Rule.IQSuperWeapons)) {
  1544. SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
  1545. /*
  1546. ** Flag the sidebar to be redrawn if necessary.
  1547. */
  1548. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1549. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1550. if (IsHuman) {
  1551. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this);
  1552. }
  1553. } else {
  1554. if (this == PlayerPtr) {
  1555. Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB);
  1556. Map.Column[1].Flag_To_Redraw();
  1557. }
  1558. }
  1559. }
  1560. }
  1561. if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) {
  1562. if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
  1563. if (SuperWeapon[SPC_SPY_MISSION].Remove()) {
  1564. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1565. IsRecalcNeeded = true;
  1566. }
  1567. } else {
  1568. if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) {
  1569. Map.Column[1].Flag_To_Redraw();
  1570. }
  1571. if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) {
  1572. Special_Weapon_AI(SPC_SPY_MISSION);
  1573. }
  1574. }
  1575. } else {
  1576. if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) {
  1577. SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false);
  1578. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1579. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1580. if (IsHuman) {
  1581. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SPY_MISSION, this);
  1582. }
  1583. } else {
  1584. if (this == PlayerPtr) {
  1585. Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION);
  1586. Map.Column[1].Flag_To_Redraw();
  1587. }
  1588. }
  1589. }
  1590. }
  1591. if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) {
  1592. if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
  1593. if (SuperWeapon[SPC_PARA_BOMB].Remove()) {
  1594. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1595. IsRecalcNeeded = true;
  1596. }
  1597. } else {
  1598. if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) {
  1599. Special_Weapon_AI(SPC_PARA_BOMB);
  1600. }
  1601. }
  1602. } else {
  1603. if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) {
  1604. SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false);
  1605. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1606. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1607. if (IsHuman) {
  1608. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, this);
  1609. }
  1610. } else {
  1611. if (this == PlayerPtr) {
  1612. Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB);
  1613. Map.Column[1].Flag_To_Redraw();
  1614. }
  1615. }
  1616. }
  1617. }
  1618. if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) {
  1619. if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
  1620. if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) {
  1621. if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
  1622. IsRecalcNeeded = true;
  1623. }
  1624. } else {
  1625. if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) {
  1626. Special_Weapon_AI(SPC_PARA_INFANTRY);
  1627. }
  1628. }
  1629. } else {
  1630. if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) {
  1631. SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false);
  1632. // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
  1633. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  1634. if (IsHuman) {
  1635. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_INFANTRY, this);
  1636. }
  1637. } else {
  1638. if (this == PlayerPtr) {
  1639. Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY);
  1640. Map.Column[1].Flag_To_Redraw();
  1641. }
  1642. }
  1643. }
  1644. }
  1645. }
  1646. /***********************************************************************************************
  1647. * HouseClass::Attacked -- Lets player know if base is under attack. *
  1648. * *
  1649. * Call this function whenever a building is attacked (with malice). This function will *
  1650. * then announce to the player that his base is under attack. It checks to make sure that *
  1651. * this is referring to the player's house rather than the enemy's. *
  1652. * *
  1653. * INPUT: none *
  1654. * *
  1655. * OUTPUT: none *
  1656. * *
  1657. * WARNINGS: none *
  1658. * *
  1659. * HISTORY: *
  1660. * 12/27/1994 JLB : Created. *
  1661. *=============================================================================================*/
  1662. void HouseClass::Attacked(BuildingClass* source)
  1663. {
  1664. assert(Houses.ID(this) == ID);
  1665. #ifdef FIXIT_BASE_ANNOUNCE
  1666. if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) {
  1667. #else
  1668. if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) {
  1669. #endif
  1670. if (Session.Type == GAME_NORMAL) {
  1671. Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
  1672. } else {
  1673. Speak(VOX_BASE_UNDER_ATTACK, this);
  1674. }
  1675. // MBL 06.13.2020 - Timing change from 2 minute cooldown, per https://jaas.ea.com/browse/TDRA-6784
  1676. // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes
  1677. // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE/2); // 30 seconds as requested
  1678. SpeakAttackDelay = Options.Normalize_Delay( (TICKS_PER_MINUTE/2)+(TICKS_PER_SECOND*5) ); // Tweaked for accuracy
  1679. /*
  1680. ** If there is a trigger event associated with being attacked, process it
  1681. ** now.
  1682. */
  1683. for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) {
  1684. HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED);
  1685. }
  1686. }
  1687. }
  1688. /***********************************************************************************************
  1689. * HouseClass::Harvested -- Adds Tiberium to the harvest storage. *
  1690. * *
  1691. * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between *
  1692. * all storage capable buildings for the house. Harvested Tiberium adds to the credit *
  1693. * value of the house, but only up to the maximum storage capacity that the house can *
  1694. * currently maintain. *
  1695. * *
  1696. * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. *
  1697. * *
  1698. * OUTPUT: none *
  1699. * *
  1700. * WARNINGS: none *
  1701. * *
  1702. * HISTORY: *
  1703. * 01/25/1995 JLB : Created. *
  1704. *=============================================================================================*/
  1705. void HouseClass::Harvested(unsigned tiberium)
  1706. {
  1707. assert(Houses.ID(this) == ID);
  1708. long oldtib = Tiberium;
  1709. Tiberium += tiberium;
  1710. if (Tiberium > Capacity) {
  1711. Tiberium = Capacity;
  1712. IsMaxedOut = true;
  1713. }
  1714. HarvestedCredits += tiberium;
  1715. Silo_Redraw_Check(oldtib, Capacity);
  1716. }
  1717. /***********************************************************************************************
  1718. * HouseClass::Stole -- Accounts for the value of a captured building. *
  1719. * *
  1720. * Use this routine whenever a building is captured. It keeps track of the cost of the *
  1721. * building for use in the scoring routine, because you get an 'economy' boost for the *
  1722. * value of the stolen building (but you don't get the credit value for it.) *
  1723. * *
  1724. * INPUT: worth -- The worth of the building we captured (stole). *
  1725. * *
  1726. * OUTPUT: none *
  1727. * *
  1728. * WARNINGS: none *
  1729. * *
  1730. * HISTORY: *
  1731. * 09/05/1996 BWG : Created. *
  1732. *=============================================================================================*/
  1733. void HouseClass::Stole(unsigned worth)
  1734. {
  1735. assert(Houses.ID(this) == ID);
  1736. StolenBuildingsCredits += worth;
  1737. }
  1738. /***********************************************************************************************
  1739. * HouseClass::Available_Money -- Fetches the total credit worth of the house. *
  1740. * *
  1741. * Use this routine to determine the total credit value of the house. This is the sum of *
  1742. * the harvested Tiberium in storage and the initial unspent cash reserves. *
  1743. * *
  1744. * INPUT: none *
  1745. * *
  1746. * OUTPUT: Returns with the total credit value of the house. *
  1747. * *
  1748. * WARNINGS: none *
  1749. * *
  1750. * HISTORY: *
  1751. * 01/25/1995 JLB : Created. *
  1752. *=============================================================================================*/
  1753. long HouseClass::Available_Money(void) const
  1754. {
  1755. assert(Houses.ID(this) == ID);
  1756. return(Tiberium + Credits);
  1757. }
  1758. /***********************************************************************************************
  1759. * HouseClass::Spend_Money -- Removes money from the house. *
  1760. * *
  1761. * Use this routine to extract money from the house. Typically, this is a result of *
  1762. * production spending. The money is extracted from available cash reserves first. When *
  1763. * cash reserves are exhausted, then Tiberium is consumed. *
  1764. * *
  1765. * INPUT: money -- The amount of money to spend. *
  1766. * *
  1767. * OUTPUT: none *
  1768. * *
  1769. * WARNINGS: none *
  1770. * *
  1771. * HISTORY: *
  1772. * 01/25/1995 JLB : Created. *
  1773. * 06/20/1995 JLB : Spends Tiberium before spending cash. *
  1774. *=============================================================================================*/
  1775. void HouseClass::Spend_Money(unsigned money)
  1776. {
  1777. assert(Houses.ID(this) == ID);
  1778. long oldtib = Tiberium;
  1779. if (money > (unsigned)Tiberium) {
  1780. money -= (unsigned)Tiberium;
  1781. Tiberium = 0;
  1782. Credits -= money;
  1783. } else {
  1784. Tiberium -= money;
  1785. }
  1786. Silo_Redraw_Check(oldtib, Capacity);
  1787. CreditsSpent += money;
  1788. }
  1789. /***********************************************************************************************
  1790. * HouseClass::Refund_Money -- Refunds money to back to the house. *
  1791. * *
  1792. * Use this routine when money needs to be refunded back to the house. This can occur when *
  1793. * construction is aborted. At this point, the exact breakdown of Tiberium or initial *
  1794. * credits used for the orignal purchase is lost. Presume as much of the money is in the *
  1795. * form of Tiberium as storage capacity will allow. *
  1796. * *
  1797. * INPUT: money -- The number of credits to refund back to the house. *
  1798. * *
  1799. * OUTPUT: none *
  1800. * *
  1801. * WARNINGS: none *
  1802. * *
  1803. * HISTORY: *
  1804. * 01/25/1995 JLB : Created. *
  1805. * 06/01/1995 JLB : Refunded money is never lost *
  1806. *=============================================================================================*/
  1807. void HouseClass::Refund_Money(unsigned money)
  1808. {
  1809. assert(Houses.ID(this) == ID);
  1810. Credits += money;
  1811. }
  1812. /***********************************************************************************************
  1813. * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. *
  1814. * *
  1815. * Use this routine to adjust the maximum storage capacity for the house. This storage *
  1816. * capacity will limit the number of Tiberium credits that can be stored at any one time. *
  1817. * *
  1818. * INPUT: adjust -- The adjustment to the Tiberium storage capacity. *
  1819. * *
  1820. * inanger -- Is this a forced adjustment to capacity due to some hostile event? *
  1821. * *
  1822. * OUTPUT: Returns with the number of Tiberium credits lost. *
  1823. * *
  1824. * WARNINGS: none *
  1825. * *
  1826. * HISTORY: *
  1827. * 01/25/1995 JLB : Created. *
  1828. *=============================================================================================*/
  1829. int HouseClass::Adjust_Capacity(int adjust, bool inanger)
  1830. {
  1831. assert(Houses.ID(this) == ID);
  1832. long oldcap = Capacity;
  1833. int retval = 0;
  1834. Capacity += adjust;
  1835. Capacity = max(Capacity, 0L);
  1836. if (Tiberium > Capacity) {
  1837. retval = Tiberium - Capacity;
  1838. Tiberium = Capacity;
  1839. if (!inanger) {
  1840. Refund_Money(retval);
  1841. retval = 0;
  1842. } else {
  1843. IsMaxedOut = true;
  1844. }
  1845. }
  1846. Silo_Redraw_Check(Tiberium, oldcap);
  1847. return(retval);
  1848. }
  1849. /***********************************************************************************************
  1850. * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. *
  1851. * *
  1852. * Call this routine when either the capacity or tiberium levels change for a house. This *
  1853. * routine will determine if the aggregate tiberium storage level will result in the *
  1854. * silos changing their imagery. If this is detected, then all the silos for this house *
  1855. * are flagged to be redrawn. *
  1856. * *
  1857. * INPUT: oldtib -- Pre-change tiberium level. *
  1858. * *
  1859. * oldcap -- Pre-change tiberium storage capacity. *
  1860. * *
  1861. * OUTPUT: none *
  1862. * *
  1863. * WARNINGS: none *
  1864. * *
  1865. * HISTORY: *
  1866. * 02/02/1995 JLB : Created. *
  1867. *=============================================================================================*/
  1868. void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap)
  1869. {
  1870. assert(Houses.ID(this) == ID);
  1871. int oldratio = 0;
  1872. if (oldcap) oldratio = (oldtib * 5) / oldcap;
  1873. int newratio = 0;
  1874. if (Capacity) newratio = (Tiberium * 5) / Capacity;
  1875. if (oldratio != newratio) {
  1876. for (int index = 0; index < Buildings.Count(); index++) {
  1877. BuildingClass * b = Buildings.Ptr(index);
  1878. if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) {
  1879. b->Mark(MARK_CHANGE);
  1880. }
  1881. }
  1882. }
  1883. }
  1884. /***********************************************************************************************
  1885. * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
  1886. * *
  1887. * This routine will determine if the house number specified is a ally to this house. *
  1888. * *
  1889. * INPUT: house -- The house number to check to see if it is an ally. *
  1890. * *
  1891. * OUTPUT: Is the house an ally? *
  1892. * *
  1893. * WARNINGS: none *
  1894. * *
  1895. * HISTORY: *
  1896. * 05/08/1995 JLB : Created. *
  1897. *=============================================================================================*/
  1898. bool HouseClass::Is_Ally(HousesType house) const
  1899. {
  1900. assert(Houses.ID(this) == ID);
  1901. if (house != HOUSE_NONE) {
  1902. return(((1<<house) & Allies) != 0);
  1903. }
  1904. return(false);
  1905. }
  1906. /***********************************************************************************************
  1907. * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
  1908. * *
  1909. * This routine will examine the specified house and determine if it is an ally. *
  1910. * *
  1911. * INPUT: house -- Pointer to the house object to check for ally relationship. *
  1912. * *
  1913. * OUTPUT: Is the specified house an ally? *
  1914. * *
  1915. * WARNINGS: none *
  1916. * *
  1917. * HISTORY: *
  1918. * 05/08/1995 JLB : Created. *
  1919. *=============================================================================================*/
  1920. bool HouseClass::Is_Ally(HouseClass const * house) const
  1921. {
  1922. assert(Houses.ID(this) == ID);
  1923. if (house) {
  1924. return(Is_Ally(house->Class->House));
  1925. }
  1926. return(false);
  1927. }
  1928. /***********************************************************************************************
  1929. * HouseClass::Is_Ally -- Checks to see if the object is an ally. *
  1930. * *
  1931. * This routine will examine the specified object and return whether it is an ally or not. *
  1932. * *
  1933. * INPUT: object -- The object to examine to see if it is an ally. *
  1934. * *
  1935. * OUTPUT: Is the specified object an ally? *
  1936. * *
  1937. * WARNINGS: none *
  1938. * *
  1939. * HISTORY: *
  1940. * 05/08/1995 JLB : Created. *
  1941. *=============================================================================================*/
  1942. bool HouseClass::Is_Ally(ObjectClass const * object) const
  1943. {
  1944. assert(Houses.ID(this) == ID);
  1945. if (object) {
  1946. return(Is_Ally(object->Owner()));
  1947. }
  1948. return(false);
  1949. }
  1950. /***********************************************************************************************
  1951. * HouseClass::Make_Ally -- Make the specified house an ally. *
  1952. * *
  1953. * This routine will make the specified house an ally to this house. An allied house will *
  1954. * not be considered a threat or potential target. *
  1955. * *
  1956. * INPUT: house -- The house to make an ally of this house. *
  1957. * *
  1958. * OUTPUT: none *
  1959. * *
  1960. * WARNINGS: none *
  1961. * *
  1962. * HISTORY: *
  1963. * 05/08/1995 JLB : Created. *
  1964. * 08/08/1995 JLB : Breaks off combat when ally commences. *
  1965. * 10/17/1995 JLB : Added reveal base when allied. *
  1966. *=============================================================================================*/
  1967. void HouseClass::Make_Ally(HousesType house)
  1968. {
  1969. assert(Houses.ID(this) == ID);
  1970. if (Is_Allowed_To_Ally(house)) {
  1971. Allies |= (1L << house);
  1972. /*
  1973. ** Don't consider the newfound ally to be an enemy -- of course.
  1974. */
  1975. if (Enemy == house) {
  1976. Enemy = HOUSE_NONE;
  1977. }
  1978. if (ScenarioInit) {
  1979. Control.Allies |= (1L << house);
  1980. }
  1981. if (Session.Type != GAME_NORMAL && !ScenarioInit) {
  1982. HouseClass * hptr = HouseClass::As_Pointer(house);
  1983. /*
  1984. ** An alliance with another human player will cause the computer
  1985. ** players (if present) to become paranoid.
  1986. */
  1987. if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) {
  1988. // if (hptr != NULL && hptr->IsHuman) {
  1989. // if (!hptr->IsHuman) {
  1990. // hptr->Make_Ally(Class->House);
  1991. // }
  1992. Computer_Paranoid();
  1993. }
  1994. char buffer[80];
  1995. /*
  1996. ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure
  1997. ** that fighting will most likely stop when the cease fire begins.
  1998. */
  1999. for (int index = 0; index < Logic.Count(); index++) {
  2000. ObjectClass * object = Logic[index];
  2001. if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) {
  2002. TARGET target = ((TechnoClass *)object)->TarCom;
  2003. if (Target_Legal(target) && As_Techno(target) != NULL) {
  2004. if (Is_Ally(As_Techno(target))) {
  2005. ((TechnoClass *)object)->Assign_Target(TARGET_NONE);
  2006. }
  2007. }
  2008. }
  2009. }
  2010. /*
  2011. ** Cause all structures to be revealed to the house that has been
  2012. ** allied with.
  2013. */
  2014. if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) {
  2015. for (int index = 0; index < Buildings.Count(); index++) {
  2016. BuildingClass const * b = Buildings.Ptr(index);
  2017. if (b && !b->IsInLimbo && (HouseClass *)b->House == this) {
  2018. Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false);
  2019. }
  2020. }
  2021. }
  2022. if (IsHuman) {
  2023. sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName);
  2024. // sprintf(buffer, Text_String(TXT_HAS_ALLIED), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[((HouseClass::As_Pointer(house))->Class->House) - HOUSE_MULTI1]->Name);
  2025. Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay);
  2026. }
  2027. #if(TEN)
  2028. //
  2029. // Notify the TEN server of the new alliance
  2030. //
  2031. if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) {
  2032. Send_TEN_Alliance(hptr->IniName, 1);
  2033. }
  2034. #endif // TEN
  2035. #if(MPATH)
  2036. //
  2037. // Notify the MPATH server of the new alliance
  2038. //
  2039. //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) {
  2040. //Send_MPATH_Alliance(hptr->IniName, 1);
  2041. //}
  2042. #endif // MPATH
  2043. Map.Flag_To_Redraw(false);
  2044. }
  2045. }
  2046. }
  2047. /***********************************************************************************************
  2048. * HouseClass::Make_Enemy -- Make an enemy of the house specified. *
  2049. * *
  2050. * This routine will flag the house specified so that it will be an enemy to this house. *
  2051. * Enemy houses are legal targets for attack. *
  2052. * *
  2053. * INPUT: house -- The house to make an enemy of this house. *
  2054. * *
  2055. * OUTPUT: none *
  2056. * *
  2057. * WARNINGS: none *
  2058. * *
  2059. * HISTORY: *
  2060. * 05/08/1995 JLB : Created. *
  2061. * 07/27/1995 JLB : Making war is a bilateral action. *
  2062. *=============================================================================================*/
  2063. void HouseClass::Make_Enemy(HousesType house)
  2064. {
  2065. assert(Houses.ID(this) == ID);
  2066. if (house != HOUSE_NONE && Is_Ally(house)) {
  2067. HouseClass * enemy = HouseClass::As_Pointer(house);
  2068. Allies &= ~(1L << house);
  2069. if (ScenarioInit) {
  2070. Control.Allies &= !(1L << house);
  2071. }
  2072. /*
  2073. ** Breaking an alliance is a bilateral event.
  2074. */
  2075. if (enemy != NULL && enemy->Is_Ally(this)) {
  2076. enemy->Allies &= ~(1L << Class->House);
  2077. if (ScenarioInit) {
  2078. Control.Allies &= ~(1L << Class->House);
  2079. }
  2080. }
  2081. if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) {
  2082. char buffer[80];
  2083. sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName);
  2084. // sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name);
  2085. Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay);
  2086. Map.Flag_To_Redraw(false);
  2087. #if(TEN)
  2088. //
  2089. // Notify the TEN server of the broken alliance
  2090. //
  2091. if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) {
  2092. Send_TEN_Alliance(enemy->IniName, 0);
  2093. }
  2094. #endif // TEN
  2095. #if(MPATH)
  2096. //
  2097. // Notify the MPATH server of the broken alliance
  2098. //
  2099. //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) {
  2100. //Send_MPATH_Alliance(enemy->IniName, 0);
  2101. //}
  2102. #endif // MPATH
  2103. }
  2104. }
  2105. }
  2106. /***********************************************************************************************
  2107. * HouseClass::Remap_Table -- Fetches the remap table for this house object. *
  2108. * *
  2109. * This routine will return with the remap table to use when displaying an object owned *
  2110. * by this house. If the object is blushing (flashing), then the lightening remap table is *
  2111. * always used. The "unit" parameter allows proper remap selection for those houses that *
  2112. * have a different remap table for buildings or units. *
  2113. * *
  2114. * INPUT: blushing -- Is the object blushing (flashing)? *
  2115. * *
  2116. * remap -- The remap control value to use. *
  2117. * REMAP_NONE No remap pointer returned at all. *
  2118. * REMAP_NORMAL Return the remap pointer for this house. *
  2119. * REMAP_ALTERNATE (Nod solo play only -- forces red remap). *
  2120. * Multiplay returns same as REMAP_NORMAL *
  2121. * *
  2122. * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. *
  2123. * *
  2124. * WARNINGS: none *
  2125. * *
  2126. * HISTORY: *
  2127. * 05/08/1995 JLB : Created. *
  2128. * 10/25/1995 JLB : Uses remap control value. *
  2129. *=============================================================================================*/
  2130. unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const
  2131. {
  2132. assert(Houses.ID(this) == ID);
  2133. if (blushing) return(&Map.FadingLight[0]);
  2134. if (remap == REMAP_NONE) return(0);
  2135. return(ColorRemaps[RemapColor].RemapTable);
  2136. }
  2137. /***********************************************************************************************
  2138. * HouseClass::Suggested_New_Team -- Determine what team should be created. *
  2139. * *
  2140. * This routine examines the house condition and returns with the team that it thinks *
  2141. * should be created. The units that are not currently a member of a team are examined *
  2142. * to determine the team needed. *
  2143. * *
  2144. * INPUT: alertcheck -- Select from the auto-create team list. *
  2145. * *
  2146. * OUTPUT: Returns with a pointer to the team type that should be created. If no team should *
  2147. * be created, then NULL is returned. *
  2148. * *
  2149. * WARNINGS: none *
  2150. * *
  2151. * HISTORY: *
  2152. * 05/08/1995 JLB : Created. *
  2153. *=============================================================================================*/
  2154. TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck)
  2155. {
  2156. assert(Houses.ID(this) == ID);
  2157. return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck));
  2158. }
  2159. /***********************************************************************************************
  2160. * HouseClass::Adjust_Threat -- Adjust threat for the region specified. *
  2161. * *
  2162. * This routine is called when the threat rating for a region needs to change. The region *
  2163. * and threat adjustment are provided. *
  2164. * *
  2165. * INPUT: region -- The region that adjustment is to occur on. *
  2166. * *
  2167. * threat -- The threat adjustment to perform. *
  2168. * *
  2169. * OUTPUT: none *
  2170. * *
  2171. * WARNINGS: none *
  2172. * *
  2173. * HISTORY: *
  2174. * 05/08/1995 JLB : Created. *
  2175. *=============================================================================================*/
  2176. void HouseClass::Adjust_Threat(int region, int threat)
  2177. {
  2178. assert(Houses.ID(this) == ID);
  2179. static int _val[] = {
  2180. -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1,
  2181. -1, 0, 1,
  2182. MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1
  2183. };
  2184. static int _thr[] = {
  2185. 2, 1, 2,
  2186. 1, 0, 1,
  2187. 2, 1, 2
  2188. };
  2189. int neg;
  2190. int * val = &_val[0];
  2191. int * thr = &_thr[0];
  2192. if (threat < 0) {
  2193. threat = -threat;
  2194. neg = true;
  2195. } else {
  2196. neg = false;
  2197. }
  2198. for (int lp = 0; lp < 9; lp ++) {
  2199. Regions[region + *val].Adjust_Threat(threat >> *thr, neg);
  2200. val++;
  2201. thr++;
  2202. }
  2203. }
  2204. /***********************************************************************************************
  2205. * HouseClass::Begin_Production -- Starts production of the specified object type. *
  2206. * *
  2207. * This routine is called from the event system. It will start production for the object *
  2208. * type specified. This will be reflected in the sidebar as well as the house factory *
  2209. * tracking variables. *
  2210. * *
  2211. * INPUT: type -- The type of object to begin production on. *
  2212. * *
  2213. * id -- The subtype of object. *
  2214. * *
  2215. * OUTPUT: Returns with the reason why, or why not, production was started. *
  2216. * *
  2217. * WARNINGS: none *
  2218. * *
  2219. * HISTORY: *
  2220. * 05/08/1995 JLB : Created. *
  2221. * 10/21/1996 JLB : Handles max object case. *
  2222. *=============================================================================================*/
  2223. ProdFailType HouseClass::Begin_Production(RTTIType type, int id)
  2224. {
  2225. assert(Houses.ID(this) == ID);
  2226. int result = true;
  2227. bool initial_start = false;
  2228. FactoryClass * fptr;
  2229. TechnoTypeClass const * tech = Fetch_Techno_Type(type, id);
  2230. fptr = Fetch_Factory(type);
  2231. /*
  2232. ** If the house is already busy producing the requested object, then
  2233. ** return with this failure code, unless we are restarting production.
  2234. */
  2235. if (fptr != NULL) {
  2236. if (fptr->Is_Building()) {
  2237. return(PROD_CANT);
  2238. }
  2239. } else {
  2240. fptr = new FactoryClass();
  2241. if (!fptr) return(PROD_CANT);
  2242. Set_Factory(type, fptr);
  2243. result = fptr->Set(*tech, *this);
  2244. initial_start = true;
  2245. /*
  2246. ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts.
  2247. ** ST - 3/17/2020 2:03PM
  2248. */
  2249. if (!result) {
  2250. Set_Factory(type, NULL);
  2251. delete fptr;
  2252. fptr = NULL;
  2253. }
  2254. }
  2255. if (result) {
  2256. fptr->Start();
  2257. /*
  2258. ** Link this factory to the sidebar so that proper graphic feedback
  2259. ** can take place.
  2260. */
  2261. // Handle Glyphx multiplayer sidebar. ST - 8/14/2019 1:26PM
  2262. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  2263. if (IsHuman) {
  2264. Sidebar_Glyphx_Factory_Link(fptr->ID, type, id, this);
  2265. }
  2266. } else {
  2267. if (PlayerPtr == this) {
  2268. Map.Factory_Link(fptr->ID, type, id);
  2269. }
  2270. }
  2271. return(PROD_OK);
  2272. }
  2273. delete fptr;
  2274. return(PROD_CANT);
  2275. }
  2276. /***********************************************************************************************
  2277. * HouseClass::Suspend_Production -- Temporarily puts production on hold. *
  2278. * *
  2279. * This routine is called from the event system whenever the production of the specified *
  2280. * type needs to be suspended. The suspended production will be reflected in the sidebar *
  2281. * as well as in the house control structure. *
  2282. * *
  2283. * INPUT: type -- The type of object that production is being suspended for. *
  2284. * *
  2285. * OUTPUT: Returns why, or why not, production was suspended. *
  2286. * *
  2287. * WARNINGS: none *
  2288. * *
  2289. * HISTORY: *
  2290. * 05/08/1995 JLB : Created. *
  2291. *=============================================================================================*/
  2292. ProdFailType HouseClass::Suspend_Production(RTTIType type)
  2293. {
  2294. assert(Houses.ID(this) == ID);
  2295. FactoryClass * fptr = Fetch_Factory(type);
  2296. /*
  2297. ** If the house is already busy producing the requested object, then
  2298. ** return with this failure code.
  2299. */
  2300. if (fptr == NULL) return(PROD_CANT);
  2301. /*
  2302. ** Actually suspend the production.
  2303. */
  2304. fptr->Suspend();
  2305. /*
  2306. ** Tell the sidebar that it needs to be redrawn because of this.
  2307. */
  2308. if (PlayerPtr == this) {
  2309. Map.SidebarClass::IsToRedraw = true;
  2310. if (!RunningAsDLL) { // Don't force a redraw when running under GlyphX. PlayerPtr==this will always be true in this case, and we don't want to force a redraw even for AI players
  2311. Map.Flag_To_Redraw(false);
  2312. }
  2313. }
  2314. return(PROD_OK);
  2315. }
  2316. /***********************************************************************************************
  2317. * HouseClass::Abandon_Production -- Abandons production of item type specified. *
  2318. * *
  2319. * This routine is called from the event system whenever production must be abandoned for *
  2320. * the type specified. This will remove the factory and pending object from the sidebar as *
  2321. * well as from the house factory record. *
  2322. * *
  2323. * INPUT: type -- The object type that production is being suspended for. *
  2324. * *
  2325. * OUTPUT: Returns the reason why or why not, production was suspended. *
  2326. * *
  2327. * WARNINGS: none *
  2328. * *
  2329. * HISTORY: *
  2330. * 05/08/1995 JLB : Created. *
  2331. *=============================================================================================*/
  2332. ProdFailType HouseClass::Abandon_Production(RTTIType type)
  2333. {
  2334. assert(Houses.ID(this) == ID);
  2335. FactoryClass * fptr = Fetch_Factory(type);
  2336. /*
  2337. ** If there is no factory to abandon, then return with a failure code.
  2338. */
  2339. if (fptr == NULL) return(PROD_CANT);
  2340. /*
  2341. ** Tell the sidebar that it needs to be redrawn because of this.
  2342. */
  2343. // Handle Glyphx multiplayer sidebar. ST - 8/7/2019 10:18AM
  2344. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  2345. if (IsHuman) {
  2346. Sidebar_Glyphx_Abandon_Production(type, fptr->ID, this);
  2347. // Need to clear pending object here if legacy renderer enabled
  2348. if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) {
  2349. Map.PendingObjectPtr = 0;
  2350. Map.PendingObject = 0;
  2351. Map.PendingHouse = HOUSE_NONE;
  2352. Map.Set_Cursor_Shape(0);
  2353. }
  2354. }
  2355. } else {
  2356. if (PlayerPtr == this) {
  2357. Map.Abandon_Production(type, fptr->ID);
  2358. if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) {
  2359. Map.PendingObjectPtr = 0;
  2360. Map.PendingObject = 0;
  2361. Map.PendingHouse = HOUSE_NONE;
  2362. Map.Set_Cursor_Shape(0);
  2363. }
  2364. }
  2365. }
  2366. /*
  2367. ** Abandon production of the object.
  2368. */
  2369. fptr->Abandon();
  2370. Set_Factory(type, NULL);
  2371. delete fptr;
  2372. return(PROD_OK);
  2373. }
  2374. /***********************************************************************************************
  2375. * HouseClass::Special_Weapon_AI -- Fires special weapon. *
  2376. * *
  2377. * This routine will pick a good target to fire the special weapon specified. *
  2378. * *
  2379. * INPUT: id -- The special weapon id to fire. *
  2380. * *
  2381. * OUTPUT: none *
  2382. * *
  2383. * WARNINGS: none *
  2384. * *
  2385. * HISTORY: *
  2386. * 06/24/1995 PWG : Created. *
  2387. *=============================================================================================*/
  2388. void HouseClass::Special_Weapon_AI(SpecialWeaponType id)
  2389. {
  2390. assert(Houses.ID(this) == ID);
  2391. /*
  2392. ** Loop through all of the building objects on the map
  2393. ** and see which ones are available.
  2394. */
  2395. BuildingClass * bestptr = NULL;
  2396. int best = -1;
  2397. for (int index = 0; index < Buildings.Count(); index++) {
  2398. BuildingClass * b = Buildings.Ptr(index);
  2399. /*
  2400. ** If the building is valid, not in limbo, not in the process of
  2401. ** being destroyed and not our ally, then we can consider it.
  2402. */
  2403. if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) {
  2404. if (Percent_Chance(90) && (b->Value() > best || best == -1)) {
  2405. best = b->Value();
  2406. bestptr = b;
  2407. }
  2408. }
  2409. }
  2410. if (bestptr) {
  2411. CELL cell = Coord_Cell(bestptr->Center_Coord());
  2412. Place_Special_Blast(id, cell);
  2413. }
  2414. }
  2415. /***********************************************************************************************
  2416. * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. *
  2417. * *
  2418. * This routine will create a blast effect at the cell specified. This is the result of *
  2419. * the special weapons. *
  2420. * *
  2421. * INPUT: id -- The special weapon id number. *
  2422. * *
  2423. * cell -- The location where the special weapon attack is to occur. *
  2424. * *
  2425. * OUTPUT: Was the special weapon successfully fired at the location specified? *
  2426. * *
  2427. * WARNINGS: none *
  2428. * *
  2429. * HISTORY: *
  2430. * 05/18/1995 JLB : commented. *
  2431. * 07/25/1995 JLB : Added scatter effect for nuclear bomb. *
  2432. * 07/28/1995 JLB : Revamped to use super weapon class controller. *
  2433. *=============================================================================================*/
  2434. extern void Logic_Switch_Player_Context(ObjectClass *object);
  2435. extern void Logic_Switch_Player_Context(HouseClass *object);
  2436. extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
  2437. bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell)
  2438. {
  2439. assert(Houses.ID(this) == ID);
  2440. // Added. ST - 12/2/2019 11:26AM
  2441. bool fired = false;
  2442. const char *what = NULL;
  2443. BuildingClass * launchsite = 0;
  2444. AnimClass * anim = 0;
  2445. switch (id) {
  2446. case SPC_SONAR_PULSE:
  2447. // Automatically discharge the sonar pulse and uncloak all subs.
  2448. if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) {
  2449. SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr);
  2450. if (this == PlayerPtr) {
  2451. Map.Column[1].Flag_To_Redraw();
  2452. Map.Activate_Pulse();
  2453. }
  2454. Sound_Effect(VOC_SONAR);
  2455. IsRecalcNeeded = true;
  2456. fired = true;
  2457. what = "SONAR";
  2458. for (int index = 0; index < Vessels.Count(); index++) {
  2459. VesselClass * sub = Vessels.Ptr(index);
  2460. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2461. if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) {
  2462. #else
  2463. if (*sub == VESSEL_SS) {
  2464. #endif
  2465. sub->PulseCountDown = 15 * TICKS_PER_SECOND;
  2466. sub->Do_Uncloak();
  2467. }
  2468. }
  2469. }
  2470. break;
  2471. case SPC_NUCLEAR_BOMB:
  2472. if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) {
  2473. if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) {
  2474. BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST);
  2475. if (bullet) {
  2476. int celly = Cell_Y(cell);
  2477. celly -= 15;
  2478. if (celly < 1) celly = 1;
  2479. COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly));
  2480. if (!bullet->Unlimbo(start, DIR_S)) {
  2481. delete bullet;
  2482. }
  2483. SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr);
  2484. IsRecalcNeeded = true;
  2485. fired = true;
  2486. what = "NUKE";
  2487. if (this == PlayerPtr) {
  2488. Map.Column[1].Flag_To_Redraw();
  2489. Map.IsTargettingMode = SPC_NONE;
  2490. }
  2491. }
  2492. } else {
  2493. /*
  2494. ** Search for a suitable launch site for this missile.
  2495. */
  2496. launchsite = Find_Building(STRUCT_MSLO);
  2497. /*
  2498. ** If a launch site was found, then proceed with the normal launch
  2499. ** sequence.
  2500. */
  2501. if (launchsite) {
  2502. launchsite->Assign_Mission(MISSION_MISSILE);
  2503. launchsite->Commence();
  2504. NukeDest = cell;
  2505. }
  2506. if (this == PlayerPtr) {
  2507. Map.IsTargettingMode = SPC_NONE;
  2508. }
  2509. SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr);
  2510. IsRecalcNeeded = true;
  2511. fired = true;
  2512. what = "NUKE";
  2513. }
  2514. }
  2515. break;
  2516. case SPC_PARA_INFANTRY:
  2517. if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) {
  2518. TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF");
  2519. if (ttype == NULL) {
  2520. ttype = new TeamTypeClass;
  2521. if (ttype != NULL) {
  2522. strcpy(ttype->IniName, "@PINF");
  2523. ttype->IsTransient = true;
  2524. ttype->IsPrebuilt = false;
  2525. ttype->IsReinforcable = false;
  2526. ttype->Origin = WAYPT_SPECIAL;
  2527. ttype->MissionCount = 1;
  2528. ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT;
  2529. ttype->MissionList[0].Data.Value = WAYPT_SPECIAL;
  2530. ttype->ClassCount = 2;
  2531. ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers();
  2532. ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1);
  2533. ttype->Members[1].Quantity = 1;
  2534. ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER);
  2535. }
  2536. }
  2537. if (ttype != NULL) {
  2538. ttype->House = Class->House;
  2539. Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT);
  2540. Do_Reinforcements(ttype);
  2541. }
  2542. // Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1);
  2543. if (this == PlayerPtr) {
  2544. Map.IsTargettingMode = SPC_NONE;
  2545. }
  2546. SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr);
  2547. IsRecalcNeeded = true;
  2548. fired = true;
  2549. what = "PARA";
  2550. }
  2551. break;
  2552. case SPC_SPY_MISSION:
  2553. if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) {
  2554. Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell));
  2555. if (this == PlayerPtr) {
  2556. Map.IsTargettingMode = SPC_NONE;
  2557. }
  2558. SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr);
  2559. IsRecalcNeeded = true;
  2560. fired = true;
  2561. what = "SPY";
  2562. }
  2563. break;
  2564. case SPC_PARA_BOMB:
  2565. if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) {
  2566. Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE);
  2567. if (this == PlayerPtr) {
  2568. Map.IsTargettingMode = SPC_NONE;
  2569. }
  2570. SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr);
  2571. IsRecalcNeeded = true;
  2572. fired = true;
  2573. what = "PARABOMB";
  2574. }
  2575. break;
  2576. case SPC_IRON_CURTAIN:
  2577. if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) {
  2578. int x = Keyboard->MouseQX - Map.TacPixelX;
  2579. int y = Keyboard->MouseQY - Map.TacPixelY;
  2580. TechnoClass * tech = Map[cell].Cell_Techno(x, y);
  2581. if (tech) {
  2582. switch (tech->What_Am_I()) {
  2583. case RTTI_UNIT:
  2584. case RTTI_BUILDING:
  2585. case RTTI_VESSEL:
  2586. case RTTI_AIRCRAFT:
  2587. tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE;
  2588. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2589. if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) {
  2590. tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND;
  2591. }
  2592. #endif
  2593. tech->Mark(MARK_CHANGE);
  2594. Sound_Effect(VOC_IRON1, tech->Center_Coord());
  2595. if (this == PlayerPtr) {
  2596. Map.IsTargettingMode = SPC_NONE;
  2597. }
  2598. SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr);
  2599. break;
  2600. default:
  2601. break;
  2602. }
  2603. }
  2604. IsRecalcNeeded = true;
  2605. fired = true;
  2606. what = "IRON";
  2607. }
  2608. break;
  2609. case SPC_CHRONOSPHERE:
  2610. if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) {
  2611. int x = Keyboard->MouseQX - Map.TacPixelX;
  2612. int y = Keyboard->MouseQY - Map.TacPixelY;
  2613. TechnoClass * tech = Map[cell].Cell_Techno(x, y);
  2614. if (tech && Is_Ally(tech)) {
  2615. if (tech->What_Am_I() == RTTI_UNIT ||
  2616. tech->What_Am_I() == RTTI_INFANTRY ||
  2617. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  2618. (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) {
  2619. #else
  2620. (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) {
  2621. #endif
  2622. if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) {
  2623. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2624. bool porthim = true;
  2625. if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) {
  2626. porthim = false;
  2627. }
  2628. if (porthim) {
  2629. #endif
  2630. HouseClass* old_player_ptr = PlayerPtr;
  2631. Logic_Switch_Player_Context(this);
  2632. Map.IsTargettingMode = SPC_CHRONO2;
  2633. On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode);
  2634. Logic_Switch_Player_Context(old_player_ptr);
  2635. UnitToTeleport = tech->As_Target();
  2636. fired = true;
  2637. what = "CHRONO";
  2638. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2639. }
  2640. #endif
  2641. }
  2642. }
  2643. }
  2644. }
  2645. break;
  2646. case SPC_CHRONO2:
  2647. {
  2648. TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport);
  2649. CELL oldcell = cell;
  2650. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2651. if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) {
  2652. #else
  2653. if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) {
  2654. #endif
  2655. /*
  2656. ** Destroy any infantryman that gets teleported
  2657. */
  2658. if (tech->What_Am_I() == RTTI_INFANTRY) {
  2659. InfantryClass * inf = (InfantryClass *)tech;
  2660. inf->Mark(MARK_UP);
  2661. inf->Coord = Cell_Coord(cell);
  2662. inf->Mark(MARK_DOWN);
  2663. int damage = inf->Strength;
  2664. inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true);
  2665. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2666. } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) {
  2667. tech->Assign_Target(tech->As_Target());
  2668. #endif
  2669. } else {
  2670. /*
  2671. ** Warp the unit to the new location.
  2672. */
  2673. DriveClass * drive = (DriveClass *)tech;
  2674. drive->MoebiusCell = Coord_Cell(drive->Coord);
  2675. oldcell = drive->MoebiusCell;
  2676. drive->Teleport_To(cell);
  2677. drive->IsMoebius = true;
  2678. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2679. if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
  2680. drive->IsMoebius = false;
  2681. }
  2682. drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE;
  2683. if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
  2684. drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE;
  2685. }
  2686. #else
  2687. drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE;
  2688. #endif
  2689. Scen.Do_BW_Fade();
  2690. Sound_Effect(VOC_CHRONO, drive->Coord);
  2691. /*
  2692. ** Set active animation on Chronospheres.
  2693. */
  2694. for (int index = 0; index < Buildings.Count(); ++index) {
  2695. BuildingClass* building = Buildings.Ptr(index);
  2696. if (building != nullptr && building->IsActive && building->Owner() == Class->House && *building == STRUCT_CHRONOSPHERE) {
  2697. building->Begin_Mode(BSTATE_ACTIVE);
  2698. }
  2699. }
  2700. }
  2701. }
  2702. UnitToTeleport = TARGET_NONE;
  2703. if (this == PlayerPtr) {
  2704. Map.IsTargettingMode = SPC_NONE;
  2705. }
  2706. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2707. if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) {
  2708. #endif
  2709. SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr);
  2710. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2711. }
  2712. #endif
  2713. IsRecalcNeeded = true;
  2714. fired = true;
  2715. what = "CHRONO2";
  2716. /*
  2717. ** Now set a percentage chance that a time quake will occur.
  2718. */
  2719. if (!TimeQuake) {
  2720. TimeQuake = Percent_Chance(Rule.QuakeChance * 100);
  2721. }
  2722. /*
  2723. ** Now set a percentage chance that a chronal vortex will appear. It
  2724. ** might appear where the object teleported to or it might appear
  2725. ** where it teleported from -- random chance.
  2726. */
  2727. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2728. // Don't allow a vortex if the teleportation was due to a chrono tank.
  2729. if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK))
  2730. #endif
  2731. if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) {
  2732. int x = Random_Pick(0, Map.MapCellWidth-1);
  2733. int y = Random_Pick(0, Map.MapCellHeight-1);
  2734. ChronalVortex.Appear(Cell_Coord(XY_Cell(Map.MapCellX + x, Map.MapCellY + y)));
  2735. // if (Percent_Chance(50)) {
  2736. // ChronalVortex.Appear(Cell_Coord(oldcell));
  2737. // } else {
  2738. // ChronalVortex.Appear(Cell_Coord(cell));
  2739. // }
  2740. }
  2741. break;
  2742. }
  2743. }
  2744. /*
  2745. ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM
  2746. */
  2747. if (IsHuman && fired && what) {
  2748. On_Achievement_Event(this, "SUPERWEAPON_FIRED", what);
  2749. }
  2750. return(true);
  2751. }
  2752. /***********************************************************************************************
  2753. * HouseClass::Place_Object -- Places the object (building) at location specified. *
  2754. * *
  2755. * This routine is called when a building has been produced and now must be placed on *
  2756. * the map. When the player clicks on the map, this routine is ultimately called when the *
  2757. * event passes through the event queue system. *
  2758. * *
  2759. * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. *
  2760. * *
  2761. * *
  2762. * cell -- The location to place the object on the map. *
  2763. * *
  2764. * OUTPUT: Was the placement successful? *
  2765. * *
  2766. * WARNINGS: none *
  2767. * *
  2768. * HISTORY: *
  2769. * 05/18/1995 JLB : Created. *
  2770. *=============================================================================================*/
  2771. extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord);
  2772. bool HouseClass::Place_Object(RTTIType type, CELL cell)
  2773. {
  2774. assert(Houses.ID(this) == ID);
  2775. TechnoClass * tech = 0;
  2776. FactoryClass * factory = Fetch_Factory(type);
  2777. /*
  2778. ** Only if there is a factory active for this type, can it be "placed".
  2779. ** In the case of a missing factory, then this request is completely bogus --
  2780. ** ignore it. This might occur if, between two events to exit the same
  2781. ** object, the mouse was clicked on the sidebar to start building again.
  2782. ** The second placement event should NOT try to place the object that is
  2783. ** just starting construction.
  2784. */
  2785. if (factory && factory->Has_Completed()) {
  2786. tech = factory->Get_Object();
  2787. if (cell == -1) {
  2788. TechnoClass * pending = factory->Get_Object();
  2789. if (pending != NULL) {
  2790. #ifdef FIXIT_HELI_LANDING
  2791. /*
  2792. ** Try to find a place for the object to appear from. For helicopters, it has the
  2793. ** option of finding a nearby helipad if no helipads are free.
  2794. */
  2795. TechnoClass * builder = pending->Who_Can_Build_Me(false, false);
  2796. if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) {
  2797. builder = pending->Who_Can_Build_Me(true, false);
  2798. }
  2799. #else
  2800. bool intheory = false;
  2801. if (pending->What_Am_I() == RTTI_AIRCRAFT) {
  2802. /*
  2803. ** BG hack - helicopters don't need a specific building to
  2804. ** emerge from, in fact, they'll land next to a building if
  2805. ** need be.
  2806. */
  2807. if( !((AircraftClass *)pending)->Class->IsFixedWing) {
  2808. intheory = true;
  2809. }
  2810. }
  2811. TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false);
  2812. #endif
  2813. TechnoTypeClass const *object_type = pending->Techno_Type_Class();
  2814. if (builder != NULL && builder->Exit_Object(pending)) {
  2815. /*
  2816. ** Since the object has left the factory under its own power, delete
  2817. ** the production manager tied to this slot in the sidebar. Its job
  2818. ** has been completed.
  2819. */
  2820. factory->Set_Is_Blocked(false);
  2821. factory->Completed();
  2822. Abandon_Production(type);
  2823. /*
  2824. ** Could be tied to an achievement. ST - 11/11/2019 11:56AM
  2825. */
  2826. if (IsHuman) {
  2827. if (object_type) {
  2828. On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName);
  2829. }
  2830. if (pending->IsActive) {
  2831. On_Ping(this, pending->Center_Coord());
  2832. }
  2833. }
  2834. switch (pending->What_Am_I()) {
  2835. case RTTI_UNIT:
  2836. JustBuiltUnit = ((UnitClass*)pending)->Class->Type;
  2837. IsBuiltSomething = true;
  2838. break;
  2839. case RTTI_VESSEL:
  2840. JustBuiltVessel = ((VesselClass*)pending)->Class->Type;
  2841. IsBuiltSomething = true;
  2842. break;
  2843. case RTTI_INFANTRY:
  2844. JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type;
  2845. IsBuiltSomething = true;
  2846. break;
  2847. case RTTI_BUILDING:
  2848. JustBuiltStructure = ((BuildingClass*)pending)->Class->Type;
  2849. IsBuiltSomething = true;
  2850. break;
  2851. case RTTI_AIRCRAFT:
  2852. JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type;
  2853. IsBuiltSomething = true;
  2854. break;
  2855. }
  2856. } else {
  2857. /*
  2858. ** The object could not leave under it's own power. Just wait
  2859. ** until the player tries to place the object again.
  2860. */
  2861. /*
  2862. ** Flag that it's blocked so we can re-try the exit later.
  2863. ** This would have been a bad idea under the old peer-peer code since it would have pumped events into
  2864. ** the queue too often. ST - 2/25/2020 11:56AM
  2865. */
  2866. factory->Set_Is_Blocked(true);
  2867. return(false);
  2868. }
  2869. }
  2870. } else {
  2871. if (tech) {
  2872. TechnoClass * builder = tech->Who_Can_Build_Me(false, false);
  2873. if (builder) {
  2874. builder->Transmit_Message(RADIO_HELLO, tech);
  2875. if (tech->Unlimbo(Cell_Coord(cell))) {
  2876. factory->Completed();
  2877. Abandon_Production(type);
  2878. if (PlayerPtr == this) {
  2879. Sound_Effect(VOC_PLACE_BUILDING_DOWN);
  2880. Map.Set_Cursor_Shape(0);
  2881. Map.PendingObjectPtr = 0;
  2882. Map.PendingObject = 0;
  2883. Map.PendingHouse = HOUSE_NONE;
  2884. }
  2885. return(true);
  2886. } else {
  2887. if (this == PlayerPtr) {
  2888. Speak(VOX_DEPLOY);
  2889. }
  2890. }
  2891. builder->Transmit_Message(RADIO_OVER_OUT);
  2892. }
  2893. return(false);
  2894. } else {
  2895. // Play a bad sound here?
  2896. return(false);
  2897. }
  2898. }
  2899. }
  2900. return(true);
  2901. }
  2902. /***********************************************************************************************
  2903. * HouseClass::Manual_Place -- Inform display system of building placement mode. *
  2904. * *
  2905. * This routine will inform the display system that building placement mode has begun. *
  2906. * The cursor will be created that matches the layout of the building shape. *
  2907. * *
  2908. * INPUT: builder -- The factory that is building this object. *
  2909. * *
  2910. * object -- The building that is going to be placed down on the map. *
  2911. * *
  2912. * OUTPUT: Was the building placement mode successfully initiated? *
  2913. * *
  2914. * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games *
  2915. * is affected. *
  2916. * *
  2917. * HISTORY: *
  2918. * 05/04/1995 JLB : Created. *
  2919. * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. *
  2920. *=============================================================================================*/
  2921. bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object)
  2922. {
  2923. assert(Houses.ID(this) == ID);
  2924. if (this == PlayerPtr && !Map.PendingObject && builder && object) {
  2925. /*
  2926. ** Ensures that object selection doesn't remain when
  2927. ** building placement takes place.
  2928. */
  2929. Unselect_All();
  2930. Map.Repair_Mode_Control(0);
  2931. Map.Sell_Mode_Control(0);
  2932. Map.PendingObject = object->Class;
  2933. Map.PendingObjectPtr = object;
  2934. Map.PendingHouse = Class->House;
  2935. Map.Set_Cursor_Shape(object->Occupy_List(true));
  2936. Map.Set_Cursor_Pos(Coord_Cell(builder->Coord));
  2937. builder->Mark(MARK_CHANGE);
  2938. return(true);
  2939. }
  2940. return(false);
  2941. }
  2942. /***************************************************************************
  2943. * HouseClass::Clobber_All -- removes all objects for this house *
  2944. * *
  2945. * INPUT: *
  2946. * none. *
  2947. * *
  2948. * OUTPUT: *
  2949. * none. *
  2950. * *
  2951. * WARNINGS: *
  2952. * This routine removes the house itself, so the multiplayer code *
  2953. * must not rely on there being "empty" houses lying around. *
  2954. * *
  2955. * HISTORY: *
  2956. * 05/16/1995 BRR : Created. *
  2957. * 06/09/1995 JLB : Handles aircraft. *
  2958. *=========================================================================*/
  2959. void HouseClass::Clobber_All(void)
  2960. {
  2961. assert(Houses.ID(this) == ID);
  2962. int i;
  2963. for (i = 0; i < ::Aircraft.Count(); i++) {
  2964. if (::Aircraft.Ptr(i)->House == this) {
  2965. delete ::Aircraft.Ptr(i);
  2966. i--;
  2967. }
  2968. }
  2969. for (i = 0; i < ::Units.Count(); i++) {
  2970. if (::Units.Ptr(i)->House == this) {
  2971. delete ::Units.Ptr(i);
  2972. i--;
  2973. }
  2974. }
  2975. for (i = 0; i < ::Vessels.Count(); i++) {
  2976. if (::Vessels.Ptr(i)->House == this) {
  2977. delete ::Vessels.Ptr(i);
  2978. i--;
  2979. }
  2980. }
  2981. for (i = 0; i < Infantry.Count(); i++) {
  2982. if (Infantry.Ptr(i)->House == this) {
  2983. delete Infantry.Ptr(i);
  2984. i--;
  2985. }
  2986. }
  2987. for (i = 0; i < Buildings.Count(); i++) {
  2988. if (Buildings.Ptr(i)->House == this) {
  2989. delete Buildings.Ptr(i);
  2990. i--;
  2991. }
  2992. }
  2993. for (i = 0; i < TeamTypes.Count(); i++) {
  2994. if (TeamTypes.Ptr(i)->House == Class->House) {
  2995. delete TeamTypes.Ptr(i);
  2996. i--;
  2997. }
  2998. }
  2999. for (i = 0; i < Triggers.Count(); i++) {
  3000. if (Triggers.Ptr(i)->Class->House == Class->House) {
  3001. delete Triggers.Ptr(i);
  3002. i--;
  3003. }
  3004. }
  3005. for (i = 0; i < TriggerTypes.Count(); i++) {
  3006. if (TriggerTypes.Ptr(i)->House == Class->House) {
  3007. delete TriggerTypes.Ptr(i);
  3008. i--;
  3009. }
  3010. }
  3011. delete this;
  3012. }
  3013. /***********************************************************************************************
  3014. * HouseClass::Detach -- Removes specified object from house tracking systems. *
  3015. * *
  3016. * This routine is called when an object is to be removed from the game system. If the *
  3017. * specified object is part of the house tracking system, then it will be removed. *
  3018. * *
  3019. * INPUT: target -- The target value of the object that is to be removed from the game. *
  3020. * *
  3021. * all -- Is the target going away for good as opposed to just cloaking/hiding? *
  3022. * *
  3023. * OUTPUT: none *
  3024. * *
  3025. * WARNINGS: none *
  3026. * *
  3027. * HISTORY: *
  3028. * 05/18/1995 JLB : commented *
  3029. *=============================================================================================*/
  3030. void HouseClass::Detach(TARGET target, bool )
  3031. {
  3032. assert(Houses.ID(this) == ID);
  3033. if (ToCapture == target) {
  3034. ToCapture = TARGET_NONE;
  3035. }
  3036. if (Is_Target_Trigger(target)) {
  3037. HouseTriggers[ID].Delete(As_Trigger(target));
  3038. }
  3039. }
  3040. /***********************************************************************************************
  3041. * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. *
  3042. * *
  3043. * This routine will examine the enemy houses and if there is a building owned by one *
  3044. * of those house, true will be returned. *
  3045. * *
  3046. * INPUT: btype -- The building type to check for. *
  3047. * *
  3048. * OUTPUT: Does a building of the specified type exist for one of the enemy houses? *
  3049. * *
  3050. * WARNINGS: none *
  3051. * *
  3052. * HISTORY: *
  3053. * 05/23/1995 JLB : Created. *
  3054. *=============================================================================================*/
  3055. bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const
  3056. {
  3057. assert(Houses.ID(this) == ID);
  3058. int bflag = 1L << btype;
  3059. for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
  3060. HouseClass * house = HouseClass::As_Pointer(index);
  3061. if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) {
  3062. return(true);
  3063. }
  3064. }
  3065. return(false);
  3066. }
  3067. /***********************************************************************************************
  3068. * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. *
  3069. * *
  3070. * This routine will examine the house status and return with a techno type pointer to *
  3071. * the object type that it thinks should be created. The type is restricted to match the *
  3072. * type specified. Typical use of this routine is by computer controlled factories. *
  3073. * *
  3074. * INPUT: objecttype -- The type of object to restrict the scan for. *
  3075. * *
  3076. * kennel -- Is this from a kennel? There are special hacks to ensure that only *
  3077. * dogs can be produced from a kennel. *
  3078. * *
  3079. * OUTPUT: Returns with a pointer to a techno type for the object type that should be *
  3080. * created. If no object should be created, then NULL is returned. *
  3081. * *
  3082. * WARNINGS: This is a time consuming routine. Only call when necessary. *
  3083. * *
  3084. * HISTORY: *
  3085. * 05/23/1995 JLB : Created. *
  3086. *=============================================================================================*/
  3087. TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const
  3088. {
  3089. assert(Houses.ID(this) == ID);
  3090. TechnoTypeClass const * techno = NULL;
  3091. switch (objecttype) {
  3092. case RTTI_AIRCRAFT:
  3093. case RTTI_AIRCRAFTTYPE:
  3094. if (BuildAircraft != AIRCRAFT_NONE) {
  3095. return(&AircraftTypeClass::As_Reference(BuildAircraft));
  3096. }
  3097. return(NULL);
  3098. case RTTI_VESSEL:
  3099. case RTTI_VESSELTYPE:
  3100. if (BuildVessel != VESSEL_NONE) {
  3101. return(&VesselTypeClass::As_Reference(BuildVessel));
  3102. }
  3103. return(NULL);
  3104. /*
  3105. ** Unit construction is based on the rule that up to twice the number required
  3106. ** to fill all teams will be created.
  3107. */
  3108. case RTTI_UNIT:
  3109. case RTTI_UNITTYPE:
  3110. if (BuildUnit != UNIT_NONE) {
  3111. return(&UnitTypeClass::As_Reference(BuildUnit));
  3112. }
  3113. return(NULL);
  3114. /*
  3115. ** Infantry construction is based on the rule that up to twice the number required
  3116. ** to fill all teams will be created.
  3117. */
  3118. case RTTI_INFANTRY:
  3119. case RTTI_INFANTRYTYPE:
  3120. if (BuildInfantry != INFANTRY_NONE) {
  3121. if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL);
  3122. if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL);
  3123. return(&InfantryTypeClass::As_Reference(BuildInfantry));
  3124. }
  3125. return(NULL);
  3126. /*
  3127. ** Building construction is based upon the preconstruction list.
  3128. */
  3129. case RTTI_BUILDING:
  3130. case RTTI_BUILDINGTYPE:
  3131. if (BuildStructure != STRUCT_NONE) {
  3132. return(&BuildingTypeClass::As_Reference(BuildStructure));
  3133. }
  3134. return(NULL);
  3135. }
  3136. return(techno);
  3137. }
  3138. /***********************************************************************************************
  3139. * HouseClass::Flag_Remove -- Removes the flag from the specified target. *
  3140. * *
  3141. * This routine will remove the flag attached to the specified target object or cell. *
  3142. * Call this routine before placing the object down. This is called inherently by the *
  3143. * the Flag_Attach() functions. *
  3144. * *
  3145. * INPUT: target -- The target that the flag was attached to but will be removed from. *
  3146. * *
  3147. * set_home -- if true, clears the flag's waypoint designation *
  3148. * *
  3149. * OUTPUT: Was the flag successfully removed from the specified target? *
  3150. * *
  3151. * WARNINGS: none *
  3152. * *
  3153. * HISTORY: *
  3154. * 05/23/1995 JLB : Created. *
  3155. *=============================================================================================*/
  3156. bool HouseClass::Flag_Remove(TARGET target, bool set_home)
  3157. {
  3158. assert(Houses.ID(this) == ID);
  3159. bool rc = false;
  3160. if (Target_Legal(target)) {
  3161. /*
  3162. ** Remove the flag from a unit
  3163. */
  3164. UnitClass * object = As_Unit(target);
  3165. if (object) {
  3166. rc = object->Flag_Remove();
  3167. if (rc && FlagLocation == target) {
  3168. FlagLocation = TARGET_NONE;
  3169. }
  3170. } else {
  3171. /*
  3172. ** Remove the flag from a cell
  3173. */
  3174. CELL cell = As_Cell(target);
  3175. if (Map.In_Radar(cell)) {
  3176. rc = Map[cell].Flag_Remove();
  3177. if (rc && FlagLocation == target) {
  3178. FlagLocation = TARGET_NONE;
  3179. }
  3180. }
  3181. }
  3182. /*
  3183. ** Handle the flag home cell:
  3184. ** If 'set_home' is set, clear the home value & the cell's overlay
  3185. */
  3186. if (set_home) {
  3187. if (FlagHome != 0) {
  3188. Map[FlagHome].Overlay = OVERLAY_NONE;
  3189. Map.Flag_Cell(FlagHome);
  3190. FlagHome = 0;
  3191. }
  3192. }
  3193. }
  3194. return(rc);
  3195. }
  3196. /***********************************************************************************************
  3197. * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). *
  3198. * *
  3199. * This routine will attach the house flag to the location specified. If the location *
  3200. * cannot contain the flag, then a suitable nearby location will be selected. *
  3201. * *
  3202. * INPUT: cell -- The desired cell location to place the flag. *
  3203. * *
  3204. * set_home -- if true, resets the flag's waypoint designation *
  3205. * *
  3206. * OUTPUT: Was the flag successfully placed? *
  3207. * *
  3208. * WARNINGS: The cell picked for the flag might very likely not be the cell requested. *
  3209. * Check the FlagLocation value to determine the final cell resting spot. *
  3210. * *
  3211. * HISTORY: *
  3212. * 05/23/1995 JLB : Created. *
  3213. * 10/08/1996 JLB : Uses map nearby cell scanning handler. *
  3214. *=============================================================================================*/
  3215. bool HouseClass::Flag_Attach(CELL cell, bool set_home)
  3216. {
  3217. assert(Houses.ID(this) == ID);
  3218. bool rc;
  3219. bool clockwise;
  3220. /*
  3221. ** Randomly decide if we're going to search cells clockwise or counter-
  3222. ** clockwise
  3223. */
  3224. clockwise = Percent_Chance(50);
  3225. /*
  3226. ** Only continue if this cell is a legal placement cell.
  3227. */
  3228. if (Map.In_Radar(cell)) {
  3229. /*
  3230. ** If the flag already exists, then it must be removed from the object
  3231. ** it is attached to.
  3232. */
  3233. Flag_Remove(FlagLocation, set_home);
  3234. /*
  3235. ** Attach the flag to the cell specified. If it can't be placed, then pick
  3236. ** a nearby cell where it can be placed.
  3237. */
  3238. CELL newcell = cell;
  3239. rc = Map[newcell].Flag_Place(Class->House);
  3240. if (!rc) {
  3241. newcell = Map.Nearby_Location(cell, SPEED_TRACK, -1, MZONE_NORMAL, true);
  3242. if (newcell != 0) {
  3243. rc = Map[newcell].Flag_Place(Class->House);
  3244. }
  3245. #ifdef OBSOLETE
  3246. /*
  3247. ** Loop for increasing distance from the desired cell.
  3248. ** For each distance, randomly pick a starting direction. Between
  3249. ** this and the clockwise/counterclockwise random value, the flag
  3250. ** should appear to be placed fairly randomly.
  3251. */
  3252. for (int dist = 1; dist < 32; dist++) {
  3253. FacingType fcounter;
  3254. FacingType rot;
  3255. /*
  3256. ** Clockwise search.
  3257. */
  3258. if (clockwise) {
  3259. rot = Random_Pick(FACING_N, FACING_NW);
  3260. for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) {
  3261. newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256));
  3262. if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) {
  3263. dist = 32;
  3264. rc = true;
  3265. break;
  3266. }
  3267. rot++;
  3268. if (rot > FACING_NW) rot = FACING_N;
  3269. }
  3270. } else {
  3271. /*
  3272. ** Counter-clockwise search
  3273. */
  3274. rot = Random_Pick(FACING_N, FACING_NW);
  3275. for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) {
  3276. newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256));
  3277. if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) {
  3278. dist = 32;
  3279. rc = true;
  3280. break;
  3281. }
  3282. rot--;
  3283. if (rot < FACING_N)
  3284. rot = FACING_NW;
  3285. }
  3286. }
  3287. }
  3288. #endif
  3289. }
  3290. /*
  3291. ** If we've found a spot for the flag, place the flag at the new cell.
  3292. ** if 'set_home' is set, OR this house has no current flag home cell,
  3293. ** mark that cell as this house's flag home cell. Otherwise fall back
  3294. ** on returning the flag to its home.
  3295. */
  3296. if (rc) {
  3297. FlagLocation = As_Target(newcell);
  3298. if (set_home || FlagHome == 0) {
  3299. Map[newcell].Overlay = OVERLAY_FLAG_SPOT;
  3300. Map[newcell].OverlayData = 0;
  3301. Map[newcell].Recalc_Attributes();
  3302. FlagHome = newcell;
  3303. }
  3304. }
  3305. else if (FlagHome != 0) {
  3306. rc = Map[FlagHome].Flag_Place(Class->House);
  3307. }
  3308. return(rc);
  3309. }
  3310. return(false);
  3311. }
  3312. /***********************************************************************************************
  3313. * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. *
  3314. * *
  3315. * This routine will attach the house flag to the specified unit. This routine is called *
  3316. * when a unit drives over a cell containing a flag. *
  3317. * *
  3318. * INPUT: object -- Pointer to the object that the house flag is to be attached to. *
  3319. * *
  3320. * set_home -- if true, clears the flag's waypoint designation *
  3321. * *
  3322. * OUTPUT: Was the flag attached successfully? *
  3323. * *
  3324. * WARNINGS: none *
  3325. * *
  3326. * HISTORY: *
  3327. * 05/23/1995 JLB : Created. *
  3328. *=============================================================================================*/
  3329. bool HouseClass::Flag_Attach(UnitClass * object, bool set_home)
  3330. {
  3331. assert(Houses.ID(this) == ID);
  3332. if (object && !object->IsInLimbo) {
  3333. Flag_Remove(FlagLocation, set_home);
  3334. /*
  3335. ** Attach the flag to the object.
  3336. */
  3337. object->Flag_Attach(Class->House);
  3338. FlagLocation = object->As_Target();
  3339. return(true);
  3340. }
  3341. return(false);
  3342. }
  3343. extern void On_Defeated_Message(const char* message, float timeout_seconds);
  3344. /***************************************************************************
  3345. * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated *
  3346. * *
  3347. * INPUT: *
  3348. * none. *
  3349. * *
  3350. * OUTPUT: *
  3351. * none. *
  3352. * *
  3353. * WARNINGS: *
  3354. * none. *
  3355. * *
  3356. * HISTORY: *
  3357. * 05/25/1995 BRR : Created. *
  3358. *=========================================================================*/
  3359. void HouseClass::MPlayer_Defeated(void)
  3360. {
  3361. assert(Houses.ID(this) == ID);
  3362. char txt[80];
  3363. int i,j;
  3364. unsigned char id;
  3365. HouseClass * hptr;
  3366. HouseClass * hptr2;
  3367. int num_alive;
  3368. int num_humans;
  3369. int all_allies;
  3370. /*
  3371. ** Set the defeat flag for this house
  3372. */
  3373. IsDefeated = true;
  3374. /*
  3375. ** If this is a computer controlled house, then all computer controlled
  3376. ** houses become paranoid.
  3377. */
  3378. if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) {
  3379. Computer_Paranoid();
  3380. }
  3381. /*
  3382. ** Remove this house's flag & flag home cell
  3383. */
  3384. if (Special.IsCaptureTheFlag) {
  3385. if (FlagLocation) {
  3386. Flag_Remove(FlagLocation, true);
  3387. } else {
  3388. if (FlagHome != 0) {
  3389. Flag_Remove(FlagHome, true);
  3390. }
  3391. }
  3392. }
  3393. /*
  3394. ** Remove any one-time superweapons the player might have.
  3395. */
  3396. for (i = SPC_FIRST; i < SPC_COUNT; i++) {
  3397. SuperWeapon[i].Remove(true);
  3398. }
  3399. /*
  3400. ** If this is me:
  3401. ** - Set MPlayerObiWan, so I can only send messages to all players, and
  3402. ** not just one (so I can't be obnoxiously omnipotent)
  3403. ** - Reveal the map
  3404. ** - Add my defeat message
  3405. */
  3406. if (PlayerPtr == this) {
  3407. Session.ObiWan = 1;
  3408. HidPage.Clear();
  3409. Map.Flag_To_Redraw(true);
  3410. /*
  3411. ** Pop up a message showing that I was defeated
  3412. */
  3413. sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName);
  3414. if (Session.Type == GAME_NORMAL) {
  3415. Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx,
  3416. TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
  3417. }
  3418. Map.Flag_To_Redraw(false);
  3419. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  3420. int timeout = Rule.MessageDelay * TICKS_PER_MINUTE;
  3421. On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE);
  3422. Sound_Effect(VOC_INCOMING_MESSAGE);
  3423. }
  3424. } else {
  3425. /*
  3426. ** If it wasn't me, find out who was defeated
  3427. */
  3428. if (IsHuman) {
  3429. sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName);
  3430. //Session.Messages.Add_Message(NULL, 0, txt, RemapColor,
  3431. // TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
  3432. Map.Flag_To_Redraw(false);
  3433. RedrawOptionsMenu = true;
  3434. int timeout = Rule.MessageDelay * TICKS_PER_MINUTE;
  3435. On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE);
  3436. Sound_Effect(VOC_INCOMING_MESSAGE);
  3437. }
  3438. }
  3439. /*
  3440. ** Find out how many players are left alive.
  3441. */
  3442. num_alive = 0;
  3443. num_humans = 0;
  3444. for (i = 0; i < Session.MaxPlayers; i++) {
  3445. hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i));
  3446. if (hptr && !hptr->IsDefeated) {
  3447. if (hptr->IsHuman) {
  3448. num_humans++;
  3449. }
  3450. num_alive++;
  3451. }
  3452. }
  3453. /*
  3454. ** If all the houses left alive are allied with each other, then in reality
  3455. ** there's only one player left:
  3456. */
  3457. all_allies = 1;
  3458. for (i = 0; i < Session.MaxPlayers; i++) {
  3459. /*
  3460. ** Get a pointer to this house
  3461. */
  3462. hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i));
  3463. if (!hptr || hptr->IsDefeated)
  3464. continue;
  3465. /*
  3466. ** Loop through all houses; if there's one left alive that this house
  3467. ** isn't allied with, then all_allies will be false
  3468. */
  3469. for (j = 0; j < Session.MaxPlayers; j++) {
  3470. hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j));
  3471. if (!hptr2) {
  3472. continue;
  3473. }
  3474. if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) {
  3475. all_allies = 0;
  3476. break;
  3477. }
  3478. }
  3479. if (!all_allies) {
  3480. break;
  3481. }
  3482. }
  3483. /*
  3484. ** If all houses left are allies, set 'num_alive' to 1; game over.
  3485. */
  3486. if (all_allies) {
  3487. num_alive = 1;
  3488. }
  3489. /*
  3490. ** If there's only one human player left or no humans left, the game is over:
  3491. ** - Determine whether this player wins or loses, based on the state of the
  3492. ** player's IsDefeated flag
  3493. ** - Find all players' indices in the Session.Score array
  3494. ** - Tally up scores for this game
  3495. */
  3496. if (num_alive == 1 || num_humans == 0) {
  3497. if (PlayerPtr->IsDefeated) {
  3498. PlayerLoses = true;
  3499. } else {
  3500. PlayerWins = true;
  3501. }
  3502. /*
  3503. ** Add up the scores
  3504. */
  3505. Tally_Score();
  3506. /*
  3507. ** Destroy all the IPX connections, since we have to go through the rest
  3508. ** of the Main_Loop() before we detect that the game is over, and we'll
  3509. ** end up waiting for frame sync packets from the other machines.
  3510. */
  3511. if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) {
  3512. i = 0;
  3513. while (Ipx.Num_Connections() && (i++ < 1000) ) {
  3514. id = Ipx.Connection_ID(0);
  3515. Ipx.Delete_Connection(id);
  3516. }
  3517. Session.NumPlayers = 0;
  3518. }
  3519. #if(TEN)
  3520. //
  3521. // Tell the TEN server who won
  3522. //
  3523. if (Session.Type == GAME_TEN) {
  3524. Send_TEN_Win_Packet();
  3525. }
  3526. #endif // TEN
  3527. #if(MPATH)
  3528. //
  3529. // Tell the MPATH server who won
  3530. //
  3531. if (Session.Type == GAME_MPATH) {
  3532. FILE *fp;
  3533. fp = fopen("winner.txt","wt");
  3534. if (fp) {
  3535. for (i = 0; i < Session.Players.Count(); i++) {
  3536. hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
  3537. if (!hptr->IsDefeated) {
  3538. fprintf(fp,"%s\n",hptr->IniName);
  3539. }
  3540. }
  3541. fclose(fp);
  3542. }
  3543. }
  3544. #endif // MPATH
  3545. }
  3546. }
  3547. /***************************************************************************
  3548. * HouseClass::Tally_Score -- Fills in the score system for this round *
  3549. * *
  3550. * INPUT: *
  3551. * none. *
  3552. * *
  3553. * OUTPUT: *
  3554. * none. *
  3555. * *
  3556. * WARNINGS: *
  3557. * none. *
  3558. * *
  3559. * HISTORY: *
  3560. * 11/29/1995 BRR : Created. *
  3561. *=========================================================================*/
  3562. void HouseClass::Tally_Score(void)
  3563. {
  3564. HousesType house;
  3565. HousesType house2;
  3566. HouseClass * hptr;
  3567. int score_index;
  3568. int i,j,k;
  3569. int max_index;
  3570. int max_count;
  3571. int count;
  3572. /*
  3573. ** Loop through all houses, tallying up each player's score
  3574. */
  3575. for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  3576. hptr = HouseClass::As_Pointer(house);
  3577. /*
  3578. ** Skip this house if it's not human.
  3579. */
  3580. if (!hptr || !hptr->IsHuman) {
  3581. continue;
  3582. }
  3583. /*
  3584. ** Now find out where this player is in the score array
  3585. */
  3586. score_index = -1;
  3587. for (i = 0; i < Session.NumScores; i++) {
  3588. if (!stricmp(hptr->IniName, Session.Score[i].Name)) {
  3589. score_index = i;
  3590. break;
  3591. }
  3592. }
  3593. /*
  3594. ** If the index is still -1, the name wasn't found; add a new entry.
  3595. */
  3596. if (score_index == -1) {
  3597. /*
  3598. ** Just add this player to the end of the array, if there's room
  3599. */
  3600. if (Session.NumScores < MAX_MULTI_NAMES) {
  3601. score_index = Session.NumScores;
  3602. Session.NumScores++;
  3603. }
  3604. /*
  3605. ** If there's not room, we have to remove somebody.
  3606. ** For each player in the scores array, count the # of '-1' entries
  3607. ** from this game backwards; the one with the most is the one that
  3608. ** hasn't played the longest; replace him with this new guy.
  3609. */
  3610. else {
  3611. max_index = 0;
  3612. max_count = 0;
  3613. for (j = 0; j < Session.NumScores; j++) {
  3614. count = 0;
  3615. for (k = Session.NumScores - 1; k >= 0; k--) {
  3616. if (Session.Score[j].Kills[k]==-1) {
  3617. count++;
  3618. }
  3619. else {
  3620. break;
  3621. }
  3622. }
  3623. if (count > max_count) {
  3624. max_count = count;
  3625. max_index = j;
  3626. }
  3627. }
  3628. score_index = max_index;
  3629. }
  3630. /*
  3631. ** Initialize this new score entry
  3632. */
  3633. Session.Score[score_index].Wins = 0;
  3634. strcpy (Session.Score[score_index].Name, hptr->IniName);
  3635. for (j = 0; j < MAX_MULTI_GAMES; j++)
  3636. Session.Score[score_index].Kills[j] = -1;
  3637. }
  3638. /*
  3639. ** Init this player's Kills to 0 (-1 means he didn't play this round;
  3640. ** 0 means he played but got no kills).
  3641. */
  3642. Session.Score[score_index].Kills[Session.CurGame] = 0;
  3643. /*
  3644. ** Init this player's color to his last-used color index
  3645. */
  3646. Session.Score[score_index].Color = hptr->RemapColor;
  3647. /*
  3648. ** If this house was undefeated, it must have been the winner.
  3649. ** (If no human houses are undefeated, the computer won.)
  3650. */
  3651. if (!hptr->IsDefeated) {
  3652. Session.Score[score_index].Wins++;
  3653. Session.Winner = score_index;
  3654. }
  3655. /*
  3656. ** Tally up all kills for this player
  3657. */
  3658. for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) {
  3659. Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2];
  3660. Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2];
  3661. }
  3662. }
  3663. }
  3664. /***************************************************************************
  3665. * HouseClass::Blowup_All -- blows up everything *
  3666. * *
  3667. * INPUT: *
  3668. * none. *
  3669. * *
  3670. * OUTPUT: *
  3671. * none. *
  3672. * *
  3673. * WARNINGS: *
  3674. * none. *
  3675. * *
  3676. * HISTORY: *
  3677. * 05/16/1995 BRR : Created. *
  3678. * 06/09/1995 JLB : Handles aircraft. *
  3679. * 05/07/1996 JLB : Handles ships. *
  3680. *=========================================================================*/
  3681. void HouseClass::Blowup_All(void)
  3682. {
  3683. assert(Houses.ID(this) == ID);
  3684. int i;
  3685. int damage;
  3686. UnitClass * uptr;
  3687. InfantryClass * iptr;
  3688. BuildingClass * bptr;
  3689. int count;
  3690. WarheadType warhead;
  3691. /*
  3692. ** Find everything owned by this house & blast it with a huge amount of damage
  3693. ** at zero range. Do units before infantry, so the units' drivers are killed
  3694. ** too. Using Explosion_Damage is like dropping a big bomb right on the
  3695. ** object; it will also damage anything around it.
  3696. */
  3697. for (i = 0; i < ::Units.Count(); i++) {
  3698. if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) {
  3699. uptr = ::Units.Ptr(i);
  3700. /*
  3701. ** Some units can't be killed with one shot, so keep damaging them until
  3702. ** they're gone. The unit will destroy itself, and put an infantry in
  3703. ** its place. When the unit destroys itself, decrement 'i' since
  3704. ** its pointer will be removed from the active pointer list.
  3705. */
  3706. count = 0;
  3707. while (::Units.Ptr(i)==uptr && uptr->Strength) {
  3708. // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
  3709. // Likely due to damage biasing based on RA factions and/or difficulty settings
  3710. // Applying this to units (vehicles), ships, buildings, and infantry, too
  3711. //
  3712. // damage = uptr->Strength; // Original
  3713. damage = 0x7fff; // Copied from TD
  3714. uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
  3715. count++;
  3716. if (count > 5 && uptr->IsActive) {
  3717. delete uptr;
  3718. break;
  3719. }
  3720. }
  3721. i--;
  3722. }
  3723. }
  3724. /*
  3725. ** Destroy all aircraft owned by this house.
  3726. */
  3727. for (i = 0; i < ::Aircraft.Count(); i++) {
  3728. if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) {
  3729. AircraftClass * aptr = ::Aircraft.Ptr(i);
  3730. // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
  3731. // Likely due to damage biasing based on RA factions and/or difficulty settings
  3732. // Applying this to units (vehicles), ships, buildings, and infantry, too
  3733. //
  3734. // damage = aptr->Strength; // Original
  3735. damage = 0x7fff; // Copied from TD
  3736. aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
  3737. if (!aptr->IsActive) {
  3738. i--;
  3739. }
  3740. }
  3741. }
  3742. /*
  3743. ** Destroy all vessels owned by this house.
  3744. */
  3745. for (i = 0; i < ::Vessels.Count(); i++) {
  3746. if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) {
  3747. VesselClass * vptr = ::Vessels.Ptr(i);
  3748. // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
  3749. // Likely due to damage biasing based on RA factions and/or difficulty settings
  3750. // Applying this to units (vehicles), ships, buildings, and infantry, too
  3751. //
  3752. // damage = vptr->Strength; // Original
  3753. damage = 0x7fff; // Copied from TD
  3754. vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
  3755. if (!vptr->IsActive) {
  3756. i--;
  3757. }
  3758. }
  3759. }
  3760. /*
  3761. ** Buildings don't delete themselves when they die; they shake the screen
  3762. ** and begin a countdown, so don't decrement 'i' when it's destroyed.
  3763. */
  3764. for (i = 0; i < Buildings.Count(); i++) {
  3765. if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) {
  3766. bptr = Buildings.Ptr(i);
  3767. count = 0;
  3768. while (Buildings.Ptr(i)==bptr && bptr->Strength) {
  3769. // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
  3770. // Likely due to damage biasing based on RA factions and/or difficulty settings
  3771. // Applying this to units (vehicles), ships, buildings, and infantry, too
  3772. //
  3773. // damage = bptr->Strength; // Original
  3774. damage = 0x7fff; // Copied from TD
  3775. bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
  3776. count++;
  3777. if (count > 5) {
  3778. delete bptr;
  3779. break;
  3780. }
  3781. }
  3782. }
  3783. }
  3784. /*
  3785. ** Infantry don't delete themselves when they die; they go into a death-
  3786. ** animation sequence, so there's no need to decrement 'i' when they die.
  3787. ** Infantry should die by different types of warheads, so their death
  3788. ** anims aren't all synchronized.
  3789. */
  3790. for (i = 0; i < Infantry.Count(); i++) {
  3791. if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) {
  3792. iptr = Infantry.Ptr(i);
  3793. count = 0;
  3794. while (Infantry.Ptr(i)==iptr && iptr->Strength) {
  3795. // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
  3796. // Likely due to damage biasing based on RA factions and/or difficulty settings
  3797. // Applying this to units (vehicles), ships, buildings, and infantry, too
  3798. //
  3799. // damage = iptr->Strength; // Original
  3800. damage = 0x7fff; // Copied from TD
  3801. warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE);
  3802. iptr->Take_Damage(damage, 0, warhead, NULL, true);
  3803. count++;
  3804. if (count > 5) {
  3805. delete iptr;
  3806. break;
  3807. }
  3808. }
  3809. }
  3810. }
  3811. }
  3812. /***********************************************************************************************
  3813. * HouseClass::Flag_To_Die -- Flags the house to blow up soon. *
  3814. * *
  3815. * When this routine is called, the house will blow up after a period of time. Typically *
  3816. * this is called when the flag is captured or the HQ destroyed. *
  3817. * *
  3818. * INPUT: none *
  3819. * *
  3820. * OUTPUT: Was the house flagged to blow up? *
  3821. * *
  3822. * WARNINGS: none *
  3823. * *
  3824. * HISTORY: *
  3825. * 06/20/1995 JLB : Created. *
  3826. *=============================================================================================*/
  3827. bool HouseClass::Flag_To_Die(void)
  3828. {
  3829. assert(Houses.ID(this) == ID);
  3830. if (!IsToWin && !IsToDie && !IsToLose) {
  3831. IsToDie = true;
  3832. BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
  3833. }
  3834. return(IsToDie);
  3835. }
  3836. /***********************************************************************************************
  3837. * HouseClass::Flag_To_Win -- Flags the house to win soon. *
  3838. * *
  3839. * When this routine is called, the house will be declared the winner after a period of *
  3840. * time. *
  3841. * *
  3842. * INPUT: none *
  3843. * *
  3844. * OUTPUT: Was the house flagged to win? *
  3845. * *
  3846. * WARNINGS: none *
  3847. * *
  3848. * HISTORY: *
  3849. * 06/20/1995 JLB : Created. *
  3850. *=============================================================================================*/
  3851. bool HouseClass::Flag_To_Win(void)
  3852. {
  3853. assert(Houses.ID(this) == ID);
  3854. if (!IsToWin && !IsToDie && !IsToLose) {
  3855. IsToWin = true;
  3856. BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
  3857. }
  3858. return(IsToWin);
  3859. }
  3860. /***********************************************************************************************
  3861. * HouseClass::Flag_To_Lose -- Flags the house to die soon. *
  3862. * *
  3863. * When this routine is called, it will spell the doom of this house. In a short while *
  3864. * all of the object owned by this house will explode. Typical use of this routine is when *
  3865. * the flag has been captured or the command vehicle has been destroyed. *
  3866. * *
  3867. * INPUT: none *
  3868. * *
  3869. * OUTPUT: Has the doom been initiated? *
  3870. * *
  3871. * WARNINGS: none *
  3872. * *
  3873. * HISTORY: *
  3874. * 06/12/1995 JLB : Created. *
  3875. *=============================================================================================*/
  3876. bool HouseClass::Flag_To_Lose(void)
  3877. {
  3878. assert(Houses.ID(this) == ID);
  3879. IsToWin = false;
  3880. if (!IsToDie && !IsToLose) {
  3881. IsToLose = true;
  3882. BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
  3883. }
  3884. return(IsToLose);
  3885. }
  3886. /***********************************************************************************************
  3887. * HouseClass::Init_Data -- Initializes the multiplayer color data. *
  3888. * *
  3889. * This routine is called when initializing the color and remap data for this house. The *
  3890. * primary user of this routine is the multiplayer version of the game, especially for *
  3891. * saving & loading multiplayer games. *
  3892. * *
  3893. * INPUT: color -- The color of this house. *
  3894. * *
  3895. * house -- The house that this should act like. *
  3896. * *
  3897. * credits -- The initial credits to assign to this house. *
  3898. * *
  3899. * OUTPUT: none *
  3900. * *
  3901. * WARNINGS: none *
  3902. * *
  3903. * HISTORY: *
  3904. * 07/29/1995 JLB : Created. *
  3905. *=============================================================================================*/
  3906. extern bool NowSavingGame; // TEMP MBL: Need to discuss better solution with Steve
  3907. void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits)
  3908. {
  3909. assert(Houses.ID(this) == ID);
  3910. Credits = Control.InitialCredits = credits;
  3911. VisibleCredits.Current = Credits;
  3912. RemapColor = color;
  3913. ActLike = house;
  3914. // MBL 03.20.2020
  3915. // Attempt to fix Red Alert credit tick-up bug after saving a game that has had harvesting underway
  3916. // Note that this code gets called with both game loads and saves
  3917. // When this function is called, sometimes credits value has Tiberium (or HarvestedCredits?) variables applied, and sometimes now
  3918. //
  3919. if (NowSavingGame == true)
  3920. {
  3921. // At this point VisibleCredits.Current (set above) does not have harvested ore/tiberium applied, but VisibleCredits.Credits does
  3922. VisibleCredits.Current = VisibleCredits.Credits;
  3923. }
  3924. }
  3925. /***********************************************************************************************
  3926. * HouseClass::Power_Fraction -- Fetches the current power output rating. *
  3927. * *
  3928. * Use this routine to fetch the current power output as a fixed point fraction. The *
  3929. * value 0x0100 is 100% power. *
  3930. * *
  3931. * INPUT: none *
  3932. * *
  3933. * OUTPUT: Returns with power rating as a fixed pointer number. *
  3934. * *
  3935. * WARNINGS: none *
  3936. * *
  3937. * HISTORY: *
  3938. * 07/22/1995 JLB : Created. *
  3939. *=============================================================================================*/
  3940. fixed HouseClass::Power_Fraction(void) const
  3941. {
  3942. assert(Houses.ID(this) == ID);
  3943. if (Power >= Drain || Drain == 0) return(1);
  3944. if (Power) {
  3945. return(fixed(Power, Drain));
  3946. }
  3947. return(0);
  3948. }
  3949. /***********************************************************************************************
  3950. * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. *
  3951. * *
  3952. * This routine will try to sell the wall at the specified location. If there is a wall *
  3953. * present and it is owned by this house, then it can be sold. *
  3954. * *
  3955. * INPUT: cell -- The cell that wall selling is desired. *
  3956. * *
  3957. * OUTPUT: none *
  3958. * *
  3959. * WARNINGS: none *
  3960. * *
  3961. * HISTORY: *
  3962. * 08/05/1995 JLB : Created. *
  3963. * 11/02/1996 JLB : Checks unsellable bit for wall type. *
  3964. *=============================================================================================*/
  3965. void HouseClass::Sell_Wall(CELL cell)
  3966. {
  3967. assert(Houses.ID(this) == ID);
  3968. if ((unsigned)cell > 0) {
  3969. OverlayType overlay = Map[cell].Overlay;
  3970. if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) {
  3971. OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay);
  3972. if (optr.IsWall) {
  3973. BuildingTypeClass const * btype = NULL;
  3974. switch (overlay) {
  3975. case OVERLAY_SANDBAG_WALL:
  3976. btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL);
  3977. break;
  3978. case OVERLAY_CYCLONE_WALL:
  3979. btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL);
  3980. break;
  3981. case OVERLAY_BRICK_WALL:
  3982. btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL);
  3983. break;
  3984. case OVERLAY_BARBWIRE_WALL:
  3985. btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL);
  3986. break;
  3987. case OVERLAY_WOOD_WALL:
  3988. btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL);
  3989. break;
  3990. case OVERLAY_FENCE:
  3991. btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE);
  3992. break;
  3993. default:
  3994. break;
  3995. }
  3996. if (btype != NULL && !btype->IsUnsellable) {
  3997. if (PlayerPtr == this) {
  3998. Sound_Effect(VOC_CASHTURN);
  3999. }
  4000. Refund_Money(btype->Raw_Cost() * Rule.RefundPercent);
  4001. Map[cell].Overlay = OVERLAY_NONE;
  4002. Map[cell].OverlayData = 0;
  4003. Map[cell].Owner = HOUSE_NONE;
  4004. Map[cell].Wall_Update();
  4005. CellClass * ncell = Map[cell].Adjacent_Cell(FACING_N);
  4006. if (ncell) ncell->Wall_Update();
  4007. CellClass * wcell = Map[cell].Adjacent_Cell(FACING_W);
  4008. if (wcell) wcell->Wall_Update();
  4009. CellClass * scell = Map[cell].Adjacent_Cell(FACING_S);
  4010. if (scell) scell->Wall_Update();
  4011. CellClass * ecell = Map[cell].Adjacent_Cell(FACING_E);
  4012. if (ecell) ecell->Wall_Update();
  4013. Map[cell].Recalc_Attributes();
  4014. Map[cell].Redraw_Objects();
  4015. Map.Radar_Pixel(cell);
  4016. Detach_This_From_All(::As_Target(cell), true);
  4017. if (optr.IsCrushable) {
  4018. Map.Zone_Reset(MZONEF_NORMAL);
  4019. } else {
  4020. Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL);
  4021. }
  4022. }
  4023. }
  4024. }
  4025. }
  4026. }
  4027. /***********************************************************************************************
  4028. * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. *
  4029. * *
  4030. * This routine is called when a construction yard needs to know what to build next. It *
  4031. * will either examine the prebuilt base list or try to figure out what to build next *
  4032. * based on the current game situation. *
  4033. * *
  4034. * INPUT: none *
  4035. * *
  4036. * OUTPUT: Returns with a pointer to the building type class to build. *
  4037. * *
  4038. * WARNINGS: none *
  4039. * *
  4040. * HISTORY: *
  4041. * 09/27/1995 JLB : Created. *
  4042. *=============================================================================================*/
  4043. BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const
  4044. {
  4045. assert(Houses.ID(this) == ID);
  4046. if (BuildStructure != STRUCT_NONE) {
  4047. return(&BuildingTypeClass::As_Reference(BuildStructure));
  4048. }
  4049. return(NULL);
  4050. }
  4051. /***********************************************************************************************
  4052. * HouseClass::Find_Building -- Finds a building of specified type. *
  4053. * *
  4054. * This routine is used to find a building of the specified type. This is particularly *
  4055. * useful for when some event requires a specific building instance. The nuclear missile *
  4056. * launch is a good example. *
  4057. * *
  4058. * INPUT: type -- The building type to scan for. *
  4059. * *
  4060. * zone -- The zone that the building must be located in. If no zone specific search *
  4061. * is desired, then pass ZONE_NONE. *
  4062. * *
  4063. * OUTPUT: Returns with a pointer to the building type requested. If there is no building *
  4064. * of the type requested, then NULL is returned. *
  4065. * *
  4066. * WARNINGS: none *
  4067. * *
  4068. * HISTORY: *
  4069. * 09/27/1995 JLB : Created. *
  4070. * 10/02/1995 JLB : Allows for zone specifics. *
  4071. *=============================================================================================*/
  4072. BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const
  4073. {
  4074. assert(Houses.ID(this) == ID);
  4075. /*
  4076. ** Only scan if we KNOW there is at least one building of the type
  4077. ** requested.
  4078. */
  4079. if (BQuantity[type] > 0) {
  4080. /*
  4081. ** Search for a suitable launch site for this missile.
  4082. */
  4083. for (int index = 0; index < Buildings.Count(); index++) {
  4084. BuildingClass * b = Buildings.Ptr(index);
  4085. if (b && !b->IsInLimbo && b->House == this && *b == type) {
  4086. if (zone == ZONE_NONE || Which_Zone(b) == zone) {
  4087. return(b);
  4088. }
  4089. }
  4090. }
  4091. }
  4092. return(NULL);
  4093. }
  4094. /***********************************************************************************************
  4095. * HouseClass::Find_Build_Location -- Finds a suitable building location. *
  4096. * *
  4097. * This routine is used to find a suitable building location for the building specified. *
  4098. * The auto base building logic uses this when building the base for the computer. *
  4099. * *
  4100. * INPUT: building -- Pointer to the building that needs to be placed down. *
  4101. * *
  4102. * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable *
  4103. * locations, then NULL is returned. *
  4104. * *
  4105. * WARNINGS: none *
  4106. * *
  4107. * HISTORY: *
  4108. * 09/27/1995 JLB : Created. *
  4109. *=============================================================================================*/
  4110. COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const
  4111. {
  4112. assert(Houses.ID(this) == ID);
  4113. int zonerating[ZONE_COUNT];
  4114. struct {
  4115. int AntiAir; // Average air defense for the base.
  4116. int AntiArmor; // Average armor defense for the base.
  4117. int AntiInfantry; // Average infantry defense for the base.
  4118. } zoneinfo = {0,0,0};
  4119. int antiair = building->Anti_Air();
  4120. int antiarmor = building->Anti_Armor();
  4121. int antiinfantry = building->Anti_Infantry();
  4122. bool adj = true;
  4123. /*
  4124. ** Never place combat buildings adjacent to each other. This is partly
  4125. ** because combat buildings don't have a bib and jamming will occur as well
  4126. ** as because spacing defensive buildings out will yield a better
  4127. ** defense.
  4128. */
  4129. if (antiair || antiarmor || antiinfantry) {
  4130. adj = false;
  4131. }
  4132. /*
  4133. ** Determine the average zone strengths for the base. This value is
  4134. ** used to determine what zones are considered under or over strength.
  4135. */
  4136. ZoneType z;
  4137. for (z = ZONE_NORTH; z < ZONE_COUNT; z++) {
  4138. zoneinfo.AntiAir += ZoneInfo[z].AirDefense;
  4139. zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense;
  4140. zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense;
  4141. }
  4142. zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH;
  4143. zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH;
  4144. zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH;
  4145. /*
  4146. ** Give each zone a rating for value. The higher the value the more desirable
  4147. ** to place the specified building in that zone. Factor the average value of
  4148. ** zone defense such that more weight is given to zones that are very under
  4149. ** defended.
  4150. */
  4151. memset(&zonerating[0], '\0', sizeof(zonerating));
  4152. for (z = ZONE_FIRST; z < ZONE_COUNT; z++) {
  4153. int diff;
  4154. diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense;
  4155. if (z == ZONE_CORE) diff /= 2;
  4156. if (diff > 0) {
  4157. zonerating[z] += min(antiair, diff);
  4158. }
  4159. diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense;
  4160. if (z == ZONE_CORE) diff /= 2;
  4161. if (diff > 0) {
  4162. zonerating[z] += min(antiarmor, diff);
  4163. }
  4164. diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense;
  4165. if (z == ZONE_CORE) diff /= 2;
  4166. if (diff > 0) {
  4167. zonerating[z] += min(antiinfantry, diff);
  4168. }
  4169. }
  4170. /*
  4171. ** Now that each zone has been given a desirability rating, find the zone
  4172. ** with the greatest value and try to place the building in that zone.
  4173. */
  4174. ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST);
  4175. int largest = 0;
  4176. for (z = ZONE_FIRST; z < ZONE_COUNT; z++) {
  4177. if (zonerating[z] > largest) {
  4178. zone = z;
  4179. largest = zonerating[z];
  4180. }
  4181. }
  4182. CELL zcell = Find_Cell_In_Zone(building, zone);
  4183. if (zcell) {
  4184. return(Cell_Coord(zcell));
  4185. }
  4186. /*
  4187. ** Could not build in preferred zone, so try building in any zone.
  4188. */
  4189. static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST};
  4190. int start = Random_Pick(0, ARRAY_SIZE(_zones)-1);
  4191. for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) {
  4192. ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)];
  4193. zcell = Find_Cell_In_Zone(building, tryzone);
  4194. if (zcell) return(zcell);
  4195. }
  4196. return(NULL);
  4197. }
  4198. /***********************************************************************************************
  4199. * HouseClass::Recalc_Center -- Recalculates the center point of the base. *
  4200. * *
  4201. * This routine will average the location of the base and record the center point. The *
  4202. * recorded center point is used to determine such things as how far the base is spread *
  4203. * out and where to protect the most. This routine should be called whenever a building *
  4204. * is created or destroyed. *
  4205. * *
  4206. * INPUT: none *
  4207. * *
  4208. * OUTPUT: none *
  4209. * *
  4210. * WARNINGS: none *
  4211. * *
  4212. * HISTORY: *
  4213. * 09/28/1995 JLB : Created. *
  4214. *=============================================================================================*/
  4215. void HouseClass::Recalc_Center(void)
  4216. {
  4217. assert(Houses.ID(this) == ID);
  4218. /*
  4219. ** First presume that there is no base. If there is a base, then these values will be
  4220. ** properly filled in below.
  4221. */
  4222. Center = 0;
  4223. Radius = 0;
  4224. for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) {
  4225. ZoneInfo[zone].AirDefense = 0;
  4226. ZoneInfo[zone].ArmorDefense = 0;
  4227. ZoneInfo[zone].InfantryDefense = 0;
  4228. }
  4229. /*
  4230. ** Only process the center base size/position calculation if there are buildings to
  4231. ** consider. When no buildings for this house are present, then no processing need
  4232. ** occur.
  4233. */
  4234. if (CurBuildings > 0) {
  4235. int x = 0;
  4236. int y = 0;
  4237. int count = 0;
  4238. int index;
  4239. for (index = 0; index < Buildings.Count(); index++) {
  4240. BuildingClass const * b = Buildings.Ptr(index);
  4241. if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
  4242. /*
  4243. ** Give more "weight" to buildings that cost more. The presumption is that cheap
  4244. ** buildings don't affect the base disposition as much as the more expensive
  4245. ** buildings do.
  4246. */
  4247. int weight = (b->Class->Cost_Of() / 1000)+1;
  4248. for (int i = 0; i < weight; i++) {
  4249. x += Coord_X(b->Center_Coord());
  4250. y += Coord_Y(b->Center_Coord());
  4251. count++;
  4252. }
  4253. }
  4254. }
  4255. /*
  4256. ** This second check for quantity of buildings is necessary because the first
  4257. ** check against CurBuildings doesn't take into account if the building is in
  4258. ** limbo, but for base calculation, the limbo state disqualifies a building
  4259. ** from being processed. Thus, CurBuildings may indicate a base, but count may
  4260. ** not match.
  4261. */
  4262. if (count > 0) {
  4263. x /= count;
  4264. y /= count;
  4265. #ifdef NEVER
  4266. /*
  4267. ** Bias the center of the base away from the edges of the map.
  4268. */
  4269. LEPTON left = Cell_To_Lepton(Map.MapCellX + 10);
  4270. LEPTON top = Cell_To_Lepton(Map.MapCellY + 10);
  4271. LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10);
  4272. LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10);
  4273. if (x < left) x = left;
  4274. if (x > right) x = right;
  4275. if (y < top) y = top;
  4276. if (y > bottom) y = bottom;
  4277. #endif
  4278. Center = XY_Coord(x, y);
  4279. }
  4280. /*
  4281. ** If there were any buildings discovered as legal to consider as part of the base,
  4282. ** then figure out the general average radius of the building disposition as it
  4283. ** relates to the center of the base.
  4284. */
  4285. if (count > 1) {
  4286. int radius = 0;
  4287. for (index = 0; index < Buildings.Count(); index++) {
  4288. BuildingClass const * b = Buildings.Ptr(index);
  4289. if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
  4290. radius += Distance(Center, b->Center_Coord());
  4291. }
  4292. }
  4293. Radius = max(radius / count, 2 * CELL_LEPTON_W);
  4294. /*
  4295. ** Determine the relative strength of each base defense zone.
  4296. */
  4297. for (index = 0; index < Buildings.Count(); index++) {
  4298. BuildingClass const * b = Buildings.Ptr(index);
  4299. if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
  4300. ZoneType z = Which_Zone(b);
  4301. if (z != ZONE_NONE) {
  4302. ZoneInfo[z].ArmorDefense += b->Anti_Armor();
  4303. ZoneInfo[z].AirDefense += b->Anti_Air();
  4304. ZoneInfo[z].InfantryDefense += b->Anti_Infantry();
  4305. }
  4306. }
  4307. }
  4308. } else {
  4309. Radius = 0x0200;
  4310. }
  4311. }
  4312. }
  4313. /***********************************************************************************************
  4314. * HouseClass::Expert_AI -- Handles expert AI processing. *
  4315. * *
  4316. * This routine is called when the computer should perform expert AI processing. This *
  4317. * method of AI is categorized as an "Expert System" process. *
  4318. * *
  4319. * INPUT: none *
  4320. * *
  4321. * OUTPUT: Returns the number of game frames to delay before calling this routine again. *
  4322. * *
  4323. * WARNINGS: This is relatively time consuming -- call periodically. *
  4324. * *
  4325. * HISTORY: *
  4326. * 09/29/1995 JLB : Created. *
  4327. *=============================================================================================*/
  4328. int HouseClass::Expert_AI(void)
  4329. {
  4330. assert(Houses.ID(this) == ID);
  4331. BuildingClass * b = 0;
  4332. bool stop = false;
  4333. int time = TICKS_PER_SECOND * 10;
  4334. /*
  4335. ** If the current enemy no longer has a base or is defeated, then don't consider
  4336. ** that house a threat anymore. Clear out the enemy record and then try
  4337. ** to find a new enemy.
  4338. */
  4339. if (Enemy != HOUSE_NONE) {
  4340. HouseClass * h = HouseClass::As_Pointer(Enemy);
  4341. if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) {
  4342. Enemy = HOUSE_NONE;
  4343. }
  4344. }
  4345. /*
  4346. ** If there is no enemy assigned to this house, then assign one now. The
  4347. ** enemy that is closest is picked. However, don't pick an enemy if the
  4348. ** base has not been established yet.
  4349. */
  4350. if (ActiveBScan && Center && Attack == 0) {
  4351. int close = 0;
  4352. HousesType enemy = HOUSE_NONE;
  4353. int maxunit = 0;
  4354. int maxinfantry = 0;
  4355. int maxvessel = 0;
  4356. int maxaircraft = 0;
  4357. int maxbuilding = 0;
  4358. int enemycount = 0;
  4359. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  4360. HouseClass * h = HouseClass::As_Pointer(house);
  4361. if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) {
  4362. /*
  4363. ** Perform a special restriction check to ensure that no enemy is chosen if
  4364. ** there is even one enemy that has not established a base yet. This will
  4365. ** ensure an accurate first pick for enemy since the distance to base
  4366. ** value can be determined.
  4367. */
  4368. if (!h->IsStarted) {
  4369. enemy = HOUSE_NONE;
  4370. break;
  4371. }
  4372. /*
  4373. ** Keep track of the number of buildings and units owned by the
  4374. ** enemy. This is used to bring up the maximum allowed to match.
  4375. */
  4376. maxunit += h->CurUnits;
  4377. maxbuilding += h->CurBuildings;
  4378. maxinfantry += h->CurInfantry;
  4379. maxvessel += h->CurVessels;
  4380. maxaircraft += h->CurAircraft;
  4381. enemycount++;
  4382. /*
  4383. ** Determine a priority value based on distance to the center of the
  4384. ** candidate base. The higher the value, the better the candidate house
  4385. ** is to becoming the preferred enemy for this house.
  4386. */
  4387. int value = ((MAP_CELL_W*2)-Distance(Center, h->Center));
  4388. value *= 2;
  4389. /*
  4390. ** In addition to distance, record the number of kills directed
  4391. ** against this house. The enemy that does more damage might be
  4392. ** considered a greater threat.
  4393. */
  4394. value += h->BuildingsKilled[Class->House]*5;
  4395. value += h->UnitsKilled[Class->House];
  4396. /*
  4397. ** Factor in the relative sizes of the bases. An enemy that has a
  4398. ** larger base will be considered a bigger threat. Conversely, a
  4399. ** smaller base is considered a lesser threat.
  4400. */
  4401. value += h->CurUnits - CurUnits;
  4402. value += h->CurBuildings - CurBuildings;
  4403. value += (h->CurInfantry - CurInfantry)/4;
  4404. /*
  4405. ** Whoever last attacked is given a little more priority as
  4406. ** a potential designated enemy.
  4407. */
  4408. if (house == LAEnemy) {
  4409. value += 100;
  4410. }
  4411. #ifdef OBSOLETE
  4412. /*
  4413. ** Human players are a given preference as the target.
  4414. */
  4415. if (h->IsHuman) {
  4416. value *= 2;
  4417. }
  4418. #endif
  4419. /*
  4420. ** Compare the calculated value for this candidate house and if it is
  4421. ** greater than the previously recorded maximum, record this house as
  4422. ** the prime candidate for enemy.
  4423. */
  4424. if (value > close) {
  4425. enemy = house;
  4426. close = value;
  4427. }
  4428. }
  4429. }
  4430. /*
  4431. ** Record this closest enemy base as the first enemy to attack.
  4432. */
  4433. Enemy = enemy;
  4434. /*
  4435. ** Up the maximum allowed units and buildings to match a rough average
  4436. ** of what the enemies are allowed.
  4437. */
  4438. if (enemycount) {
  4439. maxunit /= enemycount;
  4440. maxbuilding /= enemycount;
  4441. maxinfantry /= enemycount;
  4442. maxvessel /= enemycount;
  4443. maxaircraft /= enemycount;
  4444. }
  4445. if (Control.MaxBuilding < (unsigned)maxbuilding + 10) {
  4446. Control.MaxBuilding = maxbuilding + 10;
  4447. }
  4448. if (Control.MaxUnit < (unsigned)maxunit + 10) {
  4449. Control.MaxUnit = maxunit + 10;
  4450. }
  4451. if (Control.MaxInfantry < (unsigned)maxinfantry + 10) {
  4452. Control.MaxInfantry = maxinfantry + 10;
  4453. }
  4454. if (Control.MaxVessel < (unsigned)maxvessel + 10) {
  4455. Control.MaxVessel = maxvessel + 10;
  4456. }
  4457. if (Control.MaxAircraft < (unsigned)maxaircraft + 10) {
  4458. Control.MaxAircraft = maxaircraft + 10;
  4459. }
  4460. }
  4461. /*
  4462. ** House state transition check occurs here. Transitions that occur here are ones
  4463. ** that relate to general base condition rather than specific combat events.
  4464. ** Typically, this is limited to transitions between normal buildup mode and
  4465. ** broke mode.
  4466. */
  4467. if (State == STATE_ENDGAME) {
  4468. Fire_Sale();
  4469. Do_All_To_Hunt();
  4470. } else {
  4471. if (State == STATE_BUILDUP) {
  4472. if (Available_Money() < 25) {
  4473. State = STATE_BROKE;
  4474. }
  4475. }
  4476. if (State == STATE_BROKE) {
  4477. if (Available_Money() >= 25) {
  4478. State = STATE_BUILDUP;
  4479. }
  4480. }
  4481. if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) {
  4482. State = STATE_BUILDUP;
  4483. }
  4484. if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) {
  4485. State = STATE_ATTACKED;
  4486. }
  4487. }
  4488. /*
  4489. ** Records the urgency of all actions possible.
  4490. */
  4491. UrgencyType urgency[STRATEGY_COUNT];
  4492. StrategyType strat;
  4493. for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) {
  4494. urgency[strat] = URGENCY_NONE;
  4495. switch (strat) {
  4496. case STRATEGY_BUILD_POWER:
  4497. urgency[strat] = Check_Build_Power();
  4498. break;
  4499. case STRATEGY_BUILD_DEFENSE:
  4500. urgency[strat] = Check_Build_Defense();
  4501. break;
  4502. case STRATEGY_BUILD_INCOME:
  4503. urgency[strat] = Check_Build_Income();
  4504. break;
  4505. case STRATEGY_FIRE_SALE:
  4506. urgency[strat] = Check_Fire_Sale();
  4507. break;
  4508. case STRATEGY_BUILD_ENGINEER:
  4509. urgency[strat] = Check_Build_Engineer();
  4510. break;
  4511. case STRATEGY_BUILD_OFFENSE:
  4512. urgency[strat] = Check_Build_Offense();
  4513. break;
  4514. case STRATEGY_RAISE_MONEY:
  4515. urgency[strat] = Check_Raise_Money();
  4516. break;
  4517. case STRATEGY_RAISE_POWER:
  4518. urgency[strat] = Check_Raise_Power();
  4519. break;
  4520. case STRATEGY_LOWER_POWER:
  4521. urgency[strat] = Check_Lower_Power();
  4522. break;
  4523. case STRATEGY_ATTACK:
  4524. urgency[strat] = Check_Attack();
  4525. break;
  4526. default:
  4527. urgency[strat] = URGENCY_NONE;
  4528. break;
  4529. }
  4530. }
  4531. /*
  4532. ** Performs the action required for each of the strategies that share
  4533. ** the most urgent category. Stop processing if any strategy at the
  4534. ** highest urgency performed any action. This is because higher urgency
  4535. ** actions tend to greatly affect the lower urgency actions.
  4536. */
  4537. for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) {
  4538. bool acted = false;
  4539. for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) {
  4540. if (urgency[strat] == u) {
  4541. switch (strat) {
  4542. case STRATEGY_BUILD_POWER:
  4543. acted |= AI_Build_Power(u);
  4544. break;
  4545. case STRATEGY_BUILD_DEFENSE:
  4546. acted |= AI_Build_Defense(u);
  4547. break;
  4548. case STRATEGY_BUILD_INCOME:
  4549. acted |= AI_Build_Income(u);
  4550. break;
  4551. case STRATEGY_FIRE_SALE:
  4552. acted |= AI_Fire_Sale(u);
  4553. break;
  4554. case STRATEGY_BUILD_ENGINEER:
  4555. acted |= AI_Build_Engineer(u);
  4556. break;
  4557. case STRATEGY_BUILD_OFFENSE:
  4558. acted |= AI_Build_Offense(u);
  4559. break;
  4560. case STRATEGY_RAISE_MONEY:
  4561. acted |= AI_Raise_Money(u);
  4562. break;
  4563. case STRATEGY_RAISE_POWER:
  4564. acted |= AI_Raise_Power(u);
  4565. break;
  4566. case STRATEGY_LOWER_POWER:
  4567. acted |= AI_Lower_Power(u);
  4568. break;
  4569. case STRATEGY_ATTACK:
  4570. acted |= AI_Attack(u);
  4571. break;
  4572. default:
  4573. break;
  4574. }
  4575. }
  4576. }
  4577. }
  4578. return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2));
  4579. }
  4580. UrgencyType HouseClass::Check_Build_Power(void) const
  4581. {
  4582. assert(Houses.ID(this) == ID);
  4583. fixed frac = Power_Fraction();
  4584. UrgencyType urgency = URGENCY_NONE;
  4585. if (frac < 1 && Can_Make_Money()) {
  4586. urgency = URGENCY_LOW;
  4587. /*
  4588. ** Very low power condition is considered a higher priority.
  4589. */
  4590. if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM;
  4591. /*
  4592. ** When under attack and there is a need for power in defense,
  4593. ** then consider power building a higher priority.
  4594. */
  4595. if (State == STATE_THREATENED || State == STATE_ATTACKED) {
  4596. if (BScan | (STRUCTF_CHRONOSPHERE)) {
  4597. urgency = URGENCY_HIGH;
  4598. }
  4599. }
  4600. }
  4601. return(urgency);
  4602. }
  4603. UrgencyType HouseClass::Check_Build_Defense(void) const
  4604. {
  4605. assert(Houses.ID(this) == ID);
  4606. /*
  4607. ** This routine determines what urgency level that base defense
  4608. ** should be given. The more vulnerable the base is, the higher
  4609. ** the urgency this routine should return.
  4610. */
  4611. return(URGENCY_NONE);
  4612. }
  4613. UrgencyType HouseClass::Check_Build_Offense(void) const
  4614. {
  4615. assert(Houses.ID(this) == ID);
  4616. /*
  4617. ** This routine determines what urgency level that offensive
  4618. ** weaponry should be given. Surplus money or a very strong
  4619. ** defense will cause the offensive urgency to increase.
  4620. */
  4621. return(URGENCY_NONE);
  4622. }
  4623. /*
  4624. ** Determines what the attack state of the base is. The higher the state,
  4625. ** the greater the immediate threat to base defense is.
  4626. */
  4627. UrgencyType HouseClass::Check_Attack(void) const
  4628. {
  4629. assert(Houses.ID(this) == ID);
  4630. if (Frame > TICKS_PER_MINUTE && Attack == 0) {
  4631. if (State == STATE_ATTACKED) {
  4632. return(URGENCY_LOW);
  4633. }
  4634. return(URGENCY_CRITICAL);
  4635. }
  4636. return(URGENCY_NONE);
  4637. }
  4638. UrgencyType HouseClass::Check_Build_Income(void) const
  4639. {
  4640. assert(Houses.ID(this) == ID);
  4641. /*
  4642. ** This routine should determine if income processing buildings
  4643. ** should be constructed and at what urgency. The lower the money,
  4644. ** the lower the refineries, or recent harvester losses should
  4645. ** cause a greater urgency to be returned.
  4646. */
  4647. return(URGENCY_NONE);
  4648. }
  4649. UrgencyType HouseClass::Check_Fire_Sale(void) const
  4650. {
  4651. assert(Houses.ID(this) == ID);
  4652. /*
  4653. ** If there are no more factories at all, then sell everything off because the game
  4654. ** is basically over at this point.
  4655. */
  4656. if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) {
  4657. return(URGENCY_CRITICAL);
  4658. }
  4659. return(URGENCY_NONE);
  4660. }
  4661. UrgencyType HouseClass::Check_Build_Engineer(void) const
  4662. {
  4663. assert(Houses.ID(this) == ID);
  4664. /*
  4665. ** This routine should check to see what urgency that the production of
  4666. ** engineers should be. If a friendly building has been captured or the
  4667. ** enemy has weak defenses, then building an engineer would be a priority.
  4668. */
  4669. return(URGENCY_NONE);
  4670. }
  4671. /*
  4672. ** Checks to see if money is critically low and something must be done
  4673. ** to immediately raise cash.
  4674. */
  4675. UrgencyType HouseClass::Check_Raise_Money(void) const
  4676. {
  4677. assert(Houses.ID(this) == ID);
  4678. UrgencyType urgency = URGENCY_NONE;
  4679. if (Available_Money() < 100) {
  4680. urgency = URGENCY_LOW;
  4681. }
  4682. if (Available_Money() < 2000 && !Can_Make_Money()) {
  4683. urgency++;
  4684. }
  4685. return(urgency);
  4686. }
  4687. /*
  4688. ** Checks to see if power is very low and if so, a greater urgency to
  4689. ** build more power is returned.
  4690. */
  4691. UrgencyType HouseClass::Check_Lower_Power(void) const
  4692. {
  4693. assert(Houses.ID(this) == ID);
  4694. if (Power > Drain+300) {
  4695. return(URGENCY_LOW);
  4696. }
  4697. return(URGENCY_NONE);
  4698. }
  4699. /*
  4700. ** This routine determines if there is a power emergency. Such an
  4701. ** emergency might require selling of structures in order to free
  4702. ** up power. This might occur if the base is being attacked and there
  4703. ** are defenses that require power, but are just short of having
  4704. ** enough.
  4705. */
  4706. UrgencyType HouseClass::Check_Raise_Power(void) const
  4707. {
  4708. assert(Houses.ID(this) == ID);
  4709. UrgencyType urgency = URGENCY_NONE;
  4710. if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) {
  4711. // if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) {
  4712. urgency = URGENCY_MEDIUM;
  4713. if (State == STATE_ATTACKED) {
  4714. urgency++;
  4715. }
  4716. }
  4717. return(urgency);
  4718. }
  4719. bool HouseClass::AI_Attack(UrgencyType )
  4720. {
  4721. assert(Houses.ID(this) == ID);
  4722. bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33));
  4723. bool forced = (CurBuildings == 0);
  4724. int index;
  4725. for (index = 0; index < Aircraft.Count(); index++) {
  4726. AircraftClass * a = Aircraft.Ptr(index);
  4727. if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) {
  4728. if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) {
  4729. a->Assign_Mission(MISSION_HUNT);
  4730. }
  4731. }
  4732. }
  4733. for (index = 0; index < Units.Count(); index++) {
  4734. UnitClass * u = Units.Ptr(index);
  4735. if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) {
  4736. if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) {
  4737. u->Assign_Mission(MISSION_HUNT);
  4738. } else {
  4739. /*
  4740. ** If this unit is guarding the base, then cause it to shuffle
  4741. ** location instead.
  4742. */
  4743. if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) {
  4744. u->ArchiveTarget = ::As_Target(Where_To_Go(u));
  4745. }
  4746. }
  4747. }
  4748. }
  4749. for (index = 0; index < Infantry.Count(); index++) {
  4750. InfantryClass * i = Infantry.Ptr(index);
  4751. if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) {
  4752. if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) {
  4753. i->Assign_Mission(MISSION_HUNT);
  4754. } else {
  4755. /*
  4756. ** If this soldier is guarding the base, then cause it to shuffle
  4757. ** location instead.
  4758. */
  4759. if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) {
  4760. i->ArchiveTarget = ::As_Target(Where_To_Go(i));
  4761. }
  4762. }
  4763. }
  4764. }
  4765. Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
  4766. return(true);
  4767. }
  4768. /*
  4769. ** Given the specified urgency, build a power structure to meet
  4770. ** this need.
  4771. */
  4772. bool HouseClass::AI_Build_Power(UrgencyType ) const
  4773. {
  4774. assert(Houses.ID(this) == ID);
  4775. return(false);
  4776. }
  4777. /*
  4778. ** Given the specified urgency, build base defensive structures
  4779. ** according to need and according to existing base disposition.
  4780. */
  4781. bool HouseClass::AI_Build_Defense(UrgencyType ) const
  4782. {
  4783. assert(Houses.ID(this) == ID);
  4784. return(false);
  4785. }
  4786. /*
  4787. ** Given the specified urgency, build offensive units according
  4788. ** to need and according to the opponents base defenses.
  4789. */
  4790. bool HouseClass::AI_Build_Offense(UrgencyType ) const
  4791. {
  4792. assert(Houses.ID(this) == ID);
  4793. return(false);
  4794. }
  4795. /*
  4796. ** Given the specified urgency, build income producing
  4797. ** structures according to need.
  4798. */
  4799. bool HouseClass::AI_Build_Income(UrgencyType ) const
  4800. {
  4801. assert(Houses.ID(this) == ID);
  4802. return(false);
  4803. }
  4804. bool HouseClass::AI_Fire_Sale(UrgencyType urgency)
  4805. {
  4806. assert(Houses.ID(this) == ID);
  4807. if (CurBuildings && urgency == URGENCY_CRITICAL) {
  4808. Fire_Sale();
  4809. Do_All_To_Hunt();
  4810. return(true);
  4811. }
  4812. return(false);
  4813. }
  4814. /*
  4815. ** Given the specified urgency, build an engineer.
  4816. */
  4817. bool HouseClass::AI_Build_Engineer(UrgencyType ) const
  4818. {
  4819. assert(Houses.ID(this) == ID);
  4820. return(false);
  4821. }
  4822. /*
  4823. ** Given the specified urgency, sell of some power since
  4824. ** there appears to be excess.
  4825. */
  4826. bool HouseClass::AI_Lower_Power(UrgencyType ) const
  4827. {
  4828. assert(Houses.ID(this) == ID);
  4829. BuildingClass * b = Find_Building(STRUCT_POWER);
  4830. if (b != NULL) {
  4831. b->Sell_Back(1);
  4832. return(true);
  4833. }
  4834. b = Find_Building(STRUCT_ADVANCED_POWER);
  4835. if (b != NULL) {
  4836. b->Sell_Back(1);
  4837. return(true);
  4838. }
  4839. return(false);
  4840. }
  4841. /***********************************************************************************************
  4842. * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. *
  4843. * *
  4844. * This routine is called when the computer needs to raise power by selling off buildings. *
  4845. * Usually this occurs because of some catastrophe that has lowered power levels to *
  4846. * the danger zone. *
  4847. * *
  4848. * INPUT: urgency -- The urgency that the power needs to be raised. This controls what *
  4849. * buildings will be sold. *
  4850. * *
  4851. * OUTPUT: bool; Was a building sold to raise power? *
  4852. * *
  4853. * WARNINGS: none *
  4854. * *
  4855. * HISTORY: *
  4856. * 11/02/1996 JLB : Created. *
  4857. *=============================================================================================*/
  4858. bool HouseClass::AI_Raise_Power(UrgencyType urgency) const
  4859. {
  4860. assert(Houses.ID(this) == ID);
  4861. /*
  4862. ** Sell off structures in this order.
  4863. */
  4864. static struct {
  4865. StructType Structure;
  4866. UrgencyType Urgency;
  4867. } _types[] = {
  4868. {STRUCT_CHRONOSPHERE, URGENCY_LOW},
  4869. {STRUCT_SHIP_YARD, URGENCY_LOW},
  4870. {STRUCT_SUB_PEN, URGENCY_LOW},
  4871. {STRUCT_ADVANCED_TECH, URGENCY_LOW},
  4872. {STRUCT_FORWARD_COM, URGENCY_LOW},
  4873. {STRUCT_SOVIET_TECH, URGENCY_LOW},
  4874. {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM},
  4875. {STRUCT_RADAR, URGENCY_MEDIUM},
  4876. {STRUCT_REPAIR, URGENCY_MEDIUM},
  4877. {STRUCT_TESLA, URGENCY_HIGH}
  4878. };
  4879. /*
  4880. ** Find a structure to sell and then sell it. Bail from further scanning until
  4881. ** the next time.
  4882. */
  4883. for (int i = 0; i < ARRAY_SIZE(_types); i++) {
  4884. if (urgency >= _types[i].Urgency) {
  4885. BuildingClass * b = Find_Building(_types[i].Structure);
  4886. if (b != NULL) {
  4887. b->Sell_Back(1);
  4888. return(true);
  4889. }
  4890. }
  4891. }
  4892. return(false);
  4893. }
  4894. /***********************************************************************************************
  4895. * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. *
  4896. * *
  4897. * This routine handles the situation where the computer desperately needs cash but cannot *
  4898. * wait for normal harvesting to raise it. Buildings must be sold. *
  4899. * *
  4900. * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, *
  4901. * the more important the buildings that can be sold become. *
  4902. * *
  4903. * OUTPUT: bool; Was a building sold to raise cash? *
  4904. * *
  4905. * WARNINGS: none *
  4906. * *
  4907. * HISTORY: *
  4908. * 11/02/1996 JLB : Created. *
  4909. *=============================================================================================*/
  4910. bool HouseClass::AI_Raise_Money(UrgencyType urgency) const
  4911. {
  4912. assert(Houses.ID(this) == ID);
  4913. /*
  4914. ** Sell off structures in this order.
  4915. */
  4916. static struct {
  4917. StructType Structure;
  4918. UrgencyType Urgency;
  4919. } _types[] = {
  4920. {STRUCT_CHRONOSPHERE, URGENCY_LOW},
  4921. {STRUCT_SHIP_YARD, URGENCY_LOW},
  4922. {STRUCT_SUB_PEN, URGENCY_LOW},
  4923. {STRUCT_ADVANCED_TECH, URGENCY_LOW},
  4924. {STRUCT_FORWARD_COM, URGENCY_LOW},
  4925. {STRUCT_SOVIET_TECH, URGENCY_LOW},
  4926. {STRUCT_STORAGE,URGENCY_LOW},
  4927. {STRUCT_REPAIR,URGENCY_LOW},
  4928. {STRUCT_TESLA,URGENCY_MEDIUM},
  4929. {STRUCT_HELIPAD,URGENCY_MEDIUM},
  4930. {STRUCT_POWER,URGENCY_HIGH},
  4931. {STRUCT_AIRSTRIP,URGENCY_HIGH},
  4932. // {STRUCT_WEAP,URGENCY_HIGH},
  4933. // {STRUCT_BARRACKS,URGENCY_HIGH},
  4934. // {STRUCT_TENT,URGENCY_HIGH},
  4935. {STRUCT_CONST,URGENCY_CRITICAL}
  4936. };
  4937. BuildingClass * b = 0;
  4938. /*
  4939. ** Find a structure to sell and then sell it. Bail from further scanning until
  4940. ** the next time.
  4941. */
  4942. for (int i = 0; i < ARRAY_SIZE(_types); i++) {
  4943. if (urgency >= _types[i].Urgency) {
  4944. b = Find_Building(_types[i].Structure);
  4945. if (b != NULL) {
  4946. b->Sell_Back(1);
  4947. return(true);
  4948. }
  4949. }
  4950. }
  4951. return(false);
  4952. }
  4953. #ifdef NEVER
  4954. /***********************************************************************************************
  4955. * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. *
  4956. * *
  4957. * This logic is used to maintain a base defense. *
  4958. * *
  4959. * INPUT: none *
  4960. * *
  4961. * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
  4962. * *
  4963. * WARNINGS: none *
  4964. * *
  4965. * HISTORY: *
  4966. * 09/29/1995 JLB : Created. *
  4967. *=============================================================================================*/
  4968. int HouseClass::AI_Base_Defense(void)
  4969. {
  4970. assert(Houses.ID(this) == ID);
  4971. /*
  4972. ** Check to find if any zone of the base is over defended. Such zones should have
  4973. ** some of their defenses sold off to make better use of the money.
  4974. */
  4975. /*
  4976. ** Make sure that the core defense is only about 1/2 of the perimeter defense average.
  4977. */
  4978. int average = 0;
  4979. for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) {
  4980. average += ZoneInfo[z].AirDefense;
  4981. average += ZoneInfo[z].ArmorDefense;
  4982. average += ZoneInfo[z].InfantryDefense;
  4983. }
  4984. average /= (ZONE_COUNT-ZONE_NORTH);
  4985. /*
  4986. ** If the core value is greater than the average, then sell off some of the
  4987. ** inner defensive structures.
  4988. */
  4989. int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense;
  4990. if (core >= average) {
  4991. static StructType _stype[] = {
  4992. STRUCT_GTOWER,
  4993. STRUCT_TURRET,
  4994. STRUCT_ATOWER,
  4995. STRUCT_OBELISK,
  4996. STRUCT_TESLA,
  4997. STRUCT_SAM
  4998. };
  4999. BuildingClass * b;
  5000. for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) {
  5001. b = Find_Building(_stype[index], ZONE_CORE);
  5002. if (b) {
  5003. b->Sell_Back(1);
  5004. break;
  5005. }
  5006. }
  5007. }
  5008. /*
  5009. ** If the enemy doesn't have any offensive air capability, then sell off any
  5010. ** SAM sites. Only do this when money is moderately low.
  5011. */
  5012. if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) {
  5013. /*
  5014. ** Scan to find if ANY human opponents have aircraft or a helipad. If one
  5015. ** is found then consider that opponent to have a valid air threat potential.
  5016. ** Don't sell off SAM sites in that case.
  5017. */
  5018. bool nothreat = true;
  5019. for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
  5020. HouseClass * house = HouseClass::As_Pointer(h);
  5021. if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) {
  5022. if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) {
  5023. nothreat = false;
  5024. break;
  5025. }
  5026. }
  5027. }
  5028. }
  5029. return(TICKS_PER_SECOND*5);
  5030. }
  5031. #endif
  5032. /***********************************************************************************************
  5033. * HouseClass::AI_Building -- Determines what building to build. *
  5034. * *
  5035. * This routine handles the general case of determining what building to build next. *
  5036. * *
  5037. * INPUT: none *
  5038. * *
  5039. * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
  5040. * *
  5041. * WARNINGS: none *
  5042. * *
  5043. * HISTORY: *
  5044. * 09/29/1995 JLB : Created. *
  5045. * 11/03/1996 JLB : Tries to match aircraft of enemy *
  5046. *=============================================================================================*/
  5047. int HouseClass::AI_Building(void)
  5048. {
  5049. assert(Houses.ID(this) == ID);
  5050. if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND);
  5051. if (Session.Type == GAME_NORMAL && Base.House == Class->House) {
  5052. BaseNodeClass * node = Base.Next_Buildable();
  5053. if (node) {
  5054. BuildStructure = node->Type;
  5055. }
  5056. }
  5057. if (IsBaseBuilding) {
  5058. /*
  5059. ** Don't suggest anything to build if the base is already big enough.
  5060. */
  5061. unsigned int quant = 0;
  5062. for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
  5063. HouseClass const * hptr = HouseClass::As_Pointer(h);
  5064. if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) {
  5065. quant = hptr->CurBuildings;
  5066. }
  5067. }
  5068. quant += Rule.BaseSizeAdd;
  5069. // TCTC -- Should multiply largest player base by some rational number.
  5070. // if (CurBuildings >= quant) return(TICKS_PER_SECOND);
  5071. BuildChoice.Free_All();
  5072. BuildChoiceClass * choiceptr;
  5073. StructType stype = STRUCT_NONE;
  5074. int money = Available_Money();
  5075. int level = Control.TechLevel;
  5076. bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0);
  5077. BuildingTypeClass const * b = NULL;
  5078. HouseClass const * enemy = NULL;
  5079. if (Enemy != HOUSE_NONE) {
  5080. enemy = HouseClass::As_Pointer(Enemy);
  5081. }
  5082. level = Control.TechLevel;
  5083. /*
  5084. ** Try to build a power plant if there is insufficient power and there is enough
  5085. ** money available.
  5086. */
  5087. b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER);
  5088. if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) {
  5089. choiceptr = BuildChoice.Alloc();
  5090. if (choiceptr != NULL) {
  5091. *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
  5092. }
  5093. } else {
  5094. b = &BuildingTypeClass::As_Reference(STRUCT_POWER);
  5095. if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) {
  5096. choiceptr = BuildChoice.Alloc();
  5097. if (choiceptr != NULL) {
  5098. *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
  5099. }
  5100. }
  5101. }
  5102. /*
  5103. ** Build a refinery if there isn't one already available.
  5104. */
  5105. unsigned int current = BQuantity[STRUCT_REFINERY];
  5106. if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) {
  5107. b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY);
  5108. if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) {
  5109. choiceptr = BuildChoice.Alloc();
  5110. if (choiceptr != NULL) {
  5111. *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
  5112. }
  5113. }
  5114. }
  5115. /*
  5116. ** Always make sure there is a barracks available, but only if there
  5117. ** will be sufficient money to train troopers.
  5118. */
  5119. current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT];
  5120. if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) {
  5121. b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS);
  5122. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5123. choiceptr = BuildChoice.Alloc();
  5124. if (choiceptr != NULL) {
  5125. *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
  5126. }
  5127. } else {
  5128. b = &BuildingTypeClass::As_Reference(STRUCT_TENT);
  5129. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5130. choiceptr = BuildChoice.Alloc();
  5131. if (choiceptr != NULL) {
  5132. *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
  5133. }
  5134. }
  5135. }
  5136. }
  5137. /*
  5138. ** Try to build one dog house.
  5139. */
  5140. current = BQuantity[STRUCT_KENNEL];
  5141. if (current < 1 && (money > 300 || hasincome)) {
  5142. b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL);
  5143. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5144. choiceptr = BuildChoice.Alloc();
  5145. if (choiceptr != NULL) {
  5146. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5147. }
  5148. }
  5149. }
  5150. /*
  5151. ** Try to build one gap generator.
  5152. */
  5153. current = BQuantity[STRUCT_GAP];
  5154. if (current < 1 && Power_Fraction() >= 1 && hasincome) {
  5155. b = &BuildingTypeClass::As_Reference(STRUCT_GAP);
  5156. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5157. choiceptr = BuildChoice.Alloc();
  5158. if (choiceptr != NULL) {
  5159. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5160. }
  5161. }
  5162. }
  5163. /*
  5164. ** A source of combat vehicles is always needed, but only if there will
  5165. ** be sufficient money to build vehicles.
  5166. */
  5167. current = BQuantity[STRUCT_WEAP];
  5168. if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) {
  5169. b = &BuildingTypeClass::As_Reference(STRUCT_WEAP);
  5170. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5171. choiceptr = BuildChoice.Alloc();
  5172. if (choiceptr != NULL) {
  5173. *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
  5174. }
  5175. }
  5176. }
  5177. /*
  5178. ** Always build up some base defense.
  5179. */
  5180. current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET];
  5181. if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) {
  5182. b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET);
  5183. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5184. choiceptr = BuildChoice.Alloc();
  5185. if (choiceptr != NULL) {
  5186. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5187. }
  5188. } else {
  5189. if (Percent_Chance(50)) {
  5190. b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX);
  5191. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5192. choiceptr = BuildChoice.Alloc();
  5193. if (choiceptr != NULL) {
  5194. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5195. }
  5196. }
  5197. } else {
  5198. b = &BuildingTypeClass::As_Reference(STRUCT_TURRET);
  5199. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5200. choiceptr = BuildChoice.Alloc();
  5201. if (choiceptr != NULL) {
  5202. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5203. }
  5204. }
  5205. }
  5206. }
  5207. }
  5208. /*
  5209. ** Build some air defense.
  5210. */
  5211. current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN];
  5212. if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) {
  5213. /*
  5214. ** Building air defense only makes sense if the opponent has aircraft
  5215. ** of some kind.
  5216. */
  5217. bool airthreat = false;
  5218. int threat_quantity = 0;
  5219. if (enemy != NULL && enemy->AScan != 0) {
  5220. airthreat = true;
  5221. threat_quantity = enemy->CurAircraft;
  5222. }
  5223. if (!airthreat) {
  5224. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  5225. HouseClass * h = HouseClass::As_Pointer(house);
  5226. if (h != NULL && !Is_Ally(house) && h->AScan != 0) {
  5227. airthreat = true;
  5228. break;
  5229. }
  5230. }
  5231. }
  5232. if (airthreat) {
  5233. if (BQuantity[STRUCT_RADAR] == 0) {
  5234. b = &BuildingTypeClass::As_Reference(STRUCT_RADAR);
  5235. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5236. choiceptr = BuildChoice.Alloc();
  5237. if (choiceptr != NULL) {
  5238. *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type);
  5239. }
  5240. }
  5241. }
  5242. b = &BuildingTypeClass::As_Reference(STRUCT_SAM);
  5243. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5244. choiceptr = BuildChoice.Alloc();
  5245. if (choiceptr != NULL) {
  5246. *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
  5247. }
  5248. } else {
  5249. b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN);
  5250. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5251. choiceptr = BuildChoice.Alloc();
  5252. if (choiceptr != NULL) {
  5253. *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
  5254. }
  5255. }
  5256. }
  5257. }
  5258. }
  5259. /*
  5260. ** Advanced base defense would be good.
  5261. */
  5262. current = BQuantity[STRUCT_TESLA];
  5263. if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) {
  5264. b = &BuildingTypeClass::As_Reference(STRUCT_TESLA);
  5265. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
  5266. choiceptr = BuildChoice.Alloc();
  5267. if (choiceptr != NULL) {
  5268. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5269. }
  5270. }
  5271. }
  5272. /*
  5273. ** Build a tech center as soon as possible.
  5274. */
  5275. current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH];
  5276. if (current < 1) {
  5277. b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH);
  5278. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
  5279. choiceptr = BuildChoice.Alloc();
  5280. if (choiceptr != NULL) {
  5281. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5282. }
  5283. } else {
  5284. b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH);
  5285. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
  5286. choiceptr = BuildChoice.Alloc();
  5287. if (choiceptr != NULL) {
  5288. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5289. }
  5290. }
  5291. }
  5292. }
  5293. /*
  5294. ** A helipad would be good.
  5295. */
  5296. current = BQuantity[STRUCT_HELIPAD];
  5297. if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) {
  5298. b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD);
  5299. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5300. choiceptr = BuildChoice.Alloc();
  5301. if (choiceptr != NULL) {
  5302. int threat_quantity = 0;
  5303. if (enemy != NULL) {
  5304. threat_quantity = enemy->CurAircraft;
  5305. }
  5306. *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
  5307. }
  5308. }
  5309. }
  5310. /*
  5311. ** An airstrip would be good.
  5312. */
  5313. current = BQuantity[STRUCT_AIRSTRIP];
  5314. if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) {
  5315. b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP);
  5316. if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
  5317. choiceptr = BuildChoice.Alloc();
  5318. if (choiceptr != NULL) {
  5319. int threat_quantity = 0;
  5320. if (enemy != NULL) {
  5321. threat_quantity = enemy->CurAircraft;
  5322. }
  5323. *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
  5324. }
  5325. }
  5326. }
  5327. #ifdef OLD
  5328. /*
  5329. ** Build a repair bay if there isn't one already available.
  5330. */
  5331. current = BQuantity[STRUCT_REPAIR];
  5332. if (current == 0) {
  5333. b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR);
  5334. if (Can_Build(b, ActLike) && b->Cost_Of() < money) {
  5335. choiceptr = BuildChoice.Alloc();
  5336. if (choiceptr) {
  5337. *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
  5338. }
  5339. }
  5340. }
  5341. #endif
  5342. /*
  5343. ** Pick the choice that is the most urgent.
  5344. */
  5345. UrgencyType best = URGENCY_NONE;
  5346. int bestindex;
  5347. for (int index = 0; index < BuildChoice.Count(); index++) {
  5348. if (BuildChoice.Ptr(index)->Urgency > best) {
  5349. bestindex = index;
  5350. best = BuildChoice.Ptr(index)->Urgency;
  5351. }
  5352. }
  5353. if (best != URGENCY_NONE) {
  5354. BuildStructure = BuildChoice.Ptr(bestindex)->Structure;
  5355. }
  5356. }
  5357. return(TICKS_PER_SECOND);
  5358. }
  5359. /***********************************************************************************************
  5360. * HouseClass::AI_Unit -- Determines what unit to build next. *
  5361. * *
  5362. * This routine handles the general case of determining what units to build next. *
  5363. * *
  5364. * INPUT: none *
  5365. * *
  5366. * OUTPUT: Returns with the number of games frames to delay before calling this routine again.*
  5367. * *
  5368. * WARNINGS: none *
  5369. * *
  5370. * HISTORY: *
  5371. * 09/29/1995 JLB : Created. *
  5372. *=============================================================================================*/
  5373. int HouseClass::AI_Unit(void)
  5374. {
  5375. assert(Houses.ID(this) == ID);
  5376. if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND);
  5377. if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND);
  5378. /*
  5379. ** A computer controlled house will try to build a replacement
  5380. ** harvester if possible.
  5381. */
  5382. if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) {
  5383. if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) {
  5384. BuildUnit = UNIT_HARVESTER;
  5385. return(TICKS_PER_SECOND);
  5386. }
  5387. }
  5388. if (Session.Type == GAME_NORMAL) {
  5389. int counter[UNIT_COUNT];
  5390. memset(counter, 0x00, sizeof(counter));
  5391. /*
  5392. ** Build a list of the maximum of each type we wish to produce. This will be
  5393. ** twice the number required to fill all teams.
  5394. */
  5395. int index;
  5396. for (index = 0; index < Teams.Count(); index++) {
  5397. TeamClass * tptr = Teams.Ptr(index);
  5398. if (tptr != NULL) {
  5399. TeamTypeClass const * team = tptr->Class;
  5400. if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
  5401. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5402. TechnoTypeClass const * memtype = team->Members[subindex].Class;
  5403. if (memtype->What_Am_I() == RTTI_UNITTYPE) {
  5404. counter[((UnitTypeClass const *)memtype)->Type] = 1;
  5405. }
  5406. }
  5407. }
  5408. }
  5409. }
  5410. /*
  5411. ** Team types that are flagged as prebuilt, will always try to produce enough
  5412. ** to fill one team of this type regardless of whether there is a team active
  5413. ** of that type.
  5414. */
  5415. for (index = 0; index < TeamTypes.Count(); index++) {
  5416. TeamTypeClass const * team = TeamTypes.Ptr(index);
  5417. if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
  5418. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5419. TechnoTypeClass const * memtype = team->Members[subindex].Class;
  5420. if (memtype->What_Am_I() == RTTI_UNITTYPE) {
  5421. int subtype = ((UnitTypeClass const *)memtype)->Type;
  5422. counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
  5423. }
  5424. }
  5425. }
  5426. }
  5427. /*
  5428. ** Reduce the theoretical maximum by the actual number of objects currently
  5429. ** in play.
  5430. */
  5431. for (int uindex = 0; uindex < Units.Count(); uindex++) {
  5432. UnitClass * unit = Units.Ptr(uindex);
  5433. if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) {
  5434. counter[unit->Class->Type]--;
  5435. }
  5436. }
  5437. /*
  5438. ** Pick to build the most needed object but don't consider those objects that
  5439. ** can't be built because of scenario restrictions or insufficient cash.
  5440. */
  5441. int bestval = -1;
  5442. int bestcount = 0;
  5443. UnitType bestlist[UNIT_COUNT];
  5444. for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) {
  5445. if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
  5446. if (bestval == -1 || bestval < counter[utype]) {
  5447. bestval = counter[utype];
  5448. bestcount = 0;
  5449. }
  5450. bestlist[bestcount++] = utype;
  5451. }
  5452. }
  5453. /*
  5454. ** The unit type to build is now known. Fetch a pointer to the techno type class.
  5455. */
  5456. if (bestcount) {
  5457. BuildUnit = bestlist[Random_Pick(0, bestcount-1)];
  5458. }
  5459. }
  5460. if (IsBaseBuilding) {
  5461. int counter[UNIT_COUNT];
  5462. int total = 0;
  5463. UnitType index;
  5464. for (index = UNIT_FIRST; index < UNIT_COUNT; index++) {
  5465. UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index);
  5466. if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) {
  5467. if (utype->PrimaryWeapon != NULL) {
  5468. counter[index] = 20;
  5469. } else {
  5470. counter[index] = 1;
  5471. }
  5472. } else {
  5473. counter[index] = 0;
  5474. }
  5475. total += counter[index];
  5476. }
  5477. if (total > 0) {
  5478. int choice = Random_Pick(0, total-1);
  5479. for (index = UNIT_FIRST; index < UNIT_COUNT; index++) {
  5480. if (choice < counter[index]) {
  5481. BuildUnit = index;
  5482. break;
  5483. }
  5484. choice -= counter[index];
  5485. }
  5486. }
  5487. }
  5488. return(TICKS_PER_SECOND);
  5489. }
  5490. int HouseClass::AI_Vessel(void)
  5491. {
  5492. assert(Houses.ID(this) == ID);
  5493. if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND);
  5494. if (CurVessels >= Control.MaxVessel) {
  5495. return(TICKS_PER_SECOND);
  5496. }
  5497. if (Session.Type == GAME_NORMAL) {
  5498. int counter[VESSEL_COUNT];
  5499. if (Session.Type == GAME_NORMAL) {
  5500. memset(counter, 0x00, sizeof(counter));
  5501. } else {
  5502. for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) {
  5503. if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) {
  5504. counter[index] = 16;
  5505. } else {
  5506. counter[index] = 0;
  5507. }
  5508. }
  5509. }
  5510. /*
  5511. ** Build a list of the maximum of each type we wish to produce. This will be
  5512. ** twice the number required to fill all teams.
  5513. */
  5514. int index;
  5515. for (index = 0; index < Teams.Count(); index++) {
  5516. TeamClass * tptr = Teams.Ptr(index);
  5517. if (tptr) {
  5518. TeamTypeClass const * team = tptr->Class;
  5519. if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
  5520. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5521. if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) {
  5522. counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1;
  5523. }
  5524. }
  5525. }
  5526. }
  5527. }
  5528. /*
  5529. ** Team types that are flagged as prebuilt, will always try to produce enough
  5530. ** to fill one team of this type regardless of whether there is a team active
  5531. ** of that type.
  5532. */
  5533. for (index = 0; index < TeamTypes.Count(); index++) {
  5534. TeamTypeClass const * team = TeamTypes.Ptr(index);
  5535. if (team) {
  5536. if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
  5537. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5538. if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) {
  5539. int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type;
  5540. counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
  5541. }
  5542. }
  5543. }
  5544. }
  5545. }
  5546. /*
  5547. ** Reduce the theoretical maximum by the actual number of objects currently
  5548. ** in play.
  5549. */
  5550. for (int vindex = 0; vindex < Vessels.Count(); vindex++) {
  5551. VesselClass * unit = Vessels.Ptr(vindex);
  5552. if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) {
  5553. counter[unit->Class->Type]--;
  5554. }
  5555. }
  5556. /*
  5557. ** Pick to build the most needed object but don't consider those object that
  5558. ** can't be built because of scenario restrictions or insufficient cash.
  5559. */
  5560. int bestval = -1;
  5561. int bestcount = 0;
  5562. VesselType bestlist[VESSEL_COUNT];
  5563. for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) {
  5564. if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
  5565. if (bestval == -1 || bestval < counter[utype]) {
  5566. bestval = counter[utype];
  5567. bestcount = 0;
  5568. }
  5569. bestlist[bestcount++] = utype;
  5570. }
  5571. }
  5572. /*
  5573. ** The unit type to build is now known. Fetch a pointer to the techno type class.
  5574. */
  5575. if (bestcount) {
  5576. BuildVessel = bestlist[Random_Pick(0, bestcount-1)];
  5577. }
  5578. }
  5579. if (IsBaseBuilding) {
  5580. BuildVessel = VESSEL_NONE;
  5581. }
  5582. return(TICKS_PER_SECOND);
  5583. }
  5584. /***********************************************************************************************
  5585. * HouseClass::AI_Infantry -- Determines the infantry unit to build. *
  5586. * *
  5587. * This routine handles the general case of determining what infantry unit to build *
  5588. * next. *
  5589. * *
  5590. * INPUT: none *
  5591. * *
  5592. * OUTPUT: Returns with the number of game frames to delay before being called again. *
  5593. * *
  5594. * WARNINGS: none *
  5595. * *
  5596. * HISTORY: *
  5597. * 09/29/1995 JLB : Created. *
  5598. *=============================================================================================*/
  5599. int HouseClass::AI_Infantry(void)
  5600. {
  5601. assert(Houses.ID(this) == ID);
  5602. if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND);
  5603. if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND);
  5604. if (Session.Type == GAME_NORMAL) {
  5605. TechnoTypeClass const * techno = 0;
  5606. int counter[INFANTRY_COUNT];
  5607. memset(counter, 0x00, sizeof(counter));
  5608. /*
  5609. ** Build a list of the maximum of each type we wish to produce. This will be
  5610. ** twice the number required to fill all teams.
  5611. */
  5612. int index;
  5613. for (index = 0; index < Teams.Count(); index++) {
  5614. TeamClass * tptr = Teams.Ptr(index);
  5615. if (tptr != NULL) {
  5616. TeamTypeClass const * team = tptr->Class;
  5617. if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
  5618. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5619. if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) {
  5620. counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0);
  5621. }
  5622. }
  5623. }
  5624. }
  5625. }
  5626. /*
  5627. ** Team types that are flagged as prebuilt, will always try to produce enough
  5628. ** to fill one team of this type regardless of whether there is a team active
  5629. ** of that type.
  5630. */
  5631. for (index = 0; index < TeamTypes.Count(); index++) {
  5632. TeamTypeClass const * team = TeamTypes.Ptr(index);
  5633. if (team != NULL) {
  5634. if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
  5635. for (int subindex = 0; subindex < team->ClassCount; subindex++) {
  5636. if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) {
  5637. int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type;
  5638. // counter[subtype] = 1;
  5639. counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
  5640. counter[subtype] = min(counter[subtype], 5);
  5641. }
  5642. }
  5643. }
  5644. }
  5645. }
  5646. /*
  5647. ** Reduce the theoretical maximum by the actual number of objects currently
  5648. ** in play.
  5649. */
  5650. for (int uindex = 0; uindex < Infantry.Count(); uindex++) {
  5651. InfantryClass * infantry = Infantry.Ptr(uindex);
  5652. if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) {
  5653. counter[infantry->Class->Type]--;
  5654. }
  5655. }
  5656. /*
  5657. ** Pick to build the most needed object but don't consider those object that
  5658. ** can't be built because of scenario restrictions or insufficient cash.
  5659. */
  5660. int bestval = -1;
  5661. int bestcount = 0;
  5662. InfantryType bestlist[INFANTRY_COUNT];
  5663. for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) {
  5664. if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) {
  5665. if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
  5666. if (bestval == -1 || bestval < counter[utype]) {
  5667. bestval = counter[utype];
  5668. bestcount = 0;
  5669. }
  5670. bestlist[bestcount++] = utype;
  5671. }
  5672. }
  5673. }
  5674. /*
  5675. ** The infantry type to build is now known. Fetch a pointer to the techno type class.
  5676. */
  5677. if (bestcount) {
  5678. int pick = Random_Pick(0, bestcount-1);
  5679. BuildInfantry = bestlist[pick];
  5680. }
  5681. }
  5682. if (IsBaseBuilding) {
  5683. HouseClass const * enemy = NULL;
  5684. if (Enemy != HOUSE_NONE) {
  5685. enemy = HouseClass::As_Pointer(Enemy);
  5686. }
  5687. /*
  5688. ** This structure is used to keep track of the list of infantry types that should be
  5689. ** built. The infantry type and the value assigned to it is recorded.
  5690. */
  5691. struct {
  5692. InfantryType Type; // Infantry type.
  5693. int Value; // Relative value assigned.
  5694. } typetrack[INFANTRY_COUNT];
  5695. int count = 0;
  5696. int total = 0;
  5697. for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) {
  5698. if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) {
  5699. typetrack[count].Value = 0;
  5700. #ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility.
  5701. int clipindex = index;
  5702. if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT;
  5703. if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) {
  5704. #else
  5705. if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) {
  5706. #endif
  5707. switch (index) {
  5708. case INFANTRY_E1:
  5709. typetrack[count].Value = 3;
  5710. break;
  5711. case INFANTRY_E2:
  5712. typetrack[count].Value = 5;
  5713. break;
  5714. case INFANTRY_E3:
  5715. typetrack[count].Value = 2;
  5716. break;
  5717. case INFANTRY_E4:
  5718. typetrack[count].Value = 5;
  5719. break;
  5720. case INFANTRY_RENOVATOR:
  5721. if (CurInfantry > 5) {
  5722. typetrack[count].Value = 1 - max(IQuantity[index], 0);
  5723. }
  5724. break;
  5725. case INFANTRY_TANYA:
  5726. typetrack[count].Value = 1 - max(IQuantity[index], 0);
  5727. break;
  5728. default:
  5729. typetrack[count].Value = 0;
  5730. break;
  5731. }
  5732. }
  5733. if (typetrack[count].Value > 0) {
  5734. typetrack[count].Type = index;
  5735. total += typetrack[count].Value;
  5736. count++;
  5737. }
  5738. }
  5739. }
  5740. /*
  5741. ** If there is at least one choice, then pick it. The object picked
  5742. ** is influenced by the weight (value) assigned to it. This is accomplished
  5743. ** by picking a number between 0 and the total weight value. The appropriate
  5744. ** infantry object that matches the number picked is then selected to be built.
  5745. */
  5746. if (count > 0) {
  5747. int pick = Random_Pick(0, total-1);
  5748. for (int index = 0; index < count; index++) {
  5749. if (pick < typetrack[index].Value) {
  5750. BuildInfantry = typetrack[index].Type;
  5751. break;
  5752. }
  5753. pick -= typetrack[index].Value;
  5754. }
  5755. }
  5756. }
  5757. return(TICKS_PER_SECOND);
  5758. }
  5759. /***********************************************************************************************
  5760. * HouseClass::AI_Aircraft -- Determines what aircraft to build next. *
  5761. * *
  5762. * This routine is used to determine the general case of what aircraft to build next. *
  5763. * *
  5764. * INPUT: none *
  5765. * *
  5766. * OUTPUT: Returns with the number of frame to delay before calling this routine again. *
  5767. * *
  5768. * WARNINGS: none *
  5769. * *
  5770. * HISTORY: *
  5771. * 09/29/1995 JLB : Created. *
  5772. *=============================================================================================*/
  5773. int HouseClass::AI_Aircraft(void)
  5774. {
  5775. assert(Houses.ID(this) == ID);
  5776. if (!IsHuman && IQ >= Rule.IQAircraft) {
  5777. if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND);
  5778. if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND);
  5779. if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) &&
  5780. AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel &&
  5781. BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) {
  5782. BuildAircraft = AIRCRAFT_LONGBOW;
  5783. return(TICKS_PER_SECOND);
  5784. }
  5785. if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) &&
  5786. AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel &&
  5787. BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) {
  5788. BuildAircraft = AIRCRAFT_HIND;
  5789. return(TICKS_PER_SECOND);
  5790. }
  5791. if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) &&
  5792. AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel &&
  5793. BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) {
  5794. BuildAircraft = AIRCRAFT_MIG;
  5795. return(TICKS_PER_SECOND);
  5796. }
  5797. if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) &&
  5798. AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel &&
  5799. BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) {
  5800. BuildAircraft = AIRCRAFT_YAK;
  5801. return(TICKS_PER_SECOND);
  5802. }
  5803. }
  5804. return(TICKS_PER_SECOND);
  5805. }
  5806. /***********************************************************************************************
  5807. * HouseClass::Production_Begun -- Records that production has begun. *
  5808. * *
  5809. * This routine is used to inform the Expert System that production of the specified object *
  5810. * has begun. This allows the AI to proceed with picking another object to begin production *
  5811. * on. *
  5812. * *
  5813. * INPUT: product -- Pointer to the object that production has just begun on. *
  5814. * *
  5815. * OUTPUT: none *
  5816. * *
  5817. * WARNINGS: none *
  5818. * *
  5819. * HISTORY: *
  5820. * 09/29/1995 JLB : Created. *
  5821. *=============================================================================================*/
  5822. void HouseClass::Production_Begun(TechnoClass const * product)
  5823. {
  5824. assert(Houses.ID(this) == ID);
  5825. if (product != NULL) {
  5826. switch (product->What_Am_I()) {
  5827. case RTTI_UNIT:
  5828. if (*((UnitClass*)product) == BuildUnit) {
  5829. BuildUnit = UNIT_NONE;
  5830. }
  5831. break;
  5832. case RTTI_VESSEL:
  5833. if (*((VesselClass*)product) == BuildVessel) {
  5834. BuildVessel = VESSEL_NONE;
  5835. }
  5836. break;
  5837. case RTTI_INFANTRY:
  5838. if (*((InfantryClass*)product) == BuildInfantry) {
  5839. BuildInfantry = INFANTRY_NONE;
  5840. }
  5841. break;
  5842. case RTTI_BUILDING:
  5843. if (*((BuildingClass*)product) == BuildStructure) {
  5844. BuildStructure = STRUCT_NONE;
  5845. }
  5846. break;
  5847. case RTTI_AIRCRAFT:
  5848. if (*((AircraftClass*)product) == BuildAircraft) {
  5849. BuildAircraft = AIRCRAFT_NONE;
  5850. }
  5851. break;
  5852. default:
  5853. break;
  5854. }
  5855. }
  5856. }
  5857. /***********************************************************************************************
  5858. * HouseClass::Tracking_Remove -- Remove object from house tracking system. *
  5859. * *
  5860. * This routine informs the Expert System that the specified object is no longer part of *
  5861. * this house's inventory. This occurs when the object is destroyed or captured. *
  5862. * *
  5863. * INPUT: techno -- Pointer to the object to remove from the tracking systems of this *
  5864. * house. *
  5865. * *
  5866. * OUTPUT: none *
  5867. * *
  5868. * WARNINGS: none *
  5869. * *
  5870. * HISTORY: *
  5871. * 09/29/1995 JLB : Created. *
  5872. *=============================================================================================*/
  5873. void HouseClass::Tracking_Remove(TechnoClass const * techno)
  5874. {
  5875. assert(Houses.ID(this) == ID);
  5876. int type;
  5877. switch (techno->What_Am_I()) {
  5878. case RTTI_BUILDING:
  5879. CurBuildings--;
  5880. BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--;
  5881. break;
  5882. case RTTI_AIRCRAFT:
  5883. CurAircraft--;
  5884. AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--;
  5885. break;
  5886. case RTTI_INFANTRY:
  5887. CurInfantry--;
  5888. if (!((InfantryClass *)techno)->IsTechnician) {
  5889. type = ((InfantryTypeClass const &)techno->Class_Of()).Type;
  5890. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5891. if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT;
  5892. #endif
  5893. IQuantity[type]--;
  5894. }
  5895. break;
  5896. case RTTI_UNIT:
  5897. CurUnits--;
  5898. type = ((UnitTypeClass const &)techno->Class_Of()).Type;
  5899. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5900. if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT;
  5901. #endif
  5902. UQuantity[type]--;
  5903. break;
  5904. case RTTI_VESSEL:
  5905. CurVessels--;
  5906. type = ((VesselTypeClass const &)techno->Class_Of()).Type;
  5907. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5908. if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT;
  5909. #endif
  5910. VQuantity[type]--;
  5911. break;
  5912. default:
  5913. break;
  5914. }
  5915. }
  5916. /***********************************************************************************************
  5917. * HouseClass::Tracking_Add -- Informs house of new inventory item. *
  5918. * *
  5919. * This function is called when the specified object is now available as part of the house's*
  5920. * inventory. This occurs when the object is newly produced and also when it is captured *
  5921. * by this house. *
  5922. * *
  5923. * INPUT: techno -- Pointer to the object that is now part of the house inventory. *
  5924. * *
  5925. * OUTPUT: none *
  5926. * *
  5927. * WARNINGS: none *
  5928. * *
  5929. * HISTORY: *
  5930. * 09/29/1995 JLB : Created. *
  5931. *=============================================================================================*/
  5932. void HouseClass::Tracking_Add(TechnoClass const * techno)
  5933. {
  5934. assert(Houses.ID(this) == ID);
  5935. StructType building;
  5936. AircraftType aircraft;
  5937. InfantryType infantry;
  5938. UnitType unit;
  5939. VesselType vessel;
  5940. int quant;
  5941. switch (techno->What_Am_I()) {
  5942. case RTTI_BUILDING:
  5943. CurBuildings++;
  5944. building = ((BuildingTypeClass const &)techno->Class_Of()).Type;
  5945. BQuantity[building]++;
  5946. BScan |= (1L << building);
  5947. if (Session.Type == GAME_INTERNET) {
  5948. BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID);
  5949. }
  5950. break;
  5951. case RTTI_AIRCRAFT:
  5952. CurAircraft++;
  5953. aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type;
  5954. AQuantity[aircraft]++;
  5955. AScan |= (1L << aircraft);
  5956. if (Session.Type == GAME_INTERNET) {
  5957. AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID);
  5958. }
  5959. break;
  5960. case RTTI_INFANTRY:
  5961. CurInfantry++;
  5962. infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type;
  5963. if (!((InfantryClass *)techno)->IsTechnician) {
  5964. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5965. quant = infantry;
  5966. if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT;
  5967. IQuantity[quant]++;
  5968. #else
  5969. IQuantity[infantry]++;
  5970. #endif
  5971. if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) {
  5972. InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID);
  5973. }
  5974. IScan |= (1L << infantry);
  5975. }
  5976. break;
  5977. case RTTI_UNIT:
  5978. CurUnits++;
  5979. unit = ((UnitTypeClass const &)techno->Class_Of()).Type;
  5980. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5981. quant = unit;
  5982. if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT;
  5983. UQuantity[quant]++;
  5984. #else
  5985. UQuantity[unit]++;
  5986. #endif
  5987. UScan |= (1L << unit);
  5988. if (Session.Type == GAME_INTERNET) {
  5989. UnitTotals->Increment_Unit_Total(techno->Class_Of().ID);
  5990. }
  5991. break;
  5992. case RTTI_VESSEL:
  5993. CurVessels++;
  5994. vessel = ((VesselTypeClass const &)techno->Class_Of()).Type;
  5995. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  5996. quant = vessel;
  5997. if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT;
  5998. VQuantity[quant]++;
  5999. #else
  6000. VQuantity[vessel]++;
  6001. #endif
  6002. VScan |= (1L << vessel);
  6003. if (Session.Type == GAME_INTERNET) {
  6004. VesselTotals->Increment_Unit_Total(techno->Class_Of().ID);
  6005. }
  6006. break;
  6007. default:
  6008. break;
  6009. }
  6010. }
  6011. /***********************************************************************************************
  6012. * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. *
  6013. * *
  6014. * Use this routine to fetch a pointer to the variable that holds the number of factories *
  6015. * that can produce the specified object type. This is a helper routine used when *
  6016. * examining the number of factories as well as adjusting their number. *
  6017. * *
  6018. * INPUT: rtti -- The RTTI of the object that could be produced. *
  6019. * *
  6020. * OUTPUT: Returns with the number of factories owned by this house that could produce the *
  6021. * object of the type specified. *
  6022. * *
  6023. * WARNINGS: none *
  6024. * *
  6025. * HISTORY: *
  6026. * 07/30/1996 JLB : Created. *
  6027. *=============================================================================================*/
  6028. int * HouseClass::Factory_Counter(RTTIType rtti)
  6029. {
  6030. switch (rtti) {
  6031. case RTTI_UNITTYPE:
  6032. case RTTI_UNIT:
  6033. return(&UnitFactories);
  6034. case RTTI_VESSELTYPE:
  6035. case RTTI_VESSEL:
  6036. return(&VesselFactories);
  6037. case RTTI_AIRCRAFTTYPE:
  6038. case RTTI_AIRCRAFT:
  6039. return(&AircraftFactories);
  6040. case RTTI_INFANTRYTYPE:
  6041. case RTTI_INFANTRY:
  6042. return(&InfantryFactories);
  6043. case RTTI_BUILDINGTYPE:
  6044. case RTTI_BUILDING:
  6045. return(&BuildingFactories);
  6046. default:
  6047. break;
  6048. }
  6049. return(NULL);
  6050. }
  6051. /***********************************************************************************************
  6052. * HouseClass::Active_Remove -- Remove this object from active duty for this house. *
  6053. * *
  6054. * This routine will recognize the specified object as having been removed from active *
  6055. * duty. *
  6056. * *
  6057. * INPUT: techno -- Pointer to the object to remove from active duty. *
  6058. * *
  6059. * OUTPUT: none *
  6060. * *
  6061. * WARNINGS: none *
  6062. * *
  6063. * HISTORY: *
  6064. * 07/16/1996 JLB : Created. *
  6065. *=============================================================================================*/
  6066. void HouseClass::Active_Remove(TechnoClass const * techno)
  6067. {
  6068. if (techno == NULL) return;
  6069. if (techno->What_Am_I() == RTTI_BUILDING) {
  6070. int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild);
  6071. if (fptr != NULL) {
  6072. *fptr = *fptr - 1;
  6073. }
  6074. }
  6075. }
  6076. /***********************************************************************************************
  6077. * HouseClass::Active_Add -- Add an object to active duty for this house. *
  6078. * *
  6079. * This routine will recognize the specified object as having entered active duty. Any *
  6080. * abilities granted to the house by that object are now available. *
  6081. * *
  6082. * INPUT: techno -- Pointer to the object that is entering active duty. *
  6083. * *
  6084. * OUTPUT: none *
  6085. * *
  6086. * WARNINGS: none *
  6087. * *
  6088. * HISTORY: *
  6089. * 07/16/1996 JLB : Created. *
  6090. *=============================================================================================*/
  6091. void HouseClass::Active_Add(TechnoClass const * techno)
  6092. {
  6093. if (techno == NULL) return;
  6094. if (techno->What_Am_I() == RTTI_BUILDING) {
  6095. int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild);
  6096. if (fptr != NULL) {
  6097. *fptr = *fptr + 1;
  6098. }
  6099. }
  6100. }
  6101. /***********************************************************************************************
  6102. * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. *
  6103. * *
  6104. * This routine will determine what zone the specified coordinate lies in with respect to *
  6105. * this house's base. A location that is too distant from the base, even though it might *
  6106. * be a building, is not considered part of the base and returns ZONE_NONE. *
  6107. * *
  6108. * INPUT: coord -- The coordinate to examine. *
  6109. * *
  6110. * OUTPUT: Returns with the base zone that the specified coordinate lies in. *
  6111. * *
  6112. * WARNINGS: none *
  6113. * *
  6114. * HISTORY: *
  6115. * 10/02/1995 JLB : Created. *
  6116. *=============================================================================================*/
  6117. ZoneType HouseClass::Which_Zone(COORDINATE coord) const
  6118. {
  6119. assert(Houses.ID(this) == ID);
  6120. if (coord == 0) return(ZONE_NONE);
  6121. int distance = Distance(Center, coord);
  6122. if (distance <= Radius) return(ZONE_CORE);
  6123. if (distance > Radius*4) return(ZONE_NONE);
  6124. DirType facing = Direction(Center, coord);
  6125. if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH);
  6126. if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST);
  6127. if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH);
  6128. return(ZONE_WEST);
  6129. }
  6130. /***********************************************************************************************
  6131. * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. *
  6132. * *
  6133. * Use this routine to determine what zone the specified object lies in. *
  6134. * *
  6135. * INPUT: object -- Pointer to the object that will be checked for zone occupation. *
  6136. * *
  6137. * OUTPUT: Returns with the base zone that the object lies in. For objects that are too *
  6138. * distant from the center of the base, ZONE_NONE is returned. *
  6139. * *
  6140. * WARNINGS: none *
  6141. * *
  6142. * HISTORY: *
  6143. * 10/02/1995 JLB : Created. *
  6144. *=============================================================================================*/
  6145. ZoneType HouseClass::Which_Zone(ObjectClass const * object) const
  6146. {
  6147. assert(Houses.ID(this) == ID);
  6148. if (!object) return(ZONE_NONE);
  6149. return(Which_Zone(object->Center_Coord()));
  6150. }
  6151. /***********************************************************************************************
  6152. * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. *
  6153. * *
  6154. * This routine is used to determine what base zone the specified cell is in. *
  6155. * *
  6156. * INPUT: cell -- The cell to examine. *
  6157. * *
  6158. * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far *
  6159. * away. *
  6160. * *
  6161. * WARNINGS: none *
  6162. * *
  6163. * HISTORY: *
  6164. * 10/02/1995 JLB : Created. *
  6165. *=============================================================================================*/
  6166. ZoneType HouseClass::Which_Zone(CELL cell) const
  6167. {
  6168. assert(Houses.ID(this) == ID);
  6169. return(Which_Zone(Cell_Coord(cell)));
  6170. }
  6171. /***********************************************************************************************
  6172. * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. *
  6173. * *
  6174. * This routine will go through all game objects and reset the existence bits for the *
  6175. * owning house. This method ensures that if the object exists, then the corresponding *
  6176. * existence bit is also set. *
  6177. * *
  6178. * INPUT: none *
  6179. * *
  6180. * OUTPUT: none *
  6181. * *
  6182. * WARNINGS: none *
  6183. * *
  6184. * HISTORY: *
  6185. * 10/02/1995 JLB : Created. *
  6186. *=============================================================================================*/
  6187. void HouseClass::Recalc_Attributes(void)
  6188. {
  6189. /*
  6190. ** Clear out all tracking values that will be filled in by this
  6191. ** routine. This allows the filling in process to not worry about
  6192. ** old existing values.
  6193. */
  6194. int index;
  6195. for (index = 0; index < Houses.Count(); index++) {
  6196. HouseClass * house = Houses.Ptr(index);
  6197. if (house != NULL) {
  6198. house->BScan = 0;
  6199. house->ActiveBScan = 0;
  6200. house->IScan = 0;
  6201. house->ActiveIScan = 0;
  6202. house->UScan = 0;
  6203. house->ActiveUScan = 0;
  6204. house->AScan = 0;
  6205. house->ActiveAScan = 0;
  6206. house->VScan = 0;
  6207. house->ActiveVScan = 0;
  6208. }
  6209. }
  6210. /*
  6211. ** A second pass through the sentient objects is required so that the appropriate scan
  6212. ** bits will be set for the owner house.
  6213. */
  6214. for (index = 0; index < Units.Count(); index++) {
  6215. UnitClass const * unit = Units.Ptr(index);
  6216. unit->House->UScan |= (1L << unit->Class->Type);
  6217. if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) {
  6218. if (!unit->IsInLimbo) {
  6219. unit->House->ActiveUScan |= (1L << unit->Class->Type);
  6220. }
  6221. }
  6222. }
  6223. for (index = 0; index < Infantry.Count(); index++) {
  6224. InfantryClass const * infantry = Infantry.Ptr(index);
  6225. infantry->House->IScan |= (1L << infantry->Class->Type);
  6226. if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) {
  6227. if (!infantry->IsInLimbo) {
  6228. infantry->House->ActiveIScan |= (1L << infantry->Class->Type);
  6229. infantry->House->OldIScan |= (1L << infantry->Class->Type);
  6230. }
  6231. }
  6232. }
  6233. for (index = 0; index < Aircraft.Count(); index++) {
  6234. AircraftClass const * aircraft = Aircraft.Ptr(index);
  6235. aircraft->House->AScan |= (1L << aircraft->Class->Type);
  6236. if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) {
  6237. if (!aircraft->IsInLimbo) {
  6238. aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type);
  6239. aircraft->House->OldAScan |= (1L << aircraft->Class->Type);
  6240. }
  6241. }
  6242. }
  6243. for (index = 0; index < Buildings.Count(); index++) {
  6244. BuildingClass const * building = Buildings.Ptr(index);
  6245. if (building->Class->Type < 32) {
  6246. building->House->BScan |= (1L << building->Class->Type);
  6247. if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) {
  6248. if (!building->IsInLimbo) {
  6249. building->House->ActiveBScan |= (1L << building->Class->Type);
  6250. building->House->OldBScan |= (1L << building->Class->Type);
  6251. }
  6252. }
  6253. }
  6254. }
  6255. for (index = 0; index < Vessels.Count(); index++) {
  6256. VesselClass const * vessel = Vessels.Ptr(index);
  6257. vessel->House->VScan |= (1L << vessel->Class->Type);
  6258. if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) {
  6259. if (!vessel->IsInLimbo) {
  6260. vessel->House->ActiveVScan |= (1L << vessel->Class->Type);
  6261. vessel->House->OldVScan |= (1L << vessel->Class->Type);
  6262. }
  6263. }
  6264. }
  6265. }
  6266. /***********************************************************************************************
  6267. * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. *
  6268. * *
  6269. * This routine is used to find the cell that is closest to the center point of the *
  6270. * zone specified. Typical use of this routine is for building and unit placement so that *
  6271. * they can "cover" the specified zone. *
  6272. * *
  6273. * INPUT: zone -- The zone that the center point is to be returned. *
  6274. * *
  6275. * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. *
  6276. * *
  6277. * WARNINGS: none *
  6278. * *
  6279. * HISTORY: *
  6280. * 10/02/1995 JLB : Created. *
  6281. *=============================================================================================*/
  6282. CELL HouseClass::Zone_Cell(ZoneType zone) const
  6283. {
  6284. assert(Houses.ID(this) == ID);
  6285. switch (zone) {
  6286. case ZONE_CORE:
  6287. return(Coord_Cell(Center));
  6288. case ZONE_NORTH:
  6289. return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3)));
  6290. case ZONE_EAST:
  6291. return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3)));
  6292. case ZONE_WEST:
  6293. return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3)));
  6294. case ZONE_SOUTH:
  6295. return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3)));
  6296. default:
  6297. break;
  6298. }
  6299. return(0);
  6300. }
  6301. /***********************************************************************************************
  6302. * HouseClass::Where_To_Go -- Determines where the object should go and wait. *
  6303. * *
  6304. * This function is called for every new unit produced or delivered in order to determine *
  6305. * where the unit should "hang out" to await further orders. The best area for the *
  6306. * unit to loiter is returned as a cell location. *
  6307. * *
  6308. * INPUT: object -- Pointer to the object that needs to know where to go. *
  6309. * *
  6310. * OUTPUT: Returns with the cell that the unit should move to. *
  6311. * *
  6312. * WARNINGS: none *
  6313. * *
  6314. * HISTORY: *
  6315. * 10/02/1995 JLB : Created. *
  6316. * 11/04/1996 JLB : Simplified to use helper functions *
  6317. *=============================================================================================*/
  6318. CELL HouseClass::Where_To_Go(FootClass const * object) const
  6319. {
  6320. assert(Houses.ID(this) == ID);
  6321. assert(object != NULL);
  6322. ZoneType zone; // The zone that the object should go to.
  6323. if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) {
  6324. zone = ZONE_CORE;
  6325. } else {
  6326. zone = Random_Pick(ZONE_NORTH, ZONE_WEST);
  6327. }
  6328. CELL cell = Random_Cell_In_Zone(zone);
  6329. assert(cell != 0);
  6330. return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL));
  6331. }
  6332. /***********************************************************************************************
  6333. * HouseClass::Find_Juicy_Target -- Finds a suitable field target. *
  6334. * *
  6335. * This routine is used to find targets out in the field and away from base defense. *
  6336. * Typical of this would be the attack helicopters and the roving attack bands of *
  6337. * hunter killers. *
  6338. * *
  6339. * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. *
  6340. * *
  6341. * OUTPUT: Returns with a suitable target to attack. *
  6342. * *
  6343. * WARNINGS: none *
  6344. * *
  6345. * HISTORY: *
  6346. * 10/12/1995 JLB : Created. *
  6347. *=============================================================================================*/
  6348. TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const
  6349. {
  6350. assert(Houses.ID(this) == ID);
  6351. UnitClass * best = 0;
  6352. int value = 0;
  6353. for (int index = 0; index < Units.Count(); index++) {
  6354. UnitClass * unit = Units.Ptr(index);
  6355. if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) {
  6356. int val = Distance(coord, unit->Center_Coord());
  6357. if (unit->Anti_Air()) val *= 2;
  6358. if (*unit == UNIT_HARVESTER) val /= 2;
  6359. if (value == 0 || val < value) {
  6360. value = val;
  6361. best = unit;
  6362. }
  6363. }
  6364. }
  6365. if (best) {
  6366. return(best->As_Target());
  6367. }
  6368. return(TARGET_NONE);
  6369. }
  6370. /***********************************************************************************************
  6371. * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. *
  6372. * *
  6373. * Call this routine to fetch the total quantity of aircraft of the type specified that is *
  6374. * owned by this house. *
  6375. * *
  6376. * INPUT: aircraft -- The aircraft type to check the quantity of. *
  6377. * *
  6378. * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this *
  6379. * house. *
  6380. * *
  6381. * WARNINGS: none *
  6382. * *
  6383. * HISTORY: *
  6384. * 07/09/1996 JLB : Created. *
  6385. *=============================================================================================*/
  6386. int HouseClass::Get_Quantity(AircraftType aircraft)
  6387. {
  6388. return(AQuantity[aircraft]);
  6389. }
  6390. /***********************************************************************************************
  6391. * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. *
  6392. * *
  6393. * This is the counterpart to the Set_Factory function. It will return with a factory *
  6394. * pointer that is associated with the object type specified. *
  6395. * *
  6396. * INPUT: rtti -- The RTTI of the object type to find the factory for. *
  6397. * *
  6398. * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the *
  6399. * object type specified. *
  6400. * *
  6401. * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy *
  6402. * producing another unit of that category. *
  6403. * *
  6404. * HISTORY: *
  6405. * 07/09/1996 JLB : Created. *
  6406. *=============================================================================================*/
  6407. FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const
  6408. {
  6409. int factory_index = -1;
  6410. switch (rtti) {
  6411. case RTTI_INFANTRY:
  6412. case RTTI_INFANTRYTYPE:
  6413. factory_index = InfantryFactory;
  6414. break;
  6415. case RTTI_UNIT:
  6416. case RTTI_UNITTYPE:
  6417. factory_index = UnitFactory;
  6418. break;
  6419. case RTTI_BUILDING:
  6420. case RTTI_BUILDINGTYPE:
  6421. factory_index = BuildingFactory;
  6422. break;
  6423. case RTTI_AIRCRAFT:
  6424. case RTTI_AIRCRAFTTYPE:
  6425. factory_index = AircraftFactory;
  6426. break;
  6427. case RTTI_VESSEL:
  6428. case RTTI_VESSELTYPE:
  6429. factory_index = VesselFactory;
  6430. break;
  6431. default:
  6432. factory_index = -1;
  6433. break;
  6434. }
  6435. /*
  6436. ** Fetch the actual pointer to the factory object. If there is
  6437. ** no object factory that matches the specified rtti type, then
  6438. ** null is returned.
  6439. */
  6440. if (factory_index != -1) {
  6441. return(Factories.Raw_Ptr(factory_index));
  6442. }
  6443. return(NULL);
  6444. }
  6445. /***********************************************************************************************
  6446. * HouseClass::Set_Factory -- Assign specified factory to house tracking. *
  6447. * *
  6448. * Call this routine when a factory has been created and it now must be passed on to the *
  6449. * house for tracking purposes. The house maintains several factory pointers and this *
  6450. * routine will ensure that the factory pointer gets stored correctly. *
  6451. * *
  6452. * INPUT: rtti -- The RTTI of the object the factory it to manufacture. *
  6453. * *
  6454. * factory -- The factory object pointer. *
  6455. * *
  6456. * OUTPUT: none *
  6457. * *
  6458. * WARNINGS: none *
  6459. * *
  6460. * HISTORY: *
  6461. * 07/09/1996 JLB : Created. *
  6462. *=============================================================================================*/
  6463. void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory)
  6464. {
  6465. int * factory_index = 0;
  6466. assert(rtti != RTTI_NONE);
  6467. switch (rtti) {
  6468. case RTTI_UNIT:
  6469. case RTTI_UNITTYPE:
  6470. factory_index = &UnitFactory;
  6471. break;
  6472. case RTTI_INFANTRY:
  6473. case RTTI_INFANTRYTYPE:
  6474. factory_index = &InfantryFactory;
  6475. break;
  6476. case RTTI_VESSEL:
  6477. case RTTI_VESSELTYPE:
  6478. factory_index = &VesselFactory;
  6479. break;
  6480. case RTTI_BUILDING:
  6481. case RTTI_BUILDINGTYPE:
  6482. factory_index = &BuildingFactory;
  6483. break;
  6484. case RTTI_AIRCRAFT:
  6485. case RTTI_AIRCRAFTTYPE:
  6486. factory_index = &AircraftFactory;
  6487. break;
  6488. }
  6489. assert(factory_index != NULL);
  6490. /*
  6491. ** Assign the factory to the appropriate slot. For the case of clearing
  6492. ** the factory out, then -1 is assigned.
  6493. */
  6494. if (factory != NULL) {
  6495. *factory_index = factory->ID;
  6496. } else {
  6497. *factory_index = -1;
  6498. }
  6499. }
  6500. /***********************************************************************************************
  6501. * HouseClass::Factory_Count -- Fetches the number of factories for specified type. *
  6502. * *
  6503. * This routine will count the number of factories owned by this house that can build *
  6504. * objects of the specified type. *
  6505. * *
  6506. * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. *
  6507. * *
  6508. * OUTPUT: Returns with the number of factories that can build the object type specified. *
  6509. * *
  6510. * WARNINGS: none *
  6511. * *
  6512. * HISTORY: *
  6513. * 07/30/1996 JLB : Created. *
  6514. *=============================================================================================*/
  6515. int HouseClass::Factory_Count(RTTIType rtti) const
  6516. {
  6517. int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti);
  6518. if (ptr != NULL) {
  6519. return(*ptr);
  6520. }
  6521. return(0);
  6522. }
  6523. /***********************************************************************************************
  6524. * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. *
  6525. * *
  6526. * This will return the total number of buildings of that type owned by this house. *
  6527. * *
  6528. * INPUT: building -- The building type to check. *
  6529. * *
  6530. * OUTPUT: Returns with the number of buildings of that type owned by this house. *
  6531. * *
  6532. * WARNINGS: none *
  6533. * *
  6534. * HISTORY: *
  6535. * 07/09/1996 JLB : Created. *
  6536. *=============================================================================================*/
  6537. int HouseClass::Get_Quantity(StructType building)
  6538. {
  6539. return(BQuantity[building]);
  6540. }
  6541. /***********************************************************************************************
  6542. * HouseClass::Read_INI -- Reads house specific data from INI. *
  6543. * *
  6544. * This routine reads the house specific data for a particular *
  6545. * scenario from the scenario INI file. Typical data includes starting *
  6546. * credits, maximum unit count, etc. *
  6547. * *
  6548. * INPUT: buffer -- Pointer to loaded scenario INI file. *
  6549. * *
  6550. * OUTPUT: none *
  6551. * *
  6552. * WARNINGS: none *
  6553. * *
  6554. * HISTORY: *
  6555. * 05/24/1994 JLB : Created. *
  6556. * 05/18/1995 JLB : Creates all houses. *
  6557. *=============================================================================================*/
  6558. void HouseClass::Read_INI(CCINIClass & ini)
  6559. {
  6560. HouseClass * p; // Pointer to current player data.
  6561. char const * hname; // Pointer to house name.
  6562. for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
  6563. hname = HouseTypeClass::As_Reference(index).IniName;
  6564. p = new HouseClass(index);
  6565. p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario);
  6566. p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding);
  6567. p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit);
  6568. p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry);
  6569. p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel);
  6570. if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit;
  6571. p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100;
  6572. p->Credits = p->Control.InitialCredits;
  6573. int iq = ini.Get_Int(hname, "IQ", 0);
  6574. if (iq > Rule.MaxIQ) iq = 1;
  6575. p->IQ = p->Control.IQ = iq;
  6576. p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH);
  6577. p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false);
  6578. int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL));
  6579. p->Make_Ally(index);
  6580. p->Make_Ally(HOUSE_NEUTRAL);
  6581. for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
  6582. if ((owners & (1 << h)) != 0) {
  6583. p->Make_Ally(h);
  6584. }
  6585. }
  6586. }
  6587. }
  6588. /***********************************************************************************************
  6589. * HouseClass::Write_INI -- Writes the house data to the INI database. *
  6590. * *
  6591. * This routine will write out all data necessary to recreate it in anticipation of a *
  6592. * new scenario. All houses (that are active) will have their scenario type data written *
  6593. * out. *
  6594. * *
  6595. * INPUT: ini -- Reference to the INI database to write the data to. *
  6596. * *
  6597. * OUTPUT: none *
  6598. * *
  6599. * WARNINGS: none *
  6600. * *
  6601. * HISTORY: *
  6602. * 07/09/1996 JLB : Created. *
  6603. *=============================================================================================*/
  6604. void HouseClass::Write_INI(CCINIClass & ini)
  6605. {
  6606. /*
  6607. ** The identity house control object. Only if the house value differs from the
  6608. ** identity, will the data be written out.
  6609. */
  6610. HouseStaticClass control;
  6611. for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) {
  6612. HouseClass * p = As_Pointer(i);
  6613. if (p != NULL) {
  6614. char const * name = p->Class->IniName;
  6615. ini.Clear(name);
  6616. if (i >= HOUSE_MULTI1) continue;
  6617. if (p->Control.InitialCredits != control.InitialCredits) {
  6618. ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100));
  6619. }
  6620. if (p->Control.Edge != control.Edge) {
  6621. ini.Put_SourceType(name, "Edge", p->Control.Edge);
  6622. }
  6623. if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) {
  6624. ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit);
  6625. }
  6626. if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) {
  6627. ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry);
  6628. }
  6629. if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) {
  6630. ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding);
  6631. }
  6632. if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) {
  6633. ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel);
  6634. }
  6635. if (p->Control.TechLevel != control.TechLevel) {
  6636. ini.Put_Int(name, "TechLevel", p->Control.TechLevel);
  6637. }
  6638. if (p->Control.IQ != control.IQ) {
  6639. ini.Put_Int(name, "IQ", p->Control.IQ);
  6640. }
  6641. if (p->IsPlayerControl != false && p != PlayerPtr) {
  6642. ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl);
  6643. }
  6644. ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL)));
  6645. }
  6646. }
  6647. }
  6648. /***********************************************************************************************
  6649. * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. *
  6650. * *
  6651. * This routine will examine the current yak and mig situation verses airfields. If there *
  6652. * are equal aircraft to airfields, then this routine will return TRUE. *
  6653. * *
  6654. * INPUT: none *
  6655. * *
  6656. * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? *
  6657. * *
  6658. * WARNINGS: none *
  6659. * *
  6660. * HISTORY: *
  6661. * 09/23/1996 JLB : Created. *
  6662. *=============================================================================================*/
  6663. bool HouseClass::Is_No_YakMig(void) const
  6664. {
  6665. int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG];
  6666. /*
  6667. ** Adjust the quantity down one if there is an aircraft in production. This will
  6668. ** allow production to resume after being held.
  6669. */
  6670. FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT);
  6671. if (factory != NULL && factory->Get_Object() != NULL) {
  6672. AircraftClass const * air = (AircraftClass const *)factory->Get_Object();
  6673. if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) {
  6674. quantity -= 1;
  6675. }
  6676. }
  6677. if (quantity >= BQuantity[STRUCT_AIRSTRIP]) {
  6678. return(true);
  6679. }
  6680. return(false);
  6681. }
  6682. /***********************************************************************************************
  6683. * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? *
  6684. * *
  6685. * This is a special hack check routine to see if the object type and id specified is *
  6686. * prevented from being produced. The Yak and the Mig are so prevented if there would be *
  6687. * insufficient airfields for them to land upon. *
  6688. * *
  6689. * INPUT: rtti -- The RTTI type of the value specified. *
  6690. * *
  6691. * value -- The type number (according to the RTTI type specified). *
  6692. * *
  6693. * OUTPUT: bool; Is production of this object prohibited? *
  6694. * *
  6695. * WARNINGS: none *
  6696. * *
  6697. * HISTORY: *
  6698. * 09/23/1996 JLB : Created. *
  6699. *=============================================================================================*/
  6700. bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const
  6701. {
  6702. if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) {
  6703. return(Is_No_YakMig());
  6704. }
  6705. return(false);
  6706. }
  6707. /***********************************************************************************************
  6708. * HouseClass::Fire_Sale -- Cause all buildings to be sold. *
  6709. * *
  6710. * This routine will sell back all buildings owned by this house. *
  6711. * *
  6712. * INPUT: none *
  6713. * *
  6714. * OUTPUT: bool; Was a fire sale performed? *
  6715. * *
  6716. * WARNINGS: none *
  6717. * *
  6718. * HISTORY: *
  6719. * 09/23/1996 JLB : Created. *
  6720. *=============================================================================================*/
  6721. bool HouseClass::Fire_Sale(void)
  6722. {
  6723. if (CurBuildings > 0) {
  6724. for (int index = 0; index < Buildings.Count(); index++) {
  6725. BuildingClass * b = Buildings.Ptr(index);
  6726. if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) {
  6727. b->Sell_Back(1);
  6728. }
  6729. }
  6730. return(true);
  6731. }
  6732. return(false);
  6733. }
  6734. /***********************************************************************************************
  6735. * HouseClass::Do_All_To_Hunt -- Send all units to hunt. *
  6736. * *
  6737. * This routine will cause all combatants of this house to go into hunt mode. The effect of *
  6738. * this is to throw everything this house has to muster at the enemies of this house. *
  6739. * *
  6740. * INPUT: none *
  6741. * *
  6742. * OUTPUT: none *
  6743. * *
  6744. * WARNINGS: none *
  6745. * *
  6746. * HISTORY: *
  6747. * 09/23/1996 JLB : Created. *
  6748. * 10/02/1996 JLB : Handles aircraft too. *
  6749. *=============================================================================================*/
  6750. void HouseClass::Do_All_To_Hunt(void) const
  6751. {
  6752. int index;
  6753. for (index = 0; index < Units.Count(); index++) {
  6754. UnitClass * unit = Units.Ptr(index);
  6755. if (unit->House == this && unit->IsDown && !unit->IsInLimbo) {
  6756. if (unit->Team) unit->Team->Remove(unit);
  6757. unit->Assign_Mission(MISSION_HUNT);
  6758. }
  6759. }
  6760. for (index = 0; index < Infantry.Count(); index++) {
  6761. InfantryClass * infantry = Infantry.Ptr(index);
  6762. if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) {
  6763. if (infantry->Team) infantry->Team->Remove(infantry);
  6764. infantry->Assign_Mission(MISSION_HUNT);
  6765. }
  6766. }
  6767. for (index = 0; index < Vessels.Count(); index++) {
  6768. VesselClass * vessel = Vessels.Ptr(index);
  6769. if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) {
  6770. if (vessel->Team) vessel->Team->Remove(vessel);
  6771. vessel->Assign_Mission(MISSION_HUNT);
  6772. }
  6773. }
  6774. for (index = 0; index < Aircraft.Count(); index++) {
  6775. AircraftClass * aircraft = Aircraft.Ptr(index);
  6776. if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) {
  6777. if (aircraft->Team) aircraft->Team->Remove(aircraft);
  6778. aircraft->Assign_Mission(MISSION_HUNT);
  6779. }
  6780. }
  6781. }
  6782. /***********************************************************************************************
  6783. * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. *
  6784. * *
  6785. * Use this routine to determine if this house is legally allowed to ally with the *
  6786. * house specified. There are many reason why an alliance is not allowed. Typically, this *
  6787. * is when there would be no more opponents left to fight or if this house has been *
  6788. * defeated. *
  6789. * *
  6790. * INPUT: house -- The house that alliance with is desired. *
  6791. * *
  6792. * OUTPUT: bool; Is alliance with the house specified prohibited? *
  6793. * *
  6794. * WARNINGS: none *
  6795. * *
  6796. * HISTORY: *
  6797. * 09/23/1996 JLB : Created. *
  6798. *=============================================================================================*/
  6799. bool HouseClass::Is_Allowed_To_Ally(HousesType house) const
  6800. {
  6801. /*
  6802. ** Is not allowed to ally with a house that is patently invalid, such
  6803. ** as one that is illegally defined.
  6804. */
  6805. if (house == HOUSE_NONE) {
  6806. return(false);
  6807. }
  6808. /*
  6809. ** One cannot ally twice with the same house.
  6810. */
  6811. if (Is_Ally(house)) {
  6812. return(false);
  6813. }
  6814. /*
  6815. ** If the scenario is being set up, then alliances are always
  6816. ** allowed. No further checking is required.
  6817. */
  6818. if (ScenarioInit) {
  6819. return(true);
  6820. }
  6821. /*
  6822. ** Alliances (outside of scneario init time) are allowed only if
  6823. ** this is a multiplayer game. Otherwise, they are prohibited.
  6824. */
  6825. if (Session.Type == GAME_NORMAL) {
  6826. return(false);
  6827. }
  6828. /*
  6829. ** When the house is defeated, it can no longer make alliances.
  6830. */
  6831. if (IsDefeated) {
  6832. return(false);
  6833. }
  6834. #ifdef FIXIT_VERSION_3
  6835. // Fix to prevent ally with computer.
  6836. if ( !HouseClass::As_Pointer(house)->IsHuman) {
  6837. return(false);
  6838. }
  6839. #else // FIXIT_VERSION_3
  6840. #ifdef FIXIT_NO_COMP_ALLY
  6841. // Fix to prevent ally with computer.
  6842. if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) {
  6843. return(false);
  6844. }
  6845. #endif
  6846. #endif // FIXIT_VERSION_3
  6847. /*
  6848. ** Count the number of active houses in the game as well as the
  6849. ** number of existing allies with this house.
  6850. */
  6851. int housecount = 0;
  6852. int allycount = 0;
  6853. for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) {
  6854. HouseClass * hptr = HouseClass::As_Pointer(house2);
  6855. if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) {
  6856. housecount++;
  6857. if (Is_Ally(hptr)) {
  6858. allycount++;
  6859. }
  6860. }
  6861. }
  6862. /*
  6863. ** Alliance is not allowed if there wouldn't be any enemies left to
  6864. ** fight.
  6865. */
  6866. if (housecount == allycount+1) {
  6867. return(false);
  6868. }
  6869. return(true);
  6870. }
  6871. /***********************************************************************************************
  6872. * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. *
  6873. * *
  6874. * This routine will cause the computer players to become suspicious of the human *
  6875. * players and thus the computer players will band together in order to defeat the *
  6876. * human players. *
  6877. * *
  6878. * INPUT: none *
  6879. * *
  6880. * OUTPUT: none *
  6881. * *
  6882. * WARNINGS: none *
  6883. * *
  6884. * HISTORY: *
  6885. * 09/23/1996 JLB : Created. *
  6886. *=============================================================================================*/
  6887. void HouseClass::Computer_Paranoid(void)
  6888. {
  6889. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019
  6890. /*
  6891. ** Loop through every computer controlled house and make allies with all other computer
  6892. ** controlled houses and then make enemies with all other human controlled houses.
  6893. */
  6894. for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
  6895. HouseClass * hptr = HouseClass::As_Pointer(house);
  6896. if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) {
  6897. hptr->IsParanoid = true;
  6898. /*
  6899. ** Break alliance with every human it is allied with and make friends with
  6900. ** any other computer players.
  6901. */
  6902. for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) {
  6903. HouseClass * hptr2 = HouseClass::As_Pointer(house2);
  6904. if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) {
  6905. if (hptr2->IsHuman) {
  6906. hptr->Make_Enemy(house2);
  6907. } else {
  6908. hptr->Make_Ally(house2);
  6909. }
  6910. }
  6911. }
  6912. }
  6913. }
  6914. }
  6915. }
  6916. /***********************************************************************************************
  6917. * HouseClass::Adjust_Power -- Adjust the power value of the house. *
  6918. * *
  6919. * This routine will update the power output value of the house. It will cause any buildgins*
  6920. * that need to be redrawn to do so. *
  6921. * *
  6922. * INPUT: adjust -- The amount to adjust the power output value. *
  6923. * *
  6924. * OUTPUT: none *
  6925. * *
  6926. * WARNINGS: none *
  6927. * *
  6928. * HISTORY: *
  6929. * 11/01/1996 BWG : Created. *
  6930. *=============================================================================================*/
  6931. void HouseClass::Adjust_Power(int adjust)
  6932. {
  6933. Power += adjust;
  6934. Update_Spied_Power_Plants();
  6935. }
  6936. /***********************************************************************************************
  6937. * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. *
  6938. * *
  6939. * This routine will update the drain value of the house. It will cause any buildings that *
  6940. * need to be redraw to do so. *
  6941. * *
  6942. * INPUT: adjust -- The amount to adjust the drain (positive means more drain). *
  6943. * *
  6944. * OUTPUT: none *
  6945. * *
  6946. * WARNINGS: none *
  6947. * *
  6948. * HISTORY: *
  6949. * 11/01/1996 BWG : Created. *
  6950. *=============================================================================================*/
  6951. void HouseClass::Adjust_Drain(int adjust)
  6952. {
  6953. Drain += adjust;
  6954. Update_Spied_Power_Plants();
  6955. }
  6956. /***********************************************************************************************
  6957. * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. *
  6958. * *
  6959. * INPUT: none *
  6960. * *
  6961. * OUTPUT: none *
  6962. * *
  6963. * WARNINGS: none *
  6964. * *
  6965. * HISTORY: *
  6966. * 10/11/1996 BWG : Created. *
  6967. *=============================================================================================*/
  6968. void HouseClass::Update_Spied_Power_Plants(void)
  6969. {
  6970. int count = CurrentObject.Count();
  6971. if (count) {
  6972. for (int index = 0; index < count; index++) {
  6973. ObjectClass const * tech = CurrentObject[index];
  6974. if (tech && tech->What_Am_I()==RTTI_BUILDING) {
  6975. BuildingClass *bldg = (BuildingClass *)tech;
  6976. if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) {
  6977. if ( bldg->Spied_By() & (1<<(PlayerPtr->Class->House)) ) {
  6978. bldg->Mark(MARK_CHANGE);
  6979. }
  6980. }
  6981. }
  6982. }
  6983. }
  6984. }
  6985. /***********************************************************************************************
  6986. * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. *
  6987. * *
  6988. * Use this routine to determine where the specified object should go if it were to go *
  6989. * some random (but legal) location within the zone specified. *
  6990. * *
  6991. * INPUT: techno -- The object that is desirous of going into the zone specified. *
  6992. * *
  6993. * zone -- The zone to find a location within. *
  6994. * *
  6995. * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If *
  6996. * no valid location could be found, then 0 is returned. *
  6997. * *
  6998. * WARNINGS: none *
  6999. * *
  7000. * HISTORY: *
  7001. * 11/01/1996 JLB : Created. *
  7002. * 11/04/1996 JLB : Not so strict on zone requirement. *
  7003. *=============================================================================================*/
  7004. CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const
  7005. {
  7006. if (techno == NULL) return(0);
  7007. int bestval = -1;
  7008. int bestcell = 0;
  7009. TechnoTypeClass const * ttype = techno->Techno_Type_Class();
  7010. /*
  7011. ** Pick a random location within the zone specified.
  7012. */
  7013. CELL trycell = Random_Cell_In_Zone(zone);
  7014. short const * list = NULL;
  7015. if (techno->What_Am_I() == RTTI_BUILDING) {
  7016. list = techno->Occupy_List(true);
  7017. }
  7018. /*
  7019. ** Find a legal placement position as close as possible to the picked location while still
  7020. ** remaining within the zone.
  7021. */
  7022. for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
  7023. // if (Map.In_Radar(cell)) {
  7024. if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) {
  7025. bool ok = ttype->Legal_Placement(cell);
  7026. /*
  7027. ** Another (adjacency) check is required for buildings.
  7028. */
  7029. if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) {
  7030. ok = false;
  7031. }
  7032. if (ok) {
  7033. int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell));
  7034. if (bestval == -1 || dist < bestval) {
  7035. bestval = dist;
  7036. bestcell = cell;
  7037. }
  7038. }
  7039. }
  7040. }
  7041. /*
  7042. ** Return the best location to move to.
  7043. */
  7044. return(bestcell);
  7045. }
  7046. /***********************************************************************************************
  7047. * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. *
  7048. * *
  7049. * This routine will pick a random cell within the zone specified. The pick will be *
  7050. * clipped to the map edge when necessary. *
  7051. * *
  7052. * INPUT: zone -- The zone to pick a cell from. *
  7053. * *
  7054. * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the *
  7055. * map, then a cell in the core zone is returned instead. *
  7056. * *
  7057. * WARNINGS: none *
  7058. * *
  7059. * HISTORY: *
  7060. * 11/04/1996 JLB : Created. *
  7061. *=============================================================================================*/
  7062. CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const
  7063. {
  7064. COORDINATE coord = 0;
  7065. int maxdist = 0;
  7066. switch (zone) {
  7067. case ZONE_CORE:
  7068. coord = Coord_Scatter(Center, Random_Pick(0, Radius), true);
  7069. break;
  7070. case ZONE_NORTH:
  7071. maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H);
  7072. if (maxdist < 0) break;
  7073. coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
  7074. break;
  7075. case ZONE_EAST:
  7076. maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W);
  7077. if (maxdist < 0) break;
  7078. coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
  7079. break;
  7080. case ZONE_SOUTH:
  7081. maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H);
  7082. if (maxdist < 0) break;
  7083. coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
  7084. break;
  7085. case ZONE_WEST:
  7086. maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W);
  7087. if (maxdist < 0) break;
  7088. coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
  7089. break;
  7090. }
  7091. /*
  7092. ** Double check that the location is valid and if so, convert it into a cell
  7093. ** number.
  7094. */
  7095. CELL cell;
  7096. if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) {
  7097. if (zone == ZONE_CORE) {
  7098. /*
  7099. ** Finding a cell within the core failed, so just pick the center
  7100. ** cell. This cell is guaranteed to be valid.
  7101. */
  7102. cell = Coord_Cell(Center);
  7103. } else {
  7104. /*
  7105. ** If the edge fails, then try to find a cell within the core.
  7106. */
  7107. cell = Random_Cell_In_Zone(ZONE_CORE);
  7108. }
  7109. } else {
  7110. cell = Coord_Cell(coord);
  7111. }
  7112. /*
  7113. ** If the randomly picked location is not in the legal map area, then clip it to
  7114. ** the legal map area.
  7115. */
  7116. if (!Map.In_Radar(cell)) {
  7117. int x = Cell_X(cell);
  7118. int y = Cell_Y(cell);
  7119. if (x < Map.MapCellX) x = Map.MapCellX;
  7120. if (y < Map.MapCellY) y = Map.MapCellY;
  7121. if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1;
  7122. if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1;
  7123. cell = XY_Cell(x, y);
  7124. }
  7125. return(cell);
  7126. }
  7127. /***********************************************************************************************
  7128. * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. *
  7129. * *
  7130. * INPUT: none *
  7131. * *
  7132. * OUTPUT: Returns the bit field storing which houses this house is allied with. *
  7133. * *
  7134. * WARNINGS: none *
  7135. * *
  7136. * HISTORY: *
  7137. * 09/12/2019 JAS : Created. *
  7138. *=============================================================================================*/
  7139. unsigned HouseClass::Get_Ally_Flags()
  7140. {
  7141. return Allies;
  7142. }
  7143. /***********************************************************************************************
  7144. * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain *
  7145. * *
  7146. * INPUT: none *
  7147. * *
  7148. * OUTPUT: none *
  7149. * *
  7150. * WARNINGS: none *
  7151. * *
  7152. * HISTORY: *
  7153. * 1/31/2020 3:34PM ST : Created. *
  7154. *=============================================================================================*/
  7155. void HouseClass::Check_Pertinent_Structures(void)
  7156. {
  7157. /*
  7158. ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM
  7159. **
  7160. ** Game is over when no pertinent structures remain
  7161. */
  7162. if (!Special.IsEarlyWin) {
  7163. return;
  7164. }
  7165. if (IsToDie || IsToWin || IsToLose) {
  7166. return;
  7167. }
  7168. // MBL 07.15.2020 - Prevention of recent issue with constant "player defeated logic" and message to client spamming
  7169. // Per https://jaas.ea.com/browse/TDRA-7433
  7170. //
  7171. if (IsDefeated) {
  7172. return;
  7173. }
  7174. bool any_good_buildings = false;
  7175. for (int index = 0; index < Buildings.Count(); index++) {
  7176. BuildingClass *b = Buildings.Ptr(index);
  7177. if (b && b->IsActive && b->House == this) {
  7178. if (!b->Class->IsWall && *b != STRUCT_APMINE && *b != STRUCT_AVMINE) {
  7179. if (!Special.ModernBalance || (*b != STRUCT_SHIP_YARD && *b != STRUCT_FAKE_YARD && *b != STRUCT_SUB_PEN && *b != STRUCT_FAKE_PEN)) {
  7180. if (!b->IsInLimbo && b->Strength > 0) {
  7181. any_good_buildings = true;
  7182. break;
  7183. }
  7184. }
  7185. }
  7186. }
  7187. }
  7188. if (!any_good_buildings) {
  7189. for (int index = 0; index < Units.Count(); index++) {
  7190. UnitClass * unit = Units.Ptr(index);
  7191. if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) {
  7192. if (!unit->IsInLimbo && unit->Strength > 0) {
  7193. any_good_buildings = true;
  7194. break;
  7195. }
  7196. }
  7197. }
  7198. }
  7199. if (!any_good_buildings) {
  7200. Flag_To_Die();
  7201. }
  7202. }
  7203. /***********************************************************************************************
  7204. * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house *
  7205. * *
  7206. * INPUT: none *
  7207. * *
  7208. * OUTPUT: none *
  7209. * *
  7210. * WARNINGS: none *
  7211. * *
  7212. * HISTORY: *
  7213. * 4/23/2020 11:06PM ST : Created. *
  7214. *=============================================================================================*/
  7215. void HouseClass::Init_Unit_Trackers(void)
  7216. {
  7217. if (Session.Type == GAME_INTERNET || Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  7218. AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT);
  7219. InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT);
  7220. UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT);
  7221. BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT);
  7222. VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT);
  7223. DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT);
  7224. DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT);
  7225. DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT);
  7226. DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT);
  7227. DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT);
  7228. CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT);
  7229. TotalCrates = new UnitTrackerClass ( CRATE_COUNT );
  7230. } else {
  7231. AircraftTotals = NULL;
  7232. InfantryTotals = NULL;
  7233. UnitTotals = NULL;
  7234. BuildingTotals = NULL;
  7235. VesselTotals = NULL;
  7236. DestroyedAircraft = NULL;
  7237. DestroyedInfantry = NULL;
  7238. DestroyedUnits = NULL;
  7239. DestroyedBuildings = NULL;
  7240. DestroyedVessels = NULL;
  7241. CapturedBuildings = NULL;
  7242. TotalCrates = NULL;
  7243. }
  7244. }
  7245. /***********************************************************************************************
  7246. * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house *
  7247. * *
  7248. * INPUT: none *
  7249. * *
  7250. * OUTPUT: none *
  7251. * *
  7252. * WARNINGS: none *
  7253. * *
  7254. * HISTORY: *
  7255. * 4/23/2020 11:06PM ST : Created. *
  7256. *=============================================================================================*/
  7257. void HouseClass::Free_Unit_Trackers(void)
  7258. {
  7259. if (AircraftTotals) {
  7260. delete AircraftTotals;
  7261. AircraftTotals = NULL;
  7262. }
  7263. if (InfantryTotals) {
  7264. delete InfantryTotals;
  7265. InfantryTotals = NULL;
  7266. }
  7267. if (UnitTotals) {
  7268. delete UnitTotals;
  7269. UnitTotals = NULL;
  7270. }
  7271. if (BuildingTotals) {
  7272. delete BuildingTotals;
  7273. BuildingTotals = NULL;
  7274. }
  7275. if (VesselTotals) {
  7276. delete VesselTotals;
  7277. VesselTotals = NULL;
  7278. }
  7279. if (DestroyedAircraft) {
  7280. delete DestroyedAircraft;
  7281. DestroyedAircraft = NULL;
  7282. }
  7283. if (DestroyedInfantry) {
  7284. delete DestroyedInfantry;
  7285. DestroyedInfantry = NULL;
  7286. }
  7287. if (DestroyedUnits) {
  7288. delete DestroyedUnits;
  7289. DestroyedUnits = NULL;
  7290. }
  7291. if (DestroyedBuildings) {
  7292. delete DestroyedBuildings;
  7293. DestroyedBuildings = NULL;
  7294. }
  7295. if (DestroyedVessels) {
  7296. delete DestroyedVessels;
  7297. DestroyedVessels = NULL;
  7298. }
  7299. if (CapturedBuildings) {
  7300. delete CapturedBuildings;
  7301. CapturedBuildings = NULL;
  7302. }
  7303. if (TotalCrates) {
  7304. delete TotalCrates;
  7305. TotalCrates = NULL;
  7306. }
  7307. }