Object.cpp 204 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE Object.cpp ////////////////////////////////////////////////////////////////////////////////
  24. // Simple base object
  25. // Author: Michael S. Booth, October 2000
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_WEAPONCONDITIONMAP
  30. #include "Common/BitFlagsIO.h"
  31. #include "Common/BuildAssistant.h"
  32. #include "Common/Dict.h"
  33. #include "Common/GameEngine.h"
  34. #include "Common/GameState.h"
  35. #include "Common/ModuleFactory.h"
  36. #include "Common/Player.h"
  37. #include "Common/PlayerList.h"
  38. #include "Common/Radar.h"
  39. #include "Common/SpecialPower.h"
  40. #include "Common/Team.h"
  41. #include "Common/ThingFactory.h"
  42. #include "Common/ThingTemplate.h"
  43. #include "Common/Upgrade.h"
  44. #include "Common/WellKnownKeys.h"
  45. #include "Common/Xfer.h"
  46. #include "Common/XferCRC.h"
  47. #include "Common/PerfTimer.h"
  48. #include "GameClient/Anim2D.h"
  49. #include "GameClient/ControlBar.h"
  50. #include "GameClient/Drawable.h"
  51. #include "GameClient/Eva.h"
  52. #include "GameClient/GameClient.h"
  53. #include "GameClient/InGameUI.h"
  54. #include "GameLogic/AI.h"
  55. #include "GameLogic/AIPathfind.h"
  56. #include "GameLogic/ExperienceTracker.h"
  57. #include "GameLogic/FiringTracker.h"
  58. #include "GameLogic/GameLogic.h"
  59. #include "GameLogic/Locomotor.h"
  60. #include "GameLogic/Module/AIUpdate.h"
  61. #include "GameLogic/Module/AutoHealBehavior.h"
  62. #include "GameLogic/Module/BehaviorModule.h"
  63. #include "GameLogic/Module/BodyModule.h"
  64. #include "GameLogic/Module/CollideModule.h"
  65. #include "GameLogic/Module/ContainModule.h"
  66. #include "GameLogic/Module/CountermeasuresBehavior.h"
  67. #include "GameLogic/Module/CreateModule.h"
  68. #include "GameLogic/Module/DamageModule.h"
  69. #include "GameLogic/Module/DeletionUpdate.h"
  70. #include "GameLogic/Module/DestroyModule.h"
  71. #include "GameLogic/Module/DieModule.h"
  72. #include "GameLogic/Module/DozerAIUpdate.h"
  73. #include "GameLogic/Module/ObjectDefectionHelper.h"
  74. #include "GameLogic/Module/ObjectRepulsorHelper.h"
  75. #include "GameLogic/Module/ObjectSMCHelper.h"
  76. #include "GameLogic/Module/ObjectWeaponStatusHelper.h"
  77. #include "GameLogic/Module/OverchargeBehavior.h"
  78. #include "GameLogic/Module/PhysicsUpdate.h"
  79. #include "GameLogic/Module/PowerPlantUpgrade.h"
  80. #include "GameLogic/Module/ProductionUpdate.h"
  81. #include "GameLogic/Module/RadarUpgrade.h"
  82. #include "GameLogic/Module/RebuildHoleBehavior.h"
  83. #include "GameLogic/Module/SpawnBehavior.h"
  84. #include "GameLogic/Module/SpecialPowerModule.h"
  85. #include "GameLogic/Module/SpecialAbilityUpdate.h"
  86. #include "GameLogic/Module/StatusDamageHelper.h"
  87. #include "GameLogic/Module/StickyBombUpdate.h"
  88. #include "GameLogic/Module/SubdualDamageHelper.h"
  89. #include "GameLogic/Module/TempWeaponBonusHelper.h"
  90. #include "GameLogic/Module/ToppleUpdate.h"
  91. #include "GameLogic/Module/UpdateModule.h"
  92. #include "GameLogic/Module/UpgradeModule.h"
  93. #include "GameLogic/Object.h"
  94. #include "GameLogic/PartitionManager.h"
  95. #include "GameLogic/PolygonTrigger.h"
  96. #include "GameLogic/ScriptEngine.h"
  97. #include "GameLogic/Weapon.h"
  98. #include "GameLogic/WeaponSet.h"
  99. #include "GameLogic/Module/RadarUpdate.h"
  100. #include "GameLogic/Module/PowerPlantUpdate.h"
  101. #include "Common/CRCDebug.h"
  102. #include "Common/MiscAudio.h"
  103. #include "Common/AudioEventInfo.h"
  104. #include "Common/DynamicAudioEventInfo.h"
  105. #ifdef _INTERNAL
  106. // for occasional debugging...
  107. //#pragma optimize("", off)
  108. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  109. #endif
  110. #ifdef DEBUG_OBJECT_ID_EXISTS
  111. ObjectID TheObjectIDToDebug = INVALID_ID;
  112. #endif
  113. // ------------------------------------------------------------------------------------------------
  114. static const ModelConditionFlags s_allWeaponFireFlags[WEAPONSLOT_COUNT] =
  115. {
  116. MAKE_MODELCONDITION_MASK5(
  117. MODELCONDITION_FIRING_A,
  118. MODELCONDITION_BETWEEN_FIRING_SHOTS_A,
  119. MODELCONDITION_RELOADING_A,
  120. MODELCONDITION_PREATTACK_A,
  121. MODELCONDITION_USING_WEAPON_A
  122. ),
  123. MAKE_MODELCONDITION_MASK5(
  124. MODELCONDITION_FIRING_B,
  125. MODELCONDITION_BETWEEN_FIRING_SHOTS_B,
  126. MODELCONDITION_RELOADING_B,
  127. MODELCONDITION_PREATTACK_B,
  128. MODELCONDITION_USING_WEAPON_B
  129. ),
  130. MAKE_MODELCONDITION_MASK5(
  131. MODELCONDITION_FIRING_C,
  132. MODELCONDITION_BETWEEN_FIRING_SHOTS_C,
  133. MODELCONDITION_RELOADING_C,
  134. MODELCONDITION_PREATTACK_C,
  135. MODELCONDITION_USING_WEAPON_C
  136. )
  137. };
  138. //-------------------------------------------------------------------------------------------------
  139. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  140. //-------------------------------------------------------------------------------------------------
  141. //-------------------------------------------------------------------------------------------------
  142. #ifdef DEBUG_LOGGING
  143. AsciiString DescribeObject(const Object *obj)
  144. {
  145. if (!obj)
  146. return "<No Object>";
  147. AsciiString ret;
  148. if (obj->getName().isNotEmpty())
  149. {
  150. ret.format("Object %d (%s) [%s, owned by player %d (%ls)]",
  151. obj->getID(), obj->getName().str(), obj->getTemplate()->getName().str(),
  152. obj->getControllingPlayer()->getPlayerIndex(),
  153. obj->getControllingPlayer()->getPlayerDisplayName().str());
  154. }
  155. else
  156. {
  157. ret.format("Object %d [%s, owned by player %d (%ls)]",
  158. obj->getID(), obj->getTemplate()->getName().str(),
  159. obj->getControllingPlayer()->getPlayerIndex(),
  160. obj->getControllingPlayer()->getPlayerDisplayName().str());
  161. }
  162. return ret;
  163. }
  164. #endif // DEBUG_LOGGING
  165. //-------------------------------------------------------------------------------------------------
  166. //-------------------------------------------------------------------------------------------------
  167. Object::Object( const ThingTemplate *tt, const ObjectStatusMaskType &objectStatusMask, Team *team ) :
  168. Thing(tt),
  169. m_indicatorColor(0),
  170. m_ai(NULL),
  171. m_physics(NULL),
  172. m_geometryInfo(tt->getTemplateGeometryInfo()),
  173. m_containedBy(NULL),
  174. m_xferContainedByID(INVALID_ID),
  175. m_containedByFrame(0),
  176. m_behaviors(NULL),
  177. m_body(NULL),
  178. m_contain(NULL),
  179. m_stealth(NULL),
  180. m_partitionData(NULL),
  181. m_radarData(NULL),
  182. m_drawable(NULL),
  183. m_next(NULL),
  184. m_prev(NULL),
  185. m_team(NULL),
  186. m_experienceTracker(NULL),
  187. m_firingTracker(NULL),
  188. m_repulsorHelper(NULL),
  189. m_statusDamageHelper(NULL),
  190. m_tempWeaponBonusHelper(NULL),
  191. m_subdualDamageHelper(NULL),
  192. m_smcHelper(NULL),
  193. m_wsHelper(NULL),
  194. m_defectionHelper(NULL),
  195. m_partitionLastLook(NULL),
  196. m_partitionRevealAllLastLook(NULL),
  197. m_partitionLastShroud(NULL),
  198. m_partitionLastThreat(NULL),
  199. m_partitionLastValue(NULL),
  200. m_smcUntil(NEVER),
  201. m_privateStatus(0),
  202. m_formationID(NO_FORMATION_ID),
  203. m_isReceivingDifficultyBonus(FALSE),
  204. m_singleUseCommandUsed(FALSE),
  205. m_scriptStatus(0),
  206. m_enteredOrExitedFrame(0),
  207. m_visionSpiedMask (PLAYERMASK_NONE),
  208. m_numTriggerAreasActive(0)
  209. {
  210. #if defined(_DEBUG) || defined(_INTERNAL)
  211. m_hasDiedAlready = false;
  212. #endif
  213. //Modules have not been created yet!
  214. m_modulesReady = false;
  215. // Force the thing template to use the most overridden version of itself - jkmcd
  216. // Note that after this, the object will be using m_template, which forces the usage of the
  217. // most overridden version of tt, so this is okay.
  218. tt = (const ThingTemplate *) tt->getFinalOverride();
  219. Int i, modIdx;
  220. AsciiString modName;
  221. //Added By Sadullah Nader
  222. //Initializations inserted
  223. m_formationOffset.x = m_formationOffset.y = 0.0f;
  224. m_iPos.zero();
  225. //
  226. for (i = 0; i < MAX_PLAYER_COUNT; ++i)
  227. {
  228. m_visionSpiedBy[i] = 0;
  229. }
  230. for( i = 0; i < DISABLED_COUNT; i++ )
  231. {
  232. m_disabledTillFrame[ i ] = NEVER;
  233. }
  234. m_weaponBonusCondition = 0;
  235. m_curWeaponSetFlags.clear();
  236. // sanity
  237. if( TheGameLogic == NULL || tt == NULL )
  238. {
  239. assert( 0 );
  240. return;
  241. } // end if
  242. // Object's set of these persist for the life of the object.
  243. m_partitionLastLook = newInstance(SightingInfo);
  244. m_partitionLastLook->reset();
  245. m_partitionRevealAllLastLook = newInstance(SightingInfo);
  246. m_partitionRevealAllLastLook->reset();
  247. m_partitionLastShroud = newInstance(SightingInfo);
  248. m_partitionLastShroud->reset();
  249. m_partitionLastThreat = newInstance(SightingInfo);
  250. m_partitionLastThreat->reset();
  251. m_partitionLastValue = newInstance(SightingInfo);
  252. m_partitionLastValue->reset();
  253. // must set ID to zero, since some of these set methods
  254. // will cause network messages to be sent
  255. // which use this ID.
  256. m_id = INVALID_ID;
  257. m_producerID = INVALID_ID;
  258. m_builderID = INVALID_ID;
  259. m_status = objectStatusMask;
  260. m_layer = LAYER_GROUND;
  261. m_group = NULL;
  262. m_constructionPercent = CONSTRUCTION_COMPLETE; // complete by default
  263. m_visionRange = tt->friend_calcVisionRange();
  264. m_shroudClearingRange = tt->friend_calcShroudClearingRange();
  265. if( m_shroudClearingRange == -1.0f )
  266. m_shroudClearingRange = m_visionRange;// Backwards compatible, and perfectly logical default to assign
  267. m_shroudRange = 0.0f;
  268. m_singleUseCommandUsed = false;
  269. // assign unique object id
  270. setID( TheGameLogic->allocateObjectID() );
  271. //
  272. // allocate any modules we need to, we should keep
  273. // this at or near the end of the drawable construction so that we have
  274. // all the valid data about the thing when we create the module
  275. //
  276. Int totalModules = tt->getBehaviorModuleInfo().getCount() + NUM_SLEEP_HELPERS; // need to take into account all the helper modules
  277. // allocate the publicModule arrays
  278. // pool[]ify
  279. m_behaviors = MSGNEW("ModulePtrs") BehaviorModule*[totalModules + 1];
  280. BehaviorModule** curB = m_behaviors;
  281. const ModuleInfo& mi = tt->getBehaviorModuleInfo();
  282. // set m_team to null before the first call, to avoid naughtiness...
  283. // If no team is specified in the constructor, then assign the object
  284. // to the neutral team.
  285. setTeam(team ? team : ThePlayerList->getNeutralPlayer()->getDefaultTeam());
  286. // the helpers are done first -- even before Behaviors! -- in case a module needs
  287. // to call something that uses them.
  288. static const NameKeyType smcHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SMCHelper" );
  289. static ObjectSMCHelperModuleData smcModuleData;
  290. smcModuleData.setModuleTagNameKey( smcHelperModuleDataTagNameKey );
  291. m_smcHelper = newInstance(ObjectSMCHelper)(this, &smcModuleData);
  292. *curB++ = m_smcHelper;
  293. //Inactive bodies can't take special damage since they can't take damage
  294. Bool isInactiveBody = FALSE;
  295. for( Int infoIndex = 0; infoIndex < mi.getCount(); ++infoIndex )
  296. {
  297. modName = mi.getNthName(infoIndex);
  298. if (modName.isEmpty())
  299. continue;
  300. if( modName.compare("InactiveBody") == 0 )
  301. {
  302. isInactiveBody = TRUE;
  303. break;
  304. }
  305. }
  306. if( !isInactiveBody )
  307. {
  308. static const NameKeyType statusHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_StatusDamageHelper" );
  309. static StatusDamageHelperModuleData statusModuleData;
  310. statusModuleData.setModuleTagNameKey( statusHelperModuleDataTagNameKey );
  311. m_statusDamageHelper = newInstance(StatusDamageHelper)(this, &statusModuleData);
  312. *curB++ = m_statusDamageHelper;
  313. static const NameKeyType subdualHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SubdualDamageHelper" );
  314. static SubdualDamageHelperModuleData subdualModuleData;
  315. subdualModuleData.setModuleTagNameKey( subdualHelperModuleDataTagNameKey );
  316. m_subdualDamageHelper = newInstance(SubdualDamageHelper)(this, &subdualModuleData);
  317. *curB++ = m_subdualDamageHelper;
  318. }
  319. if (TheAI != NULL
  320. && TheAI->getAiData()->m_enableRepulsors
  321. && isKindOf(KINDOF_CAN_BE_REPULSED))
  322. {
  323. // if we can ever be a temporary-repulsor, make a repulsor helper. (srj)
  324. static const NameKeyType repulsorHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_RepulsorHelper" );
  325. static ObjectRepulsorHelperModuleData repulsorModuleData;
  326. repulsorModuleData.setModuleTagNameKey( repulsorHelperModuleDataTagNameKey );
  327. m_repulsorHelper = newInstance(ObjectRepulsorHelper)(this, &repulsorModuleData);
  328. *curB++ = m_repulsorHelper;
  329. }
  330. /** @todo srj -- figure out how to create this only on demand.
  331. currently we don't have a good way to add/remove update modules from
  332. an object on-the-fly, so we fake it here, and just skip the creation
  333. if it is impossible for this object to ever defect... */
  334. // shrubbery cannot defect. no, really.
  335. if (!tt->isKindOf(KINDOF_SHRUBBERY))
  336. {
  337. static const NameKeyType defectionModuleDataTagNameKey = NAMEKEY( "ModuleTag_DefectionHelper" );
  338. static ObjectDefectionHelperModuleData defectionModuleData;
  339. defectionModuleData.setModuleTagNameKey( defectionModuleDataTagNameKey );
  340. m_defectionHelper = newInstance(ObjectDefectionHelper)(this, &defectionModuleData);
  341. *curB++ = m_defectionHelper;
  342. }
  343. if (tt->canPossiblyHaveAnyWeapon())
  344. {
  345. // we only need a firingtracker and wshelper if we can possibly have a weapon.
  346. static const NameKeyType weaponStatusModuleDataTagNameKey = NAMEKEY( "ModuleTag_WeaponStatusHelper" );
  347. static ObjectWeaponStatusHelperModuleData weaponStatusModuleData;
  348. weaponStatusModuleData.setModuleTagNameKey( weaponStatusModuleDataTagNameKey );
  349. m_wsHelper = newInstance(ObjectWeaponStatusHelper)(this, &weaponStatusModuleData);
  350. *curB++ = m_wsHelper;
  351. static const NameKeyType firingTrackerModuleDataTagNameKey = NAMEKEY( "ModuleTag_FiringTrackerHelper" );
  352. static FiringTrackerModuleData firingTrackerModuleData;
  353. firingTrackerModuleData.setModuleTagNameKey( firingTrackerModuleDataTagNameKey );
  354. m_firingTracker = newInstance(FiringTracker)(this, &firingTrackerModuleData);
  355. *curB++ = m_firingTracker;
  356. static const NameKeyType tempWeaponBonusHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_TempWeaponBonusHelper" );
  357. static TempWeaponBonusHelperModuleData tempWeaponBonusModuleData;
  358. tempWeaponBonusModuleData.setModuleTagNameKey( tempWeaponBonusHelperModuleDataTagNameKey );
  359. m_tempWeaponBonusHelper = newInstance(TempWeaponBonusHelper)(this, &tempWeaponBonusModuleData);
  360. *curB++ = m_tempWeaponBonusHelper;
  361. }
  362. // behaviors are always done first, so they get into the publicModule arrays
  363. // before anything else.
  364. for (modIdx = 0; modIdx < mi.getCount(); ++modIdx)
  365. {
  366. modName = mi.getNthName(modIdx);
  367. if (modName.isEmpty())
  368. continue;
  369. BehaviorModule* newMod = (BehaviorModule*)TheModuleFactory->newModule(this, modName, mi.getNthData(modIdx), MODULETYPE_BEHAVIOR);
  370. *curB++ = newMod;
  371. BodyModuleInterface* body = newMod->getBody();
  372. if (body)
  373. {
  374. DEBUG_ASSERTCRASH(m_body == NULL, ("Duplicate bodies"));
  375. m_body = body;
  376. }
  377. ContainModuleInterface* contain = newMod->getContain();
  378. if (contain)
  379. {
  380. DEBUG_ASSERTCRASH(m_contain == NULL, ("Duplicate containers"));
  381. m_contain = contain;
  382. }
  383. StealthUpdate* stealth = (StealthUpdate*)newMod->getStealth();
  384. if ( stealth )
  385. {
  386. DEBUG_ASSERTCRASH( m_stealth == NULL, ("DuplicateStealthUpdates!") );
  387. m_stealth = stealth;
  388. }
  389. AIUpdateInterface* ai = newMod->getAIUpdateInterface();
  390. if (ai)
  391. {
  392. if( m_ai )
  393. {
  394. DEBUG_ASSERTCRASH( m_ai == NULL, ("%s has more than one AI module. This is illegal!\n", getTemplate()->getName().str()) );
  395. }
  396. m_ai = ai;
  397. }
  398. static NameKeyType key_PhysicsUpdate = NAMEKEY("PhysicsBehavior");
  399. if (newMod->getModuleNameKey() == key_PhysicsUpdate)
  400. {
  401. DEBUG_ASSERTCRASH(m_physics == NULL, ("You should never have more than one Physics module (%s)\n",getTemplate()->getName().str()));
  402. m_physics = (PhysicsBehavior*)newMod;
  403. }
  404. }
  405. *curB = NULL;
  406. AIUpdateInterface *ai = getAIUpdateInterface();
  407. if (ai) {
  408. ai->setAttitude(getTeam()->getPrototype()->getTemplateInfo()->m_initialTeamAttitude);
  409. if (m_team && m_team->getPrototype() && m_team->getPrototype()->getAttackPriorityName().isNotEmpty()) {
  410. AsciiString name = m_team->getPrototype()->getAttackPriorityName();
  411. const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
  412. if (info && info->getName().isNotEmpty()) {
  413. ai->setAttackInfo(info);
  414. }
  415. }
  416. }
  417. // allocate experience tracker
  418. m_experienceTracker = newInstance(ExperienceTracker)(this);
  419. // If a valid team has been assigned me, then I have a Player I can ask about my starting level
  420. const Player* controller = getControllingPlayer();
  421. m_experienceTracker->setVeterancyLevel( controller->getProductionVeterancyLevel( getTemplate()->getName() ) );
  422. /// allow for inter-Module resolution
  423. for (BehaviorModule** b = m_behaviors; *b; ++b)
  424. {
  425. (*b)->onObjectCreated();
  426. }
  427. m_numTriggerAreasActive = 0;
  428. m_enteredOrExitedFrame = 0;
  429. m_isSelectable = tt->isKindOf(KINDOF_SELECTABLE);
  430. m_healthBoxOffset.zero();// this is used for units that are amorphous, like angry mob
  431. //Modules have now been completely created!
  432. m_modulesReady = true;
  433. TheRadar->addObject( this );
  434. // register the object with the GameLogic
  435. TheGameLogic->registerObject( this );
  436. //disable occlusion for some time after object is created to allow them to exit the factory/building.
  437. m_safeOcclusionFrame = TheGameLogic->getFrame()+tt->getOcclusionDelay();
  438. m_soleHealingBenefactorID = INVALID_ID; ///< who is the only other object that can give me this non-stacking heal benefit?
  439. m_soleHealingBenefactorExpirationFrame = 0; ///< on what frame can I accept healing (thus to switch) from a new benefactor
  440. } // end Object
  441. //-------------------------------------------------------------------------------------------------
  442. /** Emit message announcing object's creation
  443. * Note: Have to do this in virtual init() method because virtual methods
  444. * don't become virtual until AFTER the constructor has completed, and we
  445. * need to send our type in this message via virtual getType(). */
  446. //-------------------------------------------------------------------------------------------------
  447. void Object::initObject()
  448. {
  449. // Weapons & Damage -------------------------------------------------------------------------------------------------
  450. // Force the initial weapon set to be instantiated & reloaded.
  451. //GS No Bad Wrong
  452. // The flags are constructed to empty, and between then and now they may be set in valid ways by onCreate modules.
  453. // We don't want to blow that away. updateWeaponSet is safe to call on its own, so I will move that to the end.
  454. // m_curWeaponSetFlags.clear();
  455. // m_weaponSet.updateWeaponSet(this);
  456. // m_weaponBonusCondition = 0;
  457. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  458. m_lastWeaponCondition[i] = WSF_INVALID;
  459. // emit message announcing object's creation
  460. TheGameLogic->sendObjectCreated( this );
  461. // If I have a valid team assigned, I can run through my Upgrade modules with his flags
  462. updateUpgradeModules();
  463. //If the player has battle plans (America Strategy Center), then apply those bonuses
  464. //to this object if applicable. Internally it validates certain kinds of objects.
  465. const Player* controller = getControllingPlayer();
  466. if (controller)
  467. {
  468. if (!getReceivingDifficultyBonus() && TheScriptEngine->getObjectsShouldReceiveDifficultyBonus())
  469. {
  470. setReceivingDifficultyBonus(TRUE);
  471. }
  472. if (controller->getNumBattlePlansActive() > 0)
  473. {
  474. controller->applyBattlePlanBonusesForObject( this );
  475. }
  476. }
  477. //For each special power module that we have, add it's type to the specialpower bits. This is
  478. //for optimal access later.
  479. for (BehaviorModule** m = m_behaviors; *m; ++m)
  480. {
  481. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  482. if (!sp)
  483. continue;
  484. const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
  485. if( spTemplate )
  486. {
  487. SET_SPECIALPOWERMASK( m_specialPowerBits, spTemplate->getSpecialPowerType() );
  488. }
  489. }
  490. // Kris -- All missiles must be projectiles! This is the perfect place to assert them!
  491. // srj: yes, but only in debug...
  492. #if defined(_DEBUG) || defined(_INTERNAL)
  493. if( !isKindOf( KINDOF_PROJECTILE ) )
  494. {
  495. if( isKindOf( KINDOF_SMALL_MISSILE ) || isKindOf( KINDOF_BALLISTIC_MISSILE ) )
  496. {
  497. //Warning only...
  498. DEBUG_CRASH( ("Missile %s must also be a KindOf = PROJECTILE in addition to being either a SMALL_MISSILE or PROJECTILE_MISSILE -- call Kris (36844) for questions!", getTemplate()->getName().str() ) );
  499. }
  500. }
  501. #endif
  502. if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
  503. // Notify script conditions to update conditions that consider unit counts.
  504. // We ignore projectiles cause they are frequently created & destroyed, and are not
  505. // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
  506. TheScriptEngine->notifyOfObjectCreationOrDestruction();
  507. TheGameLogic->updateObjectsChangedTriggerAreas();
  508. }
  509. // Everything (like weaponSet flags) is inited, so check if the WeaponSet needs to change.
  510. m_weaponSet.updateWeaponSet(this);
  511. if( isKindOf( KINDOF_MINE ) || isKindOf( KINDOF_BOOBY_TRAP ) || isKindOf( KINDOF_DEMOTRAP ) )
  512. {
  513. ThePlayerList->getNeutralPlayer()->getAcademyStats()->recordMine();
  514. }
  515. }
  516. //-------------------------------------------------------------------------------------------------
  517. //-------------------------------------------------------------------------------------------------
  518. Object::~Object()
  519. {
  520. // tell the AI the building is gone
  521. /// @todo Generalize the notion of objects entering and leaving the world, so we don't have to special case this
  522. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
  523. if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
  524. // Notify script conditions to update conditions that consider unit counts.
  525. // We ignore projectiles cause they are frequently created & destroyed, and are not
  526. // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
  527. TheGameLogic->updateObjectsChangedTriggerAreas();
  528. TheScriptEngine->notifyOfObjectCreationOrDestruction();
  529. }
  530. //
  531. // remove from radar before we NULL out the team ... the order of ops are critical here
  532. // because the radar code will sometimes look at the team info and it is assumed through
  533. // the team and player code that the team is valid
  534. //
  535. if( m_radarData )
  536. TheRadar->removeObject( this );
  537. // emit message announcing object's destruction. Again, order is important; we must do this
  538. // before wiping out the team.
  539. TheGameLogic->sendObjectDestroyed( this );
  540. // empty the team
  541. setTeam( NULL );
  542. // Object's set of these persist for the life of the object.
  543. m_partitionLastLook->deleteInstance();
  544. m_partitionLastLook = NULL;
  545. m_partitionRevealAllLastLook->deleteInstance();
  546. m_partitionRevealAllLastLook = NULL;
  547. m_partitionLastShroud->deleteInstance();
  548. m_partitionLastShroud = NULL;
  549. m_partitionLastThreat->deleteInstance();
  550. m_partitionLastThreat = NULL;
  551. m_partitionLastValue->deleteInstance();
  552. m_partitionLastValue = NULL;
  553. // remove the object from the partition system if present
  554. if( m_partitionData )
  555. ThePartitionManager->unRegisterObject( this );
  556. // if we are in a group, remove us
  557. if (m_group)
  558. m_group->remove( this );
  559. // note, do NOT free these, there are just a shadow copy!
  560. m_ai = NULL;
  561. m_physics = NULL;
  562. // delete any modules present
  563. for (BehaviorModule** b = m_behaviors; *b; ++b)
  564. {
  565. (*b)->deleteInstance();
  566. *b = NULL; // in case other modules call findModule from their dtor!
  567. }
  568. delete [] m_behaviors;
  569. m_behaviors = NULL;
  570. if( m_experienceTracker )
  571. m_experienceTracker->deleteInstance();
  572. m_experienceTracker = NULL;
  573. // we don't need to delete these, there were deleted on the m_behaviors list
  574. m_firingTracker = NULL;
  575. m_repulsorHelper = NULL;
  576. m_statusDamageHelper = NULL;
  577. m_tempWeaponBonusHelper = NULL;
  578. m_subdualDamageHelper = NULL;
  579. m_smcHelper = NULL;
  580. m_wsHelper = NULL;
  581. m_defectionHelper = NULL;
  582. // reset id to zero so we never mistaken grab "dead" objects
  583. m_id = INVALID_ID;
  584. // Instead of removing it from the named cache, notify the script engine that it has died.
  585. // The script engine will remove it from the cache if necessary. The script engine needs to take
  586. // a crack at this in case it is the current "This Object" pointer.
  587. TheScriptEngine->notifyOfObjectDestruction(this);
  588. }
  589. //-------------------------------------------------------------------------------------------------
  590. /// this object now contained in "containedBy"
  591. //-------------------------------------------------------------------------------------------------
  592. void Object::onContainedBy( Object *containedBy )
  593. {
  594. setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNSELECTABLE ) );
  595. if (containedBy && containedBy->getContain()->isEnclosingContainerFor(this))
  596. setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
  597. else
  598. clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
  599. m_containedBy = containedBy;
  600. m_containedByFrame = TheGameLogic->getFrame();
  601. handlePartitionCellMaintenance(); // which should unlook me now that I am contained
  602. }
  603. //-------------------------------------------------------------------------------------------------
  604. /// this object no longer contained in "containedBy"
  605. //-------------------------------------------------------------------------------------------------
  606. void Object::onRemovedFrom( Object *removedFrom )
  607. {
  608. clearStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_MASKED, OBJECT_STATUS_UNSELECTABLE ) );
  609. m_containedBy = NULL;
  610. m_containedByFrame = 0;
  611. handlePartitionCellMaintenance(); // get a clean look, now that I am outdoors, again
  612. }
  613. //-------------------------------------------------------------------------------------------------
  614. //-------------------------------------------------------------------------------------------------
  615. Int Object::getTransportSlotCount() const
  616. {
  617. Int count = getTemplate()->getRawTransportSlotCount();
  618. ContainModuleInterface* contain = getContain();
  619. if ( contain && contain->isSpecialZeroSlotContainer() )
  620. {
  621. count = 0;
  622. const ContainedItemsList* items = contain->getContainedItemsList();
  623. if (items)
  624. {
  625. for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
  626. {
  627. count += (*it)->getTransportSlotCount();
  628. }
  629. }
  630. }
  631. return count;
  632. }
  633. //-------------------------------------------------------------------------------------------------
  634. /** Run from GameLogic::destroyObject */
  635. //-------------------------------------------------------------------------------------------------
  636. void Object::onDestroy()
  637. {
  638. // This is the old cleanUpContain safeguard. Say goodbye so they don't try to look us up.
  639. if( m_containedBy && m_containedBy->getContain() )
  640. {
  641. m_containedBy->getContain()->removeFromContain( this );
  642. }
  643. //
  644. // run the onDelete on all modules present so they each have an opportunity to cleanup
  645. // anything they need to ... including talking to any other modules
  646. //
  647. for (BehaviorModule** b = m_behaviors; *b; ++b)
  648. {
  649. (*b)->onDelete();
  650. }
  651. //Have to remove ourself from looking as well. RebuildHoleWorkers definately hit here.
  652. handlePartitionCellMaintenance();
  653. } // end onDestroy
  654. //=============================================================================
  655. //=============================================================================
  656. void Object::setGeometryInfo(const GeometryInfo& geom)
  657. {
  658. m_geometryInfo = geom;
  659. if( m_partitionData )
  660. {
  661. // if our geometry changes, we unregister and re-register with the partitionmgr
  662. // so that our size gets updated appropriately. this shouldn't be a problem
  663. // unless setGeometryInfo gets called frequently. (srj)
  664. ThePartitionManager->unRegisterObject( this );
  665. ThePartitionManager->registerObject( this );
  666. }
  667. if (m_drawable)
  668. m_drawable->reactToGeometryChange();
  669. }
  670. //=============================================================================
  671. //=============================================================================
  672. void Object::setGeometryInfoZ( Real newZ )
  673. {
  674. // A Z change only does not need to un/register with the PartitionManager
  675. m_geometryInfo.setMaxHeightAbovePosition( newZ );
  676. if (m_drawable)
  677. m_drawable->reactToGeometryChange();
  678. }
  679. //=============================================================================
  680. void Object::friend_setUndetectedDefector( Bool status )
  681. {
  682. if (status)
  683. m_privateStatus |= UNDETECTED_DEFECTOR;
  684. else
  685. m_privateStatus &= ~UNDETECTED_DEFECTOR;
  686. }
  687. //=============================================================================
  688. void Object::restoreOriginalTeam()
  689. {
  690. if( m_team == NULL || m_originalTeamName.isEmpty() )
  691. return;
  692. Team* origTeam = TheTeamFactory->findTeam(m_originalTeamName);
  693. if (origTeam == NULL)
  694. {
  695. DEBUG_CRASH(("Object original team (%s) could not be found or created! (srj)\n",m_originalTeamName.str()));
  696. return;
  697. }
  698. if (m_team == origTeam)
  699. {
  700. DEBUG_CRASH(("Object appears to still be on its original team, so why are we attempting to restore it? (srj)\n"));
  701. return;
  702. }
  703. setTeam(origTeam);
  704. }
  705. //=============================================================================
  706. //=============================================================================
  707. void Object::setTeam( Team *team )
  708. {
  709. // In order to prevent spawning useful units for a player after he dies, we
  710. // just assign objects to the neutral player if we try to misbehave.
  711. if (team && !team->getControllingPlayer()->isPlayerActive())
  712. team = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
  713. setTemporaryTeam(team);
  714. m_originalTeamName = m_team ? m_team->getName() : AsciiString::TheEmptyString;
  715. }
  716. //=============================================================================
  717. //=============================================================================
  718. void Object::setTemporaryTeam( Team *team )
  719. {
  720. const Bool restoring = false;
  721. setOrRestoreTeam(team, restoring);
  722. }
  723. //=============================================================================
  724. //=============================================================================
  725. void Object::setOrRestoreTeam( Team* team, Bool restoring )
  726. {
  727. // don't do anything if the team hasn't changed
  728. if( m_team == team )
  729. return;
  730. Team* oldTeam = m_team;
  731. // Before Switch //////////////////////////
  732. if (m_team)
  733. {
  734. if (m_team->isInList_TeamMemberList(this))
  735. {
  736. m_team->removeFrom_TeamMemberList(this);
  737. m_team->getControllingPlayer()->becomingTeamMember(this, false);
  738. }
  739. }
  740. // Switch //////////////////////////
  741. m_team = team;
  742. // After Switch //////////////////////////
  743. if (m_team)
  744. {
  745. if (!m_team->isInList_TeamMemberList(this))
  746. {
  747. m_team->prependTo_TeamMemberList(this);
  748. m_team->getControllingPlayer()->becomingTeamMember(this, true);
  749. }
  750. // now, adjust the attitude of the unit to its new team.
  751. const TeamPrototype* proto = m_team->getPrototype();
  752. if (proto && proto->getTemplateInfo())
  753. {
  754. AIUpdateInterface *ai = getAIUpdateInterface();
  755. if (ai)
  756. {
  757. ai->setAttitude(proto->getTemplateInfo()->m_initialTeamAttitude);
  758. if (proto->getAttackPriorityName().isNotEmpty()) {
  759. AsciiString name = proto->getAttackPriorityName();
  760. const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
  761. if (info && info->getName().isNotEmpty()) {
  762. ai->setAttackInfo(info);
  763. }
  764. }
  765. }
  766. }
  767. // emit message announcing object's new alliance
  768. Drawable *draw = getDrawable();
  769. if (draw)
  770. draw->changedTeam();
  771. }
  772. // This can't just go in ::defect, because some things just do setTeam. The act of
  773. // setting a new team needs to tell the modules and do other important stuff.
  774. // And it needs to happen after the switch.
  775. if( oldTeam && team && !restoring )
  776. onCapture( oldTeam->getControllingPlayer(), team->getControllingPlayer() );
  777. //
  778. // the team changed we have a change in priorities on the radar if we are
  779. // a candidate for the radar as it is
  780. //
  781. if( m_radarData )
  782. {
  783. // removing it and adding it will cause a resort to happen
  784. TheRadar->removeObject( this );
  785. TheRadar->addObject( this );
  786. }
  787. // Tell TheInGameUI that the object has changed hands
  788. Int oldPlayerIndex = (oldTeam)?(oldTeam->getControllingPlayer()->getPlayerIndex()):-1;
  789. Int newPlayerIndex = (m_team)?(m_team->getControllingPlayer()->getPlayerIndex()):-1;
  790. if (oldPlayerIndex != newPlayerIndex)
  791. TheInGameUI->objectChangedTeam(this, oldPlayerIndex, newPlayerIndex);
  792. }
  793. //=============================================================================
  794. enum
  795. {
  796. BOOBY_TRAP_SCAN_RANGE = 25
  797. };
  798. Bool Object::checkAndDetonateBoobyTrap(const Object *victim)
  799. {
  800. if( !testStatus(OBJECT_STATUS_BOOBY_TRAPPED) )
  801. return FALSE;
  802. PartitionFilterAcceptByKindOf kindFilter(MAKE_KINDOF_MASK(KINDOF_BOOBY_TRAP), KINDOFMASK_NONE);
  803. PartitionFilterSameMapStatus filterMapStatus(this);
  804. PartitionFilter *filters[3];
  805. filters[0] = &kindFilter;
  806. filters[1] = &filterMapStatus;
  807. filters[2] = NULL;
  808. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( getPosition(), BOOBY_TRAP_SCAN_RANGE + getGeometryInfo().getBoundingCircleRadius(),
  809. FROM_CENTER_2D, filters, ITER_SORTED_NEAR_TO_FAR );
  810. MemoryPoolObjectHolder hold(iter);// This is the magic thing that frees the dynamically made iter in its destructor
  811. Object *ourBoobyTrap = NULL;
  812. for( Object *other = iter->first(); other; other = iter->next() )
  813. {
  814. if( other->getProducerID() == getID() )// Sticky bombs call the thing they are on their producer for just such an occasion
  815. {
  816. ourBoobyTrap = other;
  817. break;
  818. }
  819. }
  820. if( ourBoobyTrap )
  821. {
  822. static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
  823. StickyBombUpdate *update = (StickyBombUpdate*)ourBoobyTrap->findUpdateModule( key_StickyBombUpdate );
  824. if( update )
  825. {
  826. if( victim && ourBoobyTrap->getControllingPlayer()->getRelationship(victim->getTeam()) == ALLIES )
  827. return FALSE;// Friends don't touch friends boobies.
  828. update->detonate();
  829. return TRUE;// Booby Trapped status will be cleared by stickybomb, as they set it
  830. }
  831. }
  832. return FALSE;
  833. }
  834. //=============================================================================
  835. void Object::setStatus( ObjectStatusMaskType objectStatus, Bool set )
  836. {
  837. ObjectStatusMaskType oldStatus = m_status;
  838. if (set)
  839. m_status.set( objectStatus );
  840. else
  841. m_status.clear( objectStatus );
  842. if (m_status != oldStatus)
  843. {
  844. if( set && objectStatus.test( OBJECT_STATUS_REPULSOR ) && m_repulsorHelper != NULL )
  845. {
  846. // Damaged repulsable civilians scare (repulse) other civs, but only
  847. // for a short amount of time... use the repulsor helper to turn off repulsion shortly.
  848. m_repulsorHelper->sleepUntil(TheGameLogic->getFrame() + 2*LOGICFRAMES_PER_SECOND);
  849. }
  850. if( objectStatus.test( OBJECT_STATUS_STEALTHED ) || objectStatus.test( OBJECT_STATUS_DETECTED ) || objectStatus.test( OBJECT_STATUS_DISGUISED ) )
  851. {
  852. //Kris: Aug 20, 2003
  853. //When any of the three key status bits for stealth go on or off, then handle partition updates for vision.
  854. if( getTemplate()->getShroudRevealToAllRange() > 0.0f )
  855. {
  856. handlePartitionCellMaintenance();
  857. }
  858. }
  859. // when an object's construction status changes, it needs to have its partition data updated,
  860. // in order to maintain the shroud correctly.
  861. if( m_status.test( OBJECT_STATUS_UNDER_CONSTRUCTION ) != oldStatus.test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  862. {
  863. // CHECK FOR MINES, AND DETONATE THEM NOW
  864. ObjectIterator *iter =
  865. ThePartitionManager->iteratePotentialCollisions( getPosition(), getGeometryInfo(), getOrientation() );
  866. MemoryPoolObjectHolder hold( iter );
  867. Object *them;
  868. for( them = iter->first(); them; them = iter->next() )
  869. {
  870. if (them->isKindOf( KINDOF_MINE ))
  871. {
  872. //DETONATE ANY ENEMY MINES, OR DELETE FRIENDLY ONES
  873. Relationship r = getRelationship(them);
  874. if (r == ENEMIES)
  875. {
  876. them->kill(); // detonate mine
  877. }
  878. else
  879. {
  880. TheGameLogic->destroyObject(them);
  881. }
  882. }
  883. }// next object
  884. if (m_partitionData)
  885. m_partitionData->makeDirty(true);
  886. }
  887. }
  888. }
  889. //=============================================================================
  890. void Object::setScriptStatus( ObjectScriptStatusBit bit, Bool set )
  891. {
  892. UnsignedInt oldScriptStatus = m_scriptStatus;
  893. if( set )
  894. {
  895. m_scriptStatus |= bit;
  896. }
  897. else
  898. {
  899. m_scriptStatus &= ~bit;
  900. }
  901. if( m_scriptStatus != oldScriptStatus )
  902. {
  903. if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) )
  904. {
  905. if( m_partitionData )
  906. {
  907. // if an object becomes disabled or unpowered, then you have to update its partition data because it will
  908. // change how far it can see.
  909. m_partitionData->makeDirty(true);
  910. }
  911. if( m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED )
  912. {
  913. //I am now disabled, so tell the main game engine!
  914. setDisabled( DISABLED_SCRIPT_DISABLED );
  915. }
  916. else
  917. {
  918. //I am no longer disabled, so tell the main game engine!
  919. clearDisabled( DISABLED_SCRIPT_DISABLED );
  920. }
  921. }
  922. if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) )
  923. {
  924. if( m_partitionData )
  925. {
  926. // if an object becomes disabled or unpowered, then you have to update its partition data because it will
  927. // change how far it can see.
  928. m_partitionData->makeDirty(true);
  929. }
  930. if( m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED )
  931. {
  932. //I am now underpowered, so tell the main game engine!
  933. setDisabled( DISABLED_SCRIPT_UNDERPOWERED );
  934. }
  935. else
  936. {
  937. //I am no longer undperpowered, so tell the main game engine!
  938. clearDisabled( DISABLED_SCRIPT_UNDERPOWERED );
  939. }
  940. }
  941. }
  942. }
  943. //=============================================================================
  944. Bool Object::canCrushOrSquish(Object *otherObj, CrushSquishTestType testType ) const
  945. {
  946. DEBUG_ASSERTCRASH(this, ("null this in canCrushOrSquish"));
  947. if( !otherObj )
  948. {
  949. //Can't crush anything.
  950. return false;
  951. }
  952. if( isDisabledByType( DISABLED_UNMANNED ) )
  953. {
  954. //Unmanned vehicles cannot crush troops. This was happening when Jarmen Kell sniped
  955. //the vehicle and booted the guys out while still moving, as the vehicle is now
  956. //on a different team.
  957. return false;
  958. }
  959. UnsignedByte crusherLevel = getCrusherLevel();
  960. // order matters: we want to know if I consider it to be an ally, not vice versa
  961. if( getRelationship( otherObj ) == ALLIES )
  962. {
  963. //Friends don't let friends crush friends.
  964. return false;
  965. }
  966. if( !crusherLevel )
  967. {
  968. //Can't crush anything!
  969. return false;
  970. }
  971. //Test this case for generic infantry getting squished by vehicles!
  972. if( testType == TEST_SQUISH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
  973. {
  974. //****************************************************************************************
  975. //NOTE: This section of code is used by the pathfinder to determine if the object should
  976. // move to the target. I don't think it's the right place to check for this because
  977. // the semantics check to see if we can squish something -- not approach it. However
  978. // I'm not moving it for fear of some major breakage! -- KM
  979. //Bool squisher = crusherLevel > 0;
  980. //if( !squisher )
  981. //{
  982. // Weapon *weapon = getCurrentWeapon();
  983. // if( weapon && weapon->isContactWeapon() )
  984. // {
  985. // squisher = true;
  986. // }
  987. //}
  988. //if( squisher )
  989. //NOTE2: *** IF YOU REENABLE THIS CODE -- Move the "if( !crusherLevel ) return false" below
  990. // this squish section.
  991. //****************************************************************************************
  992. {
  993. // See if other is squishable
  994. static NameKeyType key_squish = NAMEKEY( "SquishCollide" );
  995. if( otherObj->findModule( key_squish ) )
  996. {
  997. return true; // squishable.
  998. }
  999. }
  1000. }
  1001. UnsignedByte crushableLevel = otherObj->getCrushableLevel();
  1002. if( testType == TEST_CRUSH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
  1003. {
  1004. if( crusherLevel > crushableLevel )
  1005. {
  1006. return true;
  1007. }
  1008. }
  1009. return false;
  1010. }
  1011. //-------------------------------------------------------------------------------------------------
  1012. UnsignedByte Object::getCrusherLevel() const
  1013. {
  1014. return getTemplate()->getCrusherLevel();
  1015. }
  1016. //-------------------------------------------------------------------------------------------------
  1017. UnsignedByte Object::getCrushableLevel() const
  1018. {
  1019. return getTemplate()->getCrushableLevel();
  1020. }
  1021. // ------------------------------------------------------------------------------------------------
  1022. /** Topple an object, if possible */
  1023. // ------------------------------------------------------------------------------------------------
  1024. void Object::topple( const Coord3D *toppleDirection, Real toppleSpeed, UnsignedInt options )
  1025. {
  1026. static NameKeyType key_ToppleUpdate = NAMEKEY("ToppleUpdate");
  1027. ToppleUpdate* toppleUpdate = (ToppleUpdate*)findModule(key_ToppleUpdate);
  1028. if( toppleUpdate && toppleUpdate->isAbleToBeToppled() )
  1029. {
  1030. // apply the topple force
  1031. toppleUpdate->applyTopplingForce( toppleDirection, toppleSpeed, options );
  1032. } // end if
  1033. } // end topple
  1034. //=============================================================================
  1035. void Object::setArmorSetFlag(ArmorSetType ast)
  1036. {
  1037. m_body->setArmorSetFlag(ast);
  1038. }
  1039. //=============================================================================
  1040. void Object::clearArmorSetFlag(ArmorSetType ast)
  1041. {
  1042. m_body->clearArmorSetFlag(ast);
  1043. }
  1044. //=============================================================================
  1045. Bool Object::testArmorSetFlag(ArmorSetType ast) const
  1046. {
  1047. return m_body->testArmorSetFlag(ast);
  1048. }
  1049. //=============================================================================
  1050. void Object::reloadAllAmmo(Bool now)
  1051. {
  1052. m_weaponSet.reloadAllAmmo(this, now);
  1053. }
  1054. //=============================================================================
  1055. Bool Object::isOutOfAmmo() const
  1056. {
  1057. return m_weaponSet.isOutOfAmmo();
  1058. }
  1059. //=============================================================================
  1060. Bool Object::hasAnyWeapon() const
  1061. {
  1062. return m_weaponSet.hasAnyWeapon();
  1063. }
  1064. //=============================================================================
  1065. Bool Object::hasAnyDamageWeapon() const
  1066. {
  1067. //First check to see if we have any weapons -- if not return false.
  1068. if( !m_weaponSet.hasAnyDamageWeapon() )
  1069. {
  1070. return FALSE;
  1071. }
  1072. return TRUE;
  1073. }
  1074. //=============================================================================
  1075. UnsignedInt Object::getMostPercentReadyToFireAnyWeapon() const
  1076. {
  1077. return m_weaponSet.getMostPercentReadyToFireAnyWeapon();
  1078. }
  1079. //=============================================================================
  1080. Bool Object::hasWeaponToDealDamageType(DamageType typeToDeal) const
  1081. {
  1082. return m_weaponSet.hasWeaponToDealDamageType(typeToDeal);
  1083. }
  1084. //=============================================================================
  1085. Real Object::getLargestWeaponRange() const
  1086. {
  1087. Real retVal = -1;
  1088. for (Int i = PRIMARY_WEAPON; i < WEAPONSLOT_COUNT; ++i) {
  1089. Weapon* weapon = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  1090. if (!weapon) {
  1091. continue;
  1092. }
  1093. Real tmpVal = weapon->getAttackRange(this);
  1094. if (tmpVal > retVal) {
  1095. retVal = tmpVal;
  1096. }
  1097. }
  1098. return retVal;
  1099. }
  1100. //=============================================================================
  1101. void Object::setFiringConditionForCurrentWeapon() const
  1102. {
  1103. if (m_drawable)
  1104. {
  1105. WeaponSlotType wslot = m_weaponSet.getCurWeaponSlot();
  1106. ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot(wslot, WSF_FIRING);
  1107. m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[wslot], c);
  1108. }
  1109. }
  1110. //=============================================================================
  1111. void Object::setModelConditionState( ModelConditionFlagType a )
  1112. {
  1113. if (m_drawable)
  1114. {
  1115. m_drawable->setModelConditionState(a);
  1116. }
  1117. }
  1118. //=============================================================================
  1119. void Object::clearModelConditionState( ModelConditionFlagType a )
  1120. {
  1121. if (m_drawable)
  1122. {
  1123. m_drawable->clearModelConditionState(a);
  1124. }
  1125. }
  1126. //=============================================================================
  1127. void Object::clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set )
  1128. {
  1129. if (m_drawable)
  1130. {
  1131. m_drawable->clearAndSetModelConditionState(clr, set);
  1132. }
  1133. }
  1134. //=============================================================================
  1135. void Object::clearModelConditionFlags( const ModelConditionFlags& clr )
  1136. {
  1137. if (m_drawable)
  1138. {
  1139. m_drawable->clearModelConditionFlags(clr);
  1140. }
  1141. }
  1142. //=============================================================================
  1143. void Object::setModelConditionFlags( const ModelConditionFlags& set )
  1144. {
  1145. if (m_drawable)
  1146. {
  1147. m_drawable->setModelConditionFlags(set);
  1148. }
  1149. }
  1150. //=============================================================================
  1151. void Object::clearAndSetModelConditionFlags( const ModelConditionFlags& clr, const ModelConditionFlags& set )
  1152. {
  1153. if (m_drawable)
  1154. {
  1155. m_drawable->clearAndSetModelConditionFlags(clr, set);
  1156. }
  1157. }
  1158. //=============================================================================
  1159. // Special model states are states that are turned on for a period of time, and
  1160. // turned off automatically -- used for cheer, and scripted special moment
  1161. // animations. Setting a special state will automatically clear any other
  1162. // special states that may be turned on so you can only have one at a time.
  1163. //=============================================================================
  1164. void Object::setSpecialModelConditionState( ModelConditionFlagType set, UnsignedInt frames )
  1165. {
  1166. clearSpecialModelConditionStates();
  1167. setModelConditionState( set );
  1168. if( frames == 0 )
  1169. {
  1170. frames = 1;
  1171. }
  1172. m_smcUntil = TheGameLogic->getFrame() + frames;
  1173. m_smcHelper->sleepUntil(m_smcUntil);
  1174. }
  1175. //=============================================================================
  1176. void Object::clearSpecialModelConditionStates()
  1177. {
  1178. clearModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_SPECIAL_CHEERING ) );
  1179. m_smcUntil = NEVER;
  1180. }
  1181. // Lorenzen has some interest in this, ask before deleting
  1182. //=============================================================================
  1183. //const ModelConditionFlags& Object::getModelConditionFlags() const
  1184. //{
  1185. // if (m_drawable)
  1186. // {
  1187. // return m_drawable->getModelConditionFlags();
  1188. // }
  1189. // else
  1190. // {
  1191. // DEBUG_CRASH(("NULL Drawable at this point, you can't get modelconditionflags now."));
  1192. // static ModelConditionFlags noFlags;
  1193. // return noFlags;
  1194. // }
  1195. //}
  1196. //=============================================================================
  1197. Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot)
  1198. {
  1199. if (!m_weaponSet.hasAnyWeapon())
  1200. return NULL;
  1201. if (wslot)
  1202. *wslot = m_weaponSet.getCurWeaponSlot();
  1203. return m_weaponSet.getCurWeapon();
  1204. }
  1205. //=============================================================================
  1206. const Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot) const
  1207. {
  1208. if (!m_weaponSet.hasAnyWeapon())
  1209. return NULL;
  1210. if (wslot)
  1211. *wslot = m_weaponSet.getCurWeaponSlot();
  1212. return m_weaponSet.getCurWeapon();
  1213. }
  1214. //=============================================================================
  1215. Weapon* Object::findWaypointFollowingCapableWeapon()
  1216. {
  1217. return m_weaponSet.findWaypointFollowingCapableWeapon();
  1218. }
  1219. //=============================================================================
  1220. Bool Object::getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const
  1221. {
  1222. /// @todo srj -- may need to cache this inside weaponset.
  1223. const Weapon* w = m_weaponSet.findAmmoPipShowingWeapon();
  1224. if (w)
  1225. {
  1226. numTotal = w->getClipSize();
  1227. numFull = w->getRemainingAmmo();
  1228. return true;
  1229. }
  1230. else
  1231. {
  1232. return false;
  1233. }
  1234. }
  1235. //=============================================================================
  1236. /*
  1237. NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
  1238. since that isn't an incredibly fast call, and this is called repeatedly in some inner loops
  1239. where we already know that isAbleToAttack() == true. so you should always
  1240. call isAbleToAttack prior to calling this! (srj)
  1241. */
  1242. CanAttackResult Object::getAbleToAttackSpecificObject( AbleToAttackType t, const Object* target, CommandSourceType commandSource, WeaponSlotType specificSlot ) const
  1243. {
  1244. // NO! BAD! WRONG!
  1245. // If we can't attack at all, then we cannot attack this
  1246. //if (!isAbleToAttack())
  1247. // return FALSE;
  1248. // Otherwise leave it up to our weapons.
  1249. return m_weaponSet.getAbleToAttackSpecificObject( t, this, target, commandSource, specificSlot );
  1250. }
  1251. //=============================================================================
  1252. //Used for base defenses and otherwise stationary units to see if you can attack a position potentially out of range.
  1253. CanAttackResult Object::getAbleToUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType commandSource, WeaponSlotType specificSlot ) const
  1254. {
  1255. return m_weaponSet.getAbleToUseWeaponAgainstTarget( attackType, this, victim, pos, commandSource, specificSlot );
  1256. }
  1257. //=============================================================================
  1258. Bool Object::chooseBestWeaponForTarget(const Object* target, WeaponChoiceCriteria criteria, CommandSourceType cmdSource )
  1259. {
  1260. return m_weaponSet.chooseBestWeaponForTarget(this, target, criteria, cmdSource );
  1261. }
  1262. //DECLARE_PERF_TIMER(fireCurrentWeapon)
  1263. //=============================================================================
  1264. void Object::fireCurrentWeapon(Object *target)
  1265. {
  1266. //USE_PERF_TIMER(fireCurrentWeapon)
  1267. // victim may have already been destroyed
  1268. if (target == NULL)
  1269. return;
  1270. Weapon* weapon = m_weaponSet.getCurWeapon();
  1271. if (weapon && (weapon->getStatus() == READY_TO_FIRE))
  1272. {
  1273. Bool reloaded = weapon->fireWeapon(this, target);
  1274. DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
  1275. if (m_firingTracker)
  1276. m_firingTracker->shotFired(weapon, target->getID());
  1277. if (reloaded)
  1278. releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  1279. friend_setUndetectedDefector( FALSE );// My secret is out
  1280. }
  1281. }
  1282. //=============================================================================
  1283. void Object::fireCurrentWeapon(const Coord3D* pos)
  1284. {
  1285. //USE_PERF_TIMER(fireCurrentWeapon)
  1286. if (pos == NULL)
  1287. return;
  1288. Weapon* weapon = m_weaponSet.getCurWeapon();
  1289. if (weapon && (weapon->getStatus() == READY_TO_FIRE))
  1290. {
  1291. Bool reloaded = weapon->fireWeapon(this, pos);
  1292. DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
  1293. if (m_firingTracker)
  1294. m_firingTracker->shotFired(weapon, INVALID_ID);
  1295. if (reloaded)
  1296. releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  1297. friend_setUndetectedDefector( FALSE );// My secret is out
  1298. }
  1299. }
  1300. //==============================================================================
  1301. void Object::notifyFiringTrackerShotFired( const Weapon* weaponFired, ObjectID victimID )
  1302. {
  1303. if ( m_firingTracker )
  1304. m_firingTracker->shotFired( weaponFired, victimID );
  1305. }
  1306. //=============================================================================
  1307. void Object::preFireCurrentWeapon( const Object *victim )
  1308. {
  1309. Weapon* weapon = m_weaponSet.getCurWeapon();
  1310. //If we are going to be capable of firing our weapon NEXT frame, set the pre-attack
  1311. //up now. This gets called by AIAttackFireWeaponState::onEnter().. but the update happens
  1312. //next frame.
  1313. if (weapon && TheGameLogic->getFrame() + 1 >= weapon->getPossibleNextShotFrame() )
  1314. {
  1315. weapon->preFireWeapon( this, victim );
  1316. friend_setUndetectedDefector( FALSE );// My secret is out
  1317. }
  1318. }
  1319. // ============================================================================
  1320. /** Using the firing tracker, return the frame a shot was last fired on */
  1321. // ============================================================================
  1322. UnsignedInt Object::getLastShotFiredFrame() const
  1323. {
  1324. UnsignedInt recent = 0;
  1325. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  1326. {
  1327. const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  1328. if (w)
  1329. {
  1330. UnsignedInt when = w->getLastShotFrame();
  1331. if (when > recent)
  1332. recent = when;
  1333. }
  1334. }
  1335. return recent;
  1336. }
  1337. // ============================================================================
  1338. /** Get the victim ID we last shot at */
  1339. // ============================================================================
  1340. ObjectID Object::getLastVictimID() const
  1341. {
  1342. return m_firingTracker ? m_firingTracker->getLastShotVictim() : INVALID_ID;
  1343. }
  1344. //=============================================================================
  1345. // Object::getRelationship
  1346. //=============================================================================
  1347. Relationship Object::getRelationship(const Object *that) const
  1348. {
  1349. const Team *myTeam = getTeam();
  1350. if (myTeam && that)
  1351. {
  1352. if (getIsUndetectedDefector())
  1353. {
  1354. return NEUTRAL; // so my AI does not give away my position by auto acquire
  1355. }
  1356. else if (that->getIsUndetectedDefector())
  1357. {
  1358. return ALLIES; // so I treat undetecteddefectors like they were my very own
  1359. }
  1360. else
  1361. {
  1362. return myTeam->getRelationship( that->getTeam() );
  1363. }
  1364. }
  1365. return NEUTRAL;
  1366. }
  1367. //=============================================================================
  1368. // Object::getControllingPlayer
  1369. //=============================================================================
  1370. Player * Object::getControllingPlayer() const
  1371. {
  1372. const Team* myTeam = this->getTeam();
  1373. if (myTeam)
  1374. return myTeam->getControllingPlayer();
  1375. return NULL;
  1376. }
  1377. //=============================================================================
  1378. void Object::setProducer(const Object* obj)
  1379. {
  1380. m_producerID = obj ? obj->getID() : INVALID_ID;
  1381. // seems like a good idea, but is not. (srj)
  1382. // if (obj)
  1383. // m_indicatorColor = obj->m_indicatorColor;
  1384. }
  1385. //=============================================================================
  1386. void Object::setBuilder( const Object *obj )
  1387. {
  1388. m_builderID = obj ? obj->getID() : INVALID_ID;
  1389. }
  1390. //=============================================================================
  1391. void Object::setCustomIndicatorColor(Color c)
  1392. {
  1393. if (m_indicatorColor != c)
  1394. {
  1395. m_indicatorColor = c;
  1396. if (m_drawable)
  1397. m_drawable->changedTeam();
  1398. }
  1399. }
  1400. //=============================================================================
  1401. void Object::removeCustomIndicatorColor()
  1402. {
  1403. setCustomIndicatorColor(0);
  1404. }
  1405. //=============================================================================
  1406. // Object::getIndicatorColor
  1407. //=============================================================================
  1408. Color Object::getIndicatorColor() const
  1409. {
  1410. if (m_indicatorColor == 0)
  1411. {
  1412. const Team *myTeam = getTeam();
  1413. if (myTeam)
  1414. {
  1415. const Player* p = myTeam->getControllingPlayer();
  1416. if (p)
  1417. {
  1418. return p->getPlayerColor();
  1419. }
  1420. }
  1421. return GameMakeColor(0, 0, 0, 255);
  1422. }
  1423. else
  1424. {
  1425. return m_indicatorColor;
  1426. }
  1427. }
  1428. //=============================================================================
  1429. // Object::getNightIndicatorColor - used to make blue/purple easier to see on night models.
  1430. //=============================================================================
  1431. Color Object::getNightIndicatorColor() const
  1432. {
  1433. if (m_indicatorColor == 0)
  1434. {
  1435. const Team *myTeam = getTeam();
  1436. if (myTeam)
  1437. {
  1438. const Player* p = myTeam->getControllingPlayer();
  1439. if (p)
  1440. {
  1441. return p->getPlayerNightColor();
  1442. }
  1443. }
  1444. return GameMakeColor(0, 0, 0, 255);
  1445. }
  1446. else
  1447. {
  1448. return m_indicatorColor;
  1449. }
  1450. }
  1451. //=============================================================================
  1452. // Object::isLocallyControlled
  1453. //=============================================================================
  1454. Bool Object::isLocallyControlled() const
  1455. {
  1456. return getControllingPlayer() == ThePlayerList->getLocalPlayer();
  1457. }
  1458. //=============================================================================
  1459. // Object::isLocallyControlled
  1460. //=============================================================================
  1461. Bool Object::isNeutralControlled() const
  1462. {
  1463. return getControllingPlayer() == ThePlayerList->getNeutralPlayer();
  1464. }
  1465. //-------------------------------------------------------------------------------------------------
  1466. inline Bool isPosDifferent(const Coord3D* a, const Coord3D* b)
  1467. {
  1468. // this is necessary because PhysicsBehavior may generate tiny changes even when
  1469. // "standing still", due to roundoff errors. It's important that we only invalidate
  1470. // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
  1471. // so we must put in some cleverness...
  1472. const Real THRESH = 0.01f;
  1473. if (fabs(a->x - b->x) > THRESH)
  1474. return true;
  1475. if (fabs(a->y - b->y) > THRESH)
  1476. return true;
  1477. if (fabs(a->z - b->z) > THRESH)
  1478. return true;
  1479. return false;
  1480. }
  1481. //-------------------------------------------------------------------------------------------------
  1482. inline Bool isAngleDifferent(Real a, Real b)
  1483. {
  1484. // this is necessary because PhysicsBehavior may generate tiny changes even when
  1485. // "standing still", due to roundoff errors. It's important that we only invalidate
  1486. // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
  1487. // so we must put in some cleverness...
  1488. const Real THRESH = 0.01f; // in radians, this is approx 1/2 degree.
  1489. if (fabs(a - b) > THRESH)
  1490. return true;
  1491. return false;
  1492. }
  1493. //-------------------------------------------------------------------------------------------------
  1494. void Object::reactToTurretChange( WhichTurretType turret, Real oldRotation, Real oldPitch )
  1495. {
  1496. Real currentRotation = 0.0f;
  1497. Real currentPitch = 0.0f;
  1498. if( getAI() )
  1499. {
  1500. getAI()->getTurretRotAndPitch( turret, &currentRotation, &currentPitch );
  1501. }
  1502. Bool rotationChange = (currentRotation != oldRotation);
  1503. // Bool pitchChange = (currentPitch != oldPitch);
  1504. if( rotationChange )
  1505. {
  1506. if (getContain())
  1507. getContain()->containReactToTransformChange();
  1508. }
  1509. }
  1510. //-------------------------------------------------------------------------------------------------
  1511. //DECLARE_PERF_TIMER(Object_reactToTransformChange)
  1512. void Object::reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle)
  1513. {
  1514. //USE_PERF_TIMER(Object_reactToTransformChange)
  1515. if(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)) {
  1516. DEBUG_CRASH(("Object pos is nan."));
  1517. TheGameLogic->destroyObject(this);
  1518. }
  1519. if (m_drawable)
  1520. {
  1521. m_drawable->setTransformMatrix( this->getTransformMatrix() );
  1522. }
  1523. Bool posDiff = isPosDifferent(oldPos, getPosition());
  1524. Bool angDiff = isAngleDifferent(oldAngle, getOrientation());
  1525. if (posDiff || angDiff)
  1526. {
  1527. if (m_partitionData)
  1528. m_partitionData->makeDirty(true);
  1529. if (getContain())
  1530. getContain()->containReactToTransformChange();
  1531. }
  1532. if (posDiff)
  1533. {
  1534. setTriggerAreaFlagsForChangeInPosition(); // Update for entered/exited
  1535. Region3D mapExtent;
  1536. TheTerrainLogic->getExtent(&mapExtent);
  1537. if (mapExtent.isInRegionNoZ(getPosition()))
  1538. m_privateStatus &= ~OFF_MAP;
  1539. else
  1540. m_privateStatus |= OFF_MAP;
  1541. }
  1542. }
  1543. //-------------------------------------------------------------------------------------------------
  1544. ObjectShroudStatus Object::getShroudedStatus(Int playerIndex) const
  1545. {
  1546. if (getTemplate()->isKindOf( KINDOF_ALWAYS_VISIBLE ))
  1547. return OBJECTSHROUD_CLEAR;
  1548. if (m_partitionData)
  1549. return m_partitionData->getShroudedStatus(playerIndex);
  1550. // This can happen for objects removed from the partition system (e.g.,
  1551. // for soldiers that are garrisoned inside a building).
  1552. return OBJECTSHROUD_CLEAR;
  1553. }
  1554. //-------------------------------------------------------------------------------------------------
  1555. /** Something is attempting to damage this object */
  1556. //-------------------------------------------------------------------------------------------------
  1557. void Object::attemptDamage( DamageInfo *damageInfo )
  1558. {
  1559. BodyModuleInterface* body = getBodyModule();
  1560. if (body)
  1561. body->attemptDamage( damageInfo );
  1562. // Process any shockwave forces that might affect this object due to the incurred damage
  1563. if (damageInfo->in.m_shockWaveAmount > 0.0f && damageInfo->in.m_shockWaveRadius > 0.0f)
  1564. {
  1565. //KindOfMaskType immuneToShockwaveKindofs; //NEW RESTRICTIONS ADDED
  1566. //immuneToShockwaveKindofs.set(KINDOF_PROJECTILE);// projectiles go idle in midair when they get sw'd //NEW RESTRICTIONS ADDED
  1567. //immuneToShockwaveKindofs.set(KINDOF_PRODUCED_AT_HELIPAD);//helicopters go all wonky when they get shockwaved //NEW RESTRICTIONS ADDED
  1568. PhysicsBehavior *behavior = getPhysics();
  1569. if ( behavior && (isAirborneTarget() == FALSE) && (! isKindOf(KINDOF_PROJECTILE) ) )
  1570. // if (behavior && isAnyKindOf( immuneToShockwaveKindofs ) == FALSE )//NEW RESTRICTIONS ADDED
  1571. {
  1572. // Calculate the shockwave taperoff amount due to distance from ground zero
  1573. Real shockWaveScalar = damageInfo->in.m_shockWaveVector.length();
  1574. Real distanceFromCenter = min(1.0f, shockWaveScalar / damageInfo->in.m_shockWaveRadius);
  1575. Real distanceTaper = (distanceFromCenter) * (1.0f - damageInfo->in.m_shockWaveTaperOff);
  1576. Real shockTaperMult = 1.0f - distanceTaper;
  1577. // Set up the shockwave force to use apply on object
  1578. Coord3D shockWaveForce;
  1579. shockWaveForce.set( &damageInfo->in.m_shockWaveVector );
  1580. shockWaveForce.normalize();
  1581. shockWaveForce.scale( damageInfo->in.m_shockWaveAmount * shockTaperMult );
  1582. shockWaveForce.z = shockWaveForce.length(); // Apply up force equal to the lateral force for dramatic effect
  1583. // Apply the shock to the object
  1584. behavior->applyShock(&shockWaveForce);
  1585. // Add random rotation to the object for drama
  1586. behavior->applyRandomRotation();
  1587. // Set stunned state due to the shock for the object
  1588. behavior->setStunned(true);
  1589. setModelConditionState(MODELCONDITION_STUNNED_FLAILING);
  1590. }
  1591. }
  1592. /// @todo track damage dealt/attempted
  1593. //
  1594. // if actual damage occurred, and this is an object owned by the local player we
  1595. // might do a radar event for under attack. Note that we do not even try
  1596. // to do radar events for DAMAGE_PENALTY as that damage type is a type of damage
  1597. // that occurs with explicit player knowledge
  1598. //
  1599. if( damageInfo->out.m_actualDamageDealt > 0.0f &&
  1600. damageInfo->in.m_damageType != DAMAGE_PENALTY &&
  1601. damageInfo->in.m_damageType != DAMAGE_HEALING &&
  1602. getControllingPlayer() &&
  1603. !BitTest(damageInfo->in.m_sourcePlayerMask, getControllingPlayer()->getPlayerMask()) &&
  1604. m_radarData != NULL &&
  1605. getControllingPlayer() == ThePlayerList->getLocalPlayer() )
  1606. TheRadar->tryUnderAttackEvent( this );
  1607. }
  1608. //-------------------------------------------------------------------------------------------------
  1609. void Object::attemptHealing(Real amount, const Object* source)
  1610. {
  1611. BodyModuleInterface* body = getBodyModule();
  1612. if (body)
  1613. {
  1614. DamageInfo damageInfo;
  1615. damageInfo.in.m_damageType = DAMAGE_HEALING;
  1616. damageInfo.in.m_deathType = DEATH_NONE;
  1617. damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
  1618. damageInfo.in.m_amount = amount;
  1619. body->attemptHealing( &damageInfo );
  1620. }
  1621. }
  1622. ObjectID Object::getSoleHealingBenefactor( void ) const
  1623. {
  1624. UnsignedInt now = TheGameLogic->getFrame();
  1625. if( now > m_soleHealingBenefactorExpirationFrame )
  1626. return INVALID_ID;
  1627. return m_soleHealingBenefactorID;
  1628. }
  1629. Bool Object::attemptHealingFromSoleBenefactor ( Real amount, const Object* source, UnsignedInt duration )
  1630. {///< for the non-stacking healers like ambulance and propaganda
  1631. if( ! source ) // sanity
  1632. return FALSE;
  1633. UnsignedInt now = TheGameLogic->getFrame();
  1634. ObjectID id = source->getID();
  1635. // Either it is ok to accept healing from any who offer or this is my guy, calling again
  1636. if( now > m_soleHealingBenefactorExpirationFrame || m_soleHealingBenefactorID == id )
  1637. {
  1638. m_soleHealingBenefactorID = id;
  1639. m_soleHealingBenefactorExpirationFrame = now + duration;
  1640. BodyModuleInterface* body = getBodyModule();
  1641. if (body)
  1642. {
  1643. DamageInfo damageInfo;
  1644. damageInfo.in.m_damageType = DAMAGE_HEALING;
  1645. damageInfo.in.m_deathType = DEATH_NONE;
  1646. damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
  1647. damageInfo.in.m_amount = amount;
  1648. body->attemptHealing( &damageInfo );
  1649. }
  1650. return TRUE;
  1651. }
  1652. return FALSE;
  1653. }
  1654. //-------------------------------------------------------------------------------------------------
  1655. Real Object::estimateDamage( DamageInfoInput& damageInfo ) const
  1656. {
  1657. BodyModuleInterface* body = getBodyModule();
  1658. if (body)
  1659. return body->estimateDamage( damageInfo );
  1660. return 0.0f;
  1661. }
  1662. //-------------------------------------------------------------------------------------------------
  1663. /** Do so much damage to an object that it will certainly die */
  1664. //-------------------------------------------------------------------------------------------------
  1665. void Object::kill( DamageType damageType, DeathType deathType )
  1666. {
  1667. DamageInfo damageInfo;
  1668. // Do unmodifiable damage equal to their max health to kill.
  1669. damageInfo.in.m_damageType = damageType;
  1670. damageInfo.in.m_deathType = deathType;
  1671. damageInfo.in.m_sourceID = INVALID_ID;
  1672. damageInfo.in.m_amount = getBodyModule()->getMaxHealth();
  1673. damageInfo.in.m_kill = TRUE; // Triggers object to die no matter what.
  1674. attemptDamage( &damageInfo );
  1675. DEBUG_ASSERTCRASH(!damageInfo.out.m_noEffect, ("Attempting to kill an unKillable object (InactiveBody?)\n"));
  1676. } // end kill
  1677. //-------------------------------------------------------------------------------------------------
  1678. /** Restore max health to this Object */
  1679. //-------------------------------------------------------------------------------------------------
  1680. void Object::healCompletely()
  1681. {
  1682. attemptHealing(HUGE_DAMAGE_AMOUNT, NULL);
  1683. }
  1684. //-------------------------------------------------------------------------------------------------
  1685. //-------------------------------------------------------------------------------------------------
  1686. void Object::setEffectivelyDead(Bool dead)
  1687. {
  1688. if (dead)
  1689. BitSet(m_privateStatus, EFFECTIVELY_DEAD);
  1690. else
  1691. BitClear(m_privateStatus, EFFECTIVELY_DEAD);
  1692. if (dead)
  1693. {
  1694. if( m_radarData )
  1695. TheRadar->removeObject( this );
  1696. }
  1697. }
  1698. //-------------------------------------------------------------------------------------------------
  1699. void Object::setCaptured(Bool isCaptured)
  1700. {
  1701. if (isCaptured)
  1702. BitSet(m_privateStatus, CAPTURED);
  1703. else
  1704. {
  1705. DEBUG_LOG(("Clearing Captured Status. This should never happen. jkmcd"));
  1706. BitClear(m_privateStatus, CAPTURED);
  1707. }
  1708. // No need to see if we should skip updates, this flag has no effect on skipping updates.
  1709. }
  1710. //-------------------------------------------------------------------------------------------------
  1711. Bool Object::isStructure(void) const
  1712. {
  1713. return isKindOf(KINDOF_STRUCTURE);
  1714. }
  1715. //-------------------------------------------------------------------------------------------------
  1716. Bool Object::isFactionStructure(void) const
  1717. {
  1718. return isAnyKindOf( KINDOFMASK_FS );
  1719. }
  1720. //-------------------------------------------------------------------------------------------------
  1721. Bool Object::isNonFactionStructure(void) const
  1722. {
  1723. return isStructure() && !isFactionStructure();
  1724. }
  1725. void localIsHero( Object *obj, void* userData )
  1726. {
  1727. Bool *hero = (Bool*)userData;
  1728. if( obj && obj->isKindOf( KINDOF_HERO ) )
  1729. {
  1730. *hero = TRUE;
  1731. }
  1732. }
  1733. //-------------------------------------------------------------------------------------------------
  1734. Bool Object::isHero(void) const
  1735. {
  1736. ContainModuleInterface *contain = getContain();
  1737. if( contain )
  1738. {
  1739. Bool heroInside = FALSE;
  1740. contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE );
  1741. if( heroInside )
  1742. {
  1743. return TRUE;
  1744. }
  1745. }
  1746. return isKindOf( KINDOF_HERO );
  1747. }
  1748. //-------------------------------------------------------------------------------------------------
  1749. void Object::setReceivingDifficultyBonus(Bool receive)
  1750. {
  1751. if (receive == m_isReceivingDifficultyBonus) {
  1752. return;
  1753. }
  1754. m_isReceivingDifficultyBonus = receive;
  1755. getControllingPlayer()->friend_applyDifficultyBonusesForObject(this, m_isReceivingDifficultyBonus);
  1756. }
  1757. //-------------------------------------------------------------------------------------------------
  1758. //- DISABLEDNESS STUFF ----------------------------------------------------------------------------
  1759. //-------------------------------------------------------------------------------------------------
  1760. void Object::setDisabled( DisabledType type )
  1761. {
  1762. setDisabledUntil(type, FOREVER);
  1763. }
  1764. //-------------------------------------------------------------------------------------------------
  1765. void Object::setDisabledUntil( DisabledType type, UnsignedInt frame )
  1766. {
  1767. Bool edgeCase = !isDisabled();
  1768. if( type < 0 || type >= DISABLED_COUNT )
  1769. {
  1770. DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
  1771. return;
  1772. }
  1773. //Handle audio events!
  1774. AudioEventRTS sound;
  1775. if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
  1776. {
  1777. //We've been sniped! Play a splatter sound for the pilot losing his face.
  1778. sound = TheAudio->getMiscAudio()->m_splatterVehiclePilotsBrain;
  1779. sound.setPosition( getPosition() );
  1780. TheAudio->addAudioEvent( &sound );
  1781. }
  1782. else if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_SUBDUED || type == DISABLED_HACKED )
  1783. {
  1784. //We've lost power -- make sure we aren't already out of power as the sounds shouldn't happen
  1785. //if you were already disabled.
  1786. if( !isDisabledByType( DISABLED_UNDERPOWERED ) &&
  1787. !isDisabledByType( DISABLED_EMP ) &&
  1788. !isDisabledByType( DISABLED_SUBDUED ) &&
  1789. !isDisabledByType( DISABLED_HACKED ) )
  1790. {
  1791. if( isKindOf( KINDOF_STRUCTURE ) )
  1792. {
  1793. sound = TheAudio->getMiscAudio()->m_buildingDisabled;
  1794. sound.setPosition( getPosition() );
  1795. TheAudio->addAudioEvent( &sound );
  1796. }
  1797. else if( isKindOf( KINDOF_VEHICLE ) )
  1798. {
  1799. sound = TheAudio->getMiscAudio()->m_vehicleDisabled;
  1800. sound.setPosition( getPosition() );
  1801. TheAudio->addAudioEvent( &sound );
  1802. }
  1803. }
  1804. }
  1805. if( m_disabledTillFrame[ type ] != frame )
  1806. {
  1807. // an edge-test for disabledness, for type. This INCREMENTS m_pauseCount
  1808. // srj sez: HELD nevers disables special powers.
  1809. if ( type != DISABLED_HELD && !isDisabledByType( type ) )
  1810. pauseAllSpecialPowers( TRUE );
  1811. m_disabledTillFrame[ type ] = frame;
  1812. m_disabledMask.set( type, frame > TheGameLogic->getFrame() );
  1813. if( m_drawable )
  1814. {
  1815. if( isDisabled() )
  1816. {
  1817. // Held does not tint anybody. If we are multiply disabled, the other setting will hit the tint,
  1818. // and in clear, only-held and not-disabled are both causes to untint.
  1819. // Doh. Also shouldn't be tinting when disabled by scripting.
  1820. // Doh^2. Also shouldn't be CLEARING tinting if we're disabling by held or script disabledness
  1821. // Doh^3. Unmanned is no tint too
  1822. if( type != DISABLED_HELD && type != DISABLED_SCRIPT_DISABLED && type != DISABLED_UNMANNED )
  1823. {
  1824. m_drawable->setTintStatus( TINT_STATUS_DISABLED );
  1825. }
  1826. }
  1827. }
  1828. ContainModuleInterface *contain = getContain();
  1829. if ( contain )
  1830. {
  1831. Object *rider = (Object*)contain->friend_getRider();
  1832. if ( rider )
  1833. {
  1834. rider->setDisabledUntil(type, frame);
  1835. }
  1836. }
  1837. if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) )
  1838. {
  1839. SpawnBehaviorInterface *sbi = this->getSpawnBehaviorInterface();
  1840. if ( sbi )
  1841. {
  1842. //Kris: Patch 1.01 - November 12, 2003
  1843. //Actually, we want to disable the slaves, not order them to go idle! This fix was made to
  1844. //stinger sites getting hit by an EMP to prevent the soldiers from attacking.
  1845. //sbi->orderSlavesToGoIdle( CMD_FROM_AI ); // the canattack() will take care of any future attempts to fire
  1846. sbi->orderSlavesDisabledUntil( type, frame );
  1847. }
  1848. }
  1849. }
  1850. if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
  1851. {
  1852. //strange but true: If I am a carbomb,
  1853. //my driver actually has a dead-man's
  1854. //trigger for my dynamite...
  1855. //If he gets sniped, I blow up! Wheeee!
  1856. WeaponSetFlags flags;
  1857. flags.set( WEAPONSET_CARBOMB );
  1858. const WeaponTemplateSet* set = getTemplate()->findWeaponTemplateSet( flags );
  1859. if( set && set->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
  1860. {
  1861. Object* sniper = TheGameLogic->findObjectByID( getBodyModule()->getLastDamageInfo()->in.m_sourceID );
  1862. if ( sniper )
  1863. sniper->scoreTheKill( this );
  1864. kill();
  1865. }
  1866. else
  1867. {
  1868. //This vehicle's pilot has been sniped, so we want to clear the veterancy rating (if any)
  1869. ExperienceTracker *xpTracker = getExperienceTracker();
  1870. if( xpTracker )
  1871. {
  1872. xpTracker->setExperienceAndLevel( 0, FALSE );
  1873. }
  1874. //Not only that, but it also loses any healing bonuses it may have earned in its prior life
  1875. {
  1876. static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
  1877. AutoHealBehavior* autoHeal = (AutoHealBehavior*)(findUpdateModule( key_AutoHealBehavior ));
  1878. if (autoHeal)
  1879. autoHeal->undoUpgrade();
  1880. }
  1881. }
  1882. }
  1883. // This will only be called if we were NOT disabled before coming into this function.
  1884. if (edgeCase) {
  1885. onDisabledEdge(true);
  1886. }
  1887. }
  1888. //-------------------------------------------------------------------------------------------------
  1889. UnsignedInt Object::getDisabledUntil( DisabledType type ) const
  1890. {
  1891. if( type == DISABLED_ANY )
  1892. {
  1893. UnsignedInt highestFrame = 0;
  1894. //Iterate through each disabled type and return the one with the highest frame.
  1895. for( Int i = 0; i < DISABLED_COUNT; i++ )
  1896. {
  1897. if( m_disabledMask.test( i ) && m_disabledTillFrame[ i ] > highestFrame )
  1898. {
  1899. highestFrame = m_disabledTillFrame[ i ];
  1900. }
  1901. }
  1902. return highestFrame;
  1903. }
  1904. else if( m_disabledMask.test( type ) )
  1905. {
  1906. //Specific query.
  1907. return m_disabledTillFrame[ type ];
  1908. }
  1909. //Not disabled.
  1910. return 0;
  1911. }
  1912. //-------------------------------------------------------------------------------------------------
  1913. Bool Object::clearDisabled( DisabledType type )
  1914. {
  1915. if( type < 0 || type >= DISABLED_COUNT )
  1916. {
  1917. DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
  1918. return FALSE;
  1919. }
  1920. if (!isDisabledByType(type)) {
  1921. return FALSE;
  1922. }
  1923. if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_SUBDUED || type == DISABLED_HACKED )
  1924. {
  1925. //We've regained power-- make sure we aren't still disabled by another type.
  1926. AudioEventRTS sound;
  1927. if( (!isDisabledByType( DISABLED_UNDERPOWERED ) || type == DISABLED_UNDERPOWERED ) &&
  1928. (!isDisabledByType( DISABLED_EMP ) || type == DISABLED_EMP ) &&
  1929. (!isDisabledByType( DISABLED_SUBDUED ) || type == DISABLED_SUBDUED ) &&
  1930. (!isDisabledByType( DISABLED_HACKED ) || type == DISABLED_HACKED ) )
  1931. {
  1932. if( isKindOf( KINDOF_STRUCTURE ) )
  1933. {
  1934. sound = TheAudio->getMiscAudio()->m_buildingReenabled;
  1935. sound.setPosition( getPosition() );
  1936. TheAudio->addAudioEvent( &sound );
  1937. }
  1938. else if( isKindOf( KINDOF_VEHICLE ) )
  1939. {
  1940. sound = TheAudio->getMiscAudio()->m_vehicleReenabled;
  1941. sound.setPosition( getPosition() );
  1942. TheAudio->addAudioEvent( &sound );
  1943. }
  1944. }
  1945. }
  1946. // an edge-test for disabledness, for type. This DECREMENTS m_pauseCount
  1947. // srj sez: HELD nevers disables special powers.
  1948. if ( type != DISABLED_HELD && isDisabledByType( type ) )
  1949. pauseAllSpecialPowers( FALSE );
  1950. ContainModuleInterface *contain = getContain();
  1951. if ( contain )
  1952. {
  1953. // We explicitly pass stuff in up in the set, so we need to turn it off if it is a forever type
  1954. Object *rider = (Object*)contain->friend_getRider();
  1955. if( rider && (m_disabledTillFrame[ type ] == FOREVER) )
  1956. {
  1957. rider->clearDisabled(type);
  1958. }
  1959. }
  1960. if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) )
  1961. {
  1962. SpawnBehaviorInterface *sbi = this->getSpawnBehaviorInterface();
  1963. if ( sbi )
  1964. {
  1965. //Kris: Patch 1.02 - December 17, 2003
  1966. //Make sure slaves can recover from being disabled by subdual (stinger site soldier case)
  1967. sbi->orderSlavesToClearDisabled( type );
  1968. }
  1969. }
  1970. m_disabledTillFrame[ type ] = NEVER;
  1971. m_disabledMask.set( type, 0 );
  1972. DisabledMaskType exceptions;
  1973. exceptions.set(DISABLED_HELD);
  1974. exceptions.set(DISABLED_SCRIPT_DISABLED);
  1975. exceptions.set(DISABLED_UNMANNED);
  1976. DisabledMaskType myFlagsMinusExceptions = getDisabledFlags();
  1977. myFlagsMinusExceptions.clearAndSet(exceptions, DISABLEDMASK_NONE);
  1978. // to clarify, if I am NOT disabled by anything other than DISABLED_HELD, or DISABLED_SCRIPT_DISABLED
  1979. // to clarify, count inverse intersection gives you the number of exceptions you don't have,
  1980. // and has nothing to do with checking other disabled types
  1981. // if( !isDisabled() || getDisabledFlags().countInverseIntersection( exceptions ) == 0 )
  1982. if( myFlagsMinusExceptions.count() == 0 )
  1983. {
  1984. // I have no disabled flag that is not one of the exceptions above.
  1985. if (m_drawable)
  1986. m_drawable->clearTintStatus( TINT_STATUS_DISABLED );
  1987. }
  1988. checkDisabledStatus();// in case we just edged
  1989. // if we're no longer disabled by anything, then call the edge function.
  1990. if (!isDisabled()) {
  1991. onDisabledEdge(false);
  1992. }
  1993. return TRUE;
  1994. }
  1995. //-------------------------------------------------------------------------------------------------
  1996. //Checks any timers and clears disabled statii that have expired.
  1997. //-------------------------------------------------------------------------------------------------
  1998. void Object::checkDisabledStatus()
  1999. {
  2000. UnsignedInt now = TheGameLogic->getFrame();
  2001. for( int i = 0; i < DISABLED_COUNT; i++ )
  2002. {
  2003. DisabledType type = (DisabledType)i;
  2004. if( isDisabledByType( type ) )
  2005. {
  2006. if ( now >= m_disabledTillFrame[ i ] )
  2007. {
  2008. clearDisabled( type ); // This will also DECREMENT m_pauseCount in all specialpowers
  2009. m_disabledMask.set( type, 0 );
  2010. }
  2011. }
  2012. }
  2013. }
  2014. //-------------------------------------------------------------------------------------------------
  2015. void Object::pauseAllSpecialPowers( const Bool disabling ) const
  2016. {
  2017. for (BehaviorModule** m = m_behaviors; *m; ++m)
  2018. {
  2019. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  2020. if (!sp)
  2021. continue;
  2022. sp->pauseCountdown( disabling );// So it will pause if we are disabling.
  2023. }
  2024. }
  2025. //-------------------------------------------------------------------------------------------------
  2026. //-------------------------------------------------------------------------------------------------
  2027. /** Clear the previous entered/exited flags. */
  2028. //-------------------------------------------------------------------------------------------------
  2029. void Object::updateTriggerAreaFlags()
  2030. {
  2031. Int j = 0;
  2032. // Update the flags, and remove any trigger areas that this object isn't inside.
  2033. for (Int i=0; i<m_numTriggerAreasActive; i++)
  2034. {
  2035. if (!m_triggerInfo[j].isInside)
  2036. continue;
  2037. m_triggerInfo[j].entered = false;
  2038. m_triggerInfo[j].exited = false;
  2039. m_triggerInfo[j].isInside = m_triggerInfo[i].isInside;
  2040. m_triggerInfo[j].pTrigger = m_triggerInfo[i].pTrigger;
  2041. j++;
  2042. }
  2043. m_numTriggerAreasActive = j;
  2044. }
  2045. //-------------------------------------------------------------------------------------------------
  2046. void Object::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
  2047. {
  2048. for (BehaviorModule** m = m_behaviors; *m; ++m)
  2049. {
  2050. CollideModuleInterface* collide = (*m)->getCollide();
  2051. if (!collide)
  2052. continue;
  2053. // check each time thru the loop, in case a collide module sets it
  2054. if( getStatusBits().test( OBJECT_STATUS_NO_COLLISIONS ) )
  2055. {
  2056. #ifdef DEBUG_CRC
  2057. //DEBUG_LOG(("Object::onCollide() - OBJECT_STATUS_NO_COLLISIONS set\n"));
  2058. #endif
  2059. break;
  2060. }
  2061. #ifdef DEBUG_CRC
  2062. //DEBUG_LOG(("Object::onCollide() - calling collide module\n"));
  2063. #endif
  2064. collide->onCollide(other, loc, normal);
  2065. }
  2066. }
  2067. //-------------------------------------------------------------------------------------------------
  2068. Bool Object::isSalvageCrate() const
  2069. {
  2070. for( BehaviorModule** m = m_behaviors; *m; ++m )
  2071. {
  2072. CollideModuleInterface* collide = (*m)->getCollide();
  2073. if( collide && collide->isSalvageCrateCollide() )
  2074. {
  2075. return true;
  2076. }
  2077. }
  2078. return false;
  2079. }
  2080. //-------------------------------------------------------------------------------------------------
  2081. /**
  2082. Our owning player is telling us to recheck our UpgradeModules, as an upgrade has completed
  2083. */
  2084. void Object::updateUpgradeModules()
  2085. {
  2086. if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  2087. return; // No upgrade can run if we are under construction. The three places that clear UnderConstruction will re-update us.
  2088. if( testStatus( OBJECT_STATUS_DESTROYED ) )
  2089. return; // Patch 1.03 -- Fixes crash when you upgrade a fake GLA command center to a real one if (toxic or demo).
  2090. if( getControllingPlayer() == NULL )
  2091. return; // This can only happen in game teardown. No upgrades for you without a player. Weird crashes are bad.
  2092. UpgradeMaskType playerMask = getControllingPlayer()->getCompletedUpgradeMask();
  2093. UpgradeMaskType objectMask = getObjectCompletedUpgradeMask();
  2094. UpgradeMaskType maskToCheck = playerMask;
  2095. maskToCheck.set( objectMask );
  2096. // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
  2097. // We combine all the masks in case someone has a Object AND Player combination
  2098. for (BehaviorModule** module = m_behaviors; *module; ++module)
  2099. {
  2100. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  2101. if (!upgrade)
  2102. continue;
  2103. if( !upgrade->isAlreadyUpgraded() )
  2104. {
  2105. upgrade->attemptUpgrade( maskToCheck );
  2106. }
  2107. }
  2108. }
  2109. //-------------------------------------------------------------------------------------------------
  2110. //This function sucks.
  2111. //It was added for objects that can disguise as other objects and contain upgraded subobject overrides.
  2112. //A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
  2113. //made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
  2114. //stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
  2115. //recalculate those upgraded subobjects.
  2116. //-------------------------------------------------------------------------------------------------
  2117. void Object::forceRefreshSubObjectUpgradeStatus()
  2118. {
  2119. for (BehaviorModule** module = m_behaviors; *module; ++module)
  2120. {
  2121. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  2122. if (!upgrade)
  2123. continue;
  2124. if( upgrade->isSubObjectsUpgrade() )
  2125. {
  2126. upgrade->forceRefreshUpgrade();
  2127. }
  2128. }
  2129. }
  2130. //-------------------------------------------------------------------------------------------------
  2131. /** Returns whether an object entered or exited an area. */
  2132. //-------------------------------------------------------------------------------------------------
  2133. Bool Object::didEnterOrExit() const
  2134. {
  2135. if (isKindOf(KINDOF_INERT)) {
  2136. return FALSE;
  2137. }
  2138. // note that this needs to return true if we
  2139. // entered or exited on the current frame OR
  2140. // the previous frame... since the current execution
  2141. // order is ScriptEngine, then ObjectUpdates,
  2142. // enter/exits detected in ObjectUpdate on frame N
  2143. // won't be noticed by the ScriptEngine till frame N+1.
  2144. UnsignedInt now = TheGameLogic->getFrame();
  2145. return m_enteredOrExitedFrame == now || m_enteredOrExitedFrame == now - 1;
  2146. }
  2147. //-------------------------------------------------------------------------------------------------
  2148. /** Returns whether an object entered an area. */
  2149. //-------------------------------------------------------------------------------------------------
  2150. Bool Object::didEnter(const PolygonTrigger *pTrigger) const
  2151. {
  2152. if (!didEnterOrExit())
  2153. return false;
  2154. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
  2155. for (Int i=0; i<m_numTriggerAreasActive; i++)
  2156. {
  2157. if (m_triggerInfo[i].entered && m_triggerInfo[i].pTrigger == pTrigger)
  2158. return true;
  2159. }
  2160. return false;
  2161. }
  2162. //-------------------------------------------------------------------------------------------------
  2163. /** Returns whether an object entered an area. */
  2164. //-------------------------------------------------------------------------------------------------
  2165. Bool Object::didExit(const PolygonTrigger *pTrigger) const
  2166. {
  2167. if (!didEnterOrExit())
  2168. return false;
  2169. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
  2170. for (Int i=0; i<m_numTriggerAreasActive; i++)
  2171. {
  2172. if (m_triggerInfo[i].exited && m_triggerInfo[i].pTrigger == pTrigger)
  2173. return true;
  2174. }
  2175. return false;
  2176. }
  2177. //-------------------------------------------------------------------------------------------------
  2178. /** Returns whether an object is inside an area. */
  2179. //-------------------------------------------------------------------------------------------------
  2180. Bool Object::isInside(const PolygonTrigger *pTrigger) const
  2181. {
  2182. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert is inside a trigger area. This is invalid.\n"));
  2183. for (Int i=0; i<m_numTriggerAreasActive; i++)
  2184. {
  2185. if (m_triggerInfo[i].isInside && m_triggerInfo[i].pTrigger == pTrigger)
  2186. return true;
  2187. }
  2188. return false;
  2189. }
  2190. // ------------------------------------------------------------------------------------------------
  2191. /** Get production exit interface in object is present */
  2192. // ------------------------------------------------------------------------------------------------
  2193. ExitInterface *Object::getObjectExitInterface() const
  2194. {
  2195. ExitInterface *exitInterface = NULL;
  2196. for( BehaviorModule **umod = m_behaviors; *umod; ++umod )
  2197. {
  2198. if( (exitInterface = (*umod)->getUpdateExitInterface()) != NULL )
  2199. break;
  2200. }
  2201. // If you don't have a fancy one, you may have one from your contain module,
  2202. // since if you can contain something, they will need to get out.
  2203. if( exitInterface == NULL )
  2204. {
  2205. ContainModuleInterface *cmod = getContain();
  2206. if( cmod )
  2207. {
  2208. exitInterface = cmod->getContainExitInterface();
  2209. }
  2210. }
  2211. return exitInterface;
  2212. } // end getObjectExitInterface
  2213. //-------------------------------------------------------------------------------------------------
  2214. /** Checks the object against trigger areas when the position changes. */
  2215. //-------------------------------------------------------------------------------------------------
  2216. void Object::setTriggerAreaFlagsForChangeInPosition()
  2217. {
  2218. // projectiles cannot trigger areas. (jkmcd)
  2219. // neither can inert objects, like the radar ping, etc. (jkmcd)
  2220. if (isKindOf(KINDOF_PROJECTILE) || isKindOf(KINDOF_INERT))
  2221. return;
  2222. ICoord3D iPos;
  2223. Coord3D pos = *getPosition();
  2224. iPos.x = REAL_TO_INT(pos.x);
  2225. iPos.y = REAL_TO_INT(pos.y);
  2226. iPos.z = 0; // Trigger areas compare on xy only.
  2227. if (m_iPos.x == iPos.x && m_iPos.y == iPos.y)
  2228. {
  2229. return; // didn't move enough to change integer position.
  2230. }
  2231. if (!isKindOf(KINDOF_IMMOBILE)) {
  2232. if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE) ) {
  2233. TheGameClient->notifyTerrainObjectMoved(this);
  2234. }
  2235. }
  2236. if (getAIUpdateInterface())
  2237. {
  2238. TheAI->pathfinder()->updatePos(this, getPosition());
  2239. }
  2240. UnsignedInt now = TheGameLogic->getFrame();
  2241. if (m_enteredOrExitedFrame != 0 && m_enteredOrExitedFrame != now)
  2242. updateTriggerAreaFlags();
  2243. // Check for exited.
  2244. Int i;
  2245. for (i=0; i<m_numTriggerAreasActive; i++)
  2246. {
  2247. if (!m_triggerInfo[i].pTrigger->pointInTrigger(m_iPos))
  2248. {
  2249. m_triggerInfo[i].isInside = false;
  2250. m_triggerInfo[i].exited = true;
  2251. m_enteredOrExitedFrame = now;
  2252. if (m_team)
  2253. m_team->setEnteredExited();
  2254. TheGameLogic->updateObjectsChangedTriggerAreas();
  2255. #ifdef _DEBUG
  2256. //TheScriptEngine->AppendDebugMessage("Object exited.", false);
  2257. #endif
  2258. }
  2259. }
  2260. m_iPos = iPos;
  2261. for (const PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext())
  2262. {
  2263. Bool skip = false;
  2264. for (i = 0; i < m_numTriggerAreasActive; i++)
  2265. {
  2266. if (m_triggerInfo[i].pTrigger == pTrig)
  2267. {
  2268. // Already handled this one in the check for exited above.
  2269. skip = true;
  2270. break;
  2271. }
  2272. }
  2273. if (skip)
  2274. continue;
  2275. if (pTrig->pointInTrigger(m_iPos))
  2276. {
  2277. if (m_numTriggerAreasActive < MAX_TRIGGER_AREA_INFOS)
  2278. {
  2279. m_triggerInfo[m_numTriggerAreasActive].isInside = true;
  2280. m_triggerInfo[m_numTriggerAreasActive].entered = true;
  2281. m_triggerInfo[m_numTriggerAreasActive].exited = false;
  2282. m_triggerInfo[m_numTriggerAreasActive].pTrigger = pTrig;
  2283. m_enteredOrExitedFrame = now;
  2284. if (m_team)
  2285. m_team->setEnteredExited();
  2286. TheGameLogic->updateObjectsChangedTriggerAreas();
  2287. ++m_numTriggerAreasActive;
  2288. #ifdef _DEBUG
  2289. //TheScriptEngine->AppendDebugMessage("Object entered.", false);
  2290. #endif
  2291. }
  2292. else
  2293. {
  2294. // Shouldn't happen.
  2295. static Bool didWarn = false;
  2296. if (!didWarn)
  2297. {
  2298. didWarn = true;
  2299. TheScriptEngine->AppendDebugMessage("***WARNING - Too many nested trigger areas. ***", true);
  2300. }
  2301. }
  2302. }
  2303. }
  2304. }
  2305. //-------------------------------------------------------------------------------------------------
  2306. //-------------------------------------------------------------------------------------------------
  2307. Bool Object::isInList(Object **pListHead) const
  2308. {
  2309. Bool result = m_prev || m_next || *pListHead == this;
  2310. #ifdef INTENSE_DEBUG
  2311. Bool found = false;
  2312. for (Object* o = *pListHead; o; o = o->m_next)
  2313. {
  2314. if (o == this)
  2315. {
  2316. found = true;
  2317. break;
  2318. }
  2319. }
  2320. DEBUG_ASSERTCRASH(found==result,("inconsistent links in Object::isInList"));
  2321. #endif
  2322. return result;
  2323. }
  2324. //-------------------------------------------------------------------------------------------------
  2325. //-------------------------------------------------------------------------------------------------
  2326. void Object::prependToList(Object **pListHead)
  2327. {
  2328. DEBUG_ASSERTCRASH(!isInList(pListHead), ("obj is already in a list"));
  2329. m_prev = NULL;
  2330. m_next = *pListHead;
  2331. if (*pListHead)
  2332. (*pListHead)->m_prev = this;
  2333. *pListHead = this;
  2334. }
  2335. //-------------------------------------------------------------------------------------------------
  2336. //-------------------------------------------------------------------------------------------------
  2337. void Object::setLayer(PathfindLayerEnum layer)
  2338. {
  2339. if (layer!=m_layer) {
  2340. #define no_SET_LAYER_INTENSE_DEBUG
  2341. #ifdef SET_LAYER_INTENSE_DEBUG
  2342. DEBUG_LOG(("Changing layer from %d to %d\n", m_layer, layer));
  2343. if (m_layer != LAYER_GROUND) {
  2344. if (TheTerrainLogic->objectInteractsWithBridgeLayer(this, m_layer)) {
  2345. DEBUG_CRASH(("Probably shouldn't be chaging layer. jba."));
  2346. }
  2347. }
  2348. #endif
  2349. TheAI->pathfinder()->removePos(this);
  2350. m_layer = layer;
  2351. TheAI->pathfinder()->updatePos(this, getPosition());
  2352. }
  2353. }
  2354. //-------------------------------------------------------------------------------------------------
  2355. //-------------------------------------------------------------------------------------------------
  2356. void Object::setDestinationLayer(PathfindLayerEnum layer)
  2357. {
  2358. if (layer!=m_destinationLayer) {
  2359. m_destinationLayer = layer;
  2360. }
  2361. }
  2362. // ------------------------------------------------------------------------------------------------
  2363. /** Set unique ID */
  2364. // ------------------------------------------------------------------------------------------------
  2365. void Object::setID( ObjectID id )
  2366. {
  2367. // sanity
  2368. DEBUG_ASSERTCRASH( id != INVALID_ID, ("Object::setID - Invalid id\n") );
  2369. // if id hasn't changed do nothing
  2370. if( m_id == id )
  2371. return;
  2372. // remove this objects previous id from the lookup table
  2373. TheGameLogic->removeObjectFromLookupTable( this );
  2374. // assign new id
  2375. m_id = id;
  2376. // add new id to lookup table
  2377. TheGameLogic->addObjectToLookupTable( this );
  2378. } // end setID
  2379. // ------------------------------------------------------------------------------------------------
  2380. Real Object::calculateHeightAboveTerrain(void) const
  2381. {
  2382. const Coord3D* pos = getPosition();
  2383. Real terrainZ = TheTerrainLogic->getLayerHeight( pos->x, pos->y, m_layer );
  2384. Real myZ = pos->z;
  2385. return myZ - terrainZ;
  2386. }
  2387. //-------------------------------------------------------------------------------------------------
  2388. //-------------------------------------------------------------------------------------------------
  2389. void Object::removeFromList(Object **pListHead)
  2390. {
  2391. if (m_next)
  2392. m_next->m_prev = m_prev;
  2393. if (m_prev)
  2394. m_prev->m_next = m_next;
  2395. else
  2396. *pListHead = m_next;
  2397. m_prev = NULL;
  2398. m_next = NULL;
  2399. }
  2400. //-------------------------------------------------------------------------------------------------
  2401. //-------------------------------------------------------------------------------------------------
  2402. void Object::friend_prepareForMapBoundaryAdjust(void)
  2403. {
  2404. // NOTE - DO NOT remove from pathfind map. jba.
  2405. // NO NO. jba. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
  2406. // remove from the radar, remove from the partition manager
  2407. TheRadar->removeObject(this);
  2408. ThePartitionManager->unRegisterObject(this);
  2409. // The whole PartitionManager and all of the Looker data is about to be blown away,
  2410. // so forget what I think I have done
  2411. m_partitionLastLook->reset();
  2412. m_partitionRevealAllLastLook->reset();
  2413. m_partitionLastShroud->reset();
  2414. m_partitionLastThreat->reset();
  2415. m_partitionLastValue->reset();
  2416. }
  2417. //-------------------------------------------------------------------------------------------------
  2418. //-------------------------------------------------------------------------------------------------
  2419. void Object::friend_notifyOfNewMapBoundary(void)
  2420. {
  2421. ThePartitionManager->registerObject(this);
  2422. TheRadar->addObject(this);
  2423. TheAI->pathfinder()->addObjectToPathfindMap( this );
  2424. // Now that the PartitionManager has finished its reset, we need to relook
  2425. handlePartitionCellMaintenance();
  2426. Region3D mapExtent;
  2427. TheTerrainLogic->getExtent(&mapExtent);
  2428. if (mapExtent.isInRegionNoZ(getPosition()))
  2429. m_privateStatus &= ~OFF_MAP;
  2430. else
  2431. m_privateStatus |= OFF_MAP;
  2432. }
  2433. //-------------------------------------------------------------------------------------------------
  2434. //-------------------------------------------------------------------------------------------------
  2435. void Object::calcNaturalRallyPoint(Coord2D *pt)
  2436. {
  2437. const Matrix3D *transform = getTransformMatrix();
  2438. Vector3 v;
  2439. //
  2440. // get the natural rally point from the template, this coord is in model space relative
  2441. // to the model (0,0,0)
  2442. //
  2443. /*
  2444. const Coord3D *naturalRallyPoint;
  2445. naturalRallyPoint = m_template->getNaturalRallyPoint();
  2446. v.X = naturalRallyPoint->x;
  2447. v.Y = naturalRallyPoint->y;
  2448. v.Z = naturalRallyPoint->z;
  2449. */
  2450. v.Set( 0, 0, 0 );
  2451. // transform the point into world space
  2452. transform->Transform_Vector( *transform, v, &v );
  2453. // we're only concerned with the 2D elements for now
  2454. pt->x = v.X;
  2455. pt->y = v.Y;
  2456. }
  2457. //-------------------------------------------------------------------------------------------------
  2458. Module* Object::findModule(NameKeyType key) const
  2459. {
  2460. Module* m = NULL;
  2461. for (BehaviorModule** b = m_behaviors; *b; ++b)
  2462. {
  2463. if ((*b)->getModuleNameKey() == key)
  2464. {
  2465. #ifdef INTENSE_DEBUG
  2466. if (m == NULL)
  2467. {
  2468. m = *b;
  2469. }
  2470. else
  2471. {
  2472. DEBUG_CRASH(("Duplicate modules found for name %s!\n",TheNameKeyGenerator->keyToName(key).str()));
  2473. }
  2474. #else
  2475. m = *b;
  2476. break;
  2477. #endif
  2478. }
  2479. }
  2480. return m;
  2481. }
  2482. //-------------------------------------------------------------------------------------------------
  2483. /**
  2484. * Returns true if object is currently able to move.
  2485. */
  2486. Bool Object::isMobile() const
  2487. {
  2488. if (isKindOf(KINDOF_IMMOBILE))
  2489. return false;
  2490. if( isDisabled() )
  2491. return false;
  2492. return true;
  2493. }
  2494. //-------------------------------------------------------------------------------------------------
  2495. void Object::scoreTheKill( const Object *victim )
  2496. {
  2497. // Do stuff that has nothing to do with experience points here, like tell our Player we killed something
  2498. /// @todo Multiplayer score hook location?
  2499. Player* victimController = victim->getControllingPlayer();
  2500. // if the other player is not a playable side (i.e. they are civilian, observer, whatever)
  2501. // we shouldn't count the kill.
  2502. if (victimController->isPlayableSide() == FALSE)
  2503. {
  2504. return;
  2505. }
  2506. if ( victim->isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2507. return;
  2508. Player* controller = getControllingPlayer();
  2509. if (victimController)
  2510. {
  2511. victimController->getScoreKeeper()->addObjectLost(victim);
  2512. }
  2513. Relationship r = getRelationship(victim);
  2514. if (r != ENEMIES)
  2515. return;
  2516. // Don't count kills that I do on my own buildings or units, cause thats just silly.
  2517. if (controller == victimController)
  2518. {
  2519. return;
  2520. }
  2521. if (controller)
  2522. {
  2523. controller->getScoreKeeper()->addObjectDestroyed(victim);
  2524. controller->addSkillPointsForKill(this, victim);
  2525. controller->doBountyForKill(this, victim);
  2526. }
  2527. // Now handle experience, if we can gain any
  2528. if (m_experienceTracker && m_experienceTracker->isAcceptingExperiencePoints())
  2529. {
  2530. // srj sez: per dustin, no experience (et al) for killing things under construction.
  2531. if (!victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
  2532. {
  2533. Int experienceValue = victim->getExperienceTracker()->getExperienceValue( this );
  2534. getExperienceTracker()->addExperiencePoints( experienceValue );
  2535. }
  2536. }
  2537. }
  2538. //-------------------------------------------------------------------------------------------------
  2539. VeterancyLevel Object::getVeterancyLevel() const
  2540. {
  2541. return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR;
  2542. }
  2543. //-------------------------------------------------------------------------------------------------
  2544. void Object::friend_bindToDrawable( Drawable *draw )
  2545. {
  2546. m_drawable = draw;
  2547. if (m_drawable)
  2548. {
  2549. ModelConditionFlags set;
  2550. ModelConditionFlags clr;
  2551. for (int i = 0; i < WEAPONSET_COUNT; ++i)
  2552. {
  2553. ModelConditionFlagType mcs = TheWeaponSetTypeToModelConditionTypeMap[i];
  2554. if( mcs != MODELCONDITION_INVALID )
  2555. {
  2556. if (m_curWeaponSetFlags.test(i))
  2557. set.set(mcs);
  2558. else
  2559. clr.set(mcs);
  2560. }
  2561. }
  2562. if (TheGlobalData)
  2563. {
  2564. if (TheGlobalData->m_forceModelsToFollowTimeOfDay)
  2565. {
  2566. set.set(MODELCONDITION_NIGHT, (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) ? 1 : 0);
  2567. }
  2568. if (TheGlobalData->m_forceModelsToFollowWeather)
  2569. {
  2570. set.set(MODELCONDITION_SNOW, (TheGlobalData->m_weather == WEATHER_SNOWY) ? 1 : 0);
  2571. }
  2572. }
  2573. m_drawable->clearAndSetModelConditionFlags(clr, set);
  2574. }
  2575. for (BehaviorModule** b = m_behaviors; *b; ++b)
  2576. {
  2577. (*b)->onDrawableBoundToObject();
  2578. }
  2579. }
  2580. //-------------------------------------------------------------------------------------------------
  2581. void Object::setSelectable(Bool selectable)
  2582. {
  2583. m_isSelectable = selectable;
  2584. if (m_drawable)
  2585. {
  2586. m_drawable->setSelectable(selectable);
  2587. }
  2588. }
  2589. //-------------------------------------------------------------------------------------------------
  2590. Bool Object::isSelectable() const
  2591. {
  2592. // return getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE)
  2593. // || (m_isSelectable
  2594. // && !testStatus(OBJECT_STATUS_UNSELECTABLE)
  2595. // && !isEffectivelyDead()
  2596. // && !getTemplate()->isKindOf(KINDOF_DRONE)//Most drones are unselectable from being slaved, but the SpyDrone needs help
  2597. // );
  2598. if (getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE))
  2599. return TRUE;
  2600. if ( m_isSelectable )
  2601. if ( !testStatus(OBJECT_STATUS_UNSELECTABLE) )
  2602. if ( !isEffectivelyDead() )
  2603. //if ( !getTemplate()->isKindOf(KINDOF_DRONE) )//Most drones are unselectable from being slaved, but the SpyDrone needs help
  2604. return TRUE;
  2605. return FALSE;
  2606. }
  2607. //-------------------------------------------------------------------------------------------------
  2608. Bool Object::isMassSelectable() const
  2609. {
  2610. return isSelectable() && !isKindOf(KINDOF_STRUCTURE);
  2611. }
  2612. //-------------------------------------------------------------------------------------------------
  2613. void Object::setWeaponSetFlag(WeaponSetType wst)
  2614. {
  2615. m_curWeaponSetFlags.set(wst);
  2616. m_weaponSet.updateWeaponSet(this);
  2617. if (m_drawable)
  2618. {
  2619. m_drawable->setModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
  2620. }
  2621. }
  2622. //-------------------------------------------------------------------------------------------------
  2623. void Object::clearWeaponSetFlag(WeaponSetType wst)
  2624. {
  2625. m_curWeaponSetFlags.set(wst, 0);
  2626. m_weaponSet.updateWeaponSet(this);
  2627. if (m_drawable)
  2628. {
  2629. m_drawable->clearModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
  2630. }
  2631. }
  2632. //-------------------------------------------------------------------------------------------------
  2633. Bool Object::hasSpecialPower( SpecialPowerType type ) const
  2634. {
  2635. return TEST_SPECIALPOWERMASK( m_specialPowerBits, type );
  2636. }
  2637. //-------------------------------------------------------------------------------------------------
  2638. Bool Object::hasAnySpecialPower() const
  2639. {
  2640. return SPECIALPOWERMASK_ANY_SET( m_specialPowerBits );
  2641. }
  2642. //-------------------------------------------------------------------------------------------------
  2643. void Object::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel, Bool provideFeedback )
  2644. {
  2645. updateUpgradeModules();
  2646. const UpgradeTemplate* up = TheUpgradeCenter->findVeterancyUpgrade(newLevel);
  2647. if (up)
  2648. giveUpgrade(up);
  2649. BodyModuleInterface* body = getBodyModule();
  2650. if (body)
  2651. body->onVeterancyLevelChanged( oldLevel, newLevel, provideFeedback );
  2652. Bool hideAnimationForStealth = FALSE;
  2653. if( !isLocallyControlled() &&
  2654. testStatus( OBJECT_STATUS_STEALTHED ) &&
  2655. !testStatus( OBJECT_STATUS_DETECTED ) &&
  2656. !testStatus( OBJECT_STATUS_DISGUISED ) )
  2657. {
  2658. hideAnimationForStealth = TRUE;
  2659. }
  2660. Bool doAnimation = ( ! hideAnimationForStealth
  2661. && (newLevel > oldLevel)
  2662. && ( ! isKindOf(KINDOF_IGNORED_IN_GUI))); //First, we plan to do the animation if the level went up
  2663. switch (newLevel)
  2664. {
  2665. case LEVEL_REGULAR:
  2666. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2667. clearWeaponSetFlag(WEAPONSET_ELITE);
  2668. clearWeaponSetFlag(WEAPONSET_HERO);
  2669. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2670. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2671. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2672. doAnimation = FALSE;//... but not if somehow up to Regular
  2673. break;
  2674. case LEVEL_VETERAN:
  2675. setWeaponSetFlag(WEAPONSET_VETERAN);
  2676. clearWeaponSetFlag(WEAPONSET_ELITE);
  2677. clearWeaponSetFlag(WEAPONSET_HERO);
  2678. setWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2679. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2680. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2681. break;
  2682. case LEVEL_ELITE:
  2683. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2684. setWeaponSetFlag(WEAPONSET_ELITE);
  2685. clearWeaponSetFlag(WEAPONSET_HERO);
  2686. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2687. setWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2688. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2689. break;
  2690. case LEVEL_HEROIC:
  2691. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2692. clearWeaponSetFlag(WEAPONSET_ELITE);
  2693. setWeaponSetFlag(WEAPONSET_HERO);
  2694. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2695. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2696. setWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2697. break;
  2698. }
  2699. if( doAnimation && TheGameLogic->getDrawIconUI() && provideFeedback )
  2700. {
  2701. if( TheAnim2DCollection && TheGlobalData->m_levelGainAnimationName.isEmpty() == FALSE )
  2702. {
  2703. Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_levelGainAnimationName );
  2704. Coord3D pos = *getPosition();
  2705. pos.add(&m_healthBoxOffset);
  2706. TheInGameUI->addWorldAnimation( animTemplate,
  2707. &pos,
  2708. WORLD_ANIM_FADE_ON_EXPIRE,
  2709. TheGlobalData->m_levelGainAnimationDisplayTimeInSeconds,
  2710. TheGlobalData->m_levelGainAnimationZRisePerSecond);
  2711. }
  2712. AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_unitPromoted;
  2713. soundToPlay.setObjectID( getID() );
  2714. TheAudio->addAudioEvent( &soundToPlay );
  2715. }
  2716. }
  2717. //-------------------------------------------------------------------------------------------------
  2718. /**
  2719. * Returns true if object currently has some kind of attack capability
  2720. */
  2721. Bool Object::isAbleToAttack() const
  2722. {
  2723. //******************************************************
  2724. //********* AUTOMATICALLY FALSE CONDITIONS *************
  2725. //******************************************************
  2726. // For things that may or may not be able to normally attack, but are under a status condition
  2727. if( getStatusBits().test( OBJECT_STATUS_NO_ATTACK ) )
  2728. return false;
  2729. // if we're contained within a transport we cannot attack unless it specifically allows us
  2730. const Object *containedBy = getContainedBy();
  2731. DEBUG_ASSERTCRASH( (containedBy == NULL) || (containedBy->getContain() != NULL), ("A %s thinks they are contained by something with no contain module!", getTemplate()->getName().str() ) );
  2732. if( containedBy && containedBy->getContain() && !containedBy->getContain()->isPassengerAllowedToFire( getID() ) )
  2733. return false;
  2734. // We can't fire if under construction
  2735. if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  2736. return false;
  2737. // or being sold
  2738. if( testStatus(OBJECT_STATUS_SOLD) )
  2739. return false;
  2740. if ( isDisabledByType( DISABLED_SUBDUED ) )
  2741. return FALSE; // A Microwave Tank is cooking me
  2742. //We can't fire if we, as a portable structure, are aptly disabled
  2743. if ( isKindOf( KINDOF_PORTABLE_STRUCTURE ) || isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ))
  2744. {
  2745. if( isDisabledByType( DISABLED_HACKED ) || isDisabledByType( DISABLED_EMP ) )
  2746. return false;
  2747. if ( isKindOf( KINDOF_INFANTRY ) ) // I must be a stinger soldier or similar
  2748. {
  2749. for (BehaviorModule** update = getBehaviorModules(); *update; ++update)//expensive search, limited only to stinger soldiers
  2750. {
  2751. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  2752. if ( sdu )
  2753. {
  2754. ObjectID slaverID = sdu->getSlaverID();
  2755. if ( slaverID != INVALID_ID )
  2756. {
  2757. Object *slaver = TheGameLogic->findObjectByID( slaverID );
  2758. if ( slaver && slaver->isDisabledByType( DISABLED_SUBDUED ))
  2759. return FALSE;// if my stinger site is subdued, so am I
  2760. }
  2761. break;//only expect one slavedupdate, so stop searching
  2762. }
  2763. }
  2764. }
  2765. }
  2766. //We can't fire if all our weapons are disabled!
  2767. //Currently, only turreted weapons can be disabled.
  2768. //ONLY DO THIS CHECK IF OUR UNIT DOESN'T HAVE THE
  2769. //KINDOF_CAN_ATTACK flag... nuke cannons have disabled
  2770. //turrets when not deployed, and need to be able to attack to deploy!
  2771. //Strategy centers can't attack when bombardment isn't active!
  2772. Bool anyEnabled = FALSE;
  2773. Bool anyWeapon = FALSE;
  2774. const AIUpdateInterface *ai = getAI();
  2775. if( ai && !isKindOf( KINDOF_CAN_ATTACK ) )
  2776. {
  2777. for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  2778. {
  2779. //Find the weapon in this slot.
  2780. Weapon* weapon = getWeaponInWeaponSlot( (WeaponSlotType)i );
  2781. if( !weapon )
  2782. continue;
  2783. anyWeapon = TRUE;
  2784. //We found a weapon, is it a turret?
  2785. Real dummy;
  2786. WhichTurretType tur = ai->getWhichTurretForWeaponSlot( (WeaponSlotType)i, &dummy );
  2787. if( tur == TURRET_INVALID )
  2788. {
  2789. //Currently impossible to disable a non-turreted weapon, so we
  2790. //have a non turreted weapon that is enabled. Quit.
  2791. anyEnabled = TRUE;
  2792. break;
  2793. }
  2794. if( ai->isTurretEnabled( tur ) )
  2795. {
  2796. //The turret is enable, meaning we have an enabled weapon. Quit.
  2797. anyEnabled = TRUE;
  2798. break;;
  2799. }
  2800. }
  2801. if( anyWeapon && !anyEnabled )
  2802. {
  2803. //We failed to find any active weapons.
  2804. return FALSE;
  2805. }
  2806. }
  2807. //***************************************
  2808. //********* TRUE CONDITIONS *************
  2809. //***************************************
  2810. // for certain buildings
  2811. if (isKindOf(KINDOF_CAN_ATTACK))
  2812. return true;
  2813. // for garrisonned buildings that can attack sometimes
  2814. if( getStatusBits().test( OBJECT_STATUS_CAN_ATTACK ) )
  2815. return true;
  2816. // for weaponless transports. This will make me think I can, but I will check if I literally can by looking
  2817. // at passenger weapons in CanAttack.
  2818. const ContainModuleInterface* contain = getContain();
  2819. if( contain && contain->isPassengerAllowedToFire( getID() ) && contain->getContainCount() > 0 )
  2820. return true;
  2821. // if we have AI and a weapon, assume we know how to use it
  2822. if (getAIUpdateInterface() != NULL && m_weaponSet.hasAnyWeapon())
  2823. {
  2824. // actually, we don't want to do this; we want the troop crawler to be considered "able to attack"
  2825. // even if empty, so sayeth Dustin. (srj)
  2826. // // special case: if the only damage we do is DEPLOY, we must have some guys contained.
  2827. // if (m_weaponSet.hasSingleDamageType(DAMAGE_DEPLOY))
  2828. // {
  2829. // return contain->getContainCount() > 0;
  2830. // }
  2831. // else
  2832. {
  2833. return true;
  2834. }
  2835. }
  2836. SpawnBehaviorInterface *spawnInterface = getSpawnBehaviorInterface();
  2837. if( spawnInterface )
  2838. {
  2839. if( spawnInterface->canAnySlavesAttack() )
  2840. {
  2841. return TRUE;
  2842. }
  2843. }
  2844. if (getTemplate()->isEnterGuard())
  2845. return TRUE;
  2846. //Default is no
  2847. return false;
  2848. }
  2849. //-------------------------------------------------------------------------------------------------
  2850. /**
  2851. * Mask/Un-Mask an object
  2852. */
  2853. void Object::maskObject( Bool mask )
  2854. {
  2855. // set or clear the mask bit
  2856. setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ), mask );
  2857. //
  2858. // when masking objects they become unselected ... we do this in any situation for
  2859. // any player cause you aren't allowed to select masked objects, if the object is not
  2860. // selected (ie, belongs to another player) it's no big deal cause it won't be selected
  2861. // anyway
  2862. //
  2863. if (mask)
  2864. TheGameLogic->deselectObject(this, ~getControllingPlayer()->getPlayerMask(), TRUE);
  2865. } // end maskObject
  2866. //-------------------------------------------------------------------------------------------------
  2867. /*
  2868. * returns true if the current locomotor is an airborne one
  2869. */
  2870. Bool Object::isUsingAirborneLocomotor( void ) const
  2871. {
  2872. return ( m_ai && m_ai->getCurLocomotor() && ((m_ai->getCurLocomotor()->getLegalSurfaces() & LOCOMOTORSURFACE_AIR) != 0) );
  2873. }
  2874. //-------------------------------------------------------------------------------------------------
  2875. //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
  2876. //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
  2877. //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
  2878. //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
  2879. void Object::getHealthBoxPosition(Coord3D& pos) const
  2880. {
  2881. pos = *getPosition();
  2882. pos.z += getGeometryInfo().getMaxHeightAbovePosition() + 10;
  2883. pos.add(&m_healthBoxOffset);
  2884. // this needs to get moved to the mobspawnerupdate
  2885. if (isKindOf(KINDOF_MOB_NEXUS)) // quicker idiot test
  2886. {
  2887. pos.z += 20;// dear God, I confess my kluge, and repent.
  2888. }
  2889. }
  2890. //-------------------------------------------------------------------------------------------------
  2891. //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
  2892. //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
  2893. //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
  2894. //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
  2895. Bool Object::getHealthBoxDimensions(Real &healthBoxHeight, Real &healthBoxWidth) const
  2896. {
  2897. #ifdef CALC_HEALTHBAR_FROM_HITPOINTS
  2898. Real maxHP = getBodyModule()->getMaxHealth();
  2899. if( isKindOf( KINDOF_STRUCTURE ) )
  2900. {
  2901. //enforce healthBoxHeightMinimum/Maximum
  2902. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2903. //enforce healthBoxWidthMinimum/Maximum
  2904. healthBoxWidth = min(150.0f, max(100.0f, maxHP/10));
  2905. return true;
  2906. }
  2907. else if ( isKindOf(KINDOF_MOB_NEXUS) )
  2908. {
  2909. //enforce healthBoxHeightMinimum/Maximum
  2910. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2911. //enforce healthBoxWidthMinimum/Maximum
  2912. healthBoxWidth = min(100.0f, max(66.0f, maxHP/10));
  2913. return true;
  2914. }
  2915. else if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2916. {
  2917. healthBoxHeight = 0;
  2918. healthBoxWidth = 0;
  2919. return false;
  2920. }
  2921. else
  2922. {
  2923. //enforce healthBoxHeightMinimum/Maximum
  2924. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2925. //enforce healthBoxWidthMinimum/Maximum
  2926. healthBoxWidth = min(150.0f, max(35.0f, maxHP/10));
  2927. return true;
  2928. }
  2929. #else
  2930. if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2931. {
  2932. healthBoxHeight = 0;
  2933. healthBoxWidth = 0;
  2934. return false;
  2935. }
  2936. //just add the major and minor axes
  2937. Real size = MAX(20.0f, MIN(150.0f, (getGeometryInfo().getMajorRadius() + getGeometryInfo().getMinorRadius())) );
  2938. healthBoxHeight = 3.0f;
  2939. healthBoxWidth = MAX(20.0f, size * 2.0f);
  2940. return TRUE;
  2941. #endif
  2942. }
  2943. //-------------------------------------------------------------------------------------------------
  2944. /**
  2945. * Update this object instance with properties from the map object
  2946. *
  2947. */
  2948. void Object::updateObjValuesFromMapProperties(Dict* properties)
  2949. {
  2950. Bool exists;
  2951. AsciiString valStr;
  2952. Bool valBool = false;
  2953. Int valInt = 0;
  2954. Real valReal = 0.0f;
  2955. valStr = properties->getAsciiString(TheKey_objectName, &exists);
  2956. if (exists) {
  2957. setName(valStr);
  2958. }
  2959. valInt = properties->getInt(TheKey_objectMaxHPs, &exists);
  2960. if (exists && valInt >= 0) {
  2961. BodyModuleInterface* body = getBodyModule();
  2962. if (body) {
  2963. body->setMaxHealth(valInt);
  2964. }
  2965. }
  2966. valInt = properties->getInt(TheKey_objectInitialHealth, &exists);
  2967. if (exists) {
  2968. BodyModuleInterface* body = getBodyModule();
  2969. if (body) {
  2970. body->setInitialHealth(valInt);
  2971. }
  2972. }
  2973. // set the veterancy level
  2974. valInt = properties->getInt(TheKey_objectVeterancy, &exists);
  2975. if (exists) {
  2976. if (m_experienceTracker && m_experienceTracker->isTrainable())
  2977. {
  2978. m_experienceTracker->setVeterancyLevel((VeterancyLevel)valInt);
  2979. }
  2980. }
  2981. // set the aggressiveness/mood
  2982. valInt = properties->getInt(TheKey_objectAggressiveness, &exists);
  2983. if (exists) {
  2984. AIUpdateInterface *ai = getAIUpdateInterface();
  2985. if (ai)
  2986. {
  2987. ai->setAttitude((AttitudeType)valInt);
  2988. }
  2989. }
  2990. // set recruitable
  2991. valBool = properties->getBool(TheKey_objectRecruitableAI, &exists);
  2992. if (exists) {
  2993. if (getAIUpdateInterface())
  2994. {
  2995. getAIUpdateInterface()->setIsRecruitable(valBool);
  2996. }
  2997. }
  2998. // set selectable
  2999. valBool = properties->getBool(TheKey_objectSelectable, &exists);
  3000. if (exists) {
  3001. if (valBool != isSelectable()) {
  3002. setSelectable(valBool);
  3003. }
  3004. }
  3005. // set the stopping distance
  3006. valReal = properties->getReal(TheKey_objectStoppingDistance, &exists);
  3007. if (exists && valReal >= 0.5f)
  3008. {
  3009. if (getAIUpdateInterface() && getAIUpdateInterface()->getCurLocomotor())
  3010. {
  3011. Locomotor *loco = getAIUpdateInterface()->getCurLocomotor();
  3012. loco->setCloseEnoughDist(valReal);
  3013. }
  3014. }
  3015. // set the disabledness of this object
  3016. valBool = properties->getBool(TheKey_objectEnabled, &exists);
  3017. if (exists) {
  3018. setScriptStatus(OBJECT_STATUS_SCRIPT_DISABLED, !valBool);
  3019. }
  3020. // set the disabledness of this object
  3021. valBool = properties->getBool(TheKey_objectPowered, &exists);
  3022. if (exists) {
  3023. setScriptStatus(OBJECT_STATUS_SCRIPT_UNPOWERED, !valBool);
  3024. }
  3025. // set the invulnerability of the object
  3026. valBool = properties->getBool(TheKey_objectIndestructible, &exists);
  3027. if (exists) {
  3028. BodyModuleInterface* body = getBodyModule();
  3029. if (body) {
  3030. body->setIndestructible(valBool);
  3031. }
  3032. }
  3033. // set the sellability of the object
  3034. valBool = properties->getBool(TheKey_objectUnsellable, &exists);
  3035. if (exists) {
  3036. setScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE, valBool);
  3037. }
  3038. //Set the player targetable setting of the object
  3039. valBool = properties->getBool( TheKey_objectTargetable, &exists );
  3040. if( exists )
  3041. {
  3042. setScriptStatus(OBJECT_STATUS_SCRIPT_TARGETABLE, valBool);
  3043. }
  3044. // adjust the vision distance of this object, overriding its default vision distance
  3045. valInt = properties->getInt(TheKey_objectVisualRange, &exists);
  3046. if (exists)
  3047. {
  3048. if (valInt < 0)
  3049. valInt = 0;
  3050. m_visionRange = INT_TO_REAL(valInt);
  3051. }
  3052. // adjust the shroud clearing distance of this object, overriding its default distance
  3053. valInt = properties->getInt(TheKey_objectShroudClearingDistance, &exists);
  3054. if (exists)
  3055. {
  3056. if (valInt < 0)
  3057. valInt = 0.0f;
  3058. m_shroudClearingRange = INT_TO_REAL(valInt);
  3059. }
  3060. Int upgradeNum = 0;
  3061. do
  3062. {
  3063. AsciiString keyName;
  3064. keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_objectGrantUpgrade).str(), upgradeNum);
  3065. valStr = properties->getAsciiString(NAMEKEY(keyName), &exists);
  3066. if (exists)
  3067. {
  3068. const UpgradeTemplate *ut = TheUpgradeCenter->findUpgrade(valStr);
  3069. if (ut)
  3070. giveUpgrade(ut);
  3071. }
  3072. else
  3073. {
  3074. valStr.clear();
  3075. }
  3076. ++upgradeNum;
  3077. } while (!valStr.isEmpty());
  3078. Drawable *drawable = getDrawable();
  3079. if ( drawable )
  3080. {
  3081. valInt = properties->getInt(TheKey_objectTime, &exists);
  3082. if (exists)
  3083. {
  3084. switch (valInt)
  3085. {
  3086. case 1:
  3087. drawable->clearModelConditionState(MODELCONDITION_NIGHT);
  3088. break;
  3089. case 2:
  3090. drawable->setModelConditionState(MODELCONDITION_NIGHT);
  3091. break;
  3092. default:
  3093. break;
  3094. }
  3095. }
  3096. valInt = properties->getInt(TheKey_objectWeather, &exists);
  3097. if (exists)
  3098. {
  3099. switch (valInt)
  3100. {
  3101. case 1:
  3102. drawable->clearModelConditionState(MODELCONDITION_SNOW);
  3103. break;
  3104. case 2:
  3105. drawable->setModelConditionState(MODELCONDITION_SNOW);
  3106. break;
  3107. default:
  3108. break;
  3109. }
  3110. }
  3111. // See if we are supposed to playing the ambient sound
  3112. Bool soundEnabledExists;
  3113. Bool soundEnabled = properties->getBool( TheKey_objectSoundAmbientEnabled, &soundEnabledExists );
  3114. DynamicAudioEventInfo * audioToModify = NULL;
  3115. Bool infoModified = false;
  3116. valStr = properties->getAsciiString( TheKey_objectSoundAmbient, &exists );
  3117. if ( exists )
  3118. {
  3119. if ( valStr.isEmpty() )
  3120. {
  3121. drawable->setCustomSoundAmbientOff();
  3122. soundEnabledExists = true;
  3123. soundEnabled = false; // Don't bother trying to enable later
  3124. }
  3125. else
  3126. {
  3127. const AudioEventInfo * baseInfo = TheAudio->findAudioEventInfo( valStr );
  3128. DEBUG_ASSERTCRASH( baseInfo != NULL, ("Cannot find customized ambient sound '%s'", valStr.str() ) );
  3129. if ( baseInfo != NULL )
  3130. {
  3131. audioToModify = newInstance( DynamicAudioEventInfo )( *baseInfo );
  3132. infoModified = true;
  3133. }
  3134. }
  3135. }
  3136. // Don't do anything more to audio if we forced the ambient sound off
  3137. if ( !( exists && valStr.isEmpty() ) )
  3138. {
  3139. valBool = properties->getBool( TheKey_objectSoundAmbientCustomized, &exists );
  3140. if ( exists && valBool )
  3141. {
  3142. if ( audioToModify == NULL )
  3143. {
  3144. const AudioEventInfo * baseInfo = drawable->getBaseSoundAmbientInfo( );
  3145. DEBUG_ASSERTCRASH( baseInfo != NULL, ("getBaseSoundAmbientInfo() return NULL" ) );
  3146. if ( baseInfo != NULL )
  3147. {
  3148. audioToModify = newInstance( DynamicAudioEventInfo )( *baseInfo );
  3149. }
  3150. }
  3151. if ( audioToModify != NULL )
  3152. {
  3153. valBool = properties->getBool( TheKey_objectSoundAmbientLooping, &exists );
  3154. if ( exists )
  3155. {
  3156. audioToModify->overrideLoopFlag( valBool );
  3157. infoModified = true;
  3158. }
  3159. valInt = properties->getInt( TheKey_objectSoundAmbientLoopCount, &exists );
  3160. if ( exists && BitTest( audioToModify->m_control, AC_LOOP ) )
  3161. {
  3162. audioToModify->overrideLoopCount( valInt );
  3163. infoModified = true;
  3164. }
  3165. valReal = properties->getReal( TheKey_objectSoundAmbientMinVolume, &exists );
  3166. if ( exists )
  3167. {
  3168. audioToModify->overrideMinVolume( valReal );
  3169. infoModified = true;
  3170. }
  3171. valReal = properties->getReal( TheKey_objectSoundAmbientVolume, &exists );
  3172. if ( exists )
  3173. {
  3174. audioToModify->overrideVolume( valReal );
  3175. infoModified = true;
  3176. }
  3177. valReal = properties->getReal( TheKey_objectSoundAmbientMinRange, &exists );
  3178. if ( exists )
  3179. {
  3180. audioToModify->overrideMinRange( valReal );
  3181. infoModified = true;
  3182. }
  3183. valReal = properties->getReal( TheKey_objectSoundAmbientMaxRange, &exists );
  3184. if ( exists )
  3185. {
  3186. audioToModify->overrideMaxRange( valReal );
  3187. infoModified = true;
  3188. }
  3189. valInt = properties->getInt( TheKey_objectSoundAmbientPriority, &exists );
  3190. if ( exists )
  3191. {
  3192. audioToModify->overridePriority ( (AudioPriority)valInt );
  3193. infoModified = true;
  3194. }
  3195. }
  3196. }
  3197. }
  3198. if ( !soundEnabledExists )
  3199. {
  3200. // Decide if the sound should start enabled or not, since the map maker didn't record
  3201. // a preference. Enable permanently looping sounds, disable one-shot sounds by default
  3202. // NOTE: This test should match the tests done in MapObjectProps::mapObjectPageSound::dictToEnabled()
  3203. // when it decided whether or not to show a customized sound as enabled
  3204. if ( audioToModify != NULL )
  3205. {
  3206. soundEnabled = audioToModify->isPermanentSound();
  3207. soundEnabledExists = true; // To get into enableAmbientSoundFromScript() call.
  3208. }
  3209. else
  3210. {
  3211. // Use default audio
  3212. const AudioEventInfo * baseInfo = drawable->getBaseSoundAmbientInfo( );
  3213. if ( baseInfo != NULL )
  3214. {
  3215. soundEnabled = baseInfo->isPermanentSound();
  3216. soundEnabledExists = true; // To get into enableAmbientSoundFromScript() call.
  3217. }
  3218. }
  3219. }
  3220. if ( soundEnabledExists && !soundEnabled )
  3221. {
  3222. // Make sure sound doesn't start playing when we set it
  3223. // ...FromScript because this is also controlled by the map designer not the game logic
  3224. drawable->enableAmbientSoundFromScript( false );
  3225. }
  3226. if ( infoModified && audioToModify != NULL )
  3227. {
  3228. // Give a custom, level-specific name
  3229. drawable->mangleCustomAudioName( audioToModify );
  3230. // Pass to TheAudio
  3231. TheAudio->addAudioEventInfo( audioToModify );
  3232. drawable->setCustomSoundAmbientInfo( audioToModify );
  3233. audioToModify = NULL; // Belongs to TheAudio now
  3234. }
  3235. if ( audioToModify != NULL )
  3236. {
  3237. audioToModify->deleteInstance();
  3238. audioToModify = NULL;
  3239. }
  3240. if ( soundEnabledExists && soundEnabled )
  3241. {
  3242. // Play sound now that it is set up, if needed. Don't call if already enabled because that
  3243. // can cause sound to play twice
  3244. // ...FromScript because this is also controlled by the map designer not the game logic
  3245. if ( !drawable->getAmbientSoundEnabledFromScript() )
  3246. {
  3247. drawable->enableAmbientSoundFromScript( true );
  3248. }
  3249. }
  3250. }
  3251. }
  3252. //-------------------------------------------------------------------------------------------------
  3253. void Object::friend_adjustPowerForPlayer( Bool incoming )
  3254. {
  3255. if (isDisabled() && getTemplate()->getEnergyProduction() > 0)
  3256. {
  3257. // Disabledness only affects Producers, not Consumers.
  3258. return;
  3259. }
  3260. if (incoming) {
  3261. getControllingPlayer()->getEnergy()->objectEnteringInfluence(this);
  3262. } else {
  3263. getControllingPlayer()->getEnergy()->objectLeavingInfluence(this);
  3264. }
  3265. }
  3266. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3267. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3268. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3269. //-------------------------------------------------------------------------------------------------
  3270. void Object::onDisabledEdge(Bool becomingDisabled)
  3271. {
  3272. // rip through the behavior modules and call the onDisabledEdge for any modules that care
  3273. for( BehaviorModule **module = m_behaviors; *module; ++module )
  3274. (*module)->onDisabledEdge( becomingDisabled );
  3275. DozerAIInterface *dozerAI = getAI() ? getAI()->getDozerAIInterface() : NULL;
  3276. if( becomingDisabled && dozerAI )
  3277. {
  3278. // Have to say goodbye to the thing we might be building or repairing so someone else can do it.
  3279. if( dozerAI->getCurrentTask() != DOZER_TASK_INVALID )
  3280. dozerAI->cancelTask( dozerAI->getCurrentTask() );
  3281. }
  3282. Player* controller = getControllingPlayer();
  3283. // can be called during game teardown, thus controller can be null
  3284. if (controller)
  3285. {
  3286. //@todo jkmcd - Colin suggested we rewrite this to use the interface stuff. I agree, but need
  3287. // to get some more bugs fixed today.
  3288. static NameKeyType radar = NAMEKEY("RadarUpgrade");
  3289. Module *mod = mod = findModule(radar);
  3290. if (mod) {
  3291. RadarUpgrade *radarMod = (RadarUpgrade*) mod;
  3292. if (radarMod->isAlreadyUpgraded()) {
  3293. // Need to decrement the count here, because we own a radar upgrade
  3294. if (becomingDisabled) {
  3295. controller->removeRadar(radarMod->getIsDisableProof());
  3296. } else {
  3297. controller->addRadar(radarMod->getIsDisableProof());
  3298. }
  3299. }
  3300. }
  3301. }
  3302. // We will need to adjust power ... somehow ...
  3303. Int powerToAdjust = getTemplate()->getEnergyProduction();
  3304. if( powerToAdjust > 0 )
  3305. {
  3306. // We can't affect something that consumes, or else we go low power which removes the consumption
  3307. // which makes us not low power so we add the consumption so we go low power...
  3308. // This check also guaards the IsDisabled in friend_adjustPower above
  3309. static NameKeyType powerPlant = NAMEKEY("PowerPlantUpgrade");
  3310. static NameKeyType overCharge = NAMEKEY("OverchargeBehavior");
  3311. Module* mod = findModule(powerPlant);
  3312. if (mod) {
  3313. PowerPlantUpgrade *powerPlantMod = (PowerPlantUpgrade*) mod;
  3314. if (powerPlantMod->isAlreadyUpgraded()) {
  3315. powerToAdjust += getTemplate()->getEnergyBonus();
  3316. }
  3317. }
  3318. mod = findModule(overCharge);
  3319. if (mod) {
  3320. OverchargeBehavior *overChargeMod = (OverchargeBehavior*) mod;
  3321. if (overChargeMod->isOverchargeActive()) {
  3322. powerToAdjust += getTemplate()->getEnergyBonus();
  3323. }
  3324. }
  3325. // Now, adjust the power for the player.
  3326. if (controller)
  3327. controller->getEnergy()->adjustPower(powerToAdjust, !becomingDisabled);
  3328. }
  3329. }
  3330. //-------------------------------------------------------------------------------------------------
  3331. /** Object CRC implemtation */
  3332. //-------------------------------------------------------------------------------------------------
  3333. void Object::crc( Xfer *xfer )
  3334. {
  3335. // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use
  3336. // the same interface as the XferLoad class for save game restore. This only works because
  3337. // XferCRC does not modify its data.
  3338. #ifdef DEBUG_CRC
  3339. // g_logObjectCRCs = TRUE;
  3340. // Bool g_logAllObjects = TRUE;
  3341. AsciiString logString;
  3342. AsciiString tmp;
  3343. Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */;
  3344. if (doLogging)
  3345. {
  3346. tmp.format("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex());
  3347. logString.concat(tmp);
  3348. }
  3349. #endif DEBUG_CRC
  3350. xfer->xferUnsignedByte(&m_privateStatus);
  3351. #ifdef DEBUG_CRC
  3352. if (doLogging)
  3353. {
  3354. tmp.format("m_privateStatus: %X, ", (UnsignedInt)m_privateStatus);
  3355. logString.concat(tmp);
  3356. }
  3357. #endif // DEBUG_CRC
  3358. xfer->xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
  3359. #ifdef DEBUG_CRC
  3360. if (doLogging)
  3361. {
  3362. XferCRC tmpXfer;
  3363. tmpXfer.open("tmp");
  3364. tmpXfer.xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
  3365. tmp.format("getTransformMatrix(): %8.8X, ", tmpXfer.getCRC());
  3366. tmpXfer.close();
  3367. logString.concat(tmp);
  3368. }
  3369. #endif DEBUG_CRC
  3370. #ifdef DEBUG_CRC
  3371. if (doLogging)
  3372. {
  3373. const Matrix3D *mtx = getTransformMatrix();
  3374. CRCDEBUG_LOG(("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex()));
  3375. DUMPMATRIX3D(mtx);
  3376. }
  3377. #endif DEBUG_CRC
  3378. xfer->xferUser(&m_id, sizeof(m_id));
  3379. #ifdef DEBUG_CRC
  3380. if (doLogging)
  3381. {
  3382. tmp.format("m_id: %d, ", m_id);
  3383. logString.concat(tmp);
  3384. }
  3385. #endif DEBUG_CRC
  3386. xfer->xferUser(&m_objectUpgradesCompleted, sizeof(Int64));
  3387. #ifdef DEBUG_CRC
  3388. if (doLogging)
  3389. {
  3390. tmp.format("m_objectUpgradesCompleted: %I64X, ", m_objectUpgradesCompleted);
  3391. logString.concat(tmp);
  3392. }
  3393. #endif DEBUG_CRC
  3394. if (m_experienceTracker)
  3395. xfer->xferSnapshot( m_experienceTracker );
  3396. #ifdef DEBUG_CRC
  3397. if (doLogging)
  3398. {
  3399. XferCRC tmpXfer;
  3400. tmpXfer.open("tmp");
  3401. tmpXfer.xferSnapshot(m_experienceTracker);
  3402. tmp.format("m_experienceTracker: %8.8X, ", tmpXfer.getCRC());
  3403. tmpXfer.close();
  3404. logString.concat(tmp);
  3405. }
  3406. #endif DEBUG_CRC
  3407. Real health = getBodyModule()->getHealth();
  3408. xfer->xferUser(&health, sizeof(health));
  3409. #ifdef DEBUG_CRC
  3410. if (doLogging)
  3411. {
  3412. tmp.format("health: %g/%8.8X, ", health, AS_INT(health));
  3413. logString.concat(tmp);
  3414. }
  3415. #endif DEBUG_CRC
  3416. xfer->xferUnsignedInt(&m_weaponBonusCondition);
  3417. #ifdef DEBUG_CRC
  3418. if (doLogging)
  3419. {
  3420. tmp.format("m_weaponBonusCondition: %8.8X, ", m_weaponBonusCondition);
  3421. logString.concat(tmp);
  3422. }
  3423. #endif DEBUG_CRC
  3424. Real scalar = getBodyModule()->getDamageScalar();
  3425. xfer->xferUser(&scalar, sizeof(scalar));
  3426. #ifdef DEBUG_CRC
  3427. if (doLogging)
  3428. {
  3429. tmp.format("damage scalar: %g/%8.8X\n", scalar, AS_INT(scalar));
  3430. logString.concat(tmp);
  3431. CRCDEBUG_LOG(("%s", logString.str()));
  3432. }
  3433. #endif DEBUG_CRC
  3434. for (Int i=0; i<WEAPONSLOT_COUNT; ++i)
  3435. {
  3436. Weapon *thisWeapon = getWeaponInWeaponSlot((WeaponSlotType)i);
  3437. if (thisWeapon)
  3438. {
  3439. xfer->xferSnapshot( thisWeapon );
  3440. }
  3441. }
  3442. } // end crc
  3443. //-------------------------------------------------------------------------------------------------
  3444. /** Object xfer implemtation
  3445. * Version Info:
  3446. * 1: Initial version
  3447. * 2: Xfers m_singleUseCommandUsed... determines if the single use command button has been used or not.
  3448. * 3: Xfers the solehealingbenefactor ID and expiration frame
  3449. * 4: misc stuff that got missed somehow
  3450. * 5: m_isReceivingDifficultyBonus
  3451. * 6: We do indeed need to save m_containedBy. The comment misrepresents what the contain module will do.
  3452. * 7: save full mtx, not pos+orient.
  3453. * 8: Kris: Conversion of object status bits from UnsignedInt to BitFlags<>
  3454. * 9: Extra sighting for reveal to all with different range units
  3455. */
  3456. //-------------------------------------------------------------------------------------------------
  3457. void Object::xfer( Xfer *xfer )
  3458. {
  3459. // version
  3460. const XferVersion currentVersion = 9;
  3461. XferVersion version = currentVersion;
  3462. xfer->xferVersion( &version, currentVersion );
  3463. // object ID
  3464. ObjectID id = getID();
  3465. xfer->xferObjectID( &id );
  3466. setID( id );
  3467. DEBUG_LOG(("Xfer Object %s id=%d\n",getTemplate()->getName().str(),id));
  3468. if (version >= 7)
  3469. {
  3470. Matrix3D mtx = *getTransformMatrix();
  3471. xfer->xferMatrix3D(&mtx);
  3472. setTransformMatrix(&mtx);
  3473. }
  3474. else
  3475. {
  3476. // object position
  3477. Coord3D pos = *getPosition();
  3478. xfer->xferCoord3D( &pos );
  3479. setPosition( &pos );
  3480. // orientation
  3481. Real orientation = getOrientation();
  3482. xfer->xferReal( &orientation );
  3483. setOrientation( orientation );
  3484. }
  3485. // team
  3486. TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
  3487. xfer->xferUser( &teamID, sizeof( TeamID ) );
  3488. // DON'T set the team yet; must wait till we read our status bits,
  3489. // since setTeam can affect the player's power usage, but that could
  3490. // be done incorrectly if our status bits aren't accurate yet... (srj)
  3491. // producer id
  3492. xfer->xferObjectID( &m_producerID );
  3493. // builder id
  3494. xfer->xferObjectID( &m_builderID );
  3495. // drawable id
  3496. Drawable *draw = getDrawable();
  3497. DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
  3498. xfer->xferDrawableID( &drawableID );
  3499. if( xfer->getXferMode() == XFER_LOAD )
  3500. {
  3501. // change the ID of the drawable attached to be the same ID as it was when it was saved
  3502. draw->setID( drawableID );
  3503. } // end if
  3504. // internal name
  3505. xfer->xferAsciiString( &m_name );
  3506. // status
  3507. if( version >= 8 )
  3508. {
  3509. m_status.xfer( xfer );
  3510. }
  3511. else
  3512. {
  3513. //We are loading an old version, so we must convert it from a 32-bit int to a bitflag
  3514. UnsignedInt oldStatus;
  3515. xfer->xferUnsignedInt( &oldStatus );
  3516. //Clear our status
  3517. m_status.clear();
  3518. for( int i = 0; i < 32; i++ )
  3519. {
  3520. UnsignedInt bit = 1<<i;
  3521. if( oldStatus & bit )
  3522. {
  3523. ObjectStatusTypes status = (ObjectStatusTypes)(i+1);
  3524. m_status.set( MAKE_OBJECT_STATUS_MASK( status ) );
  3525. }
  3526. }
  3527. }
  3528. // script status
  3529. xfer->xferUnsignedByte( &m_scriptStatus );
  3530. // private status
  3531. xfer->xferUnsignedByte( &m_privateStatus );
  3532. // OK, now that we have xferred our status bits, it's safe to set the team...
  3533. if( xfer->getXferMode() == XFER_LOAD )
  3534. {
  3535. Team *team = TheTeamFactory->findTeamByID( teamID );
  3536. if( team == NULL )
  3537. {
  3538. DEBUG_CRASH(( "Object::xfer - Unable to load team\n" ));
  3539. throw SC_INVALID_DATA;
  3540. }
  3541. const Bool restoring = true;
  3542. setOrRestoreTeam( team, restoring );
  3543. }
  3544. // geometry info
  3545. xfer->xferSnapshot( &m_geometryInfo );
  3546. // sighting info, last look - must be saved cause we save PartitionCell::m_shroudLevel
  3547. xfer->xferSnapshot( m_partitionLastLook );
  3548. if( version >= 9 )
  3549. xfer->xferSnapshot( m_partitionRevealAllLastLook );
  3550. // sighting info, last shroud - must be saved cause we save PartitionCell::m_shroudLevel
  3551. xfer->xferSnapshot( m_partitionLastShroud );
  3552. // vision spied by
  3553. xfer->xferUser( m_visionSpiedBy, sizeof( Int ) * MAX_PLAYER_COUNT );
  3554. // vision spied by mask
  3555. xfer->xferUser( &m_visionSpiedMask, sizeof( PlayerMaskType ) );
  3556. // sighting info, last threat
  3557. // John M says we don't need to save this (CBD)
  3558. // xfer->xferSnapshot( &m_partitionLastThreat );
  3559. // sighting info, last value
  3560. // John M says we don't need to save this (CBD)
  3561. // xfer->xferSnapshot( &m_partitionLastValue );
  3562. // vision range
  3563. xfer->xferReal( &m_visionRange );
  3564. // shroud clearing range
  3565. xfer->xferReal( &m_shroudClearingRange );
  3566. // shroud range
  3567. xfer->xferReal( &m_shroudRange );
  3568. // disabled mask
  3569. m_disabledMask.xfer( xfer );
  3570. //New var added for version 2. Determines if the single use command button has been used or not.
  3571. if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
  3572. {
  3573. xfer->xferBool( &m_singleUseCommandUsed );
  3574. }
  3575. else
  3576. {
  3577. m_singleUseCommandUsed = false;
  3578. }
  3579. // disabled till frame
  3580. xfer->xferUser( m_disabledTillFrame, sizeof( UnsignedInt ) * DISABLED_COUNT );
  3581. // special model condition until
  3582. xfer->xferUnsignedInt( &m_smcUntil );
  3583. //
  3584. // radar data ... when loading, we will remove all objects from the radar and let
  3585. // the radar system load itself as a separate chunk of data from the save file
  3586. //
  3587. if( xfer->getXferMode() == XFER_LOAD && m_radarData )
  3588. TheRadar->removeObject( this );
  3589. // experience tracker
  3590. xfer->xferSnapshot( m_experienceTracker );
  3591. //
  3592. // we do not need to do anything with our m_containedBy pointer, the post process
  3593. // of that objects contain module will actually re-do the contain process again
  3594. //
  3595. // m_containedBy <-- do nothing with this right now
  3596. if( version >= 6 )
  3597. {
  3598. // No, the contain module is just going to friend_ reach in and set this for us.
  3599. // Containers more complicated than Open (like Tunnel) can't do that. Our variable,
  3600. // our responsibility.
  3601. if( xfer->getXferMode() == XFER_SAVE )
  3602. {
  3603. if( m_containedBy != NULL )
  3604. m_xferContainedByID = m_containedBy->getID();
  3605. else
  3606. m_xferContainedByID = INVALID_ID;
  3607. }
  3608. xfer->xferObjectID( &m_xferContainedByID );
  3609. }
  3610. // contained by frame
  3611. xfer->xferUnsignedInt( &m_containedByFrame );
  3612. // construction percent
  3613. xfer->xferReal( &m_constructionPercent );
  3614. // upgrades completed
  3615. xfer->xferUpgradeMask( &m_objectUpgradesCompleted );
  3616. // original team name
  3617. xfer->xferAsciiString( &m_originalTeamName );
  3618. // indicator color
  3619. xfer->xferColor( &m_indicatorColor );
  3620. // health box offset
  3621. xfer->xferCoord3D( &m_healthBoxOffset );
  3622. // Entered & exited housekeeping.
  3623. Int i;
  3624. xfer->xferByte(&m_numTriggerAreasActive);
  3625. xfer->xferUnsignedInt(&m_enteredOrExitedFrame);
  3626. xfer->xferICoord3D(&m_iPos);
  3627. if (m_numTriggerAreasActive<0 || m_numTriggerAreasActive>MAX_TRIGGER_AREA_INFOS) {
  3628. DEBUG_CRASH(("Invalid m_numTriggerAreasActive = %d, max is %d", m_numTriggerAreasActive,
  3629. MAX_TRIGGER_AREA_INFOS));
  3630. throw SC_INVALID_DATA;
  3631. }
  3632. for (i=0; i<m_numTriggerAreasActive; i++) {
  3633. AsciiString triggerName;
  3634. if (m_triggerInfo[i].pTrigger) {
  3635. triggerName = m_triggerInfo[i].pTrigger->getTriggerName();
  3636. }
  3637. xfer->xferAsciiString(&triggerName);
  3638. if (xfer->getXferMode() == XFER_LOAD)
  3639. {
  3640. //
  3641. // CBD (11-13-2002) I'm disabling this because it appears there might be some areas with
  3642. // empty names, see John A. for more info
  3643. //
  3644. //if (triggerName.isNotEmpty())
  3645. m_triggerInfo[i].pTrigger = TheTerrainLogic->getTriggerAreaByName(triggerName);
  3646. }
  3647. xfer->xferByte(&m_triggerInfo[i].entered);
  3648. xfer->xferByte(&m_triggerInfo[i].exited);
  3649. xfer->xferByte(&m_triggerInfo[i].isInside);
  3650. }
  3651. // Layer object is pathing on.
  3652. xfer->xferUser(&m_layer, sizeof(m_layer));
  3653. // Layer of current path goal.
  3654. xfer->xferUser(&m_destinationLayer, sizeof(m_destinationLayer));
  3655. // Object selectability.
  3656. xfer->xferBool(&m_isSelectable);
  3657. xfer->xferUnsignedInt(&m_safeOcclusionFrame);
  3658. // User formations.
  3659. xfer->xferUser(&m_formationID, sizeof(m_formationID));
  3660. if (m_formationID!=NO_FORMATION_ID) {
  3661. xfer->xferCoord2D(&m_formationOffset);
  3662. }
  3663. // module count
  3664. UnsignedShort moduleCount = 0;
  3665. for (BehaviorModule** b = m_behaviors; *b; ++b)
  3666. ++moduleCount;
  3667. xfer->xferUnsignedShort( &moduleCount );
  3668. AsciiString moduleIdentifier;
  3669. BehaviorModule *module;
  3670. if( xfer->getXferMode() == XFER_SAVE )
  3671. {
  3672. // go through all modules
  3673. for (BehaviorModule** b = m_behaviors; *b; ++b)
  3674. {
  3675. // get module
  3676. module = *b;
  3677. // write module identifier
  3678. moduleIdentifier = TheNameKeyGenerator->keyToName( module->getModuleTagNameKey() );
  3679. DEBUG_ASSERTCRASH( moduleIdentifier != AsciiString::TheEmptyString,
  3680. ("Object::xfer - Module tag key does not translate to a string!\n") );
  3681. xfer->xferAsciiString( &moduleIdentifier );
  3682. // begin a data block
  3683. xfer->beginBlock();
  3684. // xfer data
  3685. xfer->xferSnapshot( module );
  3686. // end data block
  3687. xfer->endBlock();
  3688. } // end for, it
  3689. } // end if, save
  3690. else
  3691. {
  3692. AsciiString otherModuleIdentifier;
  3693. // read all module data
  3694. for( UnsignedShort i = 0; i < moduleCount; ++i )
  3695. {
  3696. // read module name
  3697. xfer->xferAsciiString( &moduleIdentifier );
  3698. NameKeyType moduleIdentifierKey = TheNameKeyGenerator->nameToKey(moduleIdentifier);
  3699. // find the module with this identifier in the module list
  3700. module = NULL;
  3701. for (BehaviorModule** b = m_behaviors; b && *b; ++b)
  3702. {
  3703. if (moduleIdentifierKey == (*b)->getModuleTagNameKey())
  3704. {
  3705. module = *b;
  3706. break;
  3707. }
  3708. } // end for, moduleIt
  3709. // start of a new block
  3710. Int dataSize = xfer->beginBlock();
  3711. //
  3712. // if we didn't find the module, it's quite possible that we have removed
  3713. // it from the object definition in a future patch, if that is so, we need to
  3714. // skip the module data in the file
  3715. //
  3716. if( module == NULL )
  3717. {
  3718. // for testing purposes, this module better be found
  3719. // DEBUG_CRASH(( "Object::xfer - Module '%s' was indicated in file, but not found on object '%s'(%d)\n",
  3720. // moduleIdentifier.str(), getTemplate()->getName().str(), getID() ));
  3721. // skip this data in the file
  3722. xfer->skip( dataSize );
  3723. } // end if
  3724. else
  3725. {
  3726. // xfer the data into this module
  3727. xfer->xferSnapshot( module );
  3728. } // end else
  3729. // end block
  3730. xfer->endBlock();
  3731. } // end for, i module count recorded in file
  3732. } // end else, load
  3733. if ( version >= 3 )
  3734. {
  3735. xfer->xferObjectID( &m_soleHealingBenefactorID );
  3736. xfer->xferUnsignedInt( &m_soleHealingBenefactorExpirationFrame );
  3737. }
  3738. else if ( xfer->getXferMode() == XFER_LOAD )
  3739. {
  3740. m_soleHealingBenefactorID = INVALID_ID;
  3741. m_soleHealingBenefactorExpirationFrame = 0;
  3742. }
  3743. // Doesn't need to be saved. These are created as needed. jba.
  3744. //AIGroup* m_group; ///< if non-NULL, we are part of this group of agents
  3745. // don't need to save m_partitionData.
  3746. DEBUG_ASSERTCRASH(!(xfer->getXferMode() == XFER_LOAD && m_partitionData == NULL), ("should not be in partitionmgr yet"));
  3747. // don't need to be saved or loaded; are inited & cached for runtime only by our ctor (srj)
  3748. //m_repulsorHelper;
  3749. //m_smcHelper;
  3750. //m_wsHelper;
  3751. //m_defectionHelper;
  3752. //m_firingTracker;
  3753. //m_contain;
  3754. //m_body;
  3755. //m_ai;
  3756. //m_physics;
  3757. #if defined(_DEBUG) || defined(_INTERNAL)
  3758. //m_hasDiedAlready;
  3759. #endif
  3760. if (version >= 4)
  3761. {
  3762. // xfer the weaponSetFlags FIRST, since we need 'em to restore the weaponSet properly. (srj)
  3763. m_curWeaponSetFlags.xfer( xfer );
  3764. xfer->xferUnsignedInt(&m_weaponBonusCondition);
  3765. xfer->xferUser(&m_lastWeaponCondition, sizeof(m_lastWeaponCondition));
  3766. // do the weaponSet itself after all the weapon-related stuff, just in case
  3767. xfer->xferSnapshot(&m_weaponSet);
  3768. m_specialPowerBits.xfer( xfer );
  3769. xfer->xferAsciiString(&m_commandSetStringOverride);
  3770. xfer->xferBool(&m_modulesReady);
  3771. }
  3772. if (version >= 5)
  3773. {
  3774. xfer->xferBool(&m_isReceivingDifficultyBonus);
  3775. }
  3776. else
  3777. m_isReceivingDifficultyBonus = FALSE;
  3778. } // end xfer
  3779. //-------------------------------------------------------------------------------------------------
  3780. /** Object load game post process phase */
  3781. //-------------------------------------------------------------------------------------------------
  3782. void Object::loadPostProcess()
  3783. {
  3784. if( m_xferContainedByID != INVALID_ID )
  3785. m_containedBy = TheGameLogic->findObjectByID(m_xferContainedByID);
  3786. else
  3787. m_containedBy = NULL;
  3788. } // end loadPostProcess
  3789. //-------------------------------------------------------------------------------------------------
  3790. /** Does this object have this upgrade */
  3791. //-------------------------------------------------------------------------------------------------
  3792. Bool Object::hasUpgrade( const UpgradeTemplate *upgradeT ) const
  3793. {
  3794. if( m_objectUpgradesCompleted.testForAll( upgradeT->getUpgradeMask() ) )
  3795. {
  3796. return TRUE;
  3797. }
  3798. return FALSE;
  3799. } // end hasUpgrade
  3800. //-------------------------------------------------------------------------------------------------
  3801. /** Is this object capable of having this upgrade */
  3802. //-------------------------------------------------------------------------------------------------
  3803. Bool Object::affectedByUpgrade( const UpgradeTemplate *upgradeT ) const
  3804. {
  3805. UpgradeMaskType objectMask = getObjectCompletedUpgradeMask();
  3806. UpgradeMaskType playerMask = getControllingPlayer()->getCompletedUpgradeMask();
  3807. UpgradeMaskType maskToCheck = playerMask;
  3808. maskToCheck.set( objectMask );
  3809. maskToCheck.set( upgradeT->getUpgradeMask() );
  3810. // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
  3811. // We combine all the masks in case someone has a Object AND Player combination
  3812. for (BehaviorModule** module = m_behaviors; *module; ++module)
  3813. {
  3814. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  3815. if (!upgrade)
  3816. continue;
  3817. if( upgrade->wouldUpgrade( maskToCheck ) )
  3818. {
  3819. // if any of my many upgrade modules would execute in response to this flag, say yes.
  3820. return TRUE;
  3821. }
  3822. }
  3823. return FALSE;
  3824. } // end affectedByUpgrade
  3825. //-------------------------------------------------------------------------------------------------
  3826. /** Give this upgrade to this object */
  3827. //-------------------------------------------------------------------------------------------------
  3828. void Object::giveUpgrade( const UpgradeTemplate *upgradeT )
  3829. {
  3830. if (upgradeT)
  3831. {
  3832. m_objectUpgradesCompleted.set( upgradeT->getUpgradeMask() );
  3833. //
  3834. // iterate through all the upgrade modules of this object and call the method to
  3835. // grant a new upgrade
  3836. //
  3837. updateUpgradeModules();
  3838. }
  3839. } // end giveUpgrade
  3840. //-------------------------------------------------------------------------------------------------
  3841. /** Remove this upgrade from this object */
  3842. //-------------------------------------------------------------------------------------------------
  3843. void Object::removeUpgrade( const UpgradeTemplate *upgradeT )
  3844. {
  3845. m_objectUpgradesCompleted.clear( upgradeT->getUpgradeMask() );
  3846. for (BehaviorModule** module = m_behaviors; *module; ++module)
  3847. {
  3848. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  3849. if (!upgrade)
  3850. continue;
  3851. // Whoa, please note that while the function is called Object::RemoveUpgrade, it is not removing anything
  3852. // in the sense of undoing the effects. It is just resetting the upgrade so it may be run again.
  3853. upgrade->resetUpgrade( upgradeT->getUpgradeMask() );
  3854. }
  3855. }
  3856. //-------------------------------------------------------------------------------------------------
  3857. /** Central point for onCapture logic */
  3858. //-------------------------------------------------------------------------------------------------
  3859. void Object::onCapture( Player *oldOwner, Player *newOwner )
  3860. {
  3861. // Everybody dhills when they captured so they don't keep doing something the new player might not want him to be doing
  3862. if( getAIUpdateInterface() && (oldOwner != newOwner) )
  3863. getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
  3864. // this gets the new owner some points
  3865. newOwner->getScoreKeeper()->addObjectCaptured(this);
  3866. // rip through the behavior modules and call the onCapture for any modules that care
  3867. for( BehaviorModule **module = m_behaviors; *module; ++module )
  3868. (*module)->onCapture( oldOwner, newOwner );
  3869. //
  3870. // We have to undo our look for the old team and redo it for the new.
  3871. // onCapture is used now, so it better be called after ownership changes and not before.
  3872. //
  3873. handlePartitionCellMaintenance();
  3874. // Design needs the player to be able to sell buildings he steals from the AI's build list, and this is the
  3875. // easiest fix. The only snafu would be a key building build listed by the AI that the player can capture
  3876. // and the AI tries to capture back but needs to not sell. In that case, a Cinematic Unsellable version
  3877. // of the building needs to be made. This fix has been okayed as the most non-lethal in November.
  3878. clearScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE);
  3879. // mark the command bar to redraw
  3880. TheControlBar->markUIDirty();
  3881. if (oldOwner!=newOwner && newOwner->isSkirmishAIPlayer()) {
  3882. // The skirmish ai doesn't know what to do with captured faction buildings except sell them.
  3883. if (isFactionStructure()) {
  3884. TheBuildAssistant->sellObject( this );
  3885. }
  3886. }
  3887. } // end onCapture
  3888. //-------------------------------------------------------------------------------------------------
  3889. /// Object level events that need to happen upon game death
  3890. void Object::onDie( DamageInfo *damageInfo )
  3891. {
  3892. checkAndDetonateBoobyTrap(NULL);// Already dying, so no need to handle death case of explosion
  3893. #if defined(_DEBUG) || defined(_INTERNAL)
  3894. DEBUG_ASSERTCRASH(m_hasDiedAlready == false, ("Object::onDie has been called multiple times. This is invalid. jkmcd"));
  3895. m_hasDiedAlready = true;
  3896. #endif
  3897. Bool selfInflicted = (damageInfo->in.m_sourceID == getID());
  3898. // FIRST, call our die modules.
  3899. for (BehaviorModule** d = m_behaviors; *d; ++d)
  3900. {
  3901. DieModuleInterface* die = (*d)->getDie();
  3902. if (die)
  3903. die->onDie(damageInfo);
  3904. }
  3905. // When objects die we remove from the radar as they're really not interesting anymore
  3906. if( m_radarData )
  3907. TheRadar->removeObject( this );
  3908. // Just in case I have been sporting one of thise fancy Terrain Decals,
  3909. //I naturally lose it now, because I'm dead.
  3910. Drawable *draw = getDrawable();
  3911. if (draw) draw->setTerrainDecalFadeTarget(0.0f, -0.03f);//fade...
  3912. //if (draw) draw->setTerrainDecal(TERRAIN_DECAL_NONE);//pop!
  3913. // objects that were spawned from something, need to tell their spawner that they have died
  3914. Object* spawner = TheGameLogic->findObjectByID( getProducerID() );
  3915. if( spawner )
  3916. {
  3917. // get the spawn behavior interface of the spawner
  3918. SpawnBehaviorInterface *spawnerBehavior = spawner->getSpawnBehaviorInterface();
  3919. if( spawnerBehavior )
  3920. spawnerBehavior->onSpawnDeath( getID(), damageInfo );
  3921. }
  3922. handlePartitionCellMaintenance();
  3923. if(m_team)
  3924. m_team->notifyTeamOfObjectDeath();
  3925. if (isLocallyControlled() && !selfInflicted) // wasLocallyControlled? :-)
  3926. {
  3927. if (isKindOf(KINDOF_STRUCTURE) && isKindOf(KINDOF_MP_COUNT_FOR_VICTORY))
  3928. {
  3929. TheEva->setShouldPlay(EVA_BuldingLost);
  3930. }
  3931. else if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE))
  3932. {
  3933. TheEva->setShouldPlay(EVA_UnitLost);
  3934. //Create a fake radar event so the user can use the spacebar to quickly jump to this!
  3935. TheRadar->tryEvent( RADAR_EVENT_FAKE, getPosition() );
  3936. }
  3937. }
  3938. // This call won't do anything if we aren't actually in the list.
  3939. //Kris: Added NULL check to prevent crash with combat bikes & their riders getting deleted on exit.
  3940. if( getControllingPlayer() )
  3941. {
  3942. TheInGameUI->removeIdleWorker( this, getControllingPlayer()->getPlayerIndex() );
  3943. }
  3944. //When a GLA hole is in the process of rebuilding, and that rebuild is lost, we need to
  3945. //tell anyone attacking it to transfer the attack to the hole that still exists.
  3946. if( testStatus( OBJECT_STATUS_RECONSTRUCTING ) )
  3947. {
  3948. Object *hole = TheGameLogic->findObjectByID( getProducerID() );
  3949. if( hole )
  3950. {
  3951. // set the information in the hole about what to build
  3952. RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( hole );
  3953. // sanity
  3954. DEBUG_ASSERTCRASH( rhbi, ("Object::onDie() - No Rebuild Hole Behavior interface on hole\n") );
  3955. // start the rebuild process
  3956. if( rhbi )
  3957. {
  3958. rhbi->startRebuildProcess( getTemplate(), getID() );
  3959. }
  3960. //Transfer any attackers from the destroyed building to the hole.
  3961. for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  3962. {
  3963. AIUpdateInterface* ai = obj->getAI();
  3964. if (!ai)
  3965. continue;
  3966. ai->transferAttack( getID(), hole->getID() );
  3967. }
  3968. }
  3969. }
  3970. }
  3971. //-------------------------------------------------------------------------------------------------
  3972. void Object::setWeaponBonusCondition(WeaponBonusConditionType wst)
  3973. {
  3974. WeaponBonusConditionFlags oldCondition = m_weaponBonusCondition;
  3975. m_weaponBonusCondition |= (1 << wst);
  3976. if( oldCondition != m_weaponBonusCondition )
  3977. {
  3978. // Our weapon bonus just changed, so we need to immediately update our weapons
  3979. m_weaponSet.weaponSetOnWeaponBonusChange(this);
  3980. }
  3981. }
  3982. //-------------------------------------------------------------------------------------------------
  3983. void Object::clearWeaponBonusCondition(WeaponBonusConditionType wst)
  3984. {
  3985. WeaponBonusConditionFlags oldCondition = m_weaponBonusCondition;
  3986. m_weaponBonusCondition &= ~(1 << wst);
  3987. if( oldCondition != m_weaponBonusCondition )
  3988. {
  3989. // Our weapon bonus just changed, so we need to immediately update our weapons
  3990. m_weaponSet.weaponSetOnWeaponBonusChange(this);
  3991. }
  3992. }
  3993. //-------------------------------------------------------------------------------------------------
  3994. /**
  3995. A weapon cannot be in charge of maintaining condition flags as it is all event driven.
  3996. I will maintain my ModelCondition myself if it should change. Firing is set by firing logic,
  3997. so I don't include it here. It is only the states that expire on timers that noone watches
  3998. that I am concerned with.
  3999. */
  4000. //-------------------------------------------------------------------------------------------------
  4001. void Object::adjustModelConditionForWeaponStatus()
  4002. {
  4003. UnsignedInt now = TheGameLogic->getFrame();
  4004. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  4005. {
  4006. const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  4007. if (!w)
  4008. {
  4009. m_lastWeaponCondition[i] = WSF_NONE;
  4010. continue;
  4011. }
  4012. WeaponSetConditionType conditionToSet = WSF_INVALID;
  4013. if (i != m_weaponSet.getCurWeaponSlot())
  4014. {
  4015. // if this isn't the current weapon, then we never set ANYTHING for it.
  4016. conditionToSet = WSF_NONE;
  4017. }
  4018. else if (w->getLastShotFrame() == now)
  4019. {
  4020. // yep, this overrides any weapon-status condition!
  4021. conditionToSet = WSF_FIRING;
  4022. }
  4023. else if (!testStatus( OBJECT_STATUS_IS_ATTACKING ))
  4024. {
  4025. // srj sez: not 100% sure about this one, but the problem is: say we were attacking,
  4026. // then issue a move command. if we didn't do this here, we might still have a 'firing'
  4027. // pose, because his weapon might be in 'reloading' mode. since we're not attacking, however,
  4028. // we really don't care, so we just force the issue here. (This might still need tweaking for the pursue state.)
  4029. conditionToSet = WSF_NONE;
  4030. }
  4031. else
  4032. {
  4033. WeaponStatus newStatus = w->getStatus();
  4034. const static WeaponSetConditionType s_wsfLookup[WEAPON_STATUS_COUNT] =
  4035. {
  4036. WSF_NONE, // READY_TO_FIRE,
  4037. WSF_NONE, // OUT_OF_AMMO,
  4038. WSF_BETWEEN, // BETWEEN_FIRING_SHOTS,
  4039. WSF_RELOADING, // RELOADING_CLIP,
  4040. WSF_PREATTACK // PRE_ATTACK,
  4041. };
  4042. conditionToSet = s_wsfLookup[newStatus];
  4043. // special case this: say we are firing in bursts: pow-pow-pow-pause, etc.
  4044. // then we might have a frame where we have reloaded and are ready-to-fire,
  4045. // but haven't fired yet this frame. in that case, use 'between' so we still have
  4046. // a firing pose, 'cuz if we use 'none' we will 'pop' back to idle for a frame. (srj)
  4047. // additional note: only do if aiming or firing, since we could also be in this state if
  4048. // we are approaching or pursuing a target! (srj)
  4049. if (newStatus == READY_TO_FIRE && conditionToSet == WSF_NONE && testStatus( OBJECT_STATUS_IS_ATTACKING ) &&
  4050. (testStatus( OBJECT_STATUS_IS_AIMING_WEAPON ) || testStatus( OBJECT_STATUS_IS_FIRING_WEAPON )))
  4051. {
  4052. conditionToSet = WSF_BETWEEN;
  4053. }
  4054. }
  4055. if (m_drawable)
  4056. {
  4057. m_drawable->updateDrawableClipStatus( w->getRemainingAmmo(), w->getClipSize(), w->getWeaponSlot() );
  4058. if (conditionToSet != WSF_INVALID && conditionToSet != m_lastWeaponCondition[i])
  4059. {
  4060. m_lastWeaponCondition[i] = conditionToSet;
  4061. ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot((WeaponSlotType)i, conditionToSet);
  4062. m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[i], c);
  4063. if (conditionToSet == WSF_PREATTACK)
  4064. {
  4065. // in the preattack state, adjust the speed of the preattack anim to match the actual time it will take
  4066. UnsignedInt preAttackDone = w->getPreAttackFinishedFrame();
  4067. if (preAttackDone > now)
  4068. m_drawable->setAnimationLoopDuration(preAttackDone - now);
  4069. }
  4070. }
  4071. }
  4072. }
  4073. }
  4074. //-------------------------------------------------------------------------------------------------
  4075. /// We have moved a 'significant' amount, so do maintenence that can be considered 'cell-based'
  4076. void Object::onPartitionCellChange()
  4077. {
  4078. handlePartitionCellMaintenance();
  4079. }
  4080. //-------------------------------------------------------------------------------------------------
  4081. void Object::handlePartitionCellMaintenance()
  4082. {
  4083. handleShroud();
  4084. handleValueMap();
  4085. handleThreatMap();
  4086. }
  4087. //-------------------------------------------------------------------------------------------------
  4088. void Object::handleShroud()
  4089. {
  4090. // Undo last looking
  4091. unlook();
  4092. // and shrouding
  4093. unshroud();
  4094. // redo shrouding
  4095. shroud();
  4096. // Redo looking
  4097. look();
  4098. }
  4099. //-------------------------------------------------------------------------------------------------
  4100. void Object::handleValueMap()
  4101. {
  4102. removeValue();
  4103. addValue();
  4104. }
  4105. //-------------------------------------------------------------------------------------------------
  4106. void Object::handleThreatMap()
  4107. {
  4108. removeThreat();
  4109. addThreat();
  4110. }
  4111. //-------------------------------------------------------------------------------------------------
  4112. void Object::addValue()
  4113. {
  4114. if( !m_partitionLastValue->isInvalid() )
  4115. {
  4116. DEBUG_CRASH( ("An Object is adding value, but hasn't removed his previous value.") );
  4117. return;
  4118. }
  4119. if (!getControllingPlayer())
  4120. return;
  4121. if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) || isEffectivelyDead() || getShroudClearingRange() <= 0.0f )
  4122. return;
  4123. m_partitionLastValue->m_where = *getPosition();
  4124. m_partitionLastValue->m_data = getTemplate()->friend_getBuildCost();
  4125. m_partitionLastValue->m_forWhom = getControllingPlayer()->getPlayerMask();
  4126. m_partitionLastValue->m_howFar = getVisionRange(); // we are valuable all the way to where we can target.
  4127. ThePartitionManager->doValueAffect(m_partitionLastValue->m_where.x,
  4128. m_partitionLastValue->m_where.y,
  4129. m_partitionLastValue->m_howFar,
  4130. m_partitionLastValue->m_data,
  4131. m_partitionLastValue->m_forWhom
  4132. );
  4133. }
  4134. //-------------------------------------------------------------------------------------------------
  4135. void Object::removeValue()
  4136. {
  4137. if( m_partitionLastValue->isInvalid() )
  4138. {
  4139. // removing before adding is valid, cause we always remove before adding. (So the first remove
  4140. // will occur before the first add)
  4141. return;
  4142. }
  4143. ThePartitionManager->undoValueAffect(m_partitionLastValue->m_where.x,
  4144. m_partitionLastValue->m_where.y,
  4145. m_partitionLastValue->m_howFar,
  4146. m_partitionLastValue->m_data,
  4147. m_partitionLastValue->m_forWhom
  4148. );
  4149. m_partitionLastValue->reset();
  4150. }
  4151. //-------------------------------------------------------------------------------------------------
  4152. void Object::addThreat()
  4153. {
  4154. if( !m_partitionLastThreat->isInvalid() )
  4155. {
  4156. DEBUG_CRASH( ("An Object is adding threat, but hasn't removed his previous threat. (He hasn't finished the threat?)") );
  4157. return;
  4158. }
  4159. if (!getControllingPlayer())
  4160. return;
  4161. if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) || isEffectivelyDead() || getShroudClearingRange() <= 0.0f )
  4162. return;
  4163. m_partitionLastThreat->m_where = *getPosition();
  4164. m_partitionLastThreat->m_data = getTemplate()->getThreatValue();
  4165. m_partitionLastThreat->m_forWhom = getControllingPlayer()->getPlayerMask();
  4166. m_partitionLastThreat->m_howFar = getVisionRange(); // we are threatening all the way to where we can target.
  4167. ThePartitionManager->doThreatAffect(m_partitionLastThreat->m_where.x,
  4168. m_partitionLastThreat->m_where.y,
  4169. m_partitionLastThreat->m_howFar,
  4170. m_partitionLastThreat->m_data,
  4171. m_partitionLastThreat->m_forWhom
  4172. );
  4173. }
  4174. //-------------------------------------------------------------------------------------------------
  4175. void Object::removeThreat()
  4176. {
  4177. if( m_partitionLastThreat->isInvalid() )
  4178. {
  4179. // removing before adding is valid, cause we always remove before adding. (So the first remove
  4180. // will occur before the first add)
  4181. return;
  4182. }
  4183. ThePartitionManager->undoThreatAffect(m_partitionLastThreat->m_where.x,
  4184. m_partitionLastThreat->m_where.y,
  4185. m_partitionLastThreat->m_howFar,
  4186. m_partitionLastThreat->m_data,
  4187. m_partitionLastThreat->m_forWhom
  4188. );
  4189. m_partitionLastThreat->reset();
  4190. }
  4191. //-------------------------------------------------------------------------------------------------
  4192. void Object::look()
  4193. {
  4194. if( ! m_partitionLastLook->isInvalid() )
  4195. {
  4196. DEBUG_CRASH( ("An Object is looking, but hasn't unlooked the last one.") );
  4197. return;
  4198. }
  4199. Player* controller = getControllingPlayer();
  4200. if ( controller )
  4201. {
  4202. // I removed the check for objects under construction by request of designers since
  4203. // they want constructing objects to have a reduced sight range now. -MW
  4204. // dead or blind things don't reveal shroud
  4205. // Some things get Destroyed directly without hitting Death.
  4206. if( !isDestroyed() && !isEffectivelyDead() )
  4207. {
  4208. ContainModuleInterface * contain = (getContainedBy() ? getContainedBy()->getContain() : NULL);
  4209. if ( contain && !contain->isGarrisonable() )
  4210. return;// dont look, 'cause you are in a tunnel, now
  4211. // GS 10-20 Need to expand that exception to all transports or else you get a perma reveal where
  4212. // you entered the transport. Remember, this hackiness is caused by the fact that we never realized that
  4213. // garrisoned buildings weren't looking, we were just seeing the leftover last look of the guy inside.
  4214. // Otherwise we'd just have enclosingContainer control looking which is the 'correct' answer.
  4215. Real shroudClearingRange = getShroudClearingRange();
  4216. if( shroudClearingRange > 0.0f )
  4217. {
  4218. PlayerMaskType lookingMask = 0;
  4219. if ( isKindOf(KINDOF_REVEAL_TO_ALL) )
  4220. {
  4221. lookingMask = PLAYERMASK_ALL;
  4222. }
  4223. else
  4224. {
  4225. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  4226. {
  4227. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  4228. // Build mask of of allies who can see me.
  4229. // This is the Object-centric game level that cares
  4230. if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) == ALLIES )
  4231. {
  4232. lookingMask |= currentPlayer->getPlayerMask();
  4233. }
  4234. }
  4235. // Other players can also be looking through our eyes.
  4236. lookingMask |= m_visionSpiedMask;
  4237. }
  4238. Coord3D pos = *getPosition();
  4239. ThePartitionManager->doShroudReveal( pos.x, pos.y, shroudClearingRange, lookingMask );
  4240. m_partitionLastLook->m_where = pos;
  4241. m_partitionLastLook->m_forWhom = lookingMask;
  4242. m_partitionLastLook->m_howFar = getShroudClearingRange();
  4243. // DEBUG_LOG(( "A %s looks at %f, %f for %x at range %f\n",
  4244. // getTemplate()->getName().str(),
  4245. // pos.x,
  4246. // pos.y,
  4247. // lookingMask,
  4248. // getShroudClearingRange()
  4249. // ));
  4250. }
  4251. //Now reveal to everyone if we're special. Note this works differently than KINDOF_REVEAL_TO_ALL because
  4252. //the kindof uses the same range as allies, spies, and owners would see. This template based shroud
  4253. //reveal to all range can specify a different value so we can get a much smaller reveal distance.
  4254. // And don't reveal while under construction. When finished, a refresh occurs, so don't worry.
  4255. Real shroudRevealToAllRange = getTemplate()->getShroudRevealToAllRange();
  4256. if( shroudRevealToAllRange > 0.0f && !testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  4257. {
  4258. //Kris: August 20, 2003
  4259. //Seeing I added this concept, I'm changing it now to only reveal to all when the unit is visible. If it's stealthed,
  4260. //we won't reveal it anymore (stealth general scudstorm).
  4261. Bool stealthedAndNotDetected = testStatus( OBJECT_STATUS_STEALTHED ) && !testStatus( OBJECT_STATUS_DETECTED ) && !testStatus( OBJECT_STATUS_DISGUISED );
  4262. if( !stealthedAndNotDetected )
  4263. {
  4264. Coord3D pos = *getPosition();
  4265. PlayerMaskType thePlayersMask = ThePlayerList->getPlayersWithRelationship( getControllingPlayer()->getPlayerIndex(), ALLOW_ENEMIES | ALLOW_NEUTRAL );
  4266. ThePartitionManager->doShroudReveal( pos.x, pos.y, shroudRevealToAllRange, thePlayersMask );
  4267. m_partitionRevealAllLastLook->m_where = pos;
  4268. m_partitionRevealAllLastLook->m_forWhom = thePlayersMask;
  4269. m_partitionRevealAllLastLook->m_howFar = shroudRevealToAllRange;
  4270. }
  4271. }
  4272. }
  4273. }
  4274. }
  4275. //-------------------------------------------------------------------------------------------------
  4276. void Object::unlook()
  4277. {
  4278. if( m_partitionLastLook->isInvalid() )
  4279. {
  4280. // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
  4281. // This early return prevents an extra unlook if you never looked. Like you have 0 vision.
  4282. return;
  4283. }
  4284. ThePartitionManager->queueUndoShroudReveal(m_partitionLastLook->m_where.x,
  4285. m_partitionLastLook->m_where.y,
  4286. m_partitionLastLook->m_howFar,
  4287. m_partitionLastLook->m_forWhom
  4288. );
  4289. // DEBUG_LOG(( "A %s queues an unlook at %f, %f for %x at range %f\n",
  4290. // getTemplate()->getName().str(),
  4291. // m_partitionLastLook.m_where.x,
  4292. // m_partitionLastLook.m_where.y,
  4293. // m_partitionLastLook.m_forWhom,
  4294. // m_partitionLastLook.m_howFar
  4295. // ));
  4296. m_partitionLastLook->reset();
  4297. if( !m_partitionRevealAllLastLook->isInvalid() )
  4298. {
  4299. ThePartitionManager->queueUndoShroudReveal(m_partitionRevealAllLastLook->m_where.x,
  4300. m_partitionRevealAllLastLook->m_where.y,
  4301. m_partitionRevealAllLastLook->m_howFar,
  4302. m_partitionRevealAllLastLook->m_forWhom
  4303. );
  4304. m_partitionRevealAllLastLook->reset();
  4305. }
  4306. }
  4307. //-------------------------------------------------------------------------------------------------
  4308. void Object::shroud()
  4309. {
  4310. if( ! m_partitionLastShroud->isInvalid() )
  4311. {
  4312. DEBUG_CRASH( ("An Object is shrouding, but hasn't unshrouded the last one.") );
  4313. return;
  4314. }
  4315. Player* controller = getControllingPlayer();
  4316. if ( controller )
  4317. {
  4318. // things under construction don't shroud. (srj), nor do dead or blind things
  4319. if( !getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !isEffectivelyDead() && getShroudRange() > 0.0f )
  4320. {
  4321. PlayerMaskType shroudingMask = 0;
  4322. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  4323. {
  4324. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  4325. //Build mask of NON-allies. This is the Object-centric game level that cares
  4326. if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) != ALLIES )
  4327. {
  4328. shroudingMask |= currentPlayer->getPlayerMask();
  4329. }
  4330. }
  4331. Coord3D pos = *getPosition();
  4332. ThePartitionManager->doShroudCover(pos.x, pos.y,
  4333. getShroudRange(),
  4334. shroudingMask);
  4335. m_partitionLastShroud->m_where = pos;
  4336. m_partitionLastShroud->m_forWhom = shroudingMask;
  4337. m_partitionLastShroud->m_howFar = getShroudRange();
  4338. }
  4339. }
  4340. }
  4341. //-------------------------------------------------------------------------------------------------
  4342. void Object::unshroud()
  4343. {
  4344. if( m_partitionLastShroud->isInvalid() )
  4345. {
  4346. // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
  4347. // This early return prevents an extra unlook if you never looked. Like you have 0 shroud generation.
  4348. return;
  4349. }
  4350. ThePartitionManager->undoShroudCover(m_partitionLastShroud->m_where.x,
  4351. m_partitionLastShroud->m_where.y,
  4352. m_partitionLastShroud->m_howFar,
  4353. m_partitionLastShroud->m_forWhom);
  4354. m_partitionLastShroud->reset();
  4355. }
  4356. //-------------------------------------------------------------------------------------------------
  4357. Real Object::getVisionRange() const
  4358. {
  4359. #if defined(_DEBUG) || defined(_INTERNAL)
  4360. if (TheGlobalData->m_debugVisibility)
  4361. {
  4362. Vector3 pos(m_visionRange, 0, 0);
  4363. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  4364. {
  4365. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  4366. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  4367. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  4368. TheGlobalData->m_debugVisibilityTileDuration,
  4369. TheGlobalData->m_debugVisibilityTargettableColor);
  4370. }
  4371. }
  4372. #endif
  4373. return m_visionRange;
  4374. }
  4375. //-------------------------------------------------------------------------------------------------
  4376. void Object::setVisionRange( Real newVisionRange )
  4377. {
  4378. m_visionRange = newVisionRange;
  4379. }
  4380. //-------------------------------------------------------------------------------------------------
  4381. Real Object::getShroudClearingRange() const
  4382. {
  4383. Real shroudClearingRange=m_shroudClearingRange;
  4384. if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  4385. {
  4386. //structures under construction have limited vision range. For now, base it
  4387. //on the geometry extents so the structure can only see itself.
  4388. shroudClearingRange = getGeometryInfo().getBoundingCircleRadius();
  4389. }
  4390. #if defined(_DEBUG) || defined(_INTERNAL)
  4391. if (TheGlobalData->m_debugVisibility)
  4392. {
  4393. Vector3 pos(shroudClearingRange, 0, 0);
  4394. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  4395. {
  4396. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  4397. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  4398. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  4399. TheGlobalData->m_debugVisibilityTileDuration,
  4400. TheGlobalData->m_debugVisibilityDeshroudColor);
  4401. }
  4402. }
  4403. #endif
  4404. return shroudClearingRange;
  4405. }
  4406. //-------------------------------------------------------------------------------------------------
  4407. void Object::setShroudClearingRange( Real newShroudClearingRange )
  4408. {
  4409. if( newShroudClearingRange != m_shroudClearingRange )
  4410. {
  4411. // The partition cell refresh is a slow operation, so only do it if you really have to.
  4412. // Range change is a valid reason to relook.
  4413. m_shroudClearingRange = newShroudClearingRange;
  4414. /*
  4415. Complete and total monkey hack fix.
  4416. The problem: newObject doesn't get an initial pos, so all objects start at 0,0,0.
  4417. Most code paths instantly move 'em to a good pos, but in some cases, that is too late:
  4418. If we have search-and-destroy battle plan, we will apply it at that point, and clear out
  4419. a vision range based on our current (wrong) location. Doh!
  4420. So, this just sez: if you are at 0,0,0, don't call handlePartitionCellMaintenance()... since
  4421. you will either (1) be moved elsewhere immediately, thus forcing it to be called via
  4422. another route anyway, or (2) not be moved, which means you are a very naughty and worthless
  4423. object anyway and we should just ignore you.
  4424. Proper fix for next version is to require initial pos to be passed in to newObject so that
  4425. all objects can start at their proper initial position from the start of the ctor.
  4426. (srj)
  4427. */
  4428. const Coord3D* pos = getPosition();
  4429. if (pos->x || pos->y || pos->z)
  4430. {
  4431. handlePartitionCellMaintenance();
  4432. }
  4433. }
  4434. }
  4435. //-------------------------------------------------------------------------------------------------
  4436. Real Object::getShroudRange() const
  4437. {
  4438. #if defined(_DEBUG) || defined(_INTERNAL)
  4439. if (TheGlobalData->m_debugVisibility)
  4440. {
  4441. Vector3 pos(m_shroudRange, 0, 0);
  4442. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  4443. {
  4444. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  4445. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  4446. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  4447. TheGlobalData->m_debugVisibilityTileDuration,
  4448. TheGlobalData->m_debugVisibilityGapColor);
  4449. }
  4450. }
  4451. #endif
  4452. return m_shroudRange;
  4453. }
  4454. //-------------------------------------------------------------------------------------------------
  4455. void Object::setShroudRange( Real newShroudRange )
  4456. {
  4457. m_shroudRange = newShroudRange;
  4458. }
  4459. //-------------------------------------------------------------------------------------------------
  4460. void Object::setVisionSpied(Bool setting, Int byWhom)
  4461. {
  4462. Bool needRefresh = FALSE; // If this setting is an edge trigger on the reference count, I need to refresh
  4463. if( setting )
  4464. {
  4465. m_visionSpiedBy[ byWhom ] = m_visionSpiedBy[ byWhom ] + 1;
  4466. if( m_visionSpiedBy[ byWhom ] == 1 )
  4467. needRefresh = TRUE;
  4468. }
  4469. else
  4470. {
  4471. m_visionSpiedBy[ byWhom ] = m_visionSpiedBy[ byWhom ] - 1;
  4472. if( m_visionSpiedBy[ byWhom ] == 0 )
  4473. needRefresh = TRUE;
  4474. }
  4475. if( needRefresh )
  4476. {
  4477. PlayerMaskType workingMask = 0;
  4478. for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
  4479. {
  4480. if( m_visionSpiedBy[i] > 0 )
  4481. BitSet( workingMask, ( 1 << i ) );
  4482. else
  4483. BitClear( workingMask, ( 1 << i ) );
  4484. }
  4485. m_visionSpiedMask = workingMask;
  4486. handlePartitionCellMaintenance();
  4487. }
  4488. }
  4489. //-------------------------------------------------------------------------------------------------
  4490. void Object::doStatusDamage( ObjectStatusTypes status, Real duration )
  4491. {
  4492. if(m_statusDamageHelper)
  4493. m_statusDamageHelper->doStatusDamage(status, duration);
  4494. }
  4495. //-------------------------------------------------------------------------------------------------
  4496. void Object::doTempWeaponBonus( WeaponBonusConditionType status, UnsignedInt duration )
  4497. {
  4498. if(m_tempWeaponBonusHelper)
  4499. m_tempWeaponBonusHelper->doTempWeaponBonus(status, duration);
  4500. }
  4501. //-------------------------------------------------------------------------------------------------
  4502. void Object::notifySubdualDamage( Real amount )
  4503. {
  4504. if(m_subdualDamageHelper)
  4505. m_subdualDamageHelper->notifySubdualDamage( amount );
  4506. // If we are gaining subdual damage, we are slowly tinting
  4507. if( getDrawable() )
  4508. {
  4509. if( amount > 0 )
  4510. getDrawable()->setTintStatus(TINT_STATUS_GAINING_SUBDUAL_DAMAGE);
  4511. else
  4512. getDrawable()->clearTintStatus(TINT_STATUS_GAINING_SUBDUAL_DAMAGE);
  4513. }
  4514. }
  4515. //-------------------------------------------------------------------------------------------------
  4516. /** Given a special power template, find the module in the object that can implement it.
  4517. * There can be at most one */
  4518. //-------------------------------------------------------------------------------------------------
  4519. SpecialPowerModuleInterface *Object::getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const
  4520. {
  4521. // sanity
  4522. if( specialPowerTemplate == NULL )
  4523. return NULL;
  4524. // search the modules for the one with the matching template
  4525. for( BehaviorModule** m = m_behaviors; *m; ++m )
  4526. {
  4527. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  4528. if (!sp)
  4529. continue;
  4530. if( sp->isModuleForPower( specialPowerTemplate ) )
  4531. return sp;
  4532. }
  4533. return NULL;
  4534. }
  4535. //-------------------------------------------------------------------------------------------------
  4536. /** Execute special power */
  4537. //-------------------------------------------------------------------------------------------------
  4538. void Object::doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced )
  4539. {
  4540. if (isDisabled())
  4541. return;
  4542. // sanity
  4543. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  4544. return;
  4545. // get the module and execute
  4546. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  4547. if( mod )
  4548. mod->doSpecialPower( commandOptions );
  4549. }
  4550. //-------------------------------------------------------------------------------------------------
  4551. /** Execute special power */
  4552. //-------------------------------------------------------------------------------------------------
  4553. void Object::doSpecialPowerAtObject( const SpecialPowerTemplate *specialPowerTemplate, Object *obj, UnsignedInt commandOptions, Bool forced )
  4554. {
  4555. if (isDisabled())
  4556. return;
  4557. // sanity
  4558. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  4559. return;
  4560. // get the module and execute
  4561. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  4562. if( mod )
  4563. mod->doSpecialPowerAtObject( obj, commandOptions );
  4564. }
  4565. //-------------------------------------------------------------------------------------------------
  4566. /** Execute special power */
  4567. //-------------------------------------------------------------------------------------------------
  4568. void Object::doSpecialPowerAtLocation( const SpecialPowerTemplate *specialPowerTemplate,
  4569. const Coord3D *loc, Real angle, UnsignedInt commandOptions, Bool forced )
  4570. {
  4571. if (isDisabled())
  4572. return;
  4573. // sanity
  4574. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  4575. return;
  4576. // get the module and execute
  4577. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  4578. if( mod )
  4579. mod->doSpecialPowerAtLocation( loc, angle, commandOptions );
  4580. }
  4581. //-------------------------------------------------------------------------------------------------
  4582. /** Execute special power */
  4583. //-------------------------------------------------------------------------------------------------
  4584. void Object::doSpecialPowerUsingWaypoints( const SpecialPowerTemplate *specialPowerTemplate, const Waypoint *way, UnsignedInt commandOptions, Bool forced )
  4585. {
  4586. if (isDisabled())
  4587. return;
  4588. // sanity
  4589. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  4590. return;
  4591. // get the module and execute
  4592. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  4593. if( mod )
  4594. mod->doSpecialPowerUsingWaypoints( way, commandOptions );
  4595. }
  4596. //-------------------------------------------------------------------------------------------------
  4597. /** Execute command button ability */
  4598. //-------------------------------------------------------------------------------------------------
  4599. void Object::doCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
  4600. {
  4601. if (isDisabled())
  4602. return;
  4603. AIUpdateInterface *ai = getAIUpdateInterface();
  4604. if( commandButton )
  4605. {
  4606. switch( commandButton->getCommandType() )
  4607. {
  4608. case GUI_COMMAND_SPECIAL_POWER:
  4609. if( commandButton->getSpecialPowerTemplate() )
  4610. {
  4611. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4612. doSpecialPower( commandButton->getSpecialPowerTemplate(), commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4613. return;
  4614. }
  4615. break;
  4616. case GUI_COMMAND_STOP:
  4617. if( ai )
  4618. {
  4619. ai->aiIdle( cmdSource );
  4620. return;
  4621. }
  4622. break;
  4623. case GUI_COMMAND_SWITCH_WEAPON:
  4624. {
  4625. WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
  4626. // GUI_COMMAND_SWITCH_WEAPON switches until un-switched, or switched to something else.
  4627. setWeaponLock( weaponSlot, LOCKED_PERMANENTLY );
  4628. return;
  4629. }
  4630. case GUI_COMMAND_FIRE_WEAPON:
  4631. if( ai )
  4632. {
  4633. if( !BitTest( commandButton->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET ) && !BitTest( commandButton->getOptions(), NEED_TARGET_POS ) )
  4634. {
  4635. setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
  4636. //LOCATION BASED FIRE WEAPON
  4637. ai->aiAttackPosition( NULL, commandButton->getMaxShotsToFire(), cmdSource );
  4638. }
  4639. else
  4640. {
  4641. DEBUG_CRASH( ("WARNING: Script doCommandButton for button %s cannot fire weapon with NO POSITION. Skipping.", commandButton->getName().str()) );
  4642. }
  4643. return;
  4644. }
  4645. break;
  4646. case GUI_COMMAND_OBJECT_UPGRADE:
  4647. case GUI_COMMAND_PLAYER_UPGRADE:
  4648. {
  4649. const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
  4650. DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in player upgrade command\n", "UNKNOWN") );
  4651. // sanity
  4652. if( upgradeT == NULL )
  4653. break;
  4654. if( upgradeT->getUpgradeType() == UPGRADE_TYPE_OBJECT )
  4655. {
  4656. if( hasUpgrade( upgradeT ) || !affectedByUpgrade( upgradeT ) )
  4657. break;
  4658. }
  4659. // producer must have a production update
  4660. ProductionUpdateInterface *pu = getProductionUpdateInterface();
  4661. if( pu == NULL )
  4662. break;
  4663. // queue the upgrade "research"
  4664. pu->queueUpgrade( upgradeT );
  4665. }
  4666. return;
  4667. case GUI_COMMAND_UNIT_BUILD:
  4668. case GUI_COMMAND_DOZER_CONSTRUCT: {
  4669. const ThingTemplate *tt = commandButton->getThingTemplate();
  4670. ProductionUpdateInterface *pu = this->getProductionUpdateInterface();
  4671. if (pu && tt) {
  4672. pu->queueCreateUnit( tt, pu->requestUniqueUnitID());
  4673. return;
  4674. }
  4675. break;
  4676. }
  4677. case GUI_COMMAND_HACK_INTERNET:{
  4678. if( ai )
  4679. {
  4680. ai->aiHackInternet( cmdSource );
  4681. return;
  4682. }
  4683. break;
  4684. }
  4685. case GUI_COMMAND_SELL:
  4686. TheBuildAssistant->sellObject( this );
  4687. return;
  4688. //Feel free to implement object based command buttons.
  4689. case GUI_COMMAND_COMBATDROP:
  4690. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4691. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4692. case GUI_COMMAND_CANCEL_UPGRADE:
  4693. case GUI_COMMAND_ATTACK_MOVE:
  4694. case GUI_COMMAND_GUARD:
  4695. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4696. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4697. case GUI_COMMAND_WAYPOINTS:
  4698. case GUI_COMMAND_EXIT_CONTAINER:
  4699. case GUI_COMMAND_EVACUATE:
  4700. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4701. case GUI_COMMAND_BEACON_DELETE:
  4702. case GUI_COMMAND_SET_RALLY_POINT:
  4703. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4704. #ifdef ALLOW_SURRENDER
  4705. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4706. #endif
  4707. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4708. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4709. #ifdef ALLOW_SURRENDER
  4710. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4711. #endif
  4712. default:
  4713. break;
  4714. }
  4715. DEBUG_CRASH( ("WARNING: Script doCommandButton for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
  4716. }
  4717. }
  4718. //-------------------------------------------------------------------------------------------------
  4719. /** Execute command button ability directed at an object target */
  4720. //-------------------------------------------------------------------------------------------------
  4721. void Object::doCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
  4722. {
  4723. if (isDisabled())
  4724. return;
  4725. AIUpdateInterface *ai = getAIUpdateInterface();
  4726. if( commandButton )
  4727. {
  4728. switch( commandButton->getCommandType() )
  4729. {
  4730. case GUI_COMMAND_COMBATDROP:
  4731. if( ai )
  4732. {
  4733. ai->aiCombatDrop( obj, *(obj->getPosition()), cmdSource );
  4734. }
  4735. return;
  4736. case GUI_COMMAND_SPECIAL_POWER:
  4737. {
  4738. if( commandButton->getSpecialPowerTemplate() )
  4739. {
  4740. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4741. doSpecialPowerAtObject( commandButton->getSpecialPowerTemplate(), obj, commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4742. }
  4743. return;
  4744. }
  4745. case GUI_COMMAND_STOP:
  4746. if( ai )
  4747. {
  4748. ai->aiIdle( cmdSource );
  4749. }
  4750. return;
  4751. case GUI_COMMAND_FIRE_WEAPON:
  4752. if( ai )
  4753. {
  4754. if( BitTest( commandButton->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET ) )
  4755. {
  4756. //OBJECT BASED FIRE WEAPON
  4757. if( !obj )
  4758. {
  4759. break;
  4760. }
  4761. if( !commandButton->isValidObjectTarget( this, obj ) )
  4762. {
  4763. break;
  4764. }
  4765. setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
  4766. if( BitTest( commandButton->getOptions(), ATTACK_OBJECTS_POSITION ) )
  4767. {
  4768. //Actually, you know what.... we want to attack the object's location instead.
  4769. ai->aiAttackPosition( obj->getPosition(), commandButton->getMaxShotsToFire(), cmdSource );
  4770. }
  4771. else
  4772. {
  4773. ai->aiAttackObject( obj, commandButton->getMaxShotsToFire(), cmdSource );
  4774. }
  4775. }
  4776. else
  4777. {
  4778. DEBUG_CRASH( ("WARNING: Script doCommandButtonAtObject for button %s cannot fire weapon at AN OBJECT. Skipping.", commandButton->getName().str()) );
  4779. }
  4780. return;
  4781. }
  4782. break;
  4783. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4784. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4785. case GUICOMMANDMODE_SABOTAGE_BUILDING:
  4786. if( ai )
  4787. {
  4788. ai->aiEnter( obj, cmdSource );
  4789. }
  4790. return;
  4791. //Feel free to implement object based command buttons.
  4792. case GUI_COMMAND_DOZER_CONSTRUCT:
  4793. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4794. case GUI_COMMAND_UNIT_BUILD:
  4795. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4796. case GUI_COMMAND_PLAYER_UPGRADE:
  4797. case GUI_COMMAND_OBJECT_UPGRADE:
  4798. case GUI_COMMAND_CANCEL_UPGRADE:
  4799. case GUI_COMMAND_ATTACK_MOVE:
  4800. case GUI_COMMAND_GUARD:
  4801. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4802. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4803. case GUI_COMMAND_WAYPOINTS:
  4804. case GUI_COMMAND_EXIT_CONTAINER:
  4805. case GUI_COMMAND_EVACUATE:
  4806. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4807. case GUI_COMMAND_BEACON_DELETE:
  4808. case GUI_COMMAND_SET_RALLY_POINT:
  4809. case GUI_COMMAND_SELL:
  4810. case GUI_COMMAND_HACK_INTERNET:
  4811. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4812. case GUI_COMMAND_SWITCH_WEAPON:
  4813. #ifdef ALLOW_SURRENDER
  4814. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4815. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4816. #endif
  4817. default:
  4818. break;
  4819. }
  4820. DEBUG_CRASH( ("WARNING: Script doCommandButtonAtObject for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
  4821. }
  4822. }
  4823. //-------------------------------------------------------------------------------------------------
  4824. /** Execute command button ability directed at a location */
  4825. //-------------------------------------------------------------------------------------------------
  4826. void Object::doCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
  4827. {
  4828. if (isDisabled())
  4829. return;
  4830. AIUpdateInterface *ai = getAIUpdateInterface();
  4831. if( commandButton )
  4832. {
  4833. switch( commandButton->getCommandType() )
  4834. {
  4835. case GUI_COMMAND_SPECIAL_POWER:
  4836. {
  4837. if( commandButton->getSpecialPowerTemplate() )
  4838. {
  4839. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4840. doSpecialPowerAtLocation( commandButton->getSpecialPowerTemplate(), pos, INVALID_ANGLE, commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4841. return;
  4842. }
  4843. break;
  4844. }
  4845. case GUI_COMMAND_ATTACK_MOVE:
  4846. if( ai )
  4847. {
  4848. ai->aiAttackMoveToPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
  4849. return;
  4850. }
  4851. break;
  4852. case GUI_COMMAND_STOP:
  4853. if( ai )
  4854. {
  4855. ai->aiIdle( cmdSource );
  4856. return;
  4857. }
  4858. break;
  4859. case GUI_COMMAND_DOZER_CONSTRUCT:
  4860. TheBuildAssistant->buildObjectNow( this, commandButton->getThingTemplate(), pos, 0.0f, getControllingPlayer() );
  4861. return;
  4862. case GUI_COMMAND_FIRE_WEAPON:
  4863. if( ai )
  4864. {
  4865. if( BitTest( commandButton->getOptions(), NEED_TARGET_POS ) )
  4866. {
  4867. //LOCATION BASED FIRE WEAPON
  4868. if( !pos )
  4869. {
  4870. break;
  4871. }
  4872. setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
  4873. ai->aiAttackPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
  4874. }
  4875. else
  4876. {
  4877. DEBUG_CRASH( ("WARNING: Script doCommandButtonAtPosition for button %s cannot fire weapon at A POSITION. Skipping.", commandButton->getName().str()) );
  4878. }
  4879. return;
  4880. }
  4881. break;
  4882. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4883. case GUI_COMMAND_UNIT_BUILD:
  4884. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4885. case GUI_COMMAND_PLAYER_UPGRADE:
  4886. case GUI_COMMAND_OBJECT_UPGRADE:
  4887. case GUI_COMMAND_CANCEL_UPGRADE:
  4888. case GUI_COMMAND_GUARD:
  4889. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4890. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4891. case GUI_COMMAND_WAYPOINTS:
  4892. case GUI_COMMAND_EXIT_CONTAINER:
  4893. case GUI_COMMAND_EVACUATE:
  4894. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4895. case GUI_COMMAND_BEACON_DELETE:
  4896. case GUI_COMMAND_SET_RALLY_POINT:
  4897. case GUI_COMMAND_SELL:
  4898. case GUI_COMMAND_HACK_INTERNET:
  4899. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4900. #ifdef ALLOW_SURRENDER
  4901. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4902. #endif
  4903. case GUI_COMMAND_COMBATDROP:
  4904. case GUI_COMMAND_SWITCH_WEAPON:
  4905. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4906. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4907. #ifdef ALLOW_SURRENDER
  4908. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4909. #endif
  4910. default:
  4911. break;
  4912. }
  4913. DEBUG_CRASH( ("WARNING: Script doCommandButtonAtPosition for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
  4914. }
  4915. }
  4916. //-------------------------------------------------------------------------------------------------
  4917. /** Execute command button ability directed at a location */
  4918. //-------------------------------------------------------------------------------------------------
  4919. void Object::doCommandButtonUsingWaypoints( const CommandButton *commandButton, const Waypoint *way, CommandSourceType cmdSource )
  4920. {
  4921. if (isDisabled())
  4922. return;
  4923. if( commandButton )
  4924. {
  4925. if( !BitTest( commandButton->getOptions(), CAN_USE_WAYPOINTS ) )
  4926. {
  4927. //Our button doesn't support waypoints.
  4928. DEBUG_CRASH( ("WARNING: Script doCommandButtonUsingWaypoints for button %s lacks CAN_USE_WAYPOINTS option. Doing nothing.", commandButton->getName().str()) );
  4929. return;
  4930. }
  4931. switch( commandButton->getCommandType() )
  4932. {
  4933. case GUI_COMMAND_SPECIAL_POWER:
  4934. {
  4935. if( commandButton->getSpecialPowerTemplate() )
  4936. {
  4937. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4938. doSpecialPowerUsingWaypoints( commandButton->getSpecialPowerTemplate(), way, commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4939. return;
  4940. }
  4941. break;
  4942. }
  4943. case GUI_COMMAND_ATTACK_MOVE:
  4944. case GUI_COMMAND_STOP:
  4945. case GUI_COMMAND_DOZER_CONSTRUCT:
  4946. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4947. case GUI_COMMAND_UNIT_BUILD:
  4948. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4949. case GUI_COMMAND_PLAYER_UPGRADE:
  4950. case GUI_COMMAND_OBJECT_UPGRADE:
  4951. case GUI_COMMAND_CANCEL_UPGRADE:
  4952. case GUI_COMMAND_GUARD:
  4953. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4954. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4955. case GUI_COMMAND_WAYPOINTS:
  4956. case GUI_COMMAND_EXIT_CONTAINER:
  4957. case GUI_COMMAND_EVACUATE:
  4958. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4959. case GUI_COMMAND_BEACON_DELETE:
  4960. case GUI_COMMAND_SET_RALLY_POINT:
  4961. case GUI_COMMAND_SELL:
  4962. case GUI_COMMAND_FIRE_WEAPON:
  4963. case GUI_COMMAND_HACK_INTERNET:
  4964. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4965. #ifdef ALLOW_SURRENDER
  4966. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4967. #endif
  4968. case GUI_COMMAND_COMBATDROP:
  4969. case GUI_COMMAND_SWITCH_WEAPON:
  4970. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4971. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4972. #ifdef ALLOW_SURRENDER
  4973. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4974. #endif
  4975. default:
  4976. break;
  4977. }
  4978. DEBUG_CRASH( ("WARNING: Script doCommandButtonUsingWaypoints for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
  4979. }
  4980. }
  4981. // ------------------------------------------------------------------------------------------------
  4982. // ------------------------------------------------------------------------------------------------
  4983. void Object::clearLeechRangeModeForAllWeapons()
  4984. {
  4985. m_weaponSet.clearLeechRangeModeForAllWeapons();
  4986. }
  4987. // ------------------------------------------------------------------------------------------------
  4988. /** Search our update modules for a production update interface and return it if one is found */
  4989. // ------------------------------------------------------------------------------------------------
  4990. ProductionUpdateInterface* Object::getProductionUpdateInterface( void )
  4991. {
  4992. ProductionUpdateInterface *pui;
  4993. // tell our update modules that we intend to do this special power.
  4994. for( BehaviorModule** u = m_behaviors; *u; ++u )
  4995. {
  4996. pui = (*u)->getProductionUpdateInterface();
  4997. if( pui )
  4998. return pui;
  4999. } // end for
  5000. return NULL;
  5001. } // end getProductionUpdateInterface
  5002. // ------------------------------------------------------------------------------------------------
  5003. // ------------------------------------------------------------------------------------------------
  5004. DockUpdateInterface *Object::getDockUpdateInterface( void )
  5005. {
  5006. DockUpdateInterface *dock = NULL;
  5007. for( BehaviorModule **u = m_behaviors; *u; ++u )
  5008. {
  5009. if( (dock = (*u)->getDockUpdateInterface()) != NULL )
  5010. return dock;
  5011. }
  5012. return NULL;
  5013. } // end getDockUpdateInterface
  5014. // ------------------------------------------------------------------------------------------------
  5015. // Search our special power modules for a specific one.
  5016. // ------------------------------------------------------------------------------------------------
  5017. SpecialPowerModuleInterface* Object::findSpecialPowerModuleInterface( SpecialPowerType type ) const
  5018. {
  5019. for (BehaviorModule** m = m_behaviors; *m; ++m)
  5020. {
  5021. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  5022. if (!sp)
  5023. continue;
  5024. const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
  5025. if (spTemplate && spTemplate->getSpecialPowerType() == type || type == SPECIAL_INVALID )
  5026. {
  5027. return sp;
  5028. }
  5029. }
  5030. return NULL;
  5031. }
  5032. // ------------------------------------------------------------------------------------------------
  5033. // Search our special power modules for the first occurrence of a shortcut special.
  5034. // ------------------------------------------------------------------------------------------------
  5035. SpecialPowerModuleInterface* Object::findAnyShortcutSpecialPowerModuleInterface() const
  5036. {
  5037. for( BehaviorModule** m = m_behaviors; *m; ++m )
  5038. {
  5039. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  5040. if (!sp)
  5041. continue;
  5042. const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
  5043. if( spTemplate && spTemplate->isShortcutPower() )
  5044. {
  5045. return sp;
  5046. }
  5047. }
  5048. return NULL;
  5049. }
  5050. // ------------------------------------------------------------------------------------------------
  5051. /** Get spawn behavior interface from object */
  5052. // ------------------------------------------------------------------------------------------------
  5053. SpawnBehaviorInterface* Object::getSpawnBehaviorInterface() const
  5054. {
  5055. for (BehaviorModule** m = m_behaviors; *m; ++m)
  5056. {
  5057. SpawnBehaviorInterface *sbi = (*m)->getSpawnBehaviorInterface();
  5058. if( sbi )
  5059. {
  5060. return sbi;
  5061. }
  5062. }
  5063. return NULL;
  5064. } // end getSpawnBehaviorInterfaceFromObject
  5065. // ------------------------------------------------------------------------------------------------
  5066. ProjectileUpdateInterface* Object::getProjectileUpdateInterface() const
  5067. {
  5068. for (BehaviorModule** m = m_behaviors; *m; ++m)
  5069. {
  5070. ProjectileUpdateInterface *pui = (*m)->getProjectileUpdateInterface();
  5071. if( pui )
  5072. {
  5073. return pui;
  5074. }
  5075. }
  5076. return NULL;
  5077. }
  5078. // ------------------------------------------------------------------------------------------------
  5079. // Simply find the special power module that is currently allowing plotting of positions to target.
  5080. // ------------------------------------------------------------------------------------------------
  5081. SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestinationActive( SpecialPowerType type ) const
  5082. {
  5083. for( BehaviorModule** u = m_behaviors; *u; ++u )
  5084. {
  5085. SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
  5086. if( spInterface )
  5087. {
  5088. if( spInterface->doesSpecialPowerHaveOverridableDestinationActive() )
  5089. {
  5090. return spInterface;
  5091. }
  5092. }
  5093. } // end for
  5094. return NULL;
  5095. }
  5096. // ------------------------------------------------------------------------------------------------
  5097. // Simply find the special power module that is potentially allowed to plot positions to target.
  5098. // ------------------------------------------------------------------------------------------------
  5099. SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestination( SpecialPowerType type ) const
  5100. {
  5101. for( BehaviorModule** u = m_behaviors; *u; ++u )
  5102. {
  5103. SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
  5104. if( spInterface )
  5105. {
  5106. if( spInterface->doesSpecialPowerHaveOverridableDestination() )
  5107. {
  5108. return spInterface;
  5109. }
  5110. }
  5111. } // end for
  5112. return NULL;
  5113. }
  5114. // ------------------------------------------------------------------------------------------------
  5115. // Search our special ability updates for a specific one.
  5116. // ------------------------------------------------------------------------------------------------
  5117. SpecialAbilityUpdate* Object::findSpecialAbilityUpdate( SpecialPowerType type ) const
  5118. {
  5119. for( BehaviorModule** u = m_behaviors; *u; ++u )
  5120. {
  5121. SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
  5122. if( spInterface && spInterface->isSpecialAbility() )
  5123. {
  5124. SpecialAbilityUpdate *spUpdate = (SpecialAbilityUpdate*)spInterface;
  5125. if( spUpdate->getSpecialPowerType() == type )
  5126. {
  5127. return spUpdate;
  5128. }
  5129. }
  5130. } // end for
  5131. return NULL;
  5132. }
  5133. // ------------------------------------------------------------------------------------------------
  5134. SpecialPowerCompletionDie* Object::findSpecialPowerCompletionDie() const
  5135. {
  5136. static NameKeyType key_SpecialPowerCompletionDie = NAMEKEY("SpecialPowerCompletionDie");
  5137. return (SpecialPowerCompletionDie*)findModule(key_SpecialPowerCompletionDie);
  5138. }
  5139. // ------------------------------------------------------------------------------------------------
  5140. Int Object::getNumConsecutiveShotsFiredAtTarget( const Object *victim ) const
  5141. {
  5142. return m_firingTracker ? m_firingTracker->getNumConsecutiveShotsAtVictim( victim ) : 0;
  5143. }
  5144. // ------------------------------------------------------------------------------------------------
  5145. // ------------------------------------------------------------------------------------------------
  5146. Bool Object::getSingleLogicalBonePosition(const char* boneName, Coord3D* position, Matrix3D* transform) const
  5147. {
  5148. if (m_drawable && m_drawable->getPristineBonePositions( boneName, 0, position, transform, 1 ) == 1 )
  5149. {
  5150. m_drawable->convertBonePosToWorldPos( position, transform, position, transform );
  5151. return true;
  5152. }
  5153. else
  5154. {
  5155. if (position)
  5156. *position = *getPosition();
  5157. if (transform)
  5158. *transform = *getTransformMatrix();
  5159. return false;
  5160. }
  5161. }
  5162. // ------------------------------------------------------------------------------------------------
  5163. // ------------------------------------------------------------------------------------------------
  5164. Bool Object::getSingleLogicalBonePositionOnTurret( WhichTurretType whichTurret, const char* boneName, Coord3D* position, Matrix3D* transform ) const
  5165. {
  5166. Coord3D turretPosition;
  5167. Coord3D bonePosition;
  5168. if( getDrawable() == NULL || getAI() == NULL )
  5169. return FALSE;
  5170. // We need to find the TurretBone's pristine position.
  5171. getDrawable()->getProjectileLaunchOffset( PRIMARY_WEAPON, 1, NULL, whichTurret, &turretPosition, NULL );
  5172. // And the required bone's pristine position
  5173. if( getDrawable()->getPristineBonePositions(boneName, 0, &bonePosition, NULL, 1) != 1 )
  5174. return FALSE;
  5175. //Then we mojo the Logic position of the required bone like Missile firing does. Using the logic twist of the turret
  5176. Real turretRotation;
  5177. getAI()->getTurretRotAndPitch( whichTurret, &turretRotation, NULL );
  5178. Matrix3D boneOffset(TRUE);// This will be from the turret to the requested bone
  5179. // Vector3 bonePositionVector( bonePosition.x - turretPosition.x,
  5180. // bonePosition.y - turretPosition.y,
  5181. // bonePosition.z - turretPosition.z );
  5182. Vector3 bonePositionVector( bonePosition.x,
  5183. bonePosition.y,
  5184. bonePosition.z );
  5185. boneOffset.Translate(bonePositionVector);
  5186. Matrix3D turnAdjustment(TRUE);// this is the turret twist to be applied to the final answer
  5187. turnAdjustment.Translate( turretPosition.x, turretPosition.y, turretPosition.z );
  5188. turnAdjustment.In_Place_Pre_Rotate_Z(turretRotation);
  5189. turnAdjustment.Translate( -turretPosition.x, -turretPosition.y, -turretPosition.z );
  5190. Matrix3D boneLogicTransform;
  5191. boneLogicTransform.mul( turnAdjustment, boneOffset );
  5192. Matrix3D worldTransform;
  5193. convertBonePosToWorldPos(NULL, &boneLogicTransform, NULL, &worldTransform);
  5194. Vector3 tmp = worldTransform.Get_Translation();
  5195. Coord3D worldPos;
  5196. worldPos.x = tmp.X;
  5197. worldPos.y = tmp.Y;
  5198. worldPos.z = tmp.Z;
  5199. if( position )
  5200. *position = worldPos;
  5201. if( transform )
  5202. *transform = worldTransform;
  5203. return TRUE;
  5204. }
  5205. // ------------------------------------------------------------------------------------------------
  5206. // ------------------------------------------------------------------------------------------------
  5207. Int Object::getMultiLogicalBonePosition(const char* boneNamePrefix, Int maxBones,
  5208. Coord3D* positions, Matrix3D* transforms,
  5209. Bool convertToWorld ) const
  5210. {
  5211. Int count;
  5212. if (m_drawable && (count = m_drawable->getPristineBonePositions( boneNamePrefix, 1, positions, transforms, maxBones )) > 0 )
  5213. {
  5214. if( convertToWorld )
  5215. {
  5216. for (Int i = 0; i < count; ++i)
  5217. m_drawable->convertBonePosToWorldPos( positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL, positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL );
  5218. }
  5219. return count;
  5220. }
  5221. else
  5222. {
  5223. return 0;
  5224. }
  5225. }
  5226. //=============================================================================
  5227. const AsciiString& Object::getCommandSetString() const
  5228. {
  5229. if (m_commandSetStringOverride.isNotEmpty())
  5230. return m_commandSetStringOverride;
  5231. return getTemplate()->friend_getCommandSetString();
  5232. }
  5233. //=============================================================================
  5234. Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade )
  5235. {
  5236. // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid.
  5237. const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString());
  5238. for( Int buttonIndex = 0; buttonIndex < MAX_COMMANDS_PER_SET; buttonIndex++ )
  5239. {
  5240. const CommandButton *button = set->getCommandButton(buttonIndex);
  5241. if( button && button->getUpgradeTemplate() && (button->getUpgradeTemplate() == upgrade) )
  5242. return TRUE; // getUpgradeTemplate only returns something if it is actually an upgrade
  5243. }
  5244. return FALSE;// Cheatin' punk.
  5245. }
  5246. //=============================================================================
  5247. // Object::defect, and related methods =
  5248. //=============================================================================
  5249. void Object::defect( Team* newTeam, UnsignedInt detectionTime )
  5250. {
  5251. if ( isContained() ) //@todo (KRIS?) make contained units unselectable, until then... lorenzen
  5252. {
  5253. return;
  5254. }
  5255. Player *player = getControllingPlayer();
  5256. if ( !player )
  5257. return;
  5258. Team* myTeam = player->getDefaultTeam();
  5259. if ( myTeam == newTeam ) // can't defect from my own team, that would be silly
  5260. return;
  5261. // things that are under construction, or sold, cannot defect.
  5262. if (testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ||
  5263. testStatus(OBJECT_STATUS_SOLD))
  5264. {
  5265. return;
  5266. }
  5267. // Before switch ////////////////////////////////////////
  5268. //Design says:
  5269. ProductionUpdateInterface *production = getProductionUpdateInterface();
  5270. if ( production )
  5271. {
  5272. production->cancelAndRefundAllProduction();
  5273. }
  5274. // pop it up on the radar, so as to warn those who care
  5275. // do this first, since after setTeam() the infiltrator
  5276. // becomes the controllingplayer, not me
  5277. // But don't do this is if the new team is not a real team. "'Enemy' infiltration" wouldn't make
  5278. // sense, and we are probably just reverting a cave or something.
  5279. if( friend_getRadarData() && newTeam->getControllingPlayer()->isPlayableSide() && myTeam->getControllingPlayer()->isPlayableSide())
  5280. {
  5281. TheRadar->tryInfiltrationEvent( this );
  5282. }
  5283. friend_setUndetectedDefector( detectionTime > 0 );
  5284. if (m_defectionHelper)
  5285. m_defectionHelper->startDefectionTimer(detectionTime);
  5286. // Switch ////////////////////////////////////////
  5287. setTeam( newTeam );
  5288. // After switch ////////////////////////////////////////
  5289. AIUpdateInterface *ai = getAI();
  5290. handlePartitionCellMaintenance();// to clear the shoud for my new master
  5291. if ( ai )
  5292. {
  5293. ai->aiIdle( CMD_FROM_AI );
  5294. }
  5295. // Play our sound indicating we've been defected. (weird verbage, but true.)
  5296. AudioEventRTS voiceDefect = *getTemplate()->getVoiceDefect();
  5297. voiceDefect.setObjectID(getID());
  5298. TheAudio->addAudioEvent(&voiceDefect);
  5299. //make the new recruit the only selected thing, awaiting new command to move, attack, etc...
  5300. Drawable *dr = getDrawable();
  5301. if (dr)
  5302. {
  5303. dr->flashAsSelected(); //This is the first of several flashes which get cue'd by doDefectorUpdateStuff()
  5304. AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
  5305. defectorTimerSound.setObjectID( getID() );
  5306. TheAudio->addAudioEvent(&defectorTimerSound);
  5307. }
  5308. ContainModuleInterface *ct = getContain();
  5309. if( ct && ct->isKickOutOnCapture() )
  5310. {
  5311. // Caves really really don't want to do this.
  5312. ct->removeAllContained( TRUE );
  5313. }
  5314. // if it has parking places, defect anything parked there.
  5315. for (BehaviorModule** i = getBehaviorModules(); *i; ++i)
  5316. {
  5317. ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
  5318. if (pp)
  5319. {
  5320. pp->defectAllParkedUnits(newTeam, detectionTime);
  5321. break;
  5322. }
  5323. }
  5324. // defect any mines that are owned by this structure, right now.
  5325. // unfortunately, structures don't keep list of mines they own, so we must do
  5326. // this the hard way :-( [fortunately, this doens't happen very often, so this
  5327. // is probably an acceptable, if icky, solution.] (srj)
  5328. for (Object* mine = TheGameLogic->getFirstObject(); mine; mine = mine->getNextObject())
  5329. {
  5330. if (mine->isKindOf(KINDOF_MINE))
  5331. {
  5332. if (mine->getProducerID() == this->getID())
  5333. {
  5334. mine->setTeam(newTeam);
  5335. }
  5336. }
  5337. }
  5338. }
  5339. //=============================================================================
  5340. // Object::goInvulnerable
  5341. //=============================================================================
  5342. void Object::goInvulnerable( UnsignedInt time )
  5343. {
  5344. const Bool WITHOUT_DEFECTOR_FX = FALSE;
  5345. friend_setUndetectedDefector( time > 0 );
  5346. if (m_defectionHelper)
  5347. m_defectionHelper->startDefectionTimer(time, WITHOUT_DEFECTOR_FX);
  5348. }
  5349. // ------------------------------------------------------------------------------------------------
  5350. /** Return the radar priority for this object type */
  5351. // ------------------------------------------------------------------------------------------------
  5352. RadarPriorityType Object::getRadarPriority( void ) const
  5353. {
  5354. RadarPriorityType priority = RADAR_PRIORITY_INVALID;
  5355. // first, get the priority at the thing template level
  5356. priority = getTemplate()->getDefaultRadarPriority();
  5357. //
  5358. // there are some objects that we want to show up on the radar when they have
  5359. // certain properties ... here we will check for those properties unless the INI
  5360. // setting of "not on radar" has been manually entered which explicitly forbids an
  5361. // object from being on the radar ... by default objects get an "invalid" priority
  5362. // on the radar and this means that we are free to decide one here if we want
  5363. //
  5364. if( priority == RADAR_PRIORITY_INVALID )
  5365. {
  5366. // objects that are "garrisonable" show up on the radar
  5367. ContainModuleInterface *cmi = getContain();
  5368. if( cmi && cmi->isGarrisonable() )
  5369. priority = RADAR_PRIORITY_STRUCTURE;
  5370. // objects that are "capturable" show up on the radar
  5371. if( isKindOf( KINDOF_CAPTURABLE ) )
  5372. priority = RADAR_PRIORITY_STRUCTURE;
  5373. } // end if
  5374. // Carbombs will show up as units regardless of their default priority
  5375. if ( testStatus( OBJECT_STATUS_IS_CARBOMB ) )
  5376. priority = RADAR_PRIORITY_UNIT;
  5377. // return the priority we're going to use
  5378. return priority;
  5379. } // end getRadarPriority
  5380. // ------------------------------------------------------------------------------------------------
  5381. AIGroup *Object::getGroup(void)
  5382. {
  5383. return m_group;
  5384. }
  5385. //-------------------------------------------------------------------------------------------------
  5386. void Object::enterGroup( AIGroup *group )
  5387. {
  5388. // DEBUG_LOG(("***AIGROUP %x involved in enterGroup on %x\n", group, this));
  5389. // if we are in another group, remove ourselves from it first
  5390. leaveGroup();
  5391. m_group = group;
  5392. }
  5393. //-------------------------------------------------------------------------------------------------
  5394. void Object::leaveGroup( void )
  5395. {
  5396. // DEBUG_LOG(("***AIGROUP %x involved in leaveGroup on %x\n", m_group, this));
  5397. // if we are in a group, remove ourselves from it
  5398. if (m_group)
  5399. {
  5400. // to avoid recursion, set m_group to NULL before removing
  5401. AIGroup *group = m_group;
  5402. m_group = NULL;
  5403. group->remove( this );
  5404. }
  5405. }
  5406. //-------------------------------------------------------------------------------------------------
  5407. Real Object::getCarrierDeckHeight() const
  5408. {
  5409. Object *producer = TheGameLogic->findObjectByID( getProducerID() );
  5410. if( producer )
  5411. {
  5412. // Find a parking place behavior.
  5413. for( BehaviorModule** i = producer->getBehaviorModules(); *i; ++i )
  5414. {
  5415. ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
  5416. if( pp )
  5417. {
  5418. return pp->getLandingDeckHeightOffset();
  5419. }
  5420. }
  5421. }
  5422. return 0.0f;
  5423. }
  5424. //-------------------------------------------------------------------------------------------------
  5425. CountermeasuresBehaviorInterface* Object::getCountermeasuresBehaviorInterface()
  5426. {
  5427. for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
  5428. {
  5429. CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
  5430. if( cbi )
  5431. {
  5432. return cbi;
  5433. }
  5434. }
  5435. return NULL;
  5436. }
  5437. //-------------------------------------------------------------------------------------------------
  5438. const CountermeasuresBehaviorInterface* Object::getCountermeasuresBehaviorInterface() const
  5439. {
  5440. for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
  5441. {
  5442. const CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
  5443. if( cbi )
  5444. {
  5445. return cbi;
  5446. }
  5447. }
  5448. return NULL;
  5449. }
  5450. //-------------------------------------------------------------------------------------------------
  5451. Bool Object::hasCountermeasures() const
  5452. {
  5453. const CountermeasuresBehaviorInterface* cbi = getCountermeasuresBehaviorInterface();
  5454. if( cbi && cbi->isActive() )
  5455. {
  5456. return TRUE;
  5457. }
  5458. return FALSE;
  5459. }
  5460. //-------------------------------------------------------------------------------------------------
  5461. void Object::reportMissileForCountermeasures( Object *missile )
  5462. {
  5463. for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
  5464. {
  5465. CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
  5466. if( cbi )
  5467. {
  5468. cbi->reportMissileForCountermeasures( missile );
  5469. }
  5470. }
  5471. }
  5472. //-------------------------------------------------------------------------------------------------
  5473. ObjectID Object::calculateCountermeasureToDivertTo( const Object& victim )
  5474. {
  5475. AIUpdateInterface *ai = getAI();
  5476. if( ai )
  5477. {
  5478. for( BehaviorModule** i = victim.getBehaviorModules(); *i; ++i )
  5479. {
  5480. CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
  5481. if( cbi )
  5482. {
  5483. ObjectID decoyID = cbi->calculateCountermeasureToDivertTo( victim );
  5484. return decoyID;
  5485. }
  5486. }
  5487. }
  5488. return INVALID_ID;
  5489. }