TECHNO.CPP 286 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349
  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/TECHNO.CPP 5 3/17/97 1:28a 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 : BASE.CPP *
  26. * *
  27. * Programmer : Joe L. Bostic *
  28. * *
  29. * Start Date : May 8, 1994 *
  30. * *
  31. * Last Update : November 1, 1996 [JLB] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * TechnoClass::AI -- Handles AI processing for techno object. *
  36. * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. *
  37. * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. *
  38. * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. *
  39. * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. *
  40. * TechnoClass::Assign_Destination -- Assigns movement destination to the object. *
  41. * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. *
  42. * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. *
  43. * TechnoClass::Can_Fire -- Determines if this techno object can fire. *
  44. * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order*
  45. * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. *
  46. * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. *
  47. * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. *
  48. * TechnoClass::Captured -- Handles capturing this object. *
  49. * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. *
  50. * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. *
  51. * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon.*
  52. * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. *
  53. * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. *
  54. * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. *
  55. * TechnoClass::Detach -- Handles removal of target from tracking system. *
  56. * TechnoClass::Do_Cloak -- Start the object into cloaking stage. *
  57. * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. *
  58. * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. *
  59. * TechnoClass::Draw_It -- Draws the health bar (if necessary). *
  60. * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. *
  61. * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. *
  62. * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. *
  63. * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. *
  64. * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. *
  65. * TechnoClass::Evaluate_Object -- Determines score value of specified object. *
  66. * TechnoClass::Exit_Object -- Causes specified object to leave this object. *
  67. * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. *
  68. * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. *
  69. * TechnoClass::Fire_At -- Fires projectile at target specified. *
  70. * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. *
  71. * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. *
  72. * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. *
  73. * TechnoClass::Greatest_Threat -- Determines best target given search criteria. *
  74. * TechnoClass::Hidden -- Returns the object back into the hidden state. *
  75. * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. *
  76. * TechnoClass::In_Range -- Determines if specified target is within weapon range. *
  77. * TechnoClass::In_Range -- Determines if specified target is within weapon range. *
  78. * TechnoClass::In_Range -- Determines if the specified coordinate is within range. *
  79. * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? *
  80. * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. *
  81. * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. *
  82. * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. *
  83. * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. *
  84. * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate.*
  85. * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? *
  86. * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. *
  87. * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. *
  88. * TechnoClass::Look -- Performs a look around (map reveal) action. *
  89. * TechnoClass::Mark -- Handles marking of techno objects. *
  90. * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. *
  91. * TechnoClass::Owner -- Who is the owner of this object? *
  92. * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.*
  93. * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. *
  94. * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. *
  95. * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. *
  96. * TechnoClass::Receive_Message -- Handles inbound message as appropriate. *
  97. * TechnoClass::Record_The_Kill -- Records the death of this object. *
  98. * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. *
  99. * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. *
  100. * TechnoClass::Renovate -- Heal a building to maximum *
  101. * TechnoClass::Response_Attack -- Handles the voice response when given attack order. *
  102. * TechnoClass::Response_Move -- Handles the voice response to a movement request. *
  103. * TechnoClass::Response_Select -- Handles the voice response when selected. *
  104. * TechnoClass::Revealed -- Handles revealing an object to the house specified. *
  105. * TechnoClass::Risk -- Fetches the risk associated with this object. *
  106. * TechnoClass::Select -- Selects object and checks to see if can be selected. *
  107. * TechnoClass::Set_Mission -- Forced mission set (used by editor). *
  108. * TechnoClass::Stun -- Prepares the object for removal from the game. *
  109. * TechnoClass::Take_Damage -- Records damage assessed to this object. *
  110. * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. *
  111. * TechnoClass::TechnoClass -- Constructor for techno type objects. *
  112. * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. *
  113. * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. *
  114. * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. *
  115. * TechnoClass::Time_To_Build -- Determines the time it would take to build this. *
  116. * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. *
  117. * TechnoClass::Value -- Fetches the target value for this object. *
  118. * TechnoClass::Visual_Character -- Determine the visual character of the object. *
  119. * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. *
  120. * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. *
  121. * TechnoClass::What_Action -- Determines what action to perform if object is selected. *
  122. * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. *
  123. * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. *
  124. * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. *
  125. * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. *
  126. * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. *
  127. * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. *
  128. * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. *
  129. * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. *
  130. * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. *
  131. * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. *
  132. * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. *
  133. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  134. #include "function.h"
  135. /***************************************************************************
  136. ** Cloaking control values.
  137. */
  138. #define MAX_UNCLOAK_STAGE 38
  139. /***************************************************************************
  140. ** These are the pointers to the special shape data that the units may need.
  141. */
  142. void const * TechnoTypeClass::WakeShapes = 0;
  143. void const * TechnoTypeClass::TurretShapes = 0;
  144. void const * TechnoTypeClass::SamShapes = 0;
  145. void const * TechnoTypeClass::MGunShapes = 0;
  146. //Xlat Tables for French and German
  147. // For name overriding
  148. //#define NEWNAMES 22
  149. #ifdef GERMAN
  150. const char* NewName[] = {
  151. "Scout Ant", "Kundschafter-Ameise",
  152. "Warrior Ant", "Krieger-Ameise",
  153. "Fire Ant", "Feuer-Ameise",
  154. "Queen Ant", "Ameisenk”nigin",
  155. "ATS", "Angriffs-U-Boot",
  156. "Tesla Tank", "Telsapanzer",
  157. "Volkov", "Modell Volkov",
  158. "Chitzkoi", "Roboterhund",
  159. "Stavros", "Stavros",
  160. "F-A Longbow", "Jagdhubschrauber Longbow",
  161. "Civilian Specialist", "Wissenschaftler",
  162. "Alloy Facility", "Legierungswerk",
  163. NULL,
  164. };
  165. #endif
  166. #ifdef FRENCH
  167. const char* NewName[] = {
  168. "Scout Ant", "Fourmi de Reconnaissance",
  169. "Warrior Ant", "Fourmi GuerriŠre",
  170. "Fire Ant", "Fourmi Lance-Flammes",
  171. "Queen Ant", "Reine des Fourmis",
  172. "ATS", "SMTA",
  173. "Tesla Tank", "Tank Tesla",
  174. "Volkov", "Volkov",
  175. "Chitzkoi", "Cyber-Chien",
  176. "Stavros", "Stavros",
  177. "F-A Longbow", "HAA (H‚licoptŠre d'Assaut Avanc‚)",
  178. "Civilian Specialist", "Sp‚cialiste Civil",
  179. "Alloy Facility", "Usine M‚tallurgique",
  180. NULL,
  181. };
  182. #endif
  183. /***************************************************************************
  184. ** Which shape to use depending on which facing is controlled by these arrays.
  185. */
  186. int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
  187. /***********************************************************************************************
  188. * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. *
  189. * *
  190. * The player's army is considered to be all those mobile units that can be selected *
  191. * and controlled by the player (they may or may not have weapons in the traditional *
  192. * sense). *
  193. * *
  194. * INPUT: none *
  195. * *
  196. * OUTPUT: bool; Is this object part of the player's selectable controllable army? *
  197. * *
  198. * WARNINGS: none *
  199. * *
  200. * HISTORY: *
  201. * 09/23/1996 JLB : Created. *
  202. *=============================================================================================*/
  203. bool TechnoClass::Is_Players_Army(void) const
  204. {
  205. /*
  206. ** An object that is dead (or about to be) is not considered part of
  207. ** the player's army.
  208. */
  209. if (Strength <= 0) {
  210. return(false);
  211. }
  212. /*
  213. ** If the object is not yet on the map or is otherwise indisposed, then
  214. ** don't consider it.
  215. */
  216. if (IsInLimbo || !IsLocked) {
  217. return(false);
  218. }
  219. /*
  220. ** Buildings, although sometimes serving a combat purpose, are not part
  221. ** of the player's army.
  222. */
  223. if (What_Am_I() == RTTI_BUILDING) {
  224. return(false);
  225. }
  226. /*
  227. ** If not discoverd by the player, then don't consider it part of the
  228. ** player's army (yet).
  229. */
  230. if (!IsDiscoveredByPlayer) {
  231. return(false);
  232. }
  233. /*
  234. ** If not selectable, then not really part of the player's active army.
  235. */
  236. if (!Techno_Type_Class()->IsSelectable) {
  237. return(false);
  238. }
  239. /*
  240. ** If not under player control, then it isn't part of the player's army.
  241. */
  242. if (!House->IsPlayerControl) {
  243. return(false);
  244. }
  245. return(true);
  246. }
  247. /***********************************************************************************************
  248. * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. *
  249. * *
  250. * Use this routine to examine the cell specified and if the cell could be a valid *
  251. * destination for a teleport, then return true. *
  252. * *
  253. * INPUT: cell -- The cell to examine as a possible legal teleport destination. *
  254. * *
  255. * OUTPUT: bool; Is the specified cell a legal teleport destination? *
  256. * *
  257. * WARNINGS: none *
  258. * *
  259. * HISTORY: *
  260. * 10/08/1996 JLB : Created. *
  261. *=============================================================================================*/
  262. bool TechnoClass::Can_Teleport_Here(CELL cell) const
  263. {
  264. /*
  265. ** Infantry are never allowed to teleport.
  266. */
  267. if (What_Am_I() == RTTI_INFANTRY) {
  268. return(false);
  269. }
  270. /*
  271. ** The destination cell must be on the playfield.
  272. */
  273. if (!Map.In_Radar(cell)) {
  274. return(false);
  275. }
  276. /*
  277. ** Teleporting directly onto the flag spot is not allowed. This is due to the
  278. ** requirement that entering that location must proceed under normal channels.
  279. */
  280. if (Map[cell].Overlay == OVERLAY_FLAG_SPOT) {
  281. return(false);
  282. }
  283. /*
  284. ** Determine if the object could enter the cell by normal means. If the
  285. ** cell is impassable, then it can't be teleported there.
  286. */
  287. TechnoTypeClass const * ttype = Techno_Type_Class();
  288. if (!Map[cell].Is_Clear_To_Move(ttype->Speed, true, true, -1, ttype->Speed == SPEED_FLOAT ? MZONE_WATER : MZONE_NORMAL)) {
  289. return(false);
  290. }
  291. return(true);
  292. }
  293. /***********************************************************************************************
  294. * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. *
  295. * *
  296. * This routine will compare the weapons this object is equipped with verses the *
  297. * candidate target object. The best weapon to use against the target will be returned. *
  298. * Special emphasis is given to weapons that can fire on the target without requiring *
  299. * this object to move within range. *
  300. * *
  301. * INPUT: target -- The candidate target to determine which weapon is best against. *
  302. * *
  303. * OUTPUT: Returns with an identifier the specifies what weapon to use against the target. *
  304. * The return value will be "0" for the primary weapon and "1" for the secondary. *
  305. * *
  306. * WARNINGS: This routine is called very frequently. It should be as efficient as possible. *
  307. * *
  308. * HISTORY: *
  309. * 08/12/1996 JLB : Created. *
  310. *=============================================================================================*/
  311. int TechnoClass::What_Weapon_Should_I_Use(TARGET target) const
  312. {
  313. if (!Target_Legal(target)) return(0);
  314. /*
  315. ** Fetch the armor of the candidate target object. Presume that if the target
  316. ** is not an object, then its armor is equivalent to wood. Who knows why?
  317. */
  318. ArmorType armor = ARMOR_WOOD;
  319. if (Is_Target_Object(target)) {
  320. armor = As_Object(target)->Class_Of().Armor;
  321. }
  322. TechnoTypeClass const * ttype = Techno_Type_Class();
  323. /*
  324. ** Get the value of the primary weapon verses the candidate target. Increase the
  325. ** value of the weapon if it happens to be in range.
  326. */
  327. int w1 = 0;
  328. WeaponTypeClass const * wptr = ttype->PrimaryWeapon;
  329. if (wptr != NULL && wptr->WarheadPtr != NULL) w1 = wptr->WarheadPtr->Modifier[armor] * 1000;
  330. if (In_Range(target, 0)) w1 *= 2;
  331. FireErrorType ok = Can_Fire(target, 0);
  332. if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w1 = 0;
  333. /*
  334. ** Calculate a similar value for the secondary weapon.
  335. */
  336. int w2 = 0;
  337. wptr = ttype->SecondaryWeapon;
  338. if (wptr != NULL && wptr->WarheadPtr != NULL) w2 = wptr->WarheadPtr->Modifier[armor] * 1000;
  339. if (In_Range(target, 1)) w2 *= 2;
  340. ok = Can_Fire(target, 1);
  341. if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w2 = 0;
  342. /*
  343. ** Return with the weapon identifier that should be used to fire upon the
  344. ** candidate target.
  345. */
  346. if (w2 > w1) return(1);
  347. return(0);
  348. }
  349. /***********************************************************************************************
  350. * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. *
  351. * *
  352. * This routine will determine the number of survivors that should run from this object *
  353. * when it is destroyed. *
  354. * *
  355. * INPUT: none *
  356. * *
  357. * OUTPUT: Returns with the maximum number of survivors that should run from this object *
  358. * when the object gets destroyed. *
  359. * *
  360. * WARNINGS: none *
  361. * *
  362. * HISTORY: *
  363. * 08/04/1996 JLB : Created. *
  364. *=============================================================================================*/
  365. int TechnoClass::How_Many_Survivors(void) const
  366. {
  367. if (Techno_Type_Class()->IsCrew) {
  368. return(1);
  369. }
  370. return(0);
  371. }
  372. /***********************************************************************************************
  373. * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon. *
  374. * *
  375. * This routine will examine the specified weapon of this object and determine how much *
  376. * damage it could inflict -- best case. *
  377. * *
  378. * INPUT: which -- Which weapon to consider. If -1 is specified, then the average of both *
  379. * weapon types (if present) is returned. *
  380. * *
  381. * OUTPUT: Returns with the combat damage that could be inflicted by the specified weapon. *
  382. * *
  383. * WARNINGS: This routine could return a negative number if a medic is scanned. *
  384. * *
  385. * HISTORY: *
  386. * 09/16/1996 JLB : Created. *
  387. *=============================================================================================*/
  388. int TechnoClass::Combat_Damage(int which) const
  389. {
  390. TechnoTypeClass const * ttype = Techno_Type_Class();
  391. int divisor = 0;
  392. int value = 0;
  393. if (which == 0 || which == -1) {
  394. if (ttype->PrimaryWeapon != NULL) {
  395. value += ttype->PrimaryWeapon->Attack;
  396. divisor += 1;
  397. }
  398. }
  399. if (which == 1 || which == -1) {
  400. if (ttype->SecondaryWeapon != NULL) {
  401. value += ttype->SecondaryWeapon->Attack;
  402. divisor += 1;
  403. }
  404. }
  405. if (divisor > 1) {
  406. return(value / divisor);
  407. }
  408. return(value);
  409. }
  410. /***********************************************************************************************
  411. * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? *
  412. * *
  413. * Determine is this object can recloak now and returns that info. Usually the answer is *
  414. * yes, but it can be overridden be derived classes. *
  415. * *
  416. * INPUT: none *
  417. * *
  418. * OUTPUT: bool; Can this object recloak now? (presumes it has the ability to cloak) *
  419. * *
  420. * WARNINGS: none *
  421. * *
  422. * HISTORY: *
  423. * 07/29/1996 JLB : Created. *
  424. *=============================================================================================*/
  425. bool TechnoClass::Is_Allowed_To_Recloak(void) const
  426. {
  427. return(true);
  428. }
  429. /***********************************************************************************************
  430. * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. *
  431. * *
  432. * This routine will determine the coordinate to use when this object fires. The coordinate *
  433. * is the location where bullets appear (or fire effects appear) when the object fires *
  434. * its weapon. *
  435. * *
  436. * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary *
  437. * weapon, 1 means secondary weapon. *
  438. * *
  439. * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon *
  440. * should appear. *
  441. * *
  442. * WARNINGS: none *
  443. * *
  444. * HISTORY: *
  445. * 07/29/1996 JLB : Created. *
  446. *=============================================================================================*/
  447. COORDINATE TechnoClass::Fire_Coord(int which) const
  448. {
  449. assert(IsActive);
  450. DirType dir = Turret_Facing();
  451. TechnoTypeClass const * tclass = Techno_Type_Class();
  452. int dist = 0;
  453. int lateral = 0;
  454. if (which == 0) {
  455. dist = tclass->PrimaryOffset;
  456. lateral = tclass->PrimaryLateral;
  457. } else {
  458. dist = tclass->SecondaryOffset;
  459. lateral = tclass->SecondaryLateral;
  460. }
  461. COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height);
  462. if (IsSecondShot) {
  463. coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral);
  464. } else {
  465. coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral);
  466. }
  467. coord = Coord_Move(coord, dir, dist);
  468. return(coord);
  469. }
  470. #ifdef CHEAT_KEYS
  471. /***********************************************************************************************
  472. * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. *
  473. * *
  474. * This routine is used to dump the status of the object class to the monochrome screen. *
  475. * This display can be used to track down or prevent bugs. *
  476. * *
  477. * INPUT: none *
  478. * *
  479. * OUTPUT: none *
  480. * *
  481. * WARNINGS: none *
  482. * *
  483. * HISTORY: *
  484. * 06/02/1994 JLB : Created. *
  485. *=============================================================================================*/
  486. void TechnoClass::Debug_Dump(MonoClass * mono) const
  487. {
  488. assert(IsActive);
  489. mono->Set_Cursor(10, 7);mono->Printf("%2d", Fetch_Rate());
  490. mono->Set_Cursor(14, 7);mono->Printf("%2d", Fetch_Stage());
  491. mono->Set_Cursor(49, 1);mono->Printf("%3d", Ammo);
  492. mono->Set_Cursor(71, 1);mono->Printf("$%4d", PurchasePrice);
  493. mono->Set_Cursor(54, 1);mono->Printf("%3d", (long)Arm);
  494. if (Is_Something_Attached()) {
  495. mono->Set_Cursor(1, 5);mono->Printf("%08X", Attached_Object());
  496. }
  497. if (Target_Legal(TarCom)) {
  498. mono->Set_Cursor(29, 3);mono->Printf("%08X", TarCom);
  499. }
  500. if (Target_Legal(SuspendedTarCom)) {
  501. mono->Set_Cursor(38, 3);mono->Printf("%08X", SuspendedTarCom);
  502. }
  503. if (Target_Legal(ArchiveTarget)) {
  504. mono->Set_Cursor(69, 5);mono->Printf("%08X", ArchiveTarget);
  505. }
  506. mono->Set_Cursor(47, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired());
  507. mono->Set_Cursor(64, 1);mono->Printf("%d(%d)", Cloak, CloakingDevice);
  508. mono->Fill_Attrib(14, 15, 12, 1, IsUseless ? MonoClass::INVERSE : MonoClass::NORMAL);
  509. mono->Fill_Attrib(14, 16, 12, 1, IsTickedOff ? MonoClass::INVERSE : MonoClass::NORMAL);
  510. mono->Fill_Attrib(14, 17, 12, 1, IsCloakable ? MonoClass::INVERSE : MonoClass::NORMAL);
  511. mono->Fill_Attrib(27, 13, 12, 1, IsLeader ? MonoClass::INVERSE : MonoClass::NORMAL);
  512. mono->Fill_Attrib(27, 14, 12, 1, IsALoaner ? MonoClass::INVERSE : MonoClass::NORMAL);
  513. mono->Fill_Attrib(27, 15, 12, 1, IsLocked ? MonoClass::INVERSE : MonoClass::NORMAL);
  514. mono->Fill_Attrib(27, 16, 12, 1, IsInRecoilState ? MonoClass::INVERSE : MonoClass::NORMAL);
  515. mono->Fill_Attrib(27, 17, 12, 1, IsTethered ? MonoClass::INVERSE : MonoClass::NORMAL);
  516. mono->Fill_Attrib(40, 13, 12, 1, IsOwnedByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL);
  517. mono->Fill_Attrib(40, 14, 12, 1, IsDiscoveredByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL);
  518. mono->Fill_Attrib(40, 15, 12, 1, IsDiscoveredByComputer ? MonoClass::INVERSE : MonoClass::NORMAL);
  519. mono->Fill_Attrib(40, 16, 12, 1, IsALemon ? MonoClass::INVERSE : MonoClass::NORMAL);
  520. mono->Set_Cursor(47, 17);mono->Printf("%3d", (long)IronCurtainCountDown);
  521. mono->Fill_Attrib(40, 17, 12, 1, IronCurtainCountDown > 0 ? MonoClass::INVERSE : MonoClass::NORMAL);
  522. RadioClass::Debug_Dump(mono);
  523. }
  524. #endif
  525. /***********************************************************************************************
  526. * TechnoClass::TechnoClass -- Default constructor for techno objects. *
  527. * *
  528. * This default constructor for techno objects is used to reset all internal variables to *
  529. * their default state. *
  530. * *
  531. * INPUT: none *
  532. * *
  533. * OUTPUT: none *
  534. * *
  535. * WARNINGS: none *
  536. * *
  537. * HISTORY: *
  538. * 12/09/1994 JLB : Created. *
  539. *=============================================================================================*/
  540. TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) :
  541. RadioClass(rtti, id),
  542. IsUseless(false),
  543. IsTickedOff(false),
  544. IsCloakable(false),
  545. IsLeader(false),
  546. IsALoaner(false),
  547. IsLocked(false),
  548. IsInRecoilState(false),
  549. IsTethered(false),
  550. IsOwnedByPlayer(false),
  551. IsDiscoveredByPlayer(false),
  552. IsDiscoveredByComputer(false),
  553. IsALemon(false),
  554. IsSecondShot(true),
  555. ArmorBias(1),
  556. FirepowerBias(1),
  557. IdleTimer(0),
  558. IronCurtainCountDown(0),
  559. SpiedBy(0),
  560. ArchiveTarget(TARGET_NONE),
  561. House(HouseClass::As_Pointer(house)),
  562. Cloak(UNCLOAKED),
  563. TarCom(TARGET_NONE),
  564. SuspendedTarCom(TARGET_NONE),
  565. PrimaryFacing(DIR_N),
  566. Arm(0),
  567. Ammo(-1),
  568. PurchasePrice(0)
  569. {
  570. IsOwnedByPlayer = (PlayerPtr == House);
  571. }
  572. /***********************************************************************************************
  573. * TechnoClass::Time_To_Build -- Determines the time it would take to build this. *
  574. * *
  575. * Use this routine to determine the amount of time it would take to build an object of *
  576. * this type. *
  577. * *
  578. * INPUT: none *
  579. * *
  580. * OUTPUT: Returns with the time it should take (unmodified by outside factors) to build *
  581. * this object. The time is expressed in game frames. *
  582. * *
  583. * WARNINGS: The time will usually be modified by power status and handicap rating for the *
  584. * owning house. The value returned is merely the raw unmodified time to build. *
  585. * *
  586. * HISTORY: *
  587. * 07/29/1996 JLB : Created. *
  588. * 09/27/1996 JLB : Takes into account the power availability. *
  589. *=============================================================================================*/
  590. //#define UNIT_BUILD_BIAS fixed(5,4)
  591. //#define UNIT_BUILD_BIAS fixed(6,4)
  592. #define UNIT_BUILD_BIAS fixed(1,1)
  593. //#define UNIT_BUILD_BIAS fixed(5,1)
  594. extern int UnitBuildPenalty;
  595. int TechnoClass::Time_To_Build(void) const
  596. {
  597. int val = Class_Of().Time_To_Build();
  598. #ifdef FIXIT_VERSION_3
  599. if (Session.Type == GAME_NORMAL ) {
  600. #else
  601. if (Session.Type == GAME_NORMAL ||
  602. PlayingAgainstVersion == VERSION_RED_ALERT_104 ||
  603. PlayingAgainstVersion == VERSION_RED_ALERT_107){
  604. #endif
  605. val *= House->BuildSpeedBias;
  606. }else{
  607. if (What_Am_I() == RTTI_BUILDING || What_Am_I() == RTTI_INFANTRY) {
  608. val *= House->BuildSpeedBias;
  609. } else {
  610. val *= House->BuildSpeedBias * fixed (UnitBuildPenalty, 100); //UNIT_BUILD_BIAS;
  611. }
  612. }
  613. /*
  614. ** Adjust the time to build based on the power output of the owning house.
  615. */
  616. fixed power = House->Power_Fraction();
  617. if (power > 1) power = 1;
  618. if (power < 1 && power > fixed::_3_4) power = fixed::_3_4;
  619. if (power < fixed::_1_2) power = fixed::_1_2;
  620. power.Inverse();
  621. val *= power;
  622. int divisor = House->Factory_Count(What_Am_I());
  623. if (divisor != 0) {
  624. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  625. // Hack: allow the multiple-factory bonus, but only up to two factories if
  626. // this is an AM<->AM game.
  627. if(NewUnitsEnabled) {
  628. val /= min(divisor,2);
  629. } else {
  630. val /= divisor;
  631. }
  632. #else
  633. val /= divisor;
  634. #endif
  635. }
  636. return(val);
  637. }
  638. /***********************************************************************************************
  639. * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? *
  640. * *
  641. * Use this routine to determine if this object should be visible on the player's radar *
  642. * screen. This routine doesn't take into consideration mapped terrain or whether the *
  643. * radar is active. It merely checks to see that if all else is functioning correctly, *
  644. * is this unit invisible or visible on the radar map. Typically, cloaked vehicles will *
  645. * not be visible. *
  646. * *
  647. * INPUT: none *
  648. * *
  649. * OUTPUT: bool; Is this object nominally visible on the player's radar screen? *
  650. * *
  651. * WARNINGS: none *
  652. * *
  653. * HISTORY: *
  654. * 05/27/1996 JLB : Created. *
  655. *=============================================================================================*/
  656. bool TechnoClass::Is_Visible_On_Radar(void) const
  657. {
  658. /*
  659. ** Hack: MRJ is invisible to radar, unless it's allied with the player.
  660. */
  661. if (What_Am_I() == RTTI_UNIT) {
  662. if(*((UnitClass *)this) == UNIT_MRJ) {
  663. if(!House->Is_Ally(PlayerPtr)) {
  664. return(false);
  665. }
  666. }
  667. }
  668. if (!Techno_Type_Class()->IsInvisible && (Cloak != CLOAKED || House->Is_Ally(PlayerPtr))) {
  669. return(true);
  670. }
  671. return(false);
  672. }
  673. /***********************************************************************************************
  674. * TechnoClass::Revealed -- Handles revealing an object to the house specified. *
  675. * *
  676. * When a unit moves out from under the shroud or when it gets delivered into already *
  677. * explored terrain, then it must be "revealed". Objects that are revealed may be *
  678. * announced to the player. The discovered bit updated accordingly. *
  679. * *
  680. * INPUT: house -- The house that this object is revealed to. *
  681. * *
  682. * OUTPUT: none *
  683. * *
  684. * WARNINGS: none *
  685. * *
  686. * HISTORY: *
  687. * 06/02/1994 JLB : Created. *
  688. * 12/27/1994 JLB : Discovered trigger event processing. *
  689. *=============================================================================================*/
  690. bool TechnoClass::Revealed(HouseClass * house)
  691. {
  692. assert(IsActive);
  693. if (house == PlayerPtr && IsDiscoveredByPlayer) return(false);
  694. if (house != PlayerPtr) {
  695. if (IsDiscoveredByComputer) return(false);
  696. IsDiscoveredByComputer = true;
  697. }
  698. if (RadioClass::Revealed(house)) {
  699. /*
  700. ** An enemy object that is discovered will go into hunt mode if
  701. ** its current mission is to ambush.
  702. */
  703. if (!house->IsHuman && Mission == MISSION_AMBUSH) {
  704. Assign_Mission(MISSION_HUNT);
  705. }
  706. if (house == PlayerPtr) {
  707. IsDiscoveredByPlayer = true;
  708. if (!IsOwnedByPlayer) {
  709. /*
  710. ** If there is a trigger event associated with this object, then process
  711. ** it for discovery purposes.
  712. */
  713. if (!ScenarioInit && Trigger.Is_Valid()) {
  714. Trigger->Spring(TEVENT_DISCOVERED, this);
  715. }
  716. /*
  717. ** Alert the enemy house to presence of the friendly side.
  718. */
  719. House->IsDiscovered = true;
  720. } else {
  721. /*
  722. ** A newly revealed object will always perform a look operation.
  723. */
  724. Look();
  725. }
  726. } else {
  727. IsDiscoveredByComputer = true;
  728. }
  729. return(true);
  730. }
  731. return(false);
  732. }
  733. /***********************************************************************************************
  734. * TechnoClass::Hidden -- Returns the object back into the hidden state. *
  735. * *
  736. * This routine is called for every object that returns to the darkness shroud or when *
  737. * it gets destroyed. This also occurs when an object enters another (such as infantry *
  738. * entering a transport helicopter). *
  739. * *
  740. * INPUT: none *
  741. * *
  742. * OUTPUT: none *
  743. * *
  744. * WARNINGS: none *
  745. * *
  746. * HISTORY: *
  747. * 06/02/1994 JLB : Created. *
  748. *=============================================================================================*/
  749. void TechnoClass::Hidden(void)
  750. {
  751. assert(IsActive);
  752. if (!IsDiscoveredByPlayer) return;
  753. if (!House->IsHuman) {
  754. IsDiscoveredByPlayer = false;
  755. }
  756. }
  757. /***********************************************************************************************
  758. * TechnoClass::Mark -- Handles marking of techno objects. *
  759. * *
  760. * On the Techno-level, marking handles transmission of the redraw command to any object *
  761. * that it is 'connected' with. This only occurs during loading and unloading. *
  762. * *
  763. * INPUT: mark -- The marking method. This routine just passes this on to base classes. *
  764. * *
  765. * OUTPUT: none *
  766. * *
  767. * WARNINGS: none *
  768. * *
  769. * HISTORY: *
  770. * 10/17/1994 JLB : Created. *
  771. *=============================================================================================*/
  772. bool TechnoClass::Mark(MarkType mark)
  773. {
  774. assert(IsActive);
  775. if (RadioClass::Mark(mark)) {
  776. /*
  777. ** When redrawing an object, if there is another object tethered to this one,
  778. ** redraw it as well.
  779. */
  780. if (IsTethered) {
  781. Transmit_Message(RADIO_REDRAW);
  782. }
  783. return(true);
  784. }
  785. return(false);
  786. }
  787. /***********************************************************************************************
  788. * TechnoClass::Receive_Message -- Handles inbound message as appropriate. *
  789. * *
  790. * This routine is used to handle inbound radio messages. It only handles those messages *
  791. * that deal with techno objects. Typically, these include loading and unloading. *
  792. * *
  793. * INPUT: from -- Pointer to the originator of the radio message. *
  794. * *
  795. * message -- The inbound radio message. *
  796. * *
  797. * param -- Reference to optional parameter that might be used to transfer *
  798. * more information than is possible with the simple radio message *
  799. * type. *
  800. * *
  801. * OUTPUT: Returns with the radio response. *
  802. * *
  803. * WARNINGS: none *
  804. * *
  805. * HISTORY: *
  806. * 10/17/1994 JLB : Created. *
  807. * 06/17/1995 JLB : Handles tether contact messages. *
  808. *=============================================================================================*/
  809. RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
  810. {
  811. assert(IsActive);
  812. switch (message) {
  813. /*
  814. ** Just received instructions to attack the specified target.
  815. */
  816. case RADIO_ATTACK_THIS:
  817. if (Techno_Type_Class()->PrimaryWeapon != NULL) {
  818. Assign_Target(param);
  819. Assign_Mission(MISSION_ATTACK);
  820. return(RADIO_ROGER);
  821. }
  822. break;
  823. /*
  824. ** Establish a tethered connection to the object in radio contact.
  825. */
  826. case RADIO_TETHER:
  827. if (!IsTethered) {
  828. IsTethered = true;
  829. Transmit_Message(RADIO_TETHER, from);
  830. return(RADIO_ROGER);
  831. }
  832. break;
  833. /*
  834. ** Break the tethered connection to the object in radio contact.
  835. */
  836. case RADIO_UNTETHER:
  837. if (IsTethered) {
  838. IsTethered = false;
  839. Transmit_Message(RADIO_UNTETHER, from);
  840. return(RADIO_ROGER);
  841. }
  842. break;
  843. /*
  844. ** A tethered unit has reached it's destination. All is
  845. ** clear, so radio contact can be broken.
  846. */
  847. case RADIO_UNLOADED:
  848. Transmit_Message(RADIO_UNTETHER, from);
  849. return(Transmit_Message(RADIO_OVER_OUT, from));
  850. /*
  851. ** When this message is received, it means that the other object
  852. ** has already turned its radio off. Turn this radio off as well.
  853. */
  854. case RADIO_OVER_OUT:
  855. Transmit_Message(RADIO_UNTETHER, from);
  856. RadioClass::Receive_Message(from, message, param);
  857. return(RADIO_ROGER);
  858. /*
  859. ** Request to be informed when unloaded. This message is transmitted
  860. ** by the transport unit to the transported unit as it is about to be
  861. ** unloaded. It is saying, "All is clear. Drive off now."
  862. */
  863. case RADIO_UNLOAD:
  864. case RADIO_BACKUP_NOW:
  865. case RADIO_HOLD_STILL:
  866. Transmit_Message(RADIO_TETHER, from);
  867. RadioClass::Receive_Message(from, message, param);
  868. return(RADIO_ROGER);
  869. /*
  870. ** Handle reloading one ammo point for this unit.
  871. */
  872. case RADIO_RELOAD:
  873. if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE);
  874. /*BG*/ Mark(MARK_CHANGE_REDRAW);
  875. Ammo++;
  876. return(RADIO_ROGER);
  877. /*
  878. ** Handle repair of this unit.
  879. */
  880. case RADIO_REPAIR:
  881. /*
  882. ** If it's a mine layer, re-arm him if he's empty. This always takes precedence
  883. ** over repair, since this operation is free.
  884. */
  885. if (What_Am_I() == RTTI_UNIT && *((UnitClass *)this) == UNIT_MINELAYER && ((UnitClass *)this)->Ammo < ((UnitClass *)this)->Class->MaxAmmo) {
  886. ((UnitClass *)this)->Ammo = ((UnitClass *)this)->Class->MaxAmmo;
  887. return(RADIO_NEGATIVE);
  888. }
  889. /*
  890. ** Determine if this unit can be repaired becaause it is under strength. If so, then
  891. ** proceed with the repair process.
  892. */
  893. if (Health_Ratio() < Rule.ConditionGreen) {
  894. int cost = Techno_Type_Class()->Repair_Cost();
  895. cost = max(cost, 1);
  896. int step = Techno_Type_Class()->Repair_Step();
  897. step = max(step, 1);
  898. /*
  899. ** If there is sufficient money to repair the unit one step, then do so.
  900. ** Otherwise return with a "can't complete" radio response.
  901. */
  902. if (House->Available_Money() >= cost) {
  903. House->Spend_Money(cost);
  904. Strength += step;
  905. /*
  906. ** Return with either an all ok or mission accomplished radio message. This
  907. ** lets the repairing object know if it should abort the repair control process
  908. ** or continue it.
  909. */
  910. if (Health_Ratio() < Rule.ConditionGreen) {
  911. return(RADIO_ROGER);
  912. } else {
  913. Strength = Techno_Type_Class()->MaxStrength;
  914. return(RADIO_ALL_DONE);
  915. }
  916. } else {
  917. return(RADIO_CANT);
  918. }
  919. }
  920. return(RADIO_NEGATIVE);
  921. default:
  922. break;
  923. }
  924. return(RadioClass::Receive_Message(from, message, param));
  925. }
  926. /***********************************************************************************************
  927. * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. *
  928. * *
  929. * This routine handles marking a game object as not a loaner. It is set only if the unit *
  930. * is not player owned and is on the regular map. This is necessary so that enemy objects *
  931. * can exist off-map but as soon as they move onto the map, are flagged so that can never *
  932. * leave it again. *
  933. * *
  934. * INPUT: why -- Specifies the circumstances under which this routine was called. *
  935. * *
  936. * OUTPUT: none *
  937. * *
  938. * WARNINGS: none *
  939. * *
  940. * HISTORY: *
  941. * 10/17/1994 JLB : Created. *
  942. * 10/26/94 JLB : Handles scanner units. *
  943. * 12/27/1994 JLB : Checks for an processes any trigger in cell. *
  944. *=============================================================================================*/
  945. void TechnoClass::Per_Cell_Process(PCPType why)
  946. {
  947. assert(IsActive);
  948. if (why == PCP_END) {
  949. CELL cell = Coord_Cell(Center_Coord());
  950. /*
  951. ** When enemy units enter the proper map area from off map, they are
  952. ** flagged so that they won't travel back off the map again.
  953. */
  954. if (!IsLocked && Map.In_Radar(cell)) {
  955. IsLocked = true;
  956. }
  957. /*
  958. ** If this object somehow moves into mapped terrain, but is not yet
  959. ** discovered, then flag it to be discovered.
  960. */
  961. if (!IsDiscoveredByPlayer && Map[cell].IsVisible) {
  962. Revealed(PlayerPtr);
  963. }
  964. }
  965. }
  966. /***********************************************************************************************
  967. * TechnoClass::Draw_It -- Draws the health bar (if necessary). *
  968. * *
  969. * This routine will draw the common elements for techno type objects. This element is *
  970. * the health bar. The main game object has already been rendered by the time this *
  971. * routine is called. *
  972. * *
  973. * INPUT: x,y -- The coordinate of the center of the unit. *
  974. * *
  975. * OUTPUT: none *
  976. * *
  977. * WARNINGS: none *
  978. * *
  979. * HISTORY: *
  980. * 10/17/1994 JLB : Created. *
  981. * 10/26/94 JLB : Knows about radar scanned cells. *
  982. * 12/13/1994 JLB : Clips health bar against map edge. *
  983. * 01/23/1995 JLB : Dynamic selected object rectangle. *
  984. *=============================================================================================*/
  985. void TechnoClass::Draw_It(int x, int y, WindowNumberType window) const
  986. {
  987. assert(IsActive);
  988. /*
  989. ** Tells the door logic that it has been drawn.
  990. */
  991. ((TechnoClass *)this)->Clear_Redraw_Flag();
  992. if (IsSelected) {
  993. GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(),
  994. WindowList[window][WINDOWX] + LogicPage->Get_XPos(),
  995. WindowList[window][WINDOWY] + LogicPage->Get_YPos(),
  996. WindowList[window][WINDOWWIDTH],
  997. WindowList[window][WINDOWHEIGHT]);
  998. /*
  999. ** The infantry select box should be a bit higher than normal.
  1000. */
  1001. if (What_Am_I() == RTTI_INFANTRY) {
  1002. y -= 6;
  1003. }
  1004. if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) {
  1005. y -= 5;
  1006. }
  1007. /*
  1008. ** Fetch the dimensions of the object. These dimensions will be used to draw
  1009. ** the selection box and the health bar.
  1010. */
  1011. int width,height;
  1012. Class_Of().Dimensions(width, height);
  1013. if (Strength && (House->Is_Ally(PlayerPtr) || Rule.IsHealthBar)) {
  1014. fixed ratio = Health_Ratio();
  1015. int pwidth; // Pixel width of bar interior.
  1016. int color; // The color to give the interior of the bargraph.
  1017. int xx = x-width/2;
  1018. int yy = y-(height/2);
  1019. /*
  1020. ** Draw the outline of the bargraph.
  1021. */
  1022. draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade);
  1023. draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK);
  1024. /*
  1025. ** Determine the width of the interior strength
  1026. ** graph.
  1027. */
  1028. pwidth = (width-2) * ratio;
  1029. pwidth = Bound(pwidth, 1, width-2);
  1030. color = LTGREEN;
  1031. if (ratio <= Rule.ConditionYellow) {
  1032. color = YELLOW;
  1033. }
  1034. if (ratio <= Rule.ConditionRed) {
  1035. color = RED;
  1036. }
  1037. draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color);
  1038. }
  1039. /*
  1040. ** Draw the selected object graphic.
  1041. */
  1042. if (IsSelected) {
  1043. int lx = width/2;
  1044. int ly = height/2;
  1045. int dx = width/5;
  1046. int dy = height/5;
  1047. int fudge = (House->Is_Ally(PlayerPtr) || Rule.IsHealthBar) ? 4 : 0;
  1048. if (What_Am_I() == RTTI_VESSEL) {
  1049. lx = width / 2;
  1050. }
  1051. // Upper left corner.
  1052. draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx+dx, fudge+y-ly, WHITE);
  1053. draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx, fudge+y-ly+dy, WHITE);
  1054. // Upper right corner.
  1055. draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx-dx, fudge+y-ly, WHITE);
  1056. draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx, fudge+y-ly+dy, WHITE);
  1057. // Lower right corner.
  1058. draw_window.Draw_Line(x+lx, y+ly, x+lx-dx, y+ly, WHITE);
  1059. draw_window.Draw_Line(x+lx, y+ly, x+lx, y+ly-dy, WHITE);
  1060. // Lower left corner.
  1061. draw_window.Draw_Line(x-lx, y+ly, x-lx+dx, y+ly, WHITE);
  1062. draw_window.Draw_Line(x-lx, y+ly, x-lx, y+ly-dy, WHITE);
  1063. if (House->Is_Ally(PlayerPtr) || (SpiedBy & (1<<(PlayerPtr->Class->House)))) {
  1064. Draw_Pips((x-lx)+5, y+ly-3, window);
  1065. }
  1066. }
  1067. }
  1068. }
  1069. /***********************************************************************************************
  1070. * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. *
  1071. * *
  1072. * This routine handles the common operation between techno objects when they are *
  1073. * unlimboed. This includes revealing the map. *
  1074. * *
  1075. * INPUT: coord -- The coordinate to unlimbo object at. *
  1076. * *
  1077. * dir (optional) -- initial facing direction for this object *
  1078. * *
  1079. * OUTPUT: bool; Was the unlimbo successful? *
  1080. * *
  1081. * WARNINGS: none *
  1082. * *
  1083. * HISTORY: *
  1084. * 11/14/1994 JLB : Created. *
  1085. *=============================================================================================*/
  1086. bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir)
  1087. {
  1088. assert(IsActive);
  1089. if (RadioClass::Unlimbo(coord, dir)) {
  1090. PrimaryFacing = dir;
  1091. Enter_Idle_Mode(true);
  1092. Commence();
  1093. IsLocked = Map.In_Radar(Coord_Cell(coord));
  1094. return(true);
  1095. }
  1096. return(false);
  1097. }
  1098. /***********************************************************************************************
  1099. * TechnoClass::In_Range -- Determines if specified target is within weapon range. *
  1100. * *
  1101. * This routine is used to compare the distance to the specified target with the range *
  1102. * of the weapon. If the target is outside of weapon range, then false is returned. *
  1103. * *
  1104. * INPUT: target -- The target to check if it is within weapon range. *
  1105. * *
  1106. * which -- Which weapon to use in determining range. 0=primary, 1=secondary. *
  1107. * *
  1108. * OUTPUT: bool; Is the specified target within weapon range? *
  1109. * *
  1110. * WARNINGS: none *
  1111. * *
  1112. * HISTORY: *
  1113. * 11/14/1994 JLB : Created. *
  1114. *=============================================================================================*/
  1115. bool TechnoClass::In_Range(TARGET target, int which) const
  1116. {
  1117. assert(IsActive);
  1118. if (IsLocked && Target_Legal(target)) {
  1119. int range = Weapon_Range(which);
  1120. BuildingClass const * building = As_Building(target);
  1121. if (building != NULL) {
  1122. range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4));
  1123. }
  1124. if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) {
  1125. return(true);
  1126. }
  1127. }
  1128. return(false);
  1129. }
  1130. /***********************************************************************************************
  1131. * TechnoClass::In_Range -- Determines if specified target is within weapon range. *
  1132. * *
  1133. * This routine will determine if the pointer to the target object passed into this *
  1134. * routine is within weapon range. *
  1135. * *
  1136. * INPUT: target -- Pointer to the target object to check if within weapon range. *
  1137. * *
  1138. * which -- Which weapon to use in determining range. 0=primary, 1=secondary. *
  1139. * *
  1140. * OUTPUT: bool; Is the target within weapon range? *
  1141. * *
  1142. * WARNINGS: none *
  1143. * *
  1144. * HISTORY: *
  1145. * 11/14/1994 JLB : Created. *
  1146. *=============================================================================================*/
  1147. bool TechnoClass::In_Range(ObjectClass const * target, int which) const
  1148. {
  1149. assert(IsActive);
  1150. if (IsLocked && target) {
  1151. int range = Weapon_Range(which);
  1152. if (target->What_Am_I() == RTTI_BUILDING) {
  1153. BuildingClass const * building = (BuildingClass const *)target;
  1154. range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4));
  1155. }
  1156. if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) {
  1157. return(true);
  1158. }
  1159. }
  1160. return(false);
  1161. }
  1162. /***********************************************************************************************
  1163. * TechnoClass::In_Range -- Determines if the specified coordinate is within range. *
  1164. * *
  1165. * Use this routine to determine if the specified coordinate is within weapon range. *
  1166. * *
  1167. * INPUT: coord -- The coordinate to examine against the object to determine range. *
  1168. * *
  1169. * which -- The weapon to consider when determining range. 0=primary, 1=secondary. *
  1170. * *
  1171. * OUTPUT: bool; Is the weapon within range? *
  1172. * *
  1173. * WARNINGS: none *
  1174. * *
  1175. * HISTORY: *
  1176. * 03/16/1995 JLB : Created. *
  1177. *=============================================================================================*/
  1178. bool TechnoClass::In_Range(COORDINATE coord, int which) const
  1179. {
  1180. assert(IsActive);
  1181. return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which));
  1182. }
  1183. /***********************************************************************************************
  1184. * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. *
  1185. * *
  1186. * This routine scans around the cell specified and if there are any friendly buildings *
  1187. * nearby, the multiplier return value will be reduced. If there are no friendly buildings *
  1188. * nearby, then the return value will be 1. It checks to see if the primary weapon is *
  1189. * supposed to perform this scan and if so, the scan will be performed. Otherwise the *
  1190. * default value is quickly returned. *
  1191. * *
  1192. * INPUT: cell -- The cell where the potential target lies. An area around this cell will *
  1193. * be scanned for friendly buildings. *
  1194. * *
  1195. * OUTPUT: Returns with the multiplier to be multiplied by the potential target score value. *
  1196. * For less opportune targets, the multiplier fraction will be less than one. For *
  1197. * all other cases, it will return the default value of 1. *
  1198. * *
  1199. * WARNINGS: none *
  1200. * *
  1201. * HISTORY: *
  1202. * 08/23/1996 JLB : Created. *
  1203. *=============================================================================================*/
  1204. fixed TechnoClass::Area_Modify(CELL cell) const
  1205. {
  1206. // assert(Techno_Type_Class()->PrimaryWeapon != NULL);
  1207. if (Techno_Type_Class()->PrimaryWeapon == NULL || !Techno_Type_Class()->PrimaryWeapon->IsSupressed) return(1);
  1208. int crange = Lepton_To_Cell(Rule.SupressRadius);
  1209. fixed odds = 1;
  1210. for (int radius = 1; radius < crange; radius++) {
  1211. /*
  1212. ** Scan the top and bottom rows of the "box".
  1213. */
  1214. for (int x = -radius; x <= radius; x++) {
  1215. CELL newcell;
  1216. if ((Cell_X(cell) + x) < Map.MapCellX) continue;
  1217. if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue;
  1218. if ((Cell_Y(cell) - radius) >= Map.MapCellY) {
  1219. newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius);
  1220. BuildingClass const * building = Map[newcell].Cell_Building();
  1221. if (building != NULL && House->Is_Ally(building)) {
  1222. odds /= 2;
  1223. }
  1224. }
  1225. if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) {
  1226. newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius);
  1227. BuildingClass const * building = Map[newcell].Cell_Building();
  1228. if (building != NULL && House->Is_Ally(building)) {
  1229. odds /= 2;
  1230. }
  1231. }
  1232. }
  1233. /*
  1234. ** Scan the left and right columns of the "box".
  1235. */
  1236. for (int y = -(radius-1); y < radius; y++) {
  1237. CELL newcell;
  1238. if ((Cell_Y(cell) + y) < Map.MapCellY) continue;
  1239. if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue;
  1240. if ((Cell_X(cell) - radius) >= Map.MapCellX) {
  1241. newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y);
  1242. BuildingClass const * building = Map[newcell].Cell_Building();
  1243. if (building != NULL && House->Is_Ally(building)) {
  1244. odds /= 2;
  1245. }
  1246. }
  1247. if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) {
  1248. newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y);
  1249. BuildingClass const * building = Map[newcell].Cell_Building();
  1250. if (building != NULL && House->Is_Ally(building)) {
  1251. odds /= 2;
  1252. }
  1253. }
  1254. }
  1255. }
  1256. return(odds);
  1257. }
  1258. /***********************************************************************************************
  1259. * TechnoClass::Evaluate_Object -- Determines score value of specified object. *
  1260. * *
  1261. * This routine is used to determine the score value (value as a potential target) of the *
  1262. * object specified. This routine will check the specified object for all the various *
  1263. * legality checks that threat scanning requires. This is the main workhorse routine for *
  1264. * target searching. *
  1265. * *
  1266. * INPUT: method -- The threat method requested. This is a combined bitflag value that *
  1267. * not only specifies the kind of targets to consider, but how far away *
  1268. * they are allowed to be. *
  1269. * *
  1270. * mask -- This is an RTTI mask to use for quickly eliminating object types. *
  1271. * The mask is created outside of this routine because this routine is *
  1272. * usually called from within a loop and this value is constant in that *
  1273. * context. *
  1274. * *
  1275. * range -- The range at which potential target objects are rejected. *
  1276. * 0 = must be within weapon range. *
  1277. * >0 = must be within this lepton distance. *
  1278. * <0 = range doesn't matter. *
  1279. * *
  1280. * object -- Pointer to the object itself. *
  1281. * *
  1282. * value -- Reference to the value variable that this routine will fill in. The *
  1283. * higher the value the more likely this object will be selected as best. *
  1284. * *
  1285. * zone -- The zone restriction if any. A -1 means no zone check required. *
  1286. * *
  1287. * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the *
  1288. * value parameter will be filled in correctly. *
  1289. * *
  1290. * WARNINGS: This routine is time consuming. Don't call unless necessary. *
  1291. * *
  1292. * HISTORY: *
  1293. * 06/30/1995 JLB : Created. *
  1294. * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. *
  1295. * 09/22/1995 JLB : Zone checking enabled. *
  1296. * 10/05/1995 JLB : Gives greater weight to designated enemy house targets. *
  1297. * 02/16/1996 JLB : Added additional threat checks. *
  1298. *=============================================================================================*/
  1299. bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone) const
  1300. {
  1301. assert(IsActive);
  1302. assert(object != NULL);
  1303. BStart(BENCH_EVAL_OBJECT);
  1304. /*
  1305. ** An object in limbo can never be a valid target.
  1306. */
  1307. if (object == NULL || object->IsInLimbo) {
  1308. BEnd(BENCH_EVAL_OBJECT);
  1309. return(false);
  1310. }
  1311. /*
  1312. ** If the object is cloaked, then it isn't a legal target.
  1313. */
  1314. if (object->Cloak == CLOAKED) {
  1315. BEnd(BENCH_EVAL_OBJECT);
  1316. return(false);
  1317. }
  1318. /*
  1319. ** If the object is in a "harmless" state, then don't bother to consider it
  1320. ** a threat.
  1321. */
  1322. if (MissionControl[object->Mission].IsNoThreat) {
  1323. BEnd(BENCH_EVAL_OBJECT);
  1324. return(false);
  1325. }
  1326. /*
  1327. ** If the object is not within the desired zone, then ignore it, but only if
  1328. ** zone checking is desired.
  1329. */
  1330. COORDINATE objectcoord = object->Center_Coord();
  1331. if (zone != -1 && Map[objectcoord].Zones[Techno_Type_Class()->MZone] != zone) {
  1332. BEnd(BENCH_EVAL_OBJECT);
  1333. return(false);
  1334. }
  1335. /*
  1336. ** Friendly units are never considered a good target. Bail if this
  1337. ** object is a friend. Unless we're a medic, of course. But then,
  1338. ** only consider it a target if it's injured.
  1339. */
  1340. if (House->Is_Ally(object)) {
  1341. if (Combat_Damage() < 0) {
  1342. if (object->Health_Ratio() == Rule.ConditionGreen) {
  1343. BEnd(BENCH_EVAL_OBJECT);
  1344. return(false);
  1345. }
  1346. } else {
  1347. BEnd(BENCH_EVAL_OBJECT);
  1348. return(false);
  1349. }
  1350. }
  1351. /*
  1352. ** If the object is further away than allowed, bail.
  1353. */
  1354. int dist = Distance(object);
  1355. if (range > 0 && dist > range) {
  1356. BEnd(BENCH_EVAL_OBJECT);
  1357. return(false);
  1358. }
  1359. if (range == 0) {
  1360. int primary = What_Weapon_Should_I_Use(object->As_Target());
  1361. if (!In_Range(object, primary)) {
  1362. BEnd(BENCH_EVAL_OBJECT);
  1363. return(false);
  1364. }
  1365. }
  1366. /*
  1367. ** If the object is not visible, then bail. Human controlled units
  1368. ** are always considered to be visible.
  1369. */
  1370. if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && Session.Type == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) {
  1371. BEnd(BENCH_EVAL_OBJECT);
  1372. return(false);
  1373. }
  1374. /*
  1375. ** Quickly eliminate all unit types that are not allowed according to the mask
  1376. ** value.
  1377. */
  1378. RTTIType otype = object->What_Am_I();
  1379. if (!((1 << otype) & mask)) {
  1380. BEnd(BENCH_EVAL_OBJECT);
  1381. return(false); // Mask failure.
  1382. }
  1383. /*
  1384. ** Determine if the target is theoretically allowed to be a target. If
  1385. ** not, then bail.
  1386. */
  1387. TechnoTypeClass const * tclass = object->Techno_Type_Class();
  1388. if (!tclass->IsLegalTarget) {
  1389. BEnd(BENCH_EVAL_OBJECT);
  1390. return(false); // Legality failure.
  1391. }
  1392. /*
  1393. ** Never consider a spy to be a valid target, unless you're a dog
  1394. */
  1395. if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_SPY) {
  1396. if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsDog) {
  1397. // continue executing...
  1398. } else {
  1399. BEnd(BENCH_EVAL_OBJECT);
  1400. return(false);
  1401. }
  1402. }
  1403. /*
  1404. ** Special case so that SAM site doesn't fire on aircraft that are landed.
  1405. */
  1406. if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) {
  1407. if (((AircraftClass *)object)->Height == 0) {
  1408. BEnd(BENCH_EVAL_OBJECT);
  1409. return(false);
  1410. }
  1411. }
  1412. /*
  1413. ** If only allowed to attack civilians, then eliminate all other types.
  1414. */
  1415. if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) {
  1416. BEnd(BENCH_EVAL_OBJECT);
  1417. return(false);
  1418. }
  1419. /*
  1420. ** If the scan is limited to capturable buildings only, then bail if the examined
  1421. ** object isn't a capturable building.
  1422. */
  1423. if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) {
  1424. BEnd(BENCH_EVAL_OBJECT);
  1425. return(false);
  1426. }
  1427. /*
  1428. ** If we're a sub and the subject is a structure, bail if the structure
  1429. ** is other than a sub pen or shipyard.
  1430. */
  1431. if (otype == RTTI_BUILDING && What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_SS) {
  1432. StructType ostruc = *(BuildingClass *)object;
  1433. if (ostruc != STRUCT_SUB_PEN && ostruc != STRUCT_SHIP_YARD) {
  1434. BEnd(BENCH_EVAL_OBJECT);
  1435. return(false);
  1436. }
  1437. }
  1438. /*
  1439. ** SPECIAL CASE: Friendly units won't automatically fire on buildings
  1440. ** if the building is not aggressive. That is, unless it is part of a team. A team
  1441. ** is allowed to pick any target it so chooses.
  1442. */
  1443. if ((!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) &&
  1444. (House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) &&
  1445. otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) {
  1446. #ifdef OBSOLETE
  1447. if ((!Is_Foot() || ((FootClass *)this)->Team.Is_Valid()) && House->IsHuman && otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) {
  1448. #endif
  1449. BEnd(BENCH_EVAL_OBJECT);
  1450. return(false);
  1451. }
  1452. /*
  1453. ** If the search is restricted to Tiberium processing objects, then
  1454. ** perform the special qualification check now.
  1455. */
  1456. if (method & THREAT_TIBERIUM) {
  1457. switch (otype) {
  1458. case RTTI_UNIT:
  1459. if (!((UnitTypeClass const *)tclass)->IsToHarvest) {
  1460. BEnd(BENCH_EVAL_OBJECT);
  1461. return(false);
  1462. }
  1463. break;
  1464. case RTTI_BUILDING:
  1465. if (!((BuildingTypeClass const *)tclass)->Capacity && Session.Type != GAME_NORMAL) {
  1466. BEnd(BENCH_EVAL_OBJECT);
  1467. return(false);
  1468. }
  1469. break;
  1470. default:
  1471. BEnd(BENCH_EVAL_OBJECT);
  1472. return(false);
  1473. }
  1474. }
  1475. /*
  1476. ** If this target value is better than the previously recorded best
  1477. ** target value then record this target for possible return as the
  1478. ** best.
  1479. */
  1480. int rawval = object->Value();
  1481. value = rawval + object->Crew.Kills;
  1482. /*
  1483. ** If the candidate object is owned by the designated enemy of this house, then
  1484. ** give it a higher value. This will tend to gravitate attacks toward the main
  1485. ** antagonist of this house.
  1486. */
  1487. if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) {
  1488. value += 500;
  1489. value *= 3;
  1490. }
  1491. /*
  1492. ** If the object is outside of the protective umbrella of the enemy base, then give it
  1493. ** a target boost value.
  1494. */
  1495. if (object->House->Which_Zone(object) == ZONE_NONE) {
  1496. value *= 2;
  1497. }
  1498. /*
  1499. ** If fake buildings are considered to be a greater target option, then boost
  1500. ** the fake building's value.
  1501. */
  1502. if ((method & THREAT_FAKES) && otype == RTTI_BUILDING) {
  1503. switch (!((BuildingTypeClass const *)tclass)->Type) {
  1504. case STRUCT_FAKECONST:
  1505. case STRUCT_FAKEWEAP:
  1506. case STRUCT_FAKE_YARD:
  1507. case STRUCT_FAKE_PEN:
  1508. case STRUCT_FAKE_RADAR:
  1509. break;
  1510. /*
  1511. ** Ignore all non-fake buildings.
  1512. */
  1513. default:
  1514. value = 0;
  1515. break;
  1516. }
  1517. }
  1518. /*
  1519. ** If power plants are to be considered a greater threat, then increase
  1520. ** their value here. Buildings that produce no power are not considered
  1521. ** a threat.
  1522. */
  1523. if ((method & THREAT_POWER) && otype == RTTI_BUILDING) {
  1524. if (((BuildingTypeClass const *)tclass)->Power > 0) {
  1525. value += ((BuildingTypeClass const *)tclass)->Power * 1000;
  1526. } else {
  1527. value = 0;
  1528. }
  1529. }
  1530. /*
  1531. ** If factories are to be considered a greater threat, then don't
  1532. ** consider any non-factory building.
  1533. */
  1534. if ((method & THREAT_FACTORIES) && otype == RTTI_BUILDING) {
  1535. if (((BuildingTypeClass const *)tclass)->ToBuild == RTTI_NONE) {
  1536. value = 0;
  1537. }
  1538. }
  1539. /*
  1540. ** If base defensive structures are to be considered a greater threat, then
  1541. ** don't consider an unarmed building to be a threat.
  1542. */
  1543. if ((method & THREAT_BASE_DEFENSE) /*&& otype == RTTI_BUILDING*/) {
  1544. if (tclass->PrimaryWeapon == NULL) {
  1545. value = 0;
  1546. }
  1547. }
  1548. /*
  1549. ** Possibly cause a reduction of the target's value if it is nearby friendly
  1550. ** structures and the primary weapon of this object is flagged for
  1551. ** friendly fire supression special check logic.
  1552. */
  1553. fixed areamod = Area_Modify(Coord_Cell(object->Center_Coord()));
  1554. if (areamod != 1) {
  1555. value = areamod * value;
  1556. }
  1557. /*
  1558. ** Adjust the target value upward if it is in the 'nervous zone' of the
  1559. ** owning base. This will tend to protect the base more thoroughly than
  1560. ** an unmodified scan would.
  1561. */
  1562. if (House->Which_Zone(object) != ZONE_NONE) {
  1563. value *= Rule.NervousBias;
  1564. }
  1565. /*
  1566. ** Lessen threat as a factor of distance.
  1567. */
  1568. if (value) {
  1569. // if (rawval) {
  1570. value = (value * 32000) / ((dist/ICON_LEPTON_W)+1);
  1571. // value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1);
  1572. // if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W;
  1573. value = max(value, 1);
  1574. BEnd(BENCH_EVAL_OBJECT);
  1575. return(true);
  1576. }
  1577. value = 0;
  1578. BEnd(BENCH_EVAL_OBJECT);
  1579. return(false);
  1580. }
  1581. /***********************************************************************************************
  1582. * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. *
  1583. * *
  1584. * This routine will examine the specified cell and return with the potential target *
  1585. * object it contains and the value of it. Use this routine when searching for threats. *
  1586. * *
  1587. * INPUT: method -- The scan method to use for target searching. *
  1588. * *
  1589. * mask -- Prebuilt mask of object RTTI types acceptable for scanning. *
  1590. * *
  1591. * range -- Scan range limit to use for elimination purposes. This ensures that *
  1592. * objects in the "corner" of a square scan get properly discarded. *
  1593. * *
  1594. * object -- Pointer to object pointer to be filled in with the object at this *
  1595. * cell as a valid target. *
  1596. * *
  1597. * value -- Reference to the value of the object in this cell. It will be set *
  1598. * according to the object's value. *
  1599. * *
  1600. * zone -- The zone restriction if any. A -1 means no zone check required. *
  1601. * *
  1602. * OUTPUT: Was a valid potential target found in this cell? *
  1603. * *
  1604. * WARNINGS: none *
  1605. * *
  1606. * HISTORY: *
  1607. * 06/19/1995 JLB : Created. *
  1608. * 09/22/1995 JLB : Zone checking enabled. *
  1609. *=============================================================================================*/
  1610. bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value, int zone) const
  1611. {
  1612. assert(IsActive);
  1613. BStart(BENCH_EVAL_CELL);
  1614. *object = NULL;
  1615. value = 0;
  1616. /*
  1617. ** If the cell is not on the legal map, then always ignore it.
  1618. */
  1619. if ((unsigned)cell > MAP_CELL_TOTAL) {
  1620. BEnd(BENCH_EVAL_CELL);
  1621. return(false);
  1622. }
  1623. if (!Map.In_Radar(cell)) {
  1624. BEnd(BENCH_EVAL_CELL);
  1625. return(false);
  1626. }
  1627. /*
  1628. ** Fetch the techno object from the cell. If there is no
  1629. ** techno object there, then bail.
  1630. */
  1631. CellClass * cellptr = &Map[cell];
  1632. /*
  1633. ** Don't consider for evaluation a cell that is not within the same zone. Only
  1634. ** perform this check if zone checking is required.
  1635. */
  1636. if (zone != -1 && cellptr->Zones[Techno_Type_Class()->MZone] != zone) {
  1637. BEnd(BENCH_EVAL_CELL);
  1638. return(false);
  1639. }
  1640. TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier();
  1641. while (tentative != NULL) {
  1642. if (tentative != this) {
  1643. if (tentative->Is_Techno()) {
  1644. if (Combat_Damage() < 0) {
  1645. if (tentative->Health_Ratio() < Rule.ConditionGreen && House->Is_Ally(tentative)) break;
  1646. } else {
  1647. if (!House->Is_Ally(tentative)) break;
  1648. }
  1649. }
  1650. }
  1651. tentative = (TechnoClass const *)(ObjectClass *)tentative->Next;
  1652. }
  1653. if (tentative == NULL) {
  1654. BEnd(BENCH_EVAL_CELL);
  1655. return(false);
  1656. }
  1657. *object = tentative;
  1658. bool result = Evaluate_Object(method, mask, range, tentative, value);
  1659. BEnd(BENCH_EVAL_CELL);
  1660. return(result);
  1661. }
  1662. /***********************************************************************************************
  1663. * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. *
  1664. * *
  1665. * This will examine the cell (as if it contained no sentient objects) and determine a *
  1666. * target value to assign to it. Typically, this is only useful for wall destroyable *
  1667. * weapons when dealing with enemy walls. *
  1668. * *
  1669. * INPUT: cell -- The cell to examine and evaluate. *
  1670. * *
  1671. * OUTPUT: Returns with the target value to assign to this cell. *
  1672. * *
  1673. * WARNINGS: none *
  1674. * *
  1675. * HISTORY: *
  1676. * 09/10/1996 JLB : Created. *
  1677. *=============================================================================================*/
  1678. int TechnoClass::Evaluate_Just_Cell(CELL cell) const
  1679. {
  1680. BStart(BENCH_EVAL_WALL);
  1681. /*
  1682. ** Ships don't scan for walls.
  1683. */
  1684. if (What_Am_I() == RTTI_VESSEL) {
  1685. return(0);
  1686. }
  1687. /*
  1688. ** First, only computer objects are allowed to automatically scan for walls.
  1689. */
  1690. if (House->IsHuman) {
  1691. BEnd(BENCH_EVAL_WALL);
  1692. return(0);
  1693. }
  1694. /*
  1695. ** Even then, if the difficulty indicates that it shouldn't search for wall
  1696. ** targets, then don't allow it to do so.
  1697. */
  1698. if (!Rule.Diff[House->Difficulty].IsWallDestroyer) {
  1699. BEnd(BENCH_EVAL_WALL);
  1700. return(0);
  1701. }
  1702. /*
  1703. ** Determine if, in fact, a wall is located at this cell location.
  1704. */
  1705. CellClass const * cellptr = &Map[cell];
  1706. if (cellptr->Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr->Overlay).IsWall) {
  1707. BEnd(BENCH_EVAL_WALL);
  1708. return(0);
  1709. }
  1710. /*
  1711. ** As a convenience to the target scanning logic, don't consider any wall to be
  1712. ** a target if it isn't in range of the primary weapon.
  1713. */
  1714. int primary = What_Weapon_Should_I_Use(::As_Target(cell));
  1715. if (!In_Range(Cell_Coord(cell), primary)) {
  1716. BEnd(BENCH_EVAL_WALL);
  1717. return(0);
  1718. }
  1719. /*
  1720. ** See if the object has a weapon that can damage walls.
  1721. */
  1722. TechnoTypeClass const * ttype = (TechnoTypeClass const *)Techno_Type_Class();
  1723. if (ttype->PrimaryWeapon == NULL || ttype->PrimaryWeapon->WarheadPtr == NULL) {
  1724. BEnd(BENCH_EVAL_WALL);
  1725. return(0);
  1726. }
  1727. /*
  1728. ** If the weapon cannot deal with ground based targets, then don't consider
  1729. ** this a valid cell target.
  1730. */
  1731. if (ttype->PrimaryWeapon->Bullet != NULL && !ttype->PrimaryWeapon->Bullet->IsAntiGround) {
  1732. BEnd(BENCH_EVAL_WALL);
  1733. return(0);
  1734. }
  1735. /*
  1736. ** If the primary weapon cannot destroy a wall, then don't give the cell any
  1737. ** value as a target.
  1738. */
  1739. if (!ttype->PrimaryWeapon->WarheadPtr->IsWallDestroyer) {
  1740. BEnd(BENCH_EVAL_WALL);
  1741. return(0);
  1742. }
  1743. /*
  1744. ** If this is a friendly wall, then don't attack it.
  1745. */
  1746. if (House->Is_Ally(cellptr->Owner)) {
  1747. BEnd(BENCH_EVAL_WALL);
  1748. return(0);
  1749. }
  1750. /*
  1751. ** Since a wall was found, then return a value adjusted according to the range the wall
  1752. ** is from the object. The greater the range, the lesser the value returned.
  1753. */
  1754. BEnd(BENCH_EVAL_WALL);
  1755. return(Weapon_Range(0) - Distance(Cell_Coord(cell)));
  1756. }
  1757. /***********************************************************************************************
  1758. * TechnoClass::Greatest_Threat -- Determines best target given search criteria. *
  1759. * *
  1760. * This routine will scan game objects looking for the best target. It is used by the *
  1761. * general target searching processes. The type of target scan to perform is controlled *
  1762. * by the method control parameter. *
  1763. * *
  1764. * INPUT: method -- The method control parameter is used to control the type of target *
  1765. * scan performed. It consists of a series of bit flags (see ThreatType) *
  1766. * that are combined to form the target scan desired. *
  1767. * *
  1768. * OUTPUT: Returns the target value of a suitable target. If no target was found then the *
  1769. * value TARGET_NONE is returned. *
  1770. * *
  1771. * WARNINGS: none *
  1772. * *
  1773. * HISTORY: *
  1774. * 11/14/1994 JLB : Created. *
  1775. * 06/20/1995 JLB : Greatly optimized scan method. *
  1776. * 09/22/1995 JLB : Takes into account the zone (if necessary). *
  1777. * 05/30/1996 JLB : Tighter elimination mask checking. *
  1778. *=============================================================================================*/
  1779. TARGET TechnoClass::Greatest_Threat(ThreatType method) const
  1780. {
  1781. assert(IsActive);
  1782. BStart(BENCH_GREATEST_THREAT);
  1783. ObjectClass const * bestobject = NULL;
  1784. int bestval = -1;
  1785. int zone = -1;
  1786. TargetScan++;
  1787. /*
  1788. ** Determine the zone that the target must be in. For aircraft and gunboats, they
  1789. ** ignore zones since they either can fly over any zone or are designed to fire into
  1790. ** other zones. If scanning for targets that are within range, then zone checking need
  1791. ** not be performed -- range checking is much more thorough and effective.
  1792. */
  1793. if (!(method & THREAT_RANGE) &&
  1794. What_Am_I() != RTTI_VESSEL &&
  1795. What_Am_I() != RTTI_BUILDING &&
  1796. What_Am_I() != RTTI_AIRCRAFT) {
  1797. zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone];
  1798. }
  1799. /*
  1800. ** Hack for dogs, 'cause they can only consider infantrymen to be a
  1801. ** threat. Medics also.
  1802. */
  1803. if (What_Am_I() == RTTI_INFANTRY) {
  1804. if (((InfantryClass *)this)->Class->IsDog || Combat_Damage() < 0) {
  1805. method = THREAT_INFANTRY | (method & (THREAT_RANGE | THREAT_AREA));
  1806. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1807. if(*(InfantryClass *)this == INFANTRY_MECHANIC) {
  1808. method = (THREAT_VEHICLES | THREAT_AIR) | (method & (THREAT_RANGE | THREAT_AREA));
  1809. }
  1810. #endif
  1811. }
  1812. }
  1813. /*
  1814. ** Build a quick elimination mask. If the RTTI of the object doesn't
  1815. ** qualify with this mask, then we KNOW that it shouldn't be considered.
  1816. */
  1817. int mask = 0;
  1818. if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT));
  1819. if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT);
  1820. if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING);
  1821. if (method & (THREAT_CIVILIANS|THREAT_BUILDINGS|THREAT_FACTORIES|THREAT_POWER|THREAT_FAKES|THREAT_BASE_DEFENSE|THREAT_TIBERIUM)) mask |= (1 << RTTI_BUILDING);
  1822. if (method & (THREAT_CIVILIANS|THREAT_INFANTRY|THREAT_BASE_DEFENSE)) mask |= (1 << RTTI_INFANTRY);
  1823. if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT);
  1824. if (method & THREAT_BASE_DEFENSE) mask |= (1 << RTTI_BUILDING);
  1825. if (method & THREAT_BOATS) mask |= (1 << RTTI_VESSEL);
  1826. /*
  1827. ** Limit area target scans use a method where the actual map cells are
  1828. ** examined for occupants. The occupant is then examined in turn. The
  1829. ** best target within the area is returned as a target.
  1830. */
  1831. if (method & (THREAT_AREA|THREAT_RANGE)) {
  1832. int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1);
  1833. int crange = range / ICON_LEPTON_W;
  1834. if (range == 0) {
  1835. crange = max(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W;
  1836. crange++;
  1837. }
  1838. CELL cell = Coord_Cell(Fire_Coord(0));
  1839. /*
  1840. ** BG: Miserable hack to get the stupid doctor to actually do area
  1841. ** guarding.
  1842. */
  1843. if (Combat_Damage() < 0) {
  1844. /*if (method & THREAT_AREA)*/ crange++;
  1845. }
  1846. /*
  1847. ** If aircraft are a legal target, then scan through all of them at this time.
  1848. ** Scanning by cell is not possible for aircraft since they are not recorded
  1849. ** at the cell level.
  1850. */
  1851. if (method & THREAT_AIR) {
  1852. for (int index = 0; index < Aircraft.Count(); index++) {
  1853. TechnoClass * object = Aircraft.Ptr(index);
  1854. int value = 0;
  1855. if (object->In_Which_Layer() != LAYER_GROUND && Evaluate_Object(method, mask, range, object, value)) {
  1856. if (value > bestval) {
  1857. bestobject = object;
  1858. bestval = value;
  1859. }
  1860. }
  1861. }
  1862. }
  1863. /*
  1864. ** When scanning the ground, always consider landed aircraft as a valid
  1865. ** potential target. This is only true if vehicles are considered a
  1866. ** valid target. A landed aircraft is considered a vehicle.
  1867. */
  1868. if (method & THREAT_VEHICLES) {
  1869. mask |= (1 << RTTI_AIRCRAFT);
  1870. }
  1871. /*
  1872. ** Radiate outward from the object's location, looking for the best
  1873. ** target.
  1874. */
  1875. CELL bestcell = -1;
  1876. int bestcellvalue = 0;
  1877. TechnoClass const * object;
  1878. int value;
  1879. // int rad = 1;
  1880. // BG: Medics need to be able to look in their own cell too.
  1881. // if (Combat_Damage() < 0 || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass*)this)->Class->IsDog)) {
  1882. // rad = 0;
  1883. // }
  1884. for (int radius = 0; radius < crange; radius++) {
  1885. /*
  1886. ** Scan the top and bottom rows of the "box".
  1887. */
  1888. for (int x = -radius; x <= radius; x++) {
  1889. CELL newcell;
  1890. if ((Cell_X(cell) + x) < Map.MapCellX) continue;
  1891. if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue;
  1892. if ((Cell_Y(cell) - radius) >= Map.MapCellY) {
  1893. newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius);
  1894. if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) {
  1895. if (bestval < value) {
  1896. bestobject = object;
  1897. }
  1898. }
  1899. if (bestobject == NULL) {
  1900. value = Evaluate_Just_Cell(newcell);
  1901. if (bestcellvalue < value) {
  1902. bestcellvalue = value;
  1903. bestcell = newcell;
  1904. }
  1905. }
  1906. }
  1907. if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) {
  1908. newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius);
  1909. if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) {
  1910. if (bestval < value) {
  1911. bestobject = object;
  1912. }
  1913. }
  1914. if (bestobject == NULL) {
  1915. value = Evaluate_Just_Cell(newcell);
  1916. if (bestcellvalue < value) {
  1917. bestcellvalue = value;
  1918. bestcell = newcell;
  1919. }
  1920. }
  1921. }
  1922. }
  1923. /*
  1924. ** Scan the left and right columns of the "box".
  1925. */
  1926. for (int y = -(radius-1); y < radius; y++) {
  1927. CELL newcell;
  1928. if ((Cell_Y(cell) + y) < Map.MapCellY) continue;
  1929. if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue;
  1930. if ((Cell_X(cell) - radius) >= Map.MapCellX) {
  1931. newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y);
  1932. if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) {
  1933. if (bestval < value) {
  1934. bestobject = object;
  1935. }
  1936. }
  1937. if (bestobject == NULL) {
  1938. value = Evaluate_Just_Cell(newcell);
  1939. if (bestcellvalue < value) {
  1940. bestcellvalue = value;
  1941. bestcell = newcell;
  1942. }
  1943. }
  1944. }
  1945. if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) {
  1946. newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y);
  1947. if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) {
  1948. if (bestval < value) {
  1949. bestobject = object;
  1950. }
  1951. }
  1952. if (bestobject == NULL) {
  1953. value = Evaluate_Just_Cell(newcell);
  1954. if (bestcellvalue < value) {
  1955. bestcellvalue = value;
  1956. bestcell = newcell;
  1957. }
  1958. }
  1959. }
  1960. }
  1961. /*
  1962. ** Bail early if a target has already been found and the range is at
  1963. ** one of the breaking points (i.e., normal range or range * 2).
  1964. */
  1965. if (bestobject != NULL) {
  1966. if (radius == crange/4) {
  1967. return(bestobject->As_Target());
  1968. }
  1969. if (radius == crange/2) {
  1970. return(bestobject->As_Target());
  1971. }
  1972. }
  1973. if (bestcell != -1) {
  1974. return(::As_Target(bestcell));
  1975. }
  1976. }
  1977. } else {
  1978. /*
  1979. ** A full map scan was requested. First scan through aircraft. The top map layer
  1980. ** is NOT scanned since that layer will probably contain more bullets and animations
  1981. ** than aircraft.
  1982. */
  1983. if (mask & (1L << RTTI_AIRCRAFT)) {
  1984. for (int index = 0; index < Aircraft.Count(); index++) {
  1985. TechnoClass * object = Aircraft.Ptr(index);
  1986. int value = 0;
  1987. if (Evaluate_Object(method, mask, -1, object, value)) {
  1988. if (value > bestval) {
  1989. bestobject = object;
  1990. bestval = value;
  1991. }
  1992. }
  1993. }
  1994. }
  1995. /*
  1996. ** When scanning the ground, always consider landed aircraft as a valid
  1997. ** potential target. This is only true if vehicles are considered a
  1998. ** valid target. A landed aircraft is considered a vehicle.
  1999. */
  2000. if (method & THREAT_VEHICLES) {
  2001. mask |= (1 << RTTI_AIRCRAFT);
  2002. }
  2003. /*
  2004. ** Now scan through the entire ground layer. This is painful, but what other
  2005. ** choice is there?
  2006. */
  2007. for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) {
  2008. ObjectClass const * object = Map.Layer[LAYER_GROUND][index];
  2009. int value = 0;
  2010. if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value, zone)) {
  2011. if (value > bestval) {
  2012. bestobject = object;
  2013. bestval = value;
  2014. }
  2015. }
  2016. }
  2017. }
  2018. BEnd(BENCH_GREATEST_THREAT);
  2019. /*
  2020. ** If a good target object was found, then return with the target value
  2021. ** of it.
  2022. */
  2023. if (bestobject != NULL) {
  2024. return(bestobject->As_Target());
  2025. }
  2026. return(TARGET_NONE);
  2027. }
  2028. /***********************************************************************************************
  2029. * TechnoClass::Owner -- Who is the owner of this object? *
  2030. * *
  2031. * Use this routine to examine this object and return who the owner is. *
  2032. * *
  2033. * INPUT: none *
  2034. * *
  2035. * OUTPUT: Returns with the house number of the owner of this object. *
  2036. * *
  2037. * WARNINGS: none *
  2038. * *
  2039. * HISTORY: *
  2040. * 12/09/1994 JLB : Created. *
  2041. *=============================================================================================*/
  2042. HousesType TechnoClass::Owner(void) const
  2043. {
  2044. assert(IsActive);
  2045. return(House->Class->House);
  2046. }
  2047. /***********************************************************************************************
  2048. * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. *
  2049. * *
  2050. * Use this routine to set the flash count for the object. This flash count is the number *
  2051. * of times the object will "flash". Typically it is called as a result of the player *
  2052. * clicking on this object in order to make it the target of a move or attack. *
  2053. * *
  2054. * INPUT: count -- The number of times the object should flash. *
  2055. * *
  2056. * OUTPUT: none *
  2057. * *
  2058. * WARNINGS: none *
  2059. * *
  2060. * HISTORY: *
  2061. * 12/09/1994 JLB : Created. *
  2062. *=============================================================================================*/
  2063. void TechnoClass::Clicked_As_Target(int count)
  2064. {
  2065. assert(IsActive);
  2066. FlashCount = count;
  2067. }
  2068. /***********************************************************************************************
  2069. * TechnoClass::AI -- Handles AI processing for techno object. *
  2070. * *
  2071. * This routine handles AI processing for techno objects. Typically, this merely dispatches *
  2072. * to the appropriate AI routines for the base classes. *
  2073. * *
  2074. * INPUT: none *
  2075. * *
  2076. * OUTPUT: none *
  2077. * *
  2078. * WARNINGS: Make sure that this routine is only called ONCE per game tick. *
  2079. * *
  2080. * HISTORY: *
  2081. * 12/09/1994 JLB : Created. *
  2082. *=============================================================================================*/
  2083. void TechnoClass::AI(void)
  2084. {
  2085. assert(IsActive);
  2086. /*
  2087. ** Handle recoil recovery here.
  2088. */
  2089. if (IsInRecoilState) {
  2090. IsInRecoilState = false;
  2091. Mark(MARK_CHANGE_REDRAW);
  2092. }
  2093. CargoClass::AI();
  2094. RadioClass::AI();
  2095. if (!IsActive || (Height > 0 && What_Am_I() != RTTI_AIRCRAFT)) return;
  2096. DoorClass::AI();
  2097. /*
  2098. ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform
  2099. ** the heal logic here.
  2100. */
  2101. if (Techno_Type_Class()->IsSelfHealing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0 && Health_Ratio() <= Rule.ConditionYellow) {
  2102. Strength++;
  2103. Mark(MARK_CHANGE);
  2104. }
  2105. /*
  2106. ** Cloaking device processing.
  2107. */
  2108. Cloaking_AI();
  2109. /*
  2110. ** If for some strange reason, the computer is firing upon itself, then
  2111. ** tell it not to.
  2112. */
  2113. if (!House->IsHuman && As_Techno(TarCom) && As_Techno(TarCom)->House->Is_Ally(this)) {
  2114. //#ifdef FIXIT_CSII // checked - ajw 9/28/98 (commented out)
  2115. //if(What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this==INFANTRY_GENERAL && Session.Type==GAME_NORMAL && House->Class->House==HOUSE_UKRAINE) {
  2116. //} else
  2117. //#endif
  2118. Assign_Target(TARGET_NONE);
  2119. }
  2120. /*
  2121. ** Perform a maintenance check to see that if somehow this object is trying to fire
  2122. ** upon an object it can never hit (because it can't reach it), then abort the tarcom
  2123. */
  2124. if (What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && (!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && (!Is_Foot() || !Is_In_Same_Zone(As_Cell(TarCom)))) {
  2125. int primary = What_Weapon_Should_I_Use(TarCom);
  2126. if (!In_Range(TarCom, primary)) {
  2127. Assign_Target(TARGET_NONE);
  2128. }
  2129. }
  2130. /*
  2131. ** Update the animation timer system. If the animation stage
  2132. ** changes, then flag the object to be redrawn as well as determine
  2133. ** if the current animation process needs to change.
  2134. */
  2135. if (What_Am_I() != RTTI_BUILDING) {
  2136. if (StageClass::About_To_Change()) {
  2137. Mark(MARK_CHANGE_REDRAW);
  2138. }
  2139. if (StageClass::Graphic_Logic() || Time_To_Redraw()) {
  2140. Mark(MARK_CHANGE_REDRAW);
  2141. }
  2142. }
  2143. /*
  2144. ** If the object is flashing and a change of flash state has occurred, then mark the
  2145. ** object to be redrawn.
  2146. */
  2147. if (FlasherClass::Process()) {
  2148. Mark(MARK_CHANGE);
  2149. }
  2150. }
  2151. /***********************************************************************************************
  2152. * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. *
  2153. * *
  2154. * This routine handles the cloaking device logic for this object. It will handle the *
  2155. * transition effects as the object cloaks or decloaks. It will also try to start an *
  2156. * object to cloak if possible. *
  2157. * *
  2158. * INPUT: none *
  2159. * *
  2160. * OUTPUT: none *
  2161. * *
  2162. * WARNINGS: none *
  2163. * *
  2164. * HISTORY: *
  2165. * 09/09/1996 JLB : Created. *
  2166. *=============================================================================================*/
  2167. void TechnoClass::Cloaking_AI(void)
  2168. {
  2169. /*
  2170. ** Handle decision to re-cloak here. Process the cloaking/decloaking operation.
  2171. */
  2172. if (IsCloakable) {
  2173. /*
  2174. ** If this object is uncloaked, but it can be cloaked and it thinks that it
  2175. ** is a good time do so, then begin cloaking.
  2176. */
  2177. if (Cloak == UNCLOAKED) {
  2178. #ifdef PREDATOR
  2179. if (IsOwnedByPlayer) Mark(MARK_CHANGE);
  2180. #endif
  2181. CloakingDevice.Graphic_Logic();
  2182. if (Is_Ready_To_Cloak()) {
  2183. if (Health_Ratio() > Rule.ConditionRed) {
  2184. Do_Cloak();
  2185. } else {
  2186. if (Percent_Chance(4)) {
  2187. Do_Cloak();
  2188. }
  2189. }
  2190. }
  2191. } else {
  2192. CloakingDevice.Graphic_Logic();
  2193. switch (Cloak) {
  2194. /*
  2195. ** Handle the uncloaking process. Always mark to redraw
  2196. ** the object and when cloaking is complete, stabilize into
  2197. ** the normal uncloaked state.
  2198. */
  2199. case UNCLOAKING:
  2200. Mark(MARK_CHANGE);
  2201. if (Visual_Character(true) == VISUAL_NORMAL) {
  2202. CloakingDevice.Set_Rate(0);
  2203. CloakingDevice.Set_Stage(0); // re-start the stage counter
  2204. Cloak = UNCLOAKED;
  2205. CloakDelay = Rule.CloakDelay * TICKS_PER_MINUTE;
  2206. Mark(MARK_CHANGE);
  2207. }
  2208. break;
  2209. /*
  2210. ** Handle the cloaking process. Always mark to redraw the object
  2211. ** and when the cloaking process is complete, stabilize into the
  2212. ** normal cloaked state.
  2213. */
  2214. case CLOAKING:
  2215. Mark(MARK_CHANGE);
  2216. if(!CloakingDevice.Fetch_Rate()) {
  2217. CloakingDevice.Set_Rate(1);
  2218. }
  2219. switch (Visual_Character(true)) {
  2220. /*
  2221. ** If badly damaged, then it can never fully cloak.
  2222. */
  2223. case VISUAL_DARKEN:
  2224. if (Health_Ratio() <= Rule.ConditionRed && Percent_Chance(25)) {
  2225. Cloak = UNCLOAKING;
  2226. }
  2227. break;
  2228. case VISUAL_HIDDEN:
  2229. Cloak = CLOAKED;
  2230. CloakingDevice.Set_Rate(0);
  2231. CloakingDevice.Set_Stage(0);
  2232. Mark(MARK_CHANGE);
  2233. Map[Center_Coord()].Redraw_Objects(true);
  2234. Map.RadarClass::Flag_To_Redraw(true);
  2235. /*
  2236. ** Special check to ensure that if the unit is carrying a captured
  2237. ** flag, it will never fully cloak.
  2238. */
  2239. if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) {
  2240. Do_Shimmer();
  2241. } else {
  2242. Detach_All(false);
  2243. }
  2244. /*
  2245. ** A computer controlled unit will try to scatter if possible so
  2246. ** that it will be much harder to locate.
  2247. */
  2248. if (What_Am_I() == RTTI_UNIT && !House->IsHuman) {
  2249. Scatter(0, true);
  2250. }
  2251. break;
  2252. }
  2253. break;
  2254. /*
  2255. ** A cloaked object will always be redrawn if it is owned by the
  2256. ** player. This ensures that the shimmering effect will animate.
  2257. */
  2258. case CLOAKED:
  2259. #ifdef PREDATOR
  2260. if (IsOwnedByPlayer) {
  2261. Mark(MARK_CHANGE);
  2262. }
  2263. #endif
  2264. break;
  2265. }
  2266. }
  2267. }
  2268. }
  2269. /***********************************************************************************************
  2270. * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. *
  2271. * *
  2272. * This routine will examine this object and determine if it can and is ready and able *
  2273. * to begin cloaking. It will also check to make sure it appears to be a good time to cloak *
  2274. * as well. *
  2275. * *
  2276. * INPUT: none *
  2277. * *
  2278. * OUTPUT: bool; Is this unit ready and able to start cloaking? *
  2279. * *
  2280. * WARNINGS: none *
  2281. * *
  2282. * HISTORY: *
  2283. * 09/09/1996 JLB : Created. *
  2284. *=============================================================================================*/
  2285. bool TechnoClass::Is_Ready_To_Cloak(void) const
  2286. {
  2287. /*
  2288. ** If it is already cloaked or in the process of cloaking, then it can't start cloaking.
  2289. */
  2290. if (Cloak == CLOAKED || (Cloak == CLOAKING && CloakingDevice.Fetch_Rate())) {
  2291. return(false);
  2292. }
  2293. /*
  2294. ** If the object cannot recloak, then it certainly is not allowed to start.
  2295. */
  2296. if (!IsCloakable || !Is_Allowed_To_Recloak()) {
  2297. return(false);
  2298. }
  2299. /*
  2300. ** If the object is currently rearming, then don't begin to recloak.
  2301. */
  2302. if (Arm != 0) {
  2303. return(false);
  2304. }
  2305. /*
  2306. ** If it seems like this object is about to fire on a target, then don't begin
  2307. ** cloaking either.
  2308. */
  2309. if (Target_Legal(TarCom) && In_Range(TarCom)) {
  2310. return(false);
  2311. }
  2312. /*
  2313. ** Recloaking can only begin if the cloaking device is not already operating.
  2314. */
  2315. if (CloakingDevice.Fetch_Stage() != 0) {
  2316. return(false);
  2317. }
  2318. /*
  2319. ** If the arbitrary cloak delay value is still counting down, then don't
  2320. ** allow recloaking just yet.
  2321. */
  2322. if (CloakDelay != 0) {
  2323. return(false);
  2324. }
  2325. /*
  2326. ** All tests passed, so this object is allowed to begin cloaking.
  2327. */
  2328. return(true);
  2329. }
  2330. /***********************************************************************************************
  2331. * TechnoClass::Select -- Selects object and checks to see if can be selected. *
  2332. * *
  2333. * This function checks to see if this techno object can be selected. If it can, then it *
  2334. * is selected. *
  2335. * *
  2336. * INPUT: none *
  2337. * *
  2338. * OUTPUT: none *
  2339. * *
  2340. * WARNINGS: none *
  2341. * *
  2342. * HISTORY: *
  2343. * 12/11/1994 JLB : Created. *
  2344. *=============================================================================================*/
  2345. bool TechnoClass::Select(void)
  2346. {
  2347. assert(IsActive);
  2348. if (!IsDiscoveredByPlayer && !House->IsPlayerControl && !Debug_Unshroud) {
  2349. return(false);
  2350. }
  2351. if (RadioClass::Select()) {
  2352. /*
  2353. ** Speak a confirmation of selection.
  2354. */
  2355. if (House->IsPlayerControl && AllowVoice) {
  2356. Response_Select();
  2357. }
  2358. return(true);
  2359. }
  2360. return(false);
  2361. }
  2362. /***********************************************************************************************
  2363. * TechnoClass::Can_Fire -- Determines if this techno object can fire. *
  2364. * *
  2365. * This performs a simple check to make sure that this techno object can fire. At this *
  2366. * level, the only thing checked for is the rearming delay. *
  2367. * *
  2368. * INPUT: none *
  2369. * *
  2370. * OUTPUT: Returns with the fire legality control code. *
  2371. * *
  2372. * WARNINGS: none *
  2373. * *
  2374. * HISTORY: *
  2375. * 12/23/1994 JLB : Created. *
  2376. *=============================================================================================*/
  2377. FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const
  2378. {
  2379. assert(IsActive);
  2380. /*
  2381. ** Don't allow firing if the target is illegal.
  2382. */
  2383. if (!Target_Legal(target)) {
  2384. return(FIRE_ILLEGAL);
  2385. }
  2386. ObjectClass * object = As_Object(target);
  2387. /*
  2388. ** If the object is completely cloaked, then you can't fire on it.
  2389. */
  2390. if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) {
  2391. return(FIRE_CANT);
  2392. }
  2393. /*
  2394. ** A falling object is too busy falling to fire.
  2395. */
  2396. if (IsFalling) {
  2397. return(FIRE_CANT);
  2398. }
  2399. /*
  2400. ** If there is no weapon, then firing is not allowed.
  2401. */
  2402. WeaponTypeClass const * weapon = ((which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon);
  2403. if (weapon == NULL) {
  2404. return(FIRE_CANT);
  2405. }
  2406. /*
  2407. ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is
  2408. ** sitting on the ground.
  2409. */
  2410. if (object != NULL && object->What_Am_I() == RTTI_AIRCRAFT &&
  2411. !weapon->Bullet->IsAntiAircraft &&
  2412. ((AircraftClass *)object)->Height > 0) {
  2413. return(FIRE_CANT);
  2414. }
  2415. /*
  2416. ** If the object is on the ground, then don't allow firing if it can't fire upon ground objects.
  2417. */
  2418. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2419. if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || (*((VesselClass*)object) != VESSEL_SS && *((VesselClass*)object) != VESSEL_MISSILESUB )) &&
  2420. #else
  2421. if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || *((VesselClass*)object) != VESSEL_SS) &&
  2422. #endif
  2423. !weapon->Bullet->IsAntiGround) {
  2424. return(FIRE_CANT);
  2425. }
  2426. if (Is_Target_Cell(target) && !weapon->Bullet->IsAntiGround) {
  2427. return(FIRE_CANT);
  2428. }
  2429. /*
  2430. ** Don't allow firing if still rearming.
  2431. */
  2432. if (Arm != 0) return(FIRE_REARM);
  2433. /*
  2434. ** The target must be within range in order to allow firing.
  2435. */
  2436. if (!In_Range(target, which)) {
  2437. return(FIRE_RANGE);
  2438. }
  2439. /*
  2440. ** If there is no ammo left, then it can't fire.
  2441. */
  2442. if (!Ammo) {
  2443. return(FIRE_AMMO);
  2444. }
  2445. /*
  2446. ** If cloaked, then firing is disabled.
  2447. */
  2448. if (Cloak != UNCLOAKED) {
  2449. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2450. // Special hack for John Archer's Hunt-The-Wumpus multiplayer mission... if
  2451. // the object firing is a cloaked civilian, don't require uncloaking before
  2452. // allowing firing.
  2453. if (What_Am_I()==RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCivilian ) {
  2454. return(FIRE_OK);
  2455. }
  2456. #endif
  2457. return(FIRE_CLOAKED);
  2458. }
  2459. return(FIRE_OK);
  2460. }
  2461. /***********************************************************************************************
  2462. * TechnoClass::Stun -- Prepares the object for removal from the game. *
  2463. * *
  2464. * This routine handles cleaning up this techno object from the game system so that when *
  2465. * it is subsequently removed, it doesn't leave any loose ends. *
  2466. * *
  2467. * INPUT: none *
  2468. * *
  2469. * OUTPUT: none *
  2470. * *
  2471. * WARNINGS: none *
  2472. * *
  2473. * HISTORY: *
  2474. * 12/23/1994 JLB : Created. *
  2475. *=============================================================================================*/
  2476. void TechnoClass::Stun(void)
  2477. {
  2478. assert(IsActive);
  2479. Assign_Target(TARGET_NONE);
  2480. Assign_Destination(TARGET_NONE);
  2481. Transmit_Message(RADIO_OVER_OUT);
  2482. Detach_All();
  2483. Unselect();
  2484. }
  2485. /***********************************************************************************************
  2486. * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. *
  2487. * *
  2488. * Use this routine to set the targeting computer for this object. It checks to make sure *
  2489. * that targeting of itself is prohibited. *
  2490. * *
  2491. * INPUT: target -- The target for this object to attack. *
  2492. * *
  2493. * OUTPUT: none *
  2494. * *
  2495. * WARNINGS: none *
  2496. * *
  2497. * HISTORY: *
  2498. * 12/23/1994 JLB : Created. *
  2499. *=============================================================================================*/
  2500. void TechnoClass::Assign_Target(TARGET target)
  2501. {
  2502. assert(IsActive);
  2503. if (target == TarCom) return;
  2504. if (!Target_Legal(target)) {
  2505. target = TARGET_NONE;
  2506. } else {
  2507. /*
  2508. ** Prevent targeting of self.
  2509. */
  2510. if (target == As_Target()) {
  2511. target = ::As_Target(Coord_Cell(Coord));
  2512. } else {
  2513. /*
  2514. ** Make sure that the target is not already dead.
  2515. */
  2516. ObjectClass * object = As_Object(target);
  2517. if (object != NULL && (object->IsActive == false || object->Strength == 0)) {
  2518. target = TARGET_NONE;
  2519. }
  2520. }
  2521. }
  2522. /*
  2523. ** Set the unit's targeting computer.
  2524. */
  2525. TarCom = target;
  2526. }
  2527. /***********************************************************************************************
  2528. * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. *
  2529. * *
  2530. * This function calculates the delay between shots. It determines this from the standard *
  2531. * rate of fire (ROF) of the base class and modifies it according to game speed and *
  2532. * whether this is the first or second shot. All single shot attackers consider their *
  2533. * shots to be "second" since the second shot is the one handled normally. The first shot *
  2534. * usually gets assigned a much shorter delay time before the next shot can fire. *
  2535. * *
  2536. * INPUT: second -- bool; Is this the second of a two shot salvo? *
  2537. * *
  2538. * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. *
  2539. * *
  2540. * WARNINGS: none *
  2541. * *
  2542. * HISTORY: *
  2543. * 12/26/1994 JLB : Created. *
  2544. *=============================================================================================*/
  2545. int TechnoClass::Rearm_Delay(bool second, int which) const
  2546. {
  2547. assert(IsActive);
  2548. if (What_Am_I() == RTTI_BUILDING && Ammo > 1) {
  2549. return(1);
  2550. }
  2551. WeaponTypeClass const * weapon = (which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon;
  2552. if (second && weapon != NULL) {
  2553. return(weapon->ROF * House->ROFBias);
  2554. }
  2555. return(3);
  2556. }
  2557. /***********************************************************************************************
  2558. * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. *
  2559. * *
  2560. * This routine is used to fire an electric zap at the target specified. *
  2561. * *
  2562. * INPUT: target -- The target to fire the zap at. *
  2563. * *
  2564. * which -- Which weapon is this zap associated with (0=primary, 1=secondary). *
  2565. * *
  2566. * source_coord -- The coordinate that the zap is to originate from. This is an *
  2567. * override value and if not specifide, the normal fire coordinate *
  2568. * is used. *
  2569. * *
  2570. * remap -- Pointer to the zap animation remap override table. If not specified *
  2571. * then the zap remains the normal blue white color. *
  2572. * *
  2573. * OUTPUT: bool; Does this object need to redraw? *
  2574. * *
  2575. * WARNINGS: none *
  2576. * *
  2577. * HISTORY: *
  2578. * 09/30/1996 BWG : Created. *
  2579. * 09/30/1996 JLB : Uses standard facing conversion and distance routines. *
  2580. *=============================================================================================*/
  2581. bool TechnoClass::Electric_Zap(TARGET target, int which, COORDINATE source_coord, unsigned char * remap)
  2582. {
  2583. int x,y,x1,y1;
  2584. COORDINATE source;
  2585. if (source_coord != 0) {
  2586. source = source_coord;
  2587. } else {
  2588. source = Fire_Coord(which);
  2589. }
  2590. COORDINATE dest = As_Coord(target);
  2591. if (What_Am_I() == RTTI_BUILDING) {
  2592. ((BuildingClass *)this)->IsCharging = false;
  2593. }
  2594. bool gonnadraw = false;
  2595. if (Map.Push_Onto_TacMap(source, dest) && SpecialDialog == SDLG_NONE) {
  2596. Map.Coord_To_Pixel(source, x, y);
  2597. Map.Coord_To_Pixel(dest, x1, y1);
  2598. x += Map.TacPixelX;
  2599. x1 += Map.TacPixelX;
  2600. y += Map.TacPixelY;
  2601. y1 += Map.TacPixelY;
  2602. Set_Logic_Page(SeenBuff);
  2603. gonnadraw = true;
  2604. }
  2605. static int _shape[]={ 2, 3, 1, 0, 2, 3, 1, 0};
  2606. static int _xadd[8][8]={
  2607. { 0, 8, 8, 8, 0, 0, 0, 0},
  2608. { 0, 8, 8, 8, 0, 0, 0, 0},
  2609. { 0, 8, 8, 8, 0, 0, 0, 0},
  2610. { 0, 8, 8, 8, 0, 0, 0, 0},
  2611. { 0, 8, 8, 8, 0, 0, 0, 0},
  2612. {-8, 0, 0, 0,-8,-8,-8,-8},
  2613. {-8, 0, 0, 0,-8,-8,-8,-8},
  2614. {-8, 0, 0, 0,-8,-8,-8,-8}
  2615. };
  2616. static int _yadd[8][8]={
  2617. {-8,-8,-8, 0, 0, 0,-8,-8},
  2618. {-8,-8,-8, 0, 0, 0,-8,-8},
  2619. { 0, 0, 0, 8, 8, 8, 0, 0},
  2620. { 0, 0, 0, 8, 8, 8, 0, 0},
  2621. { 0, 0, 0, 8, 8, 8, 0, 0},
  2622. { 0, 0, 0, 8, 8, 8, 0, 0},
  2623. { 0, 0, 0, 8, 8, 8, 0, 0},
  2624. {-8,-8,-8, 0, 0, 0,-8,-8}
  2625. };
  2626. int savex = x, savey = y;
  2627. if (gonnadraw) {
  2628. for (int shots = 0; shots < 3; shots++) {
  2629. x = savex;
  2630. y = savey;
  2631. int lastfacing = 0;
  2632. while (::Distance(x, y, x1, y1) > 8) {
  2633. /*
  2634. ** Determine true (0..7) facing from current position to
  2635. ** destination (actually the source coordinate of the zap).
  2636. */
  2637. int facing = Dir_Facing(Desired_Facing8(x, y, x1, y1));
  2638. /*
  2639. ** If there's quite a bit of distance to go,
  2640. ** we may vary the desired facing to give the
  2641. ** bolt some randomness.
  2642. */
  2643. if (::Distance(x, y, x1, y1) > 40) {
  2644. switch (Sim_Random_Pick(1, 3 + ((shots==0) ? 3 : 0))) {
  2645. case 1:
  2646. facing++;
  2647. break;
  2648. case 2:
  2649. facing--;
  2650. break;
  2651. default:
  2652. break;
  2653. }
  2654. facing &= 7;
  2655. }
  2656. /*
  2657. ** Now that we have the direction of the bolt,
  2658. ** draw it and move the x & y coords in the right
  2659. ** direction for the next piece.
  2660. */
  2661. x += _xadd[facing][lastfacing];
  2662. y += _yadd[facing][lastfacing];
  2663. if (remap != NULL) {
  2664. CC_Draw_Shape(LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, WINDOW_TACTICAL, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap);
  2665. } else {
  2666. CC_Draw_Shape(LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL);
  2667. }
  2668. lastfacing = facing;
  2669. }
  2670. }
  2671. }
  2672. return (gonnadraw);
  2673. }
  2674. /***********************************************************************************************
  2675. * TechnoClass::Fire_At -- Fires projectile at target specified. *
  2676. * *
  2677. * This is the main projectile firing code. Buildings, units, and infantry route fire *
  2678. * requests through this function. *
  2679. * *
  2680. * INPUT: target -- The target that the projectile is to be fired at. *
  2681. * *
  2682. * which -- Which weapon to fire. *
  2683. * *
  2684. * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile *
  2685. * could be created or there was some other illegality detected, the return value *
  2686. * will be NULL. *
  2687. * *
  2688. * WARNINGS: none *
  2689. * *
  2690. * HISTORY: *
  2691. * 12/26/1994 JLB : Created. *
  2692. * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. *
  2693. * 02/22/1996 JLB : Handles camera "weapon" case. *
  2694. *=============================================================================================*/
  2695. BulletClass * TechnoClass::Fire_At(TARGET target, int which)
  2696. {
  2697. assert(IsActive);
  2698. BulletClass * bullet; // Projectile.
  2699. DirType dir; // The facing to impart upon the projectile.
  2700. COORDINATE target_coord; // Coordinate of the target.
  2701. COORDINATE fire_coord; // Coordinate of firing position.
  2702. TechnoTypeClass const & tclass = *Techno_Type_Class();
  2703. ObjectClass * object;
  2704. WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon;
  2705. /*
  2706. ** If this object doesn't have a weapon, then it is obvious that firing
  2707. ** cannot ever succeed. Return with failure flag.
  2708. */
  2709. if (weapon == NULL) return(NULL);
  2710. BulletTypeClass const & btype = *weapon->Bullet;
  2711. /*
  2712. ** Perform a quick legality check to see if firing can occur.
  2713. */
  2714. if (Debug_Map || !Target_Legal(target)) {
  2715. return(NULL);
  2716. }
  2717. /*
  2718. ** Fetch the target coordinate for the target specified.
  2719. */
  2720. object = As_Object(target);
  2721. if (object != NULL) {
  2722. target_coord = object->Target_Coord();
  2723. } else {
  2724. target_coord = As_Coord(target);
  2725. }
  2726. /*
  2727. ** Get the location where the projectile should appear.
  2728. */
  2729. fire_coord = Fire_Coord(which);
  2730. /*
  2731. ** If the projectile is a homing type (such as a missile), then it will
  2732. ** launch in the direction the turret is facing, NOT necessarily the same
  2733. ** direction as the target.
  2734. */
  2735. if (btype.ROT != 0 || btype.IsDropping) {
  2736. dir = Fire_Direction();
  2737. if (btype.IsDropping) {
  2738. fire_coord = Center_Coord();
  2739. }
  2740. } else {
  2741. dir = ::Direction(fire_coord, target_coord);
  2742. }
  2743. /*
  2744. ** Create the projectile. Then process any special operations that
  2745. ** need to be performed according to the style of projectile
  2746. ** created.
  2747. */
  2748. int firepower = weapon->Attack;
  2749. if (firepower > 0) {
  2750. firepower = weapon->Attack * FirepowerBias * House->FirepowerBias;
  2751. }
  2752. /*
  2753. ** Give the bullet a boost of speed if the weapon indicates that this is required. Only
  2754. ** need to perform this check if the target is an aircraft.
  2755. */
  2756. int firespeed = weapon->MaxSpeed;
  2757. if (weapon->IsTurboBoosted && Is_Target_Aircraft(target)) {
  2758. firespeed *= Rule.TurboBoost;
  2759. }
  2760. bullet = new BulletClass(weapon->Bullet->Type, target, this, firepower, WarheadType(weapon->WarheadPtr->ID), firespeed);
  2761. if (bullet != NULL) {
  2762. /*
  2763. ** If this is firing from a moving platform, then the projectile is inaccurate.
  2764. */
  2765. if (Is_Foot() && ((FootClass const *)this)->IsDriving) {
  2766. bullet->IsInaccurate = true;
  2767. }
  2768. if (bullet->Unlimbo(fire_coord, dir)) {
  2769. } else {
  2770. delete bullet;
  2771. }
  2772. if (tclass.IsTurretEquipped) {
  2773. IsInRecoilState = true;
  2774. Mark(MARK_CHANGE_REDRAW);
  2775. }
  2776. Arm = Rearm_Delay(IsSecondShot, which);
  2777. if (tclass.Is_Two_Shooter()) {
  2778. IsSecondShot = (IsSecondShot == false);
  2779. }
  2780. /*
  2781. ** Perform any animation effect for this weapon.
  2782. */
  2783. AnimType a = weapon->Anim;
  2784. switch (a) {
  2785. case ANIM_GUN_N:
  2786. a = AnimType(a + Dir_Facing(Fire_Direction()));
  2787. break;
  2788. case ANIM_SAM_N:
  2789. a = AnimType(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current()));
  2790. break;
  2791. }
  2792. /*
  2793. ** Play any sound effect tied to this weapon type.
  2794. */
  2795. Sound_Effect(weapon->Sound, Fire_Coord(which));
  2796. /*
  2797. ** If there is a special firing animation, then create and attach it
  2798. ** now.
  2799. */
  2800. if (a != ANIM_NONE) {
  2801. AnimClass * anim = new AnimClass(a, Fire_Coord(which));
  2802. if (anim != NULL) {
  2803. anim->Attach_To(this);
  2804. }
  2805. }
  2806. /*
  2807. ** Electric zap animation.
  2808. */
  2809. if (weapon->IsElectric) {
  2810. bool gonnadraw = Electric_Zap(target, which);
  2811. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2812. if(What_Am_I() != RTTI_INFANTRY) {
  2813. Set_Stage(0);
  2814. Set_Rate(0);
  2815. }
  2816. #else
  2817. Set_Stage(0);
  2818. Set_Rate(0);
  2819. #endif
  2820. if (Ammo <= 1 && What_Am_I() == RTTI_BUILDING) {
  2821. ((BuildingClass *)this)->IsCharged = false;
  2822. }
  2823. TechnoClass * tech = As_Techno(target);
  2824. if (tech != NULL) {
  2825. tech->Clicked_As_Target(4);
  2826. }
  2827. Delay(1); // Make sure line is visible briefly
  2828. if (gonnadraw) {
  2829. Map.Flag_To_Redraw(true);
  2830. }
  2831. }
  2832. /*
  2833. ** Reduce ammunition for this object.
  2834. */
  2835. if (Ammo > 0) {
  2836. Ammo--;
  2837. }
  2838. /*
  2839. ** Firing will in all likelihood, require the unit to be redrawn. Flag it to be
  2840. ** redrawn here.
  2841. */
  2842. Mark(MARK_CHANGE);
  2843. /*
  2844. ** If a projectile was fired from a unit that is hidden in the darkness,
  2845. ** reveal that unit and a little area around it.
  2846. ** For multiplayer games, only reveal the unit if the target is the
  2847. ** local player.
  2848. */
  2849. if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || (!Map[Center_Coord()].IsMapped && (What_Am_I()!=RTTI_AIRCRAFT || !IsOwnedByPlayer)) ) {
  2850. if (Session.Type == GAME_NORMAL) {
  2851. Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false);
  2852. } else {
  2853. ObjectClass * obj = As_Object(target);
  2854. if (obj != NULL) {
  2855. HousesType tgt_owner = obj->Owner();
  2856. if (PlayerPtr->Class->House == tgt_owner) {
  2857. Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false);
  2858. }
  2859. }
  2860. }
  2861. }
  2862. }
  2863. return(bullet);
  2864. }
  2865. /***********************************************************************************************
  2866. * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. *
  2867. * *
  2868. * This routine is called when the mission for an object needs to change as a result of *
  2869. * player input. The basic operation would be to queue the event and let the action *
  2870. * occur at the frame dictated by the queuing system. However, if a voice response is *
  2871. * indicated, then perform it at this time. This will give a greater illusion of *
  2872. * immediate response. *
  2873. * *
  2874. * INPUT: mission -- The mission order to assign to this object. *
  2875. * *
  2876. * target -- The target of this object. This will be used for combat and attack. *
  2877. * *
  2878. * destination -- The movement destination for this object. *
  2879. * *
  2880. * OUTPUT: none *
  2881. * *
  2882. * WARNINGS: none *
  2883. * *
  2884. * HISTORY: *
  2885. * 05/22/1995 JLB : Created. *
  2886. *=============================================================================================*/
  2887. void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
  2888. {
  2889. assert(IsActive);
  2890. if (AllowVoice) {
  2891. if (mission == MISSION_ATTACK) {
  2892. Response_Attack();
  2893. } else {
  2894. Response_Move();
  2895. }
  2896. }
  2897. if (FormMove) {
  2898. Queue_Mission(TargetClass(this), mission, target, destination, FormSpeed, FormMaxSpeed);
  2899. } else {
  2900. /*
  2901. ** Cooerce the movement mission into a queued movement mission if the ALT key was
  2902. ** held down.
  2903. */
  2904. if (mission == MISSION_MOVE && (Keyboard->Down(Options.KeyQueueMove1) || Keyboard->Down(Options.KeyQueueMove2))) {
  2905. mission = MISSION_QMOVE;
  2906. }
  2907. Queue_Mission(TargetClass(this), mission, target, destination);
  2908. }
  2909. }
  2910. /***********************************************************************************************
  2911. * TechnoClass::What_Action -- Determines what action to perform if object is selected. *
  2912. * *
  2913. * This routine will examine the object specified and return with the action that will *
  2914. * be performed if the mouse button were clicked over the object. *
  2915. * *
  2916. * INPUT: object -- The object that the mouse button might be clicked on. *
  2917. * *
  2918. * OUTPUT: Returns with the action that will be performed if the object was clicked on. *
  2919. * *
  2920. * WARNINGS: none *
  2921. * *
  2922. * HISTORY: *
  2923. * 01/19/1995 JLB : Created. *
  2924. * 03/21/1995 JLB : Special target control for trees. *
  2925. *=============================================================================================*/
  2926. ActionType TechnoClass::What_Action(ObjectClass const * object) const
  2927. {
  2928. assert(IsActive);
  2929. if (object != NULL) {
  2930. /*
  2931. ** Return the ACTION_SELF flag if clicking on itself. However, if this
  2932. ** object cannot do anything special with itself, then just return with
  2933. ** the no action flag.
  2934. */
  2935. if (object == this && CurrentObject.Count() == 1 && House->IsPlayerControl) {
  2936. return(ACTION_SELF);
  2937. }
  2938. bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2));
  2939. bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2));
  2940. bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2));
  2941. /*
  2942. ** Special guard area mission is possible if both the control and the
  2943. ** alt keys are held down.
  2944. */
  2945. if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() /*KO && Can_Player_Fire()*/) {
  2946. // if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) {
  2947. return(ACTION_GUARD_AREA);
  2948. }
  2949. /*
  2950. ** Special override to force a move regardless of what is occupying the location.
  2951. */
  2952. if (altdown) {
  2953. if (House->IsPlayerControl && Can_Player_Move()) {
  2954. return(ACTION_MOVE);
  2955. }
  2956. }
  2957. /*
  2958. ** Override so that toggled select state can be performed while the <SHIFT> key
  2959. ** is held down.
  2960. */
  2961. if (shiftdown) {
  2962. if (House->IsPlayerControl && !IsALoaner) {
  2963. return(ACTION_TOGGLE_SELECT);
  2964. }
  2965. }
  2966. /*
  2967. ** If the weapon is blatantly disallowed from firing on the object specified, then
  2968. ** don't allow the attach check logic to proceed.
  2969. */
  2970. TechnoTypeClass const * ttype = Techno_Type_Class();
  2971. if (Is_Weapon_Equipped() &&
  2972. ttype->PrimaryWeapon->Bullet != NULL &&
  2973. ttype->PrimaryWeapon->Bullet->IsSubSurface &&
  2974. Map[object->Target_Coord()].Land_Type() != LAND_WATER) {
  2975. // Do nothing.
  2976. } else {
  2977. /*
  2978. ** If firing is possible and legal, then return this action potential.
  2979. */
  2980. if (House->IsPlayerControl && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Rule.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) {
  2981. if (Is_Weapon_Equipped() ||
  2982. (What_Am_I() == RTTI_INFANTRY &&
  2983. (((InfantryTypeClass const *)ttype)->IsBomber ||
  2984. ((InfantryTypeClass const *)ttype)->IsCapture)
  2985. )) {
  2986. int primary = What_Weapon_Should_I_Use(object->As_Target());
  2987. if (Can_Player_Move() || In_Range(object, primary)) {
  2988. if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable)) {
  2989. return(ACTION_ATTACK);
  2990. } else {
  2991. if (!Can_Player_Move()) {
  2992. return(ACTION_NONE);
  2993. } else {
  2994. return(ACTION_ATTACK);
  2995. }
  2996. }
  2997. }
  2998. }
  2999. }
  3000. }
  3001. /*
  3002. ** Possibly try to select the specified object, if that is warranted.
  3003. */
  3004. if (!Is_Weapon_Equipped() || !House->IsPlayerControl || object->Owner() == Owner()) {
  3005. if ((!IsALoaner || !IsOwnedByPlayer) && object->Class_Of().IsSelectable && !object->IsSelected) {
  3006. return(ACTION_SELECT);
  3007. }
  3008. return(ACTION_NONE);
  3009. }
  3010. }
  3011. return(ACTION_NONE);
  3012. }
  3013. /***********************************************************************************************
  3014. * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. *
  3015. * *
  3016. * Use this routine to determine what action will be performed if the specified cell *
  3017. * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action *
  3018. * nomove is used to perform special case checking for nearby cells if in fact the mouse *
  3019. * is clicked over the cell. *
  3020. * *
  3021. * INPUT: cell -- The cell to check for being clicked over. *
  3022. * *
  3023. * OUTPUT: Returns with the action that will occur if the cell is clicked on. *
  3024. * *
  3025. * WARNINGS: none *
  3026. * *
  3027. * HISTORY: *
  3028. * 01/19/1995 JLB : Created. *
  3029. * 07/10/1995 JLB : Force fire for buildings is explicitly disabled. *
  3030. *=============================================================================================*/
  3031. ActionType TechnoClass::What_Action(CELL cell) const
  3032. {
  3033. assert(IsActive);
  3034. CellClass const * cellptr = &Map[cell];
  3035. OverlayTypeClass const * optr = NULL;
  3036. bool ctrldown = Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2);
  3037. bool shiftdown = Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2);
  3038. bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2));
  3039. /*
  3040. ** Disable recognizing the <CTRL> key forced fire option when dealing with buildings.
  3041. */
  3042. if (What_Am_I() == RTTI_BUILDING) ctrldown = false;
  3043. /*
  3044. ** Disable recognizing the <CTRL> key forced fire option when dealing with submarines.
  3045. */
  3046. if(What_Am_I() == RTTI_VESSEL) {
  3047. WeaponTypeClass const * weapon = ((VesselClass *)this)->Class->PrimaryWeapon;
  3048. if (weapon && weapon->Bullet->IsSubSurface) ctrldown = false;
  3049. }
  3050. if (cellptr->Overlay != OVERLAY_NONE) {
  3051. optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  3052. }
  3053. /*
  3054. ** Special guard area mission is possible if both the control and the
  3055. ** alt keys are held down.
  3056. */
  3057. if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) {
  3058. return(ACTION_GUARD_AREA);
  3059. }
  3060. if (House->IsPlayerControl && Techno_Type_Class()->PrimaryWeapon != NULL && (ctrldown || (optr && optr->IsLegalTarget))) {
  3061. WarheadTypeClass const * whead = Techno_Type_Class()->PrimaryWeapon->WarheadPtr;
  3062. // To be fixed for firing on ore by accounting for ore and ignoring the overlay in that case.
  3063. if (optr == NULL || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) {
  3064. int primary = What_Weapon_Should_I_Use(::As_Target(cell));
  3065. if (Can_Player_Move() || In_Range(::As_Target(cell), primary)) {
  3066. return(ACTION_ATTACK);
  3067. }
  3068. }
  3069. }
  3070. if (House->IsPlayerControl && Can_Player_Move()) {
  3071. /*
  3072. ** Special override to force a move regardless of what is occupying the location.
  3073. */
  3074. if (shiftdown) {
  3075. return(ACTION_MOVE);
  3076. }
  3077. /*
  3078. ** If the object can enter the cell specified, then allow
  3079. ** movement to it.
  3080. */
  3081. if (Can_Enter_Cell(cell) <= MOVE_CLOAK) {
  3082. return(ACTION_MOVE);
  3083. }
  3084. return(ACTION_NOMOVE);
  3085. }
  3086. return(ACTION_NONE);
  3087. }
  3088. /***********************************************************************************************
  3089. * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. *
  3090. * *
  3091. * Use this routine to determine whether a movement order can be given to this object. *
  3092. * *
  3093. * INPUT: none *
  3094. * *
  3095. * OUTPUT: bool; Can this object be given a movement order by the player? *
  3096. * *
  3097. * WARNINGS: none *
  3098. * *
  3099. * HISTORY: *
  3100. * 01/19/1995 JLB : Created. *
  3101. *=============================================================================================*/
  3102. bool TechnoClass::Can_Player_Move(void) const
  3103. {
  3104. assert(IsActive);
  3105. return(House->IsPlayerControl);
  3106. }
  3107. /***********************************************************************************************
  3108. * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. *
  3109. * *
  3110. * Call this routine to determine if this object can be given a fire order by the player. *
  3111. * Such objects will affect the mouse cursor accordingly -- usually causes the targeting *
  3112. * cursor to appear. *
  3113. * *
  3114. * INPUT: none *
  3115. * *
  3116. * OUTPUT: bool; Can this object be given firing orders by the player? *
  3117. * *
  3118. * WARNINGS: none *
  3119. * *
  3120. * HISTORY: *
  3121. * 01/23/1995 JLB : Created. *
  3122. *=============================================================================================*/
  3123. bool TechnoClass::Can_Player_Fire(void) const
  3124. {
  3125. assert(IsActive);
  3126. if (House->IsPlayerControl && Is_Techno() && Techno_Type_Class()->PrimaryWeapon != NULL) {
  3127. return(true);
  3128. }
  3129. return(false);
  3130. }
  3131. /***********************************************************************************************
  3132. * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. *
  3133. * *
  3134. * Use this routine to determine if this object is equipped with a combat weapon. Such *
  3135. * determination is used by the AI system to gauge the threat potential of the object. *
  3136. * *
  3137. * INPUT: none *
  3138. * *
  3139. * OUTPUT: bool; Is this object equipped with a combat weapon? *
  3140. * *
  3141. * WARNINGS: none *
  3142. * *
  3143. * HISTORY: *
  3144. * 01/23/1995 JLB : Created. *
  3145. *=============================================================================================*/
  3146. bool TechnoClass::Is_Weapon_Equipped(void) const
  3147. {
  3148. assert(IsActive);
  3149. return(Techno_Type_Class()->PrimaryWeapon != NULL);
  3150. }
  3151. /***********************************************************************************************
  3152. * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. *
  3153. * *
  3154. * Use this routine to determine if the specified object is a candidate for repair. In *
  3155. * order to qualify, the object must be allowed to be repaired (in theory) and it must *
  3156. * be below full strength. If these conditions are met, then it can be repaired. *
  3157. * *
  3158. * INPUT: none *
  3159. * *
  3160. * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object *
  3161. * is not allowed to be repaired, or it might be full strength already. *
  3162. * *
  3163. * WARNINGS: none *
  3164. * *
  3165. * HISTORY: *
  3166. * 01/23/1995 JLB : Created. *
  3167. *=============================================================================================*/
  3168. bool TechnoClass::Can_Repair(void) const
  3169. {
  3170. assert(IsActive);
  3171. /*
  3172. ** Temporary hack to disable repair cursor over non-buildings.
  3173. */
  3174. if (What_Am_I() != RTTI_BUILDING) {
  3175. return(false);
  3176. }
  3177. return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength);
  3178. }
  3179. /***********************************************************************************************
  3180. * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. *
  3181. * *
  3182. * Use this routine to determine the maximum range for the weapon indicated. *
  3183. * *
  3184. * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. *
  3185. * *
  3186. * OUTPUT: Returns with the range of the weapon (in leptons). *
  3187. * *
  3188. * WARNINGS: none *
  3189. * *
  3190. * HISTORY: *
  3191. * 03/19/1995 JLB : Created. *
  3192. *=============================================================================================*/
  3193. int TechnoClass::Weapon_Range(int which) const
  3194. {
  3195. assert(IsActive);
  3196. assert((unsigned)which < 2);
  3197. WeaponTypeClass const * weapon = NULL;
  3198. TechnoTypeClass const & ttype = *Techno_Type_Class();
  3199. switch (which) {
  3200. case 0:
  3201. weapon = ttype.PrimaryWeapon;
  3202. break;
  3203. case 1:
  3204. weapon = ttype.SecondaryWeapon;
  3205. break;
  3206. }
  3207. if (weapon != NULL) {
  3208. return(weapon->Range);
  3209. }
  3210. return(0);
  3211. }
  3212. /***************************************************************************
  3213. * TechnoClass::Override_Mission -- temporarily overrides a units mission *
  3214. * *
  3215. * *
  3216. * *
  3217. * INPUT: MissionType mission - the mission we want to override *
  3218. * TARGET tarcom - the new target we want to override *
  3219. * TARGET navcom - the new navigation point to override*
  3220. * *
  3221. * OUTPUT: none *
  3222. * *
  3223. * WARNINGS: If a mission is already overridden, the current mission is *
  3224. * just re-assigned. *
  3225. * *
  3226. * HISTORY: *
  3227. * 04/28/1995 PWG : Created. *
  3228. *=========================================================================*/
  3229. void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom)
  3230. {
  3231. assert(IsActive);
  3232. SuspendedTarCom = TarCom;
  3233. RadioClass::Override_Mission(mission, tarcom, navcom);
  3234. Assign_Target(tarcom);
  3235. }
  3236. /***************************************************************************
  3237. * TechnoClass::Restore_Mission -- Restores an overridden mission *
  3238. * *
  3239. * INPUT: none *
  3240. * *
  3241. * OUTPUT: none *
  3242. * *
  3243. * WARNINGS: none *
  3244. * *
  3245. * HISTORY: *
  3246. * 04/28/1995 PWG : Created. *
  3247. *=========================================================================*/
  3248. bool TechnoClass::Restore_Mission(void)
  3249. {
  3250. assert(IsActive);
  3251. if (RadioClass::Restore_Mission()) {
  3252. Assign_Target(SuspendedTarCom);
  3253. return(true);
  3254. }
  3255. return(false);
  3256. }
  3257. /***********************************************************************************************
  3258. * TechnoClass::Renovate -- Heal a building to maximum *
  3259. * *
  3260. * *
  3261. * INPUT: none *
  3262. * *
  3263. * OUTPUT: none *
  3264. * *
  3265. * WARNINGS: none *
  3266. * *
  3267. * HISTORY: *
  3268. * 04/15/1996 BWG : Created. *
  3269. *=============================================================================================*/
  3270. void TechnoClass::Renovate(void)
  3271. {
  3272. assert(IsActive);
  3273. Mark(MARK_CHANGE);
  3274. Strength = Techno_Type_Class()->MaxStrength;
  3275. if (What_Am_I() == RTTI_BUILDING) {
  3276. ((BuildingClass *)this)->Repair(0);
  3277. }
  3278. }
  3279. /***********************************************************************************************
  3280. * TechnoClass::Captured -- Handles capturing this object. *
  3281. * *
  3282. * This routine is called when this object gets captured by the house specified. It handles *
  3283. * removing this object from any targeting computers and then changes the ownership of *
  3284. * the object to the new house. *
  3285. * *
  3286. * INPUT: newowner -- Pointer to the house that is now the new owner. *
  3287. * *
  3288. * OUTPUT: Was the object captured? Failure would mean that it is already under control of *
  3289. * the house specified. *
  3290. * *
  3291. * WARNINGS: none *
  3292. * *
  3293. * HISTORY: *
  3294. * 05/08/1995 JLB : Created. *
  3295. * 09/29/1995 JLB : Keeps track of quantity records. *
  3296. *=============================================================================================*/
  3297. bool TechnoClass::Captured(HouseClass * newowner)
  3298. {
  3299. assert(IsActive);
  3300. if (newowner != House) {
  3301. /*
  3302. ** Capture attempt springs any "entered" trigger. The entered trigger
  3303. ** occurs first since there may be a special trigger attached to this
  3304. ** object that flags a capture as a win and a destroy as a loss. This
  3305. ** order is necessary because the object is recorded as a kill as well.
  3306. */
  3307. if (Trigger.Is_Valid()) {
  3308. Trigger->Spring(TEVENT_PLAYER_ENTERED, this);
  3309. }
  3310. /*
  3311. ** Record this as a kill.
  3312. */
  3313. Record_The_Kill(NULL);
  3314. /*
  3315. ** Special kill record logic for capture process.
  3316. */
  3317. House->Tracking_Remove(this);
  3318. newowner->Tracking_Add(this);
  3319. switch (What_Am_I()) {
  3320. case RTTI_BUILDING:
  3321. newowner->BuildingsKilled[Owner()]++;
  3322. break;
  3323. case RTTI_AIRCRAFT:
  3324. case RTTI_INFANTRY:
  3325. case RTTI_UNIT:
  3326. case RTTI_VESSEL:
  3327. newowner->UnitsKilled[Owner()]++;
  3328. break;
  3329. default:
  3330. break;
  3331. }
  3332. House->WhoLastHurtMe = newowner->Class->House;
  3333. /*
  3334. ** Remove from targeting computers.
  3335. */
  3336. Detach_All(false);
  3337. /*
  3338. ** Change ownership now.
  3339. */
  3340. House = newowner;
  3341. IsOwnedByPlayer = (House == PlayerPtr);
  3342. return(true);
  3343. }
  3344. return(false);
  3345. }
  3346. /***********************************************************************************************
  3347. * TechnoClass::Take_Damage -- Records damage assessed to this object. *
  3348. * *
  3349. * This routine is called when this object has taken damage. It handles recording whether *
  3350. * this object has been destroyed. If it has, then mark the appropriate kill records as *
  3351. * necessary. *
  3352. * *
  3353. * INPUT: *
  3354. * *
  3355. * OUTPUT: *
  3356. * *
  3357. * WARNINGS: *
  3358. * *
  3359. * HISTORY: *
  3360. * 06/20/1995 JLB : Created. *
  3361. *=============================================================================================*/
  3362. ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced)
  3363. {
  3364. assert(IsActive);
  3365. ResultType result = RESULT_NONE;
  3366. /*
  3367. ** If not a forced damage condition, adjust damage according to house override armor
  3368. ** value.
  3369. */
  3370. if (!forced && damage > 0) {
  3371. damage = damage * ArmorBias * House->ArmorBias;
  3372. }
  3373. if (IronCurtainCountDown == 0) {
  3374. result = ObjectClass::Take_Damage(damage, distance, warhead, source, forced);
  3375. }
  3376. switch (result) {
  3377. case RESULT_DESTROYED:
  3378. Transmit_Message(RADIO_OVER_OUT);
  3379. Stun();
  3380. /*
  3381. ** If this object explodes with violent damage, then perform the explosion
  3382. ** now and use the warhead type and full strength as the explosion values.
  3383. */
  3384. if (Techno_Type_Class()->IsExploding) {
  3385. /*
  3386. ** The warhead to use is based on the weapon this object is equipped with.
  3387. */
  3388. WarheadType wh = WARHEAD_HE;
  3389. if (Techno_Type_Class()->PrimaryWeapon != NULL) {
  3390. wh = WarheadType(Techno_Type_Class()->PrimaryWeapon->WarheadPtr->ID);
  3391. }
  3392. int damage = Techno_Type_Class()->MaxStrength;
  3393. new AnimClass(Combat_Anim(damage, wh, Map[Center_Coord()].Land_Type()), Center_Coord());
  3394. int radius = damage * Rule.ExplosionSpread;
  3395. // int radius = damage/2;
  3396. Wide_Area_Damage(Center_Coord(), radius, damage, source, wh);
  3397. }
  3398. if (this == (TechnoClass *)::As_Object(House->UnitToTeleport)) {
  3399. House->UnitToTeleport = 0;
  3400. if (!Scen.IsFadingColor) {
  3401. Scen.IsFadingBW = false;
  3402. Scen.IsFadingColor = true;
  3403. Scen.FadeTimer = GRAYFADETIME;
  3404. }
  3405. if (Map.IsTargettingMode == SPC_CHRONO2) {
  3406. KeyNumType input = KN_RMOUSE;
  3407. Map.AI(input, 0, 0);
  3408. }
  3409. }
  3410. break;
  3411. /*
  3412. ** If some damage was received and this object is cloaked, shimmer
  3413. ** the cloak a bit.
  3414. */
  3415. default:
  3416. if (source != NULL && !House->Is_Ally(source)) {
  3417. IsTickedOff = true;
  3418. }
  3419. Do_Shimmer();
  3420. break;
  3421. case RESULT_NONE:
  3422. break;
  3423. }
  3424. return(result);
  3425. }
  3426. /***********************************************************************************************
  3427. * TechnoClass::Record_The_Kill -- Records the death of this object. *
  3428. * *
  3429. * This routine is used to record the death of this object. It will handle updating the *
  3430. * owner house with the kill record as well as springing any trigger events associated with *
  3431. * this object's death. *
  3432. * *
  3433. * INPUT: source -- Pointer to the source of this object's death (if there is a source). *
  3434. * *
  3435. * OUTPUT: none *
  3436. * *
  3437. * WARNINGS: none *
  3438. * *
  3439. * HISTORY: *
  3440. * 07/08/1995 JLB : Created. *
  3441. * 08/23/1995 JLB : Building loss is only counted if it received damage. *
  3442. *=============================================================================================*/
  3443. void TechnoClass::Record_The_Kill(TechnoClass * source)
  3444. {
  3445. assert(IsActive);
  3446. int total_recorded = 0;
  3447. int points = Techno_Type_Class()->Points;
  3448. /*
  3449. ** Handle any trigger event associated with this object.
  3450. */
  3451. if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_ATTACKED, this);
  3452. if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_DISCOVERED, this);
  3453. if (Trigger.Is_Valid()) Trigger->Spring(TEVENT_DESTROYED, this);
  3454. if (source != NULL) {
  3455. Crew.Made_A_Kill();
  3456. House->WhoLastHurtMe = source->Owner();
  3457. /*
  3458. ** Add up the score for killing this unit
  3459. */
  3460. source->House->PointTotal += points;
  3461. }
  3462. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3463. // Hack check: if they were trying to teleport this unit when it died, take
  3464. // the map mode out of teleportation mode.
  3465. if(IsOwnedByPlayer && Map.IsTargettingMode == SPC_CHRONO2 && House->UnitToTeleport == As_Target()) {
  3466. Map.IsTargettingMode = SPC_NONE;
  3467. }
  3468. #endif
  3469. switch (What_Am_I()) {
  3470. case RTTI_BUILDING:
  3471. {
  3472. StructType bldg = *(BuildingClass *)this;
  3473. if (bldg != STRUCT_BARREL && bldg != STRUCT_BARREL3 &&
  3474. bldg != STRUCT_APMINE && bldg != STRUCT_AVMINE) {
  3475. if (((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) {
  3476. House->BuildingsLost++;
  3477. }
  3478. if (source != NULL) {
  3479. if (Session.Type == GAME_INTERNET) {
  3480. source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type );
  3481. }
  3482. source->House->BuildingsKilled[Owner()]++;
  3483. }
  3484. /*
  3485. ** If the map is displaying the multiplayer player names & their
  3486. ** # of kills, tell it to redraw.
  3487. */
  3488. if (Map.Is_Player_Names()) {
  3489. Map.Player_Names(true);
  3490. }
  3491. }
  3492. }
  3493. break;
  3494. case RTTI_AIRCRAFT:
  3495. if (source != NULL && Session.Type == GAME_INTERNET) {
  3496. source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type );
  3497. total_recorded++;
  3498. }
  3499. //Fall through.....
  3500. case RTTI_INFANTRY:
  3501. if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) {
  3502. source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type );
  3503. total_recorded++;
  3504. }
  3505. //Fall through.....
  3506. case RTTI_UNIT:
  3507. if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) {
  3508. source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type );
  3509. total_recorded++;
  3510. }
  3511. //Fall through.....
  3512. case RTTI_VESSEL:
  3513. if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) {
  3514. source->House->DestroyedUnits->Increment_Unit_Total( ((VesselClass*)this)->Class->Type );
  3515. }
  3516. House->UnitsLost++;
  3517. if (source != NULL) source->House->UnitsKilled[Owner()]++;
  3518. /*
  3519. ** If the map is displaying the multiplayer player names & their
  3520. ** # of kills, tell it to redraw.
  3521. */
  3522. if (Map.Is_Player_Names()) {
  3523. Map.Player_Names(true);
  3524. }
  3525. break;
  3526. default:
  3527. break;
  3528. }
  3529. /*
  3530. ** Since we lost an object, we lose the associated points as well.
  3531. */
  3532. House->PointTotal -= points;
  3533. }
  3534. /***********************************************************************************************
  3535. * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. *
  3536. * *
  3537. * This routine is used to find a nearby location from center of this object. It can lean *
  3538. * toward finding a location closest to an optional object. *
  3539. * *
  3540. * INPUT: object -- Optional object that the finding algorithm will try to find a close *
  3541. * spot to. *
  3542. * *
  3543. * OUTPUT: Returns with the cell that is closest to this object. *
  3544. * *
  3545. * WARNINGS: none *
  3546. * *
  3547. * HISTORY: *
  3548. * 07/06/1995 JLB : Created. *
  3549. * 09/28/1995 JLB : Uses map scan function. *
  3550. *=============================================================================================*/
  3551. CELL TechnoClass::Nearby_Location(TechnoClass const * techno) const
  3552. {
  3553. assert(IsActive);
  3554. SpeedType speed = Techno_Type_Class()->Speed;
  3555. if (speed == SPEED_WINGED) {
  3556. speed = SPEED_TRACK;
  3557. }
  3558. CELL cell = 0;
  3559. if (techno != NULL) {
  3560. cell = Coord_Cell(techno->Center_Coord());
  3561. } else {
  3562. cell = Coord_Cell(Center_Coord());
  3563. }
  3564. return(Map.Nearby_Location(cell, speed, Map[cell].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone));
  3565. }
  3566. /***********************************************************************************************
  3567. * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. *
  3568. * *
  3569. * This routine will start the stealth tank to uncloak. *
  3570. * *
  3571. * INPUT: none *
  3572. * *
  3573. * OUTPUT: none *
  3574. * *
  3575. * WARNINGS: none *
  3576. * *
  3577. * HISTORY: *
  3578. * 05/08/1995 JLB : Created. *
  3579. *=============================================================================================*/
  3580. void TechnoClass::Do_Uncloak(void)
  3581. {
  3582. assert(IsActive);
  3583. if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) {
  3584. if (Cloak == CLOAKED) {
  3585. Map.RadarClass::Flag_To_Redraw(true);
  3586. }
  3587. Cloak = UNCLOAKING;
  3588. CloakingDevice.Set_Stage(0);
  3589. CloakingDevice.Set_Rate(1);
  3590. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3591. if(What_Am_I() == RTTI_VESSEL) {
  3592. Sound_Effect(VOC_SUBSHOW, Coord);
  3593. } else {
  3594. Sound_Effect(VOC_IRON1, Coord);
  3595. }
  3596. #else
  3597. Sound_Effect(VOC_SUBSHOW, Coord);
  3598. #endif
  3599. }
  3600. }
  3601. /***********************************************************************************************
  3602. * TechnoClass::Do_Cloak -- Start the object into cloaking stage. *
  3603. * *
  3604. * This routine will start the object into its cloaking state. *
  3605. * *
  3606. * INPUT: none *
  3607. * *
  3608. * OUTPUT: none *
  3609. * *
  3610. * WARNINGS: none *
  3611. * *
  3612. * HISTORY: *
  3613. * 07/08/1995 JLB : Created. *
  3614. *=============================================================================================*/
  3615. void TechnoClass::Do_Cloak(void)
  3616. {
  3617. assert(IsActive);
  3618. if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) {
  3619. Detach_All(false);
  3620. if (Cloak == UNCLOAKED) {
  3621. Map.RadarClass::Flag_To_Redraw(true);
  3622. }
  3623. Cloak = CLOAKING;
  3624. CloakingDevice.Set_Stage(0);
  3625. CloakingDevice.Set_Rate(1);
  3626. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  3627. if(What_Am_I() == RTTI_VESSEL) {
  3628. Sound_Effect(VOC_SUBSHOW, Coord);
  3629. } else {
  3630. Sound_Effect(VOC_IRON1, Coord);
  3631. }
  3632. #else
  3633. Sound_Effect(VOC_SUBSHOW, Coord);
  3634. #endif
  3635. }
  3636. }
  3637. /***********************************************************************************************
  3638. * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. *
  3639. * *
  3640. * This routine is called when this object should shimmer. If the object is cloaked, then *
  3641. * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no *
  3642. * effect occurs. *
  3643. * *
  3644. * INPUT: none *
  3645. * *
  3646. * OUTPUT: none *
  3647. * *
  3648. * WARNINGS: none *
  3649. * *
  3650. * HISTORY: *
  3651. * 07/29/1995 JLB : Created. *
  3652. *=============================================================================================*/
  3653. void TechnoClass::Do_Shimmer(void)
  3654. {
  3655. assert(IsActive);
  3656. #if(0)
  3657. if (IsCloakable && Cloak == CLOAKED) {
  3658. Cloak = CLOAKING;
  3659. CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2);
  3660. CloakingDevice.Set_Rate(1);
  3661. }
  3662. #else
  3663. Do_Uncloak();
  3664. #endif
  3665. }
  3666. /***********************************************************************************************
  3667. * TechnoClass::Visual_Character -- Determine the visual character of the object. *
  3668. * *
  3669. * This routine will determine how this object should be drawn. Typically, this is the *
  3670. * unmodified visible state, but cloaked objects have a different character. *
  3671. * *
  3672. * INPUT: raw -- Should the check be based on the unmodified cloak condition of the *
  3673. * object? If false, then an object owned by the player will never become *
  3674. * completely invisible. *
  3675. * *
  3676. * OUTPUT: Returns with the visual character to use when displaying this object. *
  3677. * *
  3678. * WARNINGS: none *
  3679. * *
  3680. * HISTORY: *
  3681. * 07/07/1995 JLB : Created. *
  3682. * 05/27/1996 JLB : Knows about invisible objects. *
  3683. *=============================================================================================*/
  3684. VisualType TechnoClass::Visual_Character(bool raw) const
  3685. {
  3686. assert(IsActive);
  3687. if (Techno_Type_Class()->IsInvisible && IsOwnedByPlayer) return(VISUAL_NORMAL);
  3688. if (Techno_Type_Class()->IsInvisible && !IsOwnedByPlayer && !Debug_Map) return(VISUAL_HIDDEN);
  3689. /*
  3690. ** When uncloaked or in map editor mode, always draw the object normally.
  3691. */
  3692. if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL);
  3693. /*
  3694. ** A cloaked unit will not be visible at all unless it is owned
  3695. ** by the player.
  3696. */
  3697. if (Cloak == CLOAKED) {
  3698. if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY);
  3699. return(VISUAL_HIDDEN);
  3700. }
  3701. int stage = CloakingDevice.Fetch_Stage();
  3702. if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage;
  3703. if (stage <= 0) {
  3704. return(VISUAL_NORMAL);
  3705. }
  3706. stage = fixed(stage, MAX_UNCLOAK_STAGE) * 256;
  3707. if (stage < 0x0040) return(VISUAL_INDISTINCT);
  3708. if (stage < 0x0080) return(VISUAL_DARKEN);
  3709. if (stage < 0x00C0) return(VISUAL_SHADOWY);
  3710. if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY);
  3711. if (stage < 0x00FF) return(VISUAL_RIPPLE);
  3712. return(VISUAL_HIDDEN);
  3713. }
  3714. /***********************************************************************************************
  3715. * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. *
  3716. * *
  3717. * This routine is used to draw the object. It will handle any remapping or cloaking *
  3718. * effects required. This logic is isolated here since all techno object share the same *
  3719. * render logic when it comes to remapping and cloaking. *
  3720. * *
  3721. * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. *
  3722. * *
  3723. * shapenum -- The shape number of the object in the file to use. *
  3724. * *
  3725. * x,y -- Center pixel coordinate to use for rendering this object. *
  3726. * *
  3727. * window -- The clipping window to use when rendering. *
  3728. * *
  3729. * rotation -- The rotation of the object. *
  3730. * *
  3731. * scale -- The scaling factor to use (24.8 fixed point). *
  3732. * *
  3733. * OUTPUT: none *
  3734. * *
  3735. * WARNINGS: none *
  3736. * *
  3737. * HISTORY: *
  3738. * 07/08/1995 JLB : Created. *
  3739. * 01/11/1996 JLB : Added rotation and scaling. *
  3740. *=============================================================================================*/
  3741. void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const
  3742. {
  3743. assert(IsActive);
  3744. if (shapefile != NULL) {
  3745. VisualType visual = Visual_Character();
  3746. void const * remap = Remap_Table();
  3747. void const * shadow = Map.UnitShadow;
  3748. /*
  3749. ** Create a minimum shape rectangle if one hasn't already been
  3750. ** calculated and the shape file matches the one that the
  3751. ** class thinks it should be using. This check is necessary because
  3752. ** the dimension rectangle pointer is referenced from the type class
  3753. ** object on the presumption that the shapefile pointer passed to this
  3754. ** routine matches. If it doesn't match, then the wrong rectangle information
  3755. ** will be stored into the type class object.
  3756. */
  3757. TechnoTypeClass * ttype = Techno_Type_Class();
  3758. if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) {
  3759. if (ttype->DimensionData == NULL) {
  3760. ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)];
  3761. }
  3762. if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) {
  3763. ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum);
  3764. }
  3765. }
  3766. if (Height > 0) {
  3767. shadow = Map.UnitShadowAir;
  3768. }
  3769. y -= Lepton_To_Pixel(Height);
  3770. /*
  3771. ** If they're viewing a spy, and the spy belongs to some other house,
  3772. ** make it look like an infantryman from our house
  3773. */
  3774. if (What_Am_I() == RTTI_INFANTRY) {
  3775. if (!IsOwnedByPlayer) {
  3776. if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table();
  3777. }
  3778. if (((InfantryClass *)this)->Class->IsRemapOverride) {
  3779. remap = ((InfantryClass *)this)->Class->OverrideRemap;
  3780. }
  3781. }
  3782. /*
  3783. ** Check for the special visual effect for the iron curtain
  3784. */
  3785. if (IronCurtainCountDown > 0) {
  3786. // remap = RemapEmber;
  3787. remap = DisplayClass::FadingRed;
  3788. }
  3789. #ifdef PREDATOR
  3790. if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) {
  3791. if (visual == VISUAL_SHADOWY) {
  3792. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale);
  3793. } else {
  3794. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale);
  3795. }
  3796. if (visual == VISUAL_DARKEN) {
  3797. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale);
  3798. }
  3799. }
  3800. if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) {
  3801. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale);
  3802. }
  3803. #else
  3804. switch (visual) {
  3805. case VISUAL_NORMAL:
  3806. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale);
  3807. break;
  3808. case VISUAL_INDISTINCT:
  3809. case VISUAL_DARKEN:
  3810. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale);
  3811. break;
  3812. case VISUAL_SHADOWY:
  3813. case VISUAL_RIPPLE:
  3814. CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale);
  3815. break;
  3816. case VISUAL_HIDDEN:
  3817. break;
  3818. }
  3819. #endif
  3820. }
  3821. }
  3822. /***********************************************************************************************
  3823. * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. *
  3824. * *
  3825. * This routine is used to fetch the appropriate remap table to use for this object. *
  3826. * *
  3827. * INPUT: none *
  3828. * *
  3829. * OUTPUT: Returns with a pointer to the remap table to use for this object. *
  3830. * *
  3831. * WARNINGS: none *
  3832. * *
  3833. * HISTORY: *
  3834. * 07/08/1995 JLB : Created. *
  3835. *=============================================================================================*/
  3836. void const * TechnoClass::Remap_Table(void) const
  3837. {
  3838. assert(IsActive);
  3839. if (Techno_Type_Class()->IsRemappable) {
  3840. return(House->Remap_Table(IsBlushing, Techno_Type_Class()->Remap));
  3841. }
  3842. return(ColorRemaps[PCOLOR_GOLD].RemapTable);
  3843. }
  3844. /***********************************************************************************************
  3845. * TechnoClass::Detach -- Handles removal of target from tracking system. *
  3846. * *
  3847. * This routine is called when the specified object is about to be removed from the game *
  3848. * system. The target object is removed from any tracking computers that this object may *
  3849. * have. *
  3850. * *
  3851. * INPUT: target -- The target object (as a target value) that is being removed from the *
  3852. * game. *
  3853. * *
  3854. * all -- Is the target about to die? A false value might indicate that the *
  3855. * object is merely cloaking. In such a case, radio contact will not *
  3856. * be affected. *
  3857. * *
  3858. * OUTPUT: none *
  3859. * *
  3860. * WARNINGS: none *
  3861. * *
  3862. * HISTORY: *
  3863. * 07/29/1995 JLB : Created. *
  3864. *=============================================================================================*/
  3865. void TechnoClass::Detach(TARGET target, bool all)
  3866. {
  3867. assert(IsActive);
  3868. RadioClass::Detach(target, all);
  3869. if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) {
  3870. SuspendedMission = MISSION_NONE;
  3871. SuspendedTarCom = TARGET_NONE;
  3872. }
  3873. /*
  3874. ** If the targeting computer is assigned to the target, then the targeting
  3875. ** computer must be cleared.
  3876. */
  3877. if (TarCom == target) {
  3878. Assign_Target(TARGET_NONE);
  3879. Restore_Mission();
  3880. }
  3881. /*
  3882. ** If it is in radio contact with another object, then that radio contact
  3883. ** must be broken.
  3884. */
  3885. if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) {
  3886. Transmit_Message(RADIO_OVER_OUT);
  3887. }
  3888. }
  3889. /***********************************************************************************************
  3890. * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. *
  3891. * *
  3892. * This routine handles the destruction of any cargo this object may contain. Typical of *
  3893. * this would be when a transport helicopter gets destroyed. *
  3894. * *
  3895. * INPUT: source -- The source of the destruction of the cargo. *
  3896. * *
  3897. * OUTPUT: none *
  3898. * *
  3899. * WARNINGS: none *
  3900. * *
  3901. * HISTORY: *
  3902. * 07/29/1995 JLB : Created. *
  3903. *=============================================================================================*/
  3904. void TechnoClass::Kill_Cargo(TechnoClass * source)
  3905. {
  3906. assert(IsActive);
  3907. while (Is_Something_Attached()) {
  3908. FootClass * foot = Detach_Object();
  3909. if (foot != NULL) {
  3910. foot->Record_The_Kill(source);
  3911. delete foot;
  3912. }
  3913. }
  3914. }
  3915. /***********************************************************************************************
  3916. * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. *
  3917. * *
  3918. * This routine is called when generating survivors to this object. This routine returns *
  3919. * the type of survivor to generate. *
  3920. * *
  3921. * INPUT: none *
  3922. * *
  3923. * OUTPUT: Returns the infantry type of a survivor. *
  3924. * *
  3925. * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to *
  3926. * generate. *
  3927. * *
  3928. * HISTORY: *
  3929. * 07/29/1995 JLB : Created. *
  3930. *=============================================================================================*/
  3931. InfantryType TechnoClass::Crew_Type(void) const
  3932. {
  3933. assert(IsActive);
  3934. /*
  3935. ** If this object contains no crew, then there can be no
  3936. ** crew inside, duh... return this news.
  3937. */
  3938. if (!Techno_Type_Class()->IsCrew) {
  3939. return(INFANTRY_NONE);
  3940. }
  3941. /*
  3942. ** The normal infantry survivor is the standard issue
  3943. ** minigunner. Certain buildings, especially neutral ones, tend to have
  3944. ** civilians exit them instead.
  3945. */
  3946. InfantryType infantry = INFANTRY_E1;
  3947. if (House->ActLike == HOUSE_NEUTRAL) {
  3948. infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9);
  3949. } else {
  3950. if (Techno_Type_Class()->PrimaryWeapon == NULL && Percent_Chance(15)) {
  3951. if (Percent_Chance(50)) {
  3952. infantry = INFANTRY_C1;
  3953. } else {
  3954. infantry = INFANTRY_C7;
  3955. }
  3956. }
  3957. }
  3958. return(infantry);
  3959. }
  3960. /***********************************************************************************************
  3961. * TechnoClass::Value -- Fetches the target value for this object. *
  3962. * *
  3963. * This routine is used to fetch the target value for this object. The greater the value *
  3964. * returned, the better this object is as a target. *
  3965. * *
  3966. * INPUT: none *
  3967. * *
  3968. * OUTPUT: Returns with the target value for this object. *
  3969. * *
  3970. * WARNINGS: none *
  3971. * *
  3972. * HISTORY: *
  3973. * 07/29/1995 JLB : Created. *
  3974. * 08/16/1995 JLB : Adjusted for early mission lame-out. *
  3975. *=============================================================================================*/
  3976. int TechnoClass::Value(void) const
  3977. {
  3978. assert(IsActive);
  3979. int value = 0;
  3980. /*
  3981. ** In early missions, contents of transports are not figured
  3982. ** into the total value.
  3983. */
  3984. if (Rule.Diff[House->Difficulty].IsContentScan || House->IQ >= Rule.IQContentScan) {
  3985. if (Is_Something_Attached()) {
  3986. FootClass * object = Attached_Object();
  3987. while (object != NULL) {
  3988. value += object->Value();
  3989. object = (FootClass *)(ObjectClass *)object->Next;
  3990. }
  3991. }
  3992. }
  3993. #ifdef TOFIX
  3994. /*
  3995. ** Increase the value of power producing object when there is power critical
  3996. ** defensive structures.
  3997. */
  3998. if (What_Am_I() == RTTI_BUILDING && ((BuildingClass *)this)->Class->Power) {
  3999. if (House->BScan & (STRUCTF_ATOWER|STRUCTF_OBELISK)) {
  4000. value += Techno_Type_Class()->Reward;
  4001. }
  4002. }
  4003. #endif
  4004. return Risk() + Techno_Type_Class()->Reward + value;
  4005. }
  4006. /***********************************************************************************************
  4007. * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. *
  4008. * *
  4009. * This routine will return the range to scan based on the control value specified. The *
  4010. * value returned by this routine is typically used when scanning for enemies. *
  4011. * *
  4012. * INPUT: control -- The range control parameter. *
  4013. * 0 = Use weapon range (zero is returned in this special case). *
  4014. * -1 = Scan without range restrictions (-1 is returned in this case). *
  4015. * 1 = Scan up to twice weapon range. *
  4016. * *
  4017. * OUTPUT: Returns with a range (or special value) that can be used in the threat scan *
  4018. * process. If zero is returned, then always check threat against In_Range(). If *
  4019. * -1 is returned, then no range limitation restriction exists. *
  4020. * *
  4021. * WARNINGS: none *
  4022. * *
  4023. * HISTORY: *
  4024. * 07/29/1995 JLB : Created. *
  4025. *=============================================================================================*/
  4026. int TechnoClass::Threat_Range(int control) const
  4027. {
  4028. assert(IsActive);
  4029. /*
  4030. ** Threat range means nothing if scanning the whole map. In such a case, just
  4031. ** return with the same control flag specified.
  4032. */
  4033. if (control == -1) return(-1);
  4034. /*
  4035. ** If simple guard range is requested, then return "0" since
  4036. ** this is a special control value that is calculated as the object's
  4037. ** weapon range.
  4038. */
  4039. if (control == 0) {
  4040. /*
  4041. ** For normal guard mode or for area guard mode, use the override
  4042. ** threat range value as specified by the object's type class.
  4043. */
  4044. if (Techno_Type_Class()->ThreatRange != 0) {
  4045. return(Techno_Type_Class()->ThreatRange);
  4046. }
  4047. return(0);
  4048. }
  4049. /*
  4050. ** Area guard range is specified, so figure twice the weapon range of the
  4051. ** longest range weapon this object is equipped with.
  4052. */
  4053. int range = Techno_Type_Class()->ThreatRange;
  4054. if (range == 0) {
  4055. range = max(Weapon_Range(0), Weapon_Range(1));
  4056. }
  4057. range *= 2;
  4058. range = Bound(range, 0x0000, 0x0A00);
  4059. return(range);
  4060. }
  4061. /***********************************************************************************************
  4062. * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. *
  4063. * *
  4064. * This will examine the specified cell to determine if it is in the same zone as this *
  4065. * object's location. *
  4066. * *
  4067. * INPUT: cell -- The cell that is to be checked against this object's current location. *
  4068. * *
  4069. * OUTPUT: bool; Is the specified cell in the same zone as this object is? *
  4070. * *
  4071. * WARNINGS: none *
  4072. * *
  4073. * HISTORY: *
  4074. * 10/06/1996 JLB : Created. *
  4075. *=============================================================================================*/
  4076. bool TechnoClass::Is_In_Same_Zone(CELL cell) const
  4077. {
  4078. MZoneType zone = Techno_Type_Class()->MZone;
  4079. return(Map[cell].Zones[zone] == Map[Center_Coord()].Zones[zone]);
  4080. }
  4081. /***********************************************************************************************
  4082. * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. *
  4083. * *
  4084. * This routine is called when the base is being attacked. It will pull units off of the *
  4085. * field and send them back to defend the base. This routine will make taking an enemy *
  4086. * base much more difficult. *
  4087. * *
  4088. * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. *
  4089. * *
  4090. * OUTPUT: none *
  4091. * *
  4092. * WARNINGS: This routine can drastically affect the game play. The computer will probably *
  4093. * call off its attacks as a result. *
  4094. * *
  4095. * HISTORY: *
  4096. * 06/25/1995 JLB : Commented. *
  4097. * 10/15/1996 JLB : Alternates between guard area and attack. *
  4098. * 11/01/1996 JLB : Allow recruit of guard area units in multiplay. *
  4099. *=============================================================================================*/
  4100. void TechnoClass::Base_Is_Attacked(TechnoClass const * enemy)
  4101. {
  4102. assert(IsActive);
  4103. FootClass * defender[6];
  4104. memset(defender, '\0', sizeof(defender));
  4105. int value[ARRAY_SIZE(defender)];
  4106. memset(value, '\0', sizeof(value));
  4107. int count = 0;
  4108. int weakest = 0;
  4109. int desired = enemy->Risk() * House->Control.TechLevel;
  4110. int risktotal = 0;
  4111. int zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone];
  4112. /*
  4113. ** Humans have to deal with their own base is attacked problems.
  4114. */
  4115. if (enemy == NULL || House->Is_Ally(enemy) || House->IsHuman) {
  4116. return;
  4117. }
  4118. /*
  4119. ** Don't overreact if this building can defend itself.
  4120. */
  4121. if (Session.Type == GAME_NORMAL && Techno_Type_Class()->PrimaryWeapon != NULL) return;
  4122. /*
  4123. ** If the enemy is not an infantry or a unit there is not much we can
  4124. ** do about it.
  4125. */
  4126. if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT) {
  4127. return;
  4128. }
  4129. /*
  4130. ** If we are a certain type of building, such as a barrel or land mine,
  4131. ** ignore the attack.
  4132. */
  4133. if (Techno_Type_Class()->IsInsignificant) {
  4134. return;
  4135. }
  4136. /*
  4137. ** If the threat has already been dealt with then we don't need to do
  4138. ** any work. Check for that here.
  4139. */
  4140. if (enemy->Is_Foot() && ((FootClass *)enemy)->BaseAttackTimer != 0) {
  4141. return;
  4142. }
  4143. /*
  4144. ** We will need units to defend our base. We need to suspend teams until
  4145. ** the situation has been dealt with.
  4146. */
  4147. TeamClass::Suspend_Teams(Rule.SuspendPriority, House);
  4148. /*
  4149. ** Loop through the infantry looking for those who are capable of going
  4150. ** on a rescue mission.
  4151. */
  4152. for (int index = 0; index < Infantry.Count() && desired > 0; index++) {
  4153. InfantryClass * infantry = Infantry.Ptr(index);
  4154. if (infantry != NULL && infantry->Owner() == Owner()) {
  4155. /*
  4156. ** Never recruit sticky guard units to defend a base.
  4157. */
  4158. if (!infantry->Is_Weapon_Equipped() ||
  4159. (!MissionControl[infantry->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue;
  4160. // (Mission != MISSION_GUARD_AREA || Session.Type == GAME_NORMAL)) continue;
  4161. /*
  4162. ** Don't allow a response if it doesn't have a weapon that will affect the
  4163. ** enemy object.
  4164. */
  4165. if (infantry->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) {
  4166. continue;
  4167. }
  4168. /*
  4169. ** Don't try to help if the building is on another planet.
  4170. */
  4171. if (Map[infantry->Center_Coord()].Zones[infantry->Class->MZone] != Map[Center_Coord()].Zones[infantry->Class->MZone]) continue;
  4172. /*
  4173. ** Find the amount of threat that this unit can apply to the
  4174. ** enemy.
  4175. */
  4176. int threat = infantry->Rescue_Mission(enemy->As_Target());
  4177. /*
  4178. ** If it can't apply any threat then do just skip it and do not
  4179. ** add it to the list.
  4180. */
  4181. if (!threat) {
  4182. continue;
  4183. }
  4184. /*
  4185. ** Greatly increase the threat value if this unit is already assigned to protect
  4186. ** the target.
  4187. */
  4188. if (ArchiveTarget == As_Target()) {
  4189. threat *= 100;
  4190. }
  4191. /*
  4192. ** If the value returned is negative then this unit is already
  4193. ** assigned to fighting the enemy, so subtract its value from
  4194. ** the enemy's desired value.
  4195. */
  4196. if (threat < 0) {
  4197. desired += threat;
  4198. continue;
  4199. }
  4200. if (count < ARRAY_SIZE(defender)) {
  4201. defender[count] = infantry;
  4202. value[count] = threat;
  4203. count++;
  4204. continue;
  4205. }
  4206. if (threat > weakest) {
  4207. int newweakest = threat;
  4208. for (int lp = 0; lp < count; lp++) {
  4209. if (value[lp] == weakest) {
  4210. value[lp] = threat;
  4211. defender[lp] = (FootClass *) infantry;
  4212. continue;
  4213. }
  4214. if (value[lp] < newweakest) {
  4215. newweakest = value[lp];
  4216. }
  4217. }
  4218. weakest = newweakest;
  4219. }
  4220. }
  4221. }
  4222. /*
  4223. ** Loop through the units looking for those who are capable of going
  4224. ** on a rescue mission.
  4225. */
  4226. for (index = 0; index < Units.Count() && desired > 0; index++) {
  4227. UnitClass * unit = Units.Ptr(index);
  4228. if (unit != NULL && unit->Owner() == Owner()) {
  4229. /*
  4230. ** Never recruit sticky guard units to defend a base.
  4231. */
  4232. if (!unit->Is_Weapon_Equipped() ||
  4233. (!MissionControl[unit->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue;
  4234. /*
  4235. ** Don't allow a response if it doesn't have a weapon that will affect the
  4236. ** enemy object.
  4237. */
  4238. if (unit->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) {
  4239. continue;
  4240. }
  4241. /*
  4242. ** Don't try to help if the building is on another planet.
  4243. */
  4244. if (Map[unit->Center_Coord()].Zones[unit->Class->MZone] != Map[Center_Coord()].Zones[unit->Class->MZone]) continue;
  4245. /*
  4246. ** Find the amount of threat that this unit can apply to the
  4247. ** enemy.
  4248. */
  4249. int threat = unit->Rescue_Mission(enemy->As_Target());
  4250. /*
  4251. ** If it can't apply any threat then do just skip it and do not
  4252. ** add it to the list.
  4253. */
  4254. if (!threat) {
  4255. continue;
  4256. }
  4257. /*
  4258. ** Greatly increase the threat value if this unit is already assigned to protect
  4259. ** the target.
  4260. */
  4261. if (threat > 0 && ArchiveTarget == As_Target()) {
  4262. threat *= 10;
  4263. }
  4264. /*
  4265. ** If the value returned is negative then this unit is already
  4266. ** assigned to fighting the enemy, so subtract its value from
  4267. ** the enemy's desired value.
  4268. */
  4269. if (threat < 0) {
  4270. desired += threat;
  4271. continue;
  4272. }
  4273. if (count < ARRAY_SIZE(defender)) {
  4274. defender[count] = unit;
  4275. value[count] = threat;
  4276. count++;
  4277. continue;
  4278. }
  4279. if (threat > weakest) {
  4280. int newweakest = threat;
  4281. for (int lp = 0; lp < count; lp ++) {
  4282. if (value[lp] == weakest) {
  4283. value[lp] = threat;
  4284. defender[lp] = (FootClass *) unit;
  4285. continue;
  4286. }
  4287. if (value[lp] < newweakest) {
  4288. // if (value[count] < newweakest) {
  4289. newweakest = value[lp];
  4290. }
  4291. }
  4292. weakest = newweakest;
  4293. }
  4294. }
  4295. }
  4296. if (desired > 0) {
  4297. /*
  4298. ** Sort the defenders by value, this doesn't take very long and will
  4299. ** help the closest defenders to respond.
  4300. */
  4301. for (int lp = 0; lp < count - 1; lp ++) {
  4302. for (int lp2 = lp + 1; lp2 < count; lp2++) {
  4303. if (value[lp] < value[lp2]) {
  4304. value[lp] ^= value[lp2];
  4305. value[lp2] ^= value[lp];
  4306. value[lp] ^= value[lp2];
  4307. FootClass *temp;
  4308. temp = defender[lp];
  4309. defender[lp] = defender[lp2];
  4310. defender[lp2] = temp;
  4311. }
  4312. }
  4313. }
  4314. for (lp = 0; lp < count; lp ++) {
  4315. if (Percent_Chance(50)) {
  4316. defender[lp]->Assign_Mission(MISSION_RESCUE);
  4317. } else {
  4318. defender[lp]->Assign_Mission(MISSION_GUARD_AREA);
  4319. defender[lp]->ArchiveTarget = As_Target();
  4320. }
  4321. defender[lp]->Assign_Target(enemy->As_Target());
  4322. risktotal += defender[lp]->Risk();
  4323. if (risktotal > desired) {
  4324. break;
  4325. }
  4326. }
  4327. }
  4328. if (risktotal > desired && enemy->Is_Foot()) {
  4329. ((FootClass *)enemy)->BaseAttackTimer = TICKS_PER_MINUTE * Rule.BaseDefenseDelay;
  4330. }
  4331. }
  4332. /***********************************************************************************************
  4333. * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. *
  4334. * *
  4335. * This routine is called when this object has suffered some damage and it needs to know *
  4336. * if it should fight back. The object that caused the damage is specifed as a parameter. *
  4337. * *
  4338. * INPUT: source -- The points to the object that was the source of the damage applied *
  4339. * to this object. *
  4340. * *
  4341. * OUTPUT: bool; Should retaliation occur? *
  4342. * *
  4343. * WARNINGS: none *
  4344. * *
  4345. * HISTORY: *
  4346. * 10/19/1996 JLB : Created. *
  4347. *=============================================================================================*/
  4348. bool TechnoClass::Is_Allowed_To_Retaliate(TechnoClass const * source) const
  4349. {
  4350. /*
  4351. ** If there is no source of the damage, then retaliation cannot occur.
  4352. */
  4353. if (source == NULL) return(false);
  4354. /*
  4355. ** If the mission precludes retaliation, then don't retaliate.
  4356. */
  4357. if (!MissionControl[Mission].IsRetaliate) return(false);
  4358. /*
  4359. ** Fixed wing aircraft are not responsive enough to retaliate to damage recieved.
  4360. */
  4361. if (What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)this)->Class->IsFixedWing) {
  4362. return(false);
  4363. }
  4364. /*
  4365. ** If the source of the damage is an ally, then retaliation shouldn't
  4366. ** occur either.
  4367. */
  4368. if (House->Is_Ally(source)) return(false);
  4369. /*
  4370. ** Only objects that have a damaging weapon are allowed to retaliate.
  4371. */
  4372. if (Combat_Damage() <= 0 || !Is_Weapon_Equipped()) return(false);
  4373. /*
  4374. ** If this is not equipped with a weapon that can attack the molester, then
  4375. ** don't allow retaliation.
  4376. */
  4377. TechnoTypeClass const * ttype = Techno_Type_Class();
  4378. if (ttype->PrimaryWeapon->WarheadPtr != NULL &&
  4379. ttype->PrimaryWeapon->WarheadPtr->Modifier[source->Techno_Type_Class()->Armor] == 0) {
  4380. return(false);
  4381. }
  4382. /*
  4383. ** One can never retaliate against a dog because of their peculiar nature of attacking.
  4384. ** Dogs must be attacked using normal target processing.
  4385. */
  4386. if (source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) return(false);
  4387. /*
  4388. ** Don't allow retaliation if it isn't equipped with a weapon that can deal with the threat.
  4389. */
  4390. if (source->What_Am_I() == RTTI_AIRCRAFT && !ttype->PrimaryWeapon->Bullet->IsAntiAircraft) return(false);
  4391. /*
  4392. ** Tanya is not allowed to retaliate against buildings in the normal sense while in guard mode. That
  4393. ** is, unless it is owned by the computer. Normally, Tanya can't do anything substantial to a building
  4394. ** except to blow it up.
  4395. */
  4396. if ((House->IsHuman || (Session.Type == GAME_NORMAL && House->IsPlayerControl))
  4397. && source->What_Am_I() == RTTI_BUILDING && What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const *)ttype)->IsBomber) {
  4398. return(false);
  4399. }
  4400. /*
  4401. ** If a human house is not allowed to retaliate automatically, then don't
  4402. */
  4403. if (House->IsHuman && !Rule.IsSmartDefense && (What_Am_I() != RTTI_INFANTRY || *((InfantryClass*)this) != INFANTRY_TANYA || source->What_Am_I() != RTTI_INFANTRY)) return(false);
  4404. /*
  4405. ** If this object is part of a team that prevents retaliation then don't allow retaliation.
  4406. */
  4407. if (Is_Foot() && ((FootClass *)this)->Team.Is_Valid() && ((FootClass *)this)->Team->Class->IsSuicide) {
  4408. return(false);
  4409. }
  4410. /*
  4411. ** Compare potential threat of the current target and the potential new target. Don't retaliate
  4412. ** if it is currently attacking the greater threat.
  4413. */
  4414. if (!House->IsHuman && Percent_Chance(50)) {
  4415. fixed source_val = 0;
  4416. int primary = What_Weapon_Should_I_Use(source->As_Target());
  4417. WeaponTypeClass const * weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon;
  4418. if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(source, primary)) {
  4419. source_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor];
  4420. }
  4421. fixed current_val = 0;
  4422. TechnoClass const * tech = As_Techno(TarCom);
  4423. if (tech != NULL) {
  4424. primary = What_Weapon_Should_I_Use(tech->As_Target());
  4425. weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon;
  4426. if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(tech, primary)) {
  4427. current_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor];
  4428. }
  4429. }
  4430. if (source_val <= current_val) return(false);
  4431. }
  4432. /*
  4433. ** If it is already busy attacking another target, then don't retaliate.
  4434. */
  4435. // if (In_Range(TarCom)) return(false);
  4436. /*
  4437. ** All checks passed, so return that retaliation is allowed.
  4438. */
  4439. return(true);
  4440. }
  4441. /***********************************************************************************************
  4442. * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. *
  4443. * *
  4444. * This routine will return the ownable bits for this object. The ownable bits represent *
  4445. * the houses that are allowed to own this object. *
  4446. * *
  4447. * INPUT: none *
  4448. * *
  4449. * OUTPUT: Returns the ownable bits for this object. *
  4450. * *
  4451. * WARNINGS: none *
  4452. * *
  4453. * HISTORY: *
  4454. * 07/29/1995 JLB : Created. *
  4455. *=============================================================================================*/
  4456. int TechnoClass::Get_Ownable(void) const
  4457. {
  4458. assert(IsActive);
  4459. return ((TechnoTypeClass const &)Class_Of()).Get_Ownable();
  4460. // return ((TechnoTypeClass const &)Class_Of()).Ownable;
  4461. }
  4462. /***********************************************************************************************
  4463. * TechnoClass::Risk -- Fetches the risk associated with this object. *
  4464. * *
  4465. * This routine is called when the risk value for this object needs to be determined. *
  4466. * *
  4467. * INPUT: none *
  4468. * *
  4469. * OUTPUT: Returns with the risk value for this object. *
  4470. * *
  4471. * WARNINGS: none *
  4472. * *
  4473. * HISTORY: *
  4474. * 07/29/1995 JLB : Created. *
  4475. *=============================================================================================*/
  4476. int TechnoClass::Risk(void) const
  4477. {
  4478. assert(IsActive);
  4479. return(Techno_Type_Class()->Risk);
  4480. }
  4481. /***********************************************************************************************
  4482. * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. *
  4483. * *
  4484. * This routine will return the current Tiberium load (expressed as a fixed point fraction) *
  4485. * that this object currently contains. Typical implementor of this function would be *
  4486. * the harvester. Any object that can return a non-zero value should derive from this *
  4487. * function in order to return the appropriate value. *
  4488. * *
  4489. * INPUT: none *
  4490. * *
  4491. * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. *
  4492. * 0x0000 = empty *
  4493. * 0x0080 = half full *
  4494. * 0x0100 = full *
  4495. * *
  4496. * WARNINGS: none *
  4497. * *
  4498. * HISTORY: *
  4499. * 07/29/1995 JLB : Created. *
  4500. *=============================================================================================*/
  4501. fixed TechnoClass::Tiberium_Load(void) const
  4502. {
  4503. assert(IsActive);
  4504. return(0);
  4505. }
  4506. /***********************************************************************************************
  4507. * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. *
  4508. * *
  4509. * This routine is called when an object desires to load up on this object. The object *
  4510. * desiring to load is specified. The cell that the loading object should move to is *
  4511. * determined. The direction that this object should face is also calculated. This routine *
  4512. * will be overridden by those objects that can actually load up passengers. *
  4513. * *
  4514. * INPUT: object -- The object that is desiring to load up. *
  4515. * *
  4516. * moveto -- Reference to the cell that the loading object should move to before *
  4517. * the final load process occurs (this value will be filled in). *
  4518. * *
  4519. * OUTPUT: Returns with the direction that the transport object should face. *
  4520. * *
  4521. * WARNINGS: none *
  4522. * *
  4523. * HISTORY: *
  4524. * 07/29/1995 JLB : Created. *
  4525. *=============================================================================================*/
  4526. DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const
  4527. {
  4528. assert(IsActive);
  4529. moveto = 0;
  4530. return(DIR_N);
  4531. }
  4532. /***********************************************************************************************
  4533. * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. *
  4534. * *
  4535. * This routine will return the number of pips to display on this object when the object *
  4536. * is selected. The default condition is to return no pips at all. This routine is *
  4537. * derived for those objects that can have pips. *
  4538. * *
  4539. * INPUT: none *
  4540. * *
  4541. * OUTPUT: Returns with the number of pips to display on this object when selected. *
  4542. * *
  4543. * WARNINGS: none *
  4544. * *
  4545. * HISTORY: *
  4546. * 07/29/1995 JLB : Created. *
  4547. *=============================================================================================*/
  4548. int TechnoClass::Pip_Count(void) const
  4549. {
  4550. assert(IsActive);
  4551. return(0);
  4552. }
  4553. /***********************************************************************************************
  4554. * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. *
  4555. * *
  4556. * This routine will fetch the direction that a fired projectile will take. This is *
  4557. * usually the facing of the object's weapon. This routine will be derived for the objects *
  4558. * that have their weapon barrel facing a different direction than the body. *
  4559. * *
  4560. * INPUT: none *
  4561. * *
  4562. * OUTPUT: Returns with the direction a fired projectile will take. *
  4563. * *
  4564. * WARNINGS: none *
  4565. * *
  4566. * HISTORY: *
  4567. * 07/29/1995 JLB : Created. *
  4568. *=============================================================================================*/
  4569. DirType TechnoClass::Fire_Direction(void) const
  4570. {
  4571. assert(IsActive);
  4572. return(Turret_Facing());
  4573. }
  4574. /***********************************************************************************************
  4575. * TechnoClass::Response_Select -- Handles the voice response when selected. *
  4576. * *
  4577. * This routine is called when a voice response to a select action is desired. This routine *
  4578. * should be overridden for any object that actually has a voice response. *
  4579. * *
  4580. * INPUT: none *
  4581. * *
  4582. * OUTPUT: none *
  4583. * *
  4584. * WARNINGS: This routine can generate an audio response. *
  4585. * *
  4586. * HISTORY: *
  4587. * 07/29/1995 JLB : Created. *
  4588. *=============================================================================================*/
  4589. void TechnoClass::Response_Select(void)
  4590. {
  4591. assert(IsActive);
  4592. }
  4593. /***********************************************************************************************
  4594. * TechnoClass::Response_Move -- Handles the voice response to a movement request. *
  4595. * *
  4596. * This routine is called when a voice response to a movement order is desired. This *
  4597. * routine should be overridden for any object that actually has a voice response. *
  4598. * *
  4599. * INPUT: none *
  4600. * *
  4601. * OUTPUT: none *
  4602. * *
  4603. * WARNINGS: This can generate an audio response. *
  4604. * *
  4605. * HISTORY: *
  4606. * 07/29/1995 JLB : Created. *
  4607. *=============================================================================================*/
  4608. void TechnoClass::Response_Move(void)
  4609. {
  4610. assert(IsActive);
  4611. }
  4612. /***********************************************************************************************
  4613. * TechnoClass::Response_Attack -- Handles the voice response when given attack order. *
  4614. * *
  4615. * This routine is called when a voice response to an attack order is desired. This routine *
  4616. * should be overridden for any object that actually have a voice response. *
  4617. * *
  4618. * INPUT: none *
  4619. * *
  4620. * OUTPUT: none *
  4621. * *
  4622. * WARNINGS: This can generate an audio response. *
  4623. * *
  4624. * HISTORY: *
  4625. * 07/29/1995 JLB : Created. *
  4626. *=============================================================================================*/
  4627. void TechnoClass::Response_Attack(void)
  4628. {
  4629. assert(IsActive);
  4630. }
  4631. /***********************************************************************************************
  4632. * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. *
  4633. * *
  4634. * This routine will search for a nearby target and assign it to this object's TarCom. *
  4635. * The method to use when scanning for a target is controlled by the parameter passed. *
  4636. * *
  4637. * INPUT: threat -- The threat control parameter used to control the range searched. The *
  4638. * only values recognized are THREAT_RANGE and THREAT_AREA. *
  4639. * *
  4640. * OUTPUT: Was a suitable target acquired and assigned to the TarCom? *
  4641. * *
  4642. * WARNINGS: none *
  4643. * *
  4644. * HISTORY: *
  4645. * 07/29/1995 JLB : Created. *
  4646. *=============================================================================================*/
  4647. bool TechnoClass::Target_Something_Nearby(ThreatType threat)
  4648. {
  4649. assert(IsActive);
  4650. threat = threat & (THREAT_RANGE|THREAT_AREA);
  4651. /*
  4652. ** Determine that if there is an existing target it is still legal
  4653. ** and within range.
  4654. */
  4655. if (Target_Legal(TarCom)) {
  4656. if ((threat & THREAT_RANGE)) {
  4657. int primary = What_Weapon_Should_I_Use(TarCom);
  4658. if (!In_Range(TarCom, primary)) {
  4659. Assign_Target(TARGET_NONE);
  4660. }
  4661. }
  4662. }
  4663. /*
  4664. ** If there is no target, then try to find one and assign it as
  4665. ** the target for this unit.
  4666. */
  4667. if (!Target_Legal(TarCom)) {
  4668. Assign_Target(Greatest_Threat(threat));
  4669. }
  4670. /*
  4671. ** Return with answer to question: Does this unit now have a target?
  4672. */
  4673. return(Target_Legal(TarCom));
  4674. }
  4675. /***********************************************************************************************
  4676. * TechnoClass::Exit_Object -- Causes specified object to leave this object. *
  4677. * *
  4678. * This routine is called when there is an attached object that should detach and leave *
  4679. * this object. Typical of this would be the refinery and APC. *
  4680. * *
  4681. * INPUT: object -- The object that is trying to leave this object. *
  4682. * *
  4683. * OUTPUT: Was the object successfully launched from this object? Failure might indicate that *
  4684. * there is insufficient room to detach the specified object. *
  4685. * *
  4686. * WARNINGS: none *
  4687. * *
  4688. * HISTORY: *
  4689. * 07/24/1995 JLB : Created. *
  4690. *=============================================================================================*/
  4691. int TechnoClass::Exit_Object(TechnoClass *)
  4692. {
  4693. assert(IsActive);
  4694. return(0);
  4695. }
  4696. /***********************************************************************************************
  4697. * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate. *
  4698. * *
  4699. * This will examine this object to determine if it is time and ready to perform some *
  4700. * kind of random animation. *
  4701. * *
  4702. * INPUT: none *
  4703. * *
  4704. * OUTPUT: bool; Is it time to perform an random animation? *
  4705. * *
  4706. * WARNINGS: none *
  4707. * *
  4708. * HISTORY: *
  4709. * 10/19/1996 JLB : Created. *
  4710. *=============================================================================================*/
  4711. bool TechnoClass::Is_Ready_To_Random_Animate(void) const
  4712. {
  4713. assert(IsActive);
  4714. return(IdleTimer == 0);
  4715. }
  4716. /***********************************************************************************************
  4717. * TechnoClass::Assign_Destination -- Assigns movement destination to the object. *
  4718. * *
  4719. * This routine is called when the object needs to have a new movement destination *
  4720. * assigned. This routine must be overridden since at this level, movement is not allowed. *
  4721. * *
  4722. * INPUT: destination -- The destination to assign to this object. *
  4723. * *
  4724. * OUTPUT: none *
  4725. * *
  4726. * WARNINGS: none *
  4727. * *
  4728. * HISTORY: *
  4729. * 07/24/1995 JLB : Created. *
  4730. *=============================================================================================*/
  4731. void TechnoClass::Assign_Destination(TARGET )
  4732. {
  4733. assert(IsActive);
  4734. }
  4735. /***********************************************************************************************
  4736. * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. *
  4737. * *
  4738. * This routine is called when the object should intelligently revert to an idle state. *
  4739. * Typically this routine is called after some mission has completed. This routine must *
  4740. * be overridden by the various object types. It is located at this level merely to provide *
  4741. * a virtual function entry point. *
  4742. * *
  4743. * INPUT: initial -- Is this called when the unit just leaves a factory or is initially *
  4744. * or is initially placed on the map? *
  4745. * *
  4746. * OUTPUT: none *
  4747. * *
  4748. * WARNINGS: none *
  4749. * *
  4750. * HISTORY: *
  4751. * 07/24/1995 JLB : Created. *
  4752. *=============================================================================================*/
  4753. void TechnoClass::Enter_Idle_Mode(bool )
  4754. {
  4755. assert(IsActive);
  4756. }
  4757. /***********************************************************************************************
  4758. * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. *
  4759. * *
  4760. * This routine is used to render the small transportation pip (occupant feedback graphic) *
  4761. * used for transporter object. It will also display if the techno object is "primary" *
  4762. * if necessary. *
  4763. * *
  4764. * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsequent pips *
  4765. * are drawn rightward. *
  4766. * *
  4767. * window-- The window that pip clipping is relative to. *
  4768. * *
  4769. * OUTPUT: none *
  4770. * *
  4771. * WARNINGS: none *
  4772. * *
  4773. * HISTORY: *
  4774. * 08/08/1995 JLB : Created. *
  4775. * 10/06/1995 JLB : Displays the team group number. *
  4776. * 09/10/1996 JLB : Medic hack for red pip. *
  4777. *=============================================================================================*/
  4778. void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) const
  4779. {
  4780. assert(IsActive);
  4781. /*
  4782. ** Transporter type objects have a different graphic representation for the pips. The
  4783. ** pip color represents the type of occupant.
  4784. */
  4785. if (Techno_Type_Class()->Max_Passengers() > 0) {
  4786. ObjectClass const * object = Attached_Object();
  4787. for (int index = 0; index < Class_Of().Max_Pips(); index++) {
  4788. PipEnum pip = PIP_EMPTY;
  4789. if (object != NULL) {
  4790. pip = PIP_FULL;
  4791. if (object->What_Am_I() == RTTI_INFANTRY) {
  4792. pip = ((InfantryClass *)object)->Class->Pip;
  4793. }
  4794. #ifdef FIXIT_CARRIER // checked - ajw 9/28/98
  4795. if (What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_CARRIER) {
  4796. if (object->What_Am_I() == RTTI_AIRCRAFT) {
  4797. AircraftClass *heli = (AircraftClass *)object;
  4798. if (heli->Ammo != heli->Techno_Type_Class()->MaxAmmo) {
  4799. pip = PIP_ENGINEER;
  4800. if (!heli->Ammo) {
  4801. pip = PIP_COMMANDO;
  4802. }
  4803. }
  4804. }
  4805. }
  4806. #endif
  4807. object = object->Next;
  4808. }
  4809. CC_Draw_Shape(Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4810. }
  4811. } else {
  4812. /*
  4813. ** Display number of how many attached objects there are. This is also used
  4814. ** to display the fullness rating for a harvester.
  4815. */
  4816. int pips = Pip_Count();
  4817. /*
  4818. ** Check if it's a harvester, to show the right type of pips for the
  4819. ** various minerals it could have harvested.
  4820. */
  4821. if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_HARVESTER) {
  4822. UnitClass * harv = (UnitClass *)this;
  4823. int iron = harv->Gems;
  4824. int nickel = harv->Gold;
  4825. int graypips = pips * fixed(iron, Rule.BailCount);
  4826. int greenpips = pips * fixed(nickel, Rule.BailCount);
  4827. while (greenpips + graypips < pips) {
  4828. int ironnickelmax = max(iron, nickel);
  4829. if (iron > nickel) {
  4830. graypips++;
  4831. } else {
  4832. greenpips++;
  4833. }
  4834. }
  4835. for (int index = 0; index < Class_Of().Max_Pips(); index++) {
  4836. int shape = PIP_EMPTY;
  4837. if (index < pips) {
  4838. if (greenpips) {
  4839. shape = PIP_FULL;
  4840. greenpips--;
  4841. } else {
  4842. shape = PIP_COMMANDO;
  4843. graypips--;
  4844. }
  4845. }
  4846. CC_Draw_Shape(Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4847. }
  4848. }
  4849. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  4850. /*
  4851. ** Check if it's a Chrono tank, to show the recharge gauge.
  4852. */
  4853. else if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_CHRONOTANK) {
  4854. for (int index = 0; index < 5; index++) {
  4855. int shape = PIP_EMPTY;
  4856. if (index < pips) {
  4857. switch(index) {
  4858. case 0:
  4859. case 1:
  4860. shape = PIP_COMMANDO;
  4861. break;
  4862. case 2:
  4863. case 3:
  4864. shape = PIP_ENGINEER;
  4865. break;
  4866. case 4:
  4867. shape = PIP_FULL;
  4868. default:
  4869. break;
  4870. }
  4871. }
  4872. CC_Draw_Shape(Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4873. }
  4874. #endif
  4875. } else {
  4876. bool building = false;
  4877. int pip = PIP_FULL; // green
  4878. if(!IsOwnedByPlayer && What_Am_I() == RTTI_BUILDING) {
  4879. if(*(BuildingClass *)this==STRUCT_POWER || *(BuildingClass *)this==STRUCT_ADVANCED_POWER) {
  4880. building = true;
  4881. if (House->Power_Fraction() < 1) {
  4882. pip = PIP_ENGINEER; // gold
  4883. if (House->Drain > (House->Power * 2) ) {
  4884. pip = PIP_COMMANDO;
  4885. }
  4886. }
  4887. }
  4888. }
  4889. for (int index = 0; index < (building ? 5 : Class_Of().Max_Pips()); index++) {
  4890. if (building) {
  4891. CC_Draw_Shape(Class_Of().PipShapes, pip, x, y-index*3, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4892. } else {
  4893. CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4894. }
  4895. }
  4896. //BG for (int index = 0; index < Class_Of().Max_Pips(); index++) {
  4897. //BG CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4898. //BG }
  4899. }
  4900. }
  4901. /*
  4902. ** Special hack to display a red pip on the medic.
  4903. */
  4904. if (What_Am_I() == RTTI_INFANTRY && Combat_Damage() < 0) {
  4905. CC_Draw_Shape(Class_Of().PipShapes, PIP_MEDIC, x+8, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4906. }
  4907. /*
  4908. ** Display whether this unit is a leader unit or not.
  4909. */
  4910. if (IsLeader) {
  4911. PipEnum prishape = PIP_PRIMARY;
  4912. if(What_Am_I() == RTTI_BUILDING) {
  4913. if(*((BuildingClass *)this) == STRUCT_KENNEL) {
  4914. prishape = PIP_PRI;
  4915. }
  4916. }
  4917. CC_Draw_Shape(Class_Of().PipShapes, prishape, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL);
  4918. }
  4919. /*
  4920. ** Display what group this unit belongs to. This corresponds to the team
  4921. ** number assigned with the <CTRL> key.
  4922. */
  4923. if (Is_Foot() && ((FootClass *)this)->Group != 0xFF && ((FootClass *)this)->Group < 10) {
  4924. int yval = -1;
  4925. int group = ((FootClass *)this)->Group+1;
  4926. if (Class_Of().Max_Pips()) yval -= 4;
  4927. if (group == 10) group = 0;
  4928. CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS+group, x+2, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4929. /*
  4930. ** If this unit is part of a formation, draw an 'F' after the group
  4931. ** number.
  4932. */
  4933. if ( ((FootClass *)this)->XFormOffset != 0x80000000UL) {
  4934. CC_Draw_Shape(Class_Of().PipShapes, PIP_LETTERF, x+8, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4935. }
  4936. }
  4937. /*
  4938. ** If this building is being spied on by the player, draw the money or
  4939. ** factory-producing item or whatever.
  4940. */
  4941. if (What_Am_I() == RTTI_BUILDING) {
  4942. int spiedby = SpiedBy & (1<<(PlayerPtr->Class->House));
  4943. /*
  4944. ** If it's an ore refinery or other such storage-capable building,
  4945. ** loop thru all of their buildings to see if ANY of them are spied
  4946. ** upon, 'cause once you spy any money, you've spied all of it.
  4947. */
  4948. if (((BuildingClass *)this)->Class->Capacity) {
  4949. for (int index = 0; index < Buildings.Count(); index++) {
  4950. BuildingClass * building = Buildings.Ptr(index);
  4951. if (building->House == House && building->Class->Capacity) {
  4952. spiedby |= (building->SpiedBy) & (1<<(PlayerPtr->Class->House));
  4953. }
  4954. }
  4955. }
  4956. /*
  4957. ** Print word "Decoy" above buildings that are spied upon or fake
  4958. */
  4959. if (((BuildingClass *)this)->Class->IsFake) {
  4960. if (spiedby || IsOwnedByPlayer) {
  4961. CC_Draw_Shape(Class_Of().PipShapes, PIP_DECOY, x, y-16, window, SHAPE_WIN_REL);
  4962. }
  4963. }
  4964. /*
  4965. ** See if we should print the credits for a spied refinery
  4966. */
  4967. if (spiedby) {
  4968. // If it's a refinery/silo, print the enemy's money
  4969. if (((BuildingClass *)this)->Class->Capacity) {
  4970. long money = House->Available_Money();
  4971. /*
  4972. ** Determine how many digits will be printed.
  4973. */
  4974. int digits;
  4975. int factor = 10;
  4976. for (digits = 1; digits < 9; digits++) {
  4977. if (money < factor) break;
  4978. factor *= 10;
  4979. }
  4980. int startx = x + 6 * digits - 3;// + 6 * 8;
  4981. while (money) {
  4982. int xdigit = money % 10;
  4983. money /= 10;
  4984. CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS + xdigit, startx, y-6, window, SHAPE_CENTER|SHAPE_WIN_REL);
  4985. startx -= 6;
  4986. }
  4987. }
  4988. }
  4989. }
  4990. }
  4991. /***********************************************************************************************
  4992. * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. *
  4993. * *
  4994. * This routine will be used to find a building that can serve as a docking bay. The *
  4995. * closest building that qualifies will be returned. If no building could be found then *
  4996. * return with NULL. *
  4997. * *
  4998. * INPUT: b -- The structure type to look for. *
  4999. * *
  5000. * friendly -- Allow searching for allied buildings as well. *
  5001. * *
  5002. * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. *
  5003. * *
  5004. * WARNINGS: This routine might return NULL even if there are buildings of the specified *
  5005. * type available. This is the case when the building(s) are currently busy and *
  5006. * cannot serve as a docking bay at the moment. *
  5007. * *
  5008. * HISTORY: *
  5009. * 07/18/1995 JLB : Created. *
  5010. * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. *
  5011. *=============================================================================================*/
  5012. BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const
  5013. {
  5014. assert(IsActive);
  5015. BuildingClass * best = 0;
  5016. /*
  5017. ** First check to see if there are ANY buildings of the specified
  5018. ** type in this house's inventory. If not, then don't bother to scan
  5019. ** for one.
  5020. */
  5021. if (House->Get_Quantity(b) != 0) {
  5022. int bestval = -1;
  5023. /*
  5024. ** Loop through all the buildings and find the one that matches the specification
  5025. ** and is willing to dock with this object.
  5026. */
  5027. for (int index = 0; index < Buildings.Count(); index++) {
  5028. BuildingClass * building = Buildings.Ptr(index);
  5029. /*
  5030. ** Check to see if the building qualifies (preliminary scan).
  5031. */
  5032. if (building != NULL &&
  5033. (friendly ? building->House->Is_Ally(this) : building->House == House) &&
  5034. !building->IsInLimbo &&
  5035. *building == b &&
  5036. (What_Am_I() == RTTI_AIRCRAFT || Map[building->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) &&
  5037. ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) {
  5038. /*
  5039. ** If the building qualifies and this building is better than the
  5040. ** last qualifying building (as rated by distance), then record
  5041. ** this building and keep scanning.
  5042. */
  5043. if (bestval == -1 || Distance(building) < bestval || building->IsLeader) {
  5044. best = building;
  5045. bestval = Distance(building);
  5046. }
  5047. }
  5048. }
  5049. }
  5050. return(best);
  5051. }
  5052. /***********************************************************************************************
  5053. * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. *
  5054. * *
  5055. * This routine is called when an object would like to exit from this (presumed) transport. *
  5056. * A suitable cell should be returned by this routine. The specified object will probably *
  5057. * be unloaded at that cell. *
  5058. * *
  5059. * INPUT: techno -- Pointer to the object that would like to unload. This is used to *
  5060. * determine suitability for placement. *
  5061. * *
  5062. * OUTPUT: Returns with the cell that is recommended for object exit. *
  5063. * *
  5064. * WARNINGS: none *
  5065. * *
  5066. * HISTORY: *
  5067. * 08/12/1995 JLB : Created. *
  5068. *=============================================================================================*/
  5069. CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const
  5070. {
  5071. assert(IsActive);
  5072. return(Coord_Cell(Docking_Coord()));
  5073. }
  5074. /***********************************************************************************************
  5075. * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. *
  5076. * *
  5077. * This routine is used by the selling back mechanism in order to credit the owning house *
  5078. * with some refund credits. The value returned is the credits to refund to the owner. *
  5079. * *
  5080. * INPUT: none *
  5081. * *
  5082. * OUTPUT: Returns with the credits to refund to the owner. *
  5083. * *
  5084. * WARNINGS: none *
  5085. * *
  5086. * HISTORY: *
  5087. * 08/13/1995 JLB : Created. *
  5088. *=============================================================================================*/
  5089. int TechnoClass::Refund_Amount(void) const
  5090. {
  5091. assert(IsActive);
  5092. int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias;
  5093. #ifdef TOFIX
  5094. /*
  5095. ** If the object is carrying Tiberium directly (i.e., the harvester), then
  5096. ** account for the credits of the load.
  5097. */
  5098. // cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2;
  5099. #endif
  5100. if (House->IsHuman) {
  5101. cost = cost * Rule.RefundPercent;
  5102. // cost /= 2;
  5103. }
  5104. return(cost);
  5105. }
  5106. /***********************************************************************************************
  5107. * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. *
  5108. * *
  5109. * This routine will calculate and return the anti-aircraft strength of this object. *
  5110. * Typical users of this strength value is the base defense expert system AI. *
  5111. * *
  5112. * INPUT: none *
  5113. * *
  5114. * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned *
  5115. * is an abstract number to be used for relative comparisons only. *
  5116. * *
  5117. * WARNINGS: none *
  5118. * *
  5119. * HISTORY: *
  5120. * 10/02/1995 JLB : Created. *
  5121. *=============================================================================================*/
  5122. int TechnoClass::Anti_Air(void) const
  5123. {
  5124. assert(IsActive);
  5125. if (Is_Weapon_Equipped()) {
  5126. WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon;
  5127. BulletTypeClass const * bullet = weapon->Bullet;
  5128. WarheadTypeClass const * warhead = weapon->WarheadPtr;
  5129. if (bullet->IsAntiAircraft) {
  5130. int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF;
  5131. if (Techno_Type_Class()->Is_Two_Shooter()) {
  5132. value *= 2;
  5133. }
  5134. return(value/50);
  5135. }
  5136. }
  5137. return(0);
  5138. }
  5139. /***********************************************************************************************
  5140. * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. *
  5141. * *
  5142. * This routine is used to examine and calculate the anti-armor strength of this object. *
  5143. * Typical user user of this would be the expert system base defense AI. *
  5144. * *
  5145. * INPUT: none *
  5146. * *
  5147. * OUTPUT: Returns with the relative anti-armor combat value for this object. The value *
  5148. * is abstract and is only to be used in relative comparisons. *
  5149. * *
  5150. * WARNINGS: none *
  5151. * *
  5152. * HISTORY: *
  5153. * 10/02/1995 JLB : Created. *
  5154. *=============================================================================================*/
  5155. int TechnoClass::Anti_Armor(void) const
  5156. {
  5157. assert(IsActive);
  5158. if (Is_Weapon_Equipped()) {
  5159. if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0);
  5160. WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon;
  5161. BulletTypeClass const * bullet = weapon->Bullet;
  5162. WarheadTypeClass const * warhead = weapon->WarheadPtr;
  5163. int mrange = min(weapon->Range, 0x0400);
  5164. int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF;
  5165. if (Techno_Type_Class()->Is_Two_Shooter()) {
  5166. value *= 2;
  5167. }
  5168. if (bullet->IsInaccurate) {
  5169. value /= 2;
  5170. }
  5171. return(value/50);
  5172. }
  5173. return(0);
  5174. }
  5175. /***********************************************************************************************
  5176. * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. *
  5177. * *
  5178. * This routine is used to determine the anti-infantry strength of this object. The *
  5179. * typical user of this routine is the expert system base defense AI. *
  5180. * *
  5181. * INPUT: none *
  5182. * *
  5183. * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is *
  5184. * abstract and should only be used for relative comparisons. *
  5185. * *
  5186. * WARNINGS: none *
  5187. * *
  5188. * HISTORY: *
  5189. * 10/02/1995 JLB : Created. *
  5190. *=============================================================================================*/
  5191. int TechnoClass::Anti_Infantry(void) const
  5192. {
  5193. assert(IsActive);
  5194. if (Is_Weapon_Equipped()) {
  5195. if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0);
  5196. WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon;
  5197. BulletTypeClass const * bullet = weapon->Bullet;
  5198. WarheadTypeClass const * warhead = weapon->WarheadPtr;
  5199. int mrange = min(weapon->Range, 0x0400);
  5200. int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF;
  5201. if (Techno_Type_Class()->Is_Two_Shooter()) {
  5202. value *= 2;
  5203. }
  5204. if (bullet->IsInaccurate) {
  5205. value /= 2;
  5206. }
  5207. return(value/50);
  5208. }
  5209. return(0);
  5210. }
  5211. /***********************************************************************************************
  5212. * TechnoClass::Look -- Performs a look around (map reveal) action. *
  5213. * *
  5214. * This routine will reveal the map around this object. *
  5215. * *
  5216. * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. *
  5217. * If it is absolutely known that the object has only moved one *
  5218. * cell from its previous location that it performed a Look() at, *
  5219. * then set this parameter to TRUE. It will only perform the look *
  5220. * check on the perimeter cells. *
  5221. * *
  5222. * OUTPUT: none *
  5223. * *
  5224. * WARNINGS: This routine is slow, try to call it only when necessary. *
  5225. * *
  5226. * HISTORY: *
  5227. * 03/14/1996 JLB : Created. *
  5228. *=============================================================================================*/
  5229. void TechnoClass::Look(bool incremental)
  5230. {
  5231. assert(IsActive);
  5232. assert(!IsInLimbo);
  5233. int sight_range = Techno_Type_Class()->SightRange;
  5234. if (sight_range) {
  5235. Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental);
  5236. }
  5237. }
  5238. //**********************************************************************************************
  5239. // MODULE SEPARATION -- TechnoTypeClass member functions follow.
  5240. //**********************************************************************************************
  5241. /***********************************************************************************************
  5242. * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. *
  5243. * *
  5244. * This is the normal constructor for techno type objects. It is called in the process of *
  5245. * constructing all the object type (constant) data for the various techno type objects. *
  5246. * *
  5247. * INPUT: see below... *
  5248. * *
  5249. * OUTPUT: none *
  5250. * *
  5251. * WARNINGS: none *
  5252. * *
  5253. * HISTORY: *
  5254. * 03/19/1995 JLB : Created. *
  5255. * 05/11/1996 JLB : Moderated risk calc so range doesn't dominate. *
  5256. *=============================================================================================*/
  5257. TechnoTypeClass::TechnoTypeClass(
  5258. RTTIType rtti,
  5259. int id,
  5260. int name,
  5261. char const * ininame,
  5262. RemapType remap,
  5263. int verticaloffset,
  5264. int primaryoffset,
  5265. int primarylateral,
  5266. int secondaryoffset,
  5267. int secondarylateral,
  5268. bool is_nominal,
  5269. bool is_stealthy,
  5270. bool is_selectable,
  5271. bool is_legal_target,
  5272. bool is_insignificant,
  5273. bool is_immune,
  5274. bool is_theater,
  5275. bool is_turret_equipped,
  5276. bool is_remappable,
  5277. bool is_footprint,
  5278. int rotation,
  5279. SpeedType speed) :
  5280. ObjectTypeClass( rtti,
  5281. id,
  5282. true,
  5283. is_stealthy,
  5284. is_selectable,
  5285. is_legal_target,
  5286. is_insignificant,
  5287. is_immune,
  5288. is_footprint,
  5289. name,
  5290. ininame),
  5291. Remap(remap),
  5292. IsDoubleOwned(false),
  5293. IsInvisible(false),
  5294. IsLeader(false),
  5295. IsScanner(false),
  5296. IsNominal(is_nominal),
  5297. IsTheater(is_theater),
  5298. IsTurretEquipped(is_turret_equipped),
  5299. IsCrew(false),
  5300. IsRepairable(true),
  5301. IsRemappable(is_remappable),
  5302. IsCloakable(false),
  5303. IsSelfHealing(false),
  5304. IsExploding(false),
  5305. MZone(MZONE_NORMAL),
  5306. ThreatRange(0),
  5307. MaxPassengers(0),
  5308. SightRange(0),
  5309. Cost(0),
  5310. Level(-1),
  5311. Prerequisite(STRUCTF_NONE),
  5312. Risk(0),Reward(0),
  5313. MaxSpeed(MPH_IMMOBILE),
  5314. Speed(speed),
  5315. MaxAmmo(-1),
  5316. Ownable(0),
  5317. CameoData(NULL),
  5318. Rotation(rotation),
  5319. ROT(0),
  5320. PrimaryWeapon(NULL),
  5321. SecondaryWeapon(NULL),
  5322. VerticalOffset(verticaloffset),
  5323. PrimaryOffset(primaryoffset),
  5324. PrimaryLateral(primarylateral),
  5325. SecondaryOffset(secondaryoffset),
  5326. SecondaryLateral(secondarylateral),
  5327. Points(0)
  5328. {
  5329. }
  5330. /***********************************************************************************************
  5331. * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. *
  5332. * *
  5333. * This routine is used to find the underlying cost for this object. The underlying cost *
  5334. * does not include any free items that normally come with the object when purchased *
  5335. * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a *
  5336. * harvester. *
  5337. * *
  5338. * INPUT: none *
  5339. * *
  5340. * OUTPUT: Returns with the credit cost of the base object type. *
  5341. * *
  5342. * WARNINGS: none *
  5343. * *
  5344. * HISTORY: *
  5345. * 08/13/1995 JLB : Created. *
  5346. *=============================================================================================*/
  5347. int TechnoTypeClass::Raw_Cost(void) const
  5348. {
  5349. return(Cost);
  5350. }
  5351. /***********************************************************************************************
  5352. * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. *
  5353. * *
  5354. * This routine will return the ownable bits for this object type. The ownable bits are *
  5355. * a bitflag composite of the houses that can own (build) this object type. *
  5356. * *
  5357. * INPUT: none *
  5358. * *
  5359. * OUTPUT: Returns with the ownable bits for this object type. *
  5360. * *
  5361. * WARNINGS: none *
  5362. * *
  5363. * HISTORY: *
  5364. * 07/29/1995 JLB : Created. *
  5365. *=============================================================================================*/
  5366. int TechnoTypeClass::Get_Ownable(void) const
  5367. {
  5368. if (IsDoubleOwned && Session.Type != GAME_NORMAL) {
  5369. return(Ownable | HOUSEF_SOVIET | HOUSEF_ALLIES);
  5370. }
  5371. return(Ownable);
  5372. }
  5373. /***********************************************************************************************
  5374. * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. *
  5375. * *
  5376. * This routine will return the time it takes to construct this object. Usually the time *
  5377. * to produce is directly related to cost. *
  5378. * *
  5379. * INPUT: none *
  5380. * *
  5381. * OUTPUT: Returns with the time to produce this object type. The time is expressed in the *
  5382. * form of game ticks. *
  5383. * *
  5384. * WARNINGS: none *
  5385. * *
  5386. * HISTORY: *
  5387. * 07/29/1995 JLB : Created. *
  5388. *=============================================================================================*/
  5389. int TechnoTypeClass::Time_To_Build(void) const
  5390. {
  5391. return(Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000));
  5392. }
  5393. /***********************************************************************************************
  5394. * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. *
  5395. * *
  5396. * This routine will return the cost to produce an object of this type. *
  5397. * *
  5398. * INPUT: none *
  5399. * *
  5400. * OUTPUT: Returns with the cost to produce one object of this type. *
  5401. * *
  5402. * WARNINGS: none *
  5403. * *
  5404. * HISTORY: *
  5405. * 07/29/1995 JLB : Created. *
  5406. *=============================================================================================*/
  5407. int TechnoTypeClass::Cost_Of(void) const
  5408. {
  5409. return(Cost);
  5410. }
  5411. /***********************************************************************************************
  5412. * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. *
  5413. * *
  5414. * This routine will fetch the cameo (sidebar small image) shape of this object type. *
  5415. * If there is no cameo data available (typical for non-produceable units), then NULL will *
  5416. * be returned. *
  5417. * *
  5418. * INPUT: none *
  5419. * *
  5420. * OUTPUT: Returns with a pointer to the cameo data for this object type if present. *
  5421. * *
  5422. * WARNINGS: none *
  5423. * *
  5424. * HISTORY: *
  5425. * 07/29/1995 JLB : Created. *
  5426. *=============================================================================================*/
  5427. void const * TechnoTypeClass::Get_Cameo_Data(void) const
  5428. {
  5429. return(CameoData);
  5430. }
  5431. /***********************************************************************************************
  5432. * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. *
  5433. * *
  5434. * This routine will return the cost to repair one step. At the TechnoTypeClass level, *
  5435. * this merely serves as a placeholder function. The derived classes will provide a *
  5436. * functional version of this routine. *
  5437. * *
  5438. * INPUT: none *
  5439. * *
  5440. * OUTPUT: Returns with the cost to repair one step. *
  5441. * *
  5442. * WARNINGS: none *
  5443. * *
  5444. * HISTORY: *
  5445. * 07/29/1995 JLB : Created. *
  5446. *=============================================================================================*/
  5447. int TechnoTypeClass::Repair_Cost(void) const
  5448. {
  5449. if (Is_Foot()) {
  5450. return((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent);
  5451. }
  5452. return((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent);
  5453. }
  5454. /***********************************************************************************************
  5455. * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. *
  5456. * *
  5457. * This routine merely serves as placeholder virtual function. The various type classes *
  5458. * will override this routine to return the number of health points to repair in one *
  5459. * "step". *
  5460. * *
  5461. * INPUT: none *
  5462. * *
  5463. * OUTPUT: Returns with the number of health points to repair in one step. *
  5464. * *
  5465. * WARNINGS: none *
  5466. * *
  5467. * HISTORY: *
  5468. * 07/29/1995 JLB : Created. *
  5469. *=============================================================================================*/
  5470. int TechnoTypeClass::Repair_Step(void) const
  5471. {
  5472. if (Is_Foot()) {
  5473. return(Rule.URepairStep);
  5474. }
  5475. return(Rule.RepairStep);
  5476. }
  5477. /***********************************************************************************************
  5478. * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. *
  5479. * *
  5480. * Some objects fire two shots in quick succession. If this is true for this object, then *
  5481. * a 'true' value will be returned from this routine. *
  5482. * *
  5483. * INPUT: none *
  5484. * *
  5485. * OUTPUT: bool; Is this object a two shooter? *
  5486. * *
  5487. * WARNINGS: none *
  5488. * *
  5489. * HISTORY: *
  5490. * 07/29/1996 JLB : Created. *
  5491. *=============================================================================================*/
  5492. bool TechnoTypeClass::Is_Two_Shooter(void) const
  5493. {
  5494. if (PrimaryWeapon != NULL && (PrimaryWeapon == SecondaryWeapon || PrimaryWeapon->Burst > 1)) {
  5495. return(true);
  5496. }
  5497. return(false);
  5498. }
  5499. /***********************************************************************************************
  5500. * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. *
  5501. * *
  5502. * This is a helper routine that will take a decimal percentage number and convert it *
  5503. * into a game based fixed point number. *
  5504. * *
  5505. * INPUT: val -- Decimal percent number to convert. *
  5506. * *
  5507. * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. *
  5508. * *
  5509. * WARNINGS: none *
  5510. * *
  5511. * HISTORY: *
  5512. * 06/17/1996 JLB : Created. *
  5513. *=============================================================================================*/
  5514. static inline int _Scale_To_256(int val)
  5515. {
  5516. val = min(val, 100);
  5517. val = max(val, 0);
  5518. val = ((val * 256) / 100);
  5519. val = min(val, 255);
  5520. return(val);
  5521. }
  5522. /***********************************************************************************************
  5523. * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. *
  5524. * *
  5525. * Use this routine to fill in the data for this techno type class object from the *
  5526. * database specified. Typical use of this is for the rules parsing. *
  5527. * *
  5528. * INPUT: ini -- Reference to the INI database that the information will be lifted from. *
  5529. * *
  5530. * OUTPUT: bool; Was the database used to extract information? A failure (false) response *
  5531. * would mean that the database didn't contain a section that applies to this *
  5532. * techno class object. *
  5533. * *
  5534. * WARNINGS: none *
  5535. * *
  5536. * HISTORY: *
  5537. * 07/19/1996 JLB : Created. *
  5538. *=============================================================================================*/
  5539. bool TechnoTypeClass::Read_INI(CCINIClass & ini)
  5540. {
  5541. if (ini.Is_Present(Name())) {
  5542. #ifdef FIXIT_NAME_OVERRIDE
  5543. char buffer[256];
  5544. int id = ((RTTI+1) * 100) + ID;
  5545. ini.Get_String(Name(), "Name", "", buffer, sizeof(buffer));
  5546. if (strlen(buffer) > 0) {
  5547. #if defined (GERMAN)||(FRENCH)
  5548. for(int xx=0; NewName[xx] != NULL; xx++){
  5549. if(!strcmp(NewName[xx], buffer)){
  5550. memcpy(buffer, "", sizeof(buffer));
  5551. memcpy(buffer, NewName[xx+1], sizeof(buffer));
  5552. break;
  5553. }
  5554. }
  5555. #endif
  5556. /*
  5557. ** Insert the new name text into the buffer list.
  5558. */
  5559. for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) {
  5560. if (NameIDOverride[index] == 0) {
  5561. NameOverride[index] = strdup(buffer);
  5562. NameIDOverride[index] = id;
  5563. // FullName = -(index+1);
  5564. break;
  5565. }
  5566. }
  5567. }
  5568. #endif
  5569. IsDoubleOwned = ini.Get_Bool(Name(), "DoubleOwned", IsDoubleOwned);
  5570. ThreatRange = ini.Get_Lepton(Name(), "GuardRange", ThreatRange);
  5571. IsExploding = ini.Get_Bool(Name(), "Explodes", IsExploding);
  5572. PrimaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Primary", PrimaryWeapon != NULL ? (WeaponType)(PrimaryWeapon->ID) : WEAPON_NONE));
  5573. SecondaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Secondary", SecondaryWeapon != NULL ? (WeaponType)(SecondaryWeapon->ID) : WEAPON_NONE));
  5574. IsCloakable = ini.Get_Bool(Name(), "Cloakable", IsCloakable);
  5575. IsCrushable = ini.Get_Bool(Name(), "Crushable", IsCrushable);
  5576. IsScanner = ini.Get_Bool(Name(), "Sensors", IsScanner);
  5577. Armor = ini.Get_ArmorType(Name(), "Armor", Armor);
  5578. Prerequisite = ini.Get_Buildings(Name(), "Prerequisite", Prerequisite);
  5579. MaxStrength = ini.Get_Int(Name(), "Strength", MaxStrength);
  5580. SightRange = ini.Get_Int(Name(), "Sight", SightRange);
  5581. Level = ini.Get_Int(Name(), "TechLevel", Level);
  5582. MaxSpeed = MPHType(_Scale_To_256(ini.Get_Int(Name(), "Speed", fixed(MaxSpeed, 256) * 100)));
  5583. Cost = ini.Get_Int(Name(), "Cost", Cost);
  5584. MaxAmmo = ini.Get_Int(Name(), "Ammo", MaxAmmo);
  5585. Risk = Reward = Points = ini.Get_Int(Name(), "Points", Points);
  5586. Ownable = ini.Get_Owners(Name(), "Owner", Ownable);
  5587. IsCrew = ini.Get_Bool(Name(), "Crewed", IsCrew);
  5588. IsRepairable = ini.Get_Bool(Name(), "Repairable", IsRepairable);
  5589. IsInvisible = ini.Get_Bool(Name(), "Invisible", IsInvisible);
  5590. IsSelfHealing = ini.Get_Bool(Name(), "SelfHealing", IsSelfHealing);
  5591. ROT = ini.Get_Int(Name(), "ROT", ROT);
  5592. MaxPassengers = ini.Get_Int(Name(), "Passengers", MaxPassengers);
  5593. //Mono_Printf("before image=: %s\n",GraphicName);
  5594. ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName));
  5595. //Mono_Printf("after image=: %s\n",GraphicName);if(Random_Pick(0,4)) Keyboard->Get();
  5596. IsLeader = false;
  5597. if (PrimaryWeapon != NULL && PrimaryWeapon->Attack > 0) {
  5598. IsLeader = true;
  5599. }
  5600. /*
  5601. ** Check to see what zone this object should recognize.
  5602. */
  5603. if (PrimaryWeapon != NULL && PrimaryWeapon->WarheadPtr != NULL && PrimaryWeapon->WarheadPtr->IsWallDestroyer) {
  5604. MZone = MZONE_DESTROYER;
  5605. }
  5606. if (Speed == SPEED_FLOAT) {
  5607. MZone = MZONE_WATER;
  5608. }
  5609. return(true);
  5610. }
  5611. return(false);
  5612. }
  5613. int TechnoTypeClass::Legal_Placement(CELL pos) const
  5614. {
  5615. if (pos == -1) return(0);
  5616. /*
  5617. ** Normal buildings must check to see that every foundation square is free of
  5618. ** obstacles. If this check passes for all foundation squares, only then does the
  5619. ** routine return that it is legal to place.
  5620. */
  5621. short const * offset = Occupy_List(true);
  5622. bool build = (What_Am_I() == RTTI_BUILDINGTYPE);
  5623. while (offset != NULL && *offset != REFRESH_EOL) {
  5624. CELL cell = pos + *offset++;
  5625. if (!Map.In_Radar(cell)) return(false);
  5626. if (build) {
  5627. if (!Map[cell].Is_Clear_To_Build(Speed)) {
  5628. return(0);
  5629. }
  5630. } else {
  5631. if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) {
  5632. return(0);
  5633. }
  5634. }
  5635. }
  5636. return(1);
  5637. }