HOUSE.CPP 319 KB

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