| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE Object.cpp ////////////////////////////////////////////////////////////////////////////////
- // Simple base object
- // Author: Michael S. Booth, October 2000
- ///////////////////////////////////////////////////////////////////////////////////////////////////
-
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_WEAPONCONDITIONMAP
- #include "Common/BitFlagsIO.h"
- #include "Common/BuildAssistant.h"
- #include "Common/Dict.h"
- #include "Common/GameEngine.h"
- #include "Common/GameState.h"
- #include "Common/ModuleFactory.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/Radar.h"
- #include "Common/SpecialPower.h"
- #include "Common/Team.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Upgrade.h"
- #include "Common/WellKnownKeys.h"
- #include "Common/Xfer.h"
- #include "Common/XferCRC.h"
- #include "Common/PerfTimer.h"
- #include "GameClient/Anim2D.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/Eva.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/FiringTracker.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/AutoHealBehavior.h"
- #include "GameLogic/Module/BehaviorModule.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/CollideModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/CountermeasuresBehavior.h"
- #include "GameLogic/Module/CreateModule.h"
- #include "GameLogic/Module/DamageModule.h"
- #include "GameLogic/Module/DeletionUpdate.h"
- #include "GameLogic/Module/DestroyModule.h"
- #include "GameLogic/Module/DieModule.h"
- #include "GameLogic/Module/DozerAIUpdate.h"
- #include "GameLogic/Module/ObjectDefectionHelper.h"
- #include "GameLogic/Module/ObjectRepulsorHelper.h"
- #include "GameLogic/Module/ObjectSMCHelper.h"
- #include "GameLogic/Module/ObjectWeaponStatusHelper.h"
- #include "GameLogic/Module/OverchargeBehavior.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/PowerPlantUpgrade.h"
- #include "GameLogic/Module/ProductionUpdate.h"
- #include "GameLogic/Module/RadarUpgrade.h"
- #include "GameLogic/Module/RebuildHoleBehavior.h"
- #include "GameLogic/Module/SpawnBehavior.h"
- #include "GameLogic/Module/SpecialPowerModule.h"
- #include "GameLogic/Module/SpecialAbilityUpdate.h"
- #include "GameLogic/Module/StatusDamageHelper.h"
- #include "GameLogic/Module/StickyBombUpdate.h"
- #include "GameLogic/Module/SubdualDamageHelper.h"
- #include "GameLogic/Module/TempWeaponBonusHelper.h"
- #include "GameLogic/Module/ToppleUpdate.h"
- #include "GameLogic/Module/UpdateModule.h"
- #include "GameLogic/Module/UpgradeModule.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/PolygonTrigger.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Weapon.h"
- #include "GameLogic/WeaponSet.h"
- #include "GameLogic/Module/RadarUpdate.h"
- #include "GameLogic/Module/PowerPlantUpdate.h"
- #include "Common/CRCDebug.h"
- #include "Common/MiscAudio.h"
- #include "Common/AudioEventInfo.h"
- #include "Common/DynamicAudioEventInfo.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #ifdef DEBUG_OBJECT_ID_EXISTS
- ObjectID TheObjectIDToDebug = INVALID_ID;
- #endif
- // ------------------------------------------------------------------------------------------------
- static const ModelConditionFlags s_allWeaponFireFlags[WEAPONSLOT_COUNT] =
- {
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_A,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_A,
- MODELCONDITION_RELOADING_A,
- MODELCONDITION_PREATTACK_A,
- MODELCONDITION_USING_WEAPON_A
- ),
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_B,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_B,
- MODELCONDITION_RELOADING_B,
- MODELCONDITION_PREATTACK_B,
- MODELCONDITION_USING_WEAPON_B
- ),
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_C,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_C,
- MODELCONDITION_RELOADING_C,
- MODELCONDITION_PREATTACK_C,
- MODELCONDITION_USING_WEAPON_C
- )
- };
- //-------------------------------------------------------------------------------------------------
- extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- #ifdef DEBUG_LOGGING
- AsciiString DescribeObject(const Object *obj)
- {
- if (!obj)
- return "<No Object>";
- AsciiString ret;
- if (obj->getName().isNotEmpty())
- {
- ret.format("Object %d (%s) [%s, owned by player %d (%ls)]",
- obj->getID(), obj->getName().str(), obj->getTemplate()->getName().str(),
- obj->getControllingPlayer()->getPlayerIndex(),
- obj->getControllingPlayer()->getPlayerDisplayName().str());
- }
- else
- {
- ret.format("Object %d [%s, owned by player %d (%ls)]",
- obj->getID(), obj->getTemplate()->getName().str(),
- obj->getControllingPlayer()->getPlayerIndex(),
- obj->getControllingPlayer()->getPlayerDisplayName().str());
- }
- return ret;
- }
- #endif // DEBUG_LOGGING
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Object::Object( const ThingTemplate *tt, const ObjectStatusMaskType &objectStatusMask, Team *team ) :
- Thing(tt),
- m_indicatorColor(0),
- m_ai(NULL),
- m_physics(NULL),
- m_geometryInfo(tt->getTemplateGeometryInfo()),
- m_containedBy(NULL),
- m_xferContainedByID(INVALID_ID),
- m_containedByFrame(0),
- m_behaviors(NULL),
- m_body(NULL),
- m_contain(NULL),
- m_stealth(NULL),
- m_partitionData(NULL),
- m_radarData(NULL),
- m_drawable(NULL),
- m_next(NULL),
- m_prev(NULL),
- m_team(NULL),
- m_experienceTracker(NULL),
- m_firingTracker(NULL),
- m_repulsorHelper(NULL),
- m_statusDamageHelper(NULL),
- m_tempWeaponBonusHelper(NULL),
- m_subdualDamageHelper(NULL),
- m_smcHelper(NULL),
- m_wsHelper(NULL),
- m_defectionHelper(NULL),
- m_partitionLastLook(NULL),
- m_partitionRevealAllLastLook(NULL),
- m_partitionLastShroud(NULL),
- m_partitionLastThreat(NULL),
- m_partitionLastValue(NULL),
- m_smcUntil(NEVER),
- m_privateStatus(0),
- m_formationID(NO_FORMATION_ID),
- m_isReceivingDifficultyBonus(FALSE),
- m_singleUseCommandUsed(FALSE),
- m_scriptStatus(0),
- m_enteredOrExitedFrame(0),
- m_visionSpiedMask (PLAYERMASK_NONE),
- m_numTriggerAreasActive(0)
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- m_hasDiedAlready = false;
- #endif
- //Modules have not been created yet!
- m_modulesReady = false;
- // Force the thing template to use the most overridden version of itself - jkmcd
- // Note that after this, the object will be using m_template, which forces the usage of the
- // most overridden version of tt, so this is okay.
- tt = (const ThingTemplate *) tt->getFinalOverride();
-
- Int i, modIdx;
- AsciiString modName;
-
- //Added By Sadullah Nader
- //Initializations inserted
- m_formationOffset.x = m_formationOffset.y = 0.0f;
- m_iPos.zero();
- //
- for (i = 0; i < MAX_PLAYER_COUNT; ++i)
- {
- m_visionSpiedBy[i] = 0;
- }
- for( i = 0; i < DISABLED_COUNT; i++ )
- {
- m_disabledTillFrame[ i ] = NEVER;
- }
- m_weaponBonusCondition = 0;
- m_curWeaponSetFlags.clear();
- // sanity
- if( TheGameLogic == NULL || tt == NULL )
- {
- assert( 0 );
- return;
- } // end if
- // Object's set of these persist for the life of the object.
- m_partitionLastLook = newInstance(SightingInfo);
- m_partitionLastLook->reset();
- m_partitionRevealAllLastLook = newInstance(SightingInfo);
- m_partitionRevealAllLastLook->reset();
- m_partitionLastShroud = newInstance(SightingInfo);
- m_partitionLastShroud->reset();
- m_partitionLastThreat = newInstance(SightingInfo);
- m_partitionLastThreat->reset();
- m_partitionLastValue = newInstance(SightingInfo);
- m_partitionLastValue->reset();
- // must set ID to zero, since some of these set methods
- // will cause network messages to be sent
- // which use this ID.
- m_id = INVALID_ID;
- m_producerID = INVALID_ID;
- m_builderID = INVALID_ID;
- m_status = objectStatusMask;
- m_layer = LAYER_GROUND;
- m_group = NULL;
- m_constructionPercent = CONSTRUCTION_COMPLETE; // complete by default
- m_visionRange = tt->friend_calcVisionRange();
- m_shroudClearingRange = tt->friend_calcShroudClearingRange();
- if( m_shroudClearingRange == -1.0f )
- m_shroudClearingRange = m_visionRange;// Backwards compatible, and perfectly logical default to assign
- m_shroudRange = 0.0f;
-
- m_singleUseCommandUsed = false;
- // assign unique object id
- setID( TheGameLogic->allocateObjectID() );
- //
- // allocate any modules we need to, we should keep
- // this at or near the end of the drawable construction so that we have
- // all the valid data about the thing when we create the module
- //
- Int totalModules = tt->getBehaviorModuleInfo().getCount() + NUM_SLEEP_HELPERS; // need to take into account all the helper modules
- // allocate the publicModule arrays
- // pool[]ify
- m_behaviors = MSGNEW("ModulePtrs") BehaviorModule*[totalModules + 1];
- BehaviorModule** curB = m_behaviors;
- const ModuleInfo& mi = tt->getBehaviorModuleInfo();
- // set m_team to null before the first call, to avoid naughtiness...
- // If no team is specified in the constructor, then assign the object
- // to the neutral team.
- setTeam(team ? team : ThePlayerList->getNeutralPlayer()->getDefaultTeam());
- // the helpers are done first -- even before Behaviors! -- in case a module needs
- // to call something that uses them.
- static const NameKeyType smcHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SMCHelper" );
- static ObjectSMCHelperModuleData smcModuleData;
- smcModuleData.setModuleTagNameKey( smcHelperModuleDataTagNameKey );
- m_smcHelper = newInstance(ObjectSMCHelper)(this, &smcModuleData);
- *curB++ = m_smcHelper;
- //Inactive bodies can't take special damage since they can't take damage
- Bool isInactiveBody = FALSE;
- for( Int infoIndex = 0; infoIndex < mi.getCount(); ++infoIndex )
- {
- modName = mi.getNthName(infoIndex);
- if (modName.isEmpty())
- continue;
- if( modName.compare("InactiveBody") == 0 )
- {
- isInactiveBody = TRUE;
- break;
- }
- }
- if( !isInactiveBody )
- {
- static const NameKeyType statusHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_StatusDamageHelper" );
- static StatusDamageHelperModuleData statusModuleData;
- statusModuleData.setModuleTagNameKey( statusHelperModuleDataTagNameKey );
- m_statusDamageHelper = newInstance(StatusDamageHelper)(this, &statusModuleData);
- *curB++ = m_statusDamageHelper;
- static const NameKeyType subdualHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SubdualDamageHelper" );
- static SubdualDamageHelperModuleData subdualModuleData;
- subdualModuleData.setModuleTagNameKey( subdualHelperModuleDataTagNameKey );
- m_subdualDamageHelper = newInstance(SubdualDamageHelper)(this, &subdualModuleData);
- *curB++ = m_subdualDamageHelper;
- }
- if (TheAI != NULL
- && TheAI->getAiData()->m_enableRepulsors
- && isKindOf(KINDOF_CAN_BE_REPULSED))
- {
- // if we can ever be a temporary-repulsor, make a repulsor helper. (srj)
- static const NameKeyType repulsorHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_RepulsorHelper" );
- static ObjectRepulsorHelperModuleData repulsorModuleData;
- repulsorModuleData.setModuleTagNameKey( repulsorHelperModuleDataTagNameKey );
- m_repulsorHelper = newInstance(ObjectRepulsorHelper)(this, &repulsorModuleData);
- *curB++ = m_repulsorHelper;
- }
- /** @todo srj -- figure out how to create this only on demand.
- currently we don't have a good way to add/remove update modules from
- an object on-the-fly, so we fake it here, and just skip the creation
- if it is impossible for this object to ever defect... */
- // shrubbery cannot defect. no, really.
- if (!tt->isKindOf(KINDOF_SHRUBBERY))
- {
- static const NameKeyType defectionModuleDataTagNameKey = NAMEKEY( "ModuleTag_DefectionHelper" );
- static ObjectDefectionHelperModuleData defectionModuleData;
- defectionModuleData.setModuleTagNameKey( defectionModuleDataTagNameKey );
- m_defectionHelper = newInstance(ObjectDefectionHelper)(this, &defectionModuleData);
- *curB++ = m_defectionHelper;
- }
- if (tt->canPossiblyHaveAnyWeapon())
- {
- // we only need a firingtracker and wshelper if we can possibly have a weapon.
- static const NameKeyType weaponStatusModuleDataTagNameKey = NAMEKEY( "ModuleTag_WeaponStatusHelper" );
- static ObjectWeaponStatusHelperModuleData weaponStatusModuleData;
- weaponStatusModuleData.setModuleTagNameKey( weaponStatusModuleDataTagNameKey );
- m_wsHelper = newInstance(ObjectWeaponStatusHelper)(this, &weaponStatusModuleData);
- *curB++ = m_wsHelper;
- static const NameKeyType firingTrackerModuleDataTagNameKey = NAMEKEY( "ModuleTag_FiringTrackerHelper" );
- static FiringTrackerModuleData firingTrackerModuleData;
- firingTrackerModuleData.setModuleTagNameKey( firingTrackerModuleDataTagNameKey );
- m_firingTracker = newInstance(FiringTracker)(this, &firingTrackerModuleData);
- *curB++ = m_firingTracker;
- static const NameKeyType tempWeaponBonusHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_TempWeaponBonusHelper" );
- static TempWeaponBonusHelperModuleData tempWeaponBonusModuleData;
- tempWeaponBonusModuleData.setModuleTagNameKey( tempWeaponBonusHelperModuleDataTagNameKey );
- m_tempWeaponBonusHelper = newInstance(TempWeaponBonusHelper)(this, &tempWeaponBonusModuleData);
- *curB++ = m_tempWeaponBonusHelper;
- }
- // behaviors are always done first, so they get into the publicModule arrays
- // before anything else.
- for (modIdx = 0; modIdx < mi.getCount(); ++modIdx)
- {
- modName = mi.getNthName(modIdx);
- if (modName.isEmpty())
- continue;
- BehaviorModule* newMod = (BehaviorModule*)TheModuleFactory->newModule(this, modName, mi.getNthData(modIdx), MODULETYPE_BEHAVIOR);
- *curB++ = newMod;
- BodyModuleInterface* body = newMod->getBody();
- if (body)
- {
- DEBUG_ASSERTCRASH(m_body == NULL, ("Duplicate bodies"));
- m_body = body;
- }
- ContainModuleInterface* contain = newMod->getContain();
- if (contain)
- {
- DEBUG_ASSERTCRASH(m_contain == NULL, ("Duplicate containers"));
- m_contain = contain;
- }
- StealthUpdate* stealth = (StealthUpdate*)newMod->getStealth();
- if ( stealth )
- {
- DEBUG_ASSERTCRASH( m_stealth == NULL, ("DuplicateStealthUpdates!") );
- m_stealth = stealth;
- }
- AIUpdateInterface* ai = newMod->getAIUpdateInterface();
- if (ai)
- {
- if( m_ai )
- {
- DEBUG_ASSERTCRASH( m_ai == NULL, ("%s has more than one AI module. This is illegal!\n", getTemplate()->getName().str()) );
- }
- m_ai = ai;
- }
- static NameKeyType key_PhysicsUpdate = NAMEKEY("PhysicsBehavior");
- if (newMod->getModuleNameKey() == key_PhysicsUpdate)
- {
- DEBUG_ASSERTCRASH(m_physics == NULL, ("You should never have more than one Physics module (%s)\n",getTemplate()->getName().str()));
- m_physics = (PhysicsBehavior*)newMod;
- }
- }
- *curB = NULL;
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai) {
- ai->setAttitude(getTeam()->getPrototype()->getTemplateInfo()->m_initialTeamAttitude);
- if (m_team && m_team->getPrototype() && m_team->getPrototype()->getAttackPriorityName().isNotEmpty()) {
- AsciiString name = m_team->getPrototype()->getAttackPriorityName();
- const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
- if (info && info->getName().isNotEmpty()) {
- ai->setAttackInfo(info);
- }
- }
- }
- // allocate experience tracker
- m_experienceTracker = newInstance(ExperienceTracker)(this);
- // If a valid team has been assigned me, then I have a Player I can ask about my starting level
- const Player* controller = getControllingPlayer();
- m_experienceTracker->setVeterancyLevel( controller->getProductionVeterancyLevel( getTemplate()->getName() ) );
- /// allow for inter-Module resolution
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onObjectCreated();
- }
- m_numTriggerAreasActive = 0;
- m_enteredOrExitedFrame = 0;
- m_isSelectable = tt->isKindOf(KINDOF_SELECTABLE);
- m_healthBoxOffset.zero();// this is used for units that are amorphous, like angry mob
- //Modules have now been completely created!
- m_modulesReady = true;
- TheRadar->addObject( this );
- // register the object with the GameLogic
- TheGameLogic->registerObject( this );
- //disable occlusion for some time after object is created to allow them to exit the factory/building.
- m_safeOcclusionFrame = TheGameLogic->getFrame()+tt->getOcclusionDelay();
- m_soleHealingBenefactorID = INVALID_ID; ///< who is the only other object that can give me this non-stacking heal benefit?
- m_soleHealingBenefactorExpirationFrame = 0; ///< on what frame can I accept healing (thus to switch) from a new benefactor
- } // end Object
- //-------------------------------------------------------------------------------------------------
- /** Emit message announcing object's creation
- * Note: Have to do this in virtual init() method because virtual methods
- * don't become virtual until AFTER the constructor has completed, and we
- * need to send our type in this message via virtual getType(). */
- //-------------------------------------------------------------------------------------------------
- void Object::initObject()
- {
- // Weapons & Damage -------------------------------------------------------------------------------------------------
- // Force the initial weapon set to be instantiated & reloaded.
- //GS No Bad Wrong
- // The flags are constructed to empty, and between then and now they may be set in valid ways by onCreate modules.
- // We don't want to blow that away. updateWeaponSet is safe to call on its own, so I will move that to the end.
- // m_curWeaponSetFlags.clear();
- // m_weaponSet.updateWeaponSet(this);
- // m_weaponBonusCondition = 0;
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- m_lastWeaponCondition[i] = WSF_INVALID;
- // emit message announcing object's creation
- TheGameLogic->sendObjectCreated( this );
- // If I have a valid team assigned, I can run through my Upgrade modules with his flags
- updateUpgradeModules();
- //If the player has battle plans (America Strategy Center), then apply those bonuses
- //to this object if applicable. Internally it validates certain kinds of objects.
- const Player* controller = getControllingPlayer();
- if (controller)
- {
- if (!getReceivingDifficultyBonus() && TheScriptEngine->getObjectsShouldReceiveDifficultyBonus())
- {
- setReceivingDifficultyBonus(TRUE);
- }
-
- if (controller->getNumBattlePlansActive() > 0)
- {
- controller->applyBattlePlanBonusesForObject( this );
- }
- }
- //For each special power module that we have, add it's type to the specialpower bits. This is
- //for optimal access later.
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
- if( spTemplate )
- {
- SET_SPECIALPOWERMASK( m_specialPowerBits, spTemplate->getSpecialPowerType() );
- }
- }
- // Kris -- All missiles must be projectiles! This is the perfect place to assert them!
- // srj: yes, but only in debug...
- #if defined(_DEBUG) || defined(_INTERNAL)
- if( !isKindOf( KINDOF_PROJECTILE ) )
- {
- if( isKindOf( KINDOF_SMALL_MISSILE ) || isKindOf( KINDOF_BALLISTIC_MISSILE ) )
- {
- //Warning only...
- 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() ) );
- }
- }
- #endif
- if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
- // Notify script conditions to update conditions that consider unit counts.
- // We ignore projectiles cause they are frequently created & destroyed, and are not
- // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
- TheScriptEngine->notifyOfObjectCreationOrDestruction();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- }
- // Everything (like weaponSet flags) is inited, so check if the WeaponSet needs to change.
- m_weaponSet.updateWeaponSet(this);
-
- if( isKindOf( KINDOF_MINE ) || isKindOf( KINDOF_BOOBY_TRAP ) || isKindOf( KINDOF_DEMOTRAP ) )
- {
- ThePlayerList->getNeutralPlayer()->getAcademyStats()->recordMine();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Object::~Object()
- {
- // tell the AI the building is gone
- /// @todo Generalize the notion of objects entering and leaving the world, so we don't have to special case this
- TheAI->pathfinder()->removeObjectFromPathfindMap( this );
- if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
- // Notify script conditions to update conditions that consider unit counts.
- // We ignore projectiles cause they are frequently created & destroyed, and are not
- // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
- TheGameLogic->updateObjectsChangedTriggerAreas();
- TheScriptEngine->notifyOfObjectCreationOrDestruction();
- }
- //
- // remove from radar before we NULL out the team ... the order of ops are critical here
- // because the radar code will sometimes look at the team info and it is assumed through
- // the team and player code that the team is valid
- //
- if( m_radarData )
- TheRadar->removeObject( this );
- // emit message announcing object's destruction. Again, order is important; we must do this
- // before wiping out the team.
- TheGameLogic->sendObjectDestroyed( this );
- // empty the team
- setTeam( NULL );
- // Object's set of these persist for the life of the object.
- m_partitionLastLook->deleteInstance();
- m_partitionLastLook = NULL;
- m_partitionRevealAllLastLook->deleteInstance();
- m_partitionRevealAllLastLook = NULL;
- m_partitionLastShroud->deleteInstance();
- m_partitionLastShroud = NULL;
- m_partitionLastThreat->deleteInstance();
- m_partitionLastThreat = NULL;
- m_partitionLastValue->deleteInstance();
- m_partitionLastValue = NULL;
- // remove the object from the partition system if present
- if( m_partitionData )
- ThePartitionManager->unRegisterObject( this );
- // if we are in a group, remove us
- if (m_group)
- m_group->remove( this );
-
- // note, do NOT free these, there are just a shadow copy!
- m_ai = NULL;
- m_physics = NULL;
- // delete any modules present
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->deleteInstance();
- *b = NULL; // in case other modules call findModule from their dtor!
- }
- delete [] m_behaviors;
- m_behaviors = NULL;
- if( m_experienceTracker )
- m_experienceTracker->deleteInstance();
- m_experienceTracker = NULL;
- // we don't need to delete these, there were deleted on the m_behaviors list
- m_firingTracker = NULL;
- m_repulsorHelper = NULL;
- m_statusDamageHelper = NULL;
- m_tempWeaponBonusHelper = NULL;
- m_subdualDamageHelper = NULL;
- m_smcHelper = NULL;
- m_wsHelper = NULL;
- m_defectionHelper = NULL;
- // reset id to zero so we never mistaken grab "dead" objects
- m_id = INVALID_ID;
- // Instead of removing it from the named cache, notify the script engine that it has died.
- // The script engine will remove it from the cache if necessary. The script engine needs to take
- // a crack at this in case it is the current "This Object" pointer.
- TheScriptEngine->notifyOfObjectDestruction(this);
- }
- //-------------------------------------------------------------------------------------------------
- /// this object now contained in "containedBy"
- //-------------------------------------------------------------------------------------------------
- void Object::onContainedBy( Object *containedBy )
- {
- setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNSELECTABLE ) );
- if (containedBy && containedBy->getContain()->isEnclosingContainerFor(this))
- setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- else
- clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- m_containedBy = containedBy;
- m_containedByFrame = TheGameLogic->getFrame();
- handlePartitionCellMaintenance(); // which should unlook me now that I am contained
-
- }
- //-------------------------------------------------------------------------------------------------
- /// this object no longer contained in "containedBy"
- //-------------------------------------------------------------------------------------------------
- void Object::onRemovedFrom( Object *removedFrom )
- {
- clearStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_MASKED, OBJECT_STATUS_UNSELECTABLE ) );
- m_containedBy = NULL;
- m_containedByFrame = 0;
- handlePartitionCellMaintenance(); // get a clean look, now that I am outdoors, again
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Int Object::getTransportSlotCount() const
- {
- Int count = getTemplate()->getRawTransportSlotCount();
- ContainModuleInterface* contain = getContain();
- if ( contain && contain->isSpecialZeroSlotContainer() )
- {
- count = 0;
- const ContainedItemsList* items = contain->getContainedItemsList();
- if (items)
- {
- for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
- {
- count += (*it)->getTransportSlotCount();
- }
- }
- }
- return count;
- }
- //-------------------------------------------------------------------------------------------------
- /** Run from GameLogic::destroyObject */
- //-------------------------------------------------------------------------------------------------
- void Object::onDestroy()
- {
- // This is the old cleanUpContain safeguard. Say goodbye so they don't try to look us up.
- if( m_containedBy && m_containedBy->getContain() )
- {
- m_containedBy->getContain()->removeFromContain( this );
- }
- //
- // run the onDelete on all modules present so they each have an opportunity to cleanup
- // anything they need to ... including talking to any other modules
- //
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onDelete();
- }
- //Have to remove ourself from looking as well. RebuildHoleWorkers definately hit here.
- handlePartitionCellMaintenance();
- } // end onDestroy
- //=============================================================================
- //=============================================================================
- void Object::setGeometryInfo(const GeometryInfo& geom)
- {
- m_geometryInfo = geom;
- if( m_partitionData )
- {
- // if our geometry changes, we unregister and re-register with the partitionmgr
- // so that our size gets updated appropriately. this shouldn't be a problem
- // unless setGeometryInfo gets called frequently. (srj)
- ThePartitionManager->unRegisterObject( this );
- ThePartitionManager->registerObject( this );
- }
- if (m_drawable)
- m_drawable->reactToGeometryChange();
- }
- //=============================================================================
- //=============================================================================
- void Object::setGeometryInfoZ( Real newZ )
- {
- // A Z change only does not need to un/register with the PartitionManager
- m_geometryInfo.setMaxHeightAbovePosition( newZ );
- if (m_drawable)
- m_drawable->reactToGeometryChange();
- }
- //=============================================================================
- void Object::friend_setUndetectedDefector( Bool status )
- {
- if (status)
- m_privateStatus |= UNDETECTED_DEFECTOR;
- else
- m_privateStatus &= ~UNDETECTED_DEFECTOR;
- }
- //=============================================================================
- void Object::restoreOriginalTeam()
- {
- if( m_team == NULL || m_originalTeamName.isEmpty() )
- return;
-
- Team* origTeam = TheTeamFactory->findTeam(m_originalTeamName);
- if (origTeam == NULL)
- {
- DEBUG_CRASH(("Object original team (%s) could not be found or created! (srj)\n",m_originalTeamName.str()));
- return;
- }
- if (m_team == origTeam)
- {
- DEBUG_CRASH(("Object appears to still be on its original team, so why are we attempting to restore it? (srj)\n"));
- return;
- }
- setTeam(origTeam);
- }
- //=============================================================================
- //=============================================================================
- void Object::setTeam( Team *team )
- {
- // In order to prevent spawning useful units for a player after he dies, we
- // just assign objects to the neutral player if we try to misbehave.
- if (team && !team->getControllingPlayer()->isPlayerActive())
- team = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
- setTemporaryTeam(team);
- m_originalTeamName = m_team ? m_team->getName() : AsciiString::TheEmptyString;
- }
- //=============================================================================
- //=============================================================================
- void Object::setTemporaryTeam( Team *team )
- {
- const Bool restoring = false;
- setOrRestoreTeam(team, restoring);
- }
- //=============================================================================
- //=============================================================================
- void Object::setOrRestoreTeam( Team* team, Bool restoring )
- {
- // don't do anything if the team hasn't changed
- if( m_team == team )
- return;
- Team* oldTeam = m_team;
- // Before Switch //////////////////////////
- if (m_team)
- {
- if (m_team->isInList_TeamMemberList(this))
- {
- m_team->removeFrom_TeamMemberList(this);
- m_team->getControllingPlayer()->becomingTeamMember(this, false);
- }
- }
-
- // Switch //////////////////////////
- m_team = team;
- // After Switch //////////////////////////
- if (m_team)
- {
- if (!m_team->isInList_TeamMemberList(this))
- {
- m_team->prependTo_TeamMemberList(this);
- m_team->getControllingPlayer()->becomingTeamMember(this, true);
- }
-
- // now, adjust the attitude of the unit to its new team.
- const TeamPrototype* proto = m_team->getPrototype();
- if (proto && proto->getTemplateInfo())
- {
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai)
- {
- ai->setAttitude(proto->getTemplateInfo()->m_initialTeamAttitude);
- if (proto->getAttackPriorityName().isNotEmpty()) {
- AsciiString name = proto->getAttackPriorityName();
- const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
- if (info && info->getName().isNotEmpty()) {
- ai->setAttackInfo(info);
- }
- }
- }
- }
- // emit message announcing object's new alliance
- Drawable *draw = getDrawable();
- if (draw)
- draw->changedTeam();
- }
- // This can't just go in ::defect, because some things just do setTeam. The act of
- // setting a new team needs to tell the modules and do other important stuff.
- // And it needs to happen after the switch.
- if( oldTeam && team && !restoring )
- onCapture( oldTeam->getControllingPlayer(), team->getControllingPlayer() );
- //
- // the team changed we have a change in priorities on the radar if we are
- // a candidate for the radar as it is
- //
- if( m_radarData )
- {
- // removing it and adding it will cause a resort to happen
- TheRadar->removeObject( this );
- TheRadar->addObject( this );
- }
- // Tell TheInGameUI that the object has changed hands
- Int oldPlayerIndex = (oldTeam)?(oldTeam->getControllingPlayer()->getPlayerIndex()):-1;
- Int newPlayerIndex = (m_team)?(m_team->getControllingPlayer()->getPlayerIndex()):-1;
- if (oldPlayerIndex != newPlayerIndex)
- TheInGameUI->objectChangedTeam(this, oldPlayerIndex, newPlayerIndex);
- }
- //=============================================================================
- enum
- {
- BOOBY_TRAP_SCAN_RANGE = 25
- };
- Bool Object::checkAndDetonateBoobyTrap(const Object *victim)
- {
- if( !testStatus(OBJECT_STATUS_BOOBY_TRAPPED) )
- return FALSE;
- PartitionFilterAcceptByKindOf kindFilter(MAKE_KINDOF_MASK(KINDOF_BOOBY_TRAP), KINDOFMASK_NONE);
- PartitionFilterSameMapStatus filterMapStatus(this);
- PartitionFilter *filters[3];
- filters[0] = &kindFilter;
- filters[1] = &filterMapStatus;
- filters[2] = NULL;
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( getPosition(), BOOBY_TRAP_SCAN_RANGE + getGeometryInfo().getBoundingCircleRadius(),
- FROM_CENTER_2D, filters, ITER_SORTED_NEAR_TO_FAR );
- MemoryPoolObjectHolder hold(iter);// This is the magic thing that frees the dynamically made iter in its destructor
- Object *ourBoobyTrap = NULL;
- for( Object *other = iter->first(); other; other = iter->next() )
- {
- if( other->getProducerID() == getID() )// Sticky bombs call the thing they are on their producer for just such an occasion
- {
- ourBoobyTrap = other;
- break;
- }
- }
- if( ourBoobyTrap )
- {
- static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
- StickyBombUpdate *update = (StickyBombUpdate*)ourBoobyTrap->findUpdateModule( key_StickyBombUpdate );
- if( update )
- {
- if( victim && ourBoobyTrap->getControllingPlayer()->getRelationship(victim->getTeam()) == ALLIES )
- return FALSE;// Friends don't touch friends boobies.
- update->detonate();
- return TRUE;// Booby Trapped status will be cleared by stickybomb, as they set it
- }
- }
- return FALSE;
- }
- //=============================================================================
- void Object::setStatus( ObjectStatusMaskType objectStatus, Bool set )
- {
- ObjectStatusMaskType oldStatus = m_status;
- if (set)
- m_status.set( objectStatus );
- else
- m_status.clear( objectStatus );
- if (m_status != oldStatus)
- {
- if( set && objectStatus.test( OBJECT_STATUS_REPULSOR ) && m_repulsorHelper != NULL )
- {
- // Damaged repulsable civilians scare (repulse) other civs, but only
- // for a short amount of time... use the repulsor helper to turn off repulsion shortly.
- m_repulsorHelper->sleepUntil(TheGameLogic->getFrame() + 2*LOGICFRAMES_PER_SECOND);
- }
- if( objectStatus.test( OBJECT_STATUS_STEALTHED ) || objectStatus.test( OBJECT_STATUS_DETECTED ) || objectStatus.test( OBJECT_STATUS_DISGUISED ) )
- {
- //Kris: Aug 20, 2003
- //When any of the three key status bits for stealth go on or off, then handle partition updates for vision.
- if( getTemplate()->getShroudRevealToAllRange() > 0.0f )
- {
- handlePartitionCellMaintenance();
- }
- }
- // when an object's construction status changes, it needs to have its partition data updated,
- // in order to maintain the shroud correctly.
- if( m_status.test( OBJECT_STATUS_UNDER_CONSTRUCTION ) != oldStatus.test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
- {
- // CHECK FOR MINES, AND DETONATE THEM NOW
- ObjectIterator *iter =
- ThePartitionManager->iteratePotentialCollisions( getPosition(), getGeometryInfo(), getOrientation() );
- MemoryPoolObjectHolder hold( iter );
- Object *them;
- for( them = iter->first(); them; them = iter->next() )
- {
- if (them->isKindOf( KINDOF_MINE ))
- {
- //DETONATE ANY ENEMY MINES, OR DELETE FRIENDLY ONES
- Relationship r = getRelationship(them);
- if (r == ENEMIES)
- {
- them->kill(); // detonate mine
- }
- else
- {
- TheGameLogic->destroyObject(them);
- }
- }
- }// next object
- if (m_partitionData)
- m_partitionData->makeDirty(true);
- }
- }
- }
- //=============================================================================
- void Object::setScriptStatus( ObjectScriptStatusBit bit, Bool set )
- {
- UnsignedInt oldScriptStatus = m_scriptStatus;
- if( set )
- {
- m_scriptStatus |= bit;
- }
- else
- {
- m_scriptStatus &= ~bit;
- }
- if( m_scriptStatus != oldScriptStatus )
- {
- if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) )
- {
- if( m_partitionData )
- {
- // if an object becomes disabled or unpowered, then you have to update its partition data because it will
- // change how far it can see.
- m_partitionData->makeDirty(true);
- }
- if( m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED )
- {
- //I am now disabled, so tell the main game engine!
- setDisabled( DISABLED_SCRIPT_DISABLED );
- }
- else
- {
- //I am no longer disabled, so tell the main game engine!
- clearDisabled( DISABLED_SCRIPT_DISABLED );
- }
- }
- if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) )
- {
- if( m_partitionData )
- {
- // if an object becomes disabled or unpowered, then you have to update its partition data because it will
- // change how far it can see.
- m_partitionData->makeDirty(true);
- }
- if( m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED )
- {
- //I am now underpowered, so tell the main game engine!
- setDisabled( DISABLED_SCRIPT_UNDERPOWERED );
- }
- else
- {
- //I am no longer undperpowered, so tell the main game engine!
- clearDisabled( DISABLED_SCRIPT_UNDERPOWERED );
- }
- }
- }
- }
- //=============================================================================
- Bool Object::canCrushOrSquish(Object *otherObj, CrushSquishTestType testType ) const
- {
- DEBUG_ASSERTCRASH(this, ("null this in canCrushOrSquish"));
- if( !otherObj )
- {
- //Can't crush anything.
- return false;
- }
- if( isDisabledByType( DISABLED_UNMANNED ) )
- {
- //Unmanned vehicles cannot crush troops. This was happening when Jarmen Kell sniped
- //the vehicle and booted the guys out while still moving, as the vehicle is now
- //on a different team.
- return false;
- }
- UnsignedByte crusherLevel = getCrusherLevel();
-
- // order matters: we want to know if I consider it to be an ally, not vice versa
- if( getRelationship( otherObj ) == ALLIES )
- {
- //Friends don't let friends crush friends.
- return false;
- }
- if( !crusherLevel )
- {
- //Can't crush anything!
- return false;
- }
- //Test this case for generic infantry getting squished by vehicles!
- if( testType == TEST_SQUISH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
- {
- //****************************************************************************************
- //NOTE: This section of code is used by the pathfinder to determine if the object should
- // move to the target. I don't think it's the right place to check for this because
- // the semantics check to see if we can squish something -- not approach it. However
- // I'm not moving it for fear of some major breakage! -- KM
- //Bool squisher = crusherLevel > 0;
- //if( !squisher )
- //{
- // Weapon *weapon = getCurrentWeapon();
- // if( weapon && weapon->isContactWeapon() )
- // {
- // squisher = true;
- // }
- //}
- //if( squisher )
- //NOTE2: *** IF YOU REENABLE THIS CODE -- Move the "if( !crusherLevel ) return false" below
- // this squish section.
- //****************************************************************************************
- {
- // See if other is squishable
- static NameKeyType key_squish = NAMEKEY( "SquishCollide" );
- if( otherObj->findModule( key_squish ) )
- {
- return true; // squishable.
- }
- }
- }
- UnsignedByte crushableLevel = otherObj->getCrushableLevel();
- if( testType == TEST_CRUSH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
- {
- if( crusherLevel > crushableLevel )
- {
- return true;
- }
- }
-
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- UnsignedByte Object::getCrusherLevel() const
- {
- return getTemplate()->getCrusherLevel();
- }
- //-------------------------------------------------------------------------------------------------
- UnsignedByte Object::getCrushableLevel() const
- {
- return getTemplate()->getCrushableLevel();
- }
- // ------------------------------------------------------------------------------------------------
- /** Topple an object, if possible */
- // ------------------------------------------------------------------------------------------------
- void Object::topple( const Coord3D *toppleDirection, Real toppleSpeed, UnsignedInt options )
- {
- static NameKeyType key_ToppleUpdate = NAMEKEY("ToppleUpdate");
- ToppleUpdate* toppleUpdate = (ToppleUpdate*)findModule(key_ToppleUpdate);
- if( toppleUpdate && toppleUpdate->isAbleToBeToppled() )
- {
- // apply the topple force
- toppleUpdate->applyTopplingForce( toppleDirection, toppleSpeed, options );
- } // end if
- } // end topple
- //=============================================================================
- void Object::setArmorSetFlag(ArmorSetType ast)
- {
- m_body->setArmorSetFlag(ast);
- }
- //=============================================================================
- void Object::clearArmorSetFlag(ArmorSetType ast)
- {
- m_body->clearArmorSetFlag(ast);
- }
- //=============================================================================
- Bool Object::testArmorSetFlag(ArmorSetType ast) const
- {
- return m_body->testArmorSetFlag(ast);
- }
- //=============================================================================
- void Object::reloadAllAmmo(Bool now)
- {
- m_weaponSet.reloadAllAmmo(this, now);
- }
- //=============================================================================
- Bool Object::isOutOfAmmo() const
- {
- return m_weaponSet.isOutOfAmmo();
- }
- //=============================================================================
- Bool Object::hasAnyWeapon() const
- {
- return m_weaponSet.hasAnyWeapon();
- }
- //=============================================================================
- Bool Object::hasAnyDamageWeapon() const
- {
- //First check to see if we have any weapons -- if not return false.
- if( !m_weaponSet.hasAnyDamageWeapon() )
- {
- return FALSE;
- }
- return TRUE;
- }
- //=============================================================================
- UnsignedInt Object::getMostPercentReadyToFireAnyWeapon() const
- {
- return m_weaponSet.getMostPercentReadyToFireAnyWeapon();
- }
- //=============================================================================
- Bool Object::hasWeaponToDealDamageType(DamageType typeToDeal) const
- {
- return m_weaponSet.hasWeaponToDealDamageType(typeToDeal);
- }
- //=============================================================================
- Real Object::getLargestWeaponRange() const
- {
- Real retVal = -1;
- for (Int i = PRIMARY_WEAPON; i < WEAPONSLOT_COUNT; ++i) {
- Weapon* weapon = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (!weapon) {
- continue;
- }
- Real tmpVal = weapon->getAttackRange(this);
- if (tmpVal > retVal) {
- retVal = tmpVal;
- }
- }
- return retVal;
- }
- //=============================================================================
- void Object::setFiringConditionForCurrentWeapon() const
- {
- if (m_drawable)
- {
- WeaponSlotType wslot = m_weaponSet.getCurWeaponSlot();
- ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot(wslot, WSF_FIRING);
- m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[wslot], c);
- }
- }
- //=============================================================================
- void Object::setModelConditionState( ModelConditionFlagType a )
- {
- if (m_drawable)
- {
- m_drawable->setModelConditionState(a);
- }
- }
- //=============================================================================
- void Object::clearModelConditionState( ModelConditionFlagType a )
- {
- if (m_drawable)
- {
- m_drawable->clearModelConditionState(a);
- }
- }
- //=============================================================================
- void Object::clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set )
- {
- if (m_drawable)
- {
- m_drawable->clearAndSetModelConditionState(clr, set);
- }
- }
- //=============================================================================
- void Object::clearModelConditionFlags( const ModelConditionFlags& clr )
- {
- if (m_drawable)
- {
- m_drawable->clearModelConditionFlags(clr);
- }
- }
- //=============================================================================
- void Object::setModelConditionFlags( const ModelConditionFlags& set )
- {
- if (m_drawable)
- {
- m_drawable->setModelConditionFlags(set);
- }
- }
- //=============================================================================
- void Object::clearAndSetModelConditionFlags( const ModelConditionFlags& clr, const ModelConditionFlags& set )
- {
- if (m_drawable)
- {
- m_drawable->clearAndSetModelConditionFlags(clr, set);
- }
- }
- //=============================================================================
- // Special model states are states that are turned on for a period of time, and
- // turned off automatically -- used for cheer, and scripted special moment
- // animations. Setting a special state will automatically clear any other
- // special states that may be turned on so you can only have one at a time.
- //=============================================================================
- void Object::setSpecialModelConditionState( ModelConditionFlagType set, UnsignedInt frames )
- {
- clearSpecialModelConditionStates();
- setModelConditionState( set );
- if( frames == 0 )
- {
- frames = 1;
- }
- m_smcUntil = TheGameLogic->getFrame() + frames;
- m_smcHelper->sleepUntil(m_smcUntil);
- }
- //=============================================================================
- void Object::clearSpecialModelConditionStates()
- {
- clearModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_SPECIAL_CHEERING ) );
- m_smcUntil = NEVER;
- }
- // Lorenzen has some interest in this, ask before deleting
- //=============================================================================
- //const ModelConditionFlags& Object::getModelConditionFlags() const
- //{
- // if (m_drawable)
- // {
- // return m_drawable->getModelConditionFlags();
- // }
- // else
- // {
- // DEBUG_CRASH(("NULL Drawable at this point, you can't get modelconditionflags now."));
- // static ModelConditionFlags noFlags;
- // return noFlags;
- // }
- //}
- //=============================================================================
- Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot)
- {
- if (!m_weaponSet.hasAnyWeapon())
- return NULL;
- if (wslot)
- *wslot = m_weaponSet.getCurWeaponSlot();
- return m_weaponSet.getCurWeapon();
- }
- //=============================================================================
- const Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot) const
- {
- if (!m_weaponSet.hasAnyWeapon())
- return NULL;
- if (wslot)
- *wslot = m_weaponSet.getCurWeaponSlot();
- return m_weaponSet.getCurWeapon();
- }
- //=============================================================================
- Weapon* Object::findWaypointFollowingCapableWeapon()
- {
- return m_weaponSet.findWaypointFollowingCapableWeapon();
- }
- //=============================================================================
- Bool Object::getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const
- {
- /// @todo srj -- may need to cache this inside weaponset.
- const Weapon* w = m_weaponSet.findAmmoPipShowingWeapon();
- if (w)
- {
- numTotal = w->getClipSize();
- numFull = w->getRemainingAmmo();
- return true;
- }
- else
- {
- return false;
- }
- }
- //=============================================================================
- /*
- NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
- since that isn't an incredibly fast call, and this is called repeatedly in some inner loops
- where we already know that isAbleToAttack() == true. so you should always
- call isAbleToAttack prior to calling this! (srj)
- */
- CanAttackResult Object::getAbleToAttackSpecificObject( AbleToAttackType t, const Object* target, CommandSourceType commandSource, WeaponSlotType specificSlot ) const
- {
- // NO! BAD! WRONG!
- // If we can't attack at all, then we cannot attack this
- //if (!isAbleToAttack())
- // return FALSE;
- // Otherwise leave it up to our weapons.
- return m_weaponSet.getAbleToAttackSpecificObject( t, this, target, commandSource, specificSlot );
- }
- //=============================================================================
- //Used for base defenses and otherwise stationary units to see if you can attack a position potentially out of range.
- CanAttackResult Object::getAbleToUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType commandSource, WeaponSlotType specificSlot ) const
- {
- return m_weaponSet.getAbleToUseWeaponAgainstTarget( attackType, this, victim, pos, commandSource, specificSlot );
- }
- //=============================================================================
- Bool Object::chooseBestWeaponForTarget(const Object* target, WeaponChoiceCriteria criteria, CommandSourceType cmdSource )
- {
- return m_weaponSet.chooseBestWeaponForTarget(this, target, criteria, cmdSource );
- }
- //DECLARE_PERF_TIMER(fireCurrentWeapon)
- //=============================================================================
- void Object::fireCurrentWeapon(Object *target)
- {
- //USE_PERF_TIMER(fireCurrentWeapon)
- // victim may have already been destroyed
- if (target == NULL)
- return;
- Weapon* weapon = m_weaponSet.getCurWeapon();
- if (weapon && (weapon->getStatus() == READY_TO_FIRE))
- {
- Bool reloaded = weapon->fireWeapon(this, target);
- DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
- if (m_firingTracker)
- m_firingTracker->shotFired(weapon, target->getID());
- if (reloaded)
- releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- //=============================================================================
- void Object::fireCurrentWeapon(const Coord3D* pos)
- {
- //USE_PERF_TIMER(fireCurrentWeapon)
- if (pos == NULL)
- return;
- Weapon* weapon = m_weaponSet.getCurWeapon();
- if (weapon && (weapon->getStatus() == READY_TO_FIRE))
- {
- Bool reloaded = weapon->fireWeapon(this, pos);
- DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
- if (m_firingTracker)
- m_firingTracker->shotFired(weapon, INVALID_ID);
- if (reloaded)
- releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- //==============================================================================
- void Object::notifyFiringTrackerShotFired( const Weapon* weaponFired, ObjectID victimID )
- {
- if ( m_firingTracker )
- m_firingTracker->shotFired( weaponFired, victimID );
- }
- //=============================================================================
- void Object::preFireCurrentWeapon( const Object *victim )
- {
- Weapon* weapon = m_weaponSet.getCurWeapon();
- //If we are going to be capable of firing our weapon NEXT frame, set the pre-attack
- //up now. This gets called by AIAttackFireWeaponState::onEnter().. but the update happens
- //next frame.
- if (weapon && TheGameLogic->getFrame() + 1 >= weapon->getPossibleNextShotFrame() )
- {
- weapon->preFireWeapon( this, victim );
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- // ============================================================================
- /** Using the firing tracker, return the frame a shot was last fired on */
- // ============================================================================
- UnsignedInt Object::getLastShotFiredFrame() const
- {
- UnsignedInt recent = 0;
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (w)
- {
- UnsignedInt when = w->getLastShotFrame();
- if (when > recent)
- recent = when;
- }
- }
- return recent;
- }
- // ============================================================================
- /** Get the victim ID we last shot at */
- // ============================================================================
- ObjectID Object::getLastVictimID() const
- {
- return m_firingTracker ? m_firingTracker->getLastShotVictim() : INVALID_ID;
- }
- //=============================================================================
- // Object::getRelationship
- //=============================================================================
- Relationship Object::getRelationship(const Object *that) const
- {
- const Team *myTeam = getTeam();
- if (myTeam && that)
- {
- if (getIsUndetectedDefector())
- {
- return NEUTRAL; // so my AI does not give away my position by auto acquire
- }
- else if (that->getIsUndetectedDefector())
- {
- return ALLIES; // so I treat undetecteddefectors like they were my very own
- }
- else
- {
- return myTeam->getRelationship( that->getTeam() );
- }
- }
- return NEUTRAL;
- }
- //=============================================================================
- // Object::getControllingPlayer
- //=============================================================================
- Player * Object::getControllingPlayer() const
- {
- const Team* myTeam = this->getTeam();
- if (myTeam)
- return myTeam->getControllingPlayer();
- return NULL;
- }
- //=============================================================================
- void Object::setProducer(const Object* obj)
- {
- m_producerID = obj ? obj->getID() : INVALID_ID;
- // seems like a good idea, but is not. (srj)
- // if (obj)
- // m_indicatorColor = obj->m_indicatorColor;
- }
- //=============================================================================
- void Object::setBuilder( const Object *obj )
- {
- m_builderID = obj ? obj->getID() : INVALID_ID;
- }
- //=============================================================================
- void Object::setCustomIndicatorColor(Color c)
- {
- if (m_indicatorColor != c)
- {
- m_indicatorColor = c;
- if (m_drawable)
- m_drawable->changedTeam();
- }
- }
- //=============================================================================
- void Object::removeCustomIndicatorColor()
- {
- setCustomIndicatorColor(0);
- }
- //=============================================================================
- // Object::getIndicatorColor
- //=============================================================================
- Color Object::getIndicatorColor() const
- {
- if (m_indicatorColor == 0)
- {
- const Team *myTeam = getTeam();
- if (myTeam)
- {
- const Player* p = myTeam->getControllingPlayer();
- if (p)
- {
- return p->getPlayerColor();
- }
- }
- return GameMakeColor(0, 0, 0, 255);
- }
- else
- {
- return m_indicatorColor;
- }
- }
- //=============================================================================
- // Object::getNightIndicatorColor - used to make blue/purple easier to see on night models.
- //=============================================================================
- Color Object::getNightIndicatorColor() const
- {
- if (m_indicatorColor == 0)
- {
- const Team *myTeam = getTeam();
- if (myTeam)
- {
- const Player* p = myTeam->getControllingPlayer();
- if (p)
- {
- return p->getPlayerNightColor();
- }
- }
- return GameMakeColor(0, 0, 0, 255);
- }
- else
- {
- return m_indicatorColor;
- }
- }
- //=============================================================================
- // Object::isLocallyControlled
- //=============================================================================
- Bool Object::isLocallyControlled() const
- {
- return getControllingPlayer() == ThePlayerList->getLocalPlayer();
- }
- //=============================================================================
- // Object::isLocallyControlled
- //=============================================================================
- Bool Object::isNeutralControlled() const
- {
- return getControllingPlayer() == ThePlayerList->getNeutralPlayer();
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isPosDifferent(const Coord3D* a, const Coord3D* b)
- {
- // this is necessary because PhysicsBehavior may generate tiny changes even when
- // "standing still", due to roundoff errors. It's important that we only invalidate
- // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
- // so we must put in some cleverness...
- const Real THRESH = 0.01f;
- if (fabs(a->x - b->x) > THRESH)
- return true;
- if (fabs(a->y - b->y) > THRESH)
- return true;
- if (fabs(a->z - b->z) > THRESH)
- return true;
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isAngleDifferent(Real a, Real b)
- {
- // this is necessary because PhysicsBehavior may generate tiny changes even when
- // "standing still", due to roundoff errors. It's important that we only invalidate
- // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
- // so we must put in some cleverness...
- const Real THRESH = 0.01f; // in radians, this is approx 1/2 degree.
- if (fabs(a - b) > THRESH)
- return true;
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::reactToTurretChange( WhichTurretType turret, Real oldRotation, Real oldPitch )
- {
- Real currentRotation = 0.0f;
- Real currentPitch = 0.0f;
- if( getAI() )
- {
- getAI()->getTurretRotAndPitch( turret, ¤tRotation, ¤tPitch );
- }
- Bool rotationChange = (currentRotation != oldRotation);
- // Bool pitchChange = (currentPitch != oldPitch);
- if( rotationChange )
- {
- if (getContain())
- getContain()->containReactToTransformChange();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //DECLARE_PERF_TIMER(Object_reactToTransformChange)
- void Object::reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle)
- {
- //USE_PERF_TIMER(Object_reactToTransformChange)
- if(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)) {
- DEBUG_CRASH(("Object pos is nan."));
- TheGameLogic->destroyObject(this);
- }
- if (m_drawable)
- {
- m_drawable->setTransformMatrix( this->getTransformMatrix() );
- }
- Bool posDiff = isPosDifferent(oldPos, getPosition());
- Bool angDiff = isAngleDifferent(oldAngle, getOrientation());
- if (posDiff || angDiff)
- {
- if (m_partitionData)
- m_partitionData->makeDirty(true);
-
- if (getContain())
- getContain()->containReactToTransformChange();
- }
- if (posDiff)
- {
- setTriggerAreaFlagsForChangeInPosition(); // Update for entered/exited
-
- Region3D mapExtent;
- TheTerrainLogic->getExtent(&mapExtent);
- if (mapExtent.isInRegionNoZ(getPosition()))
- m_privateStatus &= ~OFF_MAP;
- else
- m_privateStatus |= OFF_MAP;
- }
- }
- //-------------------------------------------------------------------------------------------------
- ObjectShroudStatus Object::getShroudedStatus(Int playerIndex) const
- {
- if (getTemplate()->isKindOf( KINDOF_ALWAYS_VISIBLE ))
- return OBJECTSHROUD_CLEAR;
- if (m_partitionData)
- return m_partitionData->getShroudedStatus(playerIndex);
- // This can happen for objects removed from the partition system (e.g.,
- // for soldiers that are garrisoned inside a building).
- return OBJECTSHROUD_CLEAR;
- }
- //-------------------------------------------------------------------------------------------------
- /** Something is attempting to damage this object */
- //-------------------------------------------------------------------------------------------------
- void Object::attemptDamage( DamageInfo *damageInfo )
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- body->attemptDamage( damageInfo );
-
- // Process any shockwave forces that might affect this object due to the incurred damage
- if (damageInfo->in.m_shockWaveAmount > 0.0f && damageInfo->in.m_shockWaveRadius > 0.0f)
- {
- //KindOfMaskType immuneToShockwaveKindofs; //NEW RESTRICTIONS ADDED
- //immuneToShockwaveKindofs.set(KINDOF_PROJECTILE);// projectiles go idle in midair when they get sw'd //NEW RESTRICTIONS ADDED
- //immuneToShockwaveKindofs.set(KINDOF_PRODUCED_AT_HELIPAD);//helicopters go all wonky when they get shockwaved //NEW RESTRICTIONS ADDED
- PhysicsBehavior *behavior = getPhysics();
- if ( behavior && (isAirborneTarget() == FALSE) && (! isKindOf(KINDOF_PROJECTILE) ) )
- // if (behavior && isAnyKindOf( immuneToShockwaveKindofs ) == FALSE )//NEW RESTRICTIONS ADDED
- {
- // Calculate the shockwave taperoff amount due to distance from ground zero
- Real shockWaveScalar = damageInfo->in.m_shockWaveVector.length();
- Real distanceFromCenter = min(1.0f, shockWaveScalar / damageInfo->in.m_shockWaveRadius);
- Real distanceTaper = (distanceFromCenter) * (1.0f - damageInfo->in.m_shockWaveTaperOff);
- Real shockTaperMult = 1.0f - distanceTaper;
- // Set up the shockwave force to use apply on object
- Coord3D shockWaveForce;
- shockWaveForce.set( &damageInfo->in.m_shockWaveVector );
- shockWaveForce.normalize();
- shockWaveForce.scale( damageInfo->in.m_shockWaveAmount * shockTaperMult );
- shockWaveForce.z = shockWaveForce.length(); // Apply up force equal to the lateral force for dramatic effect
- // Apply the shock to the object
- behavior->applyShock(&shockWaveForce);
- // Add random rotation to the object for drama
-
- behavior->applyRandomRotation();
- // Set stunned state due to the shock for the object
- behavior->setStunned(true);
-
- setModelConditionState(MODELCONDITION_STUNNED_FLAILING);
- }
- }
-
- /// @todo track damage dealt/attempted
- //
- // if actual damage occurred, and this is an object owned by the local player we
- // might do a radar event for under attack. Note that we do not even try
- // to do radar events for DAMAGE_PENALTY as that damage type is a type of damage
- // that occurs with explicit player knowledge
- //
- if( damageInfo->out.m_actualDamageDealt > 0.0f &&
- damageInfo->in.m_damageType != DAMAGE_PENALTY &&
- damageInfo->in.m_damageType != DAMAGE_HEALING &&
- getControllingPlayer() &&
- !BitTest(damageInfo->in.m_sourcePlayerMask, getControllingPlayer()->getPlayerMask()) &&
- m_radarData != NULL &&
- getControllingPlayer() == ThePlayerList->getLocalPlayer() )
- TheRadar->tryUnderAttackEvent( this );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::attemptHealing(Real amount, const Object* source)
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- {
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_HEALING;
- damageInfo.in.m_deathType = DEATH_NONE;
- damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
- damageInfo.in.m_amount = amount;
- body->attemptHealing( &damageInfo );
- }
- }
- ObjectID Object::getSoleHealingBenefactor( void ) const
- {
- UnsignedInt now = TheGameLogic->getFrame();
- if( now > m_soleHealingBenefactorExpirationFrame )
- return INVALID_ID;
- return m_soleHealingBenefactorID;
- }
- Bool Object::attemptHealingFromSoleBenefactor ( Real amount, const Object* source, UnsignedInt duration )
- {///< for the non-stacking healers like ambulance and propaganda
- if( ! source ) // sanity
- return FALSE;
- UnsignedInt now = TheGameLogic->getFrame();
- ObjectID id = source->getID();
- // Either it is ok to accept healing from any who offer or this is my guy, calling again
- if( now > m_soleHealingBenefactorExpirationFrame || m_soleHealingBenefactorID == id )
- {
- m_soleHealingBenefactorID = id;
- m_soleHealingBenefactorExpirationFrame = now + duration;
- BodyModuleInterface* body = getBodyModule();
- if (body)
- {
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_HEALING;
- damageInfo.in.m_deathType = DEATH_NONE;
- damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
- damageInfo.in.m_amount = amount;
- body->attemptHealing( &damageInfo );
- }
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::estimateDamage( DamageInfoInput& damageInfo ) const
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- return body->estimateDamage( damageInfo );
-
- return 0.0f;
- }
- //-------------------------------------------------------------------------------------------------
- /** Do so much damage to an object that it will certainly die */
- //-------------------------------------------------------------------------------------------------
- void Object::kill( DamageType damageType, DeathType deathType )
- {
- DamageInfo damageInfo;
- // Do unmodifiable damage equal to their max health to kill.
- damageInfo.in.m_damageType = damageType;
- damageInfo.in.m_deathType = deathType;
- damageInfo.in.m_sourceID = INVALID_ID;
- damageInfo.in.m_amount = getBodyModule()->getMaxHealth();
- damageInfo.in.m_kill = TRUE; // Triggers object to die no matter what.
- attemptDamage( &damageInfo );
- DEBUG_ASSERTCRASH(!damageInfo.out.m_noEffect, ("Attempting to kill an unKillable object (InactiveBody?)\n"));
- } // end kill
- //-------------------------------------------------------------------------------------------------
- /** Restore max health to this Object */
- //-------------------------------------------------------------------------------------------------
- void Object::healCompletely()
- {
- attemptHealing(HUGE_DAMAGE_AMOUNT, NULL);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setEffectivelyDead(Bool dead)
- {
- if (dead)
- BitSet(m_privateStatus, EFFECTIVELY_DEAD);
- else
- BitClear(m_privateStatus, EFFECTIVELY_DEAD);
- if (dead)
- {
- if( m_radarData )
- TheRadar->removeObject( this );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setCaptured(Bool isCaptured)
- {
- if (isCaptured)
- BitSet(m_privateStatus, CAPTURED);
- else
- {
- DEBUG_LOG(("Clearing Captured Status. This should never happen. jkmcd"));
- BitClear(m_privateStatus, CAPTURED);
- }
- // No need to see if we should skip updates, this flag has no effect on skipping updates.
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isStructure(void) const
- {
- return isKindOf(KINDOF_STRUCTURE);
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isFactionStructure(void) const
- {
- return isAnyKindOf( KINDOFMASK_FS );
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isNonFactionStructure(void) const
- {
- return isStructure() && !isFactionStructure();
- }
- void localIsHero( Object *obj, void* userData )
- {
- Bool *hero = (Bool*)userData;
-
- if( obj && obj->isKindOf( KINDOF_HERO ) )
- {
- *hero = TRUE;
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isHero(void) const
- {
- ContainModuleInterface *contain = getContain();
- if( contain )
- {
- Bool heroInside = FALSE;
- contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE );
- if( heroInside )
- {
- return TRUE;
- }
- }
- return isKindOf( KINDOF_HERO );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setReceivingDifficultyBonus(Bool receive)
- {
- if (receive == m_isReceivingDifficultyBonus) {
- return;
- }
- m_isReceivingDifficultyBonus = receive;
- getControllingPlayer()->friend_applyDifficultyBonusesForObject(this, m_isReceivingDifficultyBonus);
- }
- //-------------------------------------------------------------------------------------------------
- //- DISABLEDNESS STUFF ----------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setDisabled( DisabledType type )
- {
- setDisabledUntil(type, FOREVER);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setDisabledUntil( DisabledType type, UnsignedInt frame )
- {
- Bool edgeCase = !isDisabled();
- if( type < 0 || type >= DISABLED_COUNT )
- {
- DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
- return;
- }
- //Handle audio events!
- AudioEventRTS sound;
- if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
- {
- //We've been sniped! Play a splatter sound for the pilot losing his face.
- sound = TheAudio->getMiscAudio()->m_splatterVehiclePilotsBrain;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_SUBDUED || type == DISABLED_HACKED )
- {
- //We've lost power -- make sure we aren't already out of power as the sounds shouldn't happen
- //if you were already disabled.
- if( !isDisabledByType( DISABLED_UNDERPOWERED ) &&
- !isDisabledByType( DISABLED_EMP ) &&
- !isDisabledByType( DISABLED_SUBDUED ) &&
- !isDisabledByType( DISABLED_HACKED ) )
- {
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- sound = TheAudio->getMiscAudio()->m_buildingDisabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( isKindOf( KINDOF_VEHICLE ) )
- {
- sound = TheAudio->getMiscAudio()->m_vehicleDisabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- }
- }
- if( m_disabledTillFrame[ type ] != frame )
- {
- // an edge-test for disabledness, for type. This INCREMENTS m_pauseCount
- // srj sez: HELD nevers disables special powers.
- if ( type != DISABLED_HELD && !isDisabledByType( type ) )
- pauseAllSpecialPowers( TRUE );
-
- m_disabledTillFrame[ type ] = frame;
- m_disabledMask.set( type, frame > TheGameLogic->getFrame() );
- if( m_drawable )
- {
- if( isDisabled() )
- {
- // Held does not tint anybody. If we are multiply disabled, the other setting will hit the tint,
- // and in clear, only-held and not-disabled are both causes to untint.
- // Doh. Also shouldn't be tinting when disabled by scripting.
- // Doh^2. Also shouldn't be CLEARING tinting if we're disabling by held or script disabledness
- // Doh^3. Unmanned is no tint too
- if( type != DISABLED_HELD && type != DISABLED_SCRIPT_DISABLED && type != DISABLED_UNMANNED )
- {
- m_drawable->setTintStatus( TINT_STATUS_DISABLED );
- }
- }
- }
- ContainModuleInterface *contain = getContain();
- if ( contain )
- {
- Object *rider = (Object*)contain->friend_getRider();
- if ( rider )
- {
- rider->setDisabledUntil(type, frame);
- }
- }
- if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) )
- {
- SpawnBehaviorInterface *sbi = this->getSpawnBehaviorInterface();
- if ( sbi )
- {
- //Kris: Patch 1.01 - November 12, 2003
- //Actually, we want to disable the slaves, not order them to go idle! This fix was made to
- //stinger sites getting hit by an EMP to prevent the soldiers from attacking.
- //sbi->orderSlavesToGoIdle( CMD_FROM_AI ); // the canattack() will take care of any future attempts to fire
- sbi->orderSlavesDisabledUntil( type, frame );
- }
- }
- }
- if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
- {
- //strange but true: If I am a carbomb,
- //my driver actually has a dead-man's
- //trigger for my dynamite...
- //If he gets sniped, I blow up! Wheeee!
- WeaponSetFlags flags;
- flags.set( WEAPONSET_CARBOMB );
- const WeaponTemplateSet* set = getTemplate()->findWeaponTemplateSet( flags );
- if( set && set->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
- {
- Object* sniper = TheGameLogic->findObjectByID( getBodyModule()->getLastDamageInfo()->in.m_sourceID );
- if ( sniper )
- sniper->scoreTheKill( this );
-
- kill();
- }
- else
- {
- //This vehicle's pilot has been sniped, so we want to clear the veterancy rating (if any)
- ExperienceTracker *xpTracker = getExperienceTracker();
- if( xpTracker )
- {
- xpTracker->setExperienceAndLevel( 0, FALSE );
- }
- //Not only that, but it also loses any healing bonuses it may have earned in its prior life
- {
- static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
- AutoHealBehavior* autoHeal = (AutoHealBehavior*)(findUpdateModule( key_AutoHealBehavior ));
- if (autoHeal)
- autoHeal->undoUpgrade();
-
- }
- }
-
- }
-
- // This will only be called if we were NOT disabled before coming into this function.
- if (edgeCase) {
- onDisabledEdge(true);
- }
- }
- //-------------------------------------------------------------------------------------------------
- UnsignedInt Object::getDisabledUntil( DisabledType type ) const
- {
- if( type == DISABLED_ANY )
- {
- UnsignedInt highestFrame = 0;
- //Iterate through each disabled type and return the one with the highest frame.
- for( Int i = 0; i < DISABLED_COUNT; i++ )
- {
- if( m_disabledMask.test( i ) && m_disabledTillFrame[ i ] > highestFrame )
- {
- highestFrame = m_disabledTillFrame[ i ];
- }
- }
- return highestFrame;
- }
- else if( m_disabledMask.test( type ) )
- {
- //Specific query.
- return m_disabledTillFrame[ type ];
- }
- //Not disabled.
- return 0;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::clearDisabled( DisabledType type )
- {
- if( type < 0 || type >= DISABLED_COUNT )
- {
- DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
- return FALSE;
- }
- if (!isDisabledByType(type)) {
- return FALSE;
- }
- if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_SUBDUED || type == DISABLED_HACKED )
- {
- //We've regained power-- make sure we aren't still disabled by another type.
- AudioEventRTS sound;
- if( (!isDisabledByType( DISABLED_UNDERPOWERED ) || type == DISABLED_UNDERPOWERED ) &&
- (!isDisabledByType( DISABLED_EMP ) || type == DISABLED_EMP ) &&
- (!isDisabledByType( DISABLED_SUBDUED ) || type == DISABLED_SUBDUED ) &&
- (!isDisabledByType( DISABLED_HACKED ) || type == DISABLED_HACKED ) )
- {
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- sound = TheAudio->getMiscAudio()->m_buildingReenabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( isKindOf( KINDOF_VEHICLE ) )
- {
- sound = TheAudio->getMiscAudio()->m_vehicleReenabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- }
- }
- // an edge-test for disabledness, for type. This DECREMENTS m_pauseCount
- // srj sez: HELD nevers disables special powers.
- if ( type != DISABLED_HELD && isDisabledByType( type ) )
- pauseAllSpecialPowers( FALSE );
- ContainModuleInterface *contain = getContain();
- if ( contain )
- {
- // We explicitly pass stuff in up in the set, so we need to turn it off if it is a forever type
- Object *rider = (Object*)contain->friend_getRider();
- if( rider && (m_disabledTillFrame[ type ] == FOREVER) )
- {
- rider->clearDisabled(type);
- }
- }
- if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) )
- {
- SpawnBehaviorInterface *sbi = this->getSpawnBehaviorInterface();
- if ( sbi )
- {
- //Kris: Patch 1.02 - December 17, 2003
- //Make sure slaves can recover from being disabled by subdual (stinger site soldier case)
- sbi->orderSlavesToClearDisabled( type );
- }
- }
- m_disabledTillFrame[ type ] = NEVER;
- m_disabledMask.set( type, 0 );
- DisabledMaskType exceptions;
- exceptions.set(DISABLED_HELD);
- exceptions.set(DISABLED_SCRIPT_DISABLED);
- exceptions.set(DISABLED_UNMANNED);
- DisabledMaskType myFlagsMinusExceptions = getDisabledFlags();
- myFlagsMinusExceptions.clearAndSet(exceptions, DISABLEDMASK_NONE);
- // to clarify, if I am NOT disabled by anything other than DISABLED_HELD, or DISABLED_SCRIPT_DISABLED
-
- // to clarify, count inverse intersection gives you the number of exceptions you don't have,
- // and has nothing to do with checking other disabled types
- // if( !isDisabled() || getDisabledFlags().countInverseIntersection( exceptions ) == 0 )
- if( myFlagsMinusExceptions.count() == 0 )
- {
- // I have no disabled flag that is not one of the exceptions above.
- if (m_drawable)
- m_drawable->clearTintStatus( TINT_STATUS_DISABLED );
- }
- checkDisabledStatus();// in case we just edged
- // if we're no longer disabled by anything, then call the edge function.
- if (!isDisabled()) {
- onDisabledEdge(false);
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- //Checks any timers and clears disabled statii that have expired.
- //-------------------------------------------------------------------------------------------------
- void Object::checkDisabledStatus()
- {
- UnsignedInt now = TheGameLogic->getFrame();
- for( int i = 0; i < DISABLED_COUNT; i++ )
- {
- DisabledType type = (DisabledType)i;
- if( isDisabledByType( type ) )
- {
- if ( now >= m_disabledTillFrame[ i ] )
- {
- clearDisabled( type ); // This will also DECREMENT m_pauseCount in all specialpowers
- m_disabledMask.set( type, 0 );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::pauseAllSpecialPowers( const Bool disabling ) const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- sp->pauseCountdown( disabling );// So it will pause if we are disabling.
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /** Clear the previous entered/exited flags. */
- //-------------------------------------------------------------------------------------------------
- void Object::updateTriggerAreaFlags()
- {
- Int j = 0;
- // Update the flags, and remove any trigger areas that this object isn't inside.
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (!m_triggerInfo[j].isInside)
- continue;
- m_triggerInfo[j].entered = false;
- m_triggerInfo[j].exited = false;
- m_triggerInfo[j].isInside = m_triggerInfo[i].isInside;
- m_triggerInfo[j].pTrigger = m_triggerInfo[i].pTrigger;
- j++;
- }
- m_numTriggerAreasActive = j;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- CollideModuleInterface* collide = (*m)->getCollide();
- if (!collide)
- continue;
- // check each time thru the loop, in case a collide module sets it
- if( getStatusBits().test( OBJECT_STATUS_NO_COLLISIONS ) )
- {
- #ifdef DEBUG_CRC
- //DEBUG_LOG(("Object::onCollide() - OBJECT_STATUS_NO_COLLISIONS set\n"));
- #endif
- break;
- }
- #ifdef DEBUG_CRC
- //DEBUG_LOG(("Object::onCollide() - calling collide module\n"));
- #endif
- collide->onCollide(other, loc, normal);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isSalvageCrate() const
- {
- for( BehaviorModule** m = m_behaviors; *m; ++m )
- {
- CollideModuleInterface* collide = (*m)->getCollide();
- if( collide && collide->isSalvageCrateCollide() )
- {
- return true;
- }
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- Our owning player is telling us to recheck our UpgradeModules, as an upgrade has completed
- */
- void Object::updateUpgradeModules()
- {
- if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
- return; // No upgrade can run if we are under construction. The three places that clear UnderConstruction will re-update us.
-
- if( testStatus( OBJECT_STATUS_DESTROYED ) )
- return; // Patch 1.03 -- Fixes crash when you upgrade a fake GLA command center to a real one if (toxic or demo).
-
- if( getControllingPlayer() == NULL )
- return; // This can only happen in game teardown. No upgrades for you without a player. Weird crashes are bad.
- UpgradeMaskType playerMask = getControllingPlayer()->getCompletedUpgradeMask();
- UpgradeMaskType objectMask = getObjectCompletedUpgradeMask();
- UpgradeMaskType maskToCheck = playerMask;
- maskToCheck.set( objectMask );
- // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
- // We combine all the masks in case someone has a Object AND Player combination
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( !upgrade->isAlreadyUpgraded() )
- {
- upgrade->attemptUpgrade( maskToCheck );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //This function sucks.
- //It was added for objects that can disguise as other objects and contain upgraded subobject overrides.
- //A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
- //made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
- //stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
- //recalculate those upgraded subobjects.
- //-------------------------------------------------------------------------------------------------
- void Object::forceRefreshSubObjectUpgradeStatus()
- {
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( upgrade->isSubObjectsUpgrade() )
- {
- upgrade->forceRefreshUpgrade();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered or exited an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didEnterOrExit() const
- {
- if (isKindOf(KINDOF_INERT)) {
- return FALSE;
- }
- // note that this needs to return true if we
- // entered or exited on the current frame OR
- // the previous frame... since the current execution
- // order is ScriptEngine, then ObjectUpdates,
- // enter/exits detected in ObjectUpdate on frame N
- // won't be noticed by the ScriptEngine till frame N+1.
- UnsignedInt now = TheGameLogic->getFrame();
- return m_enteredOrExitedFrame == now || m_enteredOrExitedFrame == now - 1;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didEnter(const PolygonTrigger *pTrigger) const
- {
- if (!didEnterOrExit())
- return false;
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].entered && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didExit(const PolygonTrigger *pTrigger) const
- {
- if (!didEnterOrExit())
- return false;
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].exited && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object is inside an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::isInside(const PolygonTrigger *pTrigger) const
- {
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert is inside a trigger area. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].isInside && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Get production exit interface in object is present */
- // ------------------------------------------------------------------------------------------------
- ExitInterface *Object::getObjectExitInterface() const
- {
- ExitInterface *exitInterface = NULL;
- for( BehaviorModule **umod = m_behaviors; *umod; ++umod )
- {
- if( (exitInterface = (*umod)->getUpdateExitInterface()) != NULL )
- break;
- }
- // If you don't have a fancy one, you may have one from your contain module,
- // since if you can contain something, they will need to get out.
- if( exitInterface == NULL )
- {
- ContainModuleInterface *cmod = getContain();
- if( cmod )
- {
- exitInterface = cmod->getContainExitInterface();
- }
- }
- return exitInterface;
- } // end getObjectExitInterface
- //-------------------------------------------------------------------------------------------------
- /** Checks the object against trigger areas when the position changes. */
- //-------------------------------------------------------------------------------------------------
- void Object::setTriggerAreaFlagsForChangeInPosition()
- {
- // projectiles cannot trigger areas. (jkmcd)
- // neither can inert objects, like the radar ping, etc. (jkmcd)
- if (isKindOf(KINDOF_PROJECTILE) || isKindOf(KINDOF_INERT))
- return;
- ICoord3D iPos;
- Coord3D pos = *getPosition();
- iPos.x = REAL_TO_INT(pos.x);
- iPos.y = REAL_TO_INT(pos.y);
- iPos.z = 0; // Trigger areas compare on xy only.
- if (m_iPos.x == iPos.x && m_iPos.y == iPos.y)
- {
- return; // didn't move enough to change integer position.
- }
- if (!isKindOf(KINDOF_IMMOBILE)) {
- if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE) ) {
- TheGameClient->notifyTerrainObjectMoved(this);
- }
- }
- if (getAIUpdateInterface())
- {
- TheAI->pathfinder()->updatePos(this, getPosition());
- }
- UnsignedInt now = TheGameLogic->getFrame();
- if (m_enteredOrExitedFrame != 0 && m_enteredOrExitedFrame != now)
- updateTriggerAreaFlags();
- // Check for exited.
- Int i;
- for (i=0; i<m_numTriggerAreasActive; i++)
- {
- if (!m_triggerInfo[i].pTrigger->pointInTrigger(m_iPos))
- {
- m_triggerInfo[i].isInside = false;
- m_triggerInfo[i].exited = true;
- m_enteredOrExitedFrame = now;
- if (m_team)
- m_team->setEnteredExited();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- #ifdef _DEBUG
- //TheScriptEngine->AppendDebugMessage("Object exited.", false);
- #endif
- }
- }
- m_iPos = iPos;
- for (const PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext())
- {
- Bool skip = false;
- for (i = 0; i < m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].pTrigger == pTrig)
- {
- // Already handled this one in the check for exited above.
- skip = true;
- break;
- }
- }
- if (skip)
- continue;
- if (pTrig->pointInTrigger(m_iPos))
- {
- if (m_numTriggerAreasActive < MAX_TRIGGER_AREA_INFOS)
- {
- m_triggerInfo[m_numTriggerAreasActive].isInside = true;
- m_triggerInfo[m_numTriggerAreasActive].entered = true;
- m_triggerInfo[m_numTriggerAreasActive].exited = false;
- m_triggerInfo[m_numTriggerAreasActive].pTrigger = pTrig;
- m_enteredOrExitedFrame = now;
- if (m_team)
- m_team->setEnteredExited();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- ++m_numTriggerAreasActive;
- #ifdef _DEBUG
- //TheScriptEngine->AppendDebugMessage("Object entered.", false);
- #endif
- }
- else
- {
- // Shouldn't happen.
- static Bool didWarn = false;
- if (!didWarn)
- {
- didWarn = true;
- TheScriptEngine->AppendDebugMessage("***WARNING - Too many nested trigger areas. ***", true);
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool Object::isInList(Object **pListHead) const
- {
- Bool result = m_prev || m_next || *pListHead == this;
- #ifdef INTENSE_DEBUG
- Bool found = false;
- for (Object* o = *pListHead; o; o = o->m_next)
- {
- if (o == this)
- {
- found = true;
- break;
- }
- }
- DEBUG_ASSERTCRASH(found==result,("inconsistent links in Object::isInList"));
- #endif
- return result;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::prependToList(Object **pListHead)
- {
- DEBUG_ASSERTCRASH(!isInList(pListHead), ("obj is already in a list"));
- m_prev = NULL;
- m_next = *pListHead;
- if (*pListHead)
- (*pListHead)->m_prev = this;
- *pListHead = this;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setLayer(PathfindLayerEnum layer)
- {
- if (layer!=m_layer) {
- #define no_SET_LAYER_INTENSE_DEBUG
- #ifdef SET_LAYER_INTENSE_DEBUG
- DEBUG_LOG(("Changing layer from %d to %d\n", m_layer, layer));
- if (m_layer != LAYER_GROUND) {
- if (TheTerrainLogic->objectInteractsWithBridgeLayer(this, m_layer)) {
- DEBUG_CRASH(("Probably shouldn't be chaging layer. jba."));
- }
- }
- #endif
- TheAI->pathfinder()->removePos(this);
- m_layer = layer;
- TheAI->pathfinder()->updatePos(this, getPosition());
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setDestinationLayer(PathfindLayerEnum layer)
- {
- if (layer!=m_destinationLayer) {
- m_destinationLayer = layer;
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Set unique ID */
- // ------------------------------------------------------------------------------------------------
- void Object::setID( ObjectID id )
- {
- // sanity
- DEBUG_ASSERTCRASH( id != INVALID_ID, ("Object::setID - Invalid id\n") );
- // if id hasn't changed do nothing
- if( m_id == id )
- return;
-
- // remove this objects previous id from the lookup table
- TheGameLogic->removeObjectFromLookupTable( this );
- // assign new id
- m_id = id;
- // add new id to lookup table
- TheGameLogic->addObjectToLookupTable( this );
- } // end setID
- // ------------------------------------------------------------------------------------------------
- Real Object::calculateHeightAboveTerrain(void) const
- {
- const Coord3D* pos = getPosition();
- Real terrainZ = TheTerrainLogic->getLayerHeight( pos->x, pos->y, m_layer );
- Real myZ = pos->z;
- return myZ - terrainZ;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::removeFromList(Object **pListHead)
- {
- if (m_next)
- m_next->m_prev = m_prev;
- if (m_prev)
- m_prev->m_next = m_next;
- else
- *pListHead = m_next;
- m_prev = NULL;
- m_next = NULL;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::friend_prepareForMapBoundaryAdjust(void)
- {
- // NOTE - DO NOT remove from pathfind map. jba.
- // NO NO. jba. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
- // remove from the radar, remove from the partition manager
- TheRadar->removeObject(this);
- ThePartitionManager->unRegisterObject(this);
- // The whole PartitionManager and all of the Looker data is about to be blown away,
- // so forget what I think I have done
- m_partitionLastLook->reset();
- m_partitionRevealAllLastLook->reset();
- m_partitionLastShroud->reset();
- m_partitionLastThreat->reset();
- m_partitionLastValue->reset();
-
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::friend_notifyOfNewMapBoundary(void)
- {
- ThePartitionManager->registerObject(this);
- TheRadar->addObject(this);
- TheAI->pathfinder()->addObjectToPathfindMap( this );
- // Now that the PartitionManager has finished its reset, we need to relook
- handlePartitionCellMaintenance();
- Region3D mapExtent;
- TheTerrainLogic->getExtent(&mapExtent);
- if (mapExtent.isInRegionNoZ(getPosition()))
- m_privateStatus &= ~OFF_MAP;
- else
- m_privateStatus |= OFF_MAP;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::calcNaturalRallyPoint(Coord2D *pt)
- {
- const Matrix3D *transform = getTransformMatrix();
- Vector3 v;
- //
- // get the natural rally point from the template, this coord is in model space relative
- // to the model (0,0,0)
- //
- /*
- const Coord3D *naturalRallyPoint;
- naturalRallyPoint = m_template->getNaturalRallyPoint();
- v.X = naturalRallyPoint->x;
- v.Y = naturalRallyPoint->y;
- v.Z = naturalRallyPoint->z;
- */
- v.Set( 0, 0, 0 );
- // transform the point into world space
- transform->Transform_Vector( *transform, v, &v );
- // we're only concerned with the 2D elements for now
- pt->x = v.X;
- pt->y = v.Y;
- }
- //-------------------------------------------------------------------------------------------------
- Module* Object::findModule(NameKeyType key) const
- {
- Module* m = NULL;
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- if ((*b)->getModuleNameKey() == key)
- {
- #ifdef INTENSE_DEBUG
- if (m == NULL)
- {
- m = *b;
- }
- else
- {
- DEBUG_CRASH(("Duplicate modules found for name %s!\n",TheNameKeyGenerator->keyToName(key).str()));
- }
- #else
- m = *b;
- break;
- #endif
- }
- }
- return m;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Returns true if object is currently able to move.
- */
- Bool Object::isMobile() const
- {
- if (isKindOf(KINDOF_IMMOBILE))
- return false;
- if( isDisabled() )
- return false;
- return true;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::scoreTheKill( const Object *victim )
- {
- // Do stuff that has nothing to do with experience points here, like tell our Player we killed something
- /// @todo Multiplayer score hook location?
- Player* victimController = victim->getControllingPlayer();
- // if the other player is not a playable side (i.e. they are civilian, observer, whatever)
- // we shouldn't count the kill.
- if (victimController->isPlayableSide() == FALSE)
- {
- return;
- }
-
- if ( victim->isKindOf( KINDOF_IGNORED_IN_GUI ) )
- return;
- Player* controller = getControllingPlayer();
- if (victimController)
- {
- victimController->getScoreKeeper()->addObjectLost(victim);
- }
- Relationship r = getRelationship(victim);
- if (r != ENEMIES)
- return;
- // Don't count kills that I do on my own buildings or units, cause thats just silly.
- if (controller == victimController)
- {
- return;
- }
- if (controller)
- {
- controller->getScoreKeeper()->addObjectDestroyed(victim);
- controller->addSkillPointsForKill(this, victim);
- controller->doBountyForKill(this, victim);
- }
- // Now handle experience, if we can gain any
- if (m_experienceTracker && m_experienceTracker->isAcceptingExperiencePoints())
- {
- // srj sez: per dustin, no experience (et al) for killing things under construction.
- if (!victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
- {
- Int experienceValue = victim->getExperienceTracker()->getExperienceValue( this );
- getExperienceTracker()->addExperiencePoints( experienceValue );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- VeterancyLevel Object::getVeterancyLevel() const
- {
- return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::friend_bindToDrawable( Drawable *draw )
- {
- m_drawable = draw;
- if (m_drawable)
- {
- ModelConditionFlags set;
- ModelConditionFlags clr;
- for (int i = 0; i < WEAPONSET_COUNT; ++i)
- {
- ModelConditionFlagType mcs = TheWeaponSetTypeToModelConditionTypeMap[i];
- if( mcs != MODELCONDITION_INVALID )
- {
- if (m_curWeaponSetFlags.test(i))
- set.set(mcs);
- else
- clr.set(mcs);
- }
- }
- if (TheGlobalData)
- {
- if (TheGlobalData->m_forceModelsToFollowTimeOfDay)
- {
- set.set(MODELCONDITION_NIGHT, (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) ? 1 : 0);
- }
- if (TheGlobalData->m_forceModelsToFollowWeather)
- {
- set.set(MODELCONDITION_SNOW, (TheGlobalData->m_weather == WEATHER_SNOWY) ? 1 : 0);
- }
- }
- m_drawable->clearAndSetModelConditionFlags(clr, set);
- }
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onDrawableBoundToObject();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setSelectable(Bool selectable)
- {
- m_isSelectable = selectable;
- if (m_drawable)
- {
- m_drawable->setSelectable(selectable);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isSelectable() const
- {
- // return getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE)
- // || (m_isSelectable
- // && !testStatus(OBJECT_STATUS_UNSELECTABLE)
- // && !isEffectivelyDead()
- // && !getTemplate()->isKindOf(KINDOF_DRONE)//Most drones are unselectable from being slaved, but the SpyDrone needs help
- // );
-
- if (getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE))
- return TRUE;
- if ( m_isSelectable )
- if ( !testStatus(OBJECT_STATUS_UNSELECTABLE) )
- if ( !isEffectivelyDead() )
- //if ( !getTemplate()->isKindOf(KINDOF_DRONE) )//Most drones are unselectable from being slaved, but the SpyDrone needs help
- return TRUE;
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isMassSelectable() const
- {
- return isSelectable() && !isKindOf(KINDOF_STRUCTURE);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setWeaponSetFlag(WeaponSetType wst)
- {
- m_curWeaponSetFlags.set(wst);
- m_weaponSet.updateWeaponSet(this);
- if (m_drawable)
- {
- m_drawable->setModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::clearWeaponSetFlag(WeaponSetType wst)
- {
- m_curWeaponSetFlags.set(wst, 0);
- m_weaponSet.updateWeaponSet(this);
- if (m_drawable)
- {
- m_drawable->clearModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasSpecialPower( SpecialPowerType type ) const
- {
- return TEST_SPECIALPOWERMASK( m_specialPowerBits, type );
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasAnySpecialPower() const
- {
- return SPECIALPOWERMASK_ANY_SET( m_specialPowerBits );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel, Bool provideFeedback )
- {
- updateUpgradeModules();
- const UpgradeTemplate* up = TheUpgradeCenter->findVeterancyUpgrade(newLevel);
- if (up)
- giveUpgrade(up);
- BodyModuleInterface* body = getBodyModule();
- if (body)
- body->onVeterancyLevelChanged( oldLevel, newLevel, provideFeedback );
-
- Bool hideAnimationForStealth = FALSE;
- if( !isLocallyControlled() &&
- testStatus( OBJECT_STATUS_STEALTHED ) &&
- !testStatus( OBJECT_STATUS_DETECTED ) &&
- !testStatus( OBJECT_STATUS_DISGUISED ) )
- {
- hideAnimationForStealth = TRUE;
- }
- Bool doAnimation = ( ! hideAnimationForStealth
- && (newLevel > oldLevel)
- && ( ! isKindOf(KINDOF_IGNORED_IN_GUI))); //First, we plan to do the animation if the level went up
- switch (newLevel)
- {
- case LEVEL_REGULAR:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- doAnimation = FALSE;//... but not if somehow up to Regular
- break;
- case LEVEL_VETERAN:
- setWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- case LEVEL_ELITE:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- setWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- case LEVEL_HEROIC:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- setWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- }
- if( doAnimation && TheGameLogic->getDrawIconUI() && provideFeedback )
- {
- if( TheAnim2DCollection && TheGlobalData->m_levelGainAnimationName.isEmpty() == FALSE )
- {
- Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_levelGainAnimationName );
- Coord3D pos = *getPosition();
- pos.add(&m_healthBoxOffset);
- TheInGameUI->addWorldAnimation( animTemplate,
- &pos,
- WORLD_ANIM_FADE_ON_EXPIRE,
- TheGlobalData->m_levelGainAnimationDisplayTimeInSeconds,
- TheGlobalData->m_levelGainAnimationZRisePerSecond);
- }
- AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_unitPromoted;
- soundToPlay.setObjectID( getID() );
- TheAudio->addAudioEvent( &soundToPlay );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Returns true if object currently has some kind of attack capability
- */
- Bool Object::isAbleToAttack() const
- {
- //******************************************************
- //********* AUTOMATICALLY FALSE CONDITIONS *************
- //******************************************************
- // For things that may or may not be able to normally attack, but are under a status condition
- if( getStatusBits().test( OBJECT_STATUS_NO_ATTACK ) )
- return false;
- // if we're contained within a transport we cannot attack unless it specifically allows us
- const Object *containedBy = getContainedBy();
- DEBUG_ASSERTCRASH( (containedBy == NULL) || (containedBy->getContain() != NULL), ("A %s thinks they are contained by something with no contain module!", getTemplate()->getName().str() ) );
- if( containedBy && containedBy->getContain() && !containedBy->getContain()->isPassengerAllowedToFire( getID() ) )
- return false;
-
- // We can't fire if under construction
- if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
- return false;
- // or being sold
- if( testStatus(OBJECT_STATUS_SOLD) )
- return false;
- if ( isDisabledByType( DISABLED_SUBDUED ) )
- return FALSE; // A Microwave Tank is cooking me
- //We can't fire if we, as a portable structure, are aptly disabled
- if ( isKindOf( KINDOF_PORTABLE_STRUCTURE ) || isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ))
- {
- if( isDisabledByType( DISABLED_HACKED ) || isDisabledByType( DISABLED_EMP ) )
- return false;
- if ( isKindOf( KINDOF_INFANTRY ) ) // I must be a stinger soldier or similar
- {
- for (BehaviorModule** update = getBehaviorModules(); *update; ++update)//expensive search, limited only to stinger soldiers
- {
- SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
- if ( sdu )
- {
- ObjectID slaverID = sdu->getSlaverID();
- if ( slaverID != INVALID_ID )
- {
- Object *slaver = TheGameLogic->findObjectByID( slaverID );
- if ( slaver && slaver->isDisabledByType( DISABLED_SUBDUED ))
- return FALSE;// if my stinger site is subdued, so am I
- }
- break;//only expect one slavedupdate, so stop searching
- }
- }
- }
- }
-
- //We can't fire if all our weapons are disabled!
- //Currently, only turreted weapons can be disabled.
- //ONLY DO THIS CHECK IF OUR UNIT DOESN'T HAVE THE
- //KINDOF_CAN_ATTACK flag... nuke cannons have disabled
- //turrets when not deployed, and need to be able to attack to deploy!
- //Strategy centers can't attack when bombardment isn't active!
- Bool anyEnabled = FALSE;
- Bool anyWeapon = FALSE;
- const AIUpdateInterface *ai = getAI();
- if( ai && !isKindOf( KINDOF_CAN_ATTACK ) )
- {
- for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
- {
- //Find the weapon in this slot.
- Weapon* weapon = getWeaponInWeaponSlot( (WeaponSlotType)i );
- if( !weapon )
- continue;
- anyWeapon = TRUE;
-
- //We found a weapon, is it a turret?
- Real dummy;
- WhichTurretType tur = ai->getWhichTurretForWeaponSlot( (WeaponSlotType)i, &dummy );
- if( tur == TURRET_INVALID )
- {
- //Currently impossible to disable a non-turreted weapon, so we
- //have a non turreted weapon that is enabled. Quit.
- anyEnabled = TRUE;
- break;
- }
-
- if( ai->isTurretEnabled( tur ) )
- {
- //The turret is enable, meaning we have an enabled weapon. Quit.
- anyEnabled = TRUE;
- break;;
- }
- }
- if( anyWeapon && !anyEnabled )
- {
- //We failed to find any active weapons.
- return FALSE;
- }
- }
- //***************************************
- //********* TRUE CONDITIONS *************
- //***************************************
- // for certain buildings
- if (isKindOf(KINDOF_CAN_ATTACK))
- return true;
- // for garrisonned buildings that can attack sometimes
- if( getStatusBits().test( OBJECT_STATUS_CAN_ATTACK ) )
- return true;
-
- // for weaponless transports. This will make me think I can, but I will check if I literally can by looking
- // at passenger weapons in CanAttack.
- const ContainModuleInterface* contain = getContain();
- if( contain && contain->isPassengerAllowedToFire( getID() ) && contain->getContainCount() > 0 )
- return true;
- // if we have AI and a weapon, assume we know how to use it
- if (getAIUpdateInterface() != NULL && m_weaponSet.hasAnyWeapon())
- {
- // actually, we don't want to do this; we want the troop crawler to be considered "able to attack"
- // even if empty, so sayeth Dustin. (srj)
- // // special case: if the only damage we do is DEPLOY, we must have some guys contained.
- // if (m_weaponSet.hasSingleDamageType(DAMAGE_DEPLOY))
- // {
- // return contain->getContainCount() > 0;
- // }
- // else
- {
- return true;
- }
- }
- SpawnBehaviorInterface *spawnInterface = getSpawnBehaviorInterface();
- if( spawnInterface )
- {
- if( spawnInterface->canAnySlavesAttack() )
- {
- return TRUE;
- }
- }
- if (getTemplate()->isEnterGuard())
- return TRUE;
- //Default is no
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Mask/Un-Mask an object
- */
- void Object::maskObject( Bool mask )
- {
- // set or clear the mask bit
- setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ), mask );
- //
- // when masking objects they become unselected ... we do this in any situation for
- // any player cause you aren't allowed to select masked objects, if the object is not
- // selected (ie, belongs to another player) it's no big deal cause it won't be selected
- // anyway
- //
- if (mask)
- TheGameLogic->deselectObject(this, ~getControllingPlayer()->getPlayerMask(), TRUE);
- } // end maskObject
- //-------------------------------------------------------------------------------------------------
- /*
- * returns true if the current locomotor is an airborne one
- */
- Bool Object::isUsingAirborneLocomotor( void ) const
- {
- return ( m_ai && m_ai->getCurLocomotor() && ((m_ai->getCurLocomotor()->getLegalSurfaces() & LOCOMOTORSURFACE_AIR) != 0) );
- }
- //-------------------------------------------------------------------------------------------------
- //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
- //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
- //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
- //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
- void Object::getHealthBoxPosition(Coord3D& pos) const
- {
- pos = *getPosition();
- pos.z += getGeometryInfo().getMaxHeightAbovePosition() + 10;
- pos.add(&m_healthBoxOffset);
- // this needs to get moved to the mobspawnerupdate
- if (isKindOf(KINDOF_MOB_NEXUS)) // quicker idiot test
- {
- pos.z += 20;// dear God, I confess my kluge, and repent.
- }
- }
- //-------------------------------------------------------------------------------------------------
- //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
- //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
- //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
- //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
- Bool Object::getHealthBoxDimensions(Real &healthBoxHeight, Real &healthBoxWidth) const
- {
- #ifdef CALC_HEALTHBAR_FROM_HITPOINTS
- Real maxHP = getBodyModule()->getMaxHealth();
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(150.0f, max(100.0f, maxHP/10));
- return true;
- }
- else if ( isKindOf(KINDOF_MOB_NEXUS) )
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(100.0f, max(66.0f, maxHP/10));
- return true;
- }
- else if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
- {
- healthBoxHeight = 0;
- healthBoxWidth = 0;
- return false;
- }
- else
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(150.0f, max(35.0f, maxHP/10));
- return true;
- }
- #else
- if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
- {
- healthBoxHeight = 0;
- healthBoxWidth = 0;
- return false;
- }
- //just add the major and minor axes
- Real size = MAX(20.0f, MIN(150.0f, (getGeometryInfo().getMajorRadius() + getGeometryInfo().getMinorRadius())) );
- healthBoxHeight = 3.0f;
- healthBoxWidth = MAX(20.0f, size * 2.0f);
- return TRUE;
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Update this object instance with properties from the map object
- *
- */
- void Object::updateObjValuesFromMapProperties(Dict* properties)
- {
- Bool exists;
- AsciiString valStr;
- Bool valBool = false;
- Int valInt = 0;
- Real valReal = 0.0f;
- valStr = properties->getAsciiString(TheKey_objectName, &exists);
- if (exists) {
- setName(valStr);
- }
- valInt = properties->getInt(TheKey_objectMaxHPs, &exists);
- if (exists && valInt >= 0) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setMaxHealth(valInt);
- }
- }
- valInt = properties->getInt(TheKey_objectInitialHealth, &exists);
- if (exists) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setInitialHealth(valInt);
- }
- }
- // set the veterancy level
- valInt = properties->getInt(TheKey_objectVeterancy, &exists);
- if (exists) {
- if (m_experienceTracker && m_experienceTracker->isTrainable())
- {
- m_experienceTracker->setVeterancyLevel((VeterancyLevel)valInt);
- }
- }
- // set the aggressiveness/mood
- valInt = properties->getInt(TheKey_objectAggressiveness, &exists);
- if (exists) {
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai)
- {
- ai->setAttitude((AttitudeType)valInt);
- }
- }
-
- // set recruitable
- valBool = properties->getBool(TheKey_objectRecruitableAI, &exists);
- if (exists) {
- if (getAIUpdateInterface())
- {
- getAIUpdateInterface()->setIsRecruitable(valBool);
- }
- }
-
- // set selectable
- valBool = properties->getBool(TheKey_objectSelectable, &exists);
- if (exists) {
- if (valBool != isSelectable()) {
- setSelectable(valBool);
- }
- }
-
- // set the stopping distance
- valReal = properties->getReal(TheKey_objectStoppingDistance, &exists);
- if (exists && valReal >= 0.5f)
- {
- if (getAIUpdateInterface() && getAIUpdateInterface()->getCurLocomotor())
- {
- Locomotor *loco = getAIUpdateInterface()->getCurLocomotor();
- loco->setCloseEnoughDist(valReal);
- }
- }
-
- // set the disabledness of this object
- valBool = properties->getBool(TheKey_objectEnabled, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_DISABLED, !valBool);
- }
- // set the disabledness of this object
- valBool = properties->getBool(TheKey_objectPowered, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_UNPOWERED, !valBool);
- }
- // set the invulnerability of the object
- valBool = properties->getBool(TheKey_objectIndestructible, &exists);
- if (exists) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setIndestructible(valBool);
- }
- }
- // set the sellability of the object
- valBool = properties->getBool(TheKey_objectUnsellable, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE, valBool);
- }
- //Set the player targetable setting of the object
- valBool = properties->getBool( TheKey_objectTargetable, &exists );
- if( exists )
- {
- setScriptStatus(OBJECT_STATUS_SCRIPT_TARGETABLE, valBool);
- }
- // adjust the vision distance of this object, overriding its default vision distance
- valInt = properties->getInt(TheKey_objectVisualRange, &exists);
- if (exists)
- {
- if (valInt < 0)
- valInt = 0;
- m_visionRange = INT_TO_REAL(valInt);
- }
- // adjust the shroud clearing distance of this object, overriding its default distance
- valInt = properties->getInt(TheKey_objectShroudClearingDistance, &exists);
- if (exists)
- {
- if (valInt < 0)
- valInt = 0.0f;
- m_shroudClearingRange = INT_TO_REAL(valInt);
- }
- Int upgradeNum = 0;
- do
- {
- AsciiString keyName;
- keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_objectGrantUpgrade).str(), upgradeNum);
- valStr = properties->getAsciiString(NAMEKEY(keyName), &exists);
- if (exists)
- {
- const UpgradeTemplate *ut = TheUpgradeCenter->findUpgrade(valStr);
- if (ut)
- giveUpgrade(ut);
- }
- else
- {
- valStr.clear();
- }
- ++upgradeNum;
- } while (!valStr.isEmpty());
-
- Drawable *drawable = getDrawable();
- if ( drawable )
- {
- valInt = properties->getInt(TheKey_objectTime, &exists);
- if (exists)
- {
- switch (valInt)
- {
- case 1:
- drawable->clearModelConditionState(MODELCONDITION_NIGHT);
- break;
- case 2:
- drawable->setModelConditionState(MODELCONDITION_NIGHT);
- break;
- default:
- break;
- }
- }
-
- valInt = properties->getInt(TheKey_objectWeather, &exists);
- if (exists)
- {
- switch (valInt)
- {
- case 1:
- drawable->clearModelConditionState(MODELCONDITION_SNOW);
- break;
- case 2:
- drawable->setModelConditionState(MODELCONDITION_SNOW);
- break;
- default:
- break;
- }
- }
-
- // See if we are supposed to playing the ambient sound
- Bool soundEnabledExists;
- Bool soundEnabled = properties->getBool( TheKey_objectSoundAmbientEnabled, &soundEnabledExists );
-
- DynamicAudioEventInfo * audioToModify = NULL;
- Bool infoModified = false;
- valStr = properties->getAsciiString( TheKey_objectSoundAmbient, &exists );
- if ( exists )
- {
- if ( valStr.isEmpty() )
- {
- drawable->setCustomSoundAmbientOff();
- soundEnabledExists = true;
- soundEnabled = false; // Don't bother trying to enable later
- }
- else
- {
- const AudioEventInfo * baseInfo = TheAudio->findAudioEventInfo( valStr );
- DEBUG_ASSERTCRASH( baseInfo != NULL, ("Cannot find customized ambient sound '%s'", valStr.str() ) );
- if ( baseInfo != NULL )
- {
- audioToModify = newInstance( DynamicAudioEventInfo )( *baseInfo );
- infoModified = true;
- }
- }
- }
- // Don't do anything more to audio if we forced the ambient sound off
- if ( !( exists && valStr.isEmpty() ) )
- {
- valBool = properties->getBool( TheKey_objectSoundAmbientCustomized, &exists );
- if ( exists && valBool )
- {
- if ( audioToModify == NULL )
- {
- const AudioEventInfo * baseInfo = drawable->getBaseSoundAmbientInfo( );
- DEBUG_ASSERTCRASH( baseInfo != NULL, ("getBaseSoundAmbientInfo() return NULL" ) );
- if ( baseInfo != NULL )
- {
- audioToModify = newInstance( DynamicAudioEventInfo )( *baseInfo );
- }
- }
- if ( audioToModify != NULL )
- {
- valBool = properties->getBool( TheKey_objectSoundAmbientLooping, &exists );
- if ( exists )
- {
- audioToModify->overrideLoopFlag( valBool );
- infoModified = true;
- }
- valInt = properties->getInt( TheKey_objectSoundAmbientLoopCount, &exists );
- if ( exists && BitTest( audioToModify->m_control, AC_LOOP ) )
- {
- audioToModify->overrideLoopCount( valInt );
- infoModified = true;
- }
- valReal = properties->getReal( TheKey_objectSoundAmbientMinVolume, &exists );
- if ( exists )
- {
- audioToModify->overrideMinVolume( valReal );
- infoModified = true;
- }
- valReal = properties->getReal( TheKey_objectSoundAmbientVolume, &exists );
- if ( exists )
- {
- audioToModify->overrideVolume( valReal );
- infoModified = true;
- }
- valReal = properties->getReal( TheKey_objectSoundAmbientMinRange, &exists );
- if ( exists )
- {
- audioToModify->overrideMinRange( valReal );
- infoModified = true;
- }
-
- valReal = properties->getReal( TheKey_objectSoundAmbientMaxRange, &exists );
- if ( exists )
- {
- audioToModify->overrideMaxRange( valReal );
- infoModified = true;
- }
- valInt = properties->getInt( TheKey_objectSoundAmbientPriority, &exists );
- if ( exists )
- {
- audioToModify->overridePriority ( (AudioPriority)valInt );
- infoModified = true;
- }
- }
- }
- }
- if ( !soundEnabledExists )
- {
- // Decide if the sound should start enabled or not, since the map maker didn't record
- // a preference. Enable permanently looping sounds, disable one-shot sounds by default
- // NOTE: This test should match the tests done in MapObjectProps::mapObjectPageSound::dictToEnabled()
- // when it decided whether or not to show a customized sound as enabled
- if ( audioToModify != NULL )
- {
- soundEnabled = audioToModify->isPermanentSound();
- soundEnabledExists = true; // To get into enableAmbientSoundFromScript() call.
- }
- else
- {
- // Use default audio
- const AudioEventInfo * baseInfo = drawable->getBaseSoundAmbientInfo( );
- if ( baseInfo != NULL )
- {
- soundEnabled = baseInfo->isPermanentSound();
- soundEnabledExists = true; // To get into enableAmbientSoundFromScript() call.
- }
- }
- }
- if ( soundEnabledExists && !soundEnabled )
- {
- // Make sure sound doesn't start playing when we set it
- // ...FromScript because this is also controlled by the map designer not the game logic
- drawable->enableAmbientSoundFromScript( false );
- }
-
- if ( infoModified && audioToModify != NULL )
- {
- // Give a custom, level-specific name
- drawable->mangleCustomAudioName( audioToModify );
- // Pass to TheAudio
- TheAudio->addAudioEventInfo( audioToModify );
- drawable->setCustomSoundAmbientInfo( audioToModify );
- audioToModify = NULL; // Belongs to TheAudio now
- }
- if ( audioToModify != NULL )
- {
- audioToModify->deleteInstance();
- audioToModify = NULL;
- }
- if ( soundEnabledExists && soundEnabled )
- {
- // Play sound now that it is set up, if needed. Don't call if already enabled because that
- // can cause sound to play twice
- // ...FromScript because this is also controlled by the map designer not the game logic
- if ( !drawable->getAmbientSoundEnabledFromScript() )
- {
- drawable->enableAmbientSoundFromScript( true );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::friend_adjustPowerForPlayer( Bool incoming )
- {
- if (isDisabled() && getTemplate()->getEnergyProduction() > 0)
- {
- // Disabledness only affects Producers, not Consumers.
- return;
- }
- if (incoming) {
- getControllingPlayer()->getEnergy()->objectEnteringInfluence(this);
- } else {
- getControllingPlayer()->getEnergy()->objectLeavingInfluence(this);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- void Object::onDisabledEdge(Bool becomingDisabled)
- {
- // rip through the behavior modules and call the onDisabledEdge for any modules that care
- for( BehaviorModule **module = m_behaviors; *module; ++module )
- (*module)->onDisabledEdge( becomingDisabled );
- DozerAIInterface *dozerAI = getAI() ? getAI()->getDozerAIInterface() : NULL;
- if( becomingDisabled && dozerAI )
- {
- // Have to say goodbye to the thing we might be building or repairing so someone else can do it.
- if( dozerAI->getCurrentTask() != DOZER_TASK_INVALID )
- dozerAI->cancelTask( dozerAI->getCurrentTask() );
- }
- Player* controller = getControllingPlayer();
- // can be called during game teardown, thus controller can be null
- if (controller)
- {
- //@todo jkmcd - Colin suggested we rewrite this to use the interface stuff. I agree, but need
- // to get some more bugs fixed today.
- static NameKeyType radar = NAMEKEY("RadarUpgrade");
- Module *mod = mod = findModule(radar);
- if (mod) {
- RadarUpgrade *radarMod = (RadarUpgrade*) mod;
- if (radarMod->isAlreadyUpgraded()) {
- // Need to decrement the count here, because we own a radar upgrade
- if (becomingDisabled) {
- controller->removeRadar(radarMod->getIsDisableProof());
- } else {
- controller->addRadar(radarMod->getIsDisableProof());
- }
- }
- }
- }
- // We will need to adjust power ... somehow ...
- Int powerToAdjust = getTemplate()->getEnergyProduction();
-
- if( powerToAdjust > 0 )
- {
- // We can't affect something that consumes, or else we go low power which removes the consumption
- // which makes us not low power so we add the consumption so we go low power...
- // This check also guaards the IsDisabled in friend_adjustPower above
- static NameKeyType powerPlant = NAMEKEY("PowerPlantUpgrade");
- static NameKeyType overCharge = NAMEKEY("OverchargeBehavior");
-
- Module* mod = findModule(powerPlant);
- if (mod) {
- PowerPlantUpgrade *powerPlantMod = (PowerPlantUpgrade*) mod;
- if (powerPlantMod->isAlreadyUpgraded()) {
- powerToAdjust += getTemplate()->getEnergyBonus();
- }
- }
-
- mod = findModule(overCharge);
- if (mod) {
- OverchargeBehavior *overChargeMod = (OverchargeBehavior*) mod;
- if (overChargeMod->isOverchargeActive()) {
- powerToAdjust += getTemplate()->getEnergyBonus();
- }
- }
-
- // Now, adjust the power for the player.
- if (controller)
- controller->getEnergy()->adjustPower(powerToAdjust, !becomingDisabled);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Object CRC implemtation */
- //-------------------------------------------------------------------------------------------------
- void Object::crc( Xfer *xfer )
- {
- // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use
- // the same interface as the XferLoad class for save game restore. This only works because
- // XferCRC does not modify its data.
- #ifdef DEBUG_CRC
- // g_logObjectCRCs = TRUE;
- // Bool g_logAllObjects = TRUE;
- AsciiString logString;
- AsciiString tmp;
- Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */;
- if (doLogging)
- {
- tmp.format("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex());
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- xfer->xferUnsignedByte(&m_privateStatus);
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_privateStatus: %X, ", (UnsignedInt)m_privateStatus);
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- xfer->xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- XferCRC tmpXfer;
- tmpXfer.open("tmp");
- tmpXfer.xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
- tmp.format("getTransformMatrix(): %8.8X, ", tmpXfer.getCRC());
- tmpXfer.close();
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
-
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- const Matrix3D *mtx = getTransformMatrix();
- CRCDEBUG_LOG(("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex()));
- DUMPMATRIX3D(mtx);
- }
- #endif DEBUG_CRC
- xfer->xferUser(&m_id, sizeof(m_id));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_id: %d, ", m_id);
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- xfer->xferUser(&m_objectUpgradesCompleted, sizeof(Int64));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_objectUpgradesCompleted: %I64X, ", m_objectUpgradesCompleted);
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- if (m_experienceTracker)
- xfer->xferSnapshot( m_experienceTracker );
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- XferCRC tmpXfer;
- tmpXfer.open("tmp");
- tmpXfer.xferSnapshot(m_experienceTracker);
- tmp.format("m_experienceTracker: %8.8X, ", tmpXfer.getCRC());
- tmpXfer.close();
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- Real health = getBodyModule()->getHealth();
- xfer->xferUser(&health, sizeof(health));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("health: %g/%8.8X, ", health, AS_INT(health));
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- xfer->xferUnsignedInt(&m_weaponBonusCondition);
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_weaponBonusCondition: %8.8X, ", m_weaponBonusCondition);
- logString.concat(tmp);
- }
- #endif DEBUG_CRC
- Real scalar = getBodyModule()->getDamageScalar();
- xfer->xferUser(&scalar, sizeof(scalar));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("damage scalar: %g/%8.8X\n", scalar, AS_INT(scalar));
- logString.concat(tmp);
- CRCDEBUG_LOG(("%s", logString.str()));
- }
- #endif DEBUG_CRC
- for (Int i=0; i<WEAPONSLOT_COUNT; ++i)
- {
- Weapon *thisWeapon = getWeaponInWeaponSlot((WeaponSlotType)i);
- if (thisWeapon)
- {
- xfer->xferSnapshot( thisWeapon );
- }
- }
-
- } // end crc
- //-------------------------------------------------------------------------------------------------
- /** Object xfer implemtation
- * Version Info:
- * 1: Initial version
- * 2: Xfers m_singleUseCommandUsed... determines if the single use command button has been used or not.
- * 3: Xfers the solehealingbenefactor ID and expiration frame
- * 4: misc stuff that got missed somehow
- * 5: m_isReceivingDifficultyBonus
- * 6: We do indeed need to save m_containedBy. The comment misrepresents what the contain module will do.
- * 7: save full mtx, not pos+orient.
- * 8: Kris: Conversion of object status bits from UnsignedInt to BitFlags<>
- * 9: Extra sighting for reveal to all with different range units
- */
- //-------------------------------------------------------------------------------------------------
- void Object::xfer( Xfer *xfer )
- {
-
- // version
- const XferVersion currentVersion = 9;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // object ID
- ObjectID id = getID();
- xfer->xferObjectID( &id );
- setID( id );
- DEBUG_LOG(("Xfer Object %s id=%d\n",getTemplate()->getName().str(),id));
- if (version >= 7)
- {
- Matrix3D mtx = *getTransformMatrix();
- xfer->xferMatrix3D(&mtx);
- setTransformMatrix(&mtx);
- }
- else
- {
- // object position
- Coord3D pos = *getPosition();
- xfer->xferCoord3D( &pos );
- setPosition( &pos );
- // orientation
- Real orientation = getOrientation();
- xfer->xferReal( &orientation );
- setOrientation( orientation );
- }
- // team
- TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
- xfer->xferUser( &teamID, sizeof( TeamID ) );
- // DON'T set the team yet; must wait till we read our status bits,
- // since setTeam can affect the player's power usage, but that could
- // be done incorrectly if our status bits aren't accurate yet... (srj)
- // producer id
- xfer->xferObjectID( &m_producerID );
- // builder id
- xfer->xferObjectID( &m_builderID );
- // drawable id
- Drawable *draw = getDrawable();
- DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
- xfer->xferDrawableID( &drawableID );
- if( xfer->getXferMode() == XFER_LOAD )
- {
- // change the ID of the drawable attached to be the same ID as it was when it was saved
- draw->setID( drawableID );
- } // end if
- // internal name
- xfer->xferAsciiString( &m_name );
- // status
- if( version >= 8 )
- {
- m_status.xfer( xfer );
- }
- else
- {
- //We are loading an old version, so we must convert it from a 32-bit int to a bitflag
- UnsignedInt oldStatus;
- xfer->xferUnsignedInt( &oldStatus );
- //Clear our status
- m_status.clear();
- for( int i = 0; i < 32; i++ )
- {
- UnsignedInt bit = 1<<i;
- if( oldStatus & bit )
- {
- ObjectStatusTypes status = (ObjectStatusTypes)(i+1);
- m_status.set( MAKE_OBJECT_STATUS_MASK( status ) );
- }
- }
- }
- // script status
- xfer->xferUnsignedByte( &m_scriptStatus );
- // private status
- xfer->xferUnsignedByte( &m_privateStatus );
- // OK, now that we have xferred our status bits, it's safe to set the team...
- if( xfer->getXferMode() == XFER_LOAD )
- {
- Team *team = TheTeamFactory->findTeamByID( teamID );
- if( team == NULL )
- {
- DEBUG_CRASH(( "Object::xfer - Unable to load team\n" ));
- throw SC_INVALID_DATA;
- }
- const Bool restoring = true;
- setOrRestoreTeam( team, restoring );
- }
- // geometry info
- xfer->xferSnapshot( &m_geometryInfo );
- // sighting info, last look - must be saved cause we save PartitionCell::m_shroudLevel
- xfer->xferSnapshot( m_partitionLastLook );
- if( version >= 9 )
- xfer->xferSnapshot( m_partitionRevealAllLastLook );
- // sighting info, last shroud - must be saved cause we save PartitionCell::m_shroudLevel
- xfer->xferSnapshot( m_partitionLastShroud );
- // vision spied by
- xfer->xferUser( m_visionSpiedBy, sizeof( Int ) * MAX_PLAYER_COUNT );
- // vision spied by mask
- xfer->xferUser( &m_visionSpiedMask, sizeof( PlayerMaskType ) );
- // sighting info, last threat
- // John M says we don't need to save this (CBD)
- // xfer->xferSnapshot( &m_partitionLastThreat );
- // sighting info, last value
- // John M says we don't need to save this (CBD)
- // xfer->xferSnapshot( &m_partitionLastValue );
- // vision range
- xfer->xferReal( &m_visionRange );
- // shroud clearing range
- xfer->xferReal( &m_shroudClearingRange );
- // shroud range
- xfer->xferReal( &m_shroudRange );
- // disabled mask
- m_disabledMask.xfer( xfer );
- //New var added for version 2. Determines if the single use command button has been used or not.
- if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
- {
- xfer->xferBool( &m_singleUseCommandUsed );
- }
- else
- {
- m_singleUseCommandUsed = false;
- }
- // disabled till frame
- xfer->xferUser( m_disabledTillFrame, sizeof( UnsignedInt ) * DISABLED_COUNT );
- // special model condition until
- xfer->xferUnsignedInt( &m_smcUntil );
- //
- // radar data ... when loading, we will remove all objects from the radar and let
- // the radar system load itself as a separate chunk of data from the save file
- //
- if( xfer->getXferMode() == XFER_LOAD && m_radarData )
- TheRadar->removeObject( this );
- // experience tracker
- xfer->xferSnapshot( m_experienceTracker );
- //
- // we do not need to do anything with our m_containedBy pointer, the post process
- // of that objects contain module will actually re-do the contain process again
- //
- // m_containedBy <-- do nothing with this right now
- if( version >= 6 )
- {
- // No, the contain module is just going to friend_ reach in and set this for us.
- // Containers more complicated than Open (like Tunnel) can't do that. Our variable,
- // our responsibility.
- if( xfer->getXferMode() == XFER_SAVE )
- {
- if( m_containedBy != NULL )
- m_xferContainedByID = m_containedBy->getID();
- else
- m_xferContainedByID = INVALID_ID;
- }
- xfer->xferObjectID( &m_xferContainedByID );
- }
- // contained by frame
- xfer->xferUnsignedInt( &m_containedByFrame );
- // construction percent
- xfer->xferReal( &m_constructionPercent );
- // upgrades completed
- xfer->xferUpgradeMask( &m_objectUpgradesCompleted );
- // original team name
- xfer->xferAsciiString( &m_originalTeamName );
- // indicator color
- xfer->xferColor( &m_indicatorColor );
- // health box offset
- xfer->xferCoord3D( &m_healthBoxOffset );
- // Entered & exited housekeeping.
- Int i;
- xfer->xferByte(&m_numTriggerAreasActive);
- xfer->xferUnsignedInt(&m_enteredOrExitedFrame);
- xfer->xferICoord3D(&m_iPos);
- if (m_numTriggerAreasActive<0 || m_numTriggerAreasActive>MAX_TRIGGER_AREA_INFOS) {
- DEBUG_CRASH(("Invalid m_numTriggerAreasActive = %d, max is %d", m_numTriggerAreasActive,
- MAX_TRIGGER_AREA_INFOS));
- throw SC_INVALID_DATA;
- }
- for (i=0; i<m_numTriggerAreasActive; i++) {
- AsciiString triggerName;
- if (m_triggerInfo[i].pTrigger) {
- triggerName = m_triggerInfo[i].pTrigger->getTriggerName();
- }
- xfer->xferAsciiString(&triggerName);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- //
- // CBD (11-13-2002) I'm disabling this because it appears there might be some areas with
- // empty names, see John A. for more info
- //
- //if (triggerName.isNotEmpty())
- m_triggerInfo[i].pTrigger = TheTerrainLogic->getTriggerAreaByName(triggerName);
- }
- xfer->xferByte(&m_triggerInfo[i].entered);
- xfer->xferByte(&m_triggerInfo[i].exited);
- xfer->xferByte(&m_triggerInfo[i].isInside);
- }
- // Layer object is pathing on.
- xfer->xferUser(&m_layer, sizeof(m_layer));
- // Layer of current path goal.
- xfer->xferUser(&m_destinationLayer, sizeof(m_destinationLayer));
- // Object selectability.
- xfer->xferBool(&m_isSelectable);
- xfer->xferUnsignedInt(&m_safeOcclusionFrame);
- // User formations.
- xfer->xferUser(&m_formationID, sizeof(m_formationID));
- if (m_formationID!=NO_FORMATION_ID) {
- xfer->xferCoord2D(&m_formationOffset);
- }
- // module count
- UnsignedShort moduleCount = 0;
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- ++moduleCount;
- xfer->xferUnsignedShort( &moduleCount );
- AsciiString moduleIdentifier;
- BehaviorModule *module;
- if( xfer->getXferMode() == XFER_SAVE )
- {
- // go through all modules
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- // get module
- module = *b;
- // write module identifier
- moduleIdentifier = TheNameKeyGenerator->keyToName( module->getModuleTagNameKey() );
- DEBUG_ASSERTCRASH( moduleIdentifier != AsciiString::TheEmptyString,
- ("Object::xfer - Module tag key does not translate to a string!\n") );
- xfer->xferAsciiString( &moduleIdentifier );
- // begin a data block
- xfer->beginBlock();
- // xfer data
- xfer->xferSnapshot( module );
- // end data block
- xfer->endBlock();
- } // end for, it
- } // end if, save
- else
- {
- AsciiString otherModuleIdentifier;
- // read all module data
- for( UnsignedShort i = 0; i < moduleCount; ++i )
- {
- // read module name
- xfer->xferAsciiString( &moduleIdentifier );
- NameKeyType moduleIdentifierKey = TheNameKeyGenerator->nameToKey(moduleIdentifier);
- // find the module with this identifier in the module list
- module = NULL;
- for (BehaviorModule** b = m_behaviors; b && *b; ++b)
- {
- if (moduleIdentifierKey == (*b)->getModuleTagNameKey())
- {
- module = *b;
- break;
- }
- } // end for, moduleIt
- // start of a new block
- Int dataSize = xfer->beginBlock();
- //
- // if we didn't find the module, it's quite possible that we have removed
- // it from the object definition in a future patch, if that is so, we need to
- // skip the module data in the file
- //
- if( module == NULL )
- {
- // for testing purposes, this module better be found
- // DEBUG_CRASH(( "Object::xfer - Module '%s' was indicated in file, but not found on object '%s'(%d)\n",
- // moduleIdentifier.str(), getTemplate()->getName().str(), getID() ));
- // skip this data in the file
- xfer->skip( dataSize );
- } // end if
- else
- {
- // xfer the data into this module
- xfer->xferSnapshot( module );
- } // end else
-
- // end block
- xfer->endBlock();
- } // end for, i module count recorded in file
- } // end else, load
- if ( version >= 3 )
- {
- xfer->xferObjectID( &m_soleHealingBenefactorID );
- xfer->xferUnsignedInt( &m_soleHealingBenefactorExpirationFrame );
- }
- else if ( xfer->getXferMode() == XFER_LOAD )
- {
- m_soleHealingBenefactorID = INVALID_ID;
- m_soleHealingBenefactorExpirationFrame = 0;
- }
- // Doesn't need to be saved. These are created as needed. jba.
- //AIGroup* m_group; ///< if non-NULL, we are part of this group of agents
- // don't need to save m_partitionData.
- DEBUG_ASSERTCRASH(!(xfer->getXferMode() == XFER_LOAD && m_partitionData == NULL), ("should not be in partitionmgr yet"));
- // don't need to be saved or loaded; are inited & cached for runtime only by our ctor (srj)
- //m_repulsorHelper;
- //m_smcHelper;
- //m_wsHelper;
- //m_defectionHelper;
- //m_firingTracker;
- //m_contain;
- //m_body;
- //m_ai;
- //m_physics;
- #if defined(_DEBUG) || defined(_INTERNAL)
- //m_hasDiedAlready;
- #endif
- if (version >= 4)
- {
- // xfer the weaponSetFlags FIRST, since we need 'em to restore the weaponSet properly. (srj)
- m_curWeaponSetFlags.xfer( xfer );
- xfer->xferUnsignedInt(&m_weaponBonusCondition);
- xfer->xferUser(&m_lastWeaponCondition, sizeof(m_lastWeaponCondition));
- // do the weaponSet itself after all the weapon-related stuff, just in case
- xfer->xferSnapshot(&m_weaponSet);
- m_specialPowerBits.xfer( xfer );
- xfer->xferAsciiString(&m_commandSetStringOverride);
- xfer->xferBool(&m_modulesReady);
- }
- if (version >= 5)
- {
- xfer->xferBool(&m_isReceivingDifficultyBonus);
- }
- else
- m_isReceivingDifficultyBonus = FALSE;
- } // end xfer
- //-------------------------------------------------------------------------------------------------
- /** Object load game post process phase */
- //-------------------------------------------------------------------------------------------------
- void Object::loadPostProcess()
- {
- if( m_xferContainedByID != INVALID_ID )
- m_containedBy = TheGameLogic->findObjectByID(m_xferContainedByID);
- else
- m_containedBy = NULL;
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** Does this object have this upgrade */
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasUpgrade( const UpgradeTemplate *upgradeT ) const
- {
- if( m_objectUpgradesCompleted.testForAll( upgradeT->getUpgradeMask() ) )
- {
- return TRUE;
- }
- return FALSE;
- } // end hasUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Is this object capable of having this upgrade */
- //-------------------------------------------------------------------------------------------------
- Bool Object::affectedByUpgrade( const UpgradeTemplate *upgradeT ) const
- {
- UpgradeMaskType objectMask = getObjectCompletedUpgradeMask();
- UpgradeMaskType playerMask = getControllingPlayer()->getCompletedUpgradeMask();
- UpgradeMaskType maskToCheck = playerMask;
- maskToCheck.set( objectMask );
- maskToCheck.set( upgradeT->getUpgradeMask() );
- // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
- // We combine all the masks in case someone has a Object AND Player combination
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( upgrade->wouldUpgrade( maskToCheck ) )
- {
- // if any of my many upgrade modules would execute in response to this flag, say yes.
- return TRUE;
- }
- }
- return FALSE;
- } // end affectedByUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Give this upgrade to this object */
- //-------------------------------------------------------------------------------------------------
- void Object::giveUpgrade( const UpgradeTemplate *upgradeT )
- {
- if (upgradeT)
- {
- m_objectUpgradesCompleted.set( upgradeT->getUpgradeMask() );
- //
- // iterate through all the upgrade modules of this object and call the method to
- // grant a new upgrade
- //
- updateUpgradeModules();
- }
- } // end giveUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Remove this upgrade from this object */
- //-------------------------------------------------------------------------------------------------
- void Object::removeUpgrade( const UpgradeTemplate *upgradeT )
- {
- m_objectUpgradesCompleted.clear( upgradeT->getUpgradeMask() );
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- // Whoa, please note that while the function is called Object::RemoveUpgrade, it is not removing anything
- // in the sense of undoing the effects. It is just resetting the upgrade so it may be run again.
- upgrade->resetUpgrade( upgradeT->getUpgradeMask() );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Central point for onCapture logic */
- //-------------------------------------------------------------------------------------------------
- void Object::onCapture( Player *oldOwner, Player *newOwner )
- {
- // Everybody dhills when they captured so they don't keep doing something the new player might not want him to be doing
- if( getAIUpdateInterface() && (oldOwner != newOwner) )
- getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
- // this gets the new owner some points
- newOwner->getScoreKeeper()->addObjectCaptured(this);
- // rip through the behavior modules and call the onCapture for any modules that care
- for( BehaviorModule **module = m_behaviors; *module; ++module )
- (*module)->onCapture( oldOwner, newOwner );
- //
- // We have to undo our look for the old team and redo it for the new.
- // onCapture is used now, so it better be called after ownership changes and not before.
- //
- handlePartitionCellMaintenance();
-
- // Design needs the player to be able to sell buildings he steals from the AI's build list, and this is the
- // easiest fix. The only snafu would be a key building build listed by the AI that the player can capture
- // and the AI tries to capture back but needs to not sell. In that case, a Cinematic Unsellable version
- // of the building needs to be made. This fix has been okayed as the most non-lethal in November.
- clearScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE);
- // mark the command bar to redraw
- TheControlBar->markUIDirty();
- if (oldOwner!=newOwner && newOwner->isSkirmishAIPlayer()) {
- // The skirmish ai doesn't know what to do with captured faction buildings except sell them.
- if (isFactionStructure()) {
- TheBuildAssistant->sellObject( this );
- }
- }
- } // end onCapture
- //-------------------------------------------------------------------------------------------------
- /// Object level events that need to happen upon game death
- void Object::onDie( DamageInfo *damageInfo )
- {
- checkAndDetonateBoobyTrap(NULL);// Already dying, so no need to handle death case of explosion
- #if defined(_DEBUG) || defined(_INTERNAL)
- DEBUG_ASSERTCRASH(m_hasDiedAlready == false, ("Object::onDie has been called multiple times. This is invalid. jkmcd"));
- m_hasDiedAlready = true;
- #endif
-
- Bool selfInflicted = (damageInfo->in.m_sourceID == getID());
- // FIRST, call our die modules.
- for (BehaviorModule** d = m_behaviors; *d; ++d)
- {
- DieModuleInterface* die = (*d)->getDie();
- if (die)
- die->onDie(damageInfo);
- }
- // When objects die we remove from the radar as they're really not interesting anymore
- if( m_radarData )
- TheRadar->removeObject( this );
-
- // Just in case I have been sporting one of thise fancy Terrain Decals,
- //I naturally lose it now, because I'm dead.
- Drawable *draw = getDrawable();
- if (draw) draw->setTerrainDecalFadeTarget(0.0f, -0.03f);//fade...
- //if (draw) draw->setTerrainDecal(TERRAIN_DECAL_NONE);//pop!
- // objects that were spawned from something, need to tell their spawner that they have died
- Object* spawner = TheGameLogic->findObjectByID( getProducerID() );
- if( spawner )
- {
- // get the spawn behavior interface of the spawner
- SpawnBehaviorInterface *spawnerBehavior = spawner->getSpawnBehaviorInterface();
- if( spawnerBehavior )
- spawnerBehavior->onSpawnDeath( getID(), damageInfo );
- }
- handlePartitionCellMaintenance();
- if(m_team)
- m_team->notifyTeamOfObjectDeath();
- if (isLocallyControlled() && !selfInflicted) // wasLocallyControlled? :-)
- {
- if (isKindOf(KINDOF_STRUCTURE) && isKindOf(KINDOF_MP_COUNT_FOR_VICTORY))
- {
- TheEva->setShouldPlay(EVA_BuldingLost);
- }
- else if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE))
- {
- TheEva->setShouldPlay(EVA_UnitLost);
- //Create a fake radar event so the user can use the spacebar to quickly jump to this!
- TheRadar->tryEvent( RADAR_EVENT_FAKE, getPosition() );
- }
- }
- // This call won't do anything if we aren't actually in the list.
- //Kris: Added NULL check to prevent crash with combat bikes & their riders getting deleted on exit.
- if( getControllingPlayer() )
- {
- TheInGameUI->removeIdleWorker( this, getControllingPlayer()->getPlayerIndex() );
- }
- //When a GLA hole is in the process of rebuilding, and that rebuild is lost, we need to
- //tell anyone attacking it to transfer the attack to the hole that still exists.
- if( testStatus( OBJECT_STATUS_RECONSTRUCTING ) )
- {
- Object *hole = TheGameLogic->findObjectByID( getProducerID() );
- if( hole )
- {
- // set the information in the hole about what to build
- RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( hole );
- // sanity
- DEBUG_ASSERTCRASH( rhbi, ("Object::onDie() - No Rebuild Hole Behavior interface on hole\n") );
- // start the rebuild process
- if( rhbi )
- {
- rhbi->startRebuildProcess( getTemplate(), getID() );
- }
- //Transfer any attackers from the destroyed building to the hole.
- for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
- {
- AIUpdateInterface* ai = obj->getAI();
- if (!ai)
- continue;
- ai->transferAttack( getID(), hole->getID() );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setWeaponBonusCondition(WeaponBonusConditionType wst)
- {
- WeaponBonusConditionFlags oldCondition = m_weaponBonusCondition;
- m_weaponBonusCondition |= (1 << wst);
- if( oldCondition != m_weaponBonusCondition )
- {
- // Our weapon bonus just changed, so we need to immediately update our weapons
- m_weaponSet.weaponSetOnWeaponBonusChange(this);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::clearWeaponBonusCondition(WeaponBonusConditionType wst)
- {
- WeaponBonusConditionFlags oldCondition = m_weaponBonusCondition;
- m_weaponBonusCondition &= ~(1 << wst);
- if( oldCondition != m_weaponBonusCondition )
- {
- // Our weapon bonus just changed, so we need to immediately update our weapons
- m_weaponSet.weaponSetOnWeaponBonusChange(this);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**
- A weapon cannot be in charge of maintaining condition flags as it is all event driven.
- I will maintain my ModelCondition myself if it should change. Firing is set by firing logic,
- so I don't include it here. It is only the states that expire on timers that noone watches
- that I am concerned with.
- */
- //-------------------------------------------------------------------------------------------------
- void Object::adjustModelConditionForWeaponStatus()
- {
- UnsignedInt now = TheGameLogic->getFrame();
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (!w)
- {
- m_lastWeaponCondition[i] = WSF_NONE;
- continue;
- }
-
- WeaponSetConditionType conditionToSet = WSF_INVALID;
- if (i != m_weaponSet.getCurWeaponSlot())
- {
- // if this isn't the current weapon, then we never set ANYTHING for it.
- conditionToSet = WSF_NONE;
- }
- else if (w->getLastShotFrame() == now)
- {
- // yep, this overrides any weapon-status condition!
- conditionToSet = WSF_FIRING;
- }
- else if (!testStatus( OBJECT_STATUS_IS_ATTACKING ))
- {
- // srj sez: not 100% sure about this one, but the problem is: say we were attacking,
- // then issue a move command. if we didn't do this here, we might still have a 'firing'
- // pose, because his weapon might be in 'reloading' mode. since we're not attacking, however,
- // we really don't care, so we just force the issue here. (This might still need tweaking for the pursue state.)
- conditionToSet = WSF_NONE;
- }
- else
- {
- WeaponStatus newStatus = w->getStatus();
- const static WeaponSetConditionType s_wsfLookup[WEAPON_STATUS_COUNT] =
- {
- WSF_NONE, // READY_TO_FIRE,
- WSF_NONE, // OUT_OF_AMMO,
- WSF_BETWEEN, // BETWEEN_FIRING_SHOTS,
- WSF_RELOADING, // RELOADING_CLIP,
- WSF_PREATTACK // PRE_ATTACK,
- };
- conditionToSet = s_wsfLookup[newStatus];
- // special case this: say we are firing in bursts: pow-pow-pow-pause, etc.
- // then we might have a frame where we have reloaded and are ready-to-fire,
- // but haven't fired yet this frame. in that case, use 'between' so we still have
- // a firing pose, 'cuz if we use 'none' we will 'pop' back to idle for a frame. (srj)
- // additional note: only do if aiming or firing, since we could also be in this state if
- // we are approaching or pursuing a target! (srj)
- if (newStatus == READY_TO_FIRE && conditionToSet == WSF_NONE && testStatus( OBJECT_STATUS_IS_ATTACKING ) &&
- (testStatus( OBJECT_STATUS_IS_AIMING_WEAPON ) || testStatus( OBJECT_STATUS_IS_FIRING_WEAPON )))
- {
- conditionToSet = WSF_BETWEEN;
- }
- }
- if (m_drawable)
- {
- m_drawable->updateDrawableClipStatus( w->getRemainingAmmo(), w->getClipSize(), w->getWeaponSlot() );
- if (conditionToSet != WSF_INVALID && conditionToSet != m_lastWeaponCondition[i])
- {
- m_lastWeaponCondition[i] = conditionToSet;
- ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot((WeaponSlotType)i, conditionToSet);
- m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[i], c);
- if (conditionToSet == WSF_PREATTACK)
- {
- // in the preattack state, adjust the speed of the preattack anim to match the actual time it will take
- UnsignedInt preAttackDone = w->getPreAttackFinishedFrame();
- if (preAttackDone > now)
- m_drawable->setAnimationLoopDuration(preAttackDone - now);
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /// We have moved a 'significant' amount, so do maintenence that can be considered 'cell-based'
- void Object::onPartitionCellChange()
- {
- handlePartitionCellMaintenance();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handlePartitionCellMaintenance()
- {
- handleShroud();
- handleValueMap();
- handleThreatMap();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleShroud()
- {
- // Undo last looking
- unlook();
- // and shrouding
- unshroud();
- // redo shrouding
- shroud();
- // Redo looking
- look();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleValueMap()
- {
- removeValue();
- addValue();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleThreatMap()
- {
- removeThreat();
- addThreat();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::addValue()
- {
- if( !m_partitionLastValue->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is adding value, but hasn't removed his previous value.") );
- return;
- }
- if (!getControllingPlayer())
- return;
- if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) || isEffectivelyDead() || getShroudClearingRange() <= 0.0f )
- return;
- m_partitionLastValue->m_where = *getPosition();
- m_partitionLastValue->m_data = getTemplate()->friend_getBuildCost();
- m_partitionLastValue->m_forWhom = getControllingPlayer()->getPlayerMask();
- m_partitionLastValue->m_howFar = getVisionRange(); // we are valuable all the way to where we can target.
- ThePartitionManager->doValueAffect(m_partitionLastValue->m_where.x,
- m_partitionLastValue->m_where.y,
- m_partitionLastValue->m_howFar,
- m_partitionLastValue->m_data,
- m_partitionLastValue->m_forWhom
- );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::removeValue()
- {
- if( m_partitionLastValue->isInvalid() )
- {
- // removing before adding is valid, cause we always remove before adding. (So the first remove
- // will occur before the first add)
- return;
- }
- ThePartitionManager->undoValueAffect(m_partitionLastValue->m_where.x,
- m_partitionLastValue->m_where.y,
- m_partitionLastValue->m_howFar,
- m_partitionLastValue->m_data,
- m_partitionLastValue->m_forWhom
- );
- m_partitionLastValue->reset();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::addThreat()
- {
- if( !m_partitionLastThreat->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is adding threat, but hasn't removed his previous threat. (He hasn't finished the threat?)") );
- return;
- }
-
- if (!getControllingPlayer())
- return;
- if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) || isEffectivelyDead() || getShroudClearingRange() <= 0.0f )
- return;
- m_partitionLastThreat->m_where = *getPosition();
- m_partitionLastThreat->m_data = getTemplate()->getThreatValue();
- m_partitionLastThreat->m_forWhom = getControllingPlayer()->getPlayerMask();
- m_partitionLastThreat->m_howFar = getVisionRange(); // we are threatening all the way to where we can target.
- ThePartitionManager->doThreatAffect(m_partitionLastThreat->m_where.x,
- m_partitionLastThreat->m_where.y,
- m_partitionLastThreat->m_howFar,
- m_partitionLastThreat->m_data,
- m_partitionLastThreat->m_forWhom
- );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::removeThreat()
- {
- if( m_partitionLastThreat->isInvalid() )
- {
- // removing before adding is valid, cause we always remove before adding. (So the first remove
- // will occur before the first add)
- return;
- }
- ThePartitionManager->undoThreatAffect(m_partitionLastThreat->m_where.x,
- m_partitionLastThreat->m_where.y,
- m_partitionLastThreat->m_howFar,
- m_partitionLastThreat->m_data,
- m_partitionLastThreat->m_forWhom
- );
- m_partitionLastThreat->reset();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::look()
- {
- if( ! m_partitionLastLook->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is looking, but hasn't unlooked the last one.") );
- return;
- }
- Player* controller = getControllingPlayer();
- if ( controller )
- {
- // I removed the check for objects under construction by request of designers since
- // they want constructing objects to have a reduced sight range now. -MW
- // dead or blind things don't reveal shroud
-
- // Some things get Destroyed directly without hitting Death.
- if( !isDestroyed() && !isEffectivelyDead() )
- {
- ContainModuleInterface * contain = (getContainedBy() ? getContainedBy()->getContain() : NULL);
- if ( contain && !contain->isGarrisonable() )
- return;// dont look, 'cause you are in a tunnel, now
- // GS 10-20 Need to expand that exception to all transports or else you get a perma reveal where
- // you entered the transport. Remember, this hackiness is caused by the fact that we never realized that
- // garrisoned buildings weren't looking, we were just seeing the leftover last look of the guy inside.
- // Otherwise we'd just have enclosingContainer control looking which is the 'correct' answer.
- Real shroudClearingRange = getShroudClearingRange();
- if( shroudClearingRange > 0.0f )
- {
- PlayerMaskType lookingMask = 0;
- if ( isKindOf(KINDOF_REVEAL_TO_ALL) )
- {
- lookingMask = PLAYERMASK_ALL;
- }
- else
- {
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
-
- // Build mask of of allies who can see me.
- // This is the Object-centric game level that cares
- if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) == ALLIES )
- {
- lookingMask |= currentPlayer->getPlayerMask();
- }
- }
-
- // Other players can also be looking through our eyes.
- lookingMask |= m_visionSpiedMask;
- }
- Coord3D pos = *getPosition();
- ThePartitionManager->doShroudReveal( pos.x, pos.y, shroudClearingRange, lookingMask );
- m_partitionLastLook->m_where = pos;
- m_partitionLastLook->m_forWhom = lookingMask;
- m_partitionLastLook->m_howFar = getShroudClearingRange();
- // DEBUG_LOG(( "A %s looks at %f, %f for %x at range %f\n",
- // getTemplate()->getName().str(),
- // pos.x,
- // pos.y,
- // lookingMask,
- // getShroudClearingRange()
- // ));
- }
- //Now reveal to everyone if we're special. Note this works differently than KINDOF_REVEAL_TO_ALL because
- //the kindof uses the same range as allies, spies, and owners would see. This template based shroud
- //reveal to all range can specify a different value so we can get a much smaller reveal distance.
- // And don't reveal while under construction. When finished, a refresh occurs, so don't worry.
- Real shroudRevealToAllRange = getTemplate()->getShroudRevealToAllRange();
- if( shroudRevealToAllRange > 0.0f && !testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
- {
- //Kris: August 20, 2003
- //Seeing I added this concept, I'm changing it now to only reveal to all when the unit is visible. If it's stealthed,
- //we won't reveal it anymore (stealth general scudstorm).
- Bool stealthedAndNotDetected = testStatus( OBJECT_STATUS_STEALTHED ) && !testStatus( OBJECT_STATUS_DETECTED ) && !testStatus( OBJECT_STATUS_DISGUISED );
- if( !stealthedAndNotDetected )
- {
- Coord3D pos = *getPosition();
- PlayerMaskType thePlayersMask = ThePlayerList->getPlayersWithRelationship( getControllingPlayer()->getPlayerIndex(), ALLOW_ENEMIES | ALLOW_NEUTRAL );
- ThePartitionManager->doShroudReveal( pos.x, pos.y, shroudRevealToAllRange, thePlayersMask );
- m_partitionRevealAllLastLook->m_where = pos;
- m_partitionRevealAllLastLook->m_forWhom = thePlayersMask;
- m_partitionRevealAllLastLook->m_howFar = shroudRevealToAllRange;
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::unlook()
- {
- if( m_partitionLastLook->isInvalid() )
- {
- // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
- // This early return prevents an extra unlook if you never looked. Like you have 0 vision.
- return;
- }
- ThePartitionManager->queueUndoShroudReveal(m_partitionLastLook->m_where.x,
- m_partitionLastLook->m_where.y,
- m_partitionLastLook->m_howFar,
- m_partitionLastLook->m_forWhom
- );
- // DEBUG_LOG(( "A %s queues an unlook at %f, %f for %x at range %f\n",
- // getTemplate()->getName().str(),
- // m_partitionLastLook.m_where.x,
- // m_partitionLastLook.m_where.y,
- // m_partitionLastLook.m_forWhom,
- // m_partitionLastLook.m_howFar
- // ));
- m_partitionLastLook->reset();
- if( !m_partitionRevealAllLastLook->isInvalid() )
- {
- ThePartitionManager->queueUndoShroudReveal(m_partitionRevealAllLastLook->m_where.x,
- m_partitionRevealAllLastLook->m_where.y,
- m_partitionRevealAllLastLook->m_howFar,
- m_partitionRevealAllLastLook->m_forWhom
- );
-
- m_partitionRevealAllLastLook->reset();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::shroud()
- {
- if( ! m_partitionLastShroud->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is shrouding, but hasn't unshrouded the last one.") );
- return;
- }
- Player* controller = getControllingPlayer();
- if ( controller )
- {
- // things under construction don't shroud. (srj), nor do dead or blind things
- if( !getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !isEffectivelyDead() && getShroudRange() > 0.0f )
- {
- PlayerMaskType shroudingMask = 0;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- //Build mask of NON-allies. This is the Object-centric game level that cares
- if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) != ALLIES )
- {
- shroudingMask |= currentPlayer->getPlayerMask();
- }
- }
- Coord3D pos = *getPosition();
- ThePartitionManager->doShroudCover(pos.x, pos.y,
- getShroudRange(),
- shroudingMask);
- m_partitionLastShroud->m_where = pos;
- m_partitionLastShroud->m_forWhom = shroudingMask;
- m_partitionLastShroud->m_howFar = getShroudRange();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::unshroud()
- {
- if( m_partitionLastShroud->isInvalid() )
- {
- // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
- // This early return prevents an extra unlook if you never looked. Like you have 0 shroud generation.
- return;
- }
- ThePartitionManager->undoShroudCover(m_partitionLastShroud->m_where.x,
- m_partitionLastShroud->m_where.y,
- m_partitionLastShroud->m_howFar,
- m_partitionLastShroud->m_forWhom);
- m_partitionLastShroud->reset();
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getVisionRange() const
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(m_visionRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityTargettableColor);
- }
- }
- #endif
- return m_visionRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setVisionRange( Real newVisionRange )
- {
- m_visionRange = newVisionRange;
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getShroudClearingRange() const
- {
- Real shroudClearingRange=m_shroudClearingRange;
- if( getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
- {
- //structures under construction have limited vision range. For now, base it
- //on the geometry extents so the structure can only see itself.
- shroudClearingRange = getGeometryInfo().getBoundingCircleRadius();
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(shroudClearingRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityDeshroudColor);
- }
- }
- #endif
- return shroudClearingRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setShroudClearingRange( Real newShroudClearingRange )
- {
- if( newShroudClearingRange != m_shroudClearingRange )
- {
- // The partition cell refresh is a slow operation, so only do it if you really have to.
- // Range change is a valid reason to relook.
- m_shroudClearingRange = newShroudClearingRange;
- /*
- Complete and total monkey hack fix.
- The problem: newObject doesn't get an initial pos, so all objects start at 0,0,0.
- Most code paths instantly move 'em to a good pos, but in some cases, that is too late:
- If we have search-and-destroy battle plan, we will apply it at that point, and clear out
- a vision range based on our current (wrong) location. Doh!
- So, this just sez: if you are at 0,0,0, don't call handlePartitionCellMaintenance()... since
- you will either (1) be moved elsewhere immediately, thus forcing it to be called via
- another route anyway, or (2) not be moved, which means you are a very naughty and worthless
- object anyway and we should just ignore you.
- Proper fix for next version is to require initial pos to be passed in to newObject so that
- all objects can start at their proper initial position from the start of the ctor.
- (srj)
- */
- const Coord3D* pos = getPosition();
- if (pos->x || pos->y || pos->z)
- {
- handlePartitionCellMaintenance();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getShroudRange() const
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(m_shroudRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityGapColor);
- }
- }
- #endif
- return m_shroudRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setShroudRange( Real newShroudRange )
- {
- m_shroudRange = newShroudRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setVisionSpied(Bool setting, Int byWhom)
- {
- Bool needRefresh = FALSE; // If this setting is an edge trigger on the reference count, I need to refresh
- if( setting )
- {
- m_visionSpiedBy[ byWhom ] = m_visionSpiedBy[ byWhom ] + 1;
- if( m_visionSpiedBy[ byWhom ] == 1 )
- needRefresh = TRUE;
- }
- else
- {
- m_visionSpiedBy[ byWhom ] = m_visionSpiedBy[ byWhom ] - 1;
- if( m_visionSpiedBy[ byWhom ] == 0 )
- needRefresh = TRUE;
- }
- if( needRefresh )
- {
- PlayerMaskType workingMask = 0;
- for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
- {
- if( m_visionSpiedBy[i] > 0 )
- BitSet( workingMask, ( 1 << i ) );
- else
- BitClear( workingMask, ( 1 << i ) );
- }
- m_visionSpiedMask = workingMask;
- handlePartitionCellMaintenance();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::doStatusDamage( ObjectStatusTypes status, Real duration )
- {
- if(m_statusDamageHelper)
- m_statusDamageHelper->doStatusDamage(status, duration);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::doTempWeaponBonus( WeaponBonusConditionType status, UnsignedInt duration )
- {
- if(m_tempWeaponBonusHelper)
- m_tempWeaponBonusHelper->doTempWeaponBonus(status, duration);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::notifySubdualDamage( Real amount )
- {
- if(m_subdualDamageHelper)
- m_subdualDamageHelper->notifySubdualDamage( amount );
-
- // If we are gaining subdual damage, we are slowly tinting
- if( getDrawable() )
- {
- if( amount > 0 )
- getDrawable()->setTintStatus(TINT_STATUS_GAINING_SUBDUAL_DAMAGE);
- else
- getDrawable()->clearTintStatus(TINT_STATUS_GAINING_SUBDUAL_DAMAGE);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Given a special power template, find the module in the object that can implement it.
- * There can be at most one */
- //-------------------------------------------------------------------------------------------------
- SpecialPowerModuleInterface *Object::getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const
- {
- // sanity
- if( specialPowerTemplate == NULL )
- return NULL;
- // search the modules for the one with the matching template
- for( BehaviorModule** m = m_behaviors; *m; ++m )
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- if( sp->isModuleForPower( specialPowerTemplate ) )
- return sp;
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPower( commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerAtObject( const SpecialPowerTemplate *specialPowerTemplate, Object *obj, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerAtObject( obj, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerAtLocation( const SpecialPowerTemplate *specialPowerTemplate,
- const Coord3D *loc, Real angle, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerAtLocation( loc, angle, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerUsingWaypoints( const SpecialPowerTemplate *specialPowerTemplate, const Waypoint *way, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerUsingWaypoints( way, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPower( commandButton->getSpecialPowerTemplate(), commandOptions, cmdSource == CMD_FROM_SCRIPT );
- return;
- }
- break;
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- return;
- }
- break;
- case GUI_COMMAND_SWITCH_WEAPON:
- {
- WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
- // GUI_COMMAND_SWITCH_WEAPON switches until un-switched, or switched to something else.
- setWeaponLock( weaponSlot, LOCKED_PERMANENTLY );
- return;
- }
- case GUI_COMMAND_FIRE_WEAPON:
- if( ai )
- {
- if( !BitTest( commandButton->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET ) && !BitTest( commandButton->getOptions(), NEED_TARGET_POS ) )
- {
- setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
- //LOCATION BASED FIRE WEAPON
- ai->aiAttackPosition( NULL, commandButton->getMaxShotsToFire(), cmdSource );
- }
- else
- {
- DEBUG_CRASH( ("WARNING: Script doCommandButton for button %s cannot fire weapon with NO POSITION. Skipping.", commandButton->getName().str()) );
- }
- return;
- }
- break;
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_PLAYER_UPGRADE:
- {
- const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
- DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in player upgrade command\n", "UNKNOWN") );
- // sanity
- if( upgradeT == NULL )
- break;
- if( upgradeT->getUpgradeType() == UPGRADE_TYPE_OBJECT )
- {
- if( hasUpgrade( upgradeT ) || !affectedByUpgrade( upgradeT ) )
- break;
- }
- // producer must have a production update
- ProductionUpdateInterface *pu = getProductionUpdateInterface();
- if( pu == NULL )
- break;
- // queue the upgrade "research"
- pu->queueUpgrade( upgradeT );
- }
- return;
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_DOZER_CONSTRUCT: {
- const ThingTemplate *tt = commandButton->getThingTemplate();
- ProductionUpdateInterface *pu = this->getProductionUpdateInterface();
- if (pu && tt) {
- pu->queueCreateUnit( tt, pu->requestUniqueUnitID());
- return;
- }
- break;
- }
- case GUI_COMMAND_HACK_INTERNET:{
- if( ai )
- {
- ai->aiHackInternet( cmdSource );
- return;
- }
- break;
- }
- case GUI_COMMAND_SELL:
- TheBuildAssistant->sellObject( this );
- return;
-
- //Feel free to implement object based command buttons.
- case GUI_COMMAND_COMBATDROP:
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_ATTACK_MOVE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- DEBUG_CRASH( ("WARNING: Script doCommandButton for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability directed at an object target */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_COMBATDROP:
- if( ai )
- {
- ai->aiCombatDrop( obj, *(obj->getPosition()), cmdSource );
- }
- return;
- case GUI_COMMAND_SPECIAL_POWER:
- {
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPowerAtObject( commandButton->getSpecialPowerTemplate(), obj, commandOptions, cmdSource == CMD_FROM_SCRIPT );
- }
- return;
- }
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- }
- return;
- case GUI_COMMAND_FIRE_WEAPON:
- if( ai )
- {
- if( BitTest( commandButton->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET ) )
- {
- //OBJECT BASED FIRE WEAPON
- if( !obj )
- {
- break;
- }
- if( !commandButton->isValidObjectTarget( this, obj ) )
- {
- break;
- }
- setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
- if( BitTest( commandButton->getOptions(), ATTACK_OBJECTS_POSITION ) )
- {
- //Actually, you know what.... we want to attack the object's location instead.
- ai->aiAttackPosition( obj->getPosition(), commandButton->getMaxShotsToFire(), cmdSource );
- }
- else
- {
- ai->aiAttackObject( obj, commandButton->getMaxShotsToFire(), cmdSource );
- }
- }
- else
- {
- DEBUG_CRASH( ("WARNING: Script doCommandButtonAtObject for button %s cannot fire weapon at AN OBJECT. Skipping.", commandButton->getName().str()) );
- }
- return;
- }
- break;
-
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- case GUICOMMANDMODE_SABOTAGE_BUILDING:
- if( ai )
- {
- ai->aiEnter( obj, cmdSource );
- }
- return;
- //Feel free to implement object based command buttons.
- case GUI_COMMAND_DOZER_CONSTRUCT:
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_PLAYER_UPGRADE:
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_ATTACK_MOVE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_HACK_INTERNET:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- case GUI_COMMAND_SWITCH_WEAPON:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- DEBUG_CRASH( ("WARNING: Script doCommandButtonAtObject for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability directed at a location */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- {
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPowerAtLocation( commandButton->getSpecialPowerTemplate(), pos, INVALID_ANGLE, commandOptions, cmdSource == CMD_FROM_SCRIPT );
- return;
- }
- break;
- }
- case GUI_COMMAND_ATTACK_MOVE:
- if( ai )
- {
- ai->aiAttackMoveToPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
- return;
- }
- break;
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- return;
- }
- break;
- case GUI_COMMAND_DOZER_CONSTRUCT:
- TheBuildAssistant->buildObjectNow( this, commandButton->getThingTemplate(), pos, 0.0f, getControllingPlayer() );
- return;
- case GUI_COMMAND_FIRE_WEAPON:
- if( ai )
- {
- if( BitTest( commandButton->getOptions(), NEED_TARGET_POS ) )
- {
- //LOCATION BASED FIRE WEAPON
- if( !pos )
- {
- break;
- }
- setWeaponLock( commandButton->getWeaponSlot(), LOCKED_TEMPORARILY );
- ai->aiAttackPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
- }
- else
- {
- DEBUG_CRASH( ("WARNING: Script doCommandButtonAtPosition for button %s cannot fire weapon at A POSITION. Skipping.", commandButton->getName().str()) );
- }
- return;
- }
- break;
-
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_PLAYER_UPGRADE:
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_HACK_INTERNET:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUI_COMMAND_COMBATDROP:
- case GUI_COMMAND_SWITCH_WEAPON:
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- DEBUG_CRASH( ("WARNING: Script doCommandButtonAtPosition for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability directed at a location */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButtonUsingWaypoints( const CommandButton *commandButton, const Waypoint *way, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- if( commandButton )
- {
- if( !BitTest( commandButton->getOptions(), CAN_USE_WAYPOINTS ) )
- {
- //Our button doesn't support waypoints.
- DEBUG_CRASH( ("WARNING: Script doCommandButtonUsingWaypoints for button %s lacks CAN_USE_WAYPOINTS option. Doing nothing.", commandButton->getName().str()) );
- return;
- }
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- {
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPowerUsingWaypoints( commandButton->getSpecialPowerTemplate(), way, commandOptions, cmdSource == CMD_FROM_SCRIPT );
- return;
- }
- break;
- }
- case GUI_COMMAND_ATTACK_MOVE:
- case GUI_COMMAND_STOP:
- case GUI_COMMAND_DOZER_CONSTRUCT:
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_PLAYER_UPGRADE:
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_FIRE_WEAPON:
- case GUI_COMMAND_HACK_INTERNET:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUI_COMMAND_COMBATDROP:
- case GUI_COMMAND_SWITCH_WEAPON:
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- DEBUG_CRASH( ("WARNING: Script doCommandButtonUsingWaypoints for button %s not implemented. Doing nothing.", commandButton->getName().str()) );
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Object::clearLeechRangeModeForAllWeapons()
- {
- m_weaponSet.clearLeechRangeModeForAllWeapons();
- }
- // ------------------------------------------------------------------------------------------------
- /** Search our update modules for a production update interface and return it if one is found */
- // ------------------------------------------------------------------------------------------------
- ProductionUpdateInterface* Object::getProductionUpdateInterface( void )
- {
- ProductionUpdateInterface *pui;
- // tell our update modules that we intend to do this special power.
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- pui = (*u)->getProductionUpdateInterface();
- if( pui )
- return pui;
- } // end for
- return NULL;
- } // end getProductionUpdateInterface
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DockUpdateInterface *Object::getDockUpdateInterface( void )
- {
- DockUpdateInterface *dock = NULL;
- for( BehaviorModule **u = m_behaviors; *u; ++u )
- {
- if( (dock = (*u)->getDockUpdateInterface()) != NULL )
- return dock;
- }
- return NULL;
- } // end getDockUpdateInterface
- // ------------------------------------------------------------------------------------------------
- // Search our special power modules for a specific one.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerModuleInterface* Object::findSpecialPowerModuleInterface( SpecialPowerType type ) const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
- if (spTemplate && spTemplate->getSpecialPowerType() == type || type == SPECIAL_INVALID )
- {
- return sp;
- }
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // Search our special power modules for the first occurrence of a shortcut special.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerModuleInterface* Object::findAnyShortcutSpecialPowerModuleInterface() const
- {
- for( BehaviorModule** m = m_behaviors; *m; ++m )
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
- if( spTemplate && spTemplate->isShortcutPower() )
- {
- return sp;
- }
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- /** Get spawn behavior interface from object */
- // ------------------------------------------------------------------------------------------------
- SpawnBehaviorInterface* Object::getSpawnBehaviorInterface() const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpawnBehaviorInterface *sbi = (*m)->getSpawnBehaviorInterface();
- if( sbi )
- {
- return sbi;
- }
- }
- return NULL;
- } // end getSpawnBehaviorInterfaceFromObject
- // ------------------------------------------------------------------------------------------------
- ProjectileUpdateInterface* Object::getProjectileUpdateInterface() const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- ProjectileUpdateInterface *pui = (*m)->getProjectileUpdateInterface();
- if( pui )
- {
- return pui;
- }
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // Simply find the special power module that is currently allowing plotting of positions to target.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestinationActive( SpecialPowerType type ) const
- {
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
- if( spInterface )
- {
- if( spInterface->doesSpecialPowerHaveOverridableDestinationActive() )
- {
- return spInterface;
- }
- }
- } // end for
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // Simply find the special power module that is potentially allowed to plot positions to target.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestination( SpecialPowerType type ) const
- {
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
- if( spInterface )
- {
- if( spInterface->doesSpecialPowerHaveOverridableDestination() )
- {
- return spInterface;
- }
- }
- } // end for
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // Search our special ability updates for a specific one.
- // ------------------------------------------------------------------------------------------------
- SpecialAbilityUpdate* Object::findSpecialAbilityUpdate( SpecialPowerType type ) const
- {
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
- if( spInterface && spInterface->isSpecialAbility() )
- {
- SpecialAbilityUpdate *spUpdate = (SpecialAbilityUpdate*)spInterface;
- if( spUpdate->getSpecialPowerType() == type )
- {
- return spUpdate;
- }
- }
- } // end for
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- SpecialPowerCompletionDie* Object::findSpecialPowerCompletionDie() const
- {
- static NameKeyType key_SpecialPowerCompletionDie = NAMEKEY("SpecialPowerCompletionDie");
- return (SpecialPowerCompletionDie*)findModule(key_SpecialPowerCompletionDie);
- }
- // ------------------------------------------------------------------------------------------------
- Int Object::getNumConsecutiveShotsFiredAtTarget( const Object *victim ) const
- {
- return m_firingTracker ? m_firingTracker->getNumConsecutiveShotsAtVictim( victim ) : 0;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool Object::getSingleLogicalBonePosition(const char* boneName, Coord3D* position, Matrix3D* transform) const
- {
- if (m_drawable && m_drawable->getPristineBonePositions( boneName, 0, position, transform, 1 ) == 1 )
- {
- m_drawable->convertBonePosToWorldPos( position, transform, position, transform );
- return true;
- }
- else
- {
- if (position)
- *position = *getPosition();
- if (transform)
- *transform = *getTransformMatrix();
- return false;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool Object::getSingleLogicalBonePositionOnTurret( WhichTurretType whichTurret, const char* boneName, Coord3D* position, Matrix3D* transform ) const
- {
- Coord3D turretPosition;
- Coord3D bonePosition;
- if( getDrawable() == NULL || getAI() == NULL )
- return FALSE;
- // We need to find the TurretBone's pristine position.
- getDrawable()->getProjectileLaunchOffset( PRIMARY_WEAPON, 1, NULL, whichTurret, &turretPosition, NULL );
- // And the required bone's pristine position
- if( getDrawable()->getPristineBonePositions(boneName, 0, &bonePosition, NULL, 1) != 1 )
- return FALSE;
- //Then we mojo the Logic position of the required bone like Missile firing does. Using the logic twist of the turret
- Real turretRotation;
- getAI()->getTurretRotAndPitch( whichTurret, &turretRotation, NULL );
- Matrix3D boneOffset(TRUE);// This will be from the turret to the requested bone
- // Vector3 bonePositionVector( bonePosition.x - turretPosition.x,
- // bonePosition.y - turretPosition.y,
- // bonePosition.z - turretPosition.z );
- Vector3 bonePositionVector( bonePosition.x,
- bonePosition.y,
- bonePosition.z );
- boneOffset.Translate(bonePositionVector);
- Matrix3D turnAdjustment(TRUE);// this is the turret twist to be applied to the final answer
- turnAdjustment.Translate( turretPosition.x, turretPosition.y, turretPosition.z );
- turnAdjustment.In_Place_Pre_Rotate_Z(turretRotation);
- turnAdjustment.Translate( -turretPosition.x, -turretPosition.y, -turretPosition.z );
- Matrix3D boneLogicTransform;
- boneLogicTransform.mul( turnAdjustment, boneOffset );
- Matrix3D worldTransform;
- convertBonePosToWorldPos(NULL, &boneLogicTransform, NULL, &worldTransform);
- Vector3 tmp = worldTransform.Get_Translation();
- Coord3D worldPos;
- worldPos.x = tmp.X;
- worldPos.y = tmp.Y;
- worldPos.z = tmp.Z;
- if( position )
- *position = worldPos;
- if( transform )
- *transform = worldTransform;
- return TRUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Int Object::getMultiLogicalBonePosition(const char* boneNamePrefix, Int maxBones,
- Coord3D* positions, Matrix3D* transforms,
- Bool convertToWorld ) const
- {
- Int count;
- if (m_drawable && (count = m_drawable->getPristineBonePositions( boneNamePrefix, 1, positions, transforms, maxBones )) > 0 )
- {
- if( convertToWorld )
- {
- for (Int i = 0; i < count; ++i)
- m_drawable->convertBonePosToWorldPos( positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL, positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL );
- }
- return count;
- }
- else
- {
- return 0;
- }
- }
- //=============================================================================
- const AsciiString& Object::getCommandSetString() const
- {
- if (m_commandSetStringOverride.isNotEmpty())
- return m_commandSetStringOverride;
- return getTemplate()->friend_getCommandSetString();
- }
- //=============================================================================
- Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade )
- {
- // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid.
- const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString());
- for( Int buttonIndex = 0; buttonIndex < MAX_COMMANDS_PER_SET; buttonIndex++ )
- {
- const CommandButton *button = set->getCommandButton(buttonIndex);
- if( button && button->getUpgradeTemplate() && (button->getUpgradeTemplate() == upgrade) )
- return TRUE; // getUpgradeTemplate only returns something if it is actually an upgrade
- }
- return FALSE;// Cheatin' punk.
- }
- //=============================================================================
- // Object::defect, and related methods =
- //=============================================================================
- void Object::defect( Team* newTeam, UnsignedInt detectionTime )
- {
- if ( isContained() ) //@todo (KRIS?) make contained units unselectable, until then... lorenzen
- {
- return;
- }
- Player *player = getControllingPlayer();
- if ( !player )
- return;
- Team* myTeam = player->getDefaultTeam();
- if ( myTeam == newTeam ) // can't defect from my own team, that would be silly
- return;
-
- // things that are under construction, or sold, cannot defect.
- if (testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ||
- testStatus(OBJECT_STATUS_SOLD))
- {
- return;
- }
- // Before switch ////////////////////////////////////////
- //Design says:
- ProductionUpdateInterface *production = getProductionUpdateInterface();
- if ( production )
- {
- production->cancelAndRefundAllProduction();
- }
- // pop it up on the radar, so as to warn those who care
- // do this first, since after setTeam() the infiltrator
- // becomes the controllingplayer, not me
- // But don't do this is if the new team is not a real team. "'Enemy' infiltration" wouldn't make
- // sense, and we are probably just reverting a cave or something.
- if( friend_getRadarData() && newTeam->getControllingPlayer()->isPlayableSide() && myTeam->getControllingPlayer()->isPlayableSide())
- {
- TheRadar->tryInfiltrationEvent( this );
- }
- friend_setUndetectedDefector( detectionTime > 0 );
- if (m_defectionHelper)
- m_defectionHelper->startDefectionTimer(detectionTime);
- // Switch ////////////////////////////////////////
- setTeam( newTeam );
- // After switch ////////////////////////////////////////
-
- AIUpdateInterface *ai = getAI();
- handlePartitionCellMaintenance();// to clear the shoud for my new master
- if ( ai )
- {
- ai->aiIdle( CMD_FROM_AI );
- }
- // Play our sound indicating we've been defected. (weird verbage, but true.)
- AudioEventRTS voiceDefect = *getTemplate()->getVoiceDefect();
- voiceDefect.setObjectID(getID());
- TheAudio->addAudioEvent(&voiceDefect);
- //make the new recruit the only selected thing, awaiting new command to move, attack, etc...
- Drawable *dr = getDrawable();
- if (dr)
- {
- dr->flashAsSelected(); //This is the first of several flashes which get cue'd by doDefectorUpdateStuff()
- AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
- defectorTimerSound.setObjectID( getID() );
- TheAudio->addAudioEvent(&defectorTimerSound);
- }
-
- ContainModuleInterface *ct = getContain();
- if( ct && ct->isKickOutOnCapture() )
- {
- // Caves really really don't want to do this.
- ct->removeAllContained( TRUE );
- }
- // if it has parking places, defect anything parked there.
- for (BehaviorModule** i = getBehaviorModules(); *i; ++i)
- {
- ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
- if (pp)
- {
- pp->defectAllParkedUnits(newTeam, detectionTime);
- break;
- }
- }
- // defect any mines that are owned by this structure, right now.
- // unfortunately, structures don't keep list of mines they own, so we must do
- // this the hard way :-( [fortunately, this doens't happen very often, so this
- // is probably an acceptable, if icky, solution.] (srj)
- for (Object* mine = TheGameLogic->getFirstObject(); mine; mine = mine->getNextObject())
- {
- if (mine->isKindOf(KINDOF_MINE))
- {
- if (mine->getProducerID() == this->getID())
- {
- mine->setTeam(newTeam);
- }
- }
- }
- }
- //=============================================================================
- // Object::goInvulnerable
- //=============================================================================
- void Object::goInvulnerable( UnsignedInt time )
- {
- const Bool WITHOUT_DEFECTOR_FX = FALSE;
- friend_setUndetectedDefector( time > 0 );
- if (m_defectionHelper)
- m_defectionHelper->startDefectionTimer(time, WITHOUT_DEFECTOR_FX);
- }
- // ------------------------------------------------------------------------------------------------
- /** Return the radar priority for this object type */
- // ------------------------------------------------------------------------------------------------
- RadarPriorityType Object::getRadarPriority( void ) const
- {
- RadarPriorityType priority = RADAR_PRIORITY_INVALID;
- // first, get the priority at the thing template level
- priority = getTemplate()->getDefaultRadarPriority();
- //
- // there are some objects that we want to show up on the radar when they have
- // certain properties ... here we will check for those properties unless the INI
- // setting of "not on radar" has been manually entered which explicitly forbids an
- // object from being on the radar ... by default objects get an "invalid" priority
- // on the radar and this means that we are free to decide one here if we want
- //
- if( priority == RADAR_PRIORITY_INVALID )
- {
- // objects that are "garrisonable" show up on the radar
- ContainModuleInterface *cmi = getContain();
- if( cmi && cmi->isGarrisonable() )
- priority = RADAR_PRIORITY_STRUCTURE;
- // objects that are "capturable" show up on the radar
- if( isKindOf( KINDOF_CAPTURABLE ) )
- priority = RADAR_PRIORITY_STRUCTURE;
- } // end if
- // Carbombs will show up as units regardless of their default priority
- if ( testStatus( OBJECT_STATUS_IS_CARBOMB ) )
- priority = RADAR_PRIORITY_UNIT;
- // return the priority we're going to use
- return priority;
- } // end getRadarPriority
- // ------------------------------------------------------------------------------------------------
- AIGroup *Object::getGroup(void)
- {
- return m_group;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::enterGroup( AIGroup *group )
- {
- // DEBUG_LOG(("***AIGROUP %x involved in enterGroup on %x\n", group, this));
- // if we are in another group, remove ourselves from it first
- leaveGroup();
- m_group = group;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::leaveGroup( void )
- {
- // DEBUG_LOG(("***AIGROUP %x involved in leaveGroup on %x\n", m_group, this));
- // if we are in a group, remove ourselves from it
- if (m_group)
- {
- // to avoid recursion, set m_group to NULL before removing
- AIGroup *group = m_group;
- m_group = NULL;
- group->remove( this );
- }
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getCarrierDeckHeight() const
- {
- Object *producer = TheGameLogic->findObjectByID( getProducerID() );
- if( producer )
- {
- // Find a parking place behavior.
- for( BehaviorModule** i = producer->getBehaviorModules(); *i; ++i )
- {
- ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
- if( pp )
- {
- return pp->getLandingDeckHeightOffset();
- }
- }
- }
- return 0.0f;
- }
- //-------------------------------------------------------------------------------------------------
- CountermeasuresBehaviorInterface* Object::getCountermeasuresBehaviorInterface()
- {
- for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
- {
- CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
- if( cbi )
- {
- return cbi;
- }
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- const CountermeasuresBehaviorInterface* Object::getCountermeasuresBehaviorInterface() const
- {
- for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
- {
- const CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
- if( cbi )
- {
- return cbi;
- }
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasCountermeasures() const
- {
- const CountermeasuresBehaviorInterface* cbi = getCountermeasuresBehaviorInterface();
- if( cbi && cbi->isActive() )
- {
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::reportMissileForCountermeasures( Object *missile )
- {
- for( BehaviorModule** i = getBehaviorModules(); *i; ++i )
- {
- CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
- if( cbi )
- {
- cbi->reportMissileForCountermeasures( missile );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- ObjectID Object::calculateCountermeasureToDivertTo( const Object& victim )
- {
- AIUpdateInterface *ai = getAI();
- if( ai )
- {
- for( BehaviorModule** i = victim.getBehaviorModules(); *i; ++i )
- {
- CountermeasuresBehaviorInterface* cbi = (*i)->getCountermeasuresBehaviorInterface();
- if( cbi )
- {
- ObjectID decoyID = cbi->calculateCountermeasureToDivertTo( victim );
- return decoyID;
- }
- }
- }
- return INVALID_ID;
- }
|