| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232 |
- //
- // Copyright 2020 Electronic Arts Inc.
- //
- // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
- // software: you can redistribute it and/or modify it under the terms of
- // the GNU General Public License as published by the Free Software Foundation,
- // either version 3 of the License, or (at your option) any later version.
- // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
- // in the hope that it will be useful, but with permitted additional restrictions
- // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
- // distributed with this program. You should have received a copy of the
- // GNU General Public License along with permitted additional restrictions
- // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
- /* $Header: /counterstrike/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : Command & Conquer *
- * *
- * File Name : HOUSE.CPP *
- * *
- * Programmer : Joe L. Bostic *
- * *
- * Start Date : May 21, 1994 *
- * *
- * Last Update : November 4, 1996 [JLB] *
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * HouseClass::AI -- Process house logic. *
- * HouseClass::AI_Aircraft -- Determines what aircraft to build next. *
- * HouseClass::AI_Attack -- Handles offensive attack logic. *
- * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. *
- * HouseClass::AI_Building -- Determines what building to build. *
- * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. *
- * HouseClass::AI_Infantry -- Determines the infantry unit to build. *
- * HouseClass::AI_Money_Check -- Handles money production logic. *
- * HouseClass::AI_Power_Check -- Handle the power situation. *
- * HouseClass::AI_Unit -- Determines what unit to build next. *
- * HouseClass::Abandon_Production -- Abandons production of item type specified. *
- * HouseClass::Active_Add -- Add an object to active duty for this house. *
- * HouseClass::Active_Remove -- Remove this object from active duty for this house. *
- * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. *
- * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. *
- * HouseClass::Adjust_Power -- Adjust the power value of the house. *
- * HouseClass::Adjust_Threat -- Adjust threat for the region specified. *
- * HouseClass::As_Pointer -- Converts a house number into a house object pointer. *
- * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. *
- * HouseClass::Attacked -- Lets player know if base is under attack. *
- * HouseClass::Available_Money -- Fetches the total credit worth of the house. *
- * HouseClass::Begin_Production -- Starts production of the specified object type. *
- * HouseClass::Blowup_All -- blows up everything *
- * HouseClass::Can_Build -- General purpose build legality checker. *
- * HouseClass::Clobber_All -- removes all objects for this house *
- * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. *
- * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. *
- * HouseClass::Detach -- Removes specified object from house tracking systems. *
- * HouseClass::Do_All_To_Hunt -- Send all units to hunt. *
- * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. *
- * HouseClass::Expert_AI -- Handles expert AI processing. *
- * HouseClass::Factory_Count -- Fetches the number of factories for specified type. *
- * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. *
- * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. *
- * HouseClass::Find_Build_Location -- Finds a suitable building location. *
- * HouseClass::Find_Building -- Finds a building of specified type. *
- * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. *
- * HouseClass::Find_Juicy_Target -- Finds a suitable field target. *
- * HouseClass::Fire_Sale -- Cause all buildings to be sold. *
- * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). *
- * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. *
- * HouseClass::Flag_Remove -- Removes the flag from the specified target. *
- * HouseClass::Flag_To_Die -- Flags the house to blow up soon. *
- * HouseClass::Flag_To_Lose -- Flags the house to die soon. *
- * HouseClass::Flag_To_Win -- Flags the house to win soon. *
- * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. *
- * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. *
- * HouseClass::Harvested -- Adds Tiberium to the harvest storage. *
- * HouseClass::HouseClass -- Constructor for a house object. *
- * HouseClass::Init -- init's in preparation for new scenario *
- * HouseClass::Init_Data -- Initializes the multiplayer color data. *
- * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. *
- * HouseClass::Is_Ally -- Checks to see if the object is an ally. *
- * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
- * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? *
- * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. *
- * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated *
- * HouseClass::Make_Ally -- Make the specified house an ally. *
- * HouseClass::Make_Enemy -- Make an enemy of the house specified. *
- * HouseClass::Manual_Place -- Inform display system of building placement mode. *
- * HouseClass::One_Time -- Handles one time initialization of the house array. *
- * HouseClass::Place_Object -- Places the object (building) at location specified. *
- * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. *
- * HouseClass::Power_Fraction -- Fetches the current power output rating. *
- * HouseClass::Production_Begun -- Records that production has begun. *
- * HouseClass::Read_INI -- Reads house specific data from INI. *
- * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. *
- * HouseClass::Recalc_Center -- Recalculates the center point of the base. *
- * HouseClass::Refund_Money -- Refunds money to back to the house. *
- * HouseClass::Remap_Table -- Fetches the remap table for this house object. *
- * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. *
- * HouseClass::Set_Factory -- Assign specified factory to house tracking. *
- * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. *
- * HouseClass::Special_Weapon_AI -- Fires special weapon. *
- * HouseClass::Spend_Money -- Removes money from the house. *
- * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. *
- * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. *
- * HouseClass::Suggested_New_Team -- Determine what team should be created. *
- * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. *
- * HouseClass::Suspend_Production -- Temporarily puts production on hold. *
- * HouseClass::Tally_Score -- Fills in the score system for this round *
- * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. *
- * HouseClass::Tracking_Add -- Informs house of new inventory item. *
- * HouseClass::Tracking_Remove -- Remove object from house tracking system. *
- * HouseClass::Where_To_Go -- Determines where the object should go and wait. *
- * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. *
- * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. *
- * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. *
- * HouseClass::Write_INI -- Writes the house data to the INI database. *
- * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. *
- * HouseClass::delete -- Deallocator function for a house object. *
- * HouseClass::new -- Allocator for a house class. *
- * HouseClass::operator HousesType -- Conversion to HousesType operator. *
- * HouseClass::~HouseClass -- Default destructor for a house object. *
- * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. *
- * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. *
- * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. *
- * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "function.h"
- #include "vortex.h"
- //#include "WolDebug.h"
- /*
- ** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM
- */
- #include "SidebarGlyphx.h"
- TFixedIHeapClass<HouseClass::BuildChoiceClass> HouseClass::BuildChoice;
- int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Save(Pipe &) const
- {
- return(true);
- }
- int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Load(Straw &)
- {
- return(0);
- }
- void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Code_Pointers(void)
- {
- }
- void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Decode_Pointers(void)
- {
- }
- extern bool RedrawOptionsMenu;
- /***********************************************************************************************
- * HouseClass::operator HousesType -- Conversion to HousesType operator. *
- * *
- * This operator will automatically convert from a houses class object into the HousesType *
- * enumerated value. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the object's HousesType value. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/23/1995 JLB : Created. *
- *=============================================================================================*/
- HouseClass::operator HousesType(void) const
- {
- assert(Houses.ID(this) == ID);
- return(Class->House);
- }
- /***********************************************************************************************
- * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. *
- * *
- * This will calculate the current tiberium (gold) load as a ratio of the maximum storage *
- * capacity. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/31/1996 JLB : Created. *
- *=============================================================================================*/
- fixed HouseClass::Tiberium_Fraction(void) const
- {
- if (Tiberium == 0) {
- return(0);
- }
- return(fixed(Tiberium, Capacity));
- }
- /***********************************************************************************************
- * HouseClass::As_Pointer -- Converts a house number into a house object pointer. *
- * *
- * Use this routine to convert a house number into the house pointer that it represents. *
- * A simple index into the Houses template array is not sufficient, since the array order *
- * is arbitrary. An actual scan through the house object is required in order to find the *
- * house object desired. *
- * *
- * INPUT: house -- The house type number to look up. *
- * *
- * OUTPUT: Returns with a pointer to the house object that the house number represents. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/23/1995 JLB : Created. *
- *=============================================================================================*/
- HouseClass * HouseClass::As_Pointer(HousesType house)
- {
- if (house != HOUSE_NONE) {
- for (int index = 0; index < Houses.Count(); index++) {
- if (Houses.Ptr(index)->Class->House == house) {
- return(Houses.Ptr(index));
- }
- }
- }
- return(0);
- }
- /***********************************************************************************************
- * HouseClass::One_Time -- Handles one time initialization of the house array. *
- * *
- * This basically calls the constructor for each of the houses in the game. All other *
- * data specific to the house is initialized when the scenario is loaded. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Only call this ONCE at the beginning of the game. *
- * *
- * HISTORY: *
- * 12/09/1994 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::One_Time(void)
- {
- BuildChoice.Set_Heap(STRUCT_COUNT);
- }
- /***********************************************************************************************
- * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. *
- * *
- * The handicap rating will affect combat, movement, and production for the house. It can *
- * either make it more or less difficult for the house (controlled by the handicap value). *
- * *
- * INPUT: handicap -- The handicap value to assign to this house. The default value for *
- * a house is DIFF_NORMAL. *
- * *
- * OUTPUT: Returns with the old handicap value. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- * 10/22/1996 JLB : Uses act like value for multiplay only. *
- *=============================================================================================*/
- DiffType HouseClass::Assign_Handicap(DiffType handicap)
- {
- DiffType old = Difficulty;
- Difficulty = handicap;
- if (Session.Type != GAME_NORMAL) {
- HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike);
- FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias;
- GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias;
- AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias;
- ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias;
- ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias;
- CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias;
- RepairDelay = Rule.Diff[handicap].RepairDelay;
- BuildDelay = Rule.Diff[handicap].BuildDelay;
- BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias;
- } else {
- FirepowerBias = Rule.Diff[handicap].FirepowerBias;
- GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias;
- AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias;
- ArmorBias = Rule.Diff[handicap].ArmorBias;
- ROFBias = Rule.Diff[handicap].ROFBias;
- CostBias = Rule.Diff[handicap].CostBias;
- RepairDelay = Rule.Diff[handicap].RepairDelay;
- BuildDelay = Rule.Diff[handicap].BuildDelay;
- BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias;
- }
- return(old);
- }
- #ifdef CHEAT_KEYS
- void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const
- {
- mono->Set_Cursor(x, y);
- mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense);
- }
- /***********************************************************************************************
- * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. *
- * *
- * This utility function will output the current status of the house class to the mono *
- * screen. Through this information bugs may be fixed or detected. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/31/1994 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Debug_Dump(MonoClass * mono) const
- {
- mono->Set_Cursor(0, 0);
- mono->Print(Text_String(TXT_DEBUG_HOUSE));
- mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name());
- mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name());
- mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel);
- mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty);
- mono->Set_Cursor(52, 1);mono->Printf("%2d", State);
- mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage);
- mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ);
- mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer);
- mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan);
- mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name());
- mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft);
- mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits);
- mono->Set_Cursor(37, 3);mono->Printf("%5d", Power);
- mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied);
- mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal);
- mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime);
- mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime);
- mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan);
- mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name());
- mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings);
- mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium);
- mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain);
- mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]);
- mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime);
- mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime);
- mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan);
- mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name());
- mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits);
- mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits);
- mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost);
- mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies);
- mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack);
- mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan);
- mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name());
- mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry);
- mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity);
- mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost);
- mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W);
- mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer);
- mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan);
- mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name());
- mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels);
- mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center));
- mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime);
- for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) {
- mono->Set_Cursor(1+index, 15);
- if (Scen.GlobalFlags[index] != 0) {
- mono->Print("1");
- } else {
- mono->Print("0");
- }
- if (index >= 24) break;
- }
- if (Enemy != HOUSE_NONE) {
- char const * name = "";
- name = HouseClass::As_Pointer(Enemy)->Name();
- mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name());
- }
- Print_Zone_Stats(27, 11, ZONE_NORTH, mono);
- Print_Zone_Stats(27, 13, ZONE_CORE, mono);
- Print_Zone_Stats(27, 15, ZONE_SOUTH, mono);
- Print_Zone_Stats(1, 13, ZONE_WEST, mono);
- Print_Zone_Stats(53, 13, ZONE_EAST, mono);
- mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL);
- mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL);
- }
- #endif
- /***********************************************************************************************
- * HouseClass::new -- Allocator for a house class. *
- * *
- * This is the allocator for a house class. Since there can be only *
- * one of each type of house, this is allocator has restricted *
- * functionality. Any attempt to allocate a house structure for a *
- * house that already exists, just returns a pointer to the previously *
- * allocated house. *
- * *
- * INPUT: house -- The house to allocate a class object for. *
- * *
- * OUTPUT: Returns with a pointer to the allocated class object. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/22/1994 JLB : Created. *
- *=============================================================================================*/
- void * HouseClass::operator new(size_t)
- {
- void * ptr = Houses.Allocate();
- if (ptr) {
- ((HouseClass *)ptr)->IsActive = true;
- }
- return(ptr);
- }
- /***********************************************************************************************
- * HouseClass::delete -- Deallocator function for a house object. *
- * *
- * This function marks the house object as "deallocated". Such a *
- * house object is available for reallocation later. *
- * *
- * INPUT: ptr -- Pointer to the house object to deallocate. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/22/1994 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::operator delete(void * ptr)
- {
- if (ptr) {
- ((HouseClass *)ptr)->IsActive = false;
- }
- Houses.Free((HouseClass *)ptr);
- }
- /***********************************************************************************************
- * HouseClass::HouseClass -- Constructor for a house object. *
- * *
- * This function is the constructor and it marks the house object *
- * as being allocated. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/22/1994 JLB : Created. *
- *=============================================================================================*/
- #define VOX_NOT_READY VOX_NONE
- HouseClass::HouseClass(HousesType house) :
- RTTI(RTTI_HOUSE),
- ID(Houses.ID(this)),
- Class(HouseTypes.Ptr(house)),
- Difficulty(Scen.CDifficulty),
- FirepowerBias(1),
- GroundspeedBias(1),
- AirspeedBias(1),
- ArmorBias(1),
- ROFBias(1),
- CostBias(1),
- BuildSpeedBias(1),
- RepairDelay(0),
- BuildDelay(0),
- ActLike(Class->House),
- IsHuman(false),
- WasHuman(false),
- IsPlayerControl(false),
- IsStarted(false),
- IsAlerted(false),
- IsBaseBuilding(false),
- IsDiscovered(false),
- IsMaxedOut(false),
- IsDefeated(false),
- IsToDie(false),
- IsToLose(false),
- IsToWin(false),
- IsCivEvacuated(false),
- IsRecalcNeeded(true),
- IsVisionary(false),
- IsTiberiumShort(false),
- IsSpied(false),
- IsThieved(false),
- IsGPSActive(false),
- IsBuiltSomething(false),
- IsResigner(false),
- IsGiverUpper(false),
- IsParanoid(false),
- IsToLook(true),
- IsQueuedMovementToggle(false),
- DidRepair(false),
- IQ(Control.IQ),
- State(STATE_BUILDUP),
- JustBuiltStructure(STRUCT_NONE),
- JustBuiltInfantry(INFANTRY_NONE),
- JustBuiltUnit(UNIT_NONE),
- JustBuiltAircraft(AIRCRAFT_NONE),
- JustBuiltVessel(VESSEL_NONE),
- Blockage(0),
- RepairTimer(0),
- AlertTime(0),
- BorrowedTime(0),
- BScan(0),
- ActiveBScan(0),
- OldBScan(0),
- UScan(0),
- ActiveUScan(0),
- OldUScan(0),
- IScan(0),
- ActiveIScan(0),
- OldIScan(0),
- AScan(0),
- ActiveAScan(0),
- OldAScan(0),
- VScan(0),
- ActiveVScan(0),
- OldVScan(0),
- CreditsSpent(0),
- HarvestedCredits(0),
- StolenBuildingsCredits(0),
- CurUnits(0),
- CurBuildings(0),
- CurInfantry(0),
- CurVessels(0),
- CurAircraft(0),
- Tiberium(0),
- Credits(0),
- Capacity(0),
- AircraftTotals(NULL),
- InfantryTotals(NULL),
- UnitTotals(NULL),
- BuildingTotals(NULL),
- VesselTotals(NULL),
- DestroyedAircraft(NULL),
- DestroyedInfantry(NULL),
- DestroyedUnits(NULL),
- DestroyedBuildings(NULL),
- DestroyedVessels(NULL),
- CapturedBuildings(NULL),
- TotalCrates(NULL),
- AircraftFactories(0),
- InfantryFactories(0),
- UnitFactories(0),
- BuildingFactories(0),
- VesselFactories(0),
- Power(0),
- Drain(0),
- AircraftFactory(-1),
- InfantryFactory(-1),
- UnitFactory(-1),
- BuildingFactory(-1),
- VesselFactory(-1),
- Radar(RADAR_NONE),
- FlagLocation(TARGET_NONE),
- FlagHome(0),
- UnitsLost(0),
- BuildingsLost(0),
- WhoLastHurtMe(house),
- StartLocationOverride(-1),
- Center(0),
- Radius(0),
- LATime(0),
- LAType(RTTI_NONE),
- LAZone(ZONE_NONE),
- LAEnemy(HOUSE_NONE),
- ToCapture(TARGET_NONE),
- RadarSpied(0),
- PointTotal(0),
- PreferredTarget(QUARRY_ANYTHING),
- ScreenShakeTime(0),
- Attack(0),
- Enemy(HOUSE_NONE),
- AITimer(0),
- UnitToTeleport(0),
- BuildStructure(STRUCT_NONE),
- BuildUnit(UNIT_NONE),
- BuildInfantry(INFANTRY_NONE),
- BuildAircraft(AIRCRAFT_NONE),
- BuildVessel(VESSEL_NONE),
- NukeDest(0),
- Allies(0),
- DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay),
- TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay),
- TriggerTime(0),
- SpeakAttackDelay(1),
- SpeakPowerDelay(1),
- SpeakMoneyDelay(1),
- SpeakMaxedDelay(1),
- RemapColor(Class->RemapColor),
- DebugUnlockBuildables(false)
- {
- /*
- ** Explicit in-place construction of the super weapons is
- ** required here because the default constructor for super
- ** weapons must serve as a no-initialization constructor (save/load reasons).
- */
- new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
- new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY);
- new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
- new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY);
- new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY);
- new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY);
- new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
- new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER);
- memset(UnitsKilled, '\0', sizeof(UnitsKilled));
- memset(BuildingsKilled, '\0', sizeof(BuildingsKilled));
- memset(BQuantity, '\0', sizeof(BQuantity));
- memset(UQuantity, '\0', sizeof(UQuantity));
- memset(IQuantity, '\0', sizeof(IQuantity));
- memset(AQuantity, '\0', sizeof(AQuantity));
- memset(VQuantity, '\0', sizeof(VQuantity));
- strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name.
- HouseTriggers[house].Clear();
- memset((void *)&Regions[0], 0x00, sizeof(Regions));
- Make_Ally(house);
- Assign_Handicap(Scen.CDifficulty);
- /*
- ** Set the time of the first AI attack.
- */
- Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
- Init_Unit_Trackers();
- }
- /***********************************************************************************************
- * HouseClass::~HouseClass -- House class destructor *
- * *
- * *
- * *
- * INPUT: Nothing *
- * *
- * OUTPUT: Nothing *
- * *
- * WARNINGS: None *
- * *
- * HISTORY: *
- * 8/6/96 4:48PM ST : Created *
- *=============================================================================================*/
- HouseClass::~HouseClass (void)
- {
- Class = 0;
- Free_Unit_Trackers();
- }
- /***********************************************************************************************
- * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. *
- * *
- * This is the default constructor that initializes all the values to their default *
- * settings. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/31/1996 JLB : Created. *
- *=============================================================================================*/
- HouseStaticClass::HouseStaticClass(void) :
- IQ(0),
- TechLevel(1),
- Allies(0),
- MaxUnit(Rule.UnitMax/6),
- MaxBuilding(Rule.BuildingMax/6),
- MaxInfantry(Rule.InfantryMax/6),
- MaxVessel(Rule.VesselMax/6),
- MaxAircraft(Rule.UnitMax/6),
- InitialCredits(0),
- Edge(SOURCE_NORTH)
- {
- }
- /***********************************************************************************************
- * HouseClass::Can_Build -- General purpose build legality checker. *
- * *
- * This routine is called when it needs to be determined if the specified object type can *
- * be built by this house. Production and sidebar maintenance use this routine heavily. *
- * *
- * INPUT: type -- Pointer to the type of object that legality is to be checked for. *
- * *
- * house -- This is the house to check for legality against. Note that this might *
- * not be 'this' house since the check could be from a captured factory. *
- * Captured factories build what the original owner of them could build. *
- * *
- * OUTPUT: Can the specified object be built? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/04/1995 JLB : Created. *
- * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. *
- * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. *
- * 11/04/1996 JLB : Computer uses prerequisite record. *
- *=============================================================================================*/
- bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const
- {
- assert(Houses.ID(this) == ID);
- assert(type != NULL);
- /*
- ** An object with a prohibited tech level availability will never be allowed, regardless
- ** of who requests it.
- */
- if (((TechnoTypeClass const *)type)->Level == -1) return(false);
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- /*
- ** If this is a CounterStrike II-only unit, and we're playing a multiplayer
- ** game in 'downshifted' mode against CounterStrike or Red Alert, then
- ** don't allow building this unit.
- */
- if (!NewUnitsEnabled) {
- switch(type->What_Am_I()) {
- case RTTI_INFANTRYTYPE:
- if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT)
- return(false);
- break;
- case RTTI_UNITTYPE:
- if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT)
- return(false);
- break;
- case RTTI_VESSELTYPE:
- if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT)
- return(false);
- break;
- default:
- break;
- }
- }
- #endif
- /*
- ** The computer can always build everything.
- */
- if (!IsHuman && Session.Type == GAME_NORMAL) return(true);
- /*
- ** Special hack to get certain objects to exist for both sides in the game.
- */
- int own = type->Get_Ownable();
- /*
- ** Check to see if this owner can build the object type specified.
- */
- if (((1L << house) & own) == 0) {
- return(false);
- }
- /*
- ** Perform some equivalency fixups for the building existence flags.
- */
- long flags = ActiveBScan;
- /*
- ** The computer records prerequisite buildings because it can't relay on the
- ** sidebar to keep track of this information.
- */
- if (!IsHuman) {
- flags = OldBScan;
- }
- int pre = ((TechnoTypeClass const *)type)->Prerequisite;
- /*
- ** Advanced power also serves as a prerequisite for normal power.
- */
- if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER;
- /*
- ** Either tech center counts as a prerequisite.
- */
- if (Session.Type != GAME_NORMAL) {
- if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH;
- }
- int level = Control.TechLevel;
- #ifdef CHEAT_KEYS
- if (Debug_Cheat) {
- level = 98;
- pre = 0;
- }
- #endif
- // ST - 8/23/2019 4:53PM
- if (DebugUnlockBuildables) {
- level = 98;
- pre = 0;
- }
- /*
- ** See if the prerequisite requirements have been met.
- */
- return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= (unsigned)level);
- }
- /***************************************************************************
- * HouseClass::Init -- init's in preparation for new scenario *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 12/07/1994 BR : Created. *
- * 12/17/1994 JLB : Resets tracker bits. *
- *=========================================================================*/
- void HouseClass::Init(void)
- {
- Houses.Free_All();
- for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
- HouseTriggers[index].Clear();
- }
- }
- // Object selection list is switched with player context for GlyphX. ST - 8/7/2019 10:11AM
- extern void Logic_Switch_Player_Context(HouseClass *house);
- extern bool MPSuperWeaponDisable;
- /***********************************************************************************************
- * HouseClass::AI -- Process house logic. *
- * *
- * This handles the AI for the house object. It should be called once per house per game *
- * tick. It processes all house global tasks such as low power damage accumulation and *
- * house specific trigger events. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 12/27/1994 JLB : Created. *
- * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. *
- *=============================================================================================*/
- extern void Recalculate_Placement_Distances();
- void HouseClass::AI(void)
- {
- assert(Houses.ID(this) == ID);
- // Set PlayerPtr to be this house. ST - 8/7/2019 10:12AM
- Logic_Switch_Player_Context(this);
- /*
- ** If base building has been turned on by a trigger, then force the house to begin
- ** production and team creation as well. This is also true if the IQ is high enough to
- ** being base building.
- */
- if (!IsHuman && (IsBaseBuilding || IQ >= Rule.IQProduction)) {
- IsBaseBuilding = true;
- IsStarted = true;
- IsAlerted = true;
- }
- /*
- ** Check to see if the house wins.
- */
- if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) {
- IsToWin = false;
- if (this == PlayerPtr) {
- PlayerWins = true;
- } else {
- PlayerLoses = true;
- }
- }
- /*
- ** Check to see if the house loses.
- */
- if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) {
- IsToLose = false;
- if (this == PlayerPtr) {
- PlayerLoses = true;
- } else {
- PlayerWins = true;
- }
- }
- /*
- ** Check to see if all objects of this house should be blown up.
- */
- if (IsToDie && BorrowedTime == 0) {
- IsToDie = false;
- Blowup_All();
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- MPlayer_Defeated();
- }
- }
- /*
- ** Double check power values to correct illegal conditions. It is possible to
- ** get a power output of negative (one usually) as a result of damage sustained
- ** and the fixed point fractional math involved with power adjustments. If the
- ** power rating drops below zero, then make it zero.
- */
- Power = max(Power, 0);
- Drain = max(Drain, 0);
- /*
- ** If the base has been alerted to the enemy and should be attacking, then
- ** see if the attack timer has expired. If it has, then create the attack
- ** teams.
- */
- if (IsAlerted && AlertTime == 0) {
- /*
- ** Adjusted to reduce maximum number of teams created.
- */
- int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1));
- for (int index = 0; index < maxteams; index++) {
- TeamTypeClass const * ttype = Suggested_New_Team(true);
- if (ttype != NULL) {
- ScenarioInit++;
- ttype->Create_One_Of();
- ScenarioInit--;
- }
- }
- AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
- // int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2);
- // int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2);
- // AlertTime = Random_Pick(mintime, maxtime);
- }
- /*
- ** If this house's flag waypoint is a valid cell, see if there's
- ** someone sitting on it. If so, make the scatter.
- */
- if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) {
- TechnoClass * techno = Map[FlagHome].Cell_Techno();
- if (techno != NULL) {
- bool moving = false;
- if (techno->Is_Foot()) {
- if (Target_Legal(((FootClass *)techno)->NavCom)) {
- moving = true;
- }
- }
- if (!moving) {
- techno->Scatter(0, true, true);
- }
- }
- }
- /*
- ** Create teams for this house if necessary.
- ** (Use the same timer for some extra capture-the-flag logic.)
- */
- if (!IsAlerted && !TeamTime) {
- TeamTypeClass const * ttype = Suggested_New_Team(false);
- if (ttype) {
- ttype->Create_One_Of();
- }
- TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE;
- }
- /*
- ** If there is insufficient power, then all buildings that are above
- ** half strength take a little bit of damage.
- */
- if (DamageTime == 0) {
- /*
- ** When the power is below required, then the buildings will take damage over
- ** time.
- */
- if (Power_Fraction() < 1) {
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass & b = *Buildings.Ptr(index);
- if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) {
- // BG: Only damage buildings that require power, to keep the
- // land mines from blowing up under low-power conditions
- if (b.Class->Drain) {
- int damage = 1;
- b.Take_Damage(damage, 0, WARHEAD_AP, 0);
- }
- }
- }
- }
- DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay;
- }
- /*
- ** If there are no more buildings to sell, then automatically cancel the
- ** sell mode.
- */
- if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) {
- Map.Sell_Mode_Control(0);
- }
- /*
- ** Various base conditions may be announced to the player. Typically, this would be
- ** low tiberium capacity or low power.
- */
- if (PlayerPtr == this) {
- if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) {
- Speak(VOX_NEED_MO_MONEY);
- Map.Flash_Money();
- SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
- int text_id = TXT_INSUFFICIENT_FUNDS;
- char const * text = Text_String(TXT_INSUFFICIENT_FUNDS);
- if (text != NULL) {
- Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
- }
- }
- if (SpeakMaxedDelay == 0 && IsMaxedOut) {
- IsMaxedOut = false;
- if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
- Speak(VOX_NEED_MO_CAPACITY);
- SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
- }
- }
- if (SpeakPowerDelay == 0 && Power_Fraction() < 1) {
- if (ActiveBScan & STRUCTF_CONST) {
- Speak(VOX_LOW_POWER);
- SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
- Map.Flash_Power();
- int text_id = -1;
- char const * text = NULL;
- if (BQuantity[STRUCT_AAGUN] > 0) {
- text = Text_String(TXT_POWER_AAGUN);
- text_id = TXT_POWER_AAGUN;
- }
- if (BQuantity[STRUCT_TESLA] > 0) {
- text = Text_String(TXT_POWER_TESLA);
- text_id = TXT_POWER_TESLA;
- }
- if (text == NULL) {
- text = Text_String(TXT_LOW_POWER);
- text_id = TXT_LOW_POWER;
- }
- if (text != NULL) {
- Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
- }
- }
- }
- }
- /*
- ** If there is a flag associated with this house, then mark it to be
- ** redrawn.
- */
- if (Target_Legal(FlagLocation)) {
- UnitClass * unit = As_Unit(FlagLocation);
- if (unit) {
- unit->Mark(MARK_CHANGE);
- } else {
- CELL cell = As_Cell(FlagLocation);
- Map[cell].Flag_Update();
- Map[cell].Redraw_Objects();
- }
- }
- bool is_time = false;
- /*
- ** Triggers are only checked every so often. If the trigger timer has expired,
- ** then set the trigger processing flag.
- */
- if (TriggerTime == 0 || IsBuiltSomething) {
- is_time = true;
- TriggerTime = TICKS_PER_MINUTE/10;
- IsBuiltSomething = false;
- }
- /*
- ** Process any super weapon logic required.
- */
- if (Session.Type != GAME_GLYPHX_MULTIPLAYER || !MPSuperWeaponDisable) {
- Super_Weapon_Handler();
- }
- #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse.
- if( (Session.Type != GAME_NORMAL || !IsHuman) && Scen.AutoSonarTimer == 0 )
- {
- // If house has nothing but subs left, do an automatic sonar pulse to reveal them.
- if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw
- {
- int iCount = 0;
- int i;
- for( i = 0; i != STRUCT_COUNT-3; ++i )
- {
- iCount += BQuantity[ i ];
- }
- if( !iCount )
- {
- for( i = 0; i != UNIT_RA_COUNT-3; ++i )
- {
- iCount += UQuantity[ i ];
- }
- if( !iCount )
- {
- // ajw - Found bug - house's civilians are not removed from IQuantity when they die.
- // Workaround...
- for( i = 0; i <= INFANTRY_DOG; ++i )
- {
- iCount += IQuantity[ i ];
- }
- if( !iCount )
- {
- for( i = 0; i != AIRCRAFT_COUNT; ++i )
- {
- iCount += AQuantity[ i ];
- }
- if( !iCount )
- {
- for( i = 0; i != VESSEL_RA_COUNT; ++i )
- {
- if( i != VESSEL_SS )
- iCount += VQuantity[ i ];
- }
- if( !iCount )
- {
- // Do the ping.
- for (int index = 0; index < Vessels.Count(); index++) {
- VesselClass * sub = Vessels.Ptr(index);
- if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) {
- sub->PulseCountDown = 15 * TICKS_PER_SECOND;
- sub->Do_Uncloak();
- }
- }
- bAutoSonarPulse = true;
- }
- }
- }
- }
- }
- }
- }
- #endif
- if (Session.Type != GAME_NORMAL) {
- Check_Pertinent_Structures();
- }
- /*
- ** Special win/lose check for multiplayer games; by-passes the
- ** trigger system. We must wait for non-zero frame, because init
- ** may not properly set IScan etc for each house; you have to go
- ** through each object's AI before it will be properly set.
- */
- if (Session.Type != GAME_NORMAL && !IsDefeated &&
- !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) {
- MPlayer_Defeated();
- }
- /*
- ** Try to spring all events attached to this house. The triggers will check
- ** for themselves if they actually need to be sprung or not.
- */
- for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) {
- if (HouseTriggers[Class->House][index]->Spring() && index > 0) {
- index--;
- continue;
- }
- }
- /*
- ** If a radar facility is not present, but the radar is active, then turn the radar off.
- ** The radar also is turned off when the power gets below 100% capacity.
- */
- if (PlayerPtr == this) {
- bool jammed = true;
- /*
- ** Find if there are any radar facilities, and if they're jammed or not
- */
- if (IsGPSActive) {
- jammed = false;
- } else {
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * building = Buildings.Ptr(index);
- #ifdef FIXIT_RADAR_JAMMED
- if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) {
- #else
- if (building && building->House == PlayerPtr) {
- #endif
- if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) {
- if (!building->IsJammed) {
- jammed = false;
- break;
- }
- }
- }
- }
- }
- Map.Set_Jammed(this, jammed);
- // Need to add in here where we activate it when only GPS is active.
- if (Map.Is_Radar_Active()) {
- if (ActiveBScan & STRUCTF_RADAR) {
- if (Power_Fraction() < 1 && !IsGPSActive) {
- Map.Radar_Activate(0);
- }
- } else {
- if (!IsGPSActive) {
- Map.Radar_Activate(0);
- }
- }
- } else {
- if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) {
- if (Power_Fraction() >= 1 || IsGPSActive) {
- Map.Radar_Activate(1);
- }
- } else {
- if (Map.Is_Radar_Existing()) {
- Map.Radar_Activate(4);
- }
- }
- }
- if (!IsGPSActive && !(ActiveBScan & STRUCTF_RADAR)) {
- Radar = RADAR_NONE;
- } else {
- Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF;
- }
- }
- VisibleCredits.AI(false, this, true);
- /*
- ** Perform any expert system AI processing.
- */
- if (IsBaseBuilding && AITimer == 0) {
- AITimer = Expert_AI();
- }
- if (!IsBaseBuilding && State == STATE_ENDGAME) {
- Fire_Sale();
- Do_All_To_Hunt();
- }
- AI_Building();
- AI_Unit();
- AI_Vessel();
- AI_Infantry();
- AI_Aircraft();
- /*
- ** If the production possibilities need to be recalculated, then do so now. This must
- ** occur after the scan bits have been properly updated.
- */
- if (PlayerPtr == this && IsRecalcNeeded) {
- IsRecalcNeeded = false;
- Map.Recalc();
- /*
- ** This placement might affect any prerequisite requirements for construction
- ** lists. Update the buildable options accordingly.
- */
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * building = Buildings.Ptr(index);
- if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) {
- if (PlayerPtr == building->House) {
- building->Update_Buildables();
- }
- }
- }
- Recalculate_Placement_Distances();
- Check_Pertinent_Structures();
- }
- /*
- ** See if it's time to re-set the can-repair flag
- */
- if (DidRepair && RepairTimer == 0) {
- DidRepair = false;
- }
- if (this == PlayerPtr && IsToLook) {
- IsToLook = false;
- Map.All_To_Look(PlayerPtr);
- }
- }
- /***********************************************************************************************
- * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. *
- * *
- * This handles any super weapons assigned to this house. It also performs any necessary *
- * maintenance that the super weapons require. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/17/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Super_Weapon_Handler(void)
- {
- /*
- ** Perform all super weapon AI processing. This just checks to see if
- ** the graphic needs changing for the special weapon and updates the
- ** sidebar as necessary.
- */
- for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) {
- SuperClass * super = &SuperWeapon[special];
- if (super->Is_Present()) {
- /*
- ** Perform any charge-up logic for the super weapon. If the super
- ** weapon is owned by the player and a graphic change is detected, then
- ** flag the sidebar to be redrawn so the player will see the change.
- */
- if (super->AI(this == PlayerPtr)) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- }
- /*
- ** Repeating super weapons that require power will be suspended if there
- ** is insufficient power available.
- */
- if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) {
- super->Suspend(Power_Fraction() < 1);
- }
- }
- }
- /*
- ** Check to see if they have launched the GPS, but subsequently lost their
- ** tech center. If so, remove the GPS, and shroud the map.
- */
- if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) {
- IsGPSActive = false;
- /*
- ** Updated for client/server multiplayer. ST - 8/12/2019 11:32AM
- */
- if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
- if (IsPlayerControl) {
- Map.Shroud_The_Map(PlayerPtr);
- }
-
- } else {
-
- if (IsHuman) {
- Map.Shroud_The_Map(this);
- }
- }
- }
- /*
- ** Check to see if the GPS Satellite should be removed from the sidebar
- ** because of outside circumstances. The advanced technology facility
- ** being destroyed is a good example of this. Having fired the satellite
- ** is another good example, because it's a one-shot item.
- */
- if (SuperWeapon[SPC_GPS].Is_Present()) {
- if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) {
- /*
- ** Remove the missile capability when there is no advanced tech facility.
- */
- if (SuperWeapon[SPC_GPS].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- IsRecalcNeeded = true;
- }
- } else {
- /*
- ** Auto-fire the GPS satellite if it's charged up.
- */
- if (SuperWeapon[SPC_GPS].Is_Ready()) {
- SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr);
- if (SuperWeapon[SPC_GPS].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- }
- IsRecalcNeeded = true;
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * bldg = Buildings.Ptr(index);
- if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) {
- bldg->HasFired = true;
- bldg->Assign_Mission(MISSION_MISSILE);
- break;
- }
- }
- }
- }
- } else {
- /*
- ** If there is no GPS satellite present, but there is a GPS satellite
- ** facility available, then make the GPS satellite available as well.
- */
- if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 &&
- !IsGPSActive &&
- Control.TechLevel >= Rule.GPSTechLevel &&
- (IsHuman || IQ >= Rule.IQSuperWeapons)) {
- bool canfire = false;
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * bldg = Buildings.Ptr(index);
- if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) {
- if (!bldg->HasFired) {
- canfire = true;
- break;
- }
- }
- }
- if (canfire) {
- SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
- /*
- ** Flag the sidebar to be redrawn if necessary.
- */
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_GPS, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_GPS);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- }
- /*
- ** Check to see if the chronosphere should be removed from the sidebar
- ** because of outside circumstances. The chronosphere facility
- ** being destroyed is a good example of this.
- */
- if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) {
- if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) {
- /*
- ** Remove the chronosphere when there is no chronosphere facility.
- ** Note that this will not remove the one time created chronosphere.
- */
- if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) {
- if (this == PlayerPtr) {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) {
- if (Map.IsTargettingMode == SPC_CHRONO2) {
- TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport);
- if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
- } else {
- Map.IsTargettingMode = SPC_NONE;
- }
- } else {
- Map.IsTargettingMode = SPC_NONE;
- }
- }
- #else
- if (Map.IsTargettingMode == SPC_CHRONOSPHERE ||
- Map.IsTargettingMode == SPC_CHRONO2) {
- Map.IsTargettingMode = SPC_NONE;
- }
- #endif
- Map.Column[1].Flag_To_Redraw();
- }
- IsRecalcNeeded = true;
- }
- }
- } else {
- /*
- ** If there is no chronosphere present, but there is a chronosphere
- ** facility available, then make the chronosphere available as well.
- */
- if ((ActiveBScan & STRUCTF_CHRONOSPHERE) &&
- // (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) &&
- (unsigned)Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level &&
- // Control.TechLevel >= Rule.ChronoTechLevel &&
- (IsHuman || IQ >= Rule.IQSuperWeapons)) {
- SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
- /*
- ** Flag the sidebar to be redrawn if necessary.
- */
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_CHRONOSPHERE, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- /*
- ** Check to see if the iron curtain should be removed from the sidebar
- ** because of outside circumstances. The iron curtain facility
- ** being destroyed is a good example of this.
- */
- if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) {
- if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) {
- /*
- ** Remove the iron curtain when there is no iron curtain facility.
- ** Note that this will not remove the one time created iron curtain.
- */
- if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) {
- if (this == PlayerPtr) {
- if (Map.IsTargettingMode == SPC_IRON_CURTAIN) {
- Map.IsTargettingMode = SPC_NONE;
- }
- Map.Column[1].Flag_To_Redraw();
- }
- IsRecalcNeeded = true;
- }
- }
- } else {
- /*
- ** If there is no iron curtain present, but there is an iron curtain
- ** facility available, then make the iron curtain available as well.
- */
- if ((ActiveBScan & STRUCTF_IRON_CURTAIN) &&
- (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) &&
- (IsHuman || IQ >= Rule.IQSuperWeapons)) {
- SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
- /*
- ** Flag the sidebar to be redrawn if necessary.
- */
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_IRON_CURTAIN, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- /*
- ** Check to see if the sonar pulse should be removed from the sidebar
- ** because of outside circumstances. The spied-upon enemy sub pen
- ** being destroyed is a good example of this.
- */
- if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) {
- int usspy = 1 << (Class->House);
- bool present = false;
- bool powered = false;
- for (int q = 0; q < Buildings.Count() && !powered; q++) {
- BuildingClass * bldg = Buildings.Ptr(q);
- if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->Spied_By() & usspy) ) {
- present = true;
- powered = !(bldg->House->Power_Fraction() < 1);
- }
- }
- if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) {
- /*
- ** Remove the sonar pulse when there is no spied-upon enemy sub pen.
- ** Note that this will not remove the one time created sonar pulse.
- */
- if (SuperWeapon[SPC_SONAR_PULSE].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- IsRecalcNeeded = true;
- }
- }
- }
- /*
- ** Check to see if the nuclear weapon should be removed from the sidebar
- ** because of outside circumstances. The missile silos
- ** being destroyed is a good example of this.
- */
- if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) {
- if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) {
- /*
- ** Remove the nuke when there is no missile silo.
- ** Note that this will not remove the one time created nuke.
- */
- if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) {
- if (this == PlayerPtr) {
- if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) {
- Map.IsTargettingMode = SPC_NONE;
- }
- Map.Column[1].Flag_To_Redraw();
- }
- IsRecalcNeeded = true;
- }
- } else {
- /*
- ** Allow the computer to fire the nuclear weapon when the weapon is
- ** ready and the owner is the computer.
- */
- if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) {
- Special_Weapon_AI(SPC_NUCLEAR_BOMB);
- }
- }
- } else {
- /*
- ** If there is no nuclear missile present, but there is a missile
- ** silo available, then make the missile available as well.
- */
- if ((ActiveBScan & STRUCTF_MSLO) &&
- ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) &&
- (IsHuman || IQ >= Rule.IQSuperWeapons)) {
- SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1);
- /*
- ** Flag the sidebar to be redrawn if necessary.
- */
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
- if (SuperWeapon[SPC_SPY_MISSION].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- IsRecalcNeeded = true;
- }
- } else {
- if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) {
- Map.Column[1].Flag_To_Redraw();
- }
- if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) {
- Special_Weapon_AI(SPC_SPY_MISSION);
- }
- }
- } else {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) {
- SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false);
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SPY_MISSION, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
- if (SuperWeapon[SPC_PARA_BOMB].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- IsRecalcNeeded = true;
- }
- } else {
- if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) {
- Special_Weapon_AI(SPC_PARA_BOMB);
- }
- }
- } else {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) {
- SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false);
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) {
- if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) {
- if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw();
- IsRecalcNeeded = true;
- }
- } else {
- if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) {
- Special_Weapon_AI(SPC_PARA_INFANTRY);
- }
- }
- } else {
- if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) {
- SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false);
- // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_INFANTRY, this);
- }
- } else {
- if (this == PlayerPtr) {
- Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY);
- Map.Column[1].Flag_To_Redraw();
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Attacked -- Lets player know if base is under attack. *
- * *
- * Call this function whenever a building is attacked (with malice). This function will *
- * then announce to the player that his base is under attack. It checks to make sure that *
- * this is referring to the player's house rather than the enemy's. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 12/27/1994 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Attacked(BuildingClass* source)
- {
- assert(Houses.ID(this) == ID);
- #ifdef FIXIT_BASE_ANNOUNCE
- if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) {
- #else
- if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) {
- #endif
- if (Session.Type == GAME_NORMAL) {
- Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
- } else {
- Speak(VOX_BASE_UNDER_ATTACK, this);
- }
- // MBL 06.13.2020 - Timing change from 2 minute cooldown, per https://jaas.ea.com/browse/TDRA-6784
- // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes
- // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE/2); // 30 seconds as requested
- SpeakAttackDelay = Options.Normalize_Delay( (TICKS_PER_MINUTE/2)+(TICKS_PER_SECOND*5) ); // Tweaked for accuracy
- /*
- ** If there is a trigger event associated with being attacked, process it
- ** now.
- */
- for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) {
- HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED);
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Harvested -- Adds Tiberium to the harvest storage. *
- * *
- * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between *
- * all storage capable buildings for the house. Harvested Tiberium adds to the credit *
- * value of the house, but only up to the maximum storage capacity that the house can *
- * currently maintain. *
- * *
- * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/25/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Harvested(unsigned tiberium)
- {
- assert(Houses.ID(this) == ID);
- long oldtib = Tiberium;
- Tiberium += tiberium;
- if (Tiberium > Capacity) {
- Tiberium = Capacity;
- IsMaxedOut = true;
- }
- HarvestedCredits += tiberium;
- Silo_Redraw_Check(oldtib, Capacity);
- }
- /***********************************************************************************************
- * HouseClass::Stole -- Accounts for the value of a captured building. *
- * *
- * Use this routine whenever a building is captured. It keeps track of the cost of the *
- * building for use in the scoring routine, because you get an 'economy' boost for the *
- * value of the stolen building (but you don't get the credit value for it.) *
- * *
- * INPUT: worth -- The worth of the building we captured (stole). *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/05/1996 BWG : Created. *
- *=============================================================================================*/
- void HouseClass::Stole(unsigned worth)
- {
- assert(Houses.ID(this) == ID);
- StolenBuildingsCredits += worth;
- }
- /***********************************************************************************************
- * HouseClass::Available_Money -- Fetches the total credit worth of the house. *
- * *
- * Use this routine to determine the total credit value of the house. This is the sum of *
- * the harvested Tiberium in storage and the initial unspent cash reserves. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the total credit value of the house. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/25/1995 JLB : Created. *
- *=============================================================================================*/
- long HouseClass::Available_Money(void) const
- {
- assert(Houses.ID(this) == ID);
- return(Tiberium + Credits);
- }
- /***********************************************************************************************
- * HouseClass::Spend_Money -- Removes money from the house. *
- * *
- * Use this routine to extract money from the house. Typically, this is a result of *
- * production spending. The money is extracted from available cash reserves first. When *
- * cash reserves are exhausted, then Tiberium is consumed. *
- * *
- * INPUT: money -- The amount of money to spend. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/25/1995 JLB : Created. *
- * 06/20/1995 JLB : Spends Tiberium before spending cash. *
- *=============================================================================================*/
- void HouseClass::Spend_Money(unsigned money)
- {
- assert(Houses.ID(this) == ID);
- long oldtib = Tiberium;
- if (money > (unsigned)Tiberium) {
- money -= (unsigned)Tiberium;
- Tiberium = 0;
- Credits -= money;
- } else {
- Tiberium -= money;
- }
- Silo_Redraw_Check(oldtib, Capacity);
- CreditsSpent += money;
- }
- /***********************************************************************************************
- * HouseClass::Refund_Money -- Refunds money to back to the house. *
- * *
- * Use this routine when money needs to be refunded back to the house. This can occur when *
- * construction is aborted. At this point, the exact breakdown of Tiberium or initial *
- * credits used for the orignal purchase is lost. Presume as much of the money is in the *
- * form of Tiberium as storage capacity will allow. *
- * *
- * INPUT: money -- The number of credits to refund back to the house. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/25/1995 JLB : Created. *
- * 06/01/1995 JLB : Refunded money is never lost *
- *=============================================================================================*/
- void HouseClass::Refund_Money(unsigned money)
- {
- assert(Houses.ID(this) == ID);
- Credits += money;
- }
- /***********************************************************************************************
- * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. *
- * *
- * Use this routine to adjust the maximum storage capacity for the house. This storage *
- * capacity will limit the number of Tiberium credits that can be stored at any one time. *
- * *
- * INPUT: adjust -- The adjustment to the Tiberium storage capacity. *
- * *
- * inanger -- Is this a forced adjustment to capacity due to some hostile event? *
- * *
- * OUTPUT: Returns with the number of Tiberium credits lost. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/25/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::Adjust_Capacity(int adjust, bool inanger)
- {
- assert(Houses.ID(this) == ID);
- long oldcap = Capacity;
- int retval = 0;
- Capacity += adjust;
- Capacity = max(Capacity, 0L);
- if (Tiberium > Capacity) {
- retval = Tiberium - Capacity;
- Tiberium = Capacity;
- if (!inanger) {
- Refund_Money(retval);
- retval = 0;
- } else {
- IsMaxedOut = true;
- }
- }
- Silo_Redraw_Check(Tiberium, oldcap);
- return(retval);
- }
- /***********************************************************************************************
- * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. *
- * *
- * Call this routine when either the capacity or tiberium levels change for a house. This *
- * routine will determine if the aggregate tiberium storage level will result in the *
- * silos changing their imagery. If this is detected, then all the silos for this house *
- * are flagged to be redrawn. *
- * *
- * INPUT: oldtib -- Pre-change tiberium level. *
- * *
- * oldcap -- Pre-change tiberium storage capacity. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 02/02/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap)
- {
- assert(Houses.ID(this) == ID);
- int oldratio = 0;
- if (oldcap) oldratio = (oldtib * 5) / oldcap;
- int newratio = 0;
- if (Capacity) newratio = (Tiberium * 5) / Capacity;
- if (oldratio != newratio) {
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * b = Buildings.Ptr(index);
- if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) {
- b->Mark(MARK_CHANGE);
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
- * *
- * This routine will determine if the house number specified is a ally to this house. *
- * *
- * INPUT: house -- The house number to check to see if it is an ally. *
- * *
- * OUTPUT: Is the house an ally? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_Ally(HousesType house) const
- {
- assert(Houses.ID(this) == ID);
- if (house != HOUSE_NONE) {
- return(((1<<house) & Allies) != 0);
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Is_Ally -- Determines if the specified house is an ally. *
- * *
- * This routine will examine the specified house and determine if it is an ally. *
- * *
- * INPUT: house -- Pointer to the house object to check for ally relationship. *
- * *
- * OUTPUT: Is the specified house an ally? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_Ally(HouseClass const * house) const
- {
- assert(Houses.ID(this) == ID);
- if (house) {
- return(Is_Ally(house->Class->House));
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Is_Ally -- Checks to see if the object is an ally. *
- * *
- * This routine will examine the specified object and return whether it is an ally or not. *
- * *
- * INPUT: object -- The object to examine to see if it is an ally. *
- * *
- * OUTPUT: Is the specified object an ally? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_Ally(ObjectClass const * object) const
- {
- assert(Houses.ID(this) == ID);
- if (object) {
- return(Is_Ally(object->Owner()));
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Make_Ally -- Make the specified house an ally. *
- * *
- * This routine will make the specified house an ally to this house. An allied house will *
- * not be considered a threat or potential target. *
- * *
- * INPUT: house -- The house to make an ally of this house. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- * 08/08/1995 JLB : Breaks off combat when ally commences. *
- * 10/17/1995 JLB : Added reveal base when allied. *
- *=============================================================================================*/
- void HouseClass::Make_Ally(HousesType house)
- {
- assert(Houses.ID(this) == ID);
- if (Is_Allowed_To_Ally(house)) {
- Allies |= (1L << house);
- /*
- ** Don't consider the newfound ally to be an enemy -- of course.
- */
- if (Enemy == house) {
- Enemy = HOUSE_NONE;
- }
- if (ScenarioInit) {
- Control.Allies |= (1L << house);
- }
- if (Session.Type != GAME_NORMAL && !ScenarioInit) {
- HouseClass * hptr = HouseClass::As_Pointer(house);
- /*
- ** An alliance with another human player will cause the computer
- ** players (if present) to become paranoid.
- */
- if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) {
- // if (hptr != NULL && hptr->IsHuman) {
- // if (!hptr->IsHuman) {
- // hptr->Make_Ally(Class->House);
- // }
- Computer_Paranoid();
- }
- char buffer[80];
- /*
- ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure
- ** that fighting will most likely stop when the cease fire begins.
- */
- for (int index = 0; index < Logic.Count(); index++) {
- ObjectClass * object = Logic[index];
- if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) {
- TARGET target = ((TechnoClass *)object)->TarCom;
- if (Target_Legal(target) && As_Techno(target) != NULL) {
- if (Is_Ally(As_Techno(target))) {
- ((TechnoClass *)object)->Assign_Target(TARGET_NONE);
- }
- }
- }
- }
- /*
- ** Cause all structures to be revealed to the house that has been
- ** allied with.
- */
- if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) {
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass const * b = Buildings.Ptr(index);
- if (b && !b->IsInLimbo && (HouseClass *)b->House == this) {
- Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false);
- }
- }
- }
- if (IsHuman) {
- sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName);
- // 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);
- Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay);
- }
- #if(TEN)
- //
- // Notify the TEN server of the new alliance
- //
- if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) {
- Send_TEN_Alliance(hptr->IniName, 1);
- }
- #endif // TEN
- #if(MPATH)
- //
- // Notify the MPATH server of the new alliance
- //
- //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) {
- //Send_MPATH_Alliance(hptr->IniName, 1);
- //}
- #endif // MPATH
- Map.Flag_To_Redraw(false);
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Make_Enemy -- Make an enemy of the house specified. *
- * *
- * This routine will flag the house specified so that it will be an enemy to this house. *
- * Enemy houses are legal targets for attack. *
- * *
- * INPUT: house -- The house to make an enemy of this house. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- * 07/27/1995 JLB : Making war is a bilateral action. *
- *=============================================================================================*/
- void HouseClass::Make_Enemy(HousesType house)
- {
- assert(Houses.ID(this) == ID);
- if (house != HOUSE_NONE && Is_Ally(house)) {
- HouseClass * enemy = HouseClass::As_Pointer(house);
- Allies &= ~(1L << house);
- if (ScenarioInit) {
- Control.Allies &= !(1L << house);
- }
- /*
- ** Breaking an alliance is a bilateral event.
- */
- if (enemy != NULL && enemy->Is_Ally(this)) {
- enemy->Allies &= ~(1L << Class->House);
- if (ScenarioInit) {
- Control.Allies &= ~(1L << Class->House);
- }
- }
- if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) {
- char buffer[80];
- sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName);
- // sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name);
- Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay);
- Map.Flag_To_Redraw(false);
- #if(TEN)
- //
- // Notify the TEN server of the broken alliance
- //
- if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) {
- Send_TEN_Alliance(enemy->IniName, 0);
- }
- #endif // TEN
- #if(MPATH)
- //
- // Notify the MPATH server of the broken alliance
- //
- //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) {
- //Send_MPATH_Alliance(enemy->IniName, 0);
- //}
- #endif // MPATH
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Remap_Table -- Fetches the remap table for this house object. *
- * *
- * This routine will return with the remap table to use when displaying an object owned *
- * by this house. If the object is blushing (flashing), then the lightening remap table is *
- * always used. The "unit" parameter allows proper remap selection for those houses that *
- * have a different remap table for buildings or units. *
- * *
- * INPUT: blushing -- Is the object blushing (flashing)? *
- * *
- * remap -- The remap control value to use. *
- * REMAP_NONE No remap pointer returned at all. *
- * REMAP_NORMAL Return the remap pointer for this house. *
- * REMAP_ALTERNATE (Nod solo play only -- forces red remap). *
- * Multiplay returns same as REMAP_NORMAL *
- * *
- * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- * 10/25/1995 JLB : Uses remap control value. *
- *=============================================================================================*/
- unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const
- {
- assert(Houses.ID(this) == ID);
- if (blushing) return(&Map.FadingLight[0]);
- if (remap == REMAP_NONE) return(0);
- return(ColorRemaps[RemapColor].RemapTable);
- }
- /***********************************************************************************************
- * HouseClass::Suggested_New_Team -- Determine what team should be created. *
- * *
- * This routine examines the house condition and returns with the team that it thinks *
- * should be created. The units that are not currently a member of a team are examined *
- * to determine the team needed. *
- * *
- * INPUT: alertcheck -- Select from the auto-create team list. *
- * *
- * OUTPUT: Returns with a pointer to the team type that should be created. If no team should *
- * be created, then NULL is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck)
- {
- assert(Houses.ID(this) == ID);
- return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck));
- }
- /***********************************************************************************************
- * HouseClass::Adjust_Threat -- Adjust threat for the region specified. *
- * *
- * This routine is called when the threat rating for a region needs to change. The region *
- * and threat adjustment are provided. *
- * *
- * INPUT: region -- The region that adjustment is to occur on. *
- * *
- * threat -- The threat adjustment to perform. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Adjust_Threat(int region, int threat)
- {
- assert(Houses.ID(this) == ID);
- static int _val[] = {
- -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1,
- -1, 0, 1,
- MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1
- };
- static int _thr[] = {
- 2, 1, 2,
- 1, 0, 1,
- 2, 1, 2
- };
- int neg;
- int * val = &_val[0];
- int * thr = &_thr[0];
- if (threat < 0) {
- threat = -threat;
- neg = true;
- } else {
- neg = false;
- }
- for (int lp = 0; lp < 9; lp ++) {
- Regions[region + *val].Adjust_Threat(threat >> *thr, neg);
- val++;
- thr++;
- }
- }
- /***********************************************************************************************
- * HouseClass::Begin_Production -- Starts production of the specified object type. *
- * *
- * This routine is called from the event system. It will start production for the object *
- * type specified. This will be reflected in the sidebar as well as the house factory *
- * tracking variables. *
- * *
- * INPUT: type -- The type of object to begin production on. *
- * *
- * id -- The subtype of object. *
- * *
- * OUTPUT: Returns with the reason why, or why not, production was started. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- * 10/21/1996 JLB : Handles max object case. *
- *=============================================================================================*/
- ProdFailType HouseClass::Begin_Production(RTTIType type, int id)
- {
- assert(Houses.ID(this) == ID);
- int result = true;
- bool initial_start = false;
- FactoryClass * fptr;
- TechnoTypeClass const * tech = Fetch_Techno_Type(type, id);
- fptr = Fetch_Factory(type);
- /*
- ** If the house is already busy producing the requested object, then
- ** return with this failure code, unless we are restarting production.
- */
- if (fptr != NULL) {
- if (fptr->Is_Building()) {
- return(PROD_CANT);
- }
- } else {
- fptr = new FactoryClass();
- if (!fptr) return(PROD_CANT);
- Set_Factory(type, fptr);
- result = fptr->Set(*tech, *this);
- initial_start = true;
- /*
- ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts.
- ** ST - 3/17/2020 2:03PM
- */
- if (!result) {
- Set_Factory(type, NULL);
- delete fptr;
- fptr = NULL;
- }
- }
- if (result) {
- fptr->Start();
- /*
- ** Link this factory to the sidebar so that proper graphic feedback
- ** can take place.
- */
- // Handle Glyphx multiplayer sidebar. ST - 8/14/2019 1:26PM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Factory_Link(fptr->ID, type, id, this);
- }
- } else {
- if (PlayerPtr == this) {
- Map.Factory_Link(fptr->ID, type, id);
- }
- }
- return(PROD_OK);
- }
- delete fptr;
- return(PROD_CANT);
- }
- /***********************************************************************************************
- * HouseClass::Suspend_Production -- Temporarily puts production on hold. *
- * *
- * This routine is called from the event system whenever the production of the specified *
- * type needs to be suspended. The suspended production will be reflected in the sidebar *
- * as well as in the house control structure. *
- * *
- * INPUT: type -- The type of object that production is being suspended for. *
- * *
- * OUTPUT: Returns why, or why not, production was suspended. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- ProdFailType HouseClass::Suspend_Production(RTTIType type)
- {
- assert(Houses.ID(this) == ID);
- FactoryClass * fptr = Fetch_Factory(type);
- /*
- ** If the house is already busy producing the requested object, then
- ** return with this failure code.
- */
- if (fptr == NULL) return(PROD_CANT);
- /*
- ** Actually suspend the production.
- */
- fptr->Suspend();
- /*
- ** Tell the sidebar that it needs to be redrawn because of this.
- */
- if (PlayerPtr == this) {
- Map.SidebarClass::IsToRedraw = true;
- 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
- Map.Flag_To_Redraw(false);
- }
- }
- return(PROD_OK);
- }
- /***********************************************************************************************
- * HouseClass::Abandon_Production -- Abandons production of item type specified. *
- * *
- * This routine is called from the event system whenever production must be abandoned for *
- * the type specified. This will remove the factory and pending object from the sidebar as *
- * well as from the house factory record. *
- * *
- * INPUT: type -- The object type that production is being suspended for. *
- * *
- * OUTPUT: Returns the reason why or why not, production was suspended. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/08/1995 JLB : Created. *
- *=============================================================================================*/
- ProdFailType HouseClass::Abandon_Production(RTTIType type)
- {
- assert(Houses.ID(this) == ID);
- FactoryClass * fptr = Fetch_Factory(type);
- /*
- ** If there is no factory to abandon, then return with a failure code.
- */
- if (fptr == NULL) return(PROD_CANT);
- /*
- ** Tell the sidebar that it needs to be redrawn because of this.
- */
- // Handle Glyphx multiplayer sidebar. ST - 8/7/2019 10:18AM
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- if (IsHuman) {
- Sidebar_Glyphx_Abandon_Production(type, fptr->ID, this);
-
- // Need to clear pending object here if legacy renderer enabled
- if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) {
- Map.PendingObjectPtr = 0;
- Map.PendingObject = 0;
- Map.PendingHouse = HOUSE_NONE;
- Map.Set_Cursor_Shape(0);
- }
- }
- } else {
- if (PlayerPtr == this) {
- Map.Abandon_Production(type, fptr->ID);
- if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) {
- Map.PendingObjectPtr = 0;
- Map.PendingObject = 0;
- Map.PendingHouse = HOUSE_NONE;
- Map.Set_Cursor_Shape(0);
- }
- }
- }
- /*
- ** Abandon production of the object.
- */
- fptr->Abandon();
- Set_Factory(type, NULL);
- delete fptr;
- return(PROD_OK);
- }
- /***********************************************************************************************
- * HouseClass::Special_Weapon_AI -- Fires special weapon. *
- * *
- * This routine will pick a good target to fire the special weapon specified. *
- * *
- * INPUT: id -- The special weapon id to fire. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/24/1995 PWG : Created. *
- *=============================================================================================*/
- void HouseClass::Special_Weapon_AI(SpecialWeaponType id)
- {
- assert(Houses.ID(this) == ID);
- /*
- ** Loop through all of the building objects on the map
- ** and see which ones are available.
- */
- BuildingClass * bestptr = NULL;
- int best = -1;
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * b = Buildings.Ptr(index);
- /*
- ** If the building is valid, not in limbo, not in the process of
- ** being destroyed and not our ally, then we can consider it.
- */
- if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) {
- if (Percent_Chance(90) && (b->Value() > best || best == -1)) {
- best = b->Value();
- bestptr = b;
- }
- }
- }
- if (bestptr) {
- CELL cell = Coord_Cell(bestptr->Center_Coord());
- Place_Special_Blast(id, cell);
- }
- }
- /***********************************************************************************************
- * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. *
- * *
- * This routine will create a blast effect at the cell specified. This is the result of *
- * the special weapons. *
- * *
- * INPUT: id -- The special weapon id number. *
- * *
- * cell -- The location where the special weapon attack is to occur. *
- * *
- * OUTPUT: Was the special weapon successfully fired at the location specified? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/18/1995 JLB : commented. *
- * 07/25/1995 JLB : Added scatter effect for nuclear bomb. *
- * 07/28/1995 JLB : Revamped to use super weapon class controller. *
- *=============================================================================================*/
- extern void Logic_Switch_Player_Context(ObjectClass *object);
- extern void Logic_Switch_Player_Context(HouseClass *object);
- extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
- bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell)
- {
- assert(Houses.ID(this) == ID);
- // Added. ST - 12/2/2019 11:26AM
- bool fired = false;
- const char *what = NULL;
- BuildingClass * launchsite = 0;
- AnimClass * anim = 0;
- switch (id) {
- case SPC_SONAR_PULSE:
- // Automatically discharge the sonar pulse and uncloak all subs.
- if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) {
- SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr);
- if (this == PlayerPtr) {
- Map.Column[1].Flag_To_Redraw();
- Map.Activate_Pulse();
- }
- Sound_Effect(VOC_SONAR);
- IsRecalcNeeded = true;
- fired = true;
- what = "SONAR";
- for (int index = 0; index < Vessels.Count(); index++) {
- VesselClass * sub = Vessels.Ptr(index);
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) {
- #else
- if (*sub == VESSEL_SS) {
- #endif
- sub->PulseCountDown = 15 * TICKS_PER_SECOND;
- sub->Do_Uncloak();
- }
- }
- }
- break;
- case SPC_NUCLEAR_BOMB:
- if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) {
- if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) {
- BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST);
- if (bullet) {
- int celly = Cell_Y(cell);
- celly -= 15;
- if (celly < 1) celly = 1;
- COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly));
- if (!bullet->Unlimbo(start, DIR_S)) {
- delete bullet;
- }
- SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr);
- IsRecalcNeeded = true;
- fired = true;
- what = "NUKE";
- if (this == PlayerPtr) {
- Map.Column[1].Flag_To_Redraw();
- Map.IsTargettingMode = SPC_NONE;
- }
- }
- } else {
- /*
- ** Search for a suitable launch site for this missile.
- */
- launchsite = Find_Building(STRUCT_MSLO);
- /*
- ** If a launch site was found, then proceed with the normal launch
- ** sequence.
- */
- if (launchsite) {
- launchsite->Assign_Mission(MISSION_MISSILE);
- launchsite->Commence();
- NukeDest = cell;
- }
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr);
- IsRecalcNeeded = true;
- fired = true;
- what = "NUKE";
- }
- }
- break;
- case SPC_PARA_INFANTRY:
- if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) {
- TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF");
- if (ttype == NULL) {
- ttype = new TeamTypeClass;
- if (ttype != NULL) {
- strcpy(ttype->IniName, "@PINF");
- ttype->IsTransient = true;
- ttype->IsPrebuilt = false;
- ttype->IsReinforcable = false;
- ttype->Origin = WAYPT_SPECIAL;
- ttype->MissionCount = 1;
- ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT;
- ttype->MissionList[0].Data.Value = WAYPT_SPECIAL;
- ttype->ClassCount = 2;
- ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers();
- ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1);
- ttype->Members[1].Quantity = 1;
- ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER);
- }
- }
- if (ttype != NULL) {
- ttype->House = Class->House;
- Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT);
- Do_Reinforcements(ttype);
- }
- // Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1);
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr);
- IsRecalcNeeded = true;
- fired = true;
- what = "PARA";
- }
- break;
- case SPC_SPY_MISSION:
- if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) {
- Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell));
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr);
- IsRecalcNeeded = true;
- fired = true;
- what = "SPY";
- }
- break;
- case SPC_PARA_BOMB:
- if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) {
- Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE);
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr);
- IsRecalcNeeded = true;
- fired = true;
- what = "PARABOMB";
- }
- break;
- case SPC_IRON_CURTAIN:
- if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) {
- int x = Keyboard->MouseQX - Map.TacPixelX;
- int y = Keyboard->MouseQY - Map.TacPixelY;
- TechnoClass * tech = Map[cell].Cell_Techno(x, y);
- if (tech) {
- switch (tech->What_Am_I()) {
- case RTTI_UNIT:
- case RTTI_BUILDING:
- case RTTI_VESSEL:
- case RTTI_AIRCRAFT:
- tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) {
- tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND;
- }
- #endif
- tech->Mark(MARK_CHANGE);
- Sound_Effect(VOC_IRON1, tech->Center_Coord());
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr);
- break;
- default:
- break;
- }
- }
- IsRecalcNeeded = true;
- fired = true;
- what = "IRON";
- }
- break;
- case SPC_CHRONOSPHERE:
- if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) {
- int x = Keyboard->MouseQX - Map.TacPixelX;
- int y = Keyboard->MouseQY - Map.TacPixelY;
- TechnoClass * tech = Map[cell].Cell_Techno(x, y);
- if (tech && Is_Ally(tech)) {
- if (tech->What_Am_I() == RTTI_UNIT ||
- tech->What_Am_I() == RTTI_INFANTRY ||
- #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
- (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) {
- #else
- (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) {
- #endif
- if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- bool porthim = true;
- if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) {
- porthim = false;
- }
- if (porthim) {
- #endif
- HouseClass* old_player_ptr = PlayerPtr;
- Logic_Switch_Player_Context(this);
- Map.IsTargettingMode = SPC_CHRONO2;
- On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode);
- Logic_Switch_Player_Context(old_player_ptr);
- UnitToTeleport = tech->As_Target();
- fired = true;
- what = "CHRONO";
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- }
- #endif
- }
- }
- }
- }
- break;
- case SPC_CHRONO2:
- {
- TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport);
- CELL oldcell = cell;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) {
- #else
- if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) {
- #endif
- /*
- ** Destroy any infantryman that gets teleported
- */
- if (tech->What_Am_I() == RTTI_INFANTRY) {
- InfantryClass * inf = (InfantryClass *)tech;
- inf->Mark(MARK_UP);
- inf->Coord = Cell_Coord(cell);
- inf->Mark(MARK_DOWN);
- int damage = inf->Strength;
- inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true);
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) {
- tech->Assign_Target(tech->As_Target());
- #endif
- } else {
- /*
- ** Warp the unit to the new location.
- */
- DriveClass * drive = (DriveClass *)tech;
- drive->MoebiusCell = Coord_Cell(drive->Coord);
- oldcell = drive->MoebiusCell;
- drive->Teleport_To(cell);
- drive->IsMoebius = true;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
- drive->IsMoebius = false;
- }
- drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE;
- if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) {
- drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE;
- }
- #else
- drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE;
- #endif
- Scen.Do_BW_Fade();
- Sound_Effect(VOC_CHRONO, drive->Coord);
- /*
- ** Set active animation on Chronospheres.
- */
- for (int index = 0; index < Buildings.Count(); ++index) {
- BuildingClass* building = Buildings.Ptr(index);
- if (building != nullptr && building->IsActive && building->Owner() == Class->House && *building == STRUCT_CHRONOSPHERE) {
- building->Begin_Mode(BSTATE_ACTIVE);
- }
- }
- }
- }
- UnitToTeleport = TARGET_NONE;
- if (this == PlayerPtr) {
- Map.IsTargettingMode = SPC_NONE;
- }
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) {
- #endif
- SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr);
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- }
- #endif
- IsRecalcNeeded = true;
- fired = true;
- what = "CHRONO2";
- /*
- ** Now set a percentage chance that a time quake will occur.
- */
- if (!TimeQuake) {
- TimeQuake = Percent_Chance(Rule.QuakeChance * 100);
- }
- /*
- ** Now set a percentage chance that a chronal vortex will appear. It
- ** might appear where the object teleported to or it might appear
- ** where it teleported from -- random chance.
- */
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- // Don't allow a vortex if the teleportation was due to a chrono tank.
- if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK))
- #endif
- if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) {
- int x = Random_Pick(0, Map.MapCellWidth-1);
- int y = Random_Pick(0, Map.MapCellHeight-1);
- ChronalVortex.Appear(Cell_Coord(XY_Cell(Map.MapCellX + x, Map.MapCellY + y)));
- // if (Percent_Chance(50)) {
- // ChronalVortex.Appear(Cell_Coord(oldcell));
- // } else {
- // ChronalVortex.Appear(Cell_Coord(cell));
- // }
- }
- break;
- }
- }
- /*
- ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM
- */
- if (IsHuman && fired && what) {
- On_Achievement_Event(this, "SUPERWEAPON_FIRED", what);
- }
- return(true);
- }
- /***********************************************************************************************
- * HouseClass::Place_Object -- Places the object (building) at location specified. *
- * *
- * This routine is called when a building has been produced and now must be placed on *
- * the map. When the player clicks on the map, this routine is ultimately called when the *
- * event passes through the event queue system. *
- * *
- * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. *
- * *
- * *
- * cell -- The location to place the object on the map. *
- * *
- * OUTPUT: Was the placement successful? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/18/1995 JLB : Created. *
- *=============================================================================================*/
- extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord);
- bool HouseClass::Place_Object(RTTIType type, CELL cell)
- {
- assert(Houses.ID(this) == ID);
- TechnoClass * tech = 0;
- FactoryClass * factory = Fetch_Factory(type);
- /*
- ** Only if there is a factory active for this type, can it be "placed".
- ** In the case of a missing factory, then this request is completely bogus --
- ** ignore it. This might occur if, between two events to exit the same
- ** object, the mouse was clicked on the sidebar to start building again.
- ** The second placement event should NOT try to place the object that is
- ** just starting construction.
- */
- if (factory && factory->Has_Completed()) {
- tech = factory->Get_Object();
- if (cell == -1) {
- TechnoClass * pending = factory->Get_Object();
- if (pending != NULL) {
- #ifdef FIXIT_HELI_LANDING
- /*
- ** Try to find a place for the object to appear from. For helicopters, it has the
- ** option of finding a nearby helipad if no helipads are free.
- */
- TechnoClass * builder = pending->Who_Can_Build_Me(false, false);
- if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) {
- builder = pending->Who_Can_Build_Me(true, false);
- }
- #else
- bool intheory = false;
- if (pending->What_Am_I() == RTTI_AIRCRAFT) {
- /*
- ** BG hack - helicopters don't need a specific building to
- ** emerge from, in fact, they'll land next to a building if
- ** need be.
- */
- if( !((AircraftClass *)pending)->Class->IsFixedWing) {
- intheory = true;
- }
- }
- TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false);
- #endif
- TechnoTypeClass const *object_type = pending->Techno_Type_Class();
- if (builder != NULL && builder->Exit_Object(pending)) {
- /*
- ** Since the object has left the factory under its own power, delete
- ** the production manager tied to this slot in the sidebar. Its job
- ** has been completed.
- */
- factory->Set_Is_Blocked(false);
- factory->Completed();
- Abandon_Production(type);
- /*
- ** Could be tied to an achievement. ST - 11/11/2019 11:56AM
- */
- if (IsHuman) {
- if (object_type) {
- On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName);
- }
- if (pending->IsActive) {
- On_Ping(this, pending->Center_Coord());
- }
- }
- switch (pending->What_Am_I()) {
- case RTTI_UNIT:
- JustBuiltUnit = ((UnitClass*)pending)->Class->Type;
- IsBuiltSomething = true;
- break;
- case RTTI_VESSEL:
- JustBuiltVessel = ((VesselClass*)pending)->Class->Type;
- IsBuiltSomething = true;
- break;
- case RTTI_INFANTRY:
- JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type;
- IsBuiltSomething = true;
- break;
- case RTTI_BUILDING:
- JustBuiltStructure = ((BuildingClass*)pending)->Class->Type;
- IsBuiltSomething = true;
- break;
- case RTTI_AIRCRAFT:
- JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type;
- IsBuiltSomething = true;
- break;
- }
- } else {
- /*
- ** The object could not leave under it's own power. Just wait
- ** until the player tries to place the object again.
- */
-
- /*
- ** Flag that it's blocked so we can re-try the exit later.
- ** This would have been a bad idea under the old peer-peer code since it would have pumped events into
- ** the queue too often. ST - 2/25/2020 11:56AM
- */
- factory->Set_Is_Blocked(true);
- return(false);
- }
- }
- } else {
- if (tech) {
- TechnoClass * builder = tech->Who_Can_Build_Me(false, false);
- if (builder) {
- builder->Transmit_Message(RADIO_HELLO, tech);
- if (tech->Unlimbo(Cell_Coord(cell))) {
- factory->Completed();
- Abandon_Production(type);
- if (PlayerPtr == this) {
- Sound_Effect(VOC_PLACE_BUILDING_DOWN);
- Map.Set_Cursor_Shape(0);
- Map.PendingObjectPtr = 0;
- Map.PendingObject = 0;
- Map.PendingHouse = HOUSE_NONE;
- }
- return(true);
- } else {
- if (this == PlayerPtr) {
- Speak(VOX_DEPLOY);
- }
- }
- builder->Transmit_Message(RADIO_OVER_OUT);
- }
- return(false);
- } else {
- // Play a bad sound here?
- return(false);
- }
- }
- }
- return(true);
- }
- /***********************************************************************************************
- * HouseClass::Manual_Place -- Inform display system of building placement mode. *
- * *
- * This routine will inform the display system that building placement mode has begun. *
- * The cursor will be created that matches the layout of the building shape. *
- * *
- * INPUT: builder -- The factory that is building this object. *
- * *
- * object -- The building that is going to be placed down on the map. *
- * *
- * OUTPUT: Was the building placement mode successfully initiated? *
- * *
- * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games *
- * is affected. *
- * *
- * HISTORY: *
- * 05/04/1995 JLB : Created. *
- * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. *
- *=============================================================================================*/
- bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object)
- {
- assert(Houses.ID(this) == ID);
- if (this == PlayerPtr && !Map.PendingObject && builder && object) {
- /*
- ** Ensures that object selection doesn't remain when
- ** building placement takes place.
- */
- Unselect_All();
- Map.Repair_Mode_Control(0);
- Map.Sell_Mode_Control(0);
- Map.PendingObject = object->Class;
- Map.PendingObjectPtr = object;
- Map.PendingHouse = Class->House;
- Map.Set_Cursor_Shape(object->Occupy_List(true));
- Map.Set_Cursor_Pos(Coord_Cell(builder->Coord));
- builder->Mark(MARK_CHANGE);
- return(true);
- }
- return(false);
- }
- /***************************************************************************
- * HouseClass::Clobber_All -- removes all objects for this house *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * This routine removes the house itself, so the multiplayer code *
- * must not rely on there being "empty" houses lying around. *
- * *
- * HISTORY: *
- * 05/16/1995 BRR : Created. *
- * 06/09/1995 JLB : Handles aircraft. *
- *=========================================================================*/
- void HouseClass::Clobber_All(void)
- {
- assert(Houses.ID(this) == ID);
- int i;
- for (i = 0; i < ::Aircraft.Count(); i++) {
- if (::Aircraft.Ptr(i)->House == this) {
- delete ::Aircraft.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < ::Units.Count(); i++) {
- if (::Units.Ptr(i)->House == this) {
- delete ::Units.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < ::Vessels.Count(); i++) {
- if (::Vessels.Ptr(i)->House == this) {
- delete ::Vessels.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < Infantry.Count(); i++) {
- if (Infantry.Ptr(i)->House == this) {
- delete Infantry.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < Buildings.Count(); i++) {
- if (Buildings.Ptr(i)->House == this) {
- delete Buildings.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < TeamTypes.Count(); i++) {
- if (TeamTypes.Ptr(i)->House == Class->House) {
- delete TeamTypes.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < Triggers.Count(); i++) {
- if (Triggers.Ptr(i)->Class->House == Class->House) {
- delete Triggers.Ptr(i);
- i--;
- }
- }
- for (i = 0; i < TriggerTypes.Count(); i++) {
- if (TriggerTypes.Ptr(i)->House == Class->House) {
- delete TriggerTypes.Ptr(i);
- i--;
- }
- }
- delete this;
- }
- /***********************************************************************************************
- * HouseClass::Detach -- Removes specified object from house tracking systems. *
- * *
- * This routine is called when an object is to be removed from the game system. If the *
- * specified object is part of the house tracking system, then it will be removed. *
- * *
- * INPUT: target -- The target value of the object that is to be removed from the game. *
- * *
- * all -- Is the target going away for good as opposed to just cloaking/hiding? *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/18/1995 JLB : commented *
- *=============================================================================================*/
- void HouseClass::Detach(TARGET target, bool )
- {
- assert(Houses.ID(this) == ID);
- if (ToCapture == target) {
- ToCapture = TARGET_NONE;
- }
- if (Is_Target_Trigger(target)) {
- HouseTriggers[ID].Delete(As_Trigger(target));
- }
- }
- /***********************************************************************************************
- * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. *
- * *
- * This routine will examine the enemy houses and if there is a building owned by one *
- * of those house, true will be returned. *
- * *
- * INPUT: btype -- The building type to check for. *
- * *
- * OUTPUT: Does a building of the specified type exist for one of the enemy houses? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/23/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const
- {
- assert(Houses.ID(this) == ID);
- int bflag = 1L << btype;
- for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
- HouseClass * house = HouseClass::As_Pointer(index);
- if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) {
- return(true);
- }
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. *
- * *
- * This routine will examine the house status and return with a techno type pointer to *
- * the object type that it thinks should be created. The type is restricted to match the *
- * type specified. Typical use of this routine is by computer controlled factories. *
- * *
- * INPUT: objecttype -- The type of object to restrict the scan for. *
- * *
- * kennel -- Is this from a kennel? There are special hacks to ensure that only *
- * dogs can be produced from a kennel. *
- * *
- * OUTPUT: Returns with a pointer to a techno type for the object type that should be *
- * created. If no object should be created, then NULL is returned. *
- * *
- * WARNINGS: This is a time consuming routine. Only call when necessary. *
- * *
- * HISTORY: *
- * 05/23/1995 JLB : Created. *
- *=============================================================================================*/
- TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const
- {
- assert(Houses.ID(this) == ID);
- TechnoTypeClass const * techno = NULL;
- switch (objecttype) {
- case RTTI_AIRCRAFT:
- case RTTI_AIRCRAFTTYPE:
- if (BuildAircraft != AIRCRAFT_NONE) {
- return(&AircraftTypeClass::As_Reference(BuildAircraft));
- }
- return(NULL);
- case RTTI_VESSEL:
- case RTTI_VESSELTYPE:
- if (BuildVessel != VESSEL_NONE) {
- return(&VesselTypeClass::As_Reference(BuildVessel));
- }
- return(NULL);
- /*
- ** Unit construction is based on the rule that up to twice the number required
- ** to fill all teams will be created.
- */
- case RTTI_UNIT:
- case RTTI_UNITTYPE:
- if (BuildUnit != UNIT_NONE) {
- return(&UnitTypeClass::As_Reference(BuildUnit));
- }
- return(NULL);
- /*
- ** Infantry construction is based on the rule that up to twice the number required
- ** to fill all teams will be created.
- */
- case RTTI_INFANTRY:
- case RTTI_INFANTRYTYPE:
- if (BuildInfantry != INFANTRY_NONE) {
- if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL);
- if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL);
- return(&InfantryTypeClass::As_Reference(BuildInfantry));
- }
- return(NULL);
- /*
- ** Building construction is based upon the preconstruction list.
- */
- case RTTI_BUILDING:
- case RTTI_BUILDINGTYPE:
- if (BuildStructure != STRUCT_NONE) {
- return(&BuildingTypeClass::As_Reference(BuildStructure));
- }
- return(NULL);
- }
- return(techno);
- }
- /***********************************************************************************************
- * HouseClass::Flag_Remove -- Removes the flag from the specified target. *
- * *
- * This routine will remove the flag attached to the specified target object or cell. *
- * Call this routine before placing the object down. This is called inherently by the *
- * the Flag_Attach() functions. *
- * *
- * INPUT: target -- The target that the flag was attached to but will be removed from. *
- * *
- * set_home -- if true, clears the flag's waypoint designation *
- * *
- * OUTPUT: Was the flag successfully removed from the specified target? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/23/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Flag_Remove(TARGET target, bool set_home)
- {
- assert(Houses.ID(this) == ID);
- bool rc = false;
- if (Target_Legal(target)) {
- /*
- ** Remove the flag from a unit
- */
- UnitClass * object = As_Unit(target);
- if (object) {
- rc = object->Flag_Remove();
- if (rc && FlagLocation == target) {
- FlagLocation = TARGET_NONE;
- }
- } else {
- /*
- ** Remove the flag from a cell
- */
- CELL cell = As_Cell(target);
- if (Map.In_Radar(cell)) {
- rc = Map[cell].Flag_Remove();
- if (rc && FlagLocation == target) {
- FlagLocation = TARGET_NONE;
- }
- }
- }
- /*
- ** Handle the flag home cell:
- ** If 'set_home' is set, clear the home value & the cell's overlay
- */
- if (set_home) {
- if (FlagHome != 0) {
- Map[FlagHome].Overlay = OVERLAY_NONE;
- Map.Flag_Cell(FlagHome);
- FlagHome = 0;
- }
- }
- }
- return(rc);
- }
- /***********************************************************************************************
- * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). *
- * *
- * This routine will attach the house flag to the location specified. If the location *
- * cannot contain the flag, then a suitable nearby location will be selected. *
- * *
- * INPUT: cell -- The desired cell location to place the flag. *
- * *
- * set_home -- if true, resets the flag's waypoint designation *
- * *
- * OUTPUT: Was the flag successfully placed? *
- * *
- * WARNINGS: The cell picked for the flag might very likely not be the cell requested. *
- * Check the FlagLocation value to determine the final cell resting spot. *
- * *
- * HISTORY: *
- * 05/23/1995 JLB : Created. *
- * 10/08/1996 JLB : Uses map nearby cell scanning handler. *
- *=============================================================================================*/
- bool HouseClass::Flag_Attach(CELL cell, bool set_home)
- {
- assert(Houses.ID(this) == ID);
- bool rc;
- bool clockwise;
- /*
- ** Randomly decide if we're going to search cells clockwise or counter-
- ** clockwise
- */
- clockwise = Percent_Chance(50);
- /*
- ** Only continue if this cell is a legal placement cell.
- */
- if (Map.In_Radar(cell)) {
- /*
- ** If the flag already exists, then it must be removed from the object
- ** it is attached to.
- */
- Flag_Remove(FlagLocation, set_home);
- /*
- ** Attach the flag to the cell specified. If it can't be placed, then pick
- ** a nearby cell where it can be placed.
- */
- CELL newcell = cell;
- rc = Map[newcell].Flag_Place(Class->House);
- if (!rc) {
- newcell = Map.Nearby_Location(cell, SPEED_TRACK, -1, MZONE_NORMAL, true);
- if (newcell != 0) {
- rc = Map[newcell].Flag_Place(Class->House);
- }
- #ifdef OBSOLETE
- /*
- ** Loop for increasing distance from the desired cell.
- ** For each distance, randomly pick a starting direction. Between
- ** this and the clockwise/counterclockwise random value, the flag
- ** should appear to be placed fairly randomly.
- */
- for (int dist = 1; dist < 32; dist++) {
- FacingType fcounter;
- FacingType rot;
- /*
- ** Clockwise search.
- */
- if (clockwise) {
- rot = Random_Pick(FACING_N, FACING_NW);
- for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) {
- newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256));
- if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) {
- dist = 32;
- rc = true;
- break;
- }
- rot++;
- if (rot > FACING_NW) rot = FACING_N;
- }
- } else {
- /*
- ** Counter-clockwise search
- */
- rot = Random_Pick(FACING_N, FACING_NW);
- for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) {
- newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256));
- if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) {
- dist = 32;
- rc = true;
- break;
- }
- rot--;
- if (rot < FACING_N)
- rot = FACING_NW;
- }
- }
- }
- #endif
- }
- /*
- ** If we've found a spot for the flag, place the flag at the new cell.
- ** if 'set_home' is set, OR this house has no current flag home cell,
- ** mark that cell as this house's flag home cell. Otherwise fall back
- ** on returning the flag to its home.
- */
- if (rc) {
- FlagLocation = As_Target(newcell);
- if (set_home || FlagHome == 0) {
- Map[newcell].Overlay = OVERLAY_FLAG_SPOT;
- Map[newcell].OverlayData = 0;
- Map[newcell].Recalc_Attributes();
- FlagHome = newcell;
- }
- }
- else if (FlagHome != 0) {
- rc = Map[FlagHome].Flag_Place(Class->House);
- }
- return(rc);
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. *
- * *
- * This routine will attach the house flag to the specified unit. This routine is called *
- * when a unit drives over a cell containing a flag. *
- * *
- * INPUT: object -- Pointer to the object that the house flag is to be attached to. *
- * *
- * set_home -- if true, clears the flag's waypoint designation *
- * *
- * OUTPUT: Was the flag attached successfully? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/23/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Flag_Attach(UnitClass * object, bool set_home)
- {
- assert(Houses.ID(this) == ID);
- if (object && !object->IsInLimbo) {
- Flag_Remove(FlagLocation, set_home);
- /*
- ** Attach the flag to the object.
- */
- object->Flag_Attach(Class->House);
- FlagLocation = object->As_Target();
- return(true);
- }
- return(false);
- }
- extern void On_Defeated_Message(const char* message, float timeout_seconds);
- /***************************************************************************
- * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 05/25/1995 BRR : Created. *
- *=========================================================================*/
- void HouseClass::MPlayer_Defeated(void)
- {
- assert(Houses.ID(this) == ID);
- char txt[80];
- int i,j;
- unsigned char id;
- HouseClass * hptr;
- HouseClass * hptr2;
- int num_alive;
- int num_humans;
- int all_allies;
- /*
- ** Set the defeat flag for this house
- */
- IsDefeated = true;
- /*
- ** If this is a computer controlled house, then all computer controlled
- ** houses become paranoid.
- */
- if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) {
- Computer_Paranoid();
- }
- /*
- ** Remove this house's flag & flag home cell
- */
- if (Special.IsCaptureTheFlag) {
- if (FlagLocation) {
- Flag_Remove(FlagLocation, true);
- } else {
- if (FlagHome != 0) {
- Flag_Remove(FlagHome, true);
- }
- }
- }
- /*
- ** Remove any one-time superweapons the player might have.
- */
- for (i = SPC_FIRST; i < SPC_COUNT; i++) {
- SuperWeapon[i].Remove(true);
- }
- /*
- ** If this is me:
- ** - Set MPlayerObiWan, so I can only send messages to all players, and
- ** not just one (so I can't be obnoxiously omnipotent)
- ** - Reveal the map
- ** - Add my defeat message
- */
- if (PlayerPtr == this) {
- Session.ObiWan = 1;
- HidPage.Clear();
- Map.Flag_To_Redraw(true);
- /*
- ** Pop up a message showing that I was defeated
- */
- sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName);
- if (Session.Type == GAME_NORMAL) {
- Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx,
- TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
- }
- Map.Flag_To_Redraw(false);
- if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- int timeout = Rule.MessageDelay * TICKS_PER_MINUTE;
- On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE);
- Sound_Effect(VOC_INCOMING_MESSAGE);
- }
- } else {
- /*
- ** If it wasn't me, find out who was defeated
- */
- if (IsHuman) {
- sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName);
- //Session.Messages.Add_Message(NULL, 0, txt, RemapColor,
- // TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
- Map.Flag_To_Redraw(false);
- RedrawOptionsMenu = true;
- int timeout = Rule.MessageDelay * TICKS_PER_MINUTE;
- On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE);
- Sound_Effect(VOC_INCOMING_MESSAGE);
- }
- }
- /*
- ** Find out how many players are left alive.
- */
- num_alive = 0;
- num_humans = 0;
- for (i = 0; i < Session.MaxPlayers; i++) {
- hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i));
- if (hptr && !hptr->IsDefeated) {
- if (hptr->IsHuman) {
- num_humans++;
- }
- num_alive++;
- }
- }
- /*
- ** If all the houses left alive are allied with each other, then in reality
- ** there's only one player left:
- */
- all_allies = 1;
- for (i = 0; i < Session.MaxPlayers; i++) {
- /*
- ** Get a pointer to this house
- */
- hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i));
- if (!hptr || hptr->IsDefeated)
- continue;
- /*
- ** Loop through all houses; if there's one left alive that this house
- ** isn't allied with, then all_allies will be false
- */
- for (j = 0; j < Session.MaxPlayers; j++) {
- hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j));
- if (!hptr2) {
- continue;
- }
- if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) {
- all_allies = 0;
- break;
- }
- }
- if (!all_allies) {
- break;
- }
- }
- /*
- ** If all houses left are allies, set 'num_alive' to 1; game over.
- */
- if (all_allies) {
- num_alive = 1;
- }
- /*
- ** If there's only one human player left or no humans left, the game is over:
- ** - Determine whether this player wins or loses, based on the state of the
- ** player's IsDefeated flag
- ** - Find all players' indices in the Session.Score array
- ** - Tally up scores for this game
- */
- if (num_alive == 1 || num_humans == 0) {
- if (PlayerPtr->IsDefeated) {
- PlayerLoses = true;
- } else {
- PlayerWins = true;
- }
- /*
- ** Add up the scores
- */
- Tally_Score();
- /*
- ** Destroy all the IPX connections, since we have to go through the rest
- ** of the Main_Loop() before we detect that the game is over, and we'll
- ** end up waiting for frame sync packets from the other machines.
- */
- if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) {
- i = 0;
- while (Ipx.Num_Connections() && (i++ < 1000) ) {
- id = Ipx.Connection_ID(0);
- Ipx.Delete_Connection(id);
- }
- Session.NumPlayers = 0;
- }
- #if(TEN)
- //
- // Tell the TEN server who won
- //
- if (Session.Type == GAME_TEN) {
- Send_TEN_Win_Packet();
- }
- #endif // TEN
- #if(MPATH)
- //
- // Tell the MPATH server who won
- //
- if (Session.Type == GAME_MPATH) {
- FILE *fp;
- fp = fopen("winner.txt","wt");
- if (fp) {
- for (i = 0; i < Session.Players.Count(); i++) {
- hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
- if (!hptr->IsDefeated) {
- fprintf(fp,"%s\n",hptr->IniName);
- }
- }
- fclose(fp);
- }
- }
- #endif // MPATH
- }
- }
- /***************************************************************************
- * HouseClass::Tally_Score -- Fills in the score system for this round *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/29/1995 BRR : Created. *
- *=========================================================================*/
- void HouseClass::Tally_Score(void)
- {
- HousesType house;
- HousesType house2;
- HouseClass * hptr;
- int score_index;
- int i,j,k;
- int max_index;
- int max_count;
- int count;
- /*
- ** Loop through all houses, tallying up each player's score
- */
- for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
- hptr = HouseClass::As_Pointer(house);
- /*
- ** Skip this house if it's not human.
- */
- if (!hptr || !hptr->IsHuman) {
- continue;
- }
- /*
- ** Now find out where this player is in the score array
- */
- score_index = -1;
- for (i = 0; i < Session.NumScores; i++) {
- if (!stricmp(hptr->IniName, Session.Score[i].Name)) {
- score_index = i;
- break;
- }
- }
- /*
- ** If the index is still -1, the name wasn't found; add a new entry.
- */
- if (score_index == -1) {
- /*
- ** Just add this player to the end of the array, if there's room
- */
- if (Session.NumScores < MAX_MULTI_NAMES) {
- score_index = Session.NumScores;
- Session.NumScores++;
- }
- /*
- ** If there's not room, we have to remove somebody.
- ** For each player in the scores array, count the # of '-1' entries
- ** from this game backwards; the one with the most is the one that
- ** hasn't played the longest; replace him with this new guy.
- */
- else {
- max_index = 0;
- max_count = 0;
- for (j = 0; j < Session.NumScores; j++) {
- count = 0;
- for (k = Session.NumScores - 1; k >= 0; k--) {
- if (Session.Score[j].Kills[k]==-1) {
- count++;
- }
- else {
- break;
- }
- }
- if (count > max_count) {
- max_count = count;
- max_index = j;
- }
- }
- score_index = max_index;
- }
- /*
- ** Initialize this new score entry
- */
- Session.Score[score_index].Wins = 0;
- strcpy (Session.Score[score_index].Name, hptr->IniName);
- for (j = 0; j < MAX_MULTI_GAMES; j++)
- Session.Score[score_index].Kills[j] = -1;
- }
- /*
- ** Init this player's Kills to 0 (-1 means he didn't play this round;
- ** 0 means he played but got no kills).
- */
- Session.Score[score_index].Kills[Session.CurGame] = 0;
- /*
- ** Init this player's color to his last-used color index
- */
- Session.Score[score_index].Color = hptr->RemapColor;
- /*
- ** If this house was undefeated, it must have been the winner.
- ** (If no human houses are undefeated, the computer won.)
- */
- if (!hptr->IsDefeated) {
- Session.Score[score_index].Wins++;
- Session.Winner = score_index;
- }
- /*
- ** Tally up all kills for this player
- */
- for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) {
- Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2];
- Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2];
- }
- }
- }
- /***************************************************************************
- * HouseClass::Blowup_All -- blows up everything *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 05/16/1995 BRR : Created. *
- * 06/09/1995 JLB : Handles aircraft. *
- * 05/07/1996 JLB : Handles ships. *
- *=========================================================================*/
- void HouseClass::Blowup_All(void)
- {
- assert(Houses.ID(this) == ID);
- int i;
- int damage;
- UnitClass * uptr;
- InfantryClass * iptr;
- BuildingClass * bptr;
- int count;
- WarheadType warhead;
- /*
- ** Find everything owned by this house & blast it with a huge amount of damage
- ** at zero range. Do units before infantry, so the units' drivers are killed
- ** too. Using Explosion_Damage is like dropping a big bomb right on the
- ** object; it will also damage anything around it.
- */
- for (i = 0; i < ::Units.Count(); i++) {
- if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) {
- uptr = ::Units.Ptr(i);
- /*
- ** Some units can't be killed with one shot, so keep damaging them until
- ** they're gone. The unit will destroy itself, and put an infantry in
- ** its place. When the unit destroys itself, decrement 'i' since
- ** its pointer will be removed from the active pointer list.
- */
- count = 0;
- while (::Units.Ptr(i)==uptr && uptr->Strength) {
- // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
- // Likely due to damage biasing based on RA factions and/or difficulty settings
- // Applying this to units (vehicles), ships, buildings, and infantry, too
- //
- // damage = uptr->Strength; // Original
- damage = 0x7fff; // Copied from TD
- uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
- count++;
- if (count > 5 && uptr->IsActive) {
- delete uptr;
- break;
- }
- }
- i--;
- }
- }
- /*
- ** Destroy all aircraft owned by this house.
- */
- for (i = 0; i < ::Aircraft.Count(); i++) {
- if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) {
- AircraftClass * aptr = ::Aircraft.Ptr(i);
- // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
- // Likely due to damage biasing based on RA factions and/or difficulty settings
- // Applying this to units (vehicles), ships, buildings, and infantry, too
- //
- // damage = aptr->Strength; // Original
- damage = 0x7fff; // Copied from TD
- aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
- if (!aptr->IsActive) {
- i--;
- }
- }
- }
- /*
- ** Destroy all vessels owned by this house.
- */
- for (i = 0; i < ::Vessels.Count(); i++) {
- if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) {
- VesselClass * vptr = ::Vessels.Ptr(i);
- // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
- // Likely due to damage biasing based on RA factions and/or difficulty settings
- // Applying this to units (vehicles), ships, buildings, and infantry, too
- //
- // damage = vptr->Strength; // Original
- damage = 0x7fff; // Copied from TD
- vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
- if (!vptr->IsActive) {
- i--;
- }
- }
- }
- /*
- ** Buildings don't delete themselves when they die; they shake the screen
- ** and begin a countdown, so don't decrement 'i' when it's destroyed.
- */
- for (i = 0; i < Buildings.Count(); i++) {
- if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) {
- bptr = Buildings.Ptr(i);
- count = 0;
- while (Buildings.Ptr(i)==bptr && bptr->Strength) {
- // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
- // Likely due to damage biasing based on RA factions and/or difficulty settings
- // Applying this to units (vehicles), ships, buildings, and infantry, too
- //
- // damage = bptr->Strength; // Original
- damage = 0x7fff; // Copied from TD
- bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
- count++;
- if (count > 5) {
- delete bptr;
- break;
- }
- }
- }
- }
- /*
- ** Infantry don't delete themselves when they die; they go into a death-
- ** animation sequence, so there's no need to decrement 'i' when they die.
- ** Infantry should die by different types of warheads, so their death
- ** anims aren't all synchronized.
- */
- for (i = 0; i < Infantry.Count(); i++) {
- if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) {
- iptr = Infantry.Ptr(i);
- count = 0;
- while (Infantry.Ptr(i)==iptr && iptr->Strength) {
- // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840
- // Likely due to damage biasing based on RA factions and/or difficulty settings
- // Applying this to units (vehicles), ships, buildings, and infantry, too
- //
- // damage = iptr->Strength; // Original
- damage = 0x7fff; // Copied from TD
- warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE);
- iptr->Take_Damage(damage, 0, warhead, NULL, true);
- count++;
- if (count > 5) {
- delete iptr;
- break;
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Flag_To_Die -- Flags the house to blow up soon. *
- * *
- * When this routine is called, the house will blow up after a period of time. Typically *
- * this is called when the flag is captured or the HQ destroyed. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Was the house flagged to blow up? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/20/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Flag_To_Die(void)
- {
- assert(Houses.ID(this) == ID);
- if (!IsToWin && !IsToDie && !IsToLose) {
- IsToDie = true;
- BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
- }
- return(IsToDie);
- }
- /***********************************************************************************************
- * HouseClass::Flag_To_Win -- Flags the house to win soon. *
- * *
- * When this routine is called, the house will be declared the winner after a period of *
- * time. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Was the house flagged to win? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/20/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Flag_To_Win(void)
- {
- assert(Houses.ID(this) == ID);
- if (!IsToWin && !IsToDie && !IsToLose) {
- IsToWin = true;
- BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
- }
- return(IsToWin);
- }
- /***********************************************************************************************
- * HouseClass::Flag_To_Lose -- Flags the house to die soon. *
- * *
- * When this routine is called, it will spell the doom of this house. In a short while *
- * all of the object owned by this house will explode. Typical use of this routine is when *
- * the flag has been captured or the command vehicle has been destroyed. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Has the doom been initiated? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 06/12/1995 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Flag_To_Lose(void)
- {
- assert(Houses.ID(this) == ID);
- IsToWin = false;
- if (!IsToDie && !IsToLose) {
- IsToLose = true;
- BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay;
- }
- return(IsToLose);
- }
- /***********************************************************************************************
- * HouseClass::Init_Data -- Initializes the multiplayer color data. *
- * *
- * This routine is called when initializing the color and remap data for this house. The *
- * primary user of this routine is the multiplayer version of the game, especially for *
- * saving & loading multiplayer games. *
- * *
- * INPUT: color -- The color of this house. *
- * *
- * house -- The house that this should act like. *
- * *
- * credits -- The initial credits to assign to this house. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/29/1995 JLB : Created. *
- *=============================================================================================*/
- extern bool NowSavingGame; // TEMP MBL: Need to discuss better solution with Steve
- void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits)
- {
- assert(Houses.ID(this) == ID);
- Credits = Control.InitialCredits = credits;
- VisibleCredits.Current = Credits;
- RemapColor = color;
- ActLike = house;
- // MBL 03.20.2020
- // Attempt to fix Red Alert credit tick-up bug after saving a game that has had harvesting underway
- // Note that this code gets called with both game loads and saves
- // When this function is called, sometimes credits value has Tiberium (or HarvestedCredits?) variables applied, and sometimes now
- //
- if (NowSavingGame == true)
- {
- // At this point VisibleCredits.Current (set above) does not have harvested ore/tiberium applied, but VisibleCredits.Credits does
- VisibleCredits.Current = VisibleCredits.Credits;
- }
- }
- /***********************************************************************************************
- * HouseClass::Power_Fraction -- Fetches the current power output rating. *
- * *
- * Use this routine to fetch the current power output as a fixed point fraction. The *
- * value 0x0100 is 100% power. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with power rating as a fixed pointer number. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/22/1995 JLB : Created. *
- *=============================================================================================*/
- fixed HouseClass::Power_Fraction(void) const
- {
- assert(Houses.ID(this) == ID);
- if (Power >= Drain || Drain == 0) return(1);
- if (Power) {
- return(fixed(Power, Drain));
- }
- return(0);
- }
- /***********************************************************************************************
- * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. *
- * *
- * This routine will try to sell the wall at the specified location. If there is a wall *
- * present and it is owned by this house, then it can be sold. *
- * *
- * INPUT: cell -- The cell that wall selling is desired. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/05/1995 JLB : Created. *
- * 11/02/1996 JLB : Checks unsellable bit for wall type. *
- *=============================================================================================*/
- void HouseClass::Sell_Wall(CELL cell)
- {
- assert(Houses.ID(this) == ID);
- if ((unsigned)cell > 0) {
- OverlayType overlay = Map[cell].Overlay;
- if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) {
- OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay);
- if (optr.IsWall) {
- BuildingTypeClass const * btype = NULL;
- switch (overlay) {
- case OVERLAY_SANDBAG_WALL:
- btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL);
- break;
- case OVERLAY_CYCLONE_WALL:
- btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL);
- break;
- case OVERLAY_BRICK_WALL:
- btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL);
- break;
- case OVERLAY_BARBWIRE_WALL:
- btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL);
- break;
- case OVERLAY_WOOD_WALL:
- btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL);
- break;
- case OVERLAY_FENCE:
- btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE);
- break;
- default:
- break;
- }
- if (btype != NULL && !btype->IsUnsellable) {
- if (PlayerPtr == this) {
- Sound_Effect(VOC_CASHTURN);
- }
- Refund_Money(btype->Raw_Cost() * Rule.RefundPercent);
- Map[cell].Overlay = OVERLAY_NONE;
- Map[cell].OverlayData = 0;
- Map[cell].Owner = HOUSE_NONE;
- Map[cell].Wall_Update();
- CellClass * ncell = Map[cell].Adjacent_Cell(FACING_N);
- if (ncell) ncell->Wall_Update();
- CellClass * wcell = Map[cell].Adjacent_Cell(FACING_W);
- if (wcell) wcell->Wall_Update();
- CellClass * scell = Map[cell].Adjacent_Cell(FACING_S);
- if (scell) scell->Wall_Update();
- CellClass * ecell = Map[cell].Adjacent_Cell(FACING_E);
- if (ecell) ecell->Wall_Update();
- Map[cell].Recalc_Attributes();
- Map[cell].Redraw_Objects();
- Map.Radar_Pixel(cell);
- Detach_This_From_All(::As_Target(cell), true);
- if (optr.IsCrushable) {
- Map.Zone_Reset(MZONEF_NORMAL);
- } else {
- Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL);
- }
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. *
- * *
- * This routine is called when a construction yard needs to know what to build next. It *
- * will either examine the prebuilt base list or try to figure out what to build next *
- * based on the current game situation. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with a pointer to the building type class to build. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/27/1995 JLB : Created. *
- *=============================================================================================*/
- BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const
- {
- assert(Houses.ID(this) == ID);
- if (BuildStructure != STRUCT_NONE) {
- return(&BuildingTypeClass::As_Reference(BuildStructure));
- }
- return(NULL);
- }
- /***********************************************************************************************
- * HouseClass::Find_Building -- Finds a building of specified type. *
- * *
- * This routine is used to find a building of the specified type. This is particularly *
- * useful for when some event requires a specific building instance. The nuclear missile *
- * launch is a good example. *
- * *
- * INPUT: type -- The building type to scan for. *
- * *
- * zone -- The zone that the building must be located in. If no zone specific search *
- * is desired, then pass ZONE_NONE. *
- * *
- * OUTPUT: Returns with a pointer to the building type requested. If there is no building *
- * of the type requested, then NULL is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/27/1995 JLB : Created. *
- * 10/02/1995 JLB : Allows for zone specifics. *
- *=============================================================================================*/
- BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** Only scan if we KNOW there is at least one building of the type
- ** requested.
- */
- if (BQuantity[type] > 0) {
- /*
- ** Search for a suitable launch site for this missile.
- */
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * b = Buildings.Ptr(index);
- if (b && !b->IsInLimbo && b->House == this && *b == type) {
- if (zone == ZONE_NONE || Which_Zone(b) == zone) {
- return(b);
- }
- }
- }
- }
- return(NULL);
- }
- /***********************************************************************************************
- * HouseClass::Find_Build_Location -- Finds a suitable building location. *
- * *
- * This routine is used to find a suitable building location for the building specified. *
- * The auto base building logic uses this when building the base for the computer. *
- * *
- * INPUT: building -- Pointer to the building that needs to be placed down. *
- * *
- * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable *
- * locations, then NULL is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/27/1995 JLB : Created. *
- *=============================================================================================*/
- COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const
- {
- assert(Houses.ID(this) == ID);
- int zonerating[ZONE_COUNT];
- struct {
- int AntiAir; // Average air defense for the base.
- int AntiArmor; // Average armor defense for the base.
- int AntiInfantry; // Average infantry defense for the base.
- } zoneinfo = {0,0,0};
- int antiair = building->Anti_Air();
- int antiarmor = building->Anti_Armor();
- int antiinfantry = building->Anti_Infantry();
- bool adj = true;
- /*
- ** Never place combat buildings adjacent to each other. This is partly
- ** because combat buildings don't have a bib and jamming will occur as well
- ** as because spacing defensive buildings out will yield a better
- ** defense.
- */
- if (antiair || antiarmor || antiinfantry) {
- adj = false;
- }
- /*
- ** Determine the average zone strengths for the base. This value is
- ** used to determine what zones are considered under or over strength.
- */
- ZoneType z;
- for (z = ZONE_NORTH; z < ZONE_COUNT; z++) {
- zoneinfo.AntiAir += ZoneInfo[z].AirDefense;
- zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense;
- zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense;
- }
- zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH;
- zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH;
- zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH;
- /*
- ** Give each zone a rating for value. The higher the value the more desirable
- ** to place the specified building in that zone. Factor the average value of
- ** zone defense such that more weight is given to zones that are very under
- ** defended.
- */
- memset(&zonerating[0], '\0', sizeof(zonerating));
- for (z = ZONE_FIRST; z < ZONE_COUNT; z++) {
- int diff;
- diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense;
- if (z == ZONE_CORE) diff /= 2;
- if (diff > 0) {
- zonerating[z] += min(antiair, diff);
- }
- diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense;
- if (z == ZONE_CORE) diff /= 2;
- if (diff > 0) {
- zonerating[z] += min(antiarmor, diff);
- }
- diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense;
- if (z == ZONE_CORE) diff /= 2;
- if (diff > 0) {
- zonerating[z] += min(antiinfantry, diff);
- }
- }
- /*
- ** Now that each zone has been given a desirability rating, find the zone
- ** with the greatest value and try to place the building in that zone.
- */
- ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST);
- int largest = 0;
- for (z = ZONE_FIRST; z < ZONE_COUNT; z++) {
- if (zonerating[z] > largest) {
- zone = z;
- largest = zonerating[z];
- }
- }
- CELL zcell = Find_Cell_In_Zone(building, zone);
- if (zcell) {
- return(Cell_Coord(zcell));
- }
- /*
- ** Could not build in preferred zone, so try building in any zone.
- */
- static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST};
- int start = Random_Pick(0, ARRAY_SIZE(_zones)-1);
- for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) {
- ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)];
- zcell = Find_Cell_In_Zone(building, tryzone);
- if (zcell) return(zcell);
- }
- return(NULL);
- }
- /***********************************************************************************************
- * HouseClass::Recalc_Center -- Recalculates the center point of the base. *
- * *
- * This routine will average the location of the base and record the center point. The *
- * recorded center point is used to determine such things as how far the base is spread *
- * out and where to protect the most. This routine should be called whenever a building *
- * is created or destroyed. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/28/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Recalc_Center(void)
- {
- assert(Houses.ID(this) == ID);
- /*
- ** First presume that there is no base. If there is a base, then these values will be
- ** properly filled in below.
- */
- Center = 0;
- Radius = 0;
- for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) {
- ZoneInfo[zone].AirDefense = 0;
- ZoneInfo[zone].ArmorDefense = 0;
- ZoneInfo[zone].InfantryDefense = 0;
- }
- /*
- ** Only process the center base size/position calculation if there are buildings to
- ** consider. When no buildings for this house are present, then no processing need
- ** occur.
- */
- if (CurBuildings > 0) {
- int x = 0;
- int y = 0;
- int count = 0;
- int index;
- for (index = 0; index < Buildings.Count(); index++) {
- BuildingClass const * b = Buildings.Ptr(index);
- if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
- /*
- ** Give more "weight" to buildings that cost more. The presumption is that cheap
- ** buildings don't affect the base disposition as much as the more expensive
- ** buildings do.
- */
- int weight = (b->Class->Cost_Of() / 1000)+1;
- for (int i = 0; i < weight; i++) {
- x += Coord_X(b->Center_Coord());
- y += Coord_Y(b->Center_Coord());
- count++;
- }
- }
- }
- /*
- ** This second check for quantity of buildings is necessary because the first
- ** check against CurBuildings doesn't take into account if the building is in
- ** limbo, but for base calculation, the limbo state disqualifies a building
- ** from being processed. Thus, CurBuildings may indicate a base, but count may
- ** not match.
- */
- if (count > 0) {
- x /= count;
- y /= count;
- #ifdef NEVER
- /*
- ** Bias the center of the base away from the edges of the map.
- */
- LEPTON left = Cell_To_Lepton(Map.MapCellX + 10);
- LEPTON top = Cell_To_Lepton(Map.MapCellY + 10);
- LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10);
- LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10);
- if (x < left) x = left;
- if (x > right) x = right;
- if (y < top) y = top;
- if (y > bottom) y = bottom;
- #endif
- Center = XY_Coord(x, y);
- }
- /*
- ** If there were any buildings discovered as legal to consider as part of the base,
- ** then figure out the general average radius of the building disposition as it
- ** relates to the center of the base.
- */
- if (count > 1) {
- int radius = 0;
-
- for (index = 0; index < Buildings.Count(); index++) {
- BuildingClass const * b = Buildings.Ptr(index);
- if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
- radius += Distance(Center, b->Center_Coord());
- }
- }
- Radius = max(radius / count, 2 * CELL_LEPTON_W);
- /*
- ** Determine the relative strength of each base defense zone.
- */
- for (index = 0; index < Buildings.Count(); index++) {
- BuildingClass const * b = Buildings.Ptr(index);
- if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) {
- ZoneType z = Which_Zone(b);
- if (z != ZONE_NONE) {
- ZoneInfo[z].ArmorDefense += b->Anti_Armor();
- ZoneInfo[z].AirDefense += b->Anti_Air();
- ZoneInfo[z].InfantryDefense += b->Anti_Infantry();
- }
- }
- }
- } else {
- Radius = 0x0200;
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Expert_AI -- Handles expert AI processing. *
- * *
- * This routine is called when the computer should perform expert AI processing. This *
- * method of AI is categorized as an "Expert System" process. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns the number of game frames to delay before calling this routine again. *
- * *
- * WARNINGS: This is relatively time consuming -- call periodically. *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::Expert_AI(void)
- {
- assert(Houses.ID(this) == ID);
- BuildingClass * b = 0;
- bool stop = false;
- int time = TICKS_PER_SECOND * 10;
- /*
- ** If the current enemy no longer has a base or is defeated, then don't consider
- ** that house a threat anymore. Clear out the enemy record and then try
- ** to find a new enemy.
- */
- if (Enemy != HOUSE_NONE) {
- HouseClass * h = HouseClass::As_Pointer(Enemy);
- if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) {
- Enemy = HOUSE_NONE;
- }
- }
- /*
- ** If there is no enemy assigned to this house, then assign one now. The
- ** enemy that is closest is picked. However, don't pick an enemy if the
- ** base has not been established yet.
- */
- if (ActiveBScan && Center && Attack == 0) {
- int close = 0;
- HousesType enemy = HOUSE_NONE;
- int maxunit = 0;
- int maxinfantry = 0;
- int maxvessel = 0;
- int maxaircraft = 0;
- int maxbuilding = 0;
- int enemycount = 0;
- for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
- HouseClass * h = HouseClass::As_Pointer(house);
- if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) {
- /*
- ** Perform a special restriction check to ensure that no enemy is chosen if
- ** there is even one enemy that has not established a base yet. This will
- ** ensure an accurate first pick for enemy since the distance to base
- ** value can be determined.
- */
- if (!h->IsStarted) {
- enemy = HOUSE_NONE;
- break;
- }
- /*
- ** Keep track of the number of buildings and units owned by the
- ** enemy. This is used to bring up the maximum allowed to match.
- */
- maxunit += h->CurUnits;
- maxbuilding += h->CurBuildings;
- maxinfantry += h->CurInfantry;
- maxvessel += h->CurVessels;
- maxaircraft += h->CurAircraft;
- enemycount++;
- /*
- ** Determine a priority value based on distance to the center of the
- ** candidate base. The higher the value, the better the candidate house
- ** is to becoming the preferred enemy for this house.
- */
- int value = ((MAP_CELL_W*2)-Distance(Center, h->Center));
- value *= 2;
- /*
- ** In addition to distance, record the number of kills directed
- ** against this house. The enemy that does more damage might be
- ** considered a greater threat.
- */
- value += h->BuildingsKilled[Class->House]*5;
- value += h->UnitsKilled[Class->House];
- /*
- ** Factor in the relative sizes of the bases. An enemy that has a
- ** larger base will be considered a bigger threat. Conversely, a
- ** smaller base is considered a lesser threat.
- */
- value += h->CurUnits - CurUnits;
- value += h->CurBuildings - CurBuildings;
- value += (h->CurInfantry - CurInfantry)/4;
- /*
- ** Whoever last attacked is given a little more priority as
- ** a potential designated enemy.
- */
- if (house == LAEnemy) {
- value += 100;
- }
- #ifdef OBSOLETE
- /*
- ** Human players are a given preference as the target.
- */
- if (h->IsHuman) {
- value *= 2;
- }
- #endif
- /*
- ** Compare the calculated value for this candidate house and if it is
- ** greater than the previously recorded maximum, record this house as
- ** the prime candidate for enemy.
- */
- if (value > close) {
- enemy = house;
- close = value;
- }
- }
- }
- /*
- ** Record this closest enemy base as the first enemy to attack.
- */
- Enemy = enemy;
- /*
- ** Up the maximum allowed units and buildings to match a rough average
- ** of what the enemies are allowed.
- */
- if (enemycount) {
- maxunit /= enemycount;
- maxbuilding /= enemycount;
- maxinfantry /= enemycount;
- maxvessel /= enemycount;
- maxaircraft /= enemycount;
- }
- if (Control.MaxBuilding < (unsigned)maxbuilding + 10) {
- Control.MaxBuilding = maxbuilding + 10;
- }
- if (Control.MaxUnit < (unsigned)maxunit + 10) {
- Control.MaxUnit = maxunit + 10;
- }
- if (Control.MaxInfantry < (unsigned)maxinfantry + 10) {
- Control.MaxInfantry = maxinfantry + 10;
- }
- if (Control.MaxVessel < (unsigned)maxvessel + 10) {
- Control.MaxVessel = maxvessel + 10;
- }
- if (Control.MaxAircraft < (unsigned)maxaircraft + 10) {
- Control.MaxAircraft = maxaircraft + 10;
- }
- }
- /*
- ** House state transition check occurs here. Transitions that occur here are ones
- ** that relate to general base condition rather than specific combat events.
- ** Typically, this is limited to transitions between normal buildup mode and
- ** broke mode.
- */
- if (State == STATE_ENDGAME) {
- Fire_Sale();
- Do_All_To_Hunt();
- } else {
- if (State == STATE_BUILDUP) {
- if (Available_Money() < 25) {
- State = STATE_BROKE;
- }
- }
- if (State == STATE_BROKE) {
- if (Available_Money() >= 25) {
- State = STATE_BUILDUP;
- }
- }
- if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) {
- State = STATE_BUILDUP;
- }
- if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) {
- State = STATE_ATTACKED;
- }
- }
- /*
- ** Records the urgency of all actions possible.
- */
- UrgencyType urgency[STRATEGY_COUNT];
- StrategyType strat;
- for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) {
- urgency[strat] = URGENCY_NONE;
- switch (strat) {
- case STRATEGY_BUILD_POWER:
- urgency[strat] = Check_Build_Power();
- break;
- case STRATEGY_BUILD_DEFENSE:
- urgency[strat] = Check_Build_Defense();
- break;
- case STRATEGY_BUILD_INCOME:
- urgency[strat] = Check_Build_Income();
- break;
- case STRATEGY_FIRE_SALE:
- urgency[strat] = Check_Fire_Sale();
- break;
- case STRATEGY_BUILD_ENGINEER:
- urgency[strat] = Check_Build_Engineer();
- break;
- case STRATEGY_BUILD_OFFENSE:
- urgency[strat] = Check_Build_Offense();
- break;
- case STRATEGY_RAISE_MONEY:
- urgency[strat] = Check_Raise_Money();
- break;
- case STRATEGY_RAISE_POWER:
- urgency[strat] = Check_Raise_Power();
- break;
- case STRATEGY_LOWER_POWER:
- urgency[strat] = Check_Lower_Power();
- break;
- case STRATEGY_ATTACK:
- urgency[strat] = Check_Attack();
- break;
- default:
- urgency[strat] = URGENCY_NONE;
- break;
- }
- }
- /*
- ** Performs the action required for each of the strategies that share
- ** the most urgent category. Stop processing if any strategy at the
- ** highest urgency performed any action. This is because higher urgency
- ** actions tend to greatly affect the lower urgency actions.
- */
- for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) {
- bool acted = false;
- for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) {
- if (urgency[strat] == u) {
- switch (strat) {
- case STRATEGY_BUILD_POWER:
- acted |= AI_Build_Power(u);
- break;
- case STRATEGY_BUILD_DEFENSE:
- acted |= AI_Build_Defense(u);
- break;
- case STRATEGY_BUILD_INCOME:
- acted |= AI_Build_Income(u);
- break;
- case STRATEGY_FIRE_SALE:
- acted |= AI_Fire_Sale(u);
- break;
- case STRATEGY_BUILD_ENGINEER:
- acted |= AI_Build_Engineer(u);
- break;
- case STRATEGY_BUILD_OFFENSE:
- acted |= AI_Build_Offense(u);
- break;
- case STRATEGY_RAISE_MONEY:
- acted |= AI_Raise_Money(u);
- break;
- case STRATEGY_RAISE_POWER:
- acted |= AI_Raise_Power(u);
- break;
- case STRATEGY_LOWER_POWER:
- acted |= AI_Lower_Power(u);
- break;
- case STRATEGY_ATTACK:
- acted |= AI_Attack(u);
- break;
- default:
- break;
- }
- }
- }
- }
- return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2));
- }
- UrgencyType HouseClass::Check_Build_Power(void) const
- {
- assert(Houses.ID(this) == ID);
- fixed frac = Power_Fraction();
- UrgencyType urgency = URGENCY_NONE;
- if (frac < 1 && Can_Make_Money()) {
- urgency = URGENCY_LOW;
- /*
- ** Very low power condition is considered a higher priority.
- */
- if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM;
- /*
- ** When under attack and there is a need for power in defense,
- ** then consider power building a higher priority.
- */
- if (State == STATE_THREATENED || State == STATE_ATTACKED) {
- if (BScan | (STRUCTF_CHRONOSPHERE)) {
- urgency = URGENCY_HIGH;
- }
- }
- }
- return(urgency);
- }
- UrgencyType HouseClass::Check_Build_Defense(void) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** This routine determines what urgency level that base defense
- ** should be given. The more vulnerable the base is, the higher
- ** the urgency this routine should return.
- */
- return(URGENCY_NONE);
- }
- UrgencyType HouseClass::Check_Build_Offense(void) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** This routine determines what urgency level that offensive
- ** weaponry should be given. Surplus money or a very strong
- ** defense will cause the offensive urgency to increase.
- */
- return(URGENCY_NONE);
- }
- /*
- ** Determines what the attack state of the base is. The higher the state,
- ** the greater the immediate threat to base defense is.
- */
- UrgencyType HouseClass::Check_Attack(void) const
- {
- assert(Houses.ID(this) == ID);
- if (Frame > TICKS_PER_MINUTE && Attack == 0) {
- if (State == STATE_ATTACKED) {
- return(URGENCY_LOW);
- }
- return(URGENCY_CRITICAL);
- }
- return(URGENCY_NONE);
- }
- UrgencyType HouseClass::Check_Build_Income(void) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** This routine should determine if income processing buildings
- ** should be constructed and at what urgency. The lower the money,
- ** the lower the refineries, or recent harvester losses should
- ** cause a greater urgency to be returned.
- */
- return(URGENCY_NONE);
- }
- UrgencyType HouseClass::Check_Fire_Sale(void) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** If there are no more factories at all, then sell everything off because the game
- ** is basically over at this point.
- */
- if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) {
- return(URGENCY_CRITICAL);
- }
- return(URGENCY_NONE);
- }
- UrgencyType HouseClass::Check_Build_Engineer(void) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** This routine should check to see what urgency that the production of
- ** engineers should be. If a friendly building has been captured or the
- ** enemy has weak defenses, then building an engineer would be a priority.
- */
- return(URGENCY_NONE);
- }
- /*
- ** Checks to see if money is critically low and something must be done
- ** to immediately raise cash.
- */
- UrgencyType HouseClass::Check_Raise_Money(void) const
- {
- assert(Houses.ID(this) == ID);
- UrgencyType urgency = URGENCY_NONE;
- if (Available_Money() < 100) {
- urgency = URGENCY_LOW;
- }
- if (Available_Money() < 2000 && !Can_Make_Money()) {
- urgency++;
- }
- return(urgency);
- }
- /*
- ** Checks to see if power is very low and if so, a greater urgency to
- ** build more power is returned.
- */
- UrgencyType HouseClass::Check_Lower_Power(void) const
- {
- assert(Houses.ID(this) == ID);
- if (Power > Drain+300) {
- return(URGENCY_LOW);
- }
- return(URGENCY_NONE);
- }
- /*
- ** This routine determines if there is a power emergency. Such an
- ** emergency might require selling of structures in order to free
- ** up power. This might occur if the base is being attacked and there
- ** are defenses that require power, but are just short of having
- ** enough.
- */
- UrgencyType HouseClass::Check_Raise_Power(void) const
- {
- assert(Houses.ID(this) == ID);
- UrgencyType urgency = URGENCY_NONE;
- if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) {
- // if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) {
- urgency = URGENCY_MEDIUM;
- if (State == STATE_ATTACKED) {
- urgency++;
- }
- }
- return(urgency);
- }
- bool HouseClass::AI_Attack(UrgencyType )
- {
- assert(Houses.ID(this) == ID);
- bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33));
- bool forced = (CurBuildings == 0);
- int index;
- for (index = 0; index < Aircraft.Count(); index++) {
- AircraftClass * a = Aircraft.Ptr(index);
- if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) {
- if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) {
- a->Assign_Mission(MISSION_HUNT);
- }
- }
- }
- for (index = 0; index < Units.Count(); index++) {
- UnitClass * u = Units.Ptr(index);
- if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) {
- if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) {
- u->Assign_Mission(MISSION_HUNT);
- } else {
- /*
- ** If this unit is guarding the base, then cause it to shuffle
- ** location instead.
- */
- if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) {
- u->ArchiveTarget = ::As_Target(Where_To_Go(u));
- }
- }
- }
- }
- for (index = 0; index < Infantry.Count(); index++) {
- InfantryClass * i = Infantry.Ptr(index);
- if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) {
- if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) {
- i->Assign_Mission(MISSION_HUNT);
- } else {
- /*
- ** If this soldier is guarding the base, then cause it to shuffle
- ** location instead.
- */
- if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) {
- i->ArchiveTarget = ::As_Target(Where_To_Go(i));
- }
- }
- }
- }
- Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2);
- return(true);
- }
- /*
- ** Given the specified urgency, build a power structure to meet
- ** this need.
- */
- bool HouseClass::AI_Build_Power(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- return(false);
- }
- /*
- ** Given the specified urgency, build base defensive structures
- ** according to need and according to existing base disposition.
- */
- bool HouseClass::AI_Build_Defense(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- return(false);
- }
- /*
- ** Given the specified urgency, build offensive units according
- ** to need and according to the opponents base defenses.
- */
- bool HouseClass::AI_Build_Offense(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- return(false);
- }
- /*
- ** Given the specified urgency, build income producing
- ** structures according to need.
- */
- bool HouseClass::AI_Build_Income(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- return(false);
- }
- bool HouseClass::AI_Fire_Sale(UrgencyType urgency)
- {
- assert(Houses.ID(this) == ID);
- if (CurBuildings && urgency == URGENCY_CRITICAL) {
- Fire_Sale();
- Do_All_To_Hunt();
- return(true);
- }
- return(false);
- }
- /*
- ** Given the specified urgency, build an engineer.
- */
- bool HouseClass::AI_Build_Engineer(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- return(false);
- }
- /*
- ** Given the specified urgency, sell of some power since
- ** there appears to be excess.
- */
- bool HouseClass::AI_Lower_Power(UrgencyType ) const
- {
- assert(Houses.ID(this) == ID);
- BuildingClass * b = Find_Building(STRUCT_POWER);
- if (b != NULL) {
- b->Sell_Back(1);
- return(true);
- }
- b = Find_Building(STRUCT_ADVANCED_POWER);
- if (b != NULL) {
- b->Sell_Back(1);
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. *
- * *
- * This routine is called when the computer needs to raise power by selling off buildings. *
- * Usually this occurs because of some catastrophe that has lowered power levels to *
- * the danger zone. *
- * *
- * INPUT: urgency -- The urgency that the power needs to be raised. This controls what *
- * buildings will be sold. *
- * *
- * OUTPUT: bool; Was a building sold to raise power? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/02/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::AI_Raise_Power(UrgencyType urgency) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** Sell off structures in this order.
- */
- static struct {
- StructType Structure;
- UrgencyType Urgency;
- } _types[] = {
- {STRUCT_CHRONOSPHERE, URGENCY_LOW},
- {STRUCT_SHIP_YARD, URGENCY_LOW},
- {STRUCT_SUB_PEN, URGENCY_LOW},
- {STRUCT_ADVANCED_TECH, URGENCY_LOW},
- {STRUCT_FORWARD_COM, URGENCY_LOW},
- {STRUCT_SOVIET_TECH, URGENCY_LOW},
- {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM},
- {STRUCT_RADAR, URGENCY_MEDIUM},
- {STRUCT_REPAIR, URGENCY_MEDIUM},
- {STRUCT_TESLA, URGENCY_HIGH}
- };
- /*
- ** Find a structure to sell and then sell it. Bail from further scanning until
- ** the next time.
- */
- for (int i = 0; i < ARRAY_SIZE(_types); i++) {
- if (urgency >= _types[i].Urgency) {
- BuildingClass * b = Find_Building(_types[i].Structure);
- if (b != NULL) {
- b->Sell_Back(1);
- return(true);
- }
- }
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. *
- * *
- * This routine handles the situation where the computer desperately needs cash but cannot *
- * wait for normal harvesting to raise it. Buildings must be sold. *
- * *
- * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, *
- * the more important the buildings that can be sold become. *
- * *
- * OUTPUT: bool; Was a building sold to raise cash? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/02/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::AI_Raise_Money(UrgencyType urgency) const
- {
- assert(Houses.ID(this) == ID);
- /*
- ** Sell off structures in this order.
- */
- static struct {
- StructType Structure;
- UrgencyType Urgency;
- } _types[] = {
- {STRUCT_CHRONOSPHERE, URGENCY_LOW},
- {STRUCT_SHIP_YARD, URGENCY_LOW},
- {STRUCT_SUB_PEN, URGENCY_LOW},
- {STRUCT_ADVANCED_TECH, URGENCY_LOW},
- {STRUCT_FORWARD_COM, URGENCY_LOW},
- {STRUCT_SOVIET_TECH, URGENCY_LOW},
- {STRUCT_STORAGE,URGENCY_LOW},
- {STRUCT_REPAIR,URGENCY_LOW},
- {STRUCT_TESLA,URGENCY_MEDIUM},
- {STRUCT_HELIPAD,URGENCY_MEDIUM},
- {STRUCT_POWER,URGENCY_HIGH},
- {STRUCT_AIRSTRIP,URGENCY_HIGH},
- // {STRUCT_WEAP,URGENCY_HIGH},
- // {STRUCT_BARRACKS,URGENCY_HIGH},
- // {STRUCT_TENT,URGENCY_HIGH},
- {STRUCT_CONST,URGENCY_CRITICAL}
- };
- BuildingClass * b = 0;
- /*
- ** Find a structure to sell and then sell it. Bail from further scanning until
- ** the next time.
- */
- for (int i = 0; i < ARRAY_SIZE(_types); i++) {
- if (urgency >= _types[i].Urgency) {
- b = Find_Building(_types[i].Structure);
- if (b != NULL) {
- b->Sell_Back(1);
- return(true);
- }
- }
- }
- return(false);
- }
- #ifdef NEVER
- /***********************************************************************************************
- * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. *
- * *
- * This logic is used to maintain a base defense. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::AI_Base_Defense(void)
- {
- assert(Houses.ID(this) == ID);
- /*
- ** Check to find if any zone of the base is over defended. Such zones should have
- ** some of their defenses sold off to make better use of the money.
- */
- /*
- ** Make sure that the core defense is only about 1/2 of the perimeter defense average.
- */
- int average = 0;
- for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) {
- average += ZoneInfo[z].AirDefense;
- average += ZoneInfo[z].ArmorDefense;
- average += ZoneInfo[z].InfantryDefense;
- }
- average /= (ZONE_COUNT-ZONE_NORTH);
- /*
- ** If the core value is greater than the average, then sell off some of the
- ** inner defensive structures.
- */
- int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense;
- if (core >= average) {
- static StructType _stype[] = {
- STRUCT_GTOWER,
- STRUCT_TURRET,
- STRUCT_ATOWER,
- STRUCT_OBELISK,
- STRUCT_TESLA,
- STRUCT_SAM
- };
- BuildingClass * b;
- for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) {
- b = Find_Building(_stype[index], ZONE_CORE);
- if (b) {
- b->Sell_Back(1);
- break;
- }
- }
- }
- /*
- ** If the enemy doesn't have any offensive air capability, then sell off any
- ** SAM sites. Only do this when money is moderately low.
- */
- if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) {
- /*
- ** Scan to find if ANY human opponents have aircraft or a helipad. If one
- ** is found then consider that opponent to have a valid air threat potential.
- ** Don't sell off SAM sites in that case.
- */
- bool nothreat = true;
- for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
- HouseClass * house = HouseClass::As_Pointer(h);
- if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) {
- if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) {
- nothreat = false;
- break;
- }
- }
- }
- }
- return(TICKS_PER_SECOND*5);
- }
- #endif
- /***********************************************************************************************
- * HouseClass::AI_Building -- Determines what building to build. *
- * *
- * This routine handles the general case of determining what building to build next. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- * 11/03/1996 JLB : Tries to match aircraft of enemy *
- *=============================================================================================*/
- int HouseClass::AI_Building(void)
- {
- assert(Houses.ID(this) == ID);
- if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND);
- if (Session.Type == GAME_NORMAL && Base.House == Class->House) {
- BaseNodeClass * node = Base.Next_Buildable();
- if (node) {
- BuildStructure = node->Type;
- }
- }
- if (IsBaseBuilding) {
- /*
- ** Don't suggest anything to build if the base is already big enough.
- */
- unsigned int quant = 0;
- for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
- HouseClass const * hptr = HouseClass::As_Pointer(h);
- if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) {
- quant = hptr->CurBuildings;
- }
- }
- quant += Rule.BaseSizeAdd;
- // TCTC -- Should multiply largest player base by some rational number.
- // if (CurBuildings >= quant) return(TICKS_PER_SECOND);
- BuildChoice.Free_All();
- BuildChoiceClass * choiceptr;
- StructType stype = STRUCT_NONE;
- int money = Available_Money();
- int level = Control.TechLevel;
- bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0);
- BuildingTypeClass const * b = NULL;
- HouseClass const * enemy = NULL;
- if (Enemy != HOUSE_NONE) {
- enemy = HouseClass::As_Pointer(Enemy);
- }
- level = Control.TechLevel;
- /*
- ** Try to build a power plant if there is insufficient power and there is enough
- ** money available.
- */
- b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER);
- if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
- }
- } else {
- b = &BuildingTypeClass::As_Reference(STRUCT_POWER);
- if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** Build a refinery if there isn't one already available.
- */
- unsigned int current = BQuantity[STRUCT_REFINERY];
- if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) {
- b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY);
- if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** Always make sure there is a barracks available, but only if there
- ** will be sufficient money to train troopers.
- */
- current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT];
- if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) {
- b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
- }
- } else {
- b = &BuildingTypeClass::As_Reference(STRUCT_TENT);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- }
- /*
- ** Try to build one dog house.
- */
- current = BQuantity[STRUCT_KENNEL];
- if (current < 1 && (money > 300 || hasincome)) {
- b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** Try to build one gap generator.
- */
- current = BQuantity[STRUCT_GAP];
- if (current < 1 && Power_Fraction() >= 1 && hasincome) {
- b = &BuildingTypeClass::As_Reference(STRUCT_GAP);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** A source of combat vehicles is always needed, but only if there will
- ** be sufficient money to build vehicles.
- */
- current = BQuantity[STRUCT_WEAP];
- if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) {
- b = &BuildingTypeClass::As_Reference(STRUCT_WEAP);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** Always build up some base defense.
- */
- current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET];
- if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) {
- b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- } else {
- if (Percent_Chance(50)) {
- b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- } else {
- b = &BuildingTypeClass::As_Reference(STRUCT_TURRET);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- }
- }
- /*
- ** Build some air defense.
- */
- current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN];
- if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) {
- /*
- ** Building air defense only makes sense if the opponent has aircraft
- ** of some kind.
- */
- bool airthreat = false;
- int threat_quantity = 0;
- if (enemy != NULL && enemy->AScan != 0) {
- airthreat = true;
- threat_quantity = enemy->CurAircraft;
- }
- if (!airthreat) {
- for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
- HouseClass * h = HouseClass::As_Pointer(house);
- if (h != NULL && !Is_Ally(house) && h->AScan != 0) {
- airthreat = true;
- break;
- }
- }
- }
- if (airthreat) {
- if (BQuantity[STRUCT_RADAR] == 0) {
- b = &BuildingTypeClass::As_Reference(STRUCT_RADAR);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type);
- }
- }
- }
- b = &BuildingTypeClass::As_Reference(STRUCT_SAM);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
- }
- } else {
- b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- }
- }
- /*
- ** Advanced base defense would be good.
- */
- current = BQuantity[STRUCT_TESLA];
- if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) {
- b = &BuildingTypeClass::As_Reference(STRUCT_TESLA);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** Build a tech center as soon as possible.
- */
- current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH];
- if (current < 1) {
- b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- } else {
- b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- }
- /*
- ** A helipad would be good.
- */
- current = BQuantity[STRUCT_HELIPAD];
- if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) {
- b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- int threat_quantity = 0;
- if (enemy != NULL) {
- threat_quantity = enemy->CurAircraft;
- }
- *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- /*
- ** An airstrip would be good.
- */
- current = BQuantity[STRUCT_AIRSTRIP];
- if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) {
- b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP);
- if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr != NULL) {
- int threat_quantity = 0;
- if (enemy != NULL) {
- threat_quantity = enemy->CurAircraft;
- }
- *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type);
- }
- }
- }
- #ifdef OLD
- /*
- ** Build a repair bay if there isn't one already available.
- */
- current = BQuantity[STRUCT_REPAIR];
- if (current == 0) {
- b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR);
- if (Can_Build(b, ActLike) && b->Cost_Of() < money) {
- choiceptr = BuildChoice.Alloc();
- if (choiceptr) {
- *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
- }
- }
- }
- #endif
- /*
- ** Pick the choice that is the most urgent.
- */
- UrgencyType best = URGENCY_NONE;
- int bestindex;
- for (int index = 0; index < BuildChoice.Count(); index++) {
- if (BuildChoice.Ptr(index)->Urgency > best) {
- bestindex = index;
- best = BuildChoice.Ptr(index)->Urgency;
- }
- }
- if (best != URGENCY_NONE) {
- BuildStructure = BuildChoice.Ptr(bestindex)->Structure;
- }
- }
- return(TICKS_PER_SECOND);
- }
- /***********************************************************************************************
- * HouseClass::AI_Unit -- Determines what unit to build next. *
- * *
- * This routine handles the general case of determining what units to build next. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of games frames to delay before calling this routine again.*
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::AI_Unit(void)
- {
- assert(Houses.ID(this) == ID);
- if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND);
- if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND);
- /*
- ** A computer controlled house will try to build a replacement
- ** harvester if possible.
- */
- if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) {
- if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) {
- BuildUnit = UNIT_HARVESTER;
- return(TICKS_PER_SECOND);
- }
- }
- if (Session.Type == GAME_NORMAL) {
- int counter[UNIT_COUNT];
- memset(counter, 0x00, sizeof(counter));
- /*
- ** Build a list of the maximum of each type we wish to produce. This will be
- ** twice the number required to fill all teams.
- */
- int index;
- for (index = 0; index < Teams.Count(); index++) {
- TeamClass * tptr = Teams.Ptr(index);
- if (tptr != NULL) {
- TeamTypeClass const * team = tptr->Class;
- if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- TechnoTypeClass const * memtype = team->Members[subindex].Class;
- if (memtype->What_Am_I() == RTTI_UNITTYPE) {
- counter[((UnitTypeClass const *)memtype)->Type] = 1;
- }
- }
- }
- }
- }
- /*
- ** Team types that are flagged as prebuilt, will always try to produce enough
- ** to fill one team of this type regardless of whether there is a team active
- ** of that type.
- */
- for (index = 0; index < TeamTypes.Count(); index++) {
- TeamTypeClass const * team = TeamTypes.Ptr(index);
- if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- TechnoTypeClass const * memtype = team->Members[subindex].Class;
- if (memtype->What_Am_I() == RTTI_UNITTYPE) {
- int subtype = ((UnitTypeClass const *)memtype)->Type;
- counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
- }
- }
- }
- }
- /*
- ** Reduce the theoretical maximum by the actual number of objects currently
- ** in play.
- */
- for (int uindex = 0; uindex < Units.Count(); uindex++) {
- UnitClass * unit = Units.Ptr(uindex);
- if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) {
- counter[unit->Class->Type]--;
- }
- }
- /*
- ** Pick to build the most needed object but don't consider those objects that
- ** can't be built because of scenario restrictions or insufficient cash.
- */
- int bestval = -1;
- int bestcount = 0;
- UnitType bestlist[UNIT_COUNT];
- for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) {
- if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
- if (bestval == -1 || bestval < counter[utype]) {
- bestval = counter[utype];
- bestcount = 0;
- }
- bestlist[bestcount++] = utype;
- }
- }
- /*
- ** The unit type to build is now known. Fetch a pointer to the techno type class.
- */
- if (bestcount) {
- BuildUnit = bestlist[Random_Pick(0, bestcount-1)];
- }
- }
- if (IsBaseBuilding) {
- int counter[UNIT_COUNT];
- int total = 0;
- UnitType index;
- for (index = UNIT_FIRST; index < UNIT_COUNT; index++) {
- UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index);
- if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) {
- if (utype->PrimaryWeapon != NULL) {
- counter[index] = 20;
- } else {
- counter[index] = 1;
- }
- } else {
- counter[index] = 0;
- }
- total += counter[index];
- }
- if (total > 0) {
- int choice = Random_Pick(0, total-1);
- for (index = UNIT_FIRST; index < UNIT_COUNT; index++) {
- if (choice < counter[index]) {
- BuildUnit = index;
- break;
- }
- choice -= counter[index];
- }
- }
- }
- return(TICKS_PER_SECOND);
- }
- int HouseClass::AI_Vessel(void)
- {
- assert(Houses.ID(this) == ID);
- if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND);
- if (CurVessels >= Control.MaxVessel) {
- return(TICKS_PER_SECOND);
- }
- if (Session.Type == GAME_NORMAL) {
- int counter[VESSEL_COUNT];
- if (Session.Type == GAME_NORMAL) {
- memset(counter, 0x00, sizeof(counter));
- } else {
- for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) {
- if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) {
- counter[index] = 16;
- } else {
- counter[index] = 0;
- }
- }
- }
- /*
- ** Build a list of the maximum of each type we wish to produce. This will be
- ** twice the number required to fill all teams.
- */
- int index;
- for (index = 0; index < Teams.Count(); index++) {
- TeamClass * tptr = Teams.Ptr(index);
- if (tptr) {
- TeamTypeClass const * team = tptr->Class;
- if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) {
- counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1;
- }
- }
- }
- }
- }
- /*
- ** Team types that are flagged as prebuilt, will always try to produce enough
- ** to fill one team of this type regardless of whether there is a team active
- ** of that type.
- */
- for (index = 0; index < TeamTypes.Count(); index++) {
- TeamTypeClass const * team = TeamTypes.Ptr(index);
- if (team) {
- if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) {
- int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type;
- counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
- }
- }
- }
- }
- }
- /*
- ** Reduce the theoretical maximum by the actual number of objects currently
- ** in play.
- */
- for (int vindex = 0; vindex < Vessels.Count(); vindex++) {
- VesselClass * unit = Vessels.Ptr(vindex);
- if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) {
- counter[unit->Class->Type]--;
- }
- }
- /*
- ** Pick to build the most needed object but don't consider those object that
- ** can't be built because of scenario restrictions or insufficient cash.
- */
- int bestval = -1;
- int bestcount = 0;
- VesselType bestlist[VESSEL_COUNT];
- for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) {
- if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
- if (bestval == -1 || bestval < counter[utype]) {
- bestval = counter[utype];
- bestcount = 0;
- }
- bestlist[bestcount++] = utype;
- }
- }
- /*
- ** The unit type to build is now known. Fetch a pointer to the techno type class.
- */
- if (bestcount) {
- BuildVessel = bestlist[Random_Pick(0, bestcount-1)];
- }
- }
- if (IsBaseBuilding) {
- BuildVessel = VESSEL_NONE;
- }
- return(TICKS_PER_SECOND);
- }
- /***********************************************************************************************
- * HouseClass::AI_Infantry -- Determines the infantry unit to build. *
- * *
- * This routine handles the general case of determining what infantry unit to build *
- * next. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of game frames to delay before being called again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::AI_Infantry(void)
- {
- assert(Houses.ID(this) == ID);
- if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND);
- if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND);
- if (Session.Type == GAME_NORMAL) {
- TechnoTypeClass const * techno = 0;
- int counter[INFANTRY_COUNT];
- memset(counter, 0x00, sizeof(counter));
- /*
- ** Build a list of the maximum of each type we wish to produce. This will be
- ** twice the number required to fill all teams.
- */
- int index;
- for (index = 0; index < Teams.Count(); index++) {
- TeamClass * tptr = Teams.Ptr(index);
- if (tptr != NULL) {
- TeamTypeClass const * team = tptr->Class;
- if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) {
- counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0);
- }
- }
- }
- }
- }
- /*
- ** Team types that are flagged as prebuilt, will always try to produce enough
- ** to fill one team of this type regardless of whether there is a team active
- ** of that type.
- */
- for (index = 0; index < TeamTypes.Count(); index++) {
- TeamTypeClass const * team = TeamTypes.Ptr(index);
- if (team != NULL) {
- if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) {
- for (int subindex = 0; subindex < team->ClassCount; subindex++) {
- if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) {
- int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type;
- // counter[subtype] = 1;
- counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity);
- counter[subtype] = min(counter[subtype], 5);
- }
- }
- }
- }
- }
- /*
- ** Reduce the theoretical maximum by the actual number of objects currently
- ** in play.
- */
- for (int uindex = 0; uindex < Infantry.Count(); uindex++) {
- InfantryClass * infantry = Infantry.Ptr(uindex);
- if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) {
- counter[infantry->Class->Type]--;
- }
- }
- /*
- ** Pick to build the most needed object but don't consider those object that
- ** can't be built because of scenario restrictions or insufficient cash.
- */
- int bestval = -1;
- int bestcount = 0;
- InfantryType bestlist[INFANTRY_COUNT];
- for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) {
- if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) {
- if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) {
- if (bestval == -1 || bestval < counter[utype]) {
- bestval = counter[utype];
- bestcount = 0;
- }
- bestlist[bestcount++] = utype;
- }
- }
- }
- /*
- ** The infantry type to build is now known. Fetch a pointer to the techno type class.
- */
- if (bestcount) {
- int pick = Random_Pick(0, bestcount-1);
- BuildInfantry = bestlist[pick];
- }
- }
- if (IsBaseBuilding) {
- HouseClass const * enemy = NULL;
- if (Enemy != HOUSE_NONE) {
- enemy = HouseClass::As_Pointer(Enemy);
- }
- /*
- ** This structure is used to keep track of the list of infantry types that should be
- ** built. The infantry type and the value assigned to it is recorded.
- */
- struct {
- InfantryType Type; // Infantry type.
- int Value; // Relative value assigned.
- } typetrack[INFANTRY_COUNT];
- int count = 0;
- int total = 0;
- for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) {
- if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) {
- typetrack[count].Value = 0;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility.
- int clipindex = index;
- if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT;
- if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) {
- #else
- if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) {
- #endif
- switch (index) {
- case INFANTRY_E1:
- typetrack[count].Value = 3;
- break;
- case INFANTRY_E2:
- typetrack[count].Value = 5;
- break;
- case INFANTRY_E3:
- typetrack[count].Value = 2;
- break;
- case INFANTRY_E4:
- typetrack[count].Value = 5;
- break;
- case INFANTRY_RENOVATOR:
- if (CurInfantry > 5) {
- typetrack[count].Value = 1 - max(IQuantity[index], 0);
- }
- break;
- case INFANTRY_TANYA:
- typetrack[count].Value = 1 - max(IQuantity[index], 0);
- break;
- default:
- typetrack[count].Value = 0;
- break;
- }
- }
- if (typetrack[count].Value > 0) {
- typetrack[count].Type = index;
- total += typetrack[count].Value;
- count++;
- }
- }
- }
- /*
- ** If there is at least one choice, then pick it. The object picked
- ** is influenced by the weight (value) assigned to it. This is accomplished
- ** by picking a number between 0 and the total weight value. The appropriate
- ** infantry object that matches the number picked is then selected to be built.
- */
- if (count > 0) {
- int pick = Random_Pick(0, total-1);
- for (int index = 0; index < count; index++) {
- if (pick < typetrack[index].Value) {
- BuildInfantry = typetrack[index].Type;
- break;
- }
- pick -= typetrack[index].Value;
- }
- }
- }
- return(TICKS_PER_SECOND);
- }
- /***********************************************************************************************
- * HouseClass::AI_Aircraft -- Determines what aircraft to build next. *
- * *
- * This routine is used to determine the general case of what aircraft to build next. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns with the number of frame to delay before calling this routine again. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::AI_Aircraft(void)
- {
- assert(Houses.ID(this) == ID);
- if (!IsHuman && IQ >= Rule.IQAircraft) {
- if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND);
- if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND);
- if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) &&
- AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel &&
- BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) {
- BuildAircraft = AIRCRAFT_LONGBOW;
- return(TICKS_PER_SECOND);
- }
- if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) &&
- AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel &&
- BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) {
- BuildAircraft = AIRCRAFT_HIND;
- return(TICKS_PER_SECOND);
- }
- if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) &&
- AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel &&
- BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) {
- BuildAircraft = AIRCRAFT_MIG;
- return(TICKS_PER_SECOND);
- }
- if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) &&
- AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel &&
- BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) {
- BuildAircraft = AIRCRAFT_YAK;
- return(TICKS_PER_SECOND);
- }
- }
- return(TICKS_PER_SECOND);
- }
- /***********************************************************************************************
- * HouseClass::Production_Begun -- Records that production has begun. *
- * *
- * This routine is used to inform the Expert System that production of the specified object *
- * has begun. This allows the AI to proceed with picking another object to begin production *
- * on. *
- * *
- * INPUT: product -- Pointer to the object that production has just begun on. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Production_Begun(TechnoClass const * product)
- {
- assert(Houses.ID(this) == ID);
- if (product != NULL) {
- switch (product->What_Am_I()) {
- case RTTI_UNIT:
- if (*((UnitClass*)product) == BuildUnit) {
- BuildUnit = UNIT_NONE;
- }
- break;
- case RTTI_VESSEL:
- if (*((VesselClass*)product) == BuildVessel) {
- BuildVessel = VESSEL_NONE;
- }
- break;
- case RTTI_INFANTRY:
- if (*((InfantryClass*)product) == BuildInfantry) {
- BuildInfantry = INFANTRY_NONE;
- }
- break;
- case RTTI_BUILDING:
- if (*((BuildingClass*)product) == BuildStructure) {
- BuildStructure = STRUCT_NONE;
- }
- break;
- case RTTI_AIRCRAFT:
- if (*((AircraftClass*)product) == BuildAircraft) {
- BuildAircraft = AIRCRAFT_NONE;
- }
- break;
- default:
- break;
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Tracking_Remove -- Remove object from house tracking system. *
- * *
- * This routine informs the Expert System that the specified object is no longer part of *
- * this house's inventory. This occurs when the object is destroyed or captured. *
- * *
- * INPUT: techno -- Pointer to the object to remove from the tracking systems of this *
- * house. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Tracking_Remove(TechnoClass const * techno)
- {
- assert(Houses.ID(this) == ID);
- int type;
- switch (techno->What_Am_I()) {
- case RTTI_BUILDING:
- CurBuildings--;
- BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--;
- break;
- case RTTI_AIRCRAFT:
- CurAircraft--;
- AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--;
- break;
- case RTTI_INFANTRY:
- CurInfantry--;
- if (!((InfantryClass *)techno)->IsTechnician) {
- type = ((InfantryTypeClass const &)techno->Class_Of()).Type;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT;
- #endif
- IQuantity[type]--;
- }
- break;
- case RTTI_UNIT:
- CurUnits--;
- type = ((UnitTypeClass const &)techno->Class_Of()).Type;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT;
- #endif
- UQuantity[type]--;
- break;
- case RTTI_VESSEL:
- CurVessels--;
- type = ((VesselTypeClass const &)techno->Class_Of()).Type;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT;
- #endif
- VQuantity[type]--;
- break;
- default:
- break;
- }
- }
- /***********************************************************************************************
- * HouseClass::Tracking_Add -- Informs house of new inventory item. *
- * *
- * This function is called when the specified object is now available as part of the house's*
- * inventory. This occurs when the object is newly produced and also when it is captured *
- * by this house. *
- * *
- * INPUT: techno -- Pointer to the object that is now part of the house inventory. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/29/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Tracking_Add(TechnoClass const * techno)
- {
- assert(Houses.ID(this) == ID);
- StructType building;
- AircraftType aircraft;
- InfantryType infantry;
- UnitType unit;
- VesselType vessel;
- int quant;
- switch (techno->What_Am_I()) {
- case RTTI_BUILDING:
- CurBuildings++;
- building = ((BuildingTypeClass const &)techno->Class_Of()).Type;
- BQuantity[building]++;
- BScan |= (1L << building);
- if (Session.Type == GAME_INTERNET) {
- BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID);
- }
- break;
- case RTTI_AIRCRAFT:
- CurAircraft++;
- aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type;
- AQuantity[aircraft]++;
- AScan |= (1L << aircraft);
- if (Session.Type == GAME_INTERNET) {
- AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID);
- }
- break;
- case RTTI_INFANTRY:
- CurInfantry++;
- infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type;
- if (!((InfantryClass *)techno)->IsTechnician) {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- quant = infantry;
- if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT;
- IQuantity[quant]++;
- #else
- IQuantity[infantry]++;
- #endif
- if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) {
- InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID);
- }
- IScan |= (1L << infantry);
- }
- break;
- case RTTI_UNIT:
- CurUnits++;
- unit = ((UnitTypeClass const &)techno->Class_Of()).Type;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- quant = unit;
- if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT;
- UQuantity[quant]++;
- #else
- UQuantity[unit]++;
- #endif
- UScan |= (1L << unit);
- if (Session.Type == GAME_INTERNET) {
- UnitTotals->Increment_Unit_Total(techno->Class_Of().ID);
- }
- break;
- case RTTI_VESSEL:
- CurVessels++;
- vessel = ((VesselTypeClass const &)techno->Class_Of()).Type;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- quant = vessel;
- if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT;
- VQuantity[quant]++;
- #else
- VQuantity[vessel]++;
- #endif
- VScan |= (1L << vessel);
- if (Session.Type == GAME_INTERNET) {
- VesselTotals->Increment_Unit_Total(techno->Class_Of().ID);
- }
- break;
- default:
- break;
- }
- }
- /***********************************************************************************************
- * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. *
- * *
- * Use this routine to fetch a pointer to the variable that holds the number of factories *
- * that can produce the specified object type. This is a helper routine used when *
- * examining the number of factories as well as adjusting their number. *
- * *
- * INPUT: rtti -- The RTTI of the object that could be produced. *
- * *
- * OUTPUT: Returns with the number of factories owned by this house that could produce the *
- * object of the type specified. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/30/1996 JLB : Created. *
- *=============================================================================================*/
- int * HouseClass::Factory_Counter(RTTIType rtti)
- {
- switch (rtti) {
- case RTTI_UNITTYPE:
- case RTTI_UNIT:
- return(&UnitFactories);
- case RTTI_VESSELTYPE:
- case RTTI_VESSEL:
- return(&VesselFactories);
- case RTTI_AIRCRAFTTYPE:
- case RTTI_AIRCRAFT:
- return(&AircraftFactories);
- case RTTI_INFANTRYTYPE:
- case RTTI_INFANTRY:
- return(&InfantryFactories);
- case RTTI_BUILDINGTYPE:
- case RTTI_BUILDING:
- return(&BuildingFactories);
- default:
- break;
- }
- return(NULL);
- }
- /***********************************************************************************************
- * HouseClass::Active_Remove -- Remove this object from active duty for this house. *
- * *
- * This routine will recognize the specified object as having been removed from active *
- * duty. *
- * *
- * INPUT: techno -- Pointer to the object to remove from active duty. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/16/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Active_Remove(TechnoClass const * techno)
- {
- if (techno == NULL) return;
- if (techno->What_Am_I() == RTTI_BUILDING) {
- int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild);
- if (fptr != NULL) {
- *fptr = *fptr - 1;
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Active_Add -- Add an object to active duty for this house. *
- * *
- * This routine will recognize the specified object as having entered active duty. Any *
- * abilities granted to the house by that object are now available. *
- * *
- * INPUT: techno -- Pointer to the object that is entering active duty. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/16/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Active_Add(TechnoClass const * techno)
- {
- if (techno == NULL) return;
- if (techno->What_Am_I() == RTTI_BUILDING) {
- int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild);
- if (fptr != NULL) {
- *fptr = *fptr + 1;
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. *
- * *
- * This routine will determine what zone the specified coordinate lies in with respect to *
- * this house's base. A location that is too distant from the base, even though it might *
- * be a building, is not considered part of the base and returns ZONE_NONE. *
- * *
- * INPUT: coord -- The coordinate to examine. *
- * *
- * OUTPUT: Returns with the base zone that the specified coordinate lies in. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- *=============================================================================================*/
- ZoneType HouseClass::Which_Zone(COORDINATE coord) const
- {
- assert(Houses.ID(this) == ID);
- if (coord == 0) return(ZONE_NONE);
- int distance = Distance(Center, coord);
- if (distance <= Radius) return(ZONE_CORE);
- if (distance > Radius*4) return(ZONE_NONE);
- DirType facing = Direction(Center, coord);
- if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH);
- if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST);
- if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH);
- return(ZONE_WEST);
- }
- /***********************************************************************************************
- * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. *
- * *
- * Use this routine to determine what zone the specified object lies in. *
- * *
- * INPUT: object -- Pointer to the object that will be checked for zone occupation. *
- * *
- * OUTPUT: Returns with the base zone that the object lies in. For objects that are too *
- * distant from the center of the base, ZONE_NONE is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- *=============================================================================================*/
- ZoneType HouseClass::Which_Zone(ObjectClass const * object) const
- {
- assert(Houses.ID(this) == ID);
- if (!object) return(ZONE_NONE);
- return(Which_Zone(object->Center_Coord()));
- }
- /***********************************************************************************************
- * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. *
- * *
- * This routine is used to determine what base zone the specified cell is in. *
- * *
- * INPUT: cell -- The cell to examine. *
- * *
- * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far *
- * away. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- *=============================================================================================*/
- ZoneType HouseClass::Which_Zone(CELL cell) const
- {
- assert(Houses.ID(this) == ID);
- return(Which_Zone(Cell_Coord(cell)));
- }
- /***********************************************************************************************
- * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. *
- * *
- * This routine will go through all game objects and reset the existence bits for the *
- * owning house. This method ensures that if the object exists, then the corresponding *
- * existence bit is also set. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Recalc_Attributes(void)
- {
- /*
- ** Clear out all tracking values that will be filled in by this
- ** routine. This allows the filling in process to not worry about
- ** old existing values.
- */
- int index;
- for (index = 0; index < Houses.Count(); index++) {
- HouseClass * house = Houses.Ptr(index);
- if (house != NULL) {
- house->BScan = 0;
- house->ActiveBScan = 0;
- house->IScan = 0;
- house->ActiveIScan = 0;
- house->UScan = 0;
- house->ActiveUScan = 0;
- house->AScan = 0;
- house->ActiveAScan = 0;
- house->VScan = 0;
- house->ActiveVScan = 0;
- }
- }
- /*
- ** A second pass through the sentient objects is required so that the appropriate scan
- ** bits will be set for the owner house.
- */
- for (index = 0; index < Units.Count(); index++) {
- UnitClass const * unit = Units.Ptr(index);
- unit->House->UScan |= (1L << unit->Class->Type);
- if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) {
- if (!unit->IsInLimbo) {
- unit->House->ActiveUScan |= (1L << unit->Class->Type);
- }
- }
- }
- for (index = 0; index < Infantry.Count(); index++) {
- InfantryClass const * infantry = Infantry.Ptr(index);
- infantry->House->IScan |= (1L << infantry->Class->Type);
- if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) {
- if (!infantry->IsInLimbo) {
- infantry->House->ActiveIScan |= (1L << infantry->Class->Type);
- infantry->House->OldIScan |= (1L << infantry->Class->Type);
- }
- }
- }
- for (index = 0; index < Aircraft.Count(); index++) {
- AircraftClass const * aircraft = Aircraft.Ptr(index);
- aircraft->House->AScan |= (1L << aircraft->Class->Type);
- if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) {
- if (!aircraft->IsInLimbo) {
- aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type);
- aircraft->House->OldAScan |= (1L << aircraft->Class->Type);
- }
- }
- }
- for (index = 0; index < Buildings.Count(); index++) {
- BuildingClass const * building = Buildings.Ptr(index);
- if (building->Class->Type < 32) {
- building->House->BScan |= (1L << building->Class->Type);
- if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) {
- if (!building->IsInLimbo) {
- building->House->ActiveBScan |= (1L << building->Class->Type);
- building->House->OldBScan |= (1L << building->Class->Type);
- }
- }
- }
- }
- for (index = 0; index < Vessels.Count(); index++) {
- VesselClass const * vessel = Vessels.Ptr(index);
- vessel->House->VScan |= (1L << vessel->Class->Type);
- if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) {
- if (!vessel->IsInLimbo) {
- vessel->House->ActiveVScan |= (1L << vessel->Class->Type);
- vessel->House->OldVScan |= (1L << vessel->Class->Type);
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. *
- * *
- * This routine is used to find the cell that is closest to the center point of the *
- * zone specified. Typical use of this routine is for building and unit placement so that *
- * they can "cover" the specified zone. *
- * *
- * INPUT: zone -- The zone that the center point is to be returned. *
- * *
- * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- *=============================================================================================*/
- CELL HouseClass::Zone_Cell(ZoneType zone) const
- {
- assert(Houses.ID(this) == ID);
- switch (zone) {
- case ZONE_CORE:
- return(Coord_Cell(Center));
- case ZONE_NORTH:
- return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3)));
- case ZONE_EAST:
- return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3)));
- case ZONE_WEST:
- return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3)));
- case ZONE_SOUTH:
- return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3)));
- default:
- break;
- }
- return(0);
- }
- /***********************************************************************************************
- * HouseClass::Where_To_Go -- Determines where the object should go and wait. *
- * *
- * This function is called for every new unit produced or delivered in order to determine *
- * where the unit should "hang out" to await further orders. The best area for the *
- * unit to loiter is returned as a cell location. *
- * *
- * INPUT: object -- Pointer to the object that needs to know where to go. *
- * *
- * OUTPUT: Returns with the cell that the unit should move to. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/02/1995 JLB : Created. *
- * 11/04/1996 JLB : Simplified to use helper functions *
- *=============================================================================================*/
- CELL HouseClass::Where_To_Go(FootClass const * object) const
- {
- assert(Houses.ID(this) == ID);
- assert(object != NULL);
- ZoneType zone; // The zone that the object should go to.
- if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) {
- zone = ZONE_CORE;
- } else {
- zone = Random_Pick(ZONE_NORTH, ZONE_WEST);
- }
- CELL cell = Random_Cell_In_Zone(zone);
- assert(cell != 0);
- return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL));
- }
- /***********************************************************************************************
- * HouseClass::Find_Juicy_Target -- Finds a suitable field target. *
- * *
- * This routine is used to find targets out in the field and away from base defense. *
- * Typical of this would be the attack helicopters and the roving attack bands of *
- * hunter killers. *
- * *
- * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. *
- * *
- * OUTPUT: Returns with a suitable target to attack. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/12/1995 JLB : Created. *
- *=============================================================================================*/
- TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const
- {
- assert(Houses.ID(this) == ID);
- UnitClass * best = 0;
- int value = 0;
- for (int index = 0; index < Units.Count(); index++) {
- UnitClass * unit = Units.Ptr(index);
- if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) {
- int val = Distance(coord, unit->Center_Coord());
- if (unit->Anti_Air()) val *= 2;
- if (*unit == UNIT_HARVESTER) val /= 2;
- if (value == 0 || val < value) {
- value = val;
- best = unit;
- }
- }
- }
- if (best) {
- return(best->As_Target());
- }
- return(TARGET_NONE);
- }
- /***********************************************************************************************
- * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. *
- * *
- * Call this routine to fetch the total quantity of aircraft of the type specified that is *
- * owned by this house. *
- * *
- * INPUT: aircraft -- The aircraft type to check the quantity of. *
- * *
- * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this *
- * house. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::Get_Quantity(AircraftType aircraft)
- {
- return(AQuantity[aircraft]);
- }
- /***********************************************************************************************
- * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. *
- * *
- * This is the counterpart to the Set_Factory function. It will return with a factory *
- * pointer that is associated with the object type specified. *
- * *
- * INPUT: rtti -- The RTTI of the object type to find the factory for. *
- * *
- * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the *
- * object type specified. *
- * *
- * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy *
- * producing another unit of that category. *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const
- {
- int factory_index = -1;
- switch (rtti) {
- case RTTI_INFANTRY:
- case RTTI_INFANTRYTYPE:
- factory_index = InfantryFactory;
- break;
- case RTTI_UNIT:
- case RTTI_UNITTYPE:
- factory_index = UnitFactory;
- break;
- case RTTI_BUILDING:
- case RTTI_BUILDINGTYPE:
- factory_index = BuildingFactory;
- break;
- case RTTI_AIRCRAFT:
- case RTTI_AIRCRAFTTYPE:
- factory_index = AircraftFactory;
- break;
- case RTTI_VESSEL:
- case RTTI_VESSELTYPE:
- factory_index = VesselFactory;
- break;
- default:
- factory_index = -1;
- break;
- }
- /*
- ** Fetch the actual pointer to the factory object. If there is
- ** no object factory that matches the specified rtti type, then
- ** null is returned.
- */
- if (factory_index != -1) {
- return(Factories.Raw_Ptr(factory_index));
- }
- return(NULL);
- }
- /***********************************************************************************************
- * HouseClass::Set_Factory -- Assign specified factory to house tracking. *
- * *
- * Call this routine when a factory has been created and it now must be passed on to the *
- * house for tracking purposes. The house maintains several factory pointers and this *
- * routine will ensure that the factory pointer gets stored correctly. *
- * *
- * INPUT: rtti -- The RTTI of the object the factory it to manufacture. *
- * *
- * factory -- The factory object pointer. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory)
- {
- int * factory_index = 0;
- assert(rtti != RTTI_NONE);
- switch (rtti) {
- case RTTI_UNIT:
- case RTTI_UNITTYPE:
- factory_index = &UnitFactory;
- break;
- case RTTI_INFANTRY:
- case RTTI_INFANTRYTYPE:
- factory_index = &InfantryFactory;
- break;
- case RTTI_VESSEL:
- case RTTI_VESSELTYPE:
- factory_index = &VesselFactory;
- break;
- case RTTI_BUILDING:
- case RTTI_BUILDINGTYPE:
- factory_index = &BuildingFactory;
- break;
- case RTTI_AIRCRAFT:
- case RTTI_AIRCRAFTTYPE:
- factory_index = &AircraftFactory;
- break;
- }
- assert(factory_index != NULL);
- /*
- ** Assign the factory to the appropriate slot. For the case of clearing
- ** the factory out, then -1 is assigned.
- */
- if (factory != NULL) {
- *factory_index = factory->ID;
- } else {
- *factory_index = -1;
- }
- }
- /***********************************************************************************************
- * HouseClass::Factory_Count -- Fetches the number of factories for specified type. *
- * *
- * This routine will count the number of factories owned by this house that can build *
- * objects of the specified type. *
- * *
- * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. *
- * *
- * OUTPUT: Returns with the number of factories that can build the object type specified. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/30/1996 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::Factory_Count(RTTIType rtti) const
- {
- int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti);
- if (ptr != NULL) {
- return(*ptr);
- }
- return(0);
- }
- /***********************************************************************************************
- * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. *
- * *
- * This will return the total number of buildings of that type owned by this house. *
- * *
- * INPUT: building -- The building type to check. *
- * *
- * OUTPUT: Returns with the number of buildings of that type owned by this house. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- int HouseClass::Get_Quantity(StructType building)
- {
- return(BQuantity[building]);
- }
- /***********************************************************************************************
- * HouseClass::Read_INI -- Reads house specific data from INI. *
- * *
- * This routine reads the house specific data for a particular *
- * scenario from the scenario INI file. Typical data includes starting *
- * credits, maximum unit count, etc. *
- * *
- * INPUT: buffer -- Pointer to loaded scenario INI file. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 05/24/1994 JLB : Created. *
- * 05/18/1995 JLB : Creates all houses. *
- *=============================================================================================*/
- void HouseClass::Read_INI(CCINIClass & ini)
- {
- HouseClass * p; // Pointer to current player data.
- char const * hname; // Pointer to house name.
- for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) {
- hname = HouseTypeClass::As_Reference(index).IniName;
- p = new HouseClass(index);
- p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario);
- p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding);
- p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit);
- p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry);
- p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel);
- if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit;
- p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100;
- p->Credits = p->Control.InitialCredits;
- int iq = ini.Get_Int(hname, "IQ", 0);
- if (iq > Rule.MaxIQ) iq = 1;
- p->IQ = p->Control.IQ = iq;
- p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH);
- p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false);
- int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL));
- p->Make_Ally(index);
- p->Make_Ally(HOUSE_NEUTRAL);
- for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
- if ((owners & (1 << h)) != 0) {
- p->Make_Ally(h);
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Write_INI -- Writes the house data to the INI database. *
- * *
- * This routine will write out all data necessary to recreate it in anticipation of a *
- * new scenario. All houses (that are active) will have their scenario type data written *
- * out. *
- * *
- * INPUT: ini -- Reference to the INI database to write the data to. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 07/09/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Write_INI(CCINIClass & ini)
- {
- /*
- ** The identity house control object. Only if the house value differs from the
- ** identity, will the data be written out.
- */
- HouseStaticClass control;
- for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) {
- HouseClass * p = As_Pointer(i);
- if (p != NULL) {
- char const * name = p->Class->IniName;
- ini.Clear(name);
- if (i >= HOUSE_MULTI1) continue;
- if (p->Control.InitialCredits != control.InitialCredits) {
- ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100));
- }
- if (p->Control.Edge != control.Edge) {
- ini.Put_SourceType(name, "Edge", p->Control.Edge);
- }
- if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) {
- ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit);
- }
- if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) {
- ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry);
- }
- if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) {
- ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding);
- }
- if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) {
- ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel);
- }
- if (p->Control.TechLevel != control.TechLevel) {
- ini.Put_Int(name, "TechLevel", p->Control.TechLevel);
- }
- if (p->Control.IQ != control.IQ) {
- ini.Put_Int(name, "IQ", p->Control.IQ);
- }
- if (p->IsPlayerControl != false && p != PlayerPtr) {
- ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl);
- }
- ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL)));
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. *
- * *
- * This routine will examine the current yak and mig situation verses airfields. If there *
- * are equal aircraft to airfields, then this routine will return TRUE. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_No_YakMig(void) const
- {
- int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG];
- /*
- ** Adjust the quantity down one if there is an aircraft in production. This will
- ** allow production to resume after being held.
- */
- FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT);
- if (factory != NULL && factory->Get_Object() != NULL) {
- AircraftClass const * air = (AircraftClass const *)factory->Get_Object();
- if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) {
- quantity -= 1;
- }
- }
- if (quantity >= BQuantity[STRUCT_AIRSTRIP]) {
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? *
- * *
- * This is a special hack check routine to see if the object type and id specified is *
- * prevented from being produced. The Yak and the Mig are so prevented if there would be *
- * insufficient airfields for them to land upon. *
- * *
- * INPUT: rtti -- The RTTI type of the value specified. *
- * *
- * value -- The type number (according to the RTTI type specified). *
- * *
- * OUTPUT: bool; Is production of this object prohibited? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const
- {
- if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) {
- return(Is_No_YakMig());
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Fire_Sale -- Cause all buildings to be sold. *
- * *
- * This routine will sell back all buildings owned by this house. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: bool; Was a fire sale performed? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Fire_Sale(void)
- {
- if (CurBuildings > 0) {
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass * b = Buildings.Ptr(index);
- if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) {
- b->Sell_Back(1);
- }
- }
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * HouseClass::Do_All_To_Hunt -- Send all units to hunt. *
- * *
- * This routine will cause all combatants of this house to go into hunt mode. The effect of *
- * this is to throw everything this house has to muster at the enemies of this house. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- * 10/02/1996 JLB : Handles aircraft too. *
- *=============================================================================================*/
- void HouseClass::Do_All_To_Hunt(void) const
- {
- int index;
- for (index = 0; index < Units.Count(); index++) {
- UnitClass * unit = Units.Ptr(index);
- if (unit->House == this && unit->IsDown && !unit->IsInLimbo) {
- if (unit->Team) unit->Team->Remove(unit);
- unit->Assign_Mission(MISSION_HUNT);
- }
- }
- for (index = 0; index < Infantry.Count(); index++) {
- InfantryClass * infantry = Infantry.Ptr(index);
- if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) {
- if (infantry->Team) infantry->Team->Remove(infantry);
- infantry->Assign_Mission(MISSION_HUNT);
- }
- }
- for (index = 0; index < Vessels.Count(); index++) {
- VesselClass * vessel = Vessels.Ptr(index);
- if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) {
- if (vessel->Team) vessel->Team->Remove(vessel);
- vessel->Assign_Mission(MISSION_HUNT);
- }
- }
- for (index = 0; index < Aircraft.Count(); index++) {
- AircraftClass * aircraft = Aircraft.Ptr(index);
- if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) {
- if (aircraft->Team) aircraft->Team->Remove(aircraft);
- aircraft->Assign_Mission(MISSION_HUNT);
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. *
- * *
- * Use this routine to determine if this house is legally allowed to ally with the *
- * house specified. There are many reason why an alliance is not allowed. Typically, this *
- * is when there would be no more opponents left to fight or if this house has been *
- * defeated. *
- * *
- * INPUT: house -- The house that alliance with is desired. *
- * *
- * OUTPUT: bool; Is alliance with the house specified prohibited? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- *=============================================================================================*/
- bool HouseClass::Is_Allowed_To_Ally(HousesType house) const
- {
- /*
- ** Is not allowed to ally with a house that is patently invalid, such
- ** as one that is illegally defined.
- */
- if (house == HOUSE_NONE) {
- return(false);
- }
- /*
- ** One cannot ally twice with the same house.
- */
- if (Is_Ally(house)) {
- return(false);
- }
- /*
- ** If the scenario is being set up, then alliances are always
- ** allowed. No further checking is required.
- */
- if (ScenarioInit) {
- return(true);
- }
- /*
- ** Alliances (outside of scneario init time) are allowed only if
- ** this is a multiplayer game. Otherwise, they are prohibited.
- */
- if (Session.Type == GAME_NORMAL) {
- return(false);
- }
- /*
- ** When the house is defeated, it can no longer make alliances.
- */
- if (IsDefeated) {
- return(false);
- }
- #ifdef FIXIT_VERSION_3
- // Fix to prevent ally with computer.
- if ( !HouseClass::As_Pointer(house)->IsHuman) {
- return(false);
- }
- #else // FIXIT_VERSION_3
- #ifdef FIXIT_NO_COMP_ALLY
- // Fix to prevent ally with computer.
- if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) {
- return(false);
- }
- #endif
- #endif // FIXIT_VERSION_3
- /*
- ** Count the number of active houses in the game as well as the
- ** number of existing allies with this house.
- */
- int housecount = 0;
- int allycount = 0;
- for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) {
- HouseClass * hptr = HouseClass::As_Pointer(house2);
- if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) {
- housecount++;
- if (Is_Ally(hptr)) {
- allycount++;
- }
- }
- }
- /*
- ** Alliance is not allowed if there wouldn't be any enemies left to
- ** fight.
- */
- if (housecount == allycount+1) {
- return(false);
- }
- return(true);
- }
- /***********************************************************************************************
- * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. *
- * *
- * This routine will cause the computer players to become suspicious of the human *
- * players and thus the computer players will band together in order to defeat the *
- * human players. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/23/1996 JLB : Created. *
- *=============================================================================================*/
- void HouseClass::Computer_Paranoid(void)
- {
- if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019
-
- /*
- ** Loop through every computer controlled house and make allies with all other computer
- ** controlled houses and then make enemies with all other human controlled houses.
- */
- for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
- HouseClass * hptr = HouseClass::As_Pointer(house);
- if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) {
- hptr->IsParanoid = true;
- /*
- ** Break alliance with every human it is allied with and make friends with
- ** any other computer players.
- */
- for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) {
- HouseClass * hptr2 = HouseClass::As_Pointer(house2);
- if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) {
- if (hptr2->IsHuman) {
- hptr->Make_Enemy(house2);
- } else {
- hptr->Make_Ally(house2);
- }
- }
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Adjust_Power -- Adjust the power value of the house. *
- * *
- * This routine will update the power output value of the house. It will cause any buildgins*
- * that need to be redrawn to do so. *
- * *
- * INPUT: adjust -- The amount to adjust the power output value. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/01/1996 BWG : Created. *
- *=============================================================================================*/
- void HouseClass::Adjust_Power(int adjust)
- {
- Power += adjust;
- Update_Spied_Power_Plants();
- }
- /***********************************************************************************************
- * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. *
- * *
- * This routine will update the drain value of the house. It will cause any buildings that *
- * need to be redraw to do so. *
- * *
- * INPUT: adjust -- The amount to adjust the drain (positive means more drain). *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/01/1996 BWG : Created. *
- *=============================================================================================*/
- void HouseClass::Adjust_Drain(int adjust)
- {
- Drain += adjust;
- Update_Spied_Power_Plants();
- }
- /***********************************************************************************************
- * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/11/1996 BWG : Created. *
- *=============================================================================================*/
- void HouseClass::Update_Spied_Power_Plants(void)
- {
- int count = CurrentObject.Count();
- if (count) {
- for (int index = 0; index < count; index++) {
- ObjectClass const * tech = CurrentObject[index];
- if (tech && tech->What_Am_I()==RTTI_BUILDING) {
- BuildingClass *bldg = (BuildingClass *)tech;
- if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) {
- if ( bldg->Spied_By() & (1<<(PlayerPtr->Class->House)) ) {
- bldg->Mark(MARK_CHANGE);
- }
- }
- }
- }
- }
- }
- /***********************************************************************************************
- * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. *
- * *
- * Use this routine to determine where the specified object should go if it were to go *
- * some random (but legal) location within the zone specified. *
- * *
- * INPUT: techno -- The object that is desirous of going into the zone specified. *
- * *
- * zone -- The zone to find a location within. *
- * *
- * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If *
- * no valid location could be found, then 0 is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/01/1996 JLB : Created. *
- * 11/04/1996 JLB : Not so strict on zone requirement. *
- *=============================================================================================*/
- CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const
- {
- if (techno == NULL) return(0);
- int bestval = -1;
- int bestcell = 0;
- TechnoTypeClass const * ttype = techno->Techno_Type_Class();
- /*
- ** Pick a random location within the zone specified.
- */
- CELL trycell = Random_Cell_In_Zone(zone);
- short const * list = NULL;
- if (techno->What_Am_I() == RTTI_BUILDING) {
- list = techno->Occupy_List(true);
- }
- /*
- ** Find a legal placement position as close as possible to the picked location while still
- ** remaining within the zone.
- */
- for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
- // if (Map.In_Radar(cell)) {
- if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) {
- bool ok = ttype->Legal_Placement(cell);
- /*
- ** Another (adjacency) check is required for buildings.
- */
- if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) {
- ok = false;
- }
- if (ok) {
- int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell));
- if (bestval == -1 || dist < bestval) {
- bestval = dist;
- bestcell = cell;
- }
- }
- }
- }
- /*
- ** Return the best location to move to.
- */
- return(bestcell);
- }
- /***********************************************************************************************
- * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. *
- * *
- * This routine will pick a random cell within the zone specified. The pick will be *
- * clipped to the map edge when necessary. *
- * *
- * INPUT: zone -- The zone to pick a cell from. *
- * *
- * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the *
- * map, then a cell in the core zone is returned instead. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 11/04/1996 JLB : Created. *
- *=============================================================================================*/
- CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const
- {
- COORDINATE coord = 0;
- int maxdist = 0;
- switch (zone) {
- case ZONE_CORE:
- coord = Coord_Scatter(Center, Random_Pick(0, Radius), true);
- break;
- case ZONE_NORTH:
- maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H);
- if (maxdist < 0) break;
- coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
- break;
- case ZONE_EAST:
- maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W);
- if (maxdist < 0) break;
- coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
- break;
- case ZONE_SOUTH:
- maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H);
- if (maxdist < 0) break;
- coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
- break;
- case ZONE_WEST:
- maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W);
- if (maxdist < 0) break;
- coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist)));
- break;
- }
- /*
- ** Double check that the location is valid and if so, convert it into a cell
- ** number.
- */
- CELL cell;
- if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) {
- if (zone == ZONE_CORE) {
- /*
- ** Finding a cell within the core failed, so just pick the center
- ** cell. This cell is guaranteed to be valid.
- */
- cell = Coord_Cell(Center);
- } else {
- /*
- ** If the edge fails, then try to find a cell within the core.
- */
- cell = Random_Cell_In_Zone(ZONE_CORE);
- }
- } else {
- cell = Coord_Cell(coord);
- }
- /*
- ** If the randomly picked location is not in the legal map area, then clip it to
- ** the legal map area.
- */
- if (!Map.In_Radar(cell)) {
- int x = Cell_X(cell);
- int y = Cell_Y(cell);
- if (x < Map.MapCellX) x = Map.MapCellX;
- if (y < Map.MapCellY) y = Map.MapCellY;
- if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1;
- if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1;
- cell = XY_Cell(x, y);
- }
- return(cell);
- }
- /***********************************************************************************************
- * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: Returns the bit field storing which houses this house is allied with. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/12/2019 JAS : Created. *
- *=============================================================================================*/
- unsigned HouseClass::Get_Ally_Flags()
- {
- return Allies;
- }
- /***********************************************************************************************
- * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 1/31/2020 3:34PM ST : Created. *
- *=============================================================================================*/
- void HouseClass::Check_Pertinent_Structures(void)
- {
- /*
- ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM
- **
- ** Game is over when no pertinent structures remain
- */
- if (!Special.IsEarlyWin) {
- return;
- }
-
- if (IsToDie || IsToWin || IsToLose) {
- return;
- }
- // MBL 07.15.2020 - Prevention of recent issue with constant "player defeated logic" and message to client spamming
- // Per https://jaas.ea.com/browse/TDRA-7433
- //
- if (IsDefeated) {
- return;
- }
- bool any_good_buildings = false;
-
- for (int index = 0; index < Buildings.Count(); index++) {
- BuildingClass *b = Buildings.Ptr(index);
- if (b && b->IsActive && b->House == this) {
- if (!b->Class->IsWall && *b != STRUCT_APMINE && *b != STRUCT_AVMINE) {
- if (!Special.ModernBalance || (*b != STRUCT_SHIP_YARD && *b != STRUCT_FAKE_YARD && *b != STRUCT_SUB_PEN && *b != STRUCT_FAKE_PEN)) {
- if (!b->IsInLimbo && b->Strength > 0) {
- any_good_buildings = true;
- break;
- }
- }
- }
- }
- }
-
- if (!any_good_buildings) {
- for (int index = 0; index < Units.Count(); index++) {
- UnitClass * unit = Units.Ptr(index);
- if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) {
- if (!unit->IsInLimbo && unit->Strength > 0) {
- any_good_buildings = true;
- break;
- }
- }
- }
- }
- if (!any_good_buildings) {
- Flag_To_Die();
- }
- }
- /***********************************************************************************************
- * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 4/23/2020 11:06PM ST : Created. *
- *=============================================================================================*/
- void HouseClass::Init_Unit_Trackers(void)
- {
- if (Session.Type == GAME_INTERNET || Session.Type == GAME_GLYPHX_MULTIPLAYER) {
- AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT);
- InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT);
- UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT);
- BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT);
- VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT);
- DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT);
- DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT);
- DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT);
- DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT);
- DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT);
- CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT);
- TotalCrates = new UnitTrackerClass ( CRATE_COUNT );
- } else {
-
- AircraftTotals = NULL;
- InfantryTotals = NULL;
- UnitTotals = NULL;
- BuildingTotals = NULL;
- VesselTotals = NULL;
- DestroyedAircraft = NULL;
- DestroyedInfantry = NULL;
- DestroyedUnits = NULL;
- DestroyedBuildings = NULL;
- DestroyedVessels = NULL;
- CapturedBuildings = NULL;
- TotalCrates = NULL;
- }
- }
- /***********************************************************************************************
- * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 4/23/2020 11:06PM ST : Created. *
- *=============================================================================================*/
- void HouseClass::Free_Unit_Trackers(void)
- {
- if (AircraftTotals) {
- delete AircraftTotals;
- AircraftTotals = NULL;
- }
- if (InfantryTotals) {
- delete InfantryTotals;
- InfantryTotals = NULL;
- }
- if (UnitTotals) {
- delete UnitTotals;
- UnitTotals = NULL;
- }
- if (BuildingTotals) {
- delete BuildingTotals;
- BuildingTotals = NULL;
- }
- if (VesselTotals) {
- delete VesselTotals;
- VesselTotals = NULL;
- }
- if (DestroyedAircraft) {
- delete DestroyedAircraft;
- DestroyedAircraft = NULL;
- }
- if (DestroyedInfantry) {
- delete DestroyedInfantry;
- DestroyedInfantry = NULL;
- }
- if (DestroyedUnits) {
- delete DestroyedUnits;
- DestroyedUnits = NULL;
- }
- if (DestroyedBuildings) {
- delete DestroyedBuildings;
- DestroyedBuildings = NULL;
- }
- if (DestroyedVessels) {
- delete DestroyedVessels;
- DestroyedVessels = NULL;
- }
- if (CapturedBuildings) {
- delete CapturedBuildings;
- CapturedBuildings = NULL;
- }
- if (TotalCrates) {
- delete TotalCrates;
- TotalCrates = NULL;
- }
- }
|