mover.cpp 245 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510
  1. //******************************************************************************************
  2. //
  3. // mover.cpp - This file contains the Mover Class code
  4. //
  5. // MechCommander 2
  6. //
  7. //---------------------------------------------------------------------------//
  8. // Copyright (C) Microsoft Corporation. All rights reserved. //
  9. //===========================================================================//
  10. #ifndef MCLIB_h
  11. #include "mclib.h"
  12. #endif
  13. #ifndef GAMEOBJ_H
  14. #include "gameobj.h"
  15. #endif
  16. #ifndef OBJMGR_H
  17. #include "objmgr.h"
  18. #endif
  19. #ifndef MOVER_H
  20. #include "mover.h"
  21. #endif
  22. #ifndef MOVE_H
  23. #include "move.h"
  24. #endif
  25. #ifndef MECH_H
  26. #include "mech.h"
  27. #endif
  28. #ifndef GVEHICL_H
  29. #include "gvehicl.h"
  30. #endif
  31. #ifndef MECH3D_H
  32. #include "mech3d.h"
  33. #endif
  34. #ifndef CMPONENT_H
  35. #include "cmponent.h"
  36. #endif
  37. #ifndef WARRIOR_H
  38. #include "warrior.h"
  39. #endif
  40. #ifdef USE_DEBRIS
  41. #ifndef DEBRIS_H
  42. #include "debris.h"
  43. #endif
  44. #endif
  45. #ifndef GAMELOG_H
  46. #include "gamelog.h"
  47. #endif
  48. #ifndef TACORDR_H
  49. #include "tacordr.h"
  50. #endif
  51. #ifndef GAMESOUND_H
  52. #include "gamesound.h"
  53. #endif
  54. //#ifndef SOUNDSYS_H
  55. //#include "soundsys.h"
  56. //#endif
  57. //#ifndef SOUNDS_H
  58. //#include "sounds.h"
  59. //#endif
  60. #ifndef COLLSN_H
  61. #include "collsn.h"
  62. #endif
  63. #ifndef MECHCLASS_H
  64. #include "mechclass.h"
  65. #endif
  66. #ifndef UNITDESG_H
  67. #include "unitdesg.h"
  68. #endif
  69. #ifndef MULTPLYR_H
  70. #include "multplyr.h"
  71. #endif
  72. #ifndef TEAM_H
  73. #include "team.h"
  74. #endif
  75. #ifndef COMNDR_H
  76. #include "comndr.h"
  77. #endif
  78. #ifndef GROUP_H
  79. #include "group.h"
  80. #endif
  81. #ifndef CONTACT_H
  82. #include "contact.h"
  83. #endif
  84. #ifndef TRIGGER_H
  85. #include "trigger.h"
  86. #endif
  87. #ifndef MISSION_H
  88. #include "mission.h"
  89. #endif
  90. #ifndef LOGISTICSPILOT_H
  91. #include "logisticspilot.h"
  92. #endif
  93. //--------
  94. // DEFINES
  95. #define GOALMAP_CELL_DIM 61
  96. //--------
  97. // GLOBALS
  98. extern long SimpleMovePathRange;
  99. extern long GroupMoveTrailLen[2];
  100. extern long tileMulMAPCELL_DIM[MAX_MAP_CELL_WIDTH];
  101. extern long RamObjectWID;
  102. long MaxMoveGoalChecks = 60;
  103. float DelayedOrderTime = 1.0;
  104. float GroupOrderGoalOffset = 127.0;
  105. //BaseObjectPtr MoverRoster[MAX_MOVER_PART_ID - MIN_MOVER_PART_ID + 1];
  106. extern MoveMapPtr PathFindMap[2/*NUM_PATHMAPS*/];
  107. long goalMap[GOALMAP_CELL_DIM * GOALMAP_CELL_DIM];
  108. long goalMapRowStart[GOALMAP_CELL_DIM] = {
  109. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  110. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  111. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  112. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
  113. };
  114. char goalMapRowCol[GOALMAP_CELL_DIM * GOALMAP_CELL_DIM][2];
  115. long Mover::numMovers = 0;
  116. SortListPtr Mover::sortList = NULL;
  117. extern float DefaultAttackRadius;
  118. extern float WeaponFireModifiers[NUM_WEAPONFIRE_MODIFIERS];
  119. extern void DebugWeaponFireChunk (WeaponFireChunkPtr chunk1, WeaponFireChunkPtr chunk2, GameObjectPtr attacker);
  120. //--------
  121. // EXTERNS
  122. //extern ObjectMapPtr GameObjectMap;
  123. extern unsigned long NextIdNumber;
  124. #ifdef USE_SOUNDS
  125. extern bool friendlyDestroyed;
  126. extern bool enemyDestroyed;
  127. #endif
  128. extern TeamPtr homeTeam;
  129. extern bool JumpOnBlocked;
  130. extern bool FindingEscapePath;
  131. //extern float FireOddsTable[NUM_FIREODDS];
  132. extern float RankVersusChassisCombatModifier[NUM_WARRIOR_RANKS][NUM_MECH_CLASSES];
  133. float WeaponFireModifiers[NUM_WEAPONFIRE_MODIFIERS] = {
  134. 0.0, // Short Range To Target
  135. -10.0, // Medium Range To Target
  136. -20.0, // Long Range To Target
  137. -98.0, // Aimed Shot To Head
  138. -85.0, // Aimed Shot To Torso
  139. -88.0, // Aimed Shot To Arm/Leg
  140. 50.0, // Target is Stationary
  141. 0.0, // Green/Light
  142. -5.0, // Green/Medium
  143. -15.0, // Green/Heavy
  144. -25.0, // Green/Assault
  145. 5.0, // Regular/Light
  146. 0.0, // Regular/Medium
  147. -5.0, // Regular/Heavy
  148. -15.0, // Regular/Assault
  149. 10.0, // Veteran/Light
  150. 5.0, // Veteran/Medium
  151. 0.0, // Veteran/Heavy
  152. -5.0, // Veteran/Assault
  153. 15.0, // Elite/Light
  154. 10.0, // Elite/Medium
  155. 5.0, // Elite/Heavy
  156. 0.0, // Elite/Assault
  157. 0.0,
  158. 0.0,
  159. 0.0,
  160. 0.0,
  161. 0.0,
  162. 0.0,
  163. 0.0
  164. };
  165. extern float WarriorRankScale[NUM_WARRIOR_RANKS];
  166. extern char * SpecialtySkillsTable[NUM_SPECIALTY_SKILLS];
  167. float WeaponSpecialistModifier = 20.0f;
  168. extern float MovementUpdateFrequency;
  169. extern float CombatUpdateFrequency;
  170. extern float CommandUpdateFrequency;
  171. extern float PilotCheckUpdateFrequency;
  172. extern long PilotCheckModifierTable[2];
  173. //extern float ElementalTargetNoJumpDistance;
  174. extern long MechSalvageChance;
  175. extern float MapCellDiagonal;
  176. //extern long OverlayWeightIndex[NUM_OVERLAY_TYPES];
  177. extern long NumLocationCriticalSpaces[NUM_BODY_LOCATIONS];
  178. extern GlobalMapPtr GlobalMoveMap[3];
  179. extern long adjCellTable[MAPCELL_DIM * MAPCELL_DIM][8][4];
  180. extern bool useUnlimitedAmmo;
  181. extern bool CantBlowSalvage;
  182. long TargetRolo = -1;
  183. WeaponFireChunk CurMoverWeaponFireChunk;
  184. extern char* ExceptionGameMsg;
  185. extern char ChunkDebugMsg[5120];
  186. extern char OverlayIsBridge[NUM_OVERLAY_TYPES];
  187. float GroundVehiclePivotYawMultiplier = 0.25;
  188. extern bool friendlyDestroyed;
  189. extern bool enemyDestroyed;
  190. extern GameLog* CombatLog;
  191. //**********************************************************************************
  192. float Mover::newThreatMultiplier = 3.0;
  193. float Mover::marginOfError[2] = {5.0, 10.0};
  194. float Mover::refitRange;
  195. float Mover::refitTime;
  196. float Mover::refitCostArray[NUM_COSTS][NUM_FIXERS];
  197. float Mover::refitAmount;
  198. float Mover::recoverRange;
  199. float Mover::recoverTime;
  200. float Mover::recoverCost;
  201. float Mover::recoverAmount;
  202. long Mover::IndirectFireWeapons[20] = {-1,-1,-1,-1,-1,
  203. -1,-1,-1,-1,-1,
  204. -1,-1,-1,-1,-1,
  205. -1,-1,-1,-1,-1};
  206. long Mover::AreaEffectWeapons[20] = {-1,-1,-1,-1,-1,
  207. -1,-1,-1,-1,-1,
  208. -1,-1,-1,-1,-1,
  209. -1,-1,-1,-1,-1};
  210. bool Mover::inRecoverUpdate = false;
  211. long StatusChunkUnpackErr = 0;
  212. char AttackParameters[14][3] = {
  213. {ATTACK_TO_DESTROY, FIRERANGE_OPTIMAL, 1}, // DESTROY
  214. {ATTACK_TO_DESTROY, FIRERANGE_SHORT, 0}, // STOP AND HOLD FIRE
  215. {ATTACK_TO_DESTROY, FIRERANGE_SHORT, 1}, // DESTROY - SR
  216. {ATTACK_TO_DESTROY, FIRERANGE_MEDIUM, 1}, // DESTROY - MR
  217. {ATTACK_TO_DESTROY, FIRERANGE_LONG, 1}, // DESTROY - LR
  218. {ATTACK_TO_DESTROY, FIRERANGE_MEDIUM, 0}, // DESTROY FROM HERE
  219. {ATTACK_TO_DESTROY, FIRERANGE_MEDIUM, 1}, // DESTROY ALL OUT
  220. {ATTACK_TO_DISABLE, FIRERANGE_MEDIUM, 1}, // DISABLE
  221. {ATTACK_TO_DISABLE, FIRERANGE_SHORT, 0}, // STOP AND HOLD FIRE
  222. {ATTACK_TO_DISABLE, FIRERANGE_SHORT, 1}, // DISABLE - SR
  223. {ATTACK_TO_DISABLE, FIRERANGE_MEDIUM, 1}, // DISABLE - MR
  224. {ATTACK_TO_DISABLE, FIRERANGE_LONG, 1}, // DISABLE - LR
  225. {ATTACK_TO_DISABLE, FIRERANGE_MEDIUM, 0}, // DISABLE FROM HERE
  226. {ATTACK_TO_DISABLE, FIRERANGE_MEDIUM, 1} // DISABLE MAX SALVAGE
  227. };
  228. long rearAttackTable[NUM_DIRECTIONS][3][2] = {
  229. {{1, 0}, {1, -1}, {0, 1}},
  230. {{1, -1}, {0, -1}, {1, 1}},
  231. {{0, -1}, {-1, -1}, {1, 0}},
  232. {{-1, -1}, {-1, 0}, {1, -1}},
  233. {{-1, 0}, {-1, 1}, {0, -1}},
  234. {{-1, 1}, {0, 1}, {-1, -1}},
  235. {{1, 0}, {1, 1}, {-1, 0}},
  236. {{1, 1}, {1, 0}, {-1, 1}}
  237. };
  238. //---------------------------------------------------------------------------
  239. // Game System Tweakable Data
  240. //---------------------------------------------------------------------------
  241. float WeaponRange[NUM_FIRERANGES] = {
  242. 250,
  243. 500,
  244. 1000
  245. };
  246. float WeaponRanges[NUM_WEAPON_RANGE_TYPES][2] = {
  247. {0, 100},
  248. {50, 150},
  249. {100, 225},
  250. {0, 0},
  251. {0, 0}
  252. };
  253. float OptimalRangePoints[NUM_WEAPON_RANGE_TYPES] = {
  254. 25, //Short
  255. 125, //Med
  256. 200, //Long
  257. 75, //Short-Med
  258. 125 //Med-Long
  259. };
  260. bool OptimalRangePointInRange[NUM_WEAPON_RANGE_TYPES][3];
  261. unsigned char OptimalRangeTieTable[32] = {
  262. 255, 0, 1, 1,
  263. 2, 2, 2, 2,
  264. 3, 3, 3, 3,
  265. 3, 3, 3, 3,
  266. 4, 4, 4, 4,
  267. 4, 4, 4, 4,
  268. 4, 4, 4, 4,
  269. 4, 4, 4, 4
  270. };
  271. float DefaultAttackRange = 75.0;
  272. float DisableAttackModifier = 10.0;
  273. float DisableGunneryModifier = 5.0;
  274. float SalvageAttackModifier = 30.0;
  275. float PilotingCheckFactor = 1.0;
  276. long hitLevel[2] = {
  277. 10, // weak hit is damage of <10
  278. 20 // moderate hit is damage <20, else heavy hit
  279. };
  280. long ClusterSizeSRM = 2; // eventually, let's just kill clusters!
  281. long ClusterSizeLRM = 5; // eventually, let's just kill clusters!
  282. long AntiMissileSystemStats[2][2] = {
  283. {1, 2}, // IS
  284. {2, 2} // CLAN
  285. };
  286. float PilotCheckHalfRate = 5.0;
  287. float DamageRateFrequency = 10.0;
  288. byte AttitudeEffect[NUM_ATTITUDES][6] = {
  289. {50, 75, 5, -2, 0, 3},
  290. {40, 60, 10, -1, 1, 5},
  291. {30, 50, 15, 0, 2, 10},
  292. {20, 40, 20, 1, 3, 15},
  293. {10, 25, 25, 1, 4, 20},
  294. {0, 0, 32, 2, 5, 128}
  295. };
  296. //extern bool SensorAutomaticSuccess;
  297. //extern float SensorModifier[8];
  298. float SensorBaseChance = 50.0;
  299. float SensorSkillFactor = 10.0;
  300. extern long DebugMovePathType;
  301. #ifdef USEHEAT
  302. float SensorHeatMultiplier = 5.0;
  303. float SensorWeaponHeatMultiplier = 5.0;
  304. float SensorHeatSourceModifiers[2] = {10.0, -10.0};
  305. extern float WeaponHeatCheckDelay;
  306. #endif
  307. float SensorBlockingObjectModifier = -5.0;
  308. float SensorShutDownMechModifier = -50.0;
  309. float SensorRangeModifier[4][2] = {
  310. {0.25f, 50.0},
  311. {0.15f, 20.0},
  312. {0.35f, 0.0},
  313. {0.25f, -25.0}
  314. };
  315. float SensorSizeModifier[3][2] = {
  316. {25.0, -10.0},
  317. {60.0, 0.0},
  318. {999.0, 15.0}
  319. };
  320. float SensorBlockingTerrain[2] = {-25.0, -5.0};
  321. long AimedFireAbort;
  322. long AimedFireHitTable[3];
  323. float FireArc[3];
  324. //bool LongRangeMovementEnabled[MAX_TEAMS];
  325. extern char ProfessionalismOffsetTable[NUM_OFFSET_RANGES][2];
  326. extern char DecorumOffsetTable[NUM_OFFSET_RANGES][2];
  327. extern char AmmoConservationModifiers[2][2];
  328. //extern long OverlayWeightTable[NUM_OVERLAY_WEIGHT_CLASSES * NUM_OVERLAY_TYPES * MAPCELL_DIM * MAPCELL_DIM];
  329. //extern long OverlayWeightIndex[NUM_OVERLAY_TYPES];
  330. //extern float MoveMarginOfError[2];
  331. extern float MoveTimeOut;
  332. extern float MoveYieldTime;
  333. extern long DefaultMechJumpCost;
  334. extern char SensorSkillMoveRange[4];
  335. extern float SensorSkillMoveFactor[8];
  336. float SkillTry[4];
  337. float SkillSuccess[4];
  338. float KillSkill[6];
  339. float WeaponHit;
  340. float SensorSkill;
  341. long MoveChunk::err = 0;
  342. float MaxStationaryTime = 0.0;
  343. float MaxTimeRevealed = 0.0f;
  344. #define CLAN_SEEN_FLAG 2
  345. #define IS_SEEN_FLAG 1
  346. extern float worldUnitsPerMeter;
  347. char Mover::optimalCells[MAX_ATTACK_CELLRANGE][MAX_ATTACK_INCREMENTS][2];
  348. long Mover::numOptimalIncrements = 0;
  349. short Mover::rangedCellsIndices[MAX_ATTACK_CELLRANGE][2];
  350. char Mover::rangedCells[RANGED_CELLS_DIM][2];
  351. TriggerAreaManager* Mover::triggerAreaMgr = NULL;
  352. unsigned long Mover::holdFireIconHandle = 0;
  353. float HeadShotModifier = 0.0f;
  354. float ArmShotModifier = 0.0f;
  355. float LegShotModifier = 0.0f;
  356. bool CalcValidAreaTable = true;
  357. void DEBUGWINS_print (char* s, long window);
  358. //***************************************************************************
  359. // MISC routines
  360. //***************************************************************************
  361. inline MoverPtr getMoverFromHandle (long handle) {
  362. return(dynamic_cast<MoverPtr>(ObjectManager->get(handle)));
  363. }
  364. //***************************************************************************
  365. // MOVECHUNK class
  366. //***************************************************************************
  367. #define MOVECHUNK_CELLPOS_BITS 9
  368. #define MOVECHUNK_NUMSTEPS_BITS 2
  369. #define MOVECHUNK_RUN_BITS 1
  370. #define MOVECHUNK_MOVING_BITS 1
  371. #define MOVECHUNK_STEP_BITS 3
  372. #define MOVECHUNK_CELLPOS_MASK 0x000001FF
  373. #define MOVECHUNK_NUMSTEPS_MASK 0x00000003
  374. #define MOVECHUNK_RUN_MASK 0x00000001
  375. #define MOVECHUNK_MOVING_MASK 0x00000001
  376. #define MOVECHUNK_STEP_MASK 0x00000007
  377. #define MOVECHUNK_NUM_STEPS 3
  378. //---------------------------------------------------------------------------
  379. void DebugMoveChunk (MoverPtr mover, MoveChunkPtr chunk1, MoveChunkPtr chunk2) {
  380. ChunkDebugMsg[0] = NULL;
  381. char outString[512];
  382. if (mover) {
  383. sprintf(outString, "Mover = %s (%d)\n",
  384. mover->getName(),
  385. mover->getPartId());
  386. strcat(ChunkDebugMsg, outString);
  387. sprintf(outString, "Mover World Pos = (%.4f, %.4f, %.4f)\n",
  388. mover->getPosition().x,
  389. mover->getPosition().y,
  390. mover->getPosition().z);
  391. strcat(ChunkDebugMsg, outString);
  392. long cellPos[2];
  393. mover->getCellPosition(cellPos[0], cellPos[1]);
  394. sprintf(outString, "Mover Obj Cell Pos = [%d, %d]\n", cellPos[0], cellPos[1]);
  395. strcat(ChunkDebugMsg, outString);
  396. if (mover->getPilot() == NULL)
  397. strcat(ChunkDebugMsg, "NULL pilot!\n");
  398. if ((mover->getObjectClass() == BATTLEMECH) && ((BattleMechPtr)mover)->inJump) {
  399. long jumpDest[2];
  400. land->worldToCell(((BattleMechPtr)mover)->jumpGoal, jumpDest[0], jumpDest[1]);
  401. sprintf(outString, "Jumping to [%d, %d]\n", jumpDest[0], jumpDest[1]);
  402. strcat(ChunkDebugMsg, outString);
  403. }
  404. strcat(ChunkDebugMsg, "\n");
  405. }
  406. if (chunk1) {
  407. strcat(ChunkDebugMsg, "CHUNK1\n");
  408. for (long i = 0; i < 4; i++) {
  409. sprintf(outString, "stepPos[%d] = (%d, %d)\n",
  410. i,
  411. chunk1->stepPos[i][0],
  412. chunk1->stepPos[i][1]);
  413. strcat(ChunkDebugMsg, outString);
  414. }
  415. sprintf(outString, "stepRelPos = %d, %d, %d\n",
  416. chunk1->stepRelPos[0],
  417. chunk1->stepRelPos[1],
  418. chunk1->stepRelPos[2]);
  419. strcat(ChunkDebugMsg, outString);
  420. sprintf(outString, "numSteps = %d\n", chunk1->numSteps);
  421. strcat(ChunkDebugMsg, outString);
  422. sprintf(outString, "run = %c\n", chunk1->run ? 'T' : 'F');
  423. strcat(ChunkDebugMsg, outString);
  424. sprintf(outString, "moving = %c\n", chunk1->moving ? 'T' : 'F');
  425. strcat(ChunkDebugMsg, outString);
  426. }
  427. if (chunk2) {
  428. strcat(ChunkDebugMsg, "\nCHUNK2\n");
  429. for (long i = 0; i < 4; i++) {
  430. sprintf(outString, "stepPos[%d] = (%d, %d)\n",
  431. i,
  432. chunk2->stepPos[i][0],
  433. chunk2->stepPos[i][1]);
  434. strcat(ChunkDebugMsg, outString);
  435. }
  436. sprintf(outString, "stepRelPos = %d, %d, %d\n",
  437. chunk2->stepRelPos[0],
  438. chunk2->stepRelPos[1],
  439. chunk2->stepRelPos[2]);
  440. strcat(ChunkDebugMsg, outString);
  441. sprintf(outString, "numSteps = %d\n", chunk2->numSteps);
  442. strcat(ChunkDebugMsg, outString);
  443. sprintf(outString, "moving = %c\n", chunk2->moving ? 'T' : 'F');
  444. strcat(ChunkDebugMsg, outString);
  445. sprintf(outString, "run = %c\n", chunk2->run ? 'T' : 'F');
  446. strcat(ChunkDebugMsg, outString);
  447. }
  448. File* debugFile = new File;
  449. debugFile->create("mvchunk.dbg");
  450. debugFile->writeString(ChunkDebugMsg);
  451. debugFile->close();
  452. delete debugFile;
  453. debugFile = NULL;
  454. ExceptionGameMsg = ChunkDebugMsg;
  455. }
  456. //---------------------------------------------------------------------------
  457. void* MoveChunk::operator new (size_t ourSize) {
  458. void* result = systemHeap->Malloc(ourSize);
  459. return(result);
  460. }
  461. //---------------------------------------------------------------------------
  462. void MoveChunk::operator delete (void* us) {
  463. systemHeap->Free(us);
  464. }
  465. //---------------------------------------------------------------------------
  466. inline void getAdjCell (long row,
  467. long col,
  468. long dir,
  469. long& adjRow,
  470. long& adjCol) {
  471. long adjMod[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1},
  472. {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
  473. adjRow = row + adjMod[dir][0];
  474. adjCol = col + adjMod[dir][1];
  475. }
  476. //---------------------------------------------------------------------------
  477. inline long cellDirToCell (long fromCellRow,
  478. long fromCellCol,
  479. long toCellRow,
  480. long toCellCol) {
  481. //-------------------------------------------------
  482. // This routine assumes the two cells are adjacent!
  483. static long deltaDir[3][3] = {
  484. {7, 0, 1},
  485. {6, -1, 2},
  486. {5, 4, 3}
  487. };
  488. long rowCellDelta = toCellRow - fromCellRow + 1;
  489. long colCellDelta = toCellCol - fromCellCol + 1;
  490. if ((rowCellDelta < 0) || (rowCellDelta > 2))
  491. return(-2);
  492. if ((colCellDelta < 0) || (colCellDelta > 2))
  493. return(-2);
  494. long dir = deltaDir[rowCellDelta][colCellDelta];
  495. if (dir == -1)
  496. return(-2);
  497. return(dir);
  498. }
  499. //---------------------------------------------------------------------------
  500. void MoveChunk::build (MoverPtr mover, MovePathPtr path1, MovePathPtr path2) {
  501. long cellPos[2];
  502. mover->getCellPosition(cellPos[0], cellPos[1]);
  503. stepPos[0][0] = cellPos[0];
  504. stepPos[0][1] = cellPos[1];
  505. stepRelPos[0] = 0;
  506. stepRelPos[1] = 0;
  507. stepRelPos[2] = 0;
  508. numSteps = 1;
  509. moving = path1 && (path1->numSteps > 0);
  510. if (path1 && path1->numSteps > 0) {
  511. bool reachedEndOfPath1 = true;
  512. long curStep = 1;
  513. if (path1->curStep < path1->numSteps) {
  514. reachedEndOfPath1 = false;
  515. long curPath1Step = path1->curStep;
  516. bool startWithCurStep = false;
  517. if ((cellPos[0] == path1->stepList[curPath1Step].cell[0]) &&
  518. (cellPos[1] == path1->stepList[curPath1Step].cell[1]))
  519. startWithCurStep = true;
  520. else {
  521. //--------------------------------------------------------------
  522. // Whacky case, since current object position is not next to the
  523. // next step in the path...
  524. long relDir = cellDirToCell(cellPos[0],
  525. cellPos[1],
  526. path1->stepList[curPath1Step].cell[0],
  527. path1->stepList[curPath1Step].cell[1]);
  528. startWithCurStep = (relDir == -2);
  529. // if (relDir == -2)
  530. // OutputDebugString("***jumped cell***\n");
  531. }
  532. if (startWithCurStep) {
  533. //---------------------
  534. // step 0 = curStep
  535. // step 1 = curStep + 1
  536. // step 2 = curStep + 2
  537. // step 3 = curStep + 3
  538. stepPos[0][0] = path1->stepList[curPath1Step].cell[0];
  539. stepPos[0][1] = path1->stepList[curPath1Step].cell[1];
  540. curPath1Step++;
  541. numSteps = MOVECHUNK_NUM_STEPS;
  542. long numStepsLeft = path1->numSteps - curPath1Step;
  543. if (numStepsLeft < MOVECHUNK_NUM_STEPS) {
  544. numSteps = numStepsLeft;
  545. reachedEndOfPath1 = true;
  546. }
  547. for (long i = 0; i < numSteps; i++) {
  548. stepPos[curStep][0] = path1->stepList[curPath1Step].cell[0];
  549. stepPos[curStep][1] = path1->stepList[curPath1Step].cell[1];
  550. if (curStep == 1)
  551. stepRelPos[curStep - 1] = cellDirToCell(stepPos[0][0],
  552. stepPos[0][1],
  553. stepPos[1][0],
  554. stepPos[1][1]);
  555. else
  556. stepRelPos[curStep - 1] = path1->stepList[curPath1Step].direction;
  557. curPath1Step++;
  558. curStep++;
  559. }
  560. //-----------------------------------------------------
  561. // Since we count the cur position as the first step...
  562. numSteps++;
  563. }
  564. else {
  565. //---------------------
  566. // step 0 = curPosition
  567. // step 1 = curStep
  568. // step 2 = curStep + 1
  569. // step 3 = curStep + 2
  570. numSteps = MOVECHUNK_NUM_STEPS;
  571. long numStepsLeft = path1->numSteps - curPath1Step;
  572. if (numStepsLeft < MOVECHUNK_NUM_STEPS) {
  573. numSteps = numStepsLeft;
  574. reachedEndOfPath1 = true;
  575. }
  576. for (long i = 0; i < numSteps; i++) {
  577. stepPos[curStep][0] = path1->stepList[curPath1Step].cell[0];
  578. stepPos[curStep][1] = path1->stepList[curPath1Step].cell[1];
  579. if (curStep == 1)
  580. stepRelPos[curStep - 1] = cellDirToCell(stepPos[0][0],
  581. stepPos[0][1],
  582. stepPos[1][0],
  583. stepPos[1][1]);
  584. else
  585. stepRelPos[curStep - 1] = path1->stepList[curPath1Step].direction;
  586. curPath1Step++;
  587. curStep++;
  588. }
  589. //-----------------------------------------------------
  590. // Since we count the cur position as the first step...
  591. numSteps++;
  592. }
  593. }
  594. bool path2Continues = path2 && (path1->globalStep > -1) && (path2->globalStep == (path1->globalStep + 1));
  595. if (reachedEndOfPath1 && path2Continues && (path2->numStepsWhenNotPaused > 0)) {
  596. //--------------------------------------------------------------
  597. // Splice second (queued) path into the chunk.
  598. // One assumption: if the first path is empty, it is assumed the
  599. // second path is irrelevant...
  600. // Assert(curStep > 1, curStep, " MoveChunk.build: path2 and curStep == 1 ");
  601. if (numSteps < (MOVECHUNK_NUM_STEPS + 1)) {
  602. //-----------------------------------------------------
  603. // Let's fill up the remaining steps in this chunk with
  604. // steps from the second path...
  605. long numAdditionalSteps = (MOVECHUNK_NUM_STEPS + 1) - numSteps;
  606. if (path2->numSteps < numAdditionalSteps)
  607. numAdditionalSteps = path2->numSteps;
  608. for (long i = 0; i < numAdditionalSteps; i++) {
  609. Assert(curStep <= MOVECHUNK_NUM_STEPS, curStep, " MoveChunk.build: path2 and bad curStep > MOVECHUNK_NUM_STEPS ");
  610. stepPos[curStep][0] = path2->stepList[i].cell[0];
  611. stepPos[curStep][1] = path2->stepList[i].cell[1];
  612. if (curStep == 1)
  613. stepRelPos[curStep - 1] = cellDirToCell(stepPos[0][0],
  614. stepPos[0][1],
  615. stepPos[1][0],
  616. stepPos[1][1]);
  617. else
  618. stepRelPos[curStep - 1] = path2->stepList[i].direction;
  619. curStep++;
  620. }
  621. numSteps += numAdditionalSteps;
  622. Assert(numSteps <= (MOVECHUNK_NUM_STEPS + 1), curStep, " MoveChunk.build: path2 and bad curStep > MOVECHUNK_NUM_STEPS ");
  623. }
  624. }
  625. }
  626. if ((numSteps < 1) || (numSteps > 4)) {
  627. //-----------------------------------------------------------------
  628. // For now, a safety guard. However, it would be nice to catch this
  629. // sooner and avoid this HACK. EXPANSION DISK!
  630. stepPos[0][0] = cellPos[0];
  631. stepPos[0][1] = cellPos[1];
  632. stepRelPos[0] = 0;
  633. stepRelPos[1] = 0;
  634. stepRelPos[2] = 0;
  635. numSteps = 1;
  636. }
  637. run = mover->getPilot()->getMoveRun();
  638. if (run && (mover->getObjectClass() == BATTLEMECH))
  639. run = ((BattleMechPtr)mover)->canRun();
  640. data = 0;
  641. }
  642. //---------------------------------------------------------------------------
  643. // DON'T USE THIS NOW, AS JUMPGOAL IS STORED IN THE STATUS CHUNK IF IT'S
  644. // A JUMP ORDER!
  645. void MoveChunk::build (MoverPtr mover, Stuff::Vector3D jumpGoal) {
  646. long jumpCoords[2];
  647. land->worldToCell(jumpGoal, jumpCoords[0], jumpCoords[1]);
  648. stepPos[0][0] = jumpCoords[0];
  649. stepPos[0][1] = jumpCoords[1];
  650. stepRelPos[0] = 0;
  651. stepRelPos[1] = 0;
  652. stepRelPos[2] = 0;
  653. numSteps = 1;
  654. run = false;
  655. moving = false;
  656. }
  657. //---------------------------------------------------------------------------
  658. #define DEBUG_MOVECHUNK
  659. void MoveChunk::pack (MoverPtr mover) {
  660. data = stepPos[0][0];
  661. data <<= MOVECHUNK_CELLPOS_BITS;
  662. data |= stepPos[0][1];
  663. data <<= MOVECHUNK_NUMSTEPS_BITS;
  664. data |= (numSteps - 1);
  665. data <<= MOVECHUNK_RUN_BITS;
  666. if (run)
  667. data |= 1;
  668. data <<= MOVECHUNK_MOVING_BITS;
  669. if (moving)
  670. data |= 1;
  671. data <<= MOVECHUNK_STEP_BITS;
  672. data |= stepRelPos[0];
  673. data <<= MOVECHUNK_STEP_BITS;
  674. data |= stepRelPos[1];
  675. data <<= MOVECHUNK_STEP_BITS;
  676. data |= stepRelPos[2];
  677. #ifdef DEBUG_MOVECHUNK
  678. //-------------------------
  679. // Lots'a error checking...
  680. if ((numSteps < 1) || (numSteps > 4)) {
  681. DebugMoveChunk(mover, this, NULL);
  682. char errMsg[1024];
  683. sprintf(errMsg, " MoveChunk.pack: bad numSteps %d (save mvchunk.dbg file) ", numSteps);
  684. Assert(false, numSteps, errMsg);
  685. }
  686. #endif
  687. }
  688. //---------------------------------------------------------------------------
  689. void MoveChunk::unpack (MoverPtr mover) {
  690. err = 0;
  691. stepRelPos[2] = (data & MOVECHUNK_STEP_MASK);
  692. data >>= MOVECHUNK_STEP_BITS;
  693. stepRelPos[1] = (data & MOVECHUNK_STEP_MASK);
  694. data >>= MOVECHUNK_STEP_BITS;
  695. stepRelPos[0] = (data & MOVECHUNK_STEP_MASK);
  696. data >>= MOVECHUNK_STEP_BITS;
  697. moving = ((data & MOVECHUNK_MOVING_MASK) != 0);
  698. data >>= MOVECHUNK_MOVING_BITS;
  699. run = ((data & MOVECHUNK_RUN_MASK) != 0);
  700. data >>= MOVECHUNK_RUN_BITS;
  701. numSteps = (data & MOVECHUNK_NUMSTEPS_MASK) + 1;
  702. data >>= MOVECHUNK_NUMSTEPS_BITS;
  703. stepPos[0][1] = (data & MOVECHUNK_CELLPOS_MASK);
  704. data >>= MOVECHUNK_CELLPOS_BITS;
  705. stepPos[0][0] = (data & MOVECHUNK_CELLPOS_MASK);
  706. //---------------------------------------------------------------
  707. // Finally, set the delta steps based upon the first step and the
  708. // relPos list...
  709. if ((numSteps < 1) || (numSteps > 4)) {
  710. //--------------------------------------------------------------------
  711. // HACK!!!!!!!!!! All of this unpackerr stuff is a last minute hack...
  712. err = 1;
  713. return;
  714. }
  715. if (numSteps > 1) {
  716. for (long i = 0; i < (numSteps - 1); i++) {
  717. long nextStep = i + 1;
  718. if ((i < 0) || (i > 2)) {
  719. err = 2;
  720. return;
  721. }
  722. if ((stepRelPos[i] < 0) || (stepRelPos[i] > 7)) {
  723. err = 3;
  724. return;
  725. }
  726. getAdjCell(stepPos[i][0],
  727. stepPos[i][1],
  728. stepRelPos[i],
  729. stepPos[nextStep][0],
  730. stepPos[nextStep][1]);
  731. }
  732. }
  733. #ifdef DEBUG_MOVECHUNK
  734. //-------------------------
  735. // Lots'a error checking...
  736. if ((numSteps < 1) || (numSteps > 4)) {
  737. DebugMoveChunk(mover, this, NULL);
  738. char errMsg[1024];
  739. sprintf(errMsg, " MoveChunk.unpack: bad numSteps %d (save mvchunk.dbg file) ", numSteps);
  740. Assert(false, numSteps, errMsg);
  741. }
  742. #endif
  743. }
  744. //---------------------------------------------------------------------------
  745. bool MoveChunk::equalTo (MoverPtr mover, MoveChunkPtr chunk) {
  746. if (numSteps != chunk->numSteps) {
  747. DebugMoveChunk(mover, this, chunk);
  748. return(false);
  749. }
  750. if (run != chunk->run) {
  751. DebugMoveChunk(mover, this, chunk);
  752. return(false);
  753. }
  754. if (moving != chunk->moving) {
  755. DebugMoveChunk(mover, this, chunk);
  756. return(false);
  757. }
  758. for (long i = 0; i < numSteps; i++)
  759. if ((stepPos[i][0] != chunk->stepPos[i][0]) || (stepPos[i][1] != chunk->stepPos[i][1])) {
  760. DebugMoveChunk(mover, this, chunk);
  761. return(false);
  762. }
  763. for (i = 0; i < numSteps - 1; i++) {
  764. if (stepRelPos[i] != chunk->stepRelPos[i]) {
  765. DebugMoveChunk(mover, this, chunk);
  766. return(false);
  767. }
  768. }
  769. return(true);
  770. }
  771. //---------------------------------------------------------------------------
  772. void Mover::setMoveChunk (MovePathPtr path, MoveChunkPtr chunk) {
  773. for (long i = 0; i < chunk->numSteps; i++) {
  774. PathStepPtr pathStep = &path->stepList[i];
  775. pathStep->cell[0] = chunk->stepPos[i][0];
  776. pathStep->cell[1] = chunk->stepPos[i][1];
  777. pathStep->distanceToGoal = 0.0;
  778. land->cellToWorld(pathStep->cell[0], pathStep->cell[1], pathStep->destination);
  779. }
  780. path->goal = path->stepList[chunk->numSteps - 1].destination;
  781. path->target.Zero();
  782. path->numSteps = path->numStepsWhenNotPaused = chunk->numSteps;
  783. path->curStep = 0;
  784. path->cost = -1;
  785. path->marked = false;
  786. path->globalStep = -1;
  787. }
  788. //***************************************************************************
  789. // Status Chunk class
  790. //***************************************************************************
  791. void DebugStatusChunk (MoverPtr mover, StatusChunkPtr chunk1, StatusChunkPtr chunk2) {
  792. ChunkDebugMsg[0] = NULL;
  793. char outString[512];
  794. if (mover) {
  795. sprintf(outString, "\nmover = %s (%d)\n", mover->name, mover->getPartId());
  796. strcat(ChunkDebugMsg, outString);
  797. }
  798. else
  799. strcat(ChunkDebugMsg, "\nmover = ???\n");
  800. if (chunk1) {
  801. strcat(ChunkDebugMsg, "\nCHUNK1\n");
  802. GameObjectPtr target = NULL;
  803. Stuff::Vector3D targetPoint;
  804. targetPoint.Zero();
  805. bool isTargetPoint = false;
  806. if (chunk1->targetType == STATUSCHUNK_TARGET_MOVER)
  807. target = (GameObjectPtr)MPlayer->moverRoster[chunk1->targetId];
  808. else if (chunk1->targetType == STATUSCHUNK_TARGET_TERRAIN)
  809. target = ObjectManager->findByPartId(chunk1->targetId);
  810. else if (chunk1->targetType == STATUSCHUNK_TARGET_SPECIAL)
  811. target = ObjectManager->findByPartId(chunk1->targetId);
  812. else if (chunk1->targetType == STATUSCHUNK_TARGET_LOCATION) {
  813. targetPoint.x = (float)chunk1->targetCellRC[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2;
  814. targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk1->targetCellRC[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2;
  815. targetPoint.z = (float)land->getTerrainElevation(targetPoint);
  816. isTargetPoint = true;
  817. }
  818. if (target) {
  819. if (target->isMover()) {
  820. sprintf(outString, "target = %s (%d)\n", ((MoverPtr)target)->name, target->getPartId());
  821. strcat(ChunkDebugMsg, outString);
  822. }
  823. else {
  824. sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId());
  825. strcat(ChunkDebugMsg, outString);
  826. }
  827. }
  828. else if (isTargetPoint) {
  829. sprintf(outString, "target point = (%f, %f, %f)\n", targetPoint.x, targetPoint.y, targetPoint.z);
  830. strcat(ChunkDebugMsg, outString);
  831. }
  832. else if (chunk1->targetType == STATUSCHUNK_TARGET_NONE) {
  833. sprintf(outString, "target = NONE\n");
  834. strcat(ChunkDebugMsg, outString);
  835. }
  836. else {
  837. strcat(ChunkDebugMsg, "target = ???\n");
  838. if (chunk1->targetType == STATUSCHUNK_TARGET_TERRAIN) {
  839. //---------------------------------------------------------------------
  840. // If the TargetType is terrain, let's at least list all of the terrain
  841. // objects in this Block/Vertex...
  842. long numObjsInTile = 0;
  843. long partId = chunk1->targetId - chunk1->targetItemNumber;
  844. for (long i = 0; i < MAX_TERRAIN_TILE_ITEMS; i++) {
  845. GameObjectPtr curTarget = ObjectManager->findByPartId(partId + i);
  846. if (curTarget) {
  847. numObjsInTile++;
  848. sprintf(outString, " %d: objClass %d (%d)\n", i, curTarget->getObjectClass(), curTarget->getPartId());
  849. strcat(ChunkDebugMsg, outString);
  850. }
  851. }
  852. if (numObjsInTile > 0) {
  853. sprintf(outString, " There are %d terrain objects in this tile.\n", numObjsInTile);
  854. strcat(ChunkDebugMsg, outString);
  855. }
  856. }
  857. }
  858. sprintf(outString, "bodyState = %d\n", chunk1->bodyState);
  859. strcat(ChunkDebugMsg, outString);
  860. sprintf(outString, "targetType = %d\n", chunk1->targetType);
  861. strcat(ChunkDebugMsg, outString);
  862. sprintf(outString, "targetId = %d\n", chunk1->targetId);
  863. strcat(ChunkDebugMsg, outString);
  864. sprintf(outString, "targetBlockOrTrainNumber = %d\n", chunk1->targetBlockOrTrainNumber);
  865. strcat(ChunkDebugMsg, outString);
  866. sprintf(outString, "targetVertexOrCarNumber = %d\n", chunk1->targetVertexOrCarNumber);
  867. strcat(ChunkDebugMsg, outString);
  868. sprintf(outString, "targetItemNumber = %d\n", chunk1->targetItemNumber);
  869. strcat(ChunkDebugMsg, outString);
  870. sprintf(outString, "targetCellRC = (%d, %d)\n", chunk1->targetCellRC[0], chunk1->targetCellRC[1]);
  871. strcat(ChunkDebugMsg, outString);
  872. sprintf(outString, "ejectOrderGiven = %c\n", chunk1->ejectOrderGiven ? 'T' : 'F');
  873. strcat(ChunkDebugMsg, outString);
  874. sprintf(outString, "jumpOrder = %c\n", chunk1->jumpOrder ? 'T' : 'F');
  875. strcat(ChunkDebugMsg, outString);
  876. sprintf(outString, "data = %x\n", chunk1->data);
  877. strcat(ChunkDebugMsg, outString);
  878. }
  879. if (chunk2) {
  880. strcat(ChunkDebugMsg, "\nCHUNK2\n");
  881. GameObjectPtr target = NULL;
  882. Stuff::Vector3D targetPoint;
  883. targetPoint.Zero();
  884. bool isTargetPoint = false;
  885. if (chunk1->targetType == STATUSCHUNK_TARGET_MOVER)
  886. target = (GameObjectPtr)MPlayer->moverRoster[chunk2->targetId];
  887. else if (chunk1->targetType == STATUSCHUNK_TARGET_TERRAIN)
  888. target = ObjectManager->findByPartId(chunk2->targetId);
  889. else if (chunk1->targetType == STATUSCHUNK_TARGET_SPECIAL)
  890. target = ObjectManager->findByPartId(chunk2->targetId);
  891. else if (chunk1->targetType == STATUSCHUNK_TARGET_LOCATION) {
  892. targetPoint.x = (float)chunk2->targetCellRC[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2;
  893. targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk2->targetCellRC[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2;
  894. targetPoint.z = (float)land->getTerrainElevation(targetPoint);
  895. isTargetPoint = true;
  896. }
  897. if (target) {
  898. if (target->isMover()) {
  899. sprintf(outString, "target = %s (%d)\n", ((MoverPtr)target)->name, target->getPartId());
  900. strcat(ChunkDebugMsg, outString);
  901. }
  902. else {
  903. sprintf(outString, "target = objClass %d (%d)\n", target->getObjectClass(), target->getPartId());
  904. strcat(ChunkDebugMsg, outString);
  905. }
  906. }
  907. else if (isTargetPoint) {
  908. sprintf(outString, "target point = (%f, %f, %f)\n", targetPoint.x, targetPoint.y, targetPoint.z);
  909. strcat(ChunkDebugMsg, outString);
  910. }
  911. else if (chunk1->targetType == STATUSCHUNK_TARGET_NONE) {
  912. sprintf(outString, "target = NONE\n");
  913. strcat(ChunkDebugMsg, outString);
  914. }
  915. else {
  916. strcat(ChunkDebugMsg, "target = ???\n");
  917. if (chunk2->targetType == STATUSCHUNK_TARGET_TERRAIN) {
  918. //---------------------------------------------------------------------
  919. // If the TargetType is terrain, let's at least list all of the terrain
  920. // objects in this Block/Vertex...
  921. long numObjsInTile = 0;
  922. long partId = chunk1->targetId - chunk2->targetItemNumber;
  923. for (long i = 0; i < MAX_TERRAIN_TILE_ITEMS; i++) {
  924. GameObjectPtr curTarget = ObjectManager->findByPartId(partId + i);
  925. if (curTarget) {
  926. numObjsInTile++;
  927. sprintf(outString, " %d: objClass %d (%d)\n", i, curTarget->getObjectClass(), curTarget->getPartId());
  928. strcat(ChunkDebugMsg, outString);
  929. }
  930. }
  931. if (numObjsInTile > 0) {
  932. sprintf(outString, " There are %d terrain objects in this tile.\n", numObjsInTile);
  933. strcat(ChunkDebugMsg, outString);
  934. }
  935. }
  936. }
  937. sprintf(outString, "bodyState = %d\n", chunk2->bodyState);
  938. strcat(ChunkDebugMsg, outString);
  939. sprintf(outString, "targetType = %d\n", chunk2->targetType);
  940. strcat(ChunkDebugMsg, outString);
  941. sprintf(outString, "targetId = %d\n", chunk2->targetId);
  942. strcat(ChunkDebugMsg, outString);
  943. sprintf(outString, "targetBlockOrTrainNumber = %d\n", chunk2->targetBlockOrTrainNumber);
  944. strcat(ChunkDebugMsg, outString);
  945. sprintf(outString, "targetVertexOrCarNumber = %d\n", chunk2->targetVertexOrCarNumber);
  946. strcat(ChunkDebugMsg, outString);
  947. sprintf(outString, "targetItemNumber = %d\n", chunk2->targetItemNumber);
  948. strcat(ChunkDebugMsg, outString);
  949. sprintf(outString, "targetCellRC = (%d, %d)\n", chunk2->targetCellRC[0], chunk2->targetCellRC[1]);
  950. strcat(ChunkDebugMsg, outString);
  951. sprintf(outString, "ejectOrderGiven = %c\n", chunk2->ejectOrderGiven ? 'T' : 'F');
  952. strcat(ChunkDebugMsg, outString);
  953. sprintf(outString, "jumpOrder = %c\n", chunk2->jumpOrder ? 'T' : 'F');
  954. strcat(ChunkDebugMsg, outString);
  955. sprintf(outString, "data = %x\n", chunk2->data);
  956. strcat(ChunkDebugMsg, outString);
  957. }
  958. File* debugFile = new File;
  959. debugFile->create("stchunk.dbg");
  960. debugFile->writeString(ChunkDebugMsg);
  961. debugFile->close();
  962. delete debugFile;
  963. debugFile = NULL;
  964. ExceptionGameMsg = ChunkDebugMsg;
  965. }
  966. //---------------------------------------------------------------------------
  967. void* StatusChunk::operator new (size_t ourSize) {
  968. void* result;
  969. result = systemHeap->Malloc(ourSize);
  970. return(result);
  971. }
  972. //---------------------------------------------------------------------------
  973. void StatusChunk::operator delete (void* us) {
  974. systemHeap->Free(us);
  975. }
  976. //---------------------------------------------------------------------------
  977. void StatusChunk::build (MoverPtr mover) {
  978. bodyState = 0;
  979. data = 0;
  980. }
  981. //---------------------------------------------------------------------------
  982. #define DEBUG_STATUSCHUNK
  983. //#define ASSERT_STATUSCHUNK
  984. void StatusChunk::pack (MoverPtr mover) {
  985. data = 0;
  986. if (jumpOrder) {
  987. data |= targetCellRC[0];
  988. data <<= STATUSCHUNK_CELLPOS_BITS;
  989. data |= targetCellRC[1];
  990. data <<= STATUSCHUNK_TARGETTYPE_BITS;
  991. }
  992. else {
  993. switch (targetType) {
  994. case STATUSCHUNK_TARGET_MOVER:
  995. //----------------
  996. // Mover Target...
  997. data |= targetId;
  998. data <<= STATUSCHUNK_TARGETTYPE_BITS;
  999. break;
  1000. case STATUSCHUNK_TARGET_TERRAIN:
  1001. //-------------------------
  1002. // Terrain Object Target...
  1003. data |= (targetId - MIN_TERRAIN_PART_ID);
  1004. data <<= STATUSCHUNK_TARGETTYPE_BITS;
  1005. break;
  1006. case STATUSCHUNK_TARGET_SPECIAL:
  1007. data |= targetBlockOrTrainNumber;
  1008. data <<= STATUSCHUNK_TRAINCAR_BITS;
  1009. data |= targetVertexOrCarNumber;
  1010. data <<= STATUSCHUNK_TARGETTYPE_BITS;
  1011. break;
  1012. case STATUSCHUNK_TARGET_LOCATION:
  1013. //-----------------------------------------
  1014. // Must be a Location Target (or a Miss)...
  1015. data |= targetCellRC[0];
  1016. data <<= STATUSCHUNK_CELLPOS_BITS;
  1017. data |= targetCellRC[1];
  1018. data <<= STATUSCHUNK_TARGETTYPE_BITS;
  1019. break;
  1020. }
  1021. }
  1022. data |= targetType;
  1023. data <<= STATUSCHUNK_EJECTORDER_BITS;
  1024. if (ejectOrderGiven)
  1025. data |= 1;
  1026. data <<= STATUSCHUNK_JUMPORDER_BITS;
  1027. if (jumpOrder)
  1028. data |= 1;
  1029. data <<= STATUSCHUNK_BODYSTATE_BITS;
  1030. data |= bodyState;
  1031. #ifdef DEBUG_STATUSCHUNK
  1032. //-------------------------
  1033. // Lots'a error checking...
  1034. switch (targetType) {
  1035. case STATUSCHUNK_TARGET_NONE:
  1036. break;
  1037. case STATUSCHUNK_TARGET_MOVER: {
  1038. if ((targetId < 0) || (targetId >= MAX_MULTIPLAYER_MOVERS)) {
  1039. #ifdef ASSERT_STATUSCHUNK
  1040. DebugStatusChunk(mover, this, NULL);
  1041. char errMsg[1024];
  1042. sprintf(errMsg, " StatusChunk.pack: bad targetId %d (save stchunk.dbg file) ", targetId);
  1043. Assert(false, targetId, errMsg);
  1044. #else
  1045. StatusChunkUnpackErr = 1;
  1046. #endif
  1047. }
  1048. GameObjectPtr target = (GameObjectPtr)MPlayer->moverRoster[targetId];
  1049. //----------------------------------------------------------------------------
  1050. // Mover targets could be NULL now, since we free them when they're destroyed.
  1051. //if (!target) {
  1052. // #ifdef ASSERT_STATUSCHUNK
  1053. // DebugStatusChunk(mover, this, NULL);
  1054. // char errMsg[1024];
  1055. // sprintf(errMsg, " StatusChunk.pack: NULL Mover Target (save stchunk.dbg file) ");
  1056. // Assert(false, targetId, errMsg);
  1057. // #else
  1058. // StatusChunkUnpackErr = 2;
  1059. // #endif
  1060. //}
  1061. }
  1062. break;
  1063. case STATUSCHUNK_TARGET_TERRAIN: {
  1064. GameObjectPtr target = ObjectManager->findByPartId(targetId);
  1065. if (!target) {
  1066. #ifdef ASSERT_STATUSCHUNK
  1067. DebugStatusChunk(mover, this, NULL);
  1068. char errMsg[1024];
  1069. sprintf(errMsg, " StatusChunk.pack: NULL Terrain Target (save stchunk.dbg file) ");
  1070. Assert(false, targetId, errMsg);
  1071. #else
  1072. StatusChunkUnpackErr = 3;
  1073. #endif
  1074. }
  1075. }
  1076. break;
  1077. case STATUSCHUNK_TARGET_SPECIAL: {
  1078. GameObjectPtr target = ObjectManager->findByPartId(targetId);
  1079. if (!target) {
  1080. #ifdef ASSERT_STATUSCHUNK
  1081. DebugStatusChunk(mover, this, NULL);
  1082. char errMsg[1024];
  1083. sprintf(errMsg, " StatusChunk.pack: NULL Special Target (save stchunk.dbg file) ");
  1084. Assert(false, targetId, errMsg);
  1085. #else
  1086. StatusChunkUnpackErr = 4;
  1087. #endif
  1088. }
  1089. }
  1090. break;
  1091. case STATUSCHUNK_TARGET_LOCATION:
  1092. break;
  1093. default:
  1094. #ifdef ASSERT_STATUSCHUNK
  1095. DebugStatusChunk(mover, this, NULL);
  1096. char errMsg[1024];
  1097. sprintf(errMsg, " StatusChunk.pack: bad targetType %d (save stchunk.dbg file) ", targetType);
  1098. Assert(false, targetType, errMsg);
  1099. #else
  1100. StatusChunkUnpackErr = 5;
  1101. #endif
  1102. }
  1103. #endif
  1104. }
  1105. //---------------------------------------------------------------------------
  1106. void StatusChunk::unpack (MoverPtr mover) {
  1107. StatusChunkUnpackErr = 0;
  1108. unsigned long tempData = data;
  1109. bodyState = (tempData & STATUSCHUNK_BODYSTATE_MASK);
  1110. tempData >>= STATUSCHUNK_BODYSTATE_BITS;
  1111. jumpOrder = ((tempData & STATUSCHUNK_JUMPORDER_MASK) != 0);
  1112. tempData >>= STATUSCHUNK_JUMPORDER_BITS;
  1113. // if (jumpOrder)
  1114. // OutputDebugString("JUMP ORDER\n");
  1115. ejectOrderGiven = ((tempData & STATUSCHUNK_EJECTORDER_MASK) != 0);
  1116. tempData >>= STATUSCHUNK_EJECTORDER_BITS;
  1117. targetType = (tempData & STATUSCHUNK_TARGETTYPE_MASK);
  1118. tempData >>= STATUSCHUNK_TARGETTYPE_BITS;
  1119. if (jumpOrder) {
  1120. targetCellRC[1] = (tempData & STATUSCHUNK_CELLPOS_MASK);
  1121. tempData >>= STATUSCHUNK_CELLPOS_BITS;
  1122. targetCellRC[0] = (tempData & STATUSCHUNK_CELLPOS_MASK);
  1123. }
  1124. else {
  1125. switch (targetType) {
  1126. case STATUSCHUNK_TARGET_NONE:
  1127. break;
  1128. case STATUSCHUNK_TARGET_MOVER:
  1129. //----------------
  1130. // Mover Target...
  1131. targetId = (tempData & STATUSCHUNK_MOVERINDEX_MASK);
  1132. break;
  1133. case STATUSCHUNK_TARGET_TERRAIN:
  1134. //-------------------------
  1135. // Terrain Object Target...
  1136. targetId = (tempData & STATUSCHUNK_TERRAINPARTID_MASK) + MIN_TERRAIN_PART_ID;
  1137. break;
  1138. case STATUSCHUNK_TARGET_SPECIAL:
  1139. //-----------------------
  1140. // Train Object Target...
  1141. targetVertexOrCarNumber = (tempData & STATUSCHUNK_TRAIN_MASK);
  1142. tempData >>= STATUSCHUNK_TRAIN_BITS;
  1143. targetBlockOrTrainNumber = (tempData & STATUSCHUNK_TRAINCAR_MASK);
  1144. tempData >>= STATUSCHUNK_TRAINCAR_BITS;
  1145. if (targetBlockOrTrainNumber == 128)
  1146. targetId = MIN_CAMERA_DRONE_ID + targetVertexOrCarNumber;
  1147. else
  1148. targetId = MIN_TRAIN_PART_ID +
  1149. targetBlockOrTrainNumber * MAX_TRAIN_CARS +
  1150. targetVertexOrCarNumber;
  1151. break;
  1152. case STATUSCHUNK_TARGET_LOCATION:
  1153. //-----------------------------------------
  1154. // Must be a Location Target (or a Miss)...
  1155. targetCellRC[1] = (tempData & STATUSCHUNK_CELLPOS_MASK);
  1156. tempData >>= STATUSCHUNK_CELLPOS_BITS;
  1157. targetCellRC[0] = (tempData & STATUSCHUNK_CELLPOS_MASK);
  1158. break;
  1159. }
  1160. }
  1161. #ifdef DEBUG_STATUSCHUNK
  1162. //-------------------------
  1163. // Lots'a error checking...
  1164. switch (targetType) {
  1165. case STATUSCHUNK_TARGET_NONE:
  1166. break;
  1167. case STATUSCHUNK_TARGET_MOVER: {
  1168. if ((targetId < 0) || (targetId >= MAX_MULTIPLAYER_MOVERS)) {
  1169. #ifdef ASSERT_STATUSCHUNK
  1170. DebugStatusChunk(mover, this, NULL);
  1171. char errMsg[1024];
  1172. sprintf(errMsg, " StatusChunk.unpack: bad targetId %d (save stchunk.dbg file) ", targetId);
  1173. Assert(false, targetId, errMsg);
  1174. #else
  1175. StatusChunkUnpackErr = 1;
  1176. #endif
  1177. }
  1178. GameObjectPtr target = (GameObjectPtr)MPlayer->moverRoster[targetId];
  1179. //----------------------------------------------------------------------------
  1180. // Mover targets could be NULL now, since we free them when they're destroyed.
  1181. //if (!target) {
  1182. // #ifdef ASSERT_STATUSCHUNK
  1183. // DebugStatusChunk(mover, this, NULL);
  1184. // char errMsg[1024];
  1185. // sprintf(errMsg, " StatusChunk.unpack: NULL Mover Target (save stchunk.dbg file) ");
  1186. // Assert(false, targetId, errMsg);
  1187. // #else
  1188. // StatusChunkUnpackErr = 2;
  1189. // #endif
  1190. //}
  1191. }
  1192. break;
  1193. case STATUSCHUNK_TARGET_TERRAIN: {
  1194. GameObjectPtr target = ObjectManager->findByPartId(targetId);
  1195. if (!target) {
  1196. #ifdef ASSERT_STATUSCHUNK
  1197. DebugStatusChunk(mover, this, NULL);
  1198. char errMsg[1024];
  1199. sprintf(errMsg, " StatusChunk.unpack: NULL Terrain Target (save stchunk.dbg file) ");
  1200. Assert(false, targetId, errMsg);
  1201. #else
  1202. StatusChunkUnpackErr = 3;
  1203. #endif
  1204. }
  1205. }
  1206. break;
  1207. case STATUSCHUNK_TARGET_SPECIAL: {
  1208. GameObjectPtr target = ObjectManager->findByPartId(targetId);
  1209. if (!target) {
  1210. #ifdef ASSERT_STATUSCHUNK
  1211. DebugStatusChunk(mover, this, NULL);
  1212. char errMsg[1024];
  1213. sprintf(errMsg, " StatusChunk.unpack: NULL Special Target (save stchunk.dbg file) ");
  1214. Assert(false, targetId, errMsg);
  1215. #else
  1216. StatusChunkUnpackErr = 4;
  1217. #endif
  1218. }
  1219. }
  1220. break;
  1221. case STATUSCHUNK_TARGET_LOCATION:
  1222. break;
  1223. default:
  1224. #ifdef ASSERT_STATUSCHUNK
  1225. DebugStatusChunk(mover, this, NULL);
  1226. char errMsg[1024];
  1227. sprintf(errMsg, " StatusChunk.unpack: bad targetType %d (save stchunk.dbg file) ", targetType);
  1228. Assert(false, targetType, errMsg);
  1229. #else
  1230. StatusChunkUnpackErr = 5;
  1231. #endif
  1232. }
  1233. #endif
  1234. }
  1235. //---------------------------------------------------------------------------
  1236. bool StatusChunk::equalTo (StatusChunkPtr chunk) {
  1237. if (bodyState != chunk->bodyState) {
  1238. DebugStatusChunk(NULL, this, chunk);
  1239. return(false);
  1240. }
  1241. if (ejectOrderGiven != chunk->ejectOrderGiven) {
  1242. DebugStatusChunk(NULL, this, chunk);
  1243. return(false);
  1244. }
  1245. if (jumpOrder != chunk->jumpOrder) {
  1246. DebugStatusChunk(NULL, this, chunk);
  1247. return(false);
  1248. }
  1249. if (targetType != chunk->targetType) {
  1250. DebugStatusChunk(NULL, this, chunk);
  1251. return(false);
  1252. }
  1253. if (targetId != chunk->targetId) {
  1254. DebugStatusChunk(NULL, this, chunk);
  1255. return(false);
  1256. }
  1257. if (targetCellRC[0] != chunk->targetCellRC[0]) {
  1258. DebugStatusChunk(NULL, this, chunk);
  1259. return(false);
  1260. }
  1261. if (targetCellRC[1] != chunk->targetCellRC[1]) {
  1262. DebugStatusChunk(NULL, this, chunk);
  1263. return(false);
  1264. }
  1265. return(true);
  1266. }
  1267. //***************************************************************************
  1268. // MOVER DYNAMICS class
  1269. //***************************************************************************
  1270. void MoverDynamics::init (void) {
  1271. type = DYNAMICS_BASE;
  1272. }
  1273. //---------------------------------------------------------------------------
  1274. void MoverDynamics::init (DynamicsType newType) {
  1275. type = newType;
  1276. switch (newType) {
  1277. case DYNAMICS_MECH:
  1278. max.mech.torsoYawRate = 0;
  1279. max.mech.torsoYaw = 0;
  1280. break;
  1281. case DYNAMICS_GROUNDVEHICLE:
  1282. max.groundVehicle.yawRate = 0;
  1283. //cur.groundVehicle.yaw = 0.0;
  1284. break;
  1285. case DYNAMICS_ELEMENTAL:
  1286. max.elemental.yawRate = 0;
  1287. //cur.elemental.yaw = 0.0;
  1288. break;
  1289. default:
  1290. Fatal(newType, " ugh ");
  1291. }
  1292. }
  1293. //---------------------------------------------------------------------------
  1294. void MoverDynamics::init (CSVFilePtr dynamicsFile) {
  1295. if (type == DYNAMICS_MECH)
  1296. {
  1297. //Its a new day!!!
  1298. dynamicsFile->readLong(8,5,max.mech.torsoYawRate);
  1299. dynamicsFile->readLong(9,5,max.mech.torsoYaw);
  1300. }
  1301. else if (type == DYNAMICS_GROUNDVEHICLE) {
  1302. /*
  1303. long result = 0;
  1304. result = dynamicsFile->seekBlock("VehicleDynamics");
  1305. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 0 ");
  1306. result = dynamicsFile->readIdLong("maxTurretYawRate",max.groundVehicle.turretYawRate);
  1307. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 1 ");
  1308. result = dynamicsFile->readIdLong("maxTurretYaw",max.groundVehicle.turretYaw);
  1309. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 2 ");
  1310. result = dynamicsFile->readIdLong("maxVehicleYawRate",max.groundVehicle.yawRate);
  1311. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 3 ");
  1312. if (max.groundVehicle.yawRate < 720)
  1313. max.groundVehicle.yawRate = 720;
  1314. result = dynamicsFile->readIdLong("maxVehiclePivotRate",max.groundVehicle.pivotRate);
  1315. if (result != NO_ERR)
  1316. max.groundVehicle.pivotRate = (long)((float)max.groundVehicle.yawRate * GroundVehiclePivotYawMultiplier);
  1317. result = dynamicsFile->readIdFloat("maxAccel",max.groundVehicle.accel);
  1318. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 4 ");
  1319. result = dynamicsFile->readIdFloat("maxVelocity",max.groundVehicle.speed);
  1320. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 5 ");
  1321. // Frank Hack
  1322. max.groundVehicle.accel = max.groundVehicle.speed * 5.0;
  1323. */
  1324. }
  1325. else if (type == DYNAMICS_ELEMENTAL) {
  1326. Fatal(type, " MoverDynamics.init: bad elemental ");
  1327. }
  1328. else
  1329. Fatal(type, " MoverDynamics.init: bad type ");
  1330. }
  1331. //---------------------------------------------------------------------------
  1332. void MoverDynamics::init (FitIniFilePtr dynamicsFile)
  1333. {
  1334. if (type == DYNAMICS_MECH)
  1335. {
  1336. }
  1337. else if (type == DYNAMICS_GROUNDVEHICLE) {
  1338. long result = 0;
  1339. result = dynamicsFile->seekBlock("VehicleDynamics");
  1340. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 0 ");
  1341. result = dynamicsFile->readIdLong("maxTurretYawRate",max.groundVehicle.turretYawRate);
  1342. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 1 ");
  1343. result = dynamicsFile->readIdLong("maxTurretYaw",max.groundVehicle.turretYaw);
  1344. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 2 ");
  1345. result = dynamicsFile->readIdLong("maxVehicleYawRate",max.groundVehicle.yawRate);
  1346. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 3 ");
  1347. if (max.groundVehicle.yawRate < 720)
  1348. max.groundVehicle.yawRate = 720;
  1349. result = dynamicsFile->readIdLong("maxVehiclePivotRate",max.groundVehicle.pivotRate);
  1350. if (result != NO_ERR)
  1351. max.groundVehicle.pivotRate = (long)((float)max.groundVehicle.yawRate * GroundVehiclePivotYawMultiplier);
  1352. result = dynamicsFile->readIdFloat("maxAccel",max.groundVehicle.accel);
  1353. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 4 ");
  1354. result = dynamicsFile->readIdFloat("maxVelocity",max.groundVehicle.speed);
  1355. Assert(result == NO_ERR, result, " MoverDynamics.init: vehicle error 5 ");
  1356. // Frank Hack
  1357. max.groundVehicle.accel = max.groundVehicle.speed * 5.0;
  1358. }
  1359. else if (type == DYNAMICS_ELEMENTAL) {
  1360. Fatal(type, " MoverDynamics.init: bad elemental ");
  1361. }
  1362. else
  1363. Fatal(type, " MoverDynamics.init: bad type ");
  1364. }
  1365. //***************************************************************************
  1366. // MOVER CONTROL class
  1367. //***************************************************************************
  1368. void MoverControl::reset (void) {
  1369. switch (dataType) {
  1370. case CONTROL_DATA_BASE:
  1371. break;
  1372. case CONTROL_DATA_MECH:
  1373. //settings.mech.throttle = 100;
  1374. settings.mech.rotate = 0.0;
  1375. settings.mech.facingRotate = 0.0f;
  1376. settings.mech.rotateTorso = 0;
  1377. settings.mech.rotateLeftArm = 0;
  1378. settings.mech.rotateRightArm = 0;
  1379. settings.mech.gestureGoal = -1;
  1380. settings.mech.blowLeftArm = false;
  1381. settings.mech.blowRightArm = false;
  1382. settings.mech.pivot = false;
  1383. break;
  1384. case CONTROL_DATA_GROUNDVEHICLE:
  1385. settings.groundVehicle.rotate = 0.0;
  1386. settings.groundVehicle.rotateTurret = 0.0;
  1387. settings.groundVehicle.gestureGoal = -1;
  1388. settings.groundVehicle.pivot = false;
  1389. settings.groundVehicle.isWalking = false;
  1390. settings.groundVehicle.throttle = 0.0;
  1391. break;
  1392. case CONTROL_DATA_ELEMENTAL:
  1393. break;
  1394. }
  1395. }
  1396. //---------------------------------------------------------------------------
  1397. void MoverControl::brake (void) {
  1398. switch (dataType) {
  1399. case CONTROL_DATA_BASE:
  1400. break;
  1401. case CONTROL_DATA_MECH:
  1402. settings.mech.throttle = 100;
  1403. settings.mech.gestureGoal = MECH_STATE_STANDING;
  1404. break;
  1405. case CONTROL_DATA_GROUNDVEHICLE:
  1406. settings.mech.throttle = 0;
  1407. break;
  1408. case CONTROL_DATA_ELEMENTAL:
  1409. break;
  1410. }
  1411. }
  1412. //---------------------------------------------------------------------------
  1413. void MoverControl::update (MoverPtr mover) {
  1414. switch (type) {
  1415. case CONTROL_AI:
  1416. mover->updateAIControl();
  1417. break;
  1418. case CONTROL_NET:
  1419. mover->updateNetworkControl();
  1420. break;
  1421. case CONTROL_PLAYER:
  1422. mover->updatePlayerControl();
  1423. break;
  1424. default:
  1425. Fatal(type, " MoverControl.update: bad control type ");
  1426. }
  1427. }
  1428. //---------------------------------------------------------------------------
  1429. long MoverControl::init (FitIniFilePtr controlFile) {
  1430. return(NO_ERR);
  1431. }
  1432. //***************************************************************************
  1433. // MOVER class
  1434. //***************************************************************************
  1435. long Mover::loadGameSystem (FitIniFilePtr mechFile, float visualRange) {
  1436. long result = 0;
  1437. result = mechFile->seekBlock("Pathfinding");
  1438. if (result != NO_ERR)
  1439. return(result);
  1440. // long longRangeMoveEnabled[MAX_TEAMS];
  1441. // result = mechFile->readIdLongArray("LongRangeMovementEnabled", longRangeMoveEnabled, NUM_TEAMS);
  1442. // if (result != NO_ERR)
  1443. // return(result);
  1444. // for (long i = 0; i < NUM_TEAMS; i++)
  1445. // LongRangeMovementEnabled[i] = (longRangeMoveEnabled[i] == 1);
  1446. result = mechFile->readIdLong("SimplePathTileRange", SimpleMovePathRange);
  1447. if (result != NO_ERR)
  1448. return(result);
  1449. result = mechFile->readIdFloat("DelayedOrderTime", DelayedOrderTime);
  1450. if (result != NO_ERR)
  1451. return(result);
  1452. result = mechFile->readIdFloat("MoveTimeOut", MoveTimeOut);
  1453. if (result != NO_ERR)
  1454. MoveTimeOut = 30.0;
  1455. result = mechFile->readIdFloat("MoveYieldTime", MoveYieldTime);
  1456. if (result != NO_ERR)
  1457. MoveYieldTime = 1.5;
  1458. result = mechFile->readIdLongArray("GroupMoveTrailLength", GroupMoveTrailLen, 2);
  1459. if (result != NO_ERR) {
  1460. GroupMoveTrailLen[0] = 0;
  1461. GroupMoveTrailLen[1] = 1;
  1462. }
  1463. result = mechFile->readIdLongArray("GroupMoveTrailLength", GroupMoveTrailLen, 2);
  1464. if (result != NO_ERR) {
  1465. GroupMoveTrailLen[0] = 0;
  1466. GroupMoveTrailLen[1] = 1;
  1467. }
  1468. result = mechFile->readIdFloat("GroupOrderGoalOffset", GroupOrderGoalOffset);
  1469. if (result != NO_ERR)
  1470. return(result);
  1471. result = mechFile->readIdFloatArray("MoveMarginOfError", Mover::marginOfError, 2);
  1472. if (result != NO_ERR)
  1473. return(result);
  1474. // for (long i = 0; i < NUM_OVERLAY_TYPES; i++)
  1475. // OverlayWeightIndex[i] = i * MAPCELL_DIM * MAPCELL_DIM;
  1476. // result = mechFile->readIdLongArray("OverlayCellCosts", OverlayWeightTable, NUM_OVERLAY_WEIGHT_CLASSES * NUM_OVERLAY_TYPES * MAPCELL_DIM * MAPCELL_DIM);
  1477. // if (result != NO_ERR)
  1478. // return(result);
  1479. result = mechFile->seekBlock("Mover:General");
  1480. Assert(result == NO_ERR, 0, "Couldn't find Mover:General block in gamesys.fit");
  1481. result = mechFile->readIdFloat("NewThreatMultiplier", newThreatMultiplier);
  1482. if (result != NO_ERR)
  1483. newThreatMultiplier = 3.0;
  1484. result = mechFile->readIdFloat("BlockCaptureRange", GameObject::blockCaptureRange);
  1485. Assert(result == NO_ERR, 0, "Couldn't find BlockCaptureRange in Mover:General block in gamesys.fit");
  1486. result = mechFile->readIdFloat("RefitTime", Mover::refitTime);
  1487. Assert(result == NO_ERR, 0, "Couldn't find RefitTime in Mover:General block in gamesys.fit");
  1488. result = mechFile->readIdFloat("RefitRange", Mover::refitRange);
  1489. Assert(result == NO_ERR, 0, "Couldn't find RefitRange in Mover:General block in gamesys.fit");
  1490. result = mechFile->readIdFloat("RefitAmount", Mover::refitAmount);
  1491. Assert(result == NO_ERR, 0, "Couldn't find RefitAmount in Mover:General block in gamesys.fit");
  1492. result = mechFile->readIdFloat("RefitVehicleArmorCost", Mover::refitCostArray[ARMOR_REFIT_COST][REFIT_VEHICLE]);
  1493. Assert(result == NO_ERR, 0, "Couldn't find RefitVehicleArmorCost in Mover:General block in gamesys.fit");
  1494. result = mechFile->readIdFloat("RecoverTime", Mover::recoverTime);
  1495. Assert(result == NO_ERR, 0, "Couldn't find RecoverTime in Mover:General block in gamesys.fit");
  1496. result = mechFile->readIdFloat("RecoverRange", Mover::recoverRange);
  1497. Assert(result == NO_ERR, 0, "Couldn't find RecoverRange in Mover:General block in gamesys.fit");
  1498. result = mechFile->readIdFloat("RecoverAmount", Mover::recoverAmount);
  1499. Assert(result == NO_ERR, 0, "Couldn't find RecoverAmount in Mover:General block in gamesys.fit");
  1500. result = mechFile->readIdFloat("RecoverCost", Mover::recoverCost);
  1501. Assert(result == NO_ERR, 0, "Couldn't find RecoverCost in Mover:General block in gamesys.fit");
  1502. result = mechFile->readIdFloat("RefitVehicleInternalCost", Mover::refitCostArray[INTERNAL_REFIT_COST][REFIT_VEHICLE]);
  1503. Assert(result == NO_ERR, 0, "Couldn't find RefitVehicleInternalCost in Mover:General block in gamesys.fit");
  1504. result = mechFile->readIdFloat("RefitVehiclePointsToAmmo", Mover::refitCostArray[AMMO_REFIT_COST][REFIT_VEHICLE]);
  1505. Assert(result == NO_ERR, 0, "Couldn't find RefitVehiclePointsToAmmo in Mover:General block in gamesys.fit");
  1506. result = mechFile->readIdFloat("RefitBayArmorCost", Mover::refitCostArray[ARMOR_REFIT_COST][REFIT_BAY]);
  1507. Assert(result == NO_ERR, 0, "Couldn't find RefitBayArmorCost in Mover:General block in gamesys.fit");
  1508. result = mechFile->readIdFloat("RefitBayInternalCost", Mover::refitCostArray[INTERNAL_REFIT_COST][REFIT_BAY]);
  1509. Assert(result == NO_ERR, 0, "Couldn't find RefitBayInternalCost in Mover:General block in gamesys.fit");
  1510. result = mechFile->readIdFloat("RefitBayAmmoCost", Mover::refitCostArray[AMMO_REFIT_COST][REFIT_BAY]);
  1511. Assert(result == NO_ERR, 0, "Couldn't find RefitBayAmmoCost in Mover:General block in gamesys.fit");
  1512. result = mechFile->readIdLongArray("IndirectFireWeaponComponentIndex",Mover::IndirectFireWeapons,20);
  1513. Assert(result == NO_ERR, 0, "Couldn't find IndirectFireWeaponComponentIndex in Mover:General block in gamesys.fit");
  1514. result = mechFile->readIdLongArray("AreaEffectWeaponComponentIndex",Mover::AreaEffectWeapons,20);
  1515. Assert(result == NO_ERR, 0, "Couldn't find AreaEffectWeaponComponentIndex in Mover:General block in gamesys.fit");
  1516. result = mechFile->seekBlock("Mover:FireWeapon");
  1517. if (result != NO_ERR)
  1518. return(result);
  1519. // result = mechFile->readIdFloatArray("WeaponFireModifiers", WeaponFireModifiers, NUM_WEAPONFIRE_MODIFIERS);
  1520. // if (result == NO_ERR) {
  1521. // for (long i = 0; i < NUM_WARRIOR_RANKS; i++)
  1522. // for (long j = 0; j < (NUM_MECH_CLASSES - 1); j++)
  1523. // RankVersusChassisCombatModifier[i][j + 1] = WeaponFireModifiers[7 + i * NUM_WARRIOR_RANKS + j];
  1524. // }
  1525. result = mechFile->readIdFloatArray("FireArc", FireArc, 3);
  1526. if (result != NO_ERR)
  1527. return(result);
  1528. for (long arc = 0; arc < 3; arc++)
  1529. FireArc[arc] /= 2.0;
  1530. result = mechFile->readIdLong("AimedFireAbort", AimedFireAbort);
  1531. if (result != NO_ERR)
  1532. return(result);
  1533. result = mechFile->readIdLongArray("AimedFireHitTable", AimedFireHitTable, 3);
  1534. if (result != NO_ERR)
  1535. return(result);
  1536. result = mechFile->readIdFloat("DisableAttackModifier", DisableAttackModifier);
  1537. if (result != NO_ERR)
  1538. return(result);
  1539. result = mechFile->readIdFloat("DisableGunneryModifier", DisableGunneryModifier);
  1540. if (result != NO_ERR)
  1541. return(result);
  1542. result = mechFile->readIdFloat("SalvageAttackModifier", SalvageAttackModifier);
  1543. if (result != NO_ERR)
  1544. return(result);
  1545. result = mechFile->readIdFloat("MaxStationaryTime", MaxStationaryTime);
  1546. if (result != NO_ERR)
  1547. return(result);
  1548. result = mechFile->readIdFloat("MaxTimeRevealed",MaxTimeRevealed);
  1549. if (result != NO_ERR)
  1550. return(result);
  1551. result = mechFile->readIdFloat("HeadShotModifier",HeadShotModifier);
  1552. if (result != NO_ERR)
  1553. HeadShotModifier = 0.08f;
  1554. result = mechFile->readIdFloat("ArmShotModifier",ArmShotModifier);
  1555. if (result != NO_ERR)
  1556. ArmShotModifier = 0.15f;
  1557. result = mechFile->readIdFloat("LegShotModifier",LegShotModifier);
  1558. if (result != NO_ERR)
  1559. LegShotModifier = 0.20f;
  1560. result = mechFile->seekBlock("Mover:Damage");
  1561. if (result != NO_ERR)
  1562. return(result);
  1563. result = mechFile->readIdLongArray("HitLevel", hitLevel, 2);
  1564. if (result != NO_ERR)
  1565. return(result);
  1566. result = mechFile->readIdFloat("PilotingCheckFactor", PilotingCheckFactor);
  1567. if (result != NO_ERR)
  1568. return(result);
  1569. result = mechFile->seekBlock("Components");
  1570. if (result != NO_ERR)
  1571. return(result);
  1572. result = mechFile->readIdLong("ClusterSizeSRM", ClusterSizeSRM);
  1573. if (result != NO_ERR)
  1574. return(result);
  1575. // Hard code--ultimately, we kill clusters in game code!
  1576. ClusterSizeSRM = 2;
  1577. result = mechFile->readIdLong("ClusterSizeLRM", ClusterSizeLRM);
  1578. if (result != NO_ERR)
  1579. return(result);
  1580. // Hard code--ultimately, we kill clusters in game code!
  1581. ClusterSizeLRM = 5;
  1582. result = mechFile->readIdLongArray("InnerSphereAntiMissile", AntiMissileSystemStats[0], 2);
  1583. if (result != NO_ERR)
  1584. return(result);
  1585. result = mechFile->readIdLongArray("ClanAntiMissile", AntiMissileSystemStats[1], 2);
  1586. if (result != NO_ERR)
  1587. return(result);
  1588. result = mechFile->seekBlock("Warrior");
  1589. if (result != NO_ERR)
  1590. return(result);
  1591. result = mechFile->readIdFloat("DefaultAttackRadius", DefaultAttackRadius);
  1592. if (result != NO_ERR)
  1593. DefaultAttackRadius = 275.0;
  1594. result = mechFile->readIdFloatArray("WarriorRankScale", WarriorRankScale, NUM_WARRIOR_RANKS);
  1595. if (result != NO_ERR)
  1596. return(result);
  1597. char profData[NUM_OFFSET_RANGES * 2];
  1598. result = mechFile->readIdCharArray("ProfessionalismTable", profData, NUM_OFFSET_RANGES * 2);
  1599. if (result != NO_ERR)
  1600. return(result);
  1601. for (long i = 0; i < NUM_OFFSET_RANGES; i++)
  1602. memcpy(&ProfessionalismOffsetTable[i], &profData[i * 2], 2);
  1603. result = mechFile->readIdCharArray("DecorumTable", profData, NUM_OFFSET_RANGES * 2);
  1604. if (result != NO_ERR)
  1605. return(result);
  1606. for (i = 0; i < NUM_OFFSET_RANGES; i++)
  1607. memcpy(&DecorumOffsetTable[i], &profData[i * 2], 2);
  1608. result = mechFile->readIdCharArray("AmmoTable", profData, 2 * 2);
  1609. if (result != NO_ERR)
  1610. return(result);
  1611. for (i = 0; i < 2; i++)
  1612. memcpy(&AmmoConservationModifiers[i], &profData[i * 2], 2);
  1613. #ifdef USEHEAT
  1614. result = mechFile->readIdFloat("WeaponHeatCheckDelay", WeaponHeatCheckDelay);
  1615. if (result != NO_ERR)
  1616. return(result);
  1617. #endif
  1618. result = mechFile->readIdFloat("PilotCheckHalfRate", PilotCheckHalfRate);
  1619. if (result != NO_ERR)
  1620. return(result);
  1621. result = mechFile->readIdLongArray("PilotCheckModifiers", PilotCheckModifierTable, 2);
  1622. if (result != NO_ERR)
  1623. return(result);
  1624. result = mechFile->readIdFloat("DamageRateFrequency", DamageRateFrequency);
  1625. if (result != NO_ERR)
  1626. return(result);
  1627. char charData[NUM_ATTITUDES * 6];
  1628. result = mechFile->readIdCharArray("AttitudeEffect", charData, NUM_ATTITUDES * 6);
  1629. if (result != NO_ERR)
  1630. return(result);
  1631. for (i = 0; i < NUM_ATTITUDES; i++)
  1632. memcpy(&AttitudeEffect[i], &charData[i * 6], 6);
  1633. result = mechFile->readIdFloat("MovementUpdateFrequency", MovementUpdateFrequency);
  1634. if (result != NO_ERR)
  1635. return(result);
  1636. result = mechFile->readIdFloat("CombatUpdateFrequency", CombatUpdateFrequency);
  1637. if (result != NO_ERR)
  1638. return(result);
  1639. result = mechFile->readIdFloat("CommandUpdateFrequency", CommandUpdateFrequency);
  1640. if (result != NO_ERR)
  1641. return(result);
  1642. result = mechFile->readIdFloat("PilotCheckUpdateFrequency", PilotCheckUpdateFrequency);
  1643. if (result != NO_ERR)
  1644. return(result);
  1645. // result = mechFile->readIdFloatArray("FireOddsTable", FireOddsTable, NUM_FIREODDS);
  1646. // if (result != NO_ERR)
  1647. // return(result);
  1648. result = mechFile->readIdLong("SkillIncreaseCap", MechWarrior::increaseCap);
  1649. Assert(result == NO_ERR, result, " Couldn't find SkillCap variable in Warrior block of gamesys.fit ");
  1650. result = mechFile->readIdFloat("SkillMax", MechWarrior::maxSkill);
  1651. Assert(result == NO_ERR, result, " Couldn't find SkillMax variable in Warrior block of gamesys.fit ");
  1652. result = mechFile->readIdFloat("SkillMin", MechWarrior::minSkill);
  1653. Assert(result == NO_ERR, result, " Couldn't find SkillMin variable in Warrior block of gamesys.fit ");
  1654. // result = mechFile->readIdLong("JumpSkillMod", PilotJumpMod);
  1655. // Assert(result == NO_ERR, result, " Couldn't find JumpSkillMod variable in Warrior block of gamesys.fit ");
  1656. result = mechFile->seekBlock("Sensors");
  1657. if (result != NO_ERR)
  1658. return(result);
  1659. // result = mechFile->readIdFloatArray("SensorModifiers", SensorModifier, 8);
  1660. // if (result != NO_ERR)
  1661. // return(result);
  1662. result = mechFile->readIdFloat("BaseSensorRollTarget", SensorBaseChance);
  1663. if (result != NO_ERR)
  1664. return(result);
  1665. result = mechFile->readIdFloat("SensorSkillFactor", SensorSkillFactor);
  1666. if (result != NO_ERR)
  1667. return(result);
  1668. result = mechFile->readIdFloat("BlockingObjectModifier", SensorBlockingObjectModifier);
  1669. if (result != NO_ERR)
  1670. return(result);
  1671. result = mechFile->readIdFloat("ShutdownMech", SensorShutDownMechModifier);
  1672. if (result != NO_ERR)
  1673. return(result);
  1674. #ifdef USEHEAT
  1675. result = mechFile->readIdFloat("HeatMultiplier", SensorHeatMultiplier);
  1676. if (result != NO_ERR)
  1677. return(result);
  1678. result = mechFile->readIdFloat("WeaponHeatMultiplier", SensorWeaponHeatMultiplier);
  1679. if (result != NO_ERR)
  1680. return(result);
  1681. result = mechFile->readIdFloatArray("HeatSourceModifiers", SensorHeatSourceModifiers, 2);
  1682. if (result != NO_ERR)
  1683. return(result);
  1684. #endif
  1685. float rangeTable[8];
  1686. result = mechFile->readIdFloatArray("SensorRangeModifier", rangeTable, 8);
  1687. if (result != NO_ERR)
  1688. return(result);
  1689. for (i = 0; i < 4; i++) {
  1690. SensorRangeModifier[i][0] = rangeTable[i * 2];
  1691. SensorRangeModifier[i][1] = rangeTable[i * 2 + 1];
  1692. }
  1693. float sizeTable[6];
  1694. result = mechFile->readIdFloatArray("SizeModifier", sizeTable, 6);
  1695. if (result != NO_ERR)
  1696. return(result);
  1697. for (i = 0; i < 3; i++) {
  1698. SensorSizeModifier[i][0] = sizeTable[i * 2];
  1699. SensorSizeModifier[i][1] = sizeTable[i * 2 + 1];
  1700. }
  1701. long masterIDs[9];
  1702. result = mechFile->readIdLongArray("SensorMasterIDs", masterIDs, 9);
  1703. if (result != NO_ERR)
  1704. return(result);
  1705. /*
  1706. float rangeMultipliers[9];
  1707. result = mechFile->readIdFloatArray("SensorRangeMultipliers", rangeMultipliers, 9);
  1708. if (result != NO_ERR)
  1709. return(result);
  1710. //------------------------------------------------------------------------------
  1711. // The following relies upon knowing the MasterComponentID for all of the sensor
  1712. // components...
  1713. for (i = 0; i < 9; i++)
  1714. if (masterIDs[i] > -1)
  1715. MasterComponentList[masterIDs[i]].setSensorRange(visualRange * rangeMultipliers[i]);
  1716. */
  1717. result = mechFile->readIdFloatArray("BlockingTerrainModifiers", SensorBlockingTerrain, 2);
  1718. if (result != NO_ERR)
  1719. return(result);
  1720. result = mechFile->seekBlock("Skills");
  1721. if (result != NO_ERR)
  1722. Fatal(result, "Couldn't find skill block in gamesys.fit");
  1723. result = mechFile->readIdFloatArray("Skill Attempt", SkillTry, 4);
  1724. if (result != NO_ERR)
  1725. return(result);
  1726. result = mechFile->readIdFloatArray("Skill Success", SkillSuccess, 4);
  1727. if (result != NO_ERR)
  1728. return(result);
  1729. result = mechFile->readIdFloat("WeaponHit", WeaponHit);
  1730. if (result != NO_ERR)
  1731. return(result);
  1732. result = mechFile->readIdFloatArray("KillSkillValues", KillSkill, 6);
  1733. if (result != NO_ERR)
  1734. return(result);
  1735. result = mechFile->readIdFloat("Sensor Contact Skill", SensorSkill);
  1736. if (result != NO_ERR)
  1737. return(result);
  1738. return(NO_ERR);
  1739. }
  1740. //---------------------------------------------------------------------------
  1741. void Mover::set (Mover copy) {
  1742. GameObject::set(copy);
  1743. //ID = copy.ID;
  1744. positionNormal = copy.positionNormal;
  1745. velocity = copy.velocity;
  1746. rotation = copy.rotation;
  1747. strncpy(name, copy.name, MAXLEN_MOVER_NAME - 1);
  1748. chassis = copy.chassis;
  1749. damageRateTally = copy.damageRateTally;
  1750. damageRateCheckTime = copy.damageRateCheckTime;
  1751. pilotCheckDamageTally = copy.pilotCheckDamageTally;
  1752. numBodyLocations = copy.numBodyLocations;
  1753. for (long i = 0; i < numBodyLocations; i++)
  1754. body[i] = copy.body[i];
  1755. fieldedCV = copy.fieldedCV;
  1756. numArmorLocations = copy.numArmorLocations;
  1757. for (i = 0; i < numArmorLocations; i++)
  1758. armor[i] = copy.armor[i];
  1759. }
  1760. //---------------------------------------------------------------------------
  1761. void Mover::init (bool create) {
  1762. GameObject::init(create);
  1763. if (initialize) {
  1764. killed = false;
  1765. lost = false;
  1766. salvaged = false;
  1767. appearance = NULL;
  1768. lowestWeaponNodeID = -2;
  1769. sensorSystem = NULL;
  1770. lastMapCell[0] = -1;
  1771. lastMapCell[1] = -1;
  1772. contactInfo = new ContactInfo;
  1773. pilot = NULL;
  1774. pilotHandle = 0;
  1775. numWeaponHitsHandled = 0;
  1776. prevTeamId = -1;
  1777. numMovers++;
  1778. }
  1779. objectClass = MOVER;
  1780. unitGroup = 2;
  1781. causeOfDeath = -1;
  1782. iconPictureIndex = 0;
  1783. lowestWeaponNodeZ = -9999.9f;
  1784. positionNormal.Zero();
  1785. velocity.Zero();
  1786. name[0] = NULL;
  1787. chassis = 0;
  1788. startDisabled = false;
  1789. pathLocks = true;
  1790. damageRateTally = 0;
  1791. damageRateCheckTime = 1.0;
  1792. pilotCheckDamageTally = 0;
  1793. numBodyLocations = 0;
  1794. fieldedCV = 0;
  1795. numArmorLocations = 0;
  1796. attackRange = FIRERANGE_OPTIMAL;
  1797. suppressionFire = 0;
  1798. numArmorLocations = 0;
  1799. numOther = 0;
  1800. numWeapons = 0;
  1801. numAmmos = 0;
  1802. numAmmoTypes = 0;
  1803. cockpit = 255;
  1804. engine = 255;
  1805. lifeSupport = 255;
  1806. sensor = 255;
  1807. ecm = 255;
  1808. probe = 255;
  1809. jumpJets = 255;
  1810. nullSignature = 255;
  1811. maxWeaponEffectiveness = 0.0;
  1812. weaponEffectiveness = 0.0;
  1813. minRange = 0.0;
  1814. maxRange = 0.0;
  1815. optimalRange = 0.0;
  1816. numFunctionalWeapons = 0;
  1817. numAntiMissileSystems = 0;
  1818. engineBlowTime = -1.0;
  1819. maxMoveSpeed = 0.0;
  1820. shutDownThisFrame = false;
  1821. startUpThisFrame = false;
  1822. disableThisFrame = false;
  1823. //
  1824. teamId = -1;
  1825. groupId = -1;
  1826. squadId = -1;
  1827. selectionIndex = -1;
  1828. teamRosterIndex = -1;
  1829. commanderId = -1;
  1830. unitGroup = 2; // the thing the user sets by hitting ctrl and a number
  1831. pilotCheckModifier = -1;
  1832. prevPilotCheckUpdate = 0.0;
  1833. prevPilotCheckModifier = 0;
  1834. prevPilotCheckDelta = 0;
  1835. failedPilotingCheck = false;
  1836. collisionFreeFromWID = 0;
  1837. collisionFreeTime = -1.0;
  1838. lastWeaponEffectivenessCalc = 0.0;
  1839. lastOptimalRangeCalc = 0.0;
  1840. challengerWID = 0;
  1841. lastGesture = -1;
  1842. control.init();
  1843. dynamics.init();
  1844. //netPlayerId = 0;
  1845. if (MPlayer) {
  1846. #ifdef USE_STRING_RESOURCES
  1847. cLoadString(thisInstance,IDS_LOSTPLAYER+languageOffset,netPlayerName,254);
  1848. #else
  1849. sprintf(netPlayerName, "%s", "NEED STRING RESOURCES");
  1850. #endif
  1851. }
  1852. else
  1853. netPlayerName[0] = NULL;
  1854. localMoverId = -1;
  1855. netRosterIndex = -1;
  1856. statusChunk.init();
  1857. newMoveChunk = false;
  1858. moveChunk.init();
  1859. numWeaponFireChunks[CHUNK_RECEIVE] = 0;
  1860. numWeaponFireChunks[CHUNK_SEND] = 0;
  1861. numCriticalHitChunks[CHUNK_RECEIVE] = 0;
  1862. numCriticalHitChunks[CHUNK_SEND] = 0;
  1863. numRadioChunks[CHUNK_RECEIVE] = 0;
  1864. numRadioChunks[CHUNK_SEND] = 0;
  1865. ejectOrderGiven = false;
  1866. timeLeft = 1.0;
  1867. exploding = false;
  1868. withdrawing = false;
  1869. yieldTimeLeft = 0.0;
  1870. lastValidPosition.Zero();
  1871. pivotDirection = 0;
  1872. lastHustleTime = -999.0;
  1873. salvageVehicle = false;
  1874. markDistanceMoved = 0.0;
  1875. refitBuddyWID = 0;
  1876. recoverBuddyWID = 0;
  1877. crashAvoidSelf = 1;
  1878. crashAvoidPath = 1;
  1879. crashBlockSelf = 1;
  1880. crashBlockPath = 1;
  1881. crashYieldTime = 1.5;
  1882. pathLockLength = 0;
  1883. moveType = MOVETYPE_GROUND;
  1884. moveLevel = 0;
  1885. moveCenter.Zero();
  1886. moveRadius = -1.0;
  1887. followRoads = false;
  1888. overlayWeightClass = 0;
  1889. timeToClearSelection = 0.0;
  1890. timeSinceMoving = 0.0;
  1891. timeSinceFiredLast = MaxTimeRevealed;
  1892. lastMovingTargetWID = 0;
  1893. mechSalvage = true;
  1894. setTangible(true);
  1895. setFlag(OBJECT_FLAG_SENSOR, true);
  1896. if (!sortList) {
  1897. sortList = new SortList;
  1898. if (!sortList)
  1899. Fatal(0, " Unable to create Mover::sortList ");
  1900. sortList->init(4000);
  1901. }
  1902. teleportPosition.x = -999999.0;
  1903. teleportPosition.y = -999999.0;
  1904. teleportPosition.z = -999999.0;
  1905. debugPage = 0;
  1906. conStat = 0;
  1907. fadeTime = 0.0f;
  1908. alphaValue = 0x0;
  1909. if ( !holdFireIconHandle )
  1910. {
  1911. FullPathFileName path;
  1912. path.init( artPath, "blip", ".tga" );
  1913. holdFireIconHandle = mcTextureManager->loadTexture( path, gos_Texture_Alpha, gosHint_DisableMipmap | gosHint_DontShrink );
  1914. }
  1915. }
  1916. //---------------------------------------------------------------------------
  1917. void Mover::release (void) {
  1918. setTeamId(-1, false);
  1919. if (getGroup())
  1920. getGroup()->remove(this);
  1921. setCommanderId(-1);
  1922. MechWarrior::freeWarrior(pilot);
  1923. pilot = NULL;
  1924. }
  1925. //---------------------------------------------------------------------------
  1926. extern GameObjectPtr DebugGameObject[3];
  1927. void Mover::updateDebugWindow (GameDebugWindow* debugWindow) {
  1928. debugPage %= 3;
  1929. debugWindow->clear();
  1930. static char s[128];
  1931. MovePathPtr path = getPilot()->getMovePath();
  1932. long distToObj1 = -1;
  1933. if (DebugGameObject[2])
  1934. distToObj1 = (long)distanceFrom(DebugGameObject[2]->getPosition());
  1935. long sensorRange = 0;
  1936. if (sensorSystem)
  1937. sensorRange = (long)sensorSystem->getEffectiveRange();
  1938. long contStat = contactInfo->getContactStatus(0, true);
  1939. static char* contactStr[NUM_CONTACT_STATUSES] = {
  1940. " ",
  1941. "SENSOR_1",
  1942. "SENSOR_2",
  1943. "SENSOR_3",
  1944. "SENSOR_4",
  1945. "VISUAL "
  1946. };
  1947. if (debugPage == 0) {
  1948. sprintf(s, "%s (%s %02d)", getName(), getPilot()->getName(), getPilot()->getIndex());
  1949. debugWindow->print(s);
  1950. long gestID = -1;
  1951. if (getObjectClass() == BATTLEMECH)
  1952. gestID = ((Mech3DAppearance*)appearance)->getCurrentGestureId();
  1953. sprintf(s, "cmdr: %d, team: %d, gestID: %d, partID: %d", getCommanderId(), getTeamId(), gestID, getPartId());
  1954. debugWindow->print(s);
  1955. if (path->curStep < path->numStepsWhenNotPaused) {
  1956. long curDir = path->stepList[path->curStep].direction;
  1957. if (curDir > 7)
  1958. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s] JMP", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  1959. else
  1960. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s] MOV", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  1961. }
  1962. else
  1963. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s]", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  1964. debugWindow->print(s);
  1965. #if 0
  1966. if (getObjectClass() == BATTLEMECH) {
  1967. sprintf(s, "JMP: (%.f) cgid=%d, Su=%d, in=%d, Ar=%d",
  1968. getJumpRange(),
  1969. ((Mech3DAppearance*)appearance)->getCurrentGestureId(),
  1970. ((Mech3DAppearance*)appearance)->isJumpSetup() ? 1 : 0,
  1971. ((Mech3DAppearance*)appearance)->isInJump() ? 1 : 0,
  1972. ((Mech3DAppearance*)appearance)->isJumpAirborne() ? 1 : 0);
  1973. debugWindow->print(s);
  1974. }
  1975. #else
  1976. sprintf(s, "range: [%.f/%.f/%.f] %.f",
  1977. getMinFireRange(),
  1978. getOptimalFireRange(),
  1979. getMaxFireRange(),
  1980. getPilot()->getCurrentTarget() ? distanceFrom(getPilot()->getCurrentTarget()->getPosition()) : 0.0);
  1981. debugWindow->print(s);
  1982. #endif
  1983. strcpy(s, "order: ");
  1984. if (getPilot()->getNumTacOrdersQueued()) {
  1985. if (getPilot()->getExecutingTacOrderQueue()) {
  1986. char ts[40];
  1987. sprintf(ts, "[%d%c] ", getPilot()->getNumTacOrdersQueued(), getPilot()->getTacOrderQueueLooping() ? "@" : ".");
  1988. strcat(s, ts);
  1989. }
  1990. else {
  1991. char ts[40];
  1992. sprintf(ts, "[%d] ", getPilot()->getNumTacOrdersQueued());
  1993. strcat(s, ts);
  1994. }
  1995. }
  1996. getPilot()->getCurTacOrder()->debugString(getPilot(), &s[7]);
  1997. debugWindow->print(s);
  1998. if (getPilot()->getBrain())
  1999. {
  2000. ModuleInfo brainInfo;
  2001. getPilot()->getBrain()->getInfo(&brainInfo);
  2002. debugWindow->print(" ");
  2003. sprintf(s, "brain: %s", brainInfo.name);
  2004. debugWindow->print(s);
  2005. //-----------------------------------------------------------------
  2006. // Skip the "data\missions\warriors" text when printing filename...
  2007. sprintf(s, "file: %s", &brainInfo.fileName[23]);
  2008. debugWindow->print(s);
  2009. }
  2010. sprintf(s, "cont:");
  2011. if (sensorSystem) {
  2012. // display up to 4...
  2013. long numConts = 4;
  2014. if (sensorSystem->numContacts < 4)
  2015. numConts = sensorSystem->numContacts;
  2016. for (long i = 0; i < numConts; i++) {
  2017. MoverPtr contact = (MoverPtr)ObjectManager->get(sensorSystem->contacts[i] & 0x7FFF);
  2018. Assert(contact != NULL, sensorSystem->contacts[i] & 0x7FFF, " null contact ");
  2019. char s2[10];
  2020. sprintf(s2, " %d.%d", sensorSystem->contacts[i] & 0x7FFF, contact->getContactStatus(teamId, true));
  2021. strcat(s, s2);
  2022. }
  2023. if (sensorSystem->numContacts > 4)
  2024. strcat(s, " +++");
  2025. }
  2026. debugWindow->print(s);
  2027. if (getPilot()->getCurrentTarget()) {
  2028. char ts[50];
  2029. getPilot()->printWeaponsStatus(ts);
  2030. sprintf(s, "%s", ts);
  2031. }
  2032. else
  2033. sprintf(s, "no current target");
  2034. debugWindow->print(s);
  2035. for (long i = 0; i < 5; i++) {
  2036. sprintf(s, "%s", getPilot()->getDebugString(i));
  2037. debugWindow->print(s);
  2038. }
  2039. }
  2040. else if (debugPage == 1) {
  2041. sprintf(s, "%s (%s %02d)", getName(), getPilot()->getName(), getPilot()->getIndex());
  2042. debugWindow->print(s);
  2043. sprintf(s, "team: %d, handle: %d, partID: %d, rank: %d", getTeamId(), getHandle(), getPartId(), getPilot()->getRank());
  2044. debugWindow->print(s);
  2045. if (path->curStep < path->numStepsWhenNotPaused) {
  2046. long curDir = path->stepList[path->curStep].direction;
  2047. if (curDir > 7)
  2048. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s] JMP", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  2049. else
  2050. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s] MOV", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  2051. }
  2052. else
  2053. sprintf(s, "pos: [%d, %d](%d) snsr: %d[%s]", cellPositionRow, cellPositionCol, distToObj1, sensorRange, contactStr[contStat]);
  2054. debugWindow->print(s);
  2055. sprintf(s, "cur step = [%d, %d], area = %d", 0, 0, GlobalMoveMap[0]->calcArea(cellPositionRow, cellPositionCol));
  2056. debugWindow->print(s);
  2057. MoveOrders* moveOrders = getPilot()->getMoveOrders();
  2058. if (moveOrders->numGlobalSteps > 0) {
  2059. sprintf(s, "LR:");
  2060. long lastIndex = moveOrders->curGlobalStep + 8;
  2061. if (lastIndex > moveOrders->numGlobalSteps)
  2062. lastIndex = moveOrders->numGlobalSteps;
  2063. for (long i = 0; i < lastIndex; i++) {
  2064. char ss[15];
  2065. if (i == moveOrders->curGlobalStep)
  2066. sprintf(ss, " [%d]", moveOrders->globalPath[i].thruArea);
  2067. else
  2068. sprintf(ss, " %d", moveOrders->globalPath[i].thruArea);
  2069. strcat(s, ss);
  2070. }
  2071. if (lastIndex < moveOrders->numGlobalSteps)
  2072. strcat(s, " +");
  2073. debugWindow->print(s);
  2074. }
  2075. static char* moveStateStr[NUM_MOVESTATES] = {
  2076. "SS",
  2077. "FF",
  2078. "RR",
  2079. "*F",
  2080. "*R",
  2081. "*T"
  2082. };
  2083. sprintf(s, "MoveState Cur/Goal = %s[%s]",
  2084. moveStateStr[pilot->getMoveState()],
  2085. moveStateStr[pilot->getMoveStateGoal()]);
  2086. debugWindow->print(s);
  2087. GameObjectPtr target = pilot->getCurrentTarget();
  2088. float torsoRelFacing = -1.0;
  2089. if (objectClass == BATTLEMECH) {
  2090. sprintf(s, "torsoRotation = %.2f", ((BattleMechPtr)this)->torsoRotation);
  2091. debugWindow->print(s);
  2092. if (target)
  2093. torsoRelFacing = relFacingTo(target->getPosition()) + ((BattleMechPtr)this)->torsoRotation;
  2094. else if (pilot->getCurTacOrder()->code == TACTICAL_ORDER_ATTACK_POINT)
  2095. torsoRelFacing = relFacingTo(pilot->getAttackTargetPoint()) + ((BattleMechPtr)this)->torsoRotation;
  2096. else
  2097. torsoRelFacing = ((BattleMechPtr)this)->torsoRotation;
  2098. sprintf(s, "C-Bills = %d", ((BattleMechPtr)this)->cBills);
  2099. debugWindow->print(s);
  2100. }
  2101. else if (objectClass == GROUNDVEHICLE) {
  2102. sprintf(s, "turretRotation = %.2f", ((GroundVehiclePtr)this)->turretRotation);
  2103. debugWindow->print(s);
  2104. if (target)
  2105. torsoRelFacing = relFacingTo(target->getPosition()) + ((GroundVehiclePtr)this)->turretRotation;
  2106. else if (pilot->getCurTacOrder()->code == TACTICAL_ORDER_ATTACK_POINT)
  2107. torsoRelFacing = relFacingTo(pilot->getAttackTargetPoint()) + ((GroundVehiclePtr)this)->turretRotation;
  2108. else
  2109. torsoRelFacing = ((GroundVehiclePtr)this)->turretRotation;
  2110. }
  2111. sprintf(s, "torsoRelFacing = %.2f", torsoRelFacing);
  2112. debugWindow->print(s);
  2113. }
  2114. else if (debugPage == 2) {
  2115. static char* locationStrings [] = {
  2116. "head: ",
  2117. "c torso: ",
  2118. "l torso: ",
  2119. "r torso: ",
  2120. "l arm: ",
  2121. "r arm: ",
  2122. "l leg: ",
  2123. "r leg: ",
  2124. "rc torso: ",
  2125. "rl torso: ",
  2126. "rr torso: ",
  2127. "front: ",
  2128. "left: ",
  2129. "right: ",
  2130. "rear: ",
  2131. "turret: "
  2132. };
  2133. static char* bodyState[] = {
  2134. "normal",
  2135. "DAM",
  2136. "DEST"
  2137. };
  2138. sprintf(s, "%s (%s %02d)", getName(), getPilot()->getName(), getPilot()->getIndex());
  2139. debugWindow->print(s);
  2140. sprintf(s, "team: %d, partID: %d, threat: %d", getTeamId(), getPartId(), getThreatRating());
  2141. debugWindow->print(s);
  2142. debugWindow->print(" ");
  2143. if (getObjectClass() == BATTLEMECH) {
  2144. for (long i = 0; i < numBodyLocations; i++) {
  2145. sprintf(s, "%s IS:%3.1f(%02d) AR:%3.1f(%02d) %s",
  2146. locationStrings[i],
  2147. body[i].curInternalStructure,
  2148. body[i].maxInternalStructure,
  2149. armor[i].curArmor,
  2150. armor[i].maxArmor,
  2151. bodyState[body[i].damageState]);
  2152. debugWindow->print(s);
  2153. }
  2154. for (i = numArmorLocations; i < numArmorLocations; i++) {
  2155. sprintf(s, "%s AR:%02d(%02d) %s",
  2156. locationStrings[i],
  2157. armor[i].curArmor,
  2158. armor[i].maxArmor);
  2159. debugWindow->print(s);
  2160. }
  2161. }
  2162. else if (getObjectClass() == GROUNDVEHICLE) {
  2163. for (long i = 0; i < numBodyLocations; i++) {
  2164. sprintf(s, "%s IS:%3.1f(%02d) AR:%3.1f(%02d) %s",
  2165. locationStrings[i + 11],
  2166. body[i].curInternalStructure,
  2167. body[i].maxInternalStructure,
  2168. armor[i].curArmor,
  2169. armor[i].maxArmor,
  2170. bodyState[body[i].damageState]);
  2171. debugWindow->print(s);
  2172. }
  2173. }
  2174. }
  2175. }
  2176. //---------------------------------------------------------------------------
  2177. void Mover::setPartId (long newPartId) {
  2178. partId = newPartId;
  2179. //MoverRoster[newPartId - MIN_MOVER_PART_ID] = (BaseObjectPtr)this;
  2180. }
  2181. //---------------------------------------------------------------------------
  2182. long Mover::setTeamId (long _teamId, bool setup) {
  2183. if (teamId > -1) {
  2184. //----------------------------------
  2185. // Remove me from my current team...
  2186. prevTeamId = teamId;
  2187. TeamPtr team = Team::teams[teamId];
  2188. if (sensorSystem)
  2189. SensorManager->removeTeamSensor(teamId, sensorSystem);
  2190. //if (ecm != 255)
  2191. // SensorManager->removeEcm(this,);
  2192. if (pilot)
  2193. pilot->setTeam(NULL);
  2194. team->removeFromRoster(this);
  2195. }
  2196. teamId = _teamId;
  2197. Assert(teamId > -2, teamId, " Mover.setTeamId: bad teamId ");
  2198. if (setup) {
  2199. if (teamId > -1) {
  2200. TeamPtr team = Team::teams[teamId];
  2201. Assert(team != NULL, 0, " Mover.setTeamId: NULL team ");
  2202. if (sensorSystem)
  2203. SensorManager->addTeamSensor(teamId, sensorSystem);
  2204. if (pilot)
  2205. pilot->setTeam(team);
  2206. }
  2207. }
  2208. return(NO_ERR);
  2209. }
  2210. //---------------------------------------------------------------------------
  2211. TeamPtr Mover::getTeam (void) {
  2212. if (teamId == -1)
  2213. return(NULL);
  2214. return(Team::teams[teamId]);
  2215. }
  2216. //---------------------------------------------------------------------------
  2217. CommanderPtr Mover::getCommander (void) {
  2218. if (commanderId == -1)
  2219. return(NULL);
  2220. return(Commander::commanders[commanderId]);
  2221. }
  2222. //---------------------------------------------------------------------------
  2223. bool Mover::isFriendly (TeamPtr team) {
  2224. if (teamId > -1 && team)
  2225. return(Team::relations[teamId][team->getId()] == RELATION_FRIENDLY);
  2226. return(false);
  2227. }
  2228. //---------------------------------------------------------------------------
  2229. bool Mover::isEnemy (TeamPtr team) {
  2230. if (teamId > -1 && team)
  2231. return(Team::relations[teamId][team->getId()] == RELATION_ENEMY);
  2232. return(false);
  2233. }
  2234. //---------------------------------------------------------------------------
  2235. bool Mover::isNeutral (TeamPtr team) {
  2236. if (teamId > -1 && team)
  2237. return(Team::relations[teamId][team->getId()] == RELATION_NEUTRAL);
  2238. return(true);
  2239. }
  2240. //---------------------------------------------------------------------------
  2241. long Mover::setGroupId (long _groupId, bool setup) {
  2242. groupId = _groupId;
  2243. if (setup && (groupId > -1) && pilot) {
  2244. pilot->clearCurTacOrder(false);
  2245. pilot->setOrderState(ORDERSTATE_GENERAL);
  2246. }
  2247. return(NO_ERR);
  2248. }
  2249. //---------------------------------------------------------------------------
  2250. MoverGroupPtr Mover::getGroup (void) {
  2251. if (groupId == -1)
  2252. return(NULL);
  2253. return(Commander::commanders[commanderId]->getGroup(groupId));
  2254. }
  2255. //---------------------------------------------------------------------------
  2256. void Mover::setPosition (Stuff::Vector3D& newPosition) {
  2257. //-------------------------------------------
  2258. // Confirm position as on map and move it
  2259. // back on map if necessary
  2260. float maxMap = Terrain::worldUnitsMapSide / 2.0;
  2261. bool offMap = false;
  2262. if (newPosition.x < -maxMap)
  2263. {
  2264. newPosition.x = -maxMap;
  2265. offMap = true;
  2266. }
  2267. if (newPosition.x > maxMap)
  2268. {
  2269. newPosition.x = maxMap;
  2270. offMap = true;
  2271. }
  2272. if (newPosition.y < -maxMap)
  2273. {
  2274. newPosition.y = -maxMap;
  2275. offMap = true;
  2276. }
  2277. if (newPosition.y > maxMap)
  2278. {
  2279. newPosition.y = maxMap;
  2280. offMap = true;
  2281. }
  2282. if (!offMap)
  2283. {
  2284. //--------------------------------------------------------------
  2285. // New map clip code here. Actually easy once thought out!
  2286. float maxVisual = Terrain::worldUnitsPerVertex * Terrain::blocksMapSide * Terrain::verticesBlockSide / 2;
  2287. maxVisual -= 1300.0;
  2288. float clipChk1 = newPosition.y - newPosition.x;
  2289. float clipChk2 = newPosition.y + newPosition.x;
  2290. bool clipA = (clipChk1 > maxVisual);
  2291. bool clipB = (clipChk1 < -maxVisual);
  2292. bool clipC = (clipChk2 > maxVisual);
  2293. bool clipD = (clipChk2 < -maxVisual);
  2294. if (clipA || clipB || clipC || clipD)
  2295. offMap = true;
  2296. }
  2297. //--------------------------------------------
  2298. //-- Code to whack Object if it moves off of map
  2299. //-- I.e. Withdrawing or a camera Drone. hehehe.
  2300. if (offMap && withdrawing)
  2301. {
  2302. ObjectManager->getObjectType(typeHandle)->handleDestruction(this,NULL);
  2303. }
  2304. GameObject::setPosition(newPosition);
  2305. triggerAreaMgr->setHit(this);
  2306. //-------------------------------------------------------------------------------------
  2307. // If we've marked our cell as occupied, and our position has changed, then clear it...
  2308. if (lastMapCell[0] != -1)
  2309. if ((lastMapCell[0] != cellPositionRow) || (lastMapCell[1] != cellPositionCol)) {
  2310. GameMap->setMover(lastMapCell[0], lastMapCell[1], false);
  2311. lastMapCell[0] = -1;
  2312. lastMapCell[1] = -1;
  2313. }
  2314. //--------------------------------------------------
  2315. // Now, mark our current cell if we're NOT moving...
  2316. if (pilot->getMovePath()->numSteps == 0) {
  2317. lastMapCell[0] = cellPositionRow;
  2318. lastMapCell[1] = cellPositionCol;
  2319. GameMap->setMover(cellPositionRow, cellPositionCol, true);
  2320. }
  2321. //if (objPosition)
  2322. // GameObjectMap->updateObject(this);
  2323. }
  2324. //---------------------------------------------------------------------------
  2325. void Mover::rotate (float angle)
  2326. {
  2327. //Old function. If only one is passed in, facing and rotation are equal!
  2328. rotate(angle,angle);
  2329. }
  2330. //---------------------------------------------------------------------------
  2331. void Mover::rotate (float angle, float facingAngle)
  2332. {
  2333. rotation += angle;
  2334. if (rotation < -180.0)
  2335. rotation += 360.0;
  2336. else if (rotation > 180.0)
  2337. rotation -= 360.0;
  2338. }
  2339. //---------------------------------------------------------------------------
  2340. void Mover::setAwake (bool set) {
  2341. GameObject::setAwake(set);
  2342. if (set && pilot && (getStatus() == OBJECT_STATUS_SHUTDOWN))
  2343. pilot->orderPowerUp(false, ORDER_ORIGIN_SELF);
  2344. }
  2345. //---------------------------------------------------------------------------
  2346. float Mover::relFacingDelta (Stuff::Vector3D goalPos, Stuff::Vector3D targetPos) {
  2347. float relFacingToGoal = relFacingTo(goalPos);
  2348. float relFacingToTarget = relFacingTo(targetPos);
  2349. float facingDelta = 0.0;
  2350. if (relFacingToGoal >= 0) {
  2351. if (relFacingToTarget >= 0)
  2352. if (relFacingToGoal > relFacingToTarget)
  2353. facingDelta = relFacingToGoal - relFacingToTarget;
  2354. else
  2355. facingDelta = relFacingToTarget - relFacingToGoal;
  2356. else {
  2357. facingDelta = relFacingToGoal - relFacingToTarget;
  2358. if (facingDelta > 180)
  2359. facingDelta = 360.0 - facingDelta;
  2360. }
  2361. }
  2362. else {
  2363. if (relFacingToTarget >= 0) {
  2364. facingDelta = -relFacingToGoal + relFacingToTarget;
  2365. if (facingDelta > 180)
  2366. facingDelta = 360.0 - facingDelta;
  2367. }
  2368. else
  2369. if (relFacingToGoal > relFacingToTarget)
  2370. facingDelta = relFacingToGoal - relFacingToTarget;
  2371. else
  2372. facingDelta = relFacingToTarget - relFacingToGoal;
  2373. }
  2374. return(facingDelta);
  2375. }
  2376. //---------------------------------------------------------------------------
  2377. float Mover::relFacingTo (Stuff::Vector3D goal, long bodyLocation) {
  2378. #if 1
  2379. Stuff::Vector3D facingVec = getRotationVector();
  2380. Stuff::Vector3D goalVec;
  2381. goalVec.Subtract(goal, position);
  2382. goalVec.z = 0.0;
  2383. float angle = angle_from(facingVec, goalVec);
  2384. Stuff::Vector3D angleSign;
  2385. angleSign.Cross(facingVec, goalVec);
  2386. if (angleSign.z >= 0.0)
  2387. {
  2388. angle = -angle;
  2389. }
  2390. return(angle);
  2391. #else
  2392. Stuff::Vector3D curPos = position;
  2393. curPos.z = 0;
  2394. goal.z = 0;
  2395. //-------------------------
  2396. // Create vector for facing
  2397. frame_of_ref workFrame = frame;
  2398. //float angle45 = 45.0;
  2399. //workFrame.rotate_about_k(angle45);
  2400. Stuff::Vector3D velVect = -(workFrame.j);
  2401. Stuff::Vector3D goalVec;
  2402. goalVec = goal - curPos;
  2403. goalVec.normalize();
  2404. float resultAngle = velVect.angle_from(goalVec);
  2405. Stuff::Vector3D angleSign = velVect CROSS goalVec;
  2406. if (angleSign.z >= 0.0)
  2407. {
  2408. resultAngle = -resultAngle;
  2409. }
  2410. /*
  2411. if (angle < 180.0) {
  2412. //---------------------------------------------------
  2413. // Now, let's determine if it's to the right or left.
  2414. // If to the right, return a positive angle.
  2415. // If to the left, return a negative angle.
  2416. // There has to be a cleaner way to get this, right?!
  2417. float one = 1.0;
  2418. workFrame.rotate_about_k(one);
  2419. velVect = -(workFrame.j);
  2420. float angle2 = velVect.angle_from(goal - curPos);
  2421. if (angle2 < angle)
  2422. return(-angle);
  2423. }
  2424. */
  2425. return(resultAngle);
  2426. #endif
  2427. }
  2428. //------------------------------------------------------------------------------------------
  2429. float Mover::getTerrainAngle (void) {
  2430. Stuff::Vector3D worldK(0.0, 0.0, 1.0);
  2431. float result = positionNormal * worldK;
  2432. result = my_acos(result) * RADS_TO_DEGREES;
  2433. return(result);
  2434. }
  2435. //------------------------------------------------------------------------------------------
  2436. float Mover::getVelocityTilt (void) {
  2437. //---------------------------
  2438. // Create vector for velocity
  2439. Stuff::Vector3D facingVec = getRotationVector();
  2440. float result = positionNormal * facingVec;
  2441. result = my_acos(result) * RADS_TO_DEGREES;
  2442. return(result);
  2443. }
  2444. //------------------------------------------------------------------------------------------
  2445. float Mover::getFireArc (void) {
  2446. switch (getObjectClass()) {
  2447. case BATTLEMECH:
  2448. return(FireArc[0]);
  2449. break;
  2450. case GROUNDVEHICLE:
  2451. return(FireArc[1]);
  2452. break;
  2453. case ELEMENTAL:
  2454. return(FireArc[2]);
  2455. break;
  2456. }
  2457. return(45.0);
  2458. }
  2459. //------------------------------------------------------------------------------------------
  2460. void Mover::destroy (void)
  2461. {
  2462. //We only destroy on game end. Most of this is caught in the heap frees!!
  2463. //----------------------------------------------
  2464. // This will free any memory the mover is using.
  2465. if (appearance)
  2466. {
  2467. delete appearance;
  2468. appearance = NULL;
  2469. }
  2470. if (contactInfo)
  2471. {
  2472. delete contactInfo;
  2473. contactInfo = NULL;
  2474. }
  2475. }
  2476. //----------------------------------------------------------------------------------
  2477. Stuff::Vector3D Mover::relativePosition (float angle, float distance, unsigned long flags) {
  2478. //--------------------------------------------------------
  2479. // Note that the angle should be -180 <= angle <= 180, and
  2480. // the distance is in meters...
  2481. distance *= -worldUnitsPerMeter;
  2482. //--------------------------------------------
  2483. // Absolute facing, based upon north facing...
  2484. Stuff::Vector3D shiftVect(0.0, 1.0, 0.0);
  2485. if ((flags & RELPOS_FLAG_ABS) == 0) {
  2486. //----------------------------------------------------
  2487. // Create vector for facing, rotated to adjust for the
  2488. // relative angle...
  2489. shiftVect = getRotationVector();
  2490. }
  2491. Rotate(shiftVect, angle);
  2492. shiftVect *= distance;
  2493. Stuff::Vector3D curPos(position.x, position.y, 0.0);
  2494. Stuff::Vector3D relPos(curPos.x + shiftVect.x, curPos.y + shiftVect.y, 0.0);
  2495. Stuff::Vector3D start;
  2496. Stuff::Vector3D goal;
  2497. Stuff::Vector3D deltaVector;
  2498. if (flags & RELPOS_FLAG_PASSABLE_START) {
  2499. start = curPos;
  2500. goal = relPos;
  2501. }
  2502. else {
  2503. start = relPos;
  2504. goal = curPos;
  2505. }
  2506. deltaVector.Subtract(goal, start);
  2507. //-------------------------------------------------------------
  2508. // First, we need to calc the delta vector--how much we extend
  2509. // the ray everytime we check the map cell for clear placement.
  2510. deltaVector.Normalize(deltaVector);
  2511. float cellLength = Terrain::worldUnitsPerCell;
  2512. cellLength *= 0.5;
  2513. deltaVector *= cellLength;
  2514. if (deltaVector.GetLength() == 0.0)
  2515. return(curPos);
  2516. //-------------------------------------------------
  2517. // Determine the max length the ray must be cast...
  2518. float maxLength = distance_from(goal, start);
  2519. //------------------------------------------------------------
  2520. // We'll start at the target, and if it's blocked, we'll move
  2521. // toward our start location, looking for the first valid/open
  2522. // cell...
  2523. Stuff::Vector3D curPoint = start;
  2524. Stuff::Vector3D curRay;
  2525. curRay.Zero();
  2526. float rayLength = 0.0;
  2527. long cellR, cellC;
  2528. land->worldToCell(curPoint, cellR, cellC);
  2529. bool cellClear = GameMap->getPassable(cellR, cellC);
  2530. Stuff::Vector3D lastGoodPoint = curPoint;
  2531. if (flags & RELPOS_FLAG_PASSABLE_START)
  2532. while (cellClear && (rayLength < maxLength)) {
  2533. lastGoodPoint = curPoint;
  2534. curPoint += deltaVector;
  2535. curRay.Subtract(curPoint, start);
  2536. rayLength = curRay.GetLength();
  2537. land->worldToCell(curPoint, cellR, cellC);
  2538. cellClear = GameMap->getPassable(cellR, cellC);
  2539. }
  2540. else
  2541. while (!cellClear && (rayLength < maxLength)) {
  2542. lastGoodPoint = curPoint;
  2543. curPoint += deltaVector;
  2544. curRay.Subtract(curPoint, start);
  2545. rayLength = curRay.GetLength();
  2546. land->worldToCell(curPoint, cellR, cellC);
  2547. cellClear = GameMap->getPassable(cellR, cellC);
  2548. }
  2549. curPoint.x = lastGoodPoint.x;
  2550. curPoint.y = lastGoodPoint.y;
  2551. curPoint.z = land->getTerrainElevation(curPoint);
  2552. return(curPoint);
  2553. }
  2554. //---------------------------------------------------------------------------
  2555. void Mover::setSensorRange (float range) {
  2556. if (sensorSystem)
  2557. sensorSystem->setRange(range);
  2558. }
  2559. //---------------------------------------------------------------------------
  2560. bool Mover::hasActiveProbe (void) {
  2561. return(probe != 255);
  2562. }
  2563. //---------------------------------------------------------------------------
  2564. float Mover::getEcmRange (void) {
  2565. if ((ecm != 255))
  2566. return 10.0f; //ANY ECM means I am invisible!!
  2567. return(0.0);
  2568. }
  2569. //---------------------------------------------------------------------------
  2570. bool Mover::hasNullSignature (void) {
  2571. return(nullSignature != 255);
  2572. }
  2573. //---------------------------------------------------------------------------
  2574. extern bool InitWayPath;
  2575. long Mover::handleTacticalOrder (TacticalOrder tacOrder, long priority, bool queuePlayerOrder) {
  2576. //queuePlayerOrder = true;
  2577. if (queuePlayerOrder)
  2578. tacOrder.pack(NULL, NULL);
  2579. #if 1
  2580. if (MPlayer && !MPlayer->isServer()) {
  2581. //----------------------------
  2582. // Simply for test purposes...
  2583. tacOrder.pack(NULL, NULL);
  2584. TacticalOrder tacOrder2;
  2585. tacOrder2.data[0] = tacOrder.data[0];
  2586. tacOrder2.data[1] = tacOrder.data[1];
  2587. tacOrder2.unpack();
  2588. }
  2589. #endif
  2590. //----------------------------------------------------
  2591. // Any tacorder that gets here IS NOT a lance order...
  2592. bool processOrder = true;
  2593. long message = -1;
  2594. switch (tacOrder.code) {
  2595. case TACTICAL_ORDER_WAIT:
  2596. suppressionFire = false;
  2597. break;
  2598. case TACTICAL_ORDER_MOVETO_POINT:
  2599. //---------------------------------------------------
  2600. // If we're part of a selected group, offset our goal
  2601. // slightly...
  2602. if (selectionIndex != -1)
  2603. {
  2604. tacOrder.delayedTime = scenarioTime + (selectionIndex * DelayedOrderTime);
  2605. }
  2606. if (!canMove())
  2607. {
  2608. //DO NOT send readio message. Players will know artillery cannot move.
  2609. // BUT do not allow order through. Will cancel out a forceFire command!
  2610. processOrder = false;
  2611. }
  2612. break;
  2613. case TACTICAL_ORDER_JUMPTO_POINT:
  2614. case TACTICAL_ORDER_JUMPTO_OBJECT: {
  2615. bool canJump = (getObjectClass() == BATTLEMECH);
  2616. GameObjectPtr target = tacOrder.getTarget();
  2617. if (target && target->isMover() && (target->getTeam() == getTeam()))
  2618. canJump = false;
  2619. if (getJumpRange() < distanceFrom(tacOrder.getWayPoint(0)))
  2620. canJump = false;
  2621. if (getObjectClass() == BATTLEMECH) {
  2622. long cellR, cellC;
  2623. land->worldToCell(tacOrder.getWayPoint(0), cellR, cellC);
  2624. if (!GameMap->getPassable(cellR, cellC))
  2625. canJump = false;
  2626. }
  2627. if (!canJump) {
  2628. message = RADIO_ILLEGAL_ORDER;
  2629. processOrder = false;
  2630. }
  2631. //else
  2632. // message = RADIO_JUMPTO;
  2633. }
  2634. //-----------------------------------------
  2635. // No break here--should fall down below...
  2636. case TACTICAL_ORDER_MOVETO_OBJECT:
  2637. case TACTICAL_ORDER_TRAVERSE_PATH:
  2638. case TACTICAL_ORDER_PATROL_PATH:
  2639. //---------------------------------------------------
  2640. // Tell everyone we have received the order.
  2641. if (isDisabled() || !canMove()) {
  2642. processOrder = false;
  2643. }
  2644. break;
  2645. case TACTICAL_ORDER_ATTACK_OBJECT:
  2646. //tacOrder.code = TACTICAL_ORDER_ATTACK_POINT;
  2647. //tacOrder.attackParams.targetPoint = (GameObjectPtr(BaseObjectPtr(tacOrder.target)))->getPosition();
  2648. //tacOrder.target = NULL;
  2649. if (tacOrder.attackParams.method == ATTACKMETHOD_DFA) {
  2650. //-------------------------------------------------
  2651. // Let's just make it a move/jump order, for now...
  2652. tacOrder.code = TACTICAL_ORDER_JUMPTO_OBJECT;
  2653. //tacOrder.target = mouseObject;
  2654. tacOrder.moveParams.wait = false;
  2655. tacOrder.moveParams.wayPath.mode[0] = TRAVEL_MODE_SLOW;
  2656. GameObjectPtr target = tacOrder.getTarget();
  2657. if (target)
  2658. tacOrder.setWayPoint(0, target->getPosition());
  2659. }
  2660. if (tacOrder.attackParams.aimLocation != -1)
  2661. {
  2662. message = RADIO_CALLED_SHOT;
  2663. }
  2664. break;
  2665. case TACTICAL_ORDER_POWERUP:
  2666. case TACTICAL_ORDER_POWERDOWN:
  2667. //tacOrder.delayedTime = scenarioTime + 5.0;
  2668. //break;
  2669. case TACTICAL_ORDER_ESCORT:
  2670. case TACTICAL_ORDER_FOLLOW:
  2671. case TACTICAL_ORDER_GUARD:
  2672. case TACTICAL_ORDER_STOP:
  2673. case TACTICAL_ORDER_WAYPOINTS_DONE:
  2674. case TACTICAL_ORDER_EJECT:
  2675. case TACTICAL_ORDER_HOLD_FIRE:
  2676. case TACTICAL_ORDER_WITHDRAW:
  2677. case TACTICAL_ORDER_ATTACK_POINT:
  2678. case TACTICAL_ORDER_REFIT:
  2679. case TACTICAL_ORDER_GETFIXED:
  2680. case TACTICAL_ORDER_CAPTURE:
  2681. case TACTICAL_ORDER_RECOVER:
  2682. case TACTICAL_ORDER_LOAD_INTO_CARRIER:
  2683. case TACTICAL_ORDER_DEPLOY_ELEMENTALS:
  2684. break;
  2685. default: {
  2686. char s[256];
  2687. sprintf(s, "Mover::handleTacticalOrder->Bad TacOrder Code (%d)", tacOrder.code);
  2688. Assert(false, tacOrder.code, s);
  2689. return(1);
  2690. }
  2691. }
  2692. if (pilot)
  2693. pilot->radioMessage(message, true);
  2694. //-------------------------------------------------------
  2695. // Acknowledgement of a good order is done when the order
  2696. // is executed...
  2697. if (processOrder)
  2698. switch (tacOrder.origin) {
  2699. case ORDER_ORIGIN_PLAYER:
  2700. if (queuePlayerOrder)
  2701. pilot->addQueuedTacOrder(tacOrder);
  2702. else {
  2703. // if (pilot->getNumTacOrdersQueued())
  2704. //This is a hack to simply trigger the execution of
  2705. //the queued orders. The current order is ignored (and
  2706. //is simply used for this trigger)...
  2707. // pilot->executeTacOrderQueue();
  2708. // else
  2709. pilot->setPlayerTacOrder(tacOrder);
  2710. }
  2711. break;
  2712. case ORDER_ORIGIN_COMMANDER:
  2713. pilot->setGeneralTacOrder(tacOrder);
  2714. break;
  2715. case ORDER_ORIGIN_SELF:
  2716. pilot->setAlarmTacOrder(tacOrder, priority);
  2717. break;
  2718. }
  2719. return(NO_ERR);
  2720. }
  2721. //----------------------------------------------------------------------------------
  2722. void Mover::reduceAntiMissileAmmo (long numAntiMissiles) {
  2723. if (numAntiMissiles > 0)
  2724. reduceAmmo(MasterComponent::masterList[inventory[antiMissileSystem[0]].masterID].getWeaponAmmoMasterId(), numAntiMissiles);
  2725. }
  2726. //----------------------------------------------------------------------------------
  2727. void Mover::pilotingCheck (unsigned long situation, float modifier) {
  2728. failedPilotingCheck = false;
  2729. }
  2730. //-------------------------------------------------------------------------------------------
  2731. void Mover::updateDamageTakenRate (void) {
  2732. if (damageRateCheckTime < scenarioTime) {
  2733. long damageRate = (damageRateTally / DamageRateFrequency);
  2734. if (damageRate > 10 /*AttitudeEffect[pilot->getAttitude(ORDER_CURRENT)][5]*/)
  2735. pilot->triggerAlarm(PILOT_ALARM_DAMAGE_TAKEN_RATE, damageRate);
  2736. damageRateTally = 0;
  2737. damageRateCheckTime += DamageRateFrequency;
  2738. }
  2739. }
  2740. //-------------------------------------------------------------------------------------------
  2741. void Mover::setPilotHandle (long _pilotHandle) {
  2742. if (pilot) {
  2743. pilot->setVehicle(NULL);
  2744. pilot = NULL;
  2745. }
  2746. pilotHandle = _pilotHandle;
  2747. if (pilotHandle > 0) {
  2748. pilot = MechWarrior::warriorList[pilotHandle];
  2749. pilot->setVehicle((GameObjectPtr)this);
  2750. }
  2751. }
  2752. //---------------------------------------------------------------------------
  2753. void Mover::loadPilot (char* pilotFileName, char* brainFileName, LogisticsPilot *lPilot) {
  2754. if (pilot) {
  2755. MechWarrior::freeWarrior(pilot);
  2756. pilot = NULL;
  2757. }
  2758. //---------------------------------------------------------------
  2759. // Loads a new pilot into the pilot manager and then puts it into
  2760. // this mover. If a pilot already exists in this mover, it is
  2761. // replaced...
  2762. MechWarriorPtr pilot = MechWarrior::newWarrior();
  2763. if (!pilot)
  2764. STOP(("Too many pilots in this mission!"));
  2765. FullPathFileName pilotFullFileName;
  2766. pilotFullFileName.init(warriorPath, pilotFileName, ".fit");
  2767. FitIniFile* pilotFile = new FitIniFile;
  2768. gosASSERT(pilotFile != NULL);
  2769. long result = pilotFile->open(pilotFullFileName);
  2770. gosASSERT(result == NO_ERR);
  2771. result = pilot->init(pilotFile);
  2772. gosASSERT(result == NO_ERR);
  2773. pilotFile->close();
  2774. delete pilotFile;
  2775. pilotFile = NULL;
  2776. //Copy logistics data to pilot AFTER loading old data.
  2777. // ONLY if we overrode the data in logistics!!
  2778. if (lPilot)
  2779. {
  2780. pilot->skills[MWS_GUNNERY] = pilot->skillRank[MWS_GUNNERY] = lPilot->getGunnery();
  2781. pilot->skills[MWS_PILOTING] = pilot->skillRank[MWS_PILOTING] = lPilot->getPiloting();
  2782. memcpy(pilot->specialtySkills,lPilot->getSpecialtySkills(),sizeof(bool) * NUM_SPECIALTY_SKILLS);
  2783. pilot->calcRank();
  2784. }
  2785. //*********************
  2786. // NOTE: Need to send packet to other players in MP with new pilot and
  2787. // mover data!
  2788. //*********************
  2789. long numErrors, numLinesProcessed;
  2790. FullPathFileName brainFullFileName;
  2791. if (MPlayer) {
  2792. pilot->setBrainName("pbrain");
  2793. brainFullFileName.init(warriorPath, "pbrain", ".abl");
  2794. }
  2795. else {
  2796. pilot->setBrainName(brainFileName);
  2797. brainFullFileName.init(warriorPath, brainFileName, ".abl");
  2798. }
  2799. long moduleHandle = ABLi_preProcess(brainFullFileName, &numErrors, &numLinesProcessed);
  2800. gosASSERT(moduleHandle >= 0);
  2801. pilot->setBrain(moduleHandle);
  2802. setPilotHandle(pilot->getIndex());
  2803. }
  2804. //---------------------------------------------------------------------------
  2805. void Mover::setCommanderId (long _commanderId) {
  2806. if (commanderId > -1)
  2807. prevCommanderId = commanderId;
  2808. commanderId = _commanderId;
  2809. }
  2810. //---------------------------------------------------------------------------
  2811. MoverPtr Mover::getPoint (void) {
  2812. #ifdef USE_GROUPS
  2813. if (group)
  2814. return(group->getPoint());
  2815. #endif
  2816. return(NULL);
  2817. }
  2818. //---------------------------------------------------------------------------
  2819. bool Mover::hasWeaponNode (void)
  2820. {
  2821. if ((lowestWeaponNodeID == -1) || (lowestWeaponNodeID == -2) || (turn < 6))
  2822. {
  2823. lowestWeaponNodeID = appearance->getLowestWeaponNode();
  2824. Stuff::Vector3D nodePos = appearance->getWeaponNodePosition(lowestWeaponNodeID);
  2825. lowestWeaponNodeZ = nodePos.z - land->getTerrainElevation(nodePos);
  2826. }
  2827. return (lowestWeaponNodeID != -1);
  2828. }
  2829. //---------------------------------------------------------------------------
  2830. Stuff::Vector3D Mover::getLOSPosition (void)
  2831. {
  2832. float jumpLowestNode = 0.0f;
  2833. if (appearance)
  2834. {
  2835. bool oldInView = appearance->canBeSeen();
  2836. appearance->setInView(true);
  2837. if ((getStatus() == OBJECT_STATUS_NORMAL) &&
  2838. ((appearance->getCurrentGestureId() == 2) ||
  2839. (appearance->getCurrentGestureId() == 4) ||
  2840. (appearance->getCurrentGestureId() == 7) || !isMech()) &&
  2841. ((lowestWeaponNodeID == -1) || (lowestWeaponNodeID == -2) || (turn < 6)) )
  2842. {
  2843. lowestWeaponNodeID = appearance->getLowestWeaponNode();
  2844. if (lowestWeaponNodeID == -1)
  2845. {
  2846. //We must not have ever rendered this guy. Probably multiplayer.
  2847. // Update his Geometry.
  2848. appearance->update();
  2849. lowestWeaponNodeID = appearance->getLowestWeaponNode();
  2850. }
  2851. Stuff::Vector3D nodePos = appearance->getWeaponNodePosition(lowestWeaponNodeID);
  2852. lowestWeaponNodeZ = nodePos.z - land->getTerrainElevation(nodePos);
  2853. }
  2854. if (appearance->getCurrentGestureId() == 20)
  2855. {
  2856. Stuff::Vector3D nodePos = appearance->getWeaponNodePosition(lowestWeaponNodeID);
  2857. jumpLowestNode = nodePos.z - land->getTerrainElevation(nodePos);
  2858. }
  2859. if (lowestWeaponNodeZ <= 0.0f)
  2860. {
  2861. Stuff::Vector3D nodePos = appearance->getWeaponNodePosition(lowestWeaponNodeID);
  2862. lowestWeaponNodeZ = nodePos.z - land->getTerrainElevation(nodePos);
  2863. }
  2864. appearance->setInView(oldInView);
  2865. }
  2866. Stuff::Vector3D losPos = position;
  2867. if (lowestWeaponNodeZ != -9999.9f)
  2868. losPos.z += lowestWeaponNodeZ;
  2869. else
  2870. losPos.z += 15.0f; //Fudge in some higher value so shutdown mechs don't just disappear!!
  2871. if (jumpLowestNode > 0.0f)
  2872. {
  2873. losPos = position;
  2874. losPos.z += jumpLowestNode;
  2875. }
  2876. return (losPos);
  2877. }
  2878. //---------------------------------------------------------------------------
  2879. void Mover::printFireWeaponDebugInfo (GameObjectPtr target, Stuff::Vector3D* targetPoint, long chance, long aimLocation, long roll, WeaponShotInfo* shotInfo) {
  2880. if (!CombatLog)
  2881. return;
  2882. static char* locationStrings [] = {
  2883. "head",
  2884. "center torso",
  2885. "left torso",
  2886. "right torso",
  2887. "left arm",
  2888. "right arm",
  2889. "left leg",
  2890. "right leg",
  2891. "rear center torso",
  2892. "rear left torso",
  2893. "rear right torso"
  2894. };
  2895. if (roll < chance) {
  2896. if (target) {
  2897. char* targetName = target->getName();
  2898. char s[1024];
  2899. if (getObjectClass() == BATTLEMECH)
  2900. sprintf(s, "[%.2f] mech.fireWeapon HIT: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), name, target->getPartId(), targetName ? targetName : "unknown");
  2901. else
  2902. sprintf(s, "[%.2f] vehicle.fireWeapon HIT: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), name, target->getPartId(), targetName ? targetName : "unknown");
  2903. CombatLog->write(s);
  2904. sprintf(s, " chance = %03d, roll = %03d", chance, roll);
  2905. CombatLog->write(s);
  2906. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  2907. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  2908. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  2909. shotInfo->damage,
  2910. shotInfo->entryAngle);
  2911. CombatLog->write(s);
  2912. if (aimLocation != -1) {
  2913. sprintf(s, " aimed shot = (%d)%s", aimLocation, locationStrings[aimLocation]);
  2914. CombatLog->write(s);
  2915. }
  2916. sprintf(s, " attacker pilot = %s (team id = %d)", getPilot()->getName(), getTeamId());
  2917. CombatLog->write(s);
  2918. CombatLog->write(" ");
  2919. }
  2920. else if (targetPoint) {
  2921. }
  2922. }
  2923. else {
  2924. if (target) {
  2925. char* targetName = target->getName();
  2926. char s[1024];
  2927. if (getObjectClass() == BATTLEMECH)
  2928. sprintf(s, "[%.2f] mech.fireWeapon MISS: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), name, target->getPartId(), targetName ? targetName : "unknown");
  2929. else
  2930. sprintf(s, "[%.2f] vehicle.fireWeapon HIT: (%05d)%s @ (%05d)%s", scenarioTime, getPartId(), name, target->getPartId(), targetName ? targetName : "unknown");
  2931. CombatLog->write(s);
  2932. sprintf(s, " chance = %03d, roll = %03d", chance, roll);
  2933. CombatLog->write(s);
  2934. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  2935. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  2936. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  2937. shotInfo->damage,
  2938. shotInfo->entryAngle);
  2939. CombatLog->write(s);
  2940. if (aimLocation != -1) {
  2941. sprintf(s, " aimed shot = (%d)%s", aimLocation, locationStrings[aimLocation]);
  2942. CombatLog->write(s);
  2943. }
  2944. sprintf(s, " attacker pilot = %s (team id = %d)", getPilot()->getName(), getTeamId());
  2945. CombatLog->write(s);
  2946. CombatLog->write(" ");
  2947. }
  2948. else if (targetPoint) {
  2949. }
  2950. }
  2951. }
  2952. //----------------------------------------------------------------------------
  2953. bool FromMP = false;
  2954. void Mover::printHandleWeaponHitDebugInfo (WeaponShotInfo* shotInfo) {
  2955. if (!CombatLog)
  2956. return;
  2957. static char* locationStrings [] = {
  2958. "head",
  2959. "center torso",
  2960. "left torso",
  2961. "right torso",
  2962. "left arm",
  2963. "right arm",
  2964. "left leg",
  2965. "right leg",
  2966. "rear center torso",
  2967. "rear left torso",
  2968. "rear right torso"
  2969. };
  2970. char s[1024];
  2971. char statusStr[15];
  2972. if (FromMP) {
  2973. if (isDestroyed())
  2974. sprintf(statusStr, " DESTROYED:");
  2975. else if (getTeam() && Team::noPain[getTeamId()])
  2976. sprintf(statusStr, " NO PAIN:");
  2977. else
  2978. sprintf(statusStr, ":");
  2979. }
  2980. else {
  2981. if (isDestroyed())
  2982. sprintf(statusStr, " *** DESTROYED:");
  2983. else if (getTeam() && Team::noPain[getTeamId()])
  2984. sprintf(statusStr, " *** NO PAIN:");
  2985. else
  2986. sprintf(statusStr, " ***:");
  2987. }
  2988. if (getObjectClass() == BATTLEMECH)
  2989. sprintf(s, "[%.2f] mech.handleWeaponHit%s (%05d)%s ",
  2990. scenarioTime,
  2991. statusStr,
  2992. getPartId(),
  2993. name);
  2994. else
  2995. sprintf(s, "[%.2f] vehicle.handleWeaponHit%s (%05d)%s ",
  2996. scenarioTime,
  2997. statusStr,
  2998. getPartId(),
  2999. name);
  3000. CombatLog->write(s);
  3001. GameObjectPtr attacker = ObjectManager->getByWatchID(shotInfo->attackerWID);
  3002. if (attacker)
  3003. sprintf(s, " attacker = (%05d)%s", attacker->getPartId(), attacker->getName());
  3004. else
  3005. sprintf(s, " attacker = (%05)<unknown WID>", shotInfo->attackerWID);
  3006. CombatLog->write(s);
  3007. sprintf(s, " weapon = (%03d)%s, hitLocation = (%d)%s, damage = %.2f, angle = %.2f",
  3008. shotInfo->masterId, MasterComponent::masterList[shotInfo->masterId].getName(),
  3009. shotInfo->hitLocation, (shotInfo->hitLocation > -1) ? locationStrings[shotInfo->hitLocation] : "none",
  3010. shotInfo->damage,
  3011. shotInfo->entryAngle);
  3012. CombatLog->write(s);
  3013. sprintf(s, " num weaponhits = %d", numWeaponHitsHandled);
  3014. CombatLog->write(s);
  3015. CombatLog->write(" ");
  3016. }
  3017. //----------------------------------------------------------------------------
  3018. long Mover::clearWeaponFireChunks (long which) {
  3019. long numChunks = numWeaponFireChunks[which];
  3020. numWeaponFireChunks[which] = 0;
  3021. return(numChunks);
  3022. }
  3023. //---------------------------------------------------------------------------
  3024. long Mover::addWeaponFireChunk (long which, WeaponFireChunkPtr chunk) {
  3025. if (numWeaponFireChunks[which] == MAX_WEAPONFIRE_CHUNKS)
  3026. Fatal(0, " Mover::addWeaponFireChunk--Too many weaponfire chunks ");
  3027. chunk->pack(this);
  3028. WeaponFireChunk testChunk;
  3029. testChunk.init();
  3030. testChunk.data = chunk->data;
  3031. testChunk.unpack(this);
  3032. if (!chunk->equalTo(&testChunk)) {
  3033. DebugWeaponFireChunk(chunk, &testChunk, this);
  3034. Assert(false, 0, " Mover.addWeaponFireChunk: bad weaponfire chunk (save wfchunk.dbg file) ");
  3035. }
  3036. weaponFireChunks[which][numWeaponFireChunks[which]++] = chunk->data;
  3037. return(numWeaponFireChunks[which]);
  3038. }
  3039. //---------------------------------------------------------------------------
  3040. long Mover::addWeaponFireChunks (long which, unsigned long* packedChunkBuffer, long numChunks) {
  3041. if ((numWeaponFireChunks[which] + numChunks) >= MAX_WEAPONFIRE_CHUNKS)
  3042. Fatal(0, " Mover::addWeaponFireChunks--Too many weaponfire chunks ");
  3043. #if 0
  3044. memcpy(&weaponFireChunks[which][numWeaponFireChunks[which]], packedChunkBuffer, 4 * numChunks);
  3045. numWeaponFireChunks[which] += numChunks;
  3046. #else
  3047. for (long i = 0; i < numChunks; i++) {
  3048. weaponFireChunks[which][numWeaponFireChunks[which]++] = packedChunkBuffer[i];
  3049. //---------------
  3050. // FOR TESTING...
  3051. WeaponFireChunk chunk;
  3052. chunk.init();
  3053. chunk.data = packedChunkBuffer[i];
  3054. chunk.unpack(this);
  3055. }
  3056. #endif
  3057. return(numWeaponFireChunks[which]);
  3058. }
  3059. //---------------------------------------------------------------------------
  3060. long Mover::grabWeaponFireChunks (long which, unsigned long* packedChunkBuffer, long maxChunks) {
  3061. long numChunks = numWeaponFireChunks[which];
  3062. if (numChunks > maxChunks)
  3063. numChunks = maxChunks;
  3064. if (numChunks > 0) {
  3065. for (long i = 0; i < numChunks; i++)
  3066. packedChunkBuffer[i] = weaponFireChunks[which][i];
  3067. //memcpy(packedChunkBuffer, weaponFireChunks[which], 4 * numWeaponFireChunks[which]);
  3068. }
  3069. numWeaponFireChunks[which] -= numChunks;
  3070. return(numChunks);
  3071. }
  3072. //---------------------------------------------------------------------------
  3073. long Mover::updateWeaponFireChunks (long which) {
  3074. for (long i = 0; i < numWeaponFireChunks[which]; i++) {
  3075. WeaponFireChunk chunk;
  3076. chunk.init();
  3077. chunk.data = weaponFireChunks[which][i];
  3078. chunk.unpack(this);
  3079. //-----------------
  3080. // For debugging...
  3081. CurMoverWeaponFireChunk = chunk;
  3082. static float entryQuadTable[4] = {0.0, 180.0, -90.0, 90.0};
  3083. long weaponIndex = chunk.weaponIndex + numOther;
  3084. if (!isWeaponIndex(weaponIndex))
  3085. continue;
  3086. TargetRolo = chunk.targetType;
  3087. if (chunk.targetType == 0 /*WEAPONFIRECHUNK_TARGET_MOVER*/) {
  3088. GameObjectPtr target = (GameObjectPtr)MPlayer->moverRoster[chunk.targetId];
  3089. //----------------------------------------------------------------------------
  3090. // Mover targets could be NULL now, since we free them when they're destroyed.
  3091. if (target)
  3092. handleWeaponFire(weaponIndex,
  3093. target,
  3094. NULL,
  3095. chunk.hit,
  3096. entryQuadTable[chunk.entryAngle],
  3097. chunk.numMissiles,
  3098. chunk.hitLocation);
  3099. }
  3100. else if (chunk.targetType == 1 /*WEAPONFIRECHUNK_TARGET_TERRAIN*/) {
  3101. GameObjectPtr target = ObjectManager->findByPartId(chunk.targetId);
  3102. if (target == NULL) {
  3103. DebugWeaponFireChunk (&chunk, NULL, this);
  3104. Assert(false, 0, " Mover.updateWeaponFireChunks: NULL Terrain Target (save wfchunk.dbg file) ");
  3105. }
  3106. handleWeaponFire(weaponIndex,
  3107. target,
  3108. NULL,
  3109. chunk.hit,
  3110. entryQuadTable[chunk.entryAngle],
  3111. chunk.numMissiles,
  3112. chunk.hitLocation);
  3113. }
  3114. else if (chunk.targetType == 2 /*WEAPONFIRECHUNK_TARGET_TRAIN*/) {
  3115. GameObjectPtr target = ObjectManager->findByPartId(chunk.targetId);
  3116. if (target == NULL) {
  3117. DebugWeaponFireChunk (&chunk, NULL, this);
  3118. Assert(false, 0, " Mover.updateWeaponFireChunks: NULL Special Target (save wfchunk.dbg file) ");
  3119. }
  3120. handleWeaponFire(weaponIndex,
  3121. target,
  3122. NULL,
  3123. chunk.hit,
  3124. entryQuadTable[chunk.entryAngle],
  3125. chunk.numMissiles,
  3126. chunk.hitLocation);
  3127. }
  3128. else if (chunk.targetType == 3 /*WEAPONFIRECHUNK_TARGET_LOCATION*/) {
  3129. Stuff::Vector3D targetPoint;
  3130. targetPoint.x = (float)chunk.targetCell[1] * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2;
  3131. targetPoint.y = (Terrain::worldUnitsMapSide / 2) - ((float)chunk.targetCell[0] * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2;
  3132. targetPoint.z = (float)land->getTerrainElevation(targetPoint);
  3133. handleWeaponFire(weaponIndex, NULL, &targetPoint, chunk.hit, 0.0, chunk.numMissiles, 0);
  3134. }
  3135. else
  3136. Fatal(0, " Mover.updateWeaponFireChunks: bad targetType ");
  3137. }
  3138. numWeaponFireChunks[which] = 0;
  3139. return(NO_ERR);
  3140. }
  3141. //---------------------------------------------------------------------------
  3142. long Mover::clearCriticalHitChunks (long which) {
  3143. long numChunks = numCriticalHitChunks[which];
  3144. numCriticalHitChunks[which] = 0;
  3145. return(numChunks);
  3146. }
  3147. //---------------------------------------------------------------------------
  3148. long Mover::addCriticalHitChunk (long which, long bodyLocation, long criticalSpace) {
  3149. if (numCriticalHitChunks[which] == MAX_CRITICALHIT_CHUNKS)
  3150. Fatal(0, " Mover::addCriticalHitChunk--Too many criticalhit chunks ");
  3151. //-----------------------------------------------------------
  3152. // Since the "chunk" is so simple, we'll just make it here...
  3153. unsigned char chunkData = (unsigned char)((bodyLocation << 4) + criticalSpace);
  3154. criticalHitChunks[which][numCriticalHitChunks[which]++] = chunkData;
  3155. return(numCriticalHitChunks[which]);
  3156. }
  3157. //---------------------------------------------------------------------------
  3158. long Mover::addCriticalHitChunks (long which, unsigned char* packedChunkBuffer, long numChunks) {
  3159. if ((numCriticalHitChunks[which] + numChunks) >= MAX_CRITICALHIT_CHUNKS)
  3160. Fatal(0, " Mover::addCriticalHitChunks--Too many criticalhit chunks ");
  3161. memcpy(&criticalHitChunks[which][numCriticalHitChunks[which]], packedChunkBuffer, numChunks);
  3162. numCriticalHitChunks[which] += numChunks;
  3163. return(numCriticalHitChunks[which]);
  3164. }
  3165. //---------------------------------------------------------------------------
  3166. long Mover::grabCriticalHitChunks (long which, unsigned char* packedChunkBuffer) {
  3167. if (numCriticalHitChunks[which] > 0)
  3168. memcpy(packedChunkBuffer, criticalHitChunks[which], numCriticalHitChunks[which]);
  3169. return(numCriticalHitChunks[which]);
  3170. }
  3171. //---------------------------------------------------------------------------
  3172. long Mover::updateCriticalHitChunks (long which) {
  3173. numCriticalHitChunks[which] = 0;
  3174. return(NO_ERR);
  3175. }
  3176. //---------------------------------------------------------------------------
  3177. long Mover::clearRadioChunks (long which) {
  3178. long numChunks = numRadioChunks[which];
  3179. numRadioChunks[which] = 0;
  3180. return(numChunks);
  3181. }
  3182. //---------------------------------------------------------------------------
  3183. long Mover::addRadioChunk (long which, unsigned char msg) {
  3184. if (numRadioChunks[which] == MAX_RADIO_CHUNKS)
  3185. return(numRadioChunks[which]);
  3186. //Fatal(0, " Mover::addRadioChunk--Too many radio chunks ");
  3187. //-----------------------------------------------------------
  3188. // Since the "chunk" is so simple, we'll just make it here...
  3189. radioChunks[which][numRadioChunks[which]++] = msg;
  3190. return(numRadioChunks[which]);
  3191. }
  3192. //---------------------------------------------------------------------------
  3193. long Mover::addRadioChunks (long which, unsigned char* packedChunkBuffer, long numChunks) {
  3194. // if ((numRadioChunks[which] + numChunks) >= MAX_RADIO_CHUNKS)
  3195. // Fatal(0, " Mover::addRadioChunks--Too many radio chunks ");
  3196. for (long i = 0; i < numChunks; i++)
  3197. addRadioChunk(which, packedChunkBuffer[i]);
  3198. //memcpy(&radioChunks[which][numRadioChunks[which]], packedChunkBuffer, numChunks);
  3199. //numRadioChunks[which] += numChunks;
  3200. return(numRadioChunks[which]);
  3201. }
  3202. //---------------------------------------------------------------------------
  3203. long Mover::grabRadioChunks (long which, unsigned char* packedChunkBuffer) {
  3204. if (numRadioChunks[which] > 0)
  3205. memcpy(packedChunkBuffer, radioChunks[which], numRadioChunks[which]);
  3206. return(numRadioChunks[which]);
  3207. }
  3208. //---------------------------------------------------------------------------
  3209. long Mover::updateRadioChunks (long which) {
  3210. if (getCommander() == Commander::home)
  3211. for (long i = 0; i < numRadioChunks[which]; i++)
  3212. playMessage((RadioMessageType)radioChunks[which][i]);
  3213. numRadioChunks[which] = 0;
  3214. return(NO_ERR);
  3215. }
  3216. //---------------------------------------------------------------------------
  3217. void Mover::playMessage (RadioMessageType messageId, bool propogateIfMultiplayer) {
  3218. if (pilot)
  3219. pilot->radioMessage(messageId, propogateIfMultiplayer);
  3220. }
  3221. //---------------------------------------------------------------------------
  3222. void Mover::setThreatRating (short rating) {
  3223. threatRating = rating;
  3224. if (threatRating == -1) {
  3225. //-----------
  3226. // Calc it...
  3227. threatRating = calcCV();
  3228. }
  3229. }
  3230. //---------------------------------------------------------------------------
  3231. long Mover::getThreatRating (void) {
  3232. if ((scenarioTime - creationTime) < 5.0)
  3233. return((long)((float)threatRating * newThreatMultiplier));
  3234. return(threatRating);
  3235. }
  3236. //---------------------------------------------------------------------------
  3237. bool Mover::enemyRevealed (void)
  3238. {
  3239. //-----------------------------------------------------
  3240. // What is our block and vertex number?
  3241. //NOTE: This function not used Anymore!!
  3242. return(false);
  3243. }
  3244. //---------------------------------------------------------------------------
  3245. #if 0
  3246. void Mover::getDamageClass (long& damageClass, bool& shutDown) {
  3247. //--------------------------------------------
  3248. // DamageClass is based upon the current CV...
  3249. float CV = (float)curCV / (float)maxCV;
  3250. if (CV > 0.90)
  3251. damageClass = DAMAGE_CLASS_NONE;
  3252. else if (CV > 0.75)
  3253. damageClass = DAMAGE_CLASS_LIGHT;
  3254. else if (CV > 0.50)
  3255. damageClass = DAMAGE_CLASS_MODERATE;
  3256. else if (CV > 0.10)
  3257. damageClass = DAMAGE_CLASS_SEVERE;
  3258. else
  3259. damageClass = DAMAGE_CLASS_WRECKAGE;
  3260. //--------------------------------------------
  3261. // Is the mech currently shutdown due to heat?
  3262. shutDown = (status == OBJECT_STATUS_SHUTDOWN);
  3263. }
  3264. #endif
  3265. //------------------------------------------------------------------------------------------
  3266. void Mover::setTeleportPosition (Stuff::Vector3D& newPos) {
  3267. teleportPosition = newPos;
  3268. }
  3269. //------------------------------------------------------------------------------------------
  3270. long Mover::getInventoryDamage (long itemIndex) {
  3271. if (itemIndex < (numOther + numWeapons + numAmmos))
  3272. return (MasterComponent::masterList[inventory[itemIndex].masterID].getHealth() - inventory[itemIndex].health);
  3273. return(0);
  3274. }
  3275. //------------------------------------------------------------------------------------------
  3276. float Mover::getVisualRange (void) {
  3277. return(MechWarrior::maxVisualRadius);
  3278. }
  3279. //------------------------------------------------------------------------------------------
  3280. void Mover::setChallenger (GameObjectPtr challenger) {
  3281. challengerWID = challenger->getWatchID();
  3282. }
  3283. //------------------------------------------------------------------------------------------
  3284. GameObjectPtr Mover::getChallenger (void) {
  3285. //------------------------------------------------------
  3286. // We ASSUME a disabled challenger is as good as none...
  3287. GameObjectPtr obj = ObjectManager->get(challengerWID);
  3288. if (obj && obj->isDisabled()) {
  3289. challengerWID = 0;
  3290. return(NULL);
  3291. }
  3292. return(obj);
  3293. }
  3294. //------------------------------------------------------------------------------------------
  3295. Stuff::Vector3D Mover::calcOffsetMoveGoal (Stuff::Vector3D target) {
  3296. Stuff::Vector3D start = getPosition();
  3297. Stuff::Vector2DOf<float> start2d;
  3298. start2d.x = start.x;
  3299. start2d.y = start.y;
  3300. Stuff::Vector2DOf<float> target2d;
  3301. target2d.x = target.x;
  3302. target2d.y = target.y;
  3303. //---------------------------------------------------------------
  3304. // First, we need to calc the delta vector--how much we extend
  3305. // the ray everytime we check the map cell for clear placement.
  3306. Stuff::Vector2DOf<float> deltaVector;
  3307. deltaVector.Subtract(start2d, target2d);
  3308. if ((deltaVector.x == 0.0) && (deltaVector.y == 0.0)) {
  3309. return(start);
  3310. }
  3311. deltaVector.Normalize(deltaVector);
  3312. float cellLength = (Terrain::worldUnitsPerCell);
  3313. cellLength *= 0.5;
  3314. deltaVector *= cellLength;
  3315. //if (deltaVector.GetLength() == 0.0) {
  3316. // newGoal = start;
  3317. // return(NO_ERR);
  3318. //}
  3319. //------------------------------------------
  3320. // Determine how far the ray must be cast...
  3321. float distanceToTarget = distance_from(start, target) /* * metersPerWorldUnit*/;
  3322. //------------------------------------------------------------
  3323. // We'll start at the target, and if it's blocked, we'll move
  3324. // toward our start location, looking for the first valid/open
  3325. // cell...
  3326. Stuff::Vector2DOf<float> curPoint;
  3327. curPoint.x = target.x;
  3328. curPoint.y = target.y;
  3329. Stuff::Vector2DOf<float> curRay;
  3330. curRay.x = 0.0;
  3331. curRay.y = 0.0;
  3332. float rayLength = 0.0;
  3333. long cellR, cellC;
  3334. Stuff::Vector3D curPoint3d(curPoint.x, curPoint.y, 0.0);
  3335. land->worldToCell(curPoint3d, cellR, cellC);
  3336. if (!GameMap->getPassable(cellR, cellC))
  3337. while (rayLength < distanceToTarget) {
  3338. curPoint3d.x = curPoint.x;
  3339. curPoint3d.y = curPoint.y;
  3340. curPoint3d.z = 0.0;
  3341. land->worldToCell(curPoint3d, cellR, cellC);
  3342. if (GameMap->getPassable(cellR, cellC))
  3343. break;
  3344. curPoint += deltaVector;
  3345. curRay.Subtract(curPoint, start2d);
  3346. rayLength = curRay.GetLength() /* * metersPerWorldUnit*/;
  3347. }
  3348. curPoint3d.z = land->getTerrainElevation(curPoint3d);
  3349. return(curPoint3d);
  3350. }
  3351. //---------------------------------------------------------------------------
  3352. #define MAX_MOVE_GOALS 60
  3353. inline void insertMoveGoal (long goalList[MAX_MOVE_GOALS][3], long r, long c, long wt) {
  3354. //------------------------------------------------------------
  3355. // This routine assumes wt > goaList[MAX_MOVE_GOALS - 1][2]...
  3356. // AND that MAX_MOVE_GOALS >= 2...
  3357. for (long i = MAX_MOVE_GOALS - 2; i > -1; i--)
  3358. if (wt < goalList[i][2])
  3359. break;
  3360. if (i < (MAX_MOVE_GOALS - 2))
  3361. memmove(&goalList[i + 2][0], &goalList[i + 1][0], (MAX_MOVE_GOALS - 2 - i) * sizeof(long) * 3);
  3362. goalList[i + 1][0] = r;
  3363. goalList[i + 1][1] = c;
  3364. goalList[i + 1][2] = wt;
  3365. }
  3366. //---------------------------------------------------------------------------
  3367. inline bool inMapBounds (long r, long c, long mapHeight, long mapWidth) {
  3368. return((r >= 0) && (r < mapHeight) && (c >= 0) && (c < mapWidth));
  3369. }
  3370. //---------------------------------------------------------------------------
  3371. long calcBestGoalFromTarget (long targetPos[2], long maxPos[2], float minRange, float bestRange, long* bestCell) {
  3372. float startLocal = 40.0f;
  3373. //------------------------------------------------------------------------------------------
  3374. // Within magic radius. Check REAL LOS now.
  3375. // Check is really simple.
  3376. // Find deltaCellRow and deltaCellCol and iterate over them from source to dest.
  3377. // If the magic line ever goes BELOW the terrainElevation PLUS localElevation return false.
  3378. Stuff::Vector3D startPos, endPos;
  3379. startPos.Zero();
  3380. endPos.Zero();
  3381. land->getCellPos(maxPos[0], maxPos[1], endPos);
  3382. land->getCellPos(targetPos[0], targetPos[1], startPos);
  3383. Stuff::Vector3D deltaCellVec;
  3384. deltaCellVec.y = maxPos[0] - targetPos[0];
  3385. deltaCellVec.x = maxPos[1] - targetPos[1];
  3386. deltaCellVec.z = 0.0f;
  3387. float startHeight = startPos.z;
  3388. float cellLength = (Terrain::worldUnitsPerCell * metersPerWorldUnit);
  3389. float length = deltaCellVec.GetApproximateLength();
  3390. long lastCellR = -1;
  3391. long lastCellC = -1;
  3392. if (length > Stuff::SMALL) {
  3393. float colLength = deltaCellVec.x / length;
  3394. float rowLength = deltaCellVec.y / length;
  3395. float heightLen = (endPos.z - startPos.z) / length;
  3396. float lastCol = fabs(colLength * 2.0);
  3397. float lastRow = fabs(rowLength * 2.0);
  3398. float startCellRow = targetPos[0];
  3399. float startCellCol = targetPos[1];
  3400. float endCellRow = maxPos[0];
  3401. float endCellCol = maxPos[1];
  3402. Stuff::Vector3D currentPos = startPos;
  3403. Stuff::Vector3D dist;
  3404. dist.Subtract(endPos,currentPos);
  3405. float remainingDist = dist.GetApproximateLength();
  3406. float bestRemainingDist = remainingDist - (bestRange * worldUnitsPerMeter);
  3407. bool colDone = false, rowDone = false;
  3408. while (!colDone || !rowDone) {
  3409. if (fabs(startCellRow - endCellRow) > lastRow) //DO NOT INCLUDE LAST CELL!!!!!
  3410. startCellRow += rowLength;
  3411. else {
  3412. startCellRow = (endCellRow - lastRow);
  3413. rowDone = true;
  3414. }
  3415. if (fabs(startCellCol - endCellCol) > lastCol) //DO NOT INCLUDE LAST CELL!!!!!
  3416. startCellCol += colLength;
  3417. else {
  3418. startCellCol = (endCellCol - lastCol);
  3419. colDone = true;
  3420. }
  3421. startHeight += heightLen;
  3422. long startCellC = startCellCol;
  3423. long startCellR = startCellRow;
  3424. land->getCellPos(startCellR,startCellC,currentPos);
  3425. float localElev = (worldUnitsPerMeter * (float)GameMap->getLocalHeight(startCellR,startCellC));
  3426. currentPos.z += localElev;
  3427. if (startHeight+startLocal < currentPos.z)
  3428. if (GameMap->inBounds(lastCellR, lastCellC) && GameMap->getCell(lastCellR, lastCellC)->getPassable()) {
  3429. bestCell[0] = lastCellR;
  3430. bestCell[1] = lastCellC;
  3431. return(1);
  3432. }
  3433. dist.Subtract(endPos,currentPos);
  3434. remainingDist = dist.GetApproximateLength();
  3435. if (fabs(remainingDist - bestRemainingDist) < cellLength)
  3436. if (remainingDist > bestRemainingDist)
  3437. if (GameMap->inBounds(startCellR, startCellC) && GameMap->getCell(startCellR, startCellC)->getPassable()) {
  3438. bestCell[0] = startCellR;
  3439. bestCell[1] = startCellC;
  3440. return(1);
  3441. }
  3442. if (remainingDist < 10.0) {
  3443. if (GameMap->inBounds(startCellR, startCellC) && GameMap->getCell(startCellR, startCellC)->getPassable()) {
  3444. bestCell[0] = startCellR;
  3445. bestCell[1] = startCellC;
  3446. return(1);
  3447. }
  3448. else
  3449. return(0);
  3450. }
  3451. if (GameMap->inBounds(startCellR, startCellC) && GameMap->getCell(startCellR, startCellC)->getPassable()) {
  3452. //-----------------------------------------------------------
  3453. // Save the last passable cell checked so far that has LOS...
  3454. lastCellR = startCellR;
  3455. lastCellC = startCellC;
  3456. }
  3457. }
  3458. }
  3459. if (lastCellR != -1) {
  3460. bestCell[0] = lastCellR;
  3461. bestCell[1] = lastCellC;
  3462. return(1);
  3463. }
  3464. return(0);
  3465. }
  3466. //----------------------------------------------------------------------------
  3467. long Mover::calcLineOfSightView (long range) {
  3468. GameMap->clearCellDebugs(1);
  3469. for (long r = cellPositionRow - range; r < cellPositionRow + range; r++)
  3470. for (long c = cellPositionCol - range; c < cellPositionCol + range; c++) {
  3471. if (GameMap->inBounds(r, c)) {
  3472. if (lineOfSight(r, c, false))
  3473. GameMap->setCellDebug(r, c, 1, 1);
  3474. else {
  3475. GameMap->setCellDebug(r, c, 2, 1);
  3476. }
  3477. }
  3478. }
  3479. return(0);
  3480. }
  3481. //----------------------------------------------------------------------------
  3482. void Mover::setMoveType (long type) {
  3483. moveType = type;
  3484. //--------------------------------------------------------------------
  3485. // For now, level = type. This can change in the future, so always set
  3486. // it here!
  3487. if (moveType == MOVETYPE_GROUND)
  3488. moveLevel = 0;
  3489. else if (moveType == MOVETYPE_AIR)
  3490. moveLevel = 2;
  3491. else
  3492. STOP(("Mover.setMoveType: bad moveType %d", type));
  3493. }
  3494. //-----------------------------------------------------------------------------
  3495. //void Mover::precalcAttackZones (void) {
  3496. //}
  3497. //-----------------------------------------------------------------------------
  3498. long Mover::calcGlobalPath (GlobalPathStep* globalPath, GameObjectPtr obj, Stuff::Vector3D* location, bool useClosedAreas) {
  3499. #if 1
  3500. long startArea = GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol);
  3501. long goalArea = -1;
  3502. long goalCell[2] = {-1, -1};
  3503. if (obj) {
  3504. goalArea = GlobalMoveMap[moveLevel]->calcArea(obj->cellPositionRow, obj->cellPositionCol);
  3505. if (goalArea == -1) {
  3506. Stuff::Vector3D goalPos = obj->getPosition();
  3507. if (obj->isBuilding()) {
  3508. BuildingPtr building = (BuildingPtr)obj;
  3509. long goalRow = 0, goalCol = 0;
  3510. bool foundGoal = building->calcAdjacentAreaCell(moveLevel, -1, goalRow, goalCol);
  3511. if (foundGoal)
  3512. land->cellToWorld(goalRow, goalCol, goalPos);
  3513. else {
  3514. Stuff::Vector3D objectPos = obj->getPosition();
  3515. goalPos = calcOffsetMoveGoal(objectPos);
  3516. }
  3517. }
  3518. else {
  3519. Stuff::Vector3D objectPos = obj->getPosition();
  3520. goalPos = calcOffsetMoveGoal(objectPos);
  3521. }
  3522. land->worldToCell(goalPos, goalCell[0], goalCell[1]);
  3523. goalArea = GlobalMoveMap[moveLevel]->calcArea(goalCell[0], goalCell[1]);
  3524. }
  3525. }
  3526. else if (location) {
  3527. land->worldToCell(*location, goalCell[0], goalCell[1]);
  3528. goalArea = GlobalMoveMap[moveLevel]->calcArea(goalCell[0], goalCell[1]);
  3529. }
  3530. if (goalArea == -1)
  3531. return(0);
  3532. bool startAreaOpen = (startArea >= 0) && GlobalMoveMap[moveLevel]->areas[startArea].open;
  3533. long numSteps = -1;
  3534. if (startAreaOpen) {
  3535. if (numFunctionalWeapons > 0)
  3536. GlobalMoveMap[moveLevel]->useClosedAreas = useClosedAreas;
  3537. GlobalMoveMap[moveLevel]->moverTeamID = getTeamId();
  3538. numSteps = GlobalMoveMap[moveLevel]->calcPath(startArea,
  3539. goalArea,
  3540. globalPath,
  3541. cellPositionRow,
  3542. cellPositionCol,
  3543. goalCell[0],
  3544. goalCell[1]
  3545. );
  3546. GlobalMoveMap[moveLevel]->useClosedAreas = false;
  3547. }
  3548. return(numSteps);
  3549. #else
  3550. return(0);
  3551. #endif
  3552. }
  3553. //-----------------------------------------------------------------------------
  3554. bool Mover::canMoveHere (Stuff::Vector3D worldPos) {
  3555. //-------------------------------------------
  3556. // If I'm a coptor, terrain doesn't matter...
  3557. if (moveLevel == 2)
  3558. return(true);
  3559. if (!GameMap->getPassable(worldPos))
  3560. return(false);
  3561. //---------------------------------------------------------------
  3562. // If I'm a hovercraft and this cell is passable, I know I can...
  3563. if (moveLevel == 1)
  3564. return(true);
  3565. //--------------------------------------------------------
  3566. // Otherwise, I'm a ground mover and there may be water...
  3567. return(land->getWater(worldPos) < 2);
  3568. }
  3569. //-----------------------------------------------------------------------------
  3570. #ifdef LAB_ONLY
  3571. extern __int64 MCTimeCalcGoal1Update;
  3572. extern __int64 MCTimeCalcGoal2Update;
  3573. extern __int64 MCTimeCalcGoal3Update;
  3574. extern __int64 MCTimeCalcGoal4Update;
  3575. extern __int64 MCTimeCalcGoal5Update;
  3576. extern __int64 MCTimeCalcGoal6Update;
  3577. #endif
  3578. long Mover::calcMoveGoal (GameObjectPtr target,
  3579. Stuff::Vector3D moveCenter,
  3580. float moveRadius,
  3581. Stuff::Vector3D moveGoal,
  3582. long selectionIndex,
  3583. Stuff::Vector3D& newGoal,
  3584. long numValidAreas,
  3585. short* validAreas,
  3586. unsigned long moveParams) {
  3587. __int64 startTime = 0;
  3588. if (goalMapRowStart[0] == -1) {
  3589. for (long i = 0; i < GOALMAP_CELL_DIM; i++)
  3590. goalMapRowStart[i] = i * GOALMAP_CELL_DIM;
  3591. for (long r = 0; r < GOALMAP_CELL_DIM; r++)
  3592. for (long c = 0; c < GOALMAP_CELL_DIM; c++) {
  3593. long index = r * GOALMAP_CELL_DIM + c;
  3594. goalMapRowCol[index][0] = r;
  3595. goalMapRowCol[index][1] = c;
  3596. }
  3597. }
  3598. bool playerMove = ((moveParams & MOVEPARAM_PLAYER) != 0);
  3599. bool movingToRepair = (pilot->getCurTacOrder()->code == TACTICAL_ORDER_REFIT);
  3600. bool movingToCapture = (pilot->getCurTacOrder()->code == TACTICAL_ORDER_CAPTURE);
  3601. bool avoidStationaryMovers = movingToRepair;
  3602. bool noTravelOffMap = !GameMap->getOffMap(cellPositionRow, cellPositionCol);
  3603. if (moveParams & MOVEPARAM_MYSTERY_PARAM) {
  3604. newGoal = moveGoal;
  3605. return(NO_ERR);
  3606. }
  3607. if (moveParams & MOVEPARAM_STEP_TOWARD_TARGET) {
  3608. //--------------------------------------------------------------------------
  3609. // We want to pick a goal just a couple cells away from our position, but in
  3610. // the direction of our target...
  3611. Stuff::Vector3D aimVector;
  3612. aimVector.Subtract(moveGoal, position);
  3613. aimVector.z = 0;
  3614. if (aimVector.GetLength() > 0.0) {
  3615. aimVector.Normalize(aimVector);
  3616. float stepLength = (Terrain::worldUnitsPerVertex * 1.5);
  3617. aimVector.x *= stepLength;
  3618. aimVector.y *= stepLength;
  3619. aimVector.z = land->getTerrainElevation(aimVector);
  3620. Stuff::Vector3D targetGoal;
  3621. targetGoal.Add(position, aimVector);
  3622. newGoal = calcOffsetMoveGoal(targetGoal);
  3623. }
  3624. return(NO_ERR);
  3625. }
  3626. if (moveParams & MOVEPARAM_STEP_ADJACENT_TARGET) {
  3627. //--------------------------------------------------------------------------
  3628. // We want to pick a goal just a couple cells away from our position, but in
  3629. // the direction of our target...
  3630. newGoal = calcOffsetMoveGoal(moveGoal);
  3631. return(NO_ERR);
  3632. }
  3633. //----------------------------
  3634. // The Goal Map is in CELLS...
  3635. memset(goalMap, 0, sizeof(long) * GOALMAP_CELL_DIM * GOALMAP_CELL_DIM);
  3636. long centerCell[2];
  3637. land->worldToCell(moveCenter, centerCell[0], centerCell[1]);
  3638. long goalCell[2];
  3639. land->worldToCell(moveGoal, goalCell[0], goalCell[1]);
  3640. long mapCellUL[2];
  3641. mapCellUL[0] = centerCell[0] - GOALMAP_CELL_DIM / 2;
  3642. mapCellUL[1] = centerCell[1] - GOALMAP_CELL_DIM / 2;
  3643. //DEBUG
  3644. //=================================
  3645. Stuff::Vector3D start;
  3646. land->cellToWorld(mapCellUL[0], mapCellUL[1], start);
  3647. //==============================
  3648. bool isAttackOrder = pilot->getCurTacOrder()->isCombatOrder();
  3649. if (!target && !isAttackOrder) {
  3650. newGoal = calcOffsetMoveGoal(moveGoal);
  3651. return(NO_ERR);
  3652. }
  3653. //--------
  3654. // HACK!!!
  3655. if (target && (pilot->getCurTacOrder()->code == TACTICAL_ORDER_GUARD))
  3656. isAttackOrder = true;
  3657. if (!target)
  3658. moveGoal.z = land->getTerrainElevation(moveGoal);
  3659. long optimalCellRange = 2;
  3660. if (movingToRepair)
  3661. optimalCellRange = 1;
  3662. if (isAttackOrder) {
  3663. float metersPerCell = Terrain::worldUnitsPerCell * metersPerWorldUnit;
  3664. float fireRangeInMeters = pilot->getSituationFireRange();
  3665. long fireCellRange = 0;
  3666. if (fireRangeInMeters > 0.0) {
  3667. fireCellRange = (long)(pilot->getSituationFireRange() / metersPerCell);
  3668. if (fireCellRange < 1)
  3669. fireCellRange = 1;
  3670. if (fireCellRange > (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/)
  3671. fireCellRange = (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/;
  3672. }
  3673. else if (fireRangeInMeters == -1.0)
  3674. fireCellRange = 2;
  3675. long minCellRange = (long)(getMinFireRange() / metersPerCell);
  3676. if (minCellRange > (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/)
  3677. minCellRange = (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/;
  3678. long maxCellRange = (long)(getMaxFireRange() / metersPerCell);
  3679. if (maxCellRange > (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/)
  3680. maxCellRange = (MAX_ATTACK_CELLRANGE - 1)/*(GOALMAP_CELL_DIM / 2)*/;
  3681. //----------------------------------------
  3682. // Anything beyond our max range is bad...
  3683. long fireRange = (long)pilot->getCurTacOrder()->attackParams.range;
  3684. if (/*!playerMove && */(fireRange != FIRERANGE_SHORT) && (fireRange != FIRERANGE_MEDIUM) && (fireRange != FIRERANGE_LONG)) {
  3685. long firstIndex = rangedCellsIndices[maxCellRange + 1][0];
  3686. long lastIndex = rangedCellsIndices[MAX_ATTACK_CELLRANGE - 1][1];
  3687. for (long i = firstIndex; i < lastIndex; i++) {
  3688. long r = goalCell[0] - mapCellUL[0] + rangedCells[i][0];
  3689. long c = goalCell[1] - mapCellUL[1] + rangedCells[i][1];
  3690. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3691. goalMap[goalMapRowStart[r] + c] -= 500;
  3692. }
  3693. }
  3694. //---------------------------------------------------
  3695. // If we have a max move radius (i.e. guarding area),
  3696. // anything beyond our radius is bad...
  3697. startTime = GetCycles();
  3698. if (moveRadius > 0.0) {
  3699. long moveCellRange = moveRadius / metersPerCell;
  3700. if (moveCellRange < 1)
  3701. moveCellRange = 1;
  3702. if (moveCellRange > (MAX_ATTACK_CELLRANGE - 1))
  3703. moveCellRange = (MAX_ATTACK_CELLRANGE - 1);
  3704. long firstIndex = rangedCellsIndices[moveCellRange + 1][0];
  3705. long lastIndex = rangedCellsIndices[MAX_ATTACK_CELLRANGE - 1][1];
  3706. for (long i = firstIndex; i < lastIndex; i++) {
  3707. long r = centerCell[0] - mapCellUL[0] + rangedCells[i][0];
  3708. long c = centerCell[1] - mapCellUL[1] + rangedCells[i][1];
  3709. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3710. goalMap[goalMapRowStart[r] + c] -= 5000;
  3711. }
  3712. }
  3713. #ifdef LAB_ONLY
  3714. MCTimeCalcGoal2Update += (GetCycles() - startTime);
  3715. #endif
  3716. //---------------------------------------------------------------------------
  3717. // If the pilot has a set fire range, let's use it in determining how far out
  3718. // we would consider attacking. If we're ramming, fireCellrange will stay at
  3719. // 0 since firerange would then be 0.0...
  3720. if (fireRangeInMeters > 0.0) {
  3721. //---------------------------------------------------------------
  3722. // Due to new minimum range rule, we do NOT want to be within our
  3723. // min range...
  3724. if (minCellRange > 0) {
  3725. long lastIndex = rangedCellsIndices[minCellRange][0];
  3726. for (long i = 0; i < lastIndex; i++) {
  3727. long r = goalCell[0] - mapCellUL[0] + rangedCells[i][0];
  3728. long c = goalCell[1] - mapCellUL[1] + rangedCells[i][1];
  3729. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3730. goalMap[goalMapRowStart[r] + c] -= 500;
  3731. }
  3732. }
  3733. }
  3734. else if (fireRangeInMeters == -1.0)
  3735. fireCellRange = 2;
  3736. optimalCellRange = fireCellRange;
  3737. }
  3738. //-----------------------------------------------------------------------
  3739. // Optimal Range (based upon current fire range of pilot, if possible)...
  3740. if (moveParams & MOVEPARAM_RANDOM_OPTIMAL)
  3741. for (long i = 0; i < numOptimalIncrements; i++) {
  3742. long r = goalCell[0] - mapCellUL[0] + optimalCells[optimalCellRange][i][0];
  3743. long c = goalCell[0] - mapCellUL[0] + optimalCells[optimalCellRange][i][1];
  3744. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3745. goalMap[goalMapRowStart[r] + c] += (500 + RandomNumber(40) * 5);
  3746. }
  3747. else
  3748. for (long i = 0; i < numOptimalIncrements; i++) {
  3749. long r = goalCell[0] - mapCellUL[0] + optimalCells[optimalCellRange][i][0];
  3750. long c = goalCell[0] - mapCellUL[0] + optimalCells[optimalCellRange][i][1];
  3751. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3752. goalMap[goalMapRowStart[r] + c] += 500;
  3753. }
  3754. //--------------------------------------
  3755. // Calc the closest map cell to start...
  3756. long startCell[2];
  3757. land->worldToCell(position, startCell[0], startCell[1]);
  3758. startCell[0] -= mapCellUL[0];
  3759. startCell[1] -= mapCellUL[1];
  3760. if (startCell[0] < 0)
  3761. startCell[0] = 0;
  3762. else if (startCell[0] >= GOALMAP_CELL_DIM)
  3763. startCell[0] = GOALMAP_CELL_DIM - 1;
  3764. if (startCell[1] < 0)
  3765. startCell[1] = 0;
  3766. else if (startCell[1] >= GOALMAP_CELL_DIM)
  3767. startCell[1] = GOALMAP_CELL_DIM - 1;
  3768. //---------------------------------------------------------------------------
  3769. //NOTE: Do we even want the following, now? I think this was more a hack than
  3770. //anything. Take it out and see what effect it has... -gd 7/24/00
  3771. //--------------------------------------------------------------
  3772. // The closer we are to the goal, the better (within reason:)...
  3773. if (!playerMove)
  3774. for (long cellR = -1; cellR < 2; cellR++)
  3775. for (long cellC = -1; cellC < 2; cellC++) {
  3776. long r = goalCell[0] - mapCellUL[0] + cellR;
  3777. long c = goalCell[1] - mapCellUL[1] + cellC;
  3778. if (inMapBounds(r, c, GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3779. goalMap[goalMapRowStart[r] + c] += 100;
  3780. }
  3781. //----------------------------------
  3782. // If we must move somewhere else...
  3783. if (moveParams & MOVEPARAM_SOMEWHERE_ELSE)
  3784. goalMap[goalMapRowStart[startCell[0]] + startCell[1]] -= 10000;
  3785. //-------------------------------------
  3786. // Cells closer to the start cell get a
  3787. // slight bonus...
  3788. for (long r = 0; r < GOALMAP_CELL_DIM; r++)
  3789. for (long c = 0; c < GOALMAP_CELL_DIM; c++) {
  3790. long dist = 0;
  3791. if (r > startCell[0])
  3792. dist += (r - startCell[0]);
  3793. else
  3794. dist += (startCell[0] - r);
  3795. if (c > startCell[1])
  3796. dist += (c - startCell[1]);
  3797. else
  3798. dist += (startCell[1] - c);
  3799. goalMap[goalMapRowStart[r] + c] -= (dist * 1);
  3800. }
  3801. //----------------------------------------------------------------------
  3802. // If we're attacking this target, let's use a selectionIndex based upon
  3803. // the number of people in my unit attacking this target...
  3804. if (getGroup()) {
  3805. MoverPtr mates[MAX_MOVERGROUP_COUNT];
  3806. long numMates = getGroup()->getMovers(mates);
  3807. for (long i = 0; i < numMates; i++) {
  3808. if (mates[i] == this)
  3809. continue;
  3810. MechWarriorPtr pilot = mates[i]->getPilot();
  3811. Stuff::Vector3D goal;
  3812. goal.x = goal.y = goal.z = 0.0f;
  3813. if (pilot && pilot->getMoveGlobalGoal(goal)) {
  3814. long worldCell[2];
  3815. land->worldToCell(goal, worldCell[0], worldCell[1]);
  3816. worldCell[0] -= mapCellUL[0];
  3817. worldCell[1] -= mapCellUL[1];
  3818. if (inMapBounds(worldCell[0], worldCell[1], GOALMAP_CELL_DIM, GOALMAP_CELL_DIM))
  3819. goalMap[goalMapRowStart[worldCell[0]] + worldCell[1]] -= 100;
  3820. }
  3821. }
  3822. }
  3823. //-----------------------
  3824. // Init the area lists...
  3825. char validAreaTable[MAX_GLOBALMAP_AREAS];
  3826. for (long i = 0; i < GlobalMoveMap[moveLevel]->numAreas; i++)
  3827. validAreaTable[i] = -1;
  3828. long curArea = GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol);
  3829. long goalArea = GlobalMoveMap[moveLevel]->calcArea(goalCell[0], goalCell[1]);
  3830. if (numValidAreas > 0) {
  3831. for (long i = 0; i < numValidAreas; i++) {
  3832. if (validAreas[i] == goalArea) {
  3833. numValidAreas = i + 1;
  3834. break;
  3835. }
  3836. }
  3837. }
  3838. for (i = 0; i < numValidAreas; i++)
  3839. validAreaTable[validAreas[i]] = 1;
  3840. startTime = GetCycles();
  3841. long deepWaterWeight = ((moveLevel == 1) ? 0 : 999999);
  3842. //-----------------------------------------
  3843. // Finally, lay down the terrain weights...
  3844. for (r = 0; r < GOALMAP_CELL_DIM; r++)
  3845. for (long c = 0; c < GOALMAP_CELL_DIM; c++) {
  3846. long curCellRow = mapCellUL[0] + r;
  3847. long curCellCol = mapCellUL[1] + c;
  3848. if (GameMap->inBounds(curCellRow, curCellCol))
  3849. {
  3850. //------------------------------------------------------------
  3851. // In the long run, we should simply have a look-up table that
  3852. // contains a weight for each terrain\tile type (including
  3853. // transitions)...
  3854. MapCellPtr mapCell = GameMap->getCell(curCellRow, curCellCol);
  3855. //-----------------------
  3856. // Tile (terrain) type...
  3857. //long tileType = curTile.getTileType();
  3858. long goalMapIndex = goalMapRowStart[r] + c;
  3859. if (!mapCell->getPassable())
  3860. goalMap[goalMapIndex] -= 10000;
  3861. if (mapCell->getOffMap())
  3862. goalMap[goalMapIndex] -= 10000;
  3863. if (mapCell->getDeepWater())
  3864. goalMap[goalMapIndex] -= deepWaterWeight;
  3865. bool moverHere = mapCell->getMover();
  3866. if (avoidStationaryMovers && moverHere)
  3867. goalMap[goalMapIndex] -= 1000;
  3868. if (noTravelOffMap && GameMap->getOffMap(curCellRow, curCellCol))
  3869. goalMap[goalMapIndex] -= 50000;
  3870. long area = GlobalMoveMap[moveLevel]->calcArea(curCellRow, curCellCol);
  3871. if ((area != curArea) && (area != -1) && (curArea != -1)) {
  3872. if (moveLevel < 2) {
  3873. //if (GlobalMoveMap[moveLevel]->areas[area].type == AREA_TYPE_NORMAL) {
  3874. if (GlobalMoveMap[moveLevel]->getPathExists(curArea, area) == GLOBALPATH_EXISTS_UNKNOWN) {
  3875. if (CalcValidAreaTable) {
  3876. GlobalPathStep globalPath[128];
  3877. long numSteps = GlobalMoveMap[moveLevel]->calcPath(curArea, area, globalPath);
  3878. if (numSteps > 0) {
  3879. for (long j = 0; j < numSteps; j++)
  3880. for (long k = 0; k < numSteps; k++)
  3881. GlobalMoveMap[moveLevel]->setPathExists(globalPath[j].thruArea, globalPath[k].thruArea, GLOBALPATH_EXISTS_TRUE);
  3882. }
  3883. else {
  3884. GlobalMoveMap[moveLevel]->setPathExists(area, curArea, GLOBALPATH_EXISTS_FALSE);
  3885. GlobalMoveMap[moveLevel]->setPathExists(curArea, area, GLOBALPATH_EXISTS_FALSE);
  3886. }
  3887. }
  3888. else
  3889. GlobalMoveMap[moveLevel]->setPathExists(curArea, area, GLOBALPATH_EXISTS_FALSE);
  3890. }
  3891. if (GlobalMoveMap[moveLevel]->getPathExists(curArea, area) == GLOBALPATH_EXISTS_FALSE)
  3892. goalMap[goalMapRowStart[r] + c] -= 50000;
  3893. //}
  3894. }
  3895. }
  3896. }
  3897. else
  3898. goalMap[goalMapRowStart[r] + c] = -999999;
  3899. }
  3900. #ifdef LAB_ONLY
  3901. MCTimeCalcGoal1Update += (GetCycles() - startTime);
  3902. #endif
  3903. startTime = GetCycles();
  3904. long goalList[MAX_MOVE_GOALS][2];
  3905. //------------------
  3906. // Setup the list...
  3907. for (i = 0; i < MAX_MOVE_GOALS; i++) {
  3908. goalList[i][0] = -1;
  3909. goalList[i][1] = -99999;
  3910. }
  3911. for (i = 0; i < MAX_MOVE_GOALS; i++) {
  3912. //-----------------------------------------
  3913. // This assumes that MAX_MOVE_GOALS >= 2...
  3914. long weight = goalMap[i];
  3915. for (long j = MAX_MOVE_GOALS - 2; j > -1; j--)
  3916. if (weight < goalList[j][1])
  3917. break;
  3918. if (j < (MAX_MOVE_GOALS - 2))
  3919. memmove(&goalList[j + 2][0], &goalList[j + 1][0], (MAX_MOVE_GOALS - 2 - j) * sizeof(long) * 2);
  3920. goalList[j + 1][0] = i;
  3921. goalList[j + 1][1] = weight;
  3922. }
  3923. for (i = MAX_MOVE_GOALS; i < (GOALMAP_CELL_DIM * GOALMAP_CELL_DIM); i++) {
  3924. //------------------------------------------------------------
  3925. // This routine assumes wt > goaList[MAX_MOVE_GOALS - 1][2]...
  3926. // AND that MAX_MOVE_GOALS >= 2...
  3927. long weight = goalMap[i];
  3928. if (weight > goalList[MAX_MOVE_GOALS - 1][1]) {
  3929. for (long j = MAX_MOVE_GOALS - 2; j > -1; j--)
  3930. if (weight < goalList[j][1])
  3931. break;
  3932. if (j < (MAX_MOVE_GOALS - 2))
  3933. memmove(&goalList[j + 2][0], &goalList[j + 1][0], (MAX_MOVE_GOALS - 2 - j) * sizeof(long) * 2);
  3934. goalList[j + 1][0] = i;
  3935. goalList[j + 1][1] = weight;
  3936. }
  3937. }
  3938. #ifdef LAB_ONLY
  3939. MCTimeCalcGoal3Update += (GetCycles() - startTime);
  3940. #endif
  3941. //----------------------------------------------------------------------
  3942. // If we're attacking this target, let's use a selectionIndex based upon
  3943. // the number of people in my unit attacking this target...
  3944. //#ifdef USE_GROUPS
  3945. if ((getObjectClass() == ELEMENTAL) && target && getGroup()) {
  3946. MoverPtr mates[MAX_MOVERGROUP_COUNT];
  3947. long numMates = getGroup()->getMovers(mates);
  3948. selectionIndex = 0;
  3949. for (long i = 0; i < numMates; i++) {
  3950. if (mates[i] == this)
  3951. break;
  3952. MechWarriorPtr pilot = mates[i]->getPilot();
  3953. if (pilot) {
  3954. if (pilot->getCurrentTarget() == target)
  3955. selectionIndex++;
  3956. }
  3957. }
  3958. }
  3959. //#endif
  3960. //---------------------------------
  3961. // so they don't select wacky goals
  3962. selectionIndex = -1;
  3963. //------------------------------
  3964. // Now, pick the goal we want...
  3965. long curGoalIndex = 0;
  3966. if ((selectionIndex > 0) && (selectionIndex < MAX_MOVE_GOALS))
  3967. curGoalIndex = selectionIndex;
  3968. long curGoalCell[2];
  3969. bool noLOF = true;
  3970. curGoalCell[0] = mapCellUL[0] + goalMapRowCol[goalList[0][0]][0];
  3971. curGoalCell[1] = mapCellUL[1] + goalMapRowCol[goalList[0][0]][1];
  3972. //Stuff::Vector3D goalPos = moveGoal;
  3973. //goalPos.z += target->getAppearRadius(); //Lets not look along the ground, shall we.
  3974. //Target probably has some altitude
  3975. if (hasWeaponNode()) {
  3976. Stuff::Vector3D curMoverPosition;
  3977. curMoverPosition = position;
  3978. long curMoverRow = cellPositionRow;
  3979. long curMoverCol = cellPositionCol;
  3980. startTime = GetCycles();
  3981. long i = 0;
  3982. ObjectManager->useMoverLineOfSightTable = false;
  3983. while (noLOF && (i < MaxMoveGoalChecks)) {
  3984. long index = goalList[i][0];
  3985. curGoalCell[0] = mapCellUL[0] + goalMapRowCol[index][0];
  3986. curGoalCell[1] = mapCellUL[1] + goalMapRowCol[index][1];
  3987. i++;
  3988. if (goalList[i][1] > -900000) {
  3989. Stuff::Vector3D start;
  3990. land->cellToWorld(curGoalCell[0], curGoalCell[1], start);
  3991. //start.x = (float)(mapCellUL[1] + curGoalCell[1]) * Terrain::worldUnitsPerCell + Terrain::worldUnitsPerCell / 2 - Terrain::worldUnitsMapSide / 2;
  3992. //start.y = (Terrain::worldUnitsMapSide / 2) - ((float)(mapCellUL[0] + curGoalCell[0]) * Terrain::worldUnitsPerCell) - Terrain::worldUnitsPerCell / 2;
  3993. start.z = land->getTerrainElevation(start)/* + 25.0f*/; //For that matter, so do we!
  3994. position = start;
  3995. cellPositionRow = curGoalCell[0];
  3996. cellPositionCol = curGoalCell[1];
  3997. __int64 lstartTime = GetCycles();
  3998. if (goalList[i][1] > -10000) {
  3999. if (movingToCapture)
  4000. noLOF = false;
  4001. else if (target)
  4002. noLOF = !lineOfSight(target, 0.0f, false);
  4003. else
  4004. noLOF = !lineOfSight(moveGoal, false);
  4005. }
  4006. #ifdef LAB_ONLY
  4007. MCTimeCalcGoal4Update += (GetCycles() - lstartTime);
  4008. #endif
  4009. }
  4010. }
  4011. #ifdef LAB_ONLY
  4012. MCTimeCalcGoal5Update += (GetCycles() - startTime);
  4013. #endif
  4014. ObjectManager->useMoverLineOfSightTable = true;
  4015. position = curMoverPosition;
  4016. cellPositionRow = curMoverRow;
  4017. cellPositionCol = curMoverCol;
  4018. }
  4019. GameMap->clearCellDebugs(2);
  4020. startTime = GetCycles();
  4021. if (noLOF && hasWeaponNode()) {
  4022. long targetPos[2], maxPos[2], bestCell[4][2];
  4023. float castRange;
  4024. long result[4];
  4025. long resultCost[4] = {0, 0, 0, 0};
  4026. long confidence[4];
  4027. long bestBest = 0;
  4028. if (moveRadius > 0.0) {
  4029. targetPos[0] = centerCell[0];
  4030. targetPos[1] = centerCell[1];
  4031. maxPos[0] = goalCell[0];
  4032. maxPos[1] = goalCell[1];
  4033. castRange = moveRadius;
  4034. result[0] = calcBestGoalFromTarget(targetPos, maxPos, 0.0, castRange, bestCell[0]);
  4035. if (result[0])
  4036. resultCost[0] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol),GlobalMoveMap[moveLevel]->calcArea(bestCell[0][0], bestCell[0][1]), false, confidence[0], true);
  4037. bestCell[1][0] = centerCell[0];
  4038. bestCell[1][1] = centerCell[1];
  4039. result[1] = 1;
  4040. if (result[1])
  4041. resultCost[1] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol),GlobalMoveMap[moveLevel]->calcArea(bestCell[1][0], bestCell[1][1]), false, confidence[1], true);
  4042. if (resultCost[1] > 0)
  4043. if (resultCost[1] < resultCost[bestBest])
  4044. bestBest = 1;
  4045. if (resultCost[bestBest] > 0) {
  4046. curGoalCell[0] = bestCell[bestBest][0];
  4047. curGoalCell[1] = bestCell[bestBest][1];
  4048. }
  4049. if (DebugGameObject[0] == this) {
  4050. if (result[0])
  4051. GameMap->setCellDebug(bestCell[0][0], bestCell[0][1], 3, 2);
  4052. if (result[1])
  4053. GameMap->setCellDebug(bestCell[1][0], bestCell[1][1], 3, 2);
  4054. }
  4055. }
  4056. else {
  4057. targetPos[0] = goalCell[0];
  4058. targetPos[1] = goalCell[1];
  4059. maxPos[0] = targetPos[0] - 10;
  4060. maxPos[1] = targetPos[1];
  4061. castRange = pilot->getSituationFireRange();
  4062. long bestBest = 0;
  4063. result[0] = calcBestGoalFromTarget(targetPos, maxPos, 0.0, castRange, bestCell[0]);
  4064. if (result[0])
  4065. resultCost[0] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol),GlobalMoveMap[moveLevel]->calcArea(bestCell[0][0], bestCell[0][1]), false, confidence[0], true);
  4066. maxPos[0] = targetPos[0];
  4067. maxPos[1] = targetPos[1] + 10;
  4068. result[1] = calcBestGoalFromTarget(targetPos, maxPos, 0.0, pilot->getSituationFireRange(), bestCell[1]);
  4069. if (result[1])
  4070. resultCost[1] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol),GlobalMoveMap[moveLevel]->calcArea(bestCell[1][0], bestCell[1][1]), false, confidence[1], true);
  4071. if (resultCost[1] > 0)
  4072. if (resultCost[1] < resultCost[bestBest])
  4073. bestBest = 1;
  4074. maxPos[0] = targetPos[0] + 10;
  4075. maxPos[1] = targetPos[1];
  4076. result[2] = calcBestGoalFromTarget(targetPos, maxPos, 0.0, pilot->getSituationFireRange(), bestCell[2]);
  4077. if (result[2])
  4078. resultCost[2] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol), GlobalMoveMap[moveLevel]->calcArea(bestCell[2][0], bestCell[2][1]), false, confidence[2], true);
  4079. if (resultCost[2] > 0)
  4080. if (resultCost[2] < resultCost[bestBest])
  4081. bestBest = 2;
  4082. maxPos[0] = targetPos[0];
  4083. maxPos[1] = targetPos[1] - 10;
  4084. result[3] = calcBestGoalFromTarget(targetPos, maxPos, 0.0, pilot->getSituationFireRange(), bestCell[3]);
  4085. if (result[3])
  4086. resultCost[3] = GlobalMoveMap[moveLevel]->getPathCost(GlobalMoveMap[moveLevel]->calcArea(cellPositionRow, cellPositionCol),GlobalMoveMap[moveLevel]->calcArea(bestCell[3][0], bestCell[3][1]), false, confidence[3], true);
  4087. if (resultCost[3] > 0)
  4088. if (resultCost[3] < resultCost[bestBest])
  4089. bestBest = 3;
  4090. if (resultCost[bestBest] > 0) {
  4091. curGoalCell[0] = bestCell[bestBest][0];
  4092. curGoalCell[1] = bestCell[bestBest][1];
  4093. }
  4094. if (DebugGameObject[0] == this) {
  4095. if (result[0])
  4096. GameMap->setCellDebug(bestCell[0][0], bestCell[0][1], 3, 2);
  4097. if (result[1])
  4098. GameMap->setCellDebug(bestCell[1][0], bestCell[1][1], 3, 2);
  4099. if (result[2])
  4100. GameMap->setCellDebug(bestCell[2][0], bestCell[2][1], 3, 2);
  4101. if (result[3])
  4102. GameMap->setCellDebug(bestCell[3][0], bestCell[3][1], 3, 2);
  4103. }
  4104. }
  4105. }
  4106. GameMap->setCellDebug(curGoalCell[0], curGoalCell[1], 3, 2);
  4107. #ifdef LAB_ONLY
  4108. MCTimeCalcGoal6Update += (GetCycles() - startTime);
  4109. #endif
  4110. //--------------------------------------------
  4111. // Let's calc the woorld coord of this cell...
  4112. land->cellToWorld(curGoalCell[0], curGoalCell[1], newGoal);
  4113. newGoal = calcOffsetMoveGoal(newGoal);
  4114. //float distToGoal = distance_from(newGoal, goal) * metersPerWorldUnit;
  4115. return(NO_ERR);
  4116. }
  4117. //---------------------------------------------------------------------------
  4118. long Mover::calcMovePath (MovePathPtr path,
  4119. long pathType,
  4120. Stuff::Vector3D start,
  4121. Stuff::Vector3D goal,
  4122. long* goalCell,
  4123. unsigned long moveParams) {
  4124. //-------------------------------------------------------------------------
  4125. // This assumes the goal is already the "optimum" goal (it should have been
  4126. // passed thru "calcMoveGoal" before being sent here)...
  4127. if (!PathFindMap[SECTOR_PATHMAP] || !PathFindMap[SIMPLE_PATHMAP])
  4128. Fatal(0, " No PathFindMap in Mover::calcMovePath ");
  4129. long posCellR, posCellC;
  4130. land->worldToCell(start, posCellR, posCellC);
  4131. long goalCellR, goalCellC;
  4132. land->worldToCell(goal, goalCellR, goalCellC);
  4133. path->clear();
  4134. long result = 0;
  4135. if (pathType == MOVEPATH_SIMPLE) {
  4136. long mapULr = posCellR - SimpleMovePathRange;
  4137. if (mapULr < 0)
  4138. mapULr = 0;
  4139. long mapULc = posCellC - SimpleMovePathRange;
  4140. if (mapULc < 0)
  4141. mapULc = 0;
  4142. float cellLength = (Terrain::worldUnitsPerCell * metersPerWorldUnit);
  4143. long clearCost = 0;
  4144. if (maxMoveSpeed != 0.0)
  4145. clearCost = (float2short)(cellLength / maxMoveSpeed * 50.0);
  4146. if (clearCost > 0) {
  4147. long jumpCost = 0;
  4148. long numOffsets = 8;
  4149. if (!pilot->onHomeTeam() && !MPlayer)
  4150. getJumpRange(&numOffsets, &jumpCost);
  4151. #ifdef USE_ELEMENTALS
  4152. if (getObjectClass() == ELEMENTAL) {
  4153. GameObjectPtr target = pilot->getLastTarget();
  4154. if (target && (distanceFrom(target->getPosition()) < ElementalTargetNoJumpDistance)) {
  4155. jumpCost = 0;
  4156. numOffsets = 8;
  4157. }
  4158. else
  4159. JumpOnBlocked = true;
  4160. }
  4161. #endif
  4162. if (isMineSweeper())
  4163. moveParams |= MOVEPARAM_SWEEP_MINES;
  4164. if (followRoads)
  4165. moveParams |= MOVEPARAM_FOLLOW_ROADS;
  4166. if (isMech())
  4167. moveParams |= MOVEPARAM_WATER_SHALLOW;
  4168. if (moveLevel == 1)
  4169. moveParams |= (MOVEPARAM_WATER_SHALLOW + MOVEPARAM_WATER_DEEP);
  4170. PathFindMap[SIMPLE_PATHMAP]->setMover(getWatchID(), getTeamId(), isLayingMines());
  4171. PathFindMap[SIMPLE_PATHMAP]->setUp(mapULr,
  4172. mapULc,
  4173. SimpleMovePathRange * 2 + 1,
  4174. SimpleMovePathRange * 2 + 1,
  4175. moveLevel,
  4176. &start,
  4177. posCellR,
  4178. posCellC,
  4179. goal,
  4180. goalCellR - mapULr,
  4181. goalCellC - mapULc,
  4182. clearCost,
  4183. jumpCost,
  4184. numOffsets,
  4185. moveParams);
  4186. //---------------------
  4187. // Set up debug info...
  4188. DebugMovePathType = pathType;
  4189. long goalCell[2];
  4190. if (numOffsets > 8)
  4191. result = PathFindMap[SIMPLE_PATHMAP]->calcPathJUMP(path, NULL, goalCell);
  4192. else
  4193. result = PathFindMap[SIMPLE_PATHMAP]->calcPath(path, NULL, goalCell);
  4194. PathFindMap[SIMPLE_PATHMAP]->setMover(0);
  4195. JumpOnBlocked = false;
  4196. }
  4197. }
  4198. else {
  4199. //--------------------------------------------------------------------------------------
  4200. // We are now assuming all local pathfinding will fit within two adjacent sectors.
  4201. // So, we can assume both the start and goal locations are within the two adj sectors...
  4202. float cellLength = (Terrain::worldUnitsPerCell * metersPerWorldUnit);
  4203. long clearCost = 0;
  4204. if (maxMoveSpeed != 0.0)
  4205. clearCost = (long)(cellLength / maxMoveSpeed * 50.0);
  4206. if (clearCost > 0) {
  4207. long jumpCost = 0;
  4208. long numOffsets = 8;
  4209. if (!pilot->onHomeTeam() && !MPlayer)
  4210. getJumpRange(&numOffsets, &jumpCost);
  4211. long startSector[2];
  4212. startSector[0] = posCellR / SECTOR_DIM;
  4213. startSector[1] = posCellC / SECTOR_DIM;
  4214. long goalSector[2];
  4215. goalSector[0] = goalCellR / SECTOR_DIM;
  4216. goalSector[1] = goalCellC / SECTOR_DIM;
  4217. long sectorULr = startSector[0] * SECTOR_DIM;
  4218. if (startSector[0] > goalSector[0])
  4219. sectorULr = goalSector[0] * SECTOR_DIM;
  4220. long sectorULc = startSector[1] * SECTOR_DIM;
  4221. if (startSector[1] > goalSector[1])
  4222. sectorULc = goalSector[1] * SECTOR_DIM;
  4223. #ifdef USE_ELEMENTALS
  4224. if (getObjectClass() == ELEMENTAL) {
  4225. GameObjectPtr target = pilot->getLastTarget();
  4226. if (target && (distanceFrom(target->getPosition()) < ElementalTargetNoJumpDistance)) {
  4227. jumpCost = 0;
  4228. numOffsets = 8;
  4229. }
  4230. else
  4231. JumpOnBlocked = true;
  4232. }
  4233. #endif
  4234. if (isMineSweeper())
  4235. moveParams |= MOVEPARAM_SWEEP_MINES;
  4236. if (followRoads)
  4237. moveParams |= MOVEPARAM_FOLLOW_ROADS;
  4238. if (isMech())
  4239. moveParams |= MOVEPARAM_WATER_SHALLOW;
  4240. if (moveLevel == 1)
  4241. moveParams |= (MOVEPARAM_WATER_SHALLOW + MOVEPARAM_WATER_DEEP);
  4242. if (moveParams & MOVEPARAM_JUMP)
  4243. moveParams |= 0;
  4244. PathFindMap[SECTOR_PATHMAP]->setMover(getWatchID(), getTeamId(), isLayingMines());
  4245. PathFindMap[SECTOR_PATHMAP]->setUp(sectorULr,
  4246. sectorULc,
  4247. SECTOR_DIM * 2,
  4248. SECTOR_DIM * 2,
  4249. moveLevel,
  4250. &start,
  4251. posCellR,
  4252. posCellC,
  4253. goal,
  4254. goalCellR - sectorULr,
  4255. goalCellC - sectorULc,
  4256. clearCost,
  4257. jumpCost,
  4258. numOffsets,
  4259. moveParams);
  4260. //---------------------
  4261. // Set up debug info...
  4262. DebugMovePathType = pathType;
  4263. if (numOffsets > 8)
  4264. result = PathFindMap[SECTOR_PATHMAP]->calcPathJUMP(path, NULL, goalCell);
  4265. else
  4266. result = PathFindMap[SECTOR_PATHMAP]->calcPath(path, NULL, goalCell);
  4267. PathFindMap[SECTOR_PATHMAP]->setMover(0);
  4268. JumpOnBlocked = false;
  4269. }
  4270. }
  4271. #if 0
  4272. File* pathDebugFile = new File;
  4273. pathDebugFile->create("movemap1.dbg");
  4274. PathFindMap->writeDebug(pathDebugFile);
  4275. pathDebugFile->close();
  4276. delete pathDebugFile;
  4277. pathDebugFile = NULL;
  4278. #endif
  4279. return(result);
  4280. }
  4281. //---------------------------------------------------------------------------
  4282. long Mover::calcEscapePath (MovePathPtr path,
  4283. Stuff::Vector3D start,
  4284. Stuff::Vector3D goal,
  4285. long* goalCell,
  4286. unsigned long moveParams,
  4287. Stuff::Vector3D& escapeGoal) {
  4288. //------------------------------------------
  4289. // If nothing else, clear the escape goal...
  4290. escapeGoal.x = -999999.0;
  4291. escapeGoal.y = -999999.0;
  4292. escapeGoal.z = -999999.0;
  4293. if (!PathFindMap[SECTOR_PATHMAP] || !PathFindMap[SIMPLE_PATHMAP])
  4294. Fatal(0, " No PathFindMap in Mover::calcMovePath ");
  4295. long posCellR, posCellC;
  4296. land->worldToCell(start, posCellR, posCellC);
  4297. long goalCellR, goalCellC;
  4298. land->worldToCell(goal, goalCellR, goalCellC);
  4299. path->clear();
  4300. long result = 0;
  4301. long mapULr = posCellR - SimpleMovePathRange;
  4302. if (mapULr < 0)
  4303. mapULr = 0;
  4304. long mapULc = posCellC - SimpleMovePathRange;
  4305. if (mapULc < 0)
  4306. mapULc = 0;
  4307. float cellLength = (Terrain::worldUnitsPerCell * metersPerWorldUnit);
  4308. long clearCost = 0;
  4309. if (maxMoveSpeed != 0.0)
  4310. clearCost = (float2short)(cellLength / maxMoveSpeed * 50.0);
  4311. if (clearCost > 0) {
  4312. long jumpCost = 0;
  4313. long numOffsets = 8;
  4314. if (!pilot->onHomeTeam() && !MPlayer)
  4315. getJumpRange(&numOffsets, &jumpCost);
  4316. #ifdef USE_ELEMENTALS
  4317. if (getObjectClass() == ELEMENTAL) {
  4318. GameObjectPtr target = pilot->getLastTarget();
  4319. if (target && (distanceFrom(target->getPosition()) < ElementalTargetNoJumpDistance)) {
  4320. jumpCost = 0;
  4321. numOffsets = 8;
  4322. }
  4323. else
  4324. JumpOnBlocked = true;
  4325. }
  4326. #endif
  4327. if (isMineSweeper())
  4328. moveParams |= MOVEPARAM_SWEEP_MINES;
  4329. if (followRoads)
  4330. moveParams |= MOVEPARAM_FOLLOW_ROADS;
  4331. FindingEscapePath = true;
  4332. PathFindMap[SIMPLE_PATHMAP]->setMover(getWatchID(), getTeamId(), isLayingMines());
  4333. PathFindMap[SIMPLE_PATHMAP]->setUp(mapULr,
  4334. mapULc,
  4335. SimpleMovePathRange * 2 + 1,
  4336. SimpleMovePathRange * 2 + 1,
  4337. moveLevel,
  4338. &start,
  4339. posCellR,
  4340. posCellC,
  4341. goal,
  4342. goalCellR - mapULr,
  4343. goalCellC - mapULc,
  4344. clearCost,
  4345. jumpCost,
  4346. numOffsets,
  4347. moveParams);
  4348. //---------------------
  4349. // Set up debug info...
  4350. DebugMovePathType = 0;
  4351. long goalCell[2];
  4352. result = PathFindMap[SIMPLE_PATHMAP]->calcEscapePath(path, &escapeGoal, goalCell);
  4353. JumpOnBlocked = false;
  4354. FindingEscapePath = false;
  4355. }
  4356. #if 0
  4357. File* pathDebugFile = new File;
  4358. pathDebugFile->create("movemap1.dbg");
  4359. PathFindMap->writeDebug(pathDebugFile);
  4360. pathDebugFile->close();
  4361. delete pathDebugFile;
  4362. pathDebugFile = NULL;
  4363. #endif
  4364. return(result);
  4365. }
  4366. //---------------------------------------------------------------------------
  4367. bool Mover::getAdjacentCellPathLocked (long level, long cellRow, long cellCol, long dir) {
  4368. static long adjCellTable[8][2] = {
  4369. {-1, 0},
  4370. {-1, 1},
  4371. {0, 1},
  4372. {1, 1},
  4373. {1, 0},
  4374. {1, -1},
  4375. {0, -1},
  4376. {-1, -1}
  4377. };
  4378. long adjCellRow = cellRow + adjCellTable[dir][0];
  4379. long adjCellCol = cellCol + adjCellTable[dir][1];
  4380. if (level < 0)
  4381. return(false);
  4382. if (level > (NUM_MOVE_LEVELS - 1))
  4383. return(false);
  4384. return(GameMap->getPathlock(level, adjCellRow, adjCellCol));
  4385. }
  4386. //---------------------------------------------------------------------------
  4387. bool Mover::getPathRangeLock (long range, bool* reachedEnd) {
  4388. MovePathPtr path = pilot->getMovePath();
  4389. if (path)
  4390. return(path->isLocked((moveLevel == 2), -1, range, reachedEnd));
  4391. return(false);
  4392. }
  4393. //---------------------------------------------------------------------------
  4394. long Mover::setPathRangeLock (bool set, long range) {
  4395. MovePathPtr path = pilot->getMovePath();
  4396. long lockLevel = (moveLevel == 2);
  4397. if (set) {
  4398. if (pathLockLength > 0)
  4399. setPathRangeLock(false);
  4400. if (path && (path->numSteps > 0)) {
  4401. long start = path->curStep;
  4402. long lastStep = start + range;
  4403. if (lastStep >= path->numStepsWhenNotPaused)
  4404. lastStep = path->numStepsWhenNotPaused;
  4405. pathLockLength = 0;
  4406. for (long i = start; i < lastStep; i++) {
  4407. if (GameMap->getPathlock(lockLevel, path->stepList[i].cell[0], path->stepList[i].cell[1]))
  4408. return(-1);
  4409. GameMap->setPathlock((moveLevel == 2), path->stepList[i].cell[0], path->stepList[i].cell[1], true);
  4410. pathLockList[pathLockLength][0] = path->stepList[i].cell[0];
  4411. pathLockList[pathLockLength][1] = path->stepList[i].cell[1];
  4412. pathLockLength++;
  4413. }
  4414. }
  4415. }
  4416. else {
  4417. for (long i = 0; i < pathLockLength; i++)
  4418. GameMap->setPathlock(lockLevel, pathLockList[i][0], pathLockList[i][1], false);
  4419. pathLockLength = 0;
  4420. }
  4421. return(NO_ERR);
  4422. }
  4423. //---------------------------------------------------------------------------
  4424. void Mover::updatePathLock (bool set)
  4425. {
  4426. if (getObjectClass() == BATTLEMECH)
  4427. if (((BattleMechPtr)this)->inJump)
  4428. return;
  4429. //For movers which can be stepped on.
  4430. if (!pathLocks)
  4431. return;
  4432. //--------------------------------
  4433. // First, set the cell we're in...
  4434. GameMap->setPathlock((moveLevel == 2), cellPositionRow, cellPositionCol, set);
  4435. //------------------------------------------------------------------------------------------
  4436. // If we're yielding, we should NOT set our path range on. If we are yielding, we can always
  4437. // remove what we've already pathlocked...
  4438. if (!set || !pilot->isYielding())
  4439. setPathRangeLock(set, crashBlockPath);
  4440. }
  4441. //---------------------------------------------------------------------------
  4442. bool Mover::getPathRangeBlocked (long range, bool* reachedEnd) {
  4443. if (moveLevel > 0)
  4444. return(false);
  4445. MovePathPtr path = pilot->getMovePath();
  4446. if (path)
  4447. return(path->isBlocked(-1, range, reachedEnd));
  4448. return(false);
  4449. }
  4450. //---------------------------------------------------------------------------
  4451. void Mover::updateHustleTime (void) {
  4452. #if 0 //Redo when bridges come into play.
  4453. long overlay = GameMap->getOverlay(cellPositionRow, cellPositionCol);
  4454. //---------------------------------------------------------------------------------
  4455. // To avoid blocking movement on bridges, we'll keep track of a "hustle" time
  4456. // for each mover. This will keep us from blocking bridges (and, in the future,
  4457. // any other weird little predicaments) for our buddies. If we're on a bridge (or
  4458. // we're recently on a bridge), our hustle flag will get set so we move it (even if
  4459. // we're in a group move) and don't clog up the tight path...
  4460. switch (overlay) {
  4461. case OVERLAY_WATER_BRIDGE_NS:
  4462. case OVERLAY_WATER_BRIDGE_NS_DESTROYED:
  4463. case OVERLAY_WATER_BRIDGE_EW:
  4464. case OVERLAY_WATER_BRIDGE_EW_DESTROYED:
  4465. case OVERLAY_RAILROAD_WATER_BRIDGE_NS:
  4466. case OVERLAY_RAILROAD_WATER_BRIDGE_NS_DESTROYED:
  4467. case OVERLAY_RAILROAD_WATER_BRIDGE_EW:
  4468. case OVERLAY_RAILROAD_WATER_BRIDGE_EW_DESTROYED:
  4469. lastHustleTime = scenarioTime;
  4470. break;
  4471. }
  4472. #endif
  4473. }
  4474. //---------------------------------------------------------------------------
  4475. long Mover::bounceToAdjCell (void) {
  4476. //--------------------------------------------------------------------
  4477. // Bounces mover to an adjacent open cell. Convenient for collisions:)
  4478. for (long dir = 0; dir < 8; dir++) {
  4479. static long adjCellTable[8][2] = {
  4480. {-1, 0},
  4481. {-1, 1},
  4482. {0, 1},
  4483. {1, 1},
  4484. {1, 0},
  4485. {1, -1},
  4486. {0, -1},
  4487. {-1, -1}
  4488. };
  4489. long adjRow = cellPositionRow + adjCellTable[dir][0];
  4490. long adjCol = cellPositionCol + adjCellTable[dir][1];
  4491. bool cellPathLocked = GameMap->getPathlock((moveLevel == 2), adjRow, adjCol);
  4492. bool cellPassable = GameMap->getPassable(adjRow, adjCol);
  4493. if (!cellPathLocked && cellPassable) {
  4494. //----------------------------------------
  4495. // This is open. Let's set our position...
  4496. bool pathLockMarked = GameMap->getPathlock((moveLevel == 2), cellPositionRow, cellPositionCol);
  4497. if (pathLockMarked)
  4498. updatePathLock(false);
  4499. Stuff::Vector3D newPosition;
  4500. land->cellToWorld(adjRow, adjCol, newPosition);
  4501. setPosition(newPosition);
  4502. pilot->pausePath();
  4503. if (pathLockMarked)
  4504. updatePathLock(true);
  4505. return(dir);
  4506. }
  4507. }
  4508. return(-1);
  4509. }
  4510. //---------------------------------------------------------------------------
  4511. long Mover::calcMovePath (MovePathPtr path,
  4512. Stuff::Vector3D start,
  4513. long thruArea[2],
  4514. long goalDoor,
  4515. Stuff::Vector3D finalGoal,
  4516. Stuff::Vector3D* goal,
  4517. long* goalCell,
  4518. unsigned long moveParams) {
  4519. //-------------------------------------------------------------------------
  4520. // This assumes the goal is already the "optimum" goal (it should have been
  4521. // passed thru "calcMoveGoal" before being sent here)...
  4522. if (!PathFindMap[SECTOR_PATHMAP] || !PathFindMap[SIMPLE_PATHMAP])
  4523. Fatal(0, " No PathFindMap in Mover::calcMovePath ");
  4524. //---------------------------------------------------------------------------------
  4525. // We are now assuming all local pathfinding will fit within one sector. So, we can
  4526. // assume both the start and goal locations are within the sector...
  4527. long result = 0;
  4528. path->clear();
  4529. float cellLength = (Terrain::worldUnitsPerCell * metersPerWorldUnit);
  4530. long clearCost = 0;
  4531. if (maxMoveSpeed != 0)
  4532. clearCost = (long)(cellLength / maxMoveSpeed * 50.0);
  4533. if (clearCost > 0) {
  4534. long jumpCost = 0;
  4535. long numOffsets = 8;
  4536. if (!pilot->onHomeTeam() && !MPlayer)
  4537. getJumpRange(&numOffsets, &jumpCost);
  4538. long posCellR, posCellC;
  4539. land->worldToCell(start, posCellR, posCellC);
  4540. #ifdef USE_ELEMENTALS
  4541. if (getObjectClass() == ELEMENTAL) {
  4542. GameObjectPtr target = pilot->getLastTarget();
  4543. if (target && (distanceFrom(target->getPosition()) < ElementalTargetNoJumpDistance)) {
  4544. jumpCost = 0;
  4545. numOffsets = 8;
  4546. }
  4547. else
  4548. JumpOnBlocked = true;
  4549. }
  4550. #endif
  4551. if (isMineSweeper())
  4552. moveParams |= MOVEPARAM_SWEEP_MINES;
  4553. if (followRoads)
  4554. moveParams |= MOVEPARAM_FOLLOW_ROADS;
  4555. if (isMech())
  4556. moveParams |= MOVEPARAM_WATER_SHALLOW;
  4557. if (moveLevel == 1)
  4558. moveParams |= (MOVEPARAM_WATER_SHALLOW + MOVEPARAM_WATER_DEEP);
  4559. PathFindMap[SECTOR_PATHMAP]->setMover(getWatchID(), getTeamId(), isLayingMines());
  4560. result = PathFindMap[SECTOR_PATHMAP]->setUp(
  4561. moveLevel,
  4562. &start,
  4563. posCellR,
  4564. posCellC,
  4565. thruArea,
  4566. goalDoor,
  4567. finalGoal,
  4568. clearCost,
  4569. jumpCost,
  4570. numOffsets,
  4571. moveParams);
  4572. if (result == -1) {
  4573. //-------------------------------------------------------
  4574. // Goal door is blocked. Can't get thru, at the moment...
  4575. JumpOnBlocked = false;
  4576. return(-999);
  4577. }
  4578. if (numOffsets > 8)
  4579. result = PathFindMap[SECTOR_PATHMAP]->calcPathJUMP(path, goal, goalCell);
  4580. else
  4581. result = PathFindMap[SECTOR_PATHMAP]->calcPath(path, goal, goalCell);
  4582. //if ((goalCell[0] == -1) || (goalCell[1] == -1))
  4583. // STOP(("Mover.calcMovePath: bad goal cell--get GLENN!"));
  4584. PathFindMap[SECTOR_PATHMAP]->setMover(0);
  4585. JumpOnBlocked = false;
  4586. }
  4587. #if 0
  4588. File* pathDebugFile = new File;
  4589. pathDebugFile->create("movemap1.dbg");
  4590. PathFindMap->writeDebug(pathDebugFile);
  4591. pathDebugFile->close();
  4592. delete pathDebugFile;
  4593. pathDebugFile = NULL;
  4594. #endif
  4595. return(result);
  4596. }
  4597. //---------------------------------------------------------------------------
  4598. long Mover::getContacts (long* contactList, long contactCriteria, long sortType) {
  4599. if (sensorSystem)
  4600. return(sensorSystem->getTeamContacts(contactList, contactCriteria, sortType));
  4601. return(0);
  4602. }
  4603. //------------------------------------------------------------------------------------------
  4604. long Mover::getContactStatus (long scanningTeamID, bool includingAllies) {
  4605. if (getFlag(OBJECT_FLAG_REMOVED))
  4606. return(CONTACT_NONE);
  4607. return(getContactInfo()->getContactStatus(scanningTeamID, true));
  4608. }
  4609. //------------------------------------------------------------------------------------------
  4610. float Mover::weaponLocked (long weaponIndex, Stuff::Vector3D targetPosition) {
  4611. long bodyLocation = inventory[weaponIndex].bodyLocation;
  4612. return(relFacingTo(targetPosition, bodyLocation));
  4613. }
  4614. //------------------------------------------------------------------------------------------
  4615. bool Mover::weaponInRange (long weaponIndex, float metersToTarget, float buffer) {
  4616. MasterComponentPtr weapon = &MasterComponent::masterList[inventory[weaponIndex].masterID];
  4617. float minRange = WeaponRanges[weapon->getWeaponRange()][0] - buffer;
  4618. float maxRange = WeaponRanges[weapon->getWeaponRange()][1] + buffer;
  4619. if (metersToTarget < minRange)
  4620. return(false);
  4621. if (metersToTarget > maxRange)
  4622. return(false);
  4623. return(true);
  4624. }
  4625. //------------------------------------------------------------------------------------------
  4626. long Mover::getWeaponsReady (long* list, long listSize) {
  4627. //-----------------------------------------
  4628. // Make sure list is >= the number of ready
  4629. // weapons!
  4630. long numReady = 0;
  4631. if (listSize == -1)
  4632. for (long item = numOther; item < (numOther + numWeapons); item++) {
  4633. if (isWeaponReady(item)) {
  4634. if (list)
  4635. list[numReady++] = item;
  4636. else
  4637. numReady++;
  4638. }
  4639. }
  4640. else
  4641. for (long item = 0; item < listSize; item++) {
  4642. if (isWeaponReady(list[item])) {
  4643. if (list)
  4644. list[numReady++] = list[item];
  4645. else
  4646. numReady++;
  4647. }
  4648. }
  4649. return(numReady);
  4650. }
  4651. //------------------------------------------------------------------------------------------
  4652. long Mover::getWeaponsLocked (long* list, long listSize) {
  4653. //------------------------------------------
  4654. // Make sure list is >= the number of locked
  4655. // weapons!
  4656. GameObjectPtr target = pilot->getCurrentTarget();
  4657. if (!target)
  4658. return(-2);
  4659. Stuff::Vector3D targetPosition = target->getPosition();
  4660. long numLocked = 0;
  4661. float fireArc = getFireArc();
  4662. if (listSize == -1)
  4663. for (long item = numOther; item < (numOther + numWeapons); item++) {
  4664. float relAngle = weaponLocked(item, targetPosition);
  4665. if ((relAngle >= -fireArc) && (relAngle <= fireArc))
  4666. list[numLocked++] = item;
  4667. }
  4668. else
  4669. for (long item = 0; item < listSize; item++) {
  4670. float relAngle = weaponLocked(list[item], targetPosition);
  4671. if ((relAngle >= -fireArc) && (relAngle <= fireArc))
  4672. list[numLocked++] = list[item];
  4673. }
  4674. return(numLocked);
  4675. }
  4676. //------------------------------------------------------------------------------------------
  4677. long Mover::getWeaponsInRange (long* list, long listSize, float fireRangeOrder) {
  4678. //------------------------------------------
  4679. // Make sure list is >= the number of locked
  4680. // weapons!
  4681. GameObjectPtr target = pilot->getCurrentTarget();
  4682. if (!target)
  4683. return(-2);
  4684. float rangeToTarget = distanceFrom(target->getPosition());
  4685. //------------------------------------------------
  4686. // For now, we always obey the fire range order...
  4687. #ifdef STUPID_RULES
  4688. if (rangeToTarget > fireRangeOrder)
  4689. return(-3);
  4690. #endif
  4691. long numInRange = 0;
  4692. if (listSize == -1) {
  4693. for (long item = numOther; item < (numOther + numWeapons); item++)
  4694. if (weaponInRange(item, rangeToTarget, MapCellDiagonal))
  4695. list[numInRange++] = item;
  4696. }
  4697. else {
  4698. for (long item = 0; item < listSize; item++)
  4699. if (weaponInRange(list[item], rangeToTarget, MapCellDiagonal))
  4700. list[numInRange++] = list[item];
  4701. }
  4702. return(numInRange);
  4703. }
  4704. //------------------------------------------------------------------------------------------
  4705. long Mover::getWeaponShots (long weaponIndex) {
  4706. if (!isWeaponIndex(weaponIndex))
  4707. return(-1);
  4708. //--------------------------------------------------------------------
  4709. // All ammo, as listed in the Master Component Table, is a fixed index
  4710. // greater than the weapon itself.
  4711. if (MasterComponent::masterList[inventory[weaponIndex].masterID].getWeaponAmmoType())
  4712. return(ammoTypeTotal[inventory[weaponIndex].ammoIndex].curAmount);
  4713. //--------------------------------------------------------------------------
  4714. // If no ammotype, then it has unlimited ammo (energy weapons, for example).
  4715. return(UNLIMITED_SHOTS);
  4716. }
  4717. //------------------------------------------------------------------------------------------
  4718. bool Mover::getWeaponIndirectFire (long weaponIndex)
  4719. {
  4720. if (!isWeaponIndex(weaponIndex))
  4721. return(false);
  4722. //--------------------------------------------------------------------
  4723. for (long i=0;i<20;i++)
  4724. {
  4725. if (IndirectFireWeapons[i] == inventory[weaponIndex].masterID)
  4726. return true;
  4727. }
  4728. //--------------------------------------------------------------------------
  4729. // If no ammotype, then it has unlimited ammo (energy weapons, for example).
  4730. return(false);
  4731. }
  4732. //------------------------------------------------------------------------------------------
  4733. bool Mover::getWeaponAreaEffect (long weaponIndex)
  4734. {
  4735. if (!isWeaponIndex(weaponIndex))
  4736. return(false);
  4737. //--------------------------------------------------------------------
  4738. for (long i=0;i<20;i++)
  4739. {
  4740. if (AreaEffectWeapons[i] == inventory[weaponIndex].masterID)
  4741. return true;
  4742. }
  4743. //--------------------------------------------------------------------------
  4744. // If no ammotype, then it has unlimited ammo (energy weapons, for example).
  4745. return(false);
  4746. }
  4747. //------------------------------------------------------------------------------------------
  4748. float Mover::getWeaponAmmoLevel (long weaponIndex) {
  4749. if (!isWeaponIndex(weaponIndex))
  4750. return(-1.0);
  4751. return((float)(ammoTypeTotal[inventory[weaponIndex].ammoIndex].curAmount) / (float)(ammoTypeTotal[inventory[weaponIndex].ammoIndex].maxAmount));
  4752. }
  4753. bool Mover::getWeaponIsEnergy( long weaponIndex )
  4754. {
  4755. return MasterComponent::masterList[inventory[weaponIndex].masterID].getForm() == COMPONENT_FORM_WEAPON_ENERGY;
  4756. }
  4757. //------------------------------------------------------------------------------------------
  4758. void Mover::calcWeaponEffectiveness (bool setMax) {
  4759. long effectiveness = 0;
  4760. float avgSkill;
  4761. //----------------------------------
  4762. // Record time of our latest calc...
  4763. lastWeaponEffectivenessCalc = scenarioTime;
  4764. if (pilot)
  4765. avgSkill = (float)pilot->getSkill(MWS_GUNNERY) / 50.0;
  4766. else
  4767. avgSkill = 1.0;
  4768. //Ammo is NOT part of weapon effectiveness anymore
  4769. // Per Mike Lee
  4770. // -fs
  4771. for (long curWeapon = numOther; curWeapon < (numOther + numWeapons); curWeapon++) {
  4772. if (setMax || (!inventory[curWeapon].disabled ))
  4773. effectiveness += inventory[curWeapon].effectiveness * avgSkill;
  4774. }
  4775. if (setMax)
  4776. maxWeaponEffectiveness = effectiveness;
  4777. else
  4778. {
  4779. weaponEffectiveness = effectiveness;
  4780. if (weaponEffectiveness == 0)
  4781. playMessage(RADIO_WEAPONS_OUT);
  4782. }
  4783. }
  4784. //---------------------------------------------------------------------------
  4785. void Mover::calcAmmoTotals (void)
  4786. {
  4787. AmmoTally totalList[100];
  4788. numAmmoTypes = 0;
  4789. if (numWeapons > 0)
  4790. {
  4791. //-----------------------------------------------------------
  4792. // We have some ammo in our inventory, so let's count 'em up.
  4793. // First, make a list of all weapon types we have...
  4794. for (long i = numOther; i < (numOther + numWeapons); i++)
  4795. {
  4796. long weaponMasterId = inventory[i].masterID;
  4797. bool newAmmoType = true;
  4798. for (long curAmmo = 0; curAmmo < (long)numAmmoTypes; curAmmo++)
  4799. {
  4800. if (totalList[curAmmo].masterId == (long)MasterComponent::masterList[weaponMasterId].getWeaponAmmoMasterId())
  4801. {
  4802. newAmmoType = false;
  4803. break;
  4804. }
  4805. }
  4806. if (newAmmoType)
  4807. {
  4808. totalList[numAmmoTypes].masterId = MasterComponent::masterList[weaponMasterId].getWeaponAmmoMasterId();
  4809. if (MasterComponent::masterList[weaponMasterId].getWeaponAmmoType())
  4810. {
  4811. totalList[numAmmoTypes].curAmount = 0;
  4812. totalList[numAmmoTypes].maxAmount = 0;
  4813. }
  4814. else
  4815. {
  4816. totalList[numAmmoTypes].curAmount = UNLIMITED_SHOTS;
  4817. totalList[numAmmoTypes].maxAmount = UNLIMITED_SHOTS;
  4818. }
  4819. numAmmoTypes++;
  4820. }
  4821. }
  4822. //--------------------------------------------------
  4823. // Now, go through all ammo we have and tally 'em...
  4824. for (i = numOther + numWeapons; i < (numOther + numWeapons + numAmmos); i++)
  4825. {
  4826. long ammoMasterId = inventory[i].masterID;
  4827. bool foundAmmoWeapon = false;
  4828. for (long curAmmo = 0; curAmmo < numAmmoTypes; curAmmo++)
  4829. {
  4830. if (totalList[curAmmo].masterId == ammoMasterId)
  4831. {
  4832. foundAmmoWeapon = true;
  4833. if (useUnlimitedAmmo)
  4834. {
  4835. totalList[curAmmo].curAmount = UNLIMITED_SHOTS;
  4836. totalList[curAmmo].maxAmount = UNLIMITED_SHOTS;
  4837. break;
  4838. }
  4839. else
  4840. {
  4841. totalList[curAmmo].curAmount += inventory[i].amount;
  4842. totalList[curAmmo].maxAmount += inventory[i].amount;
  4843. break;
  4844. }
  4845. }
  4846. }
  4847. if (!foundAmmoWeapon)
  4848. STOP(("Mech %s has ammo for a weapon it is not carrying! Ammo Index is %d",name,ammoMasterId));
  4849. }
  4850. //---------------------------------------------------------------------------
  4851. // Since we now know the totals, create the actual total list for this mover...
  4852. memcpy(ammoTypeTotal, totalList, sizeof(AmmoTally) * numAmmoTypes);
  4853. }
  4854. }
  4855. //------------------------------------------------------------------------------------------
  4856. long Mover::calcFireRanges (void) {
  4857. lastOptimalRangeCalc = scenarioTime;
  4858. //---------------------------
  4859. // Calc min and max ranges...
  4860. maxRange = 0;
  4861. minRange = 1000000.0;
  4862. numFunctionalWeapons = 0;
  4863. for (long curWeapon = numOther; curWeapon < (numOther + numWeapons); curWeapon++) {
  4864. if (!inventory[curWeapon].disabled && (getWeaponShots(curWeapon) > 0)) {
  4865. long range = (long)MasterComponent::masterList[inventory[curWeapon].masterID].getWeaponRange();
  4866. float minWeaponRange = WeaponRanges[range][0];
  4867. float maxWeaponRange = WeaponRanges[range][1];
  4868. if (maxWeaponRange > maxRange)
  4869. maxRange = maxWeaponRange;
  4870. if (minWeaponRange < minRange)
  4871. minRange = minWeaponRange;
  4872. numFunctionalWeapons++;
  4873. }
  4874. }
  4875. //---------------------------
  4876. // Now, calc optimal range...
  4877. float rangeTotals[NUM_WEAPON_RANGE_TYPES] = {0, 0, 0, 0, 0};
  4878. for (curWeapon = numOther; curWeapon < (numOther + numWeapons); curWeapon++) {
  4879. MasterComponent* weapon = &MasterComponent::masterList[inventory[curWeapon].masterID];
  4880. if (!inventory[curWeapon].disabled && (getWeaponShots(curWeapon) > 0)) {
  4881. float damageTimeRating = weapon->getWeaponDamage() / weapon->getWeaponRecycleTime();
  4882. //---------------------------------
  4883. // Go thru the five range points...
  4884. for (long i = 0; i < 5; i++)
  4885. if (OptimalRangePointInRange[i][weapon->getWeaponRange()])
  4886. rangeTotals[i] += damageTimeRating;
  4887. }
  4888. }
  4889. float lastOptimalRange = optimalRange;
  4890. if (numFunctionalWeapons == 0) {
  4891. optimalRange = 0.0;
  4892. return(optimalRange != lastOptimalRange);
  4893. }
  4894. else {
  4895. sortList->clear();
  4896. for (long i = 0; i < NUM_WEAPON_RANGE_TYPES; i++) {
  4897. sortList->setId(i, i);
  4898. sortList->setValue(i, rangeTotals[i]);
  4899. }
  4900. sortList->sort();
  4901. if (sortList->getValue(0) <= 0.0) {
  4902. optimalRange = 0.0;
  4903. return(optimalRange != lastOptimalRange);
  4904. }
  4905. }
  4906. long optimalRangeType = sortList->getId(0);
  4907. if (sortList->getValue(0) == sortList->getValue(1)) {
  4908. //--------------------------------------------------------------
  4909. // A tie, so pick one based upon the pre-set range priorities...
  4910. unsigned char tieFlags = 0;
  4911. for (long i = 0; i < 5; i++)
  4912. if (sortList->getValue(i) == sortList->getValue(0))
  4913. tieFlags |= (1 << sortList->getId(i));
  4914. optimalRangeType = OptimalRangeTieTable[tieFlags];
  4915. if (optimalRangeType == 255) {
  4916. optimalRange = 0.0;
  4917. return(optimalRange != lastOptimalRange);
  4918. }
  4919. }
  4920. optimalRange = OptimalRangePoints[optimalRangeType];
  4921. return(optimalRange != lastOptimalRange);
  4922. }
  4923. //------------------------------------------------------------------------------------------
  4924. float Mover::getOrderedFireRange (long* attackRange) {
  4925. long fireRange = pilot->getCurTacOrder()->attackParams.range;
  4926. if (attackRange)
  4927. fireRange = *attackRange;
  4928. float orderedFireRange = -1.0;
  4929. switch (fireRange) {
  4930. case FIRERANGE_DEFAULT:
  4931. orderedFireRange = getOptimalFireRange();
  4932. break;
  4933. case FIRERANGE_RAMMING:
  4934. orderedFireRange = 0.0;
  4935. break;
  4936. case FIRERANGE_LONGEST:
  4937. orderedFireRange = getMaxFireRange();
  4938. break;
  4939. case FIRERANGE_OPTIMAL:
  4940. orderedFireRange = getOptimalFireRange();
  4941. break;
  4942. case FIRERANGE_SHORT:
  4943. orderedFireRange = (WeaponRanges[WEAPON_RANGE_SHORT][1] - WeaponRanges[WEAPON_RANGE_SHORT][0])/2.0;
  4944. break;
  4945. case FIRERANGE_MEDIUM:
  4946. orderedFireRange = (WeaponRanges[WEAPON_RANGE_MEDIUM][1] - WeaponRanges[WEAPON_RANGE_MEDIUM][0])/2.0;
  4947. break;
  4948. case FIRERANGE_LONG:
  4949. orderedFireRange = (WeaponRanges[WEAPON_RANGE_LONG][1] - WeaponRanges[WEAPON_RANGE_LONG][0])/2.0;
  4950. break;
  4951. }
  4952. return(orderedFireRange);
  4953. }
  4954. //------------------------------------------------------------------------------------------
  4955. float Mover::getMinFireRange (void) {
  4956. return(minRange);
  4957. }
  4958. //------------------------------------------------------------------------------------------
  4959. float Mover::getMaxFireRange (void) {
  4960. return(maxRange);
  4961. }
  4962. //------------------------------------------------------------------------------------------
  4963. float Mover::getOptimalFireRange (void) {
  4964. return(optimalRange);
  4965. }
  4966. //------------------------------------------------------------------------------------------
  4967. // COMBAT routines
  4968. //------------------------------------------------------------------------------------------
  4969. bool Mover::isWeaponIndex (long itemIndex) {
  4970. return((itemIndex >= numOther) && (itemIndex < (numOther + numWeapons)));
  4971. }
  4972. //---------------------------------------------------------------------------
  4973. bool Mover::isWeaponMissile (long weaponIndex) {
  4974. return(MasterComponent::masterList[inventory[weaponIndex].masterID].getForm() == COMPONENT_FORM_WEAPON_MISSILE);
  4975. }
  4976. //---------------------------------------------------------------------------
  4977. bool Mover::isWeaponReady (long weaponIndex) {
  4978. if (inventory[weaponIndex].disabled)
  4979. return(false);
  4980. else if (inventory[weaponIndex].readyTime > scenarioTime)
  4981. return(false);
  4982. return(true);
  4983. }
  4984. //------------------------------------------------------------------------------------------
  4985. void Mover::startWeaponRecycle (long weaponIndex) {
  4986. inventory[weaponIndex].readyTime = scenarioTime +
  4987. MasterComponent::masterList[inventory[weaponIndex].masterID].getWeaponRecycleTime();
  4988. }
  4989. //------------------------------------------------------------------------------------------
  4990. long Mover::tallyAmmo (long ammoMasterId) {
  4991. long tally = 0;
  4992. long firstAmmo = numOther + numWeapons;
  4993. for (long item = firstAmmo; item < firstAmmo + numAmmos; item++) {
  4994. if (inventory[item].masterID == ammoMasterId)
  4995. tally += inventory[item].amount;
  4996. }
  4997. return(tally);
  4998. }
  4999. //---------------------------------------------------------------------------
  5000. bool Mover::needsRefit(void) {
  5001. bool result = false;
  5002. if (!refitBuddyWID && getObjectClass() == BATTLEMECH)
  5003. {
  5004. for (long loc=0; loc<numArmorLocations; loc++)
  5005. {
  5006. if (loc<numBodyLocations && (loc == MECH_ARMOR_LOCATION_LARM || loc == MECH_ARMOR_LOCATION_RARM) &&
  5007. body[loc].damageState == IS_DAMAGE_DESTROYED)
  5008. continue;
  5009. if ((loc<numBodyLocations && body[loc].curInternalStructure < body[loc].maxInternalStructure) ||
  5010. (armor[loc].curArmor < armor[loc].maxArmor))
  5011. {
  5012. result = true;
  5013. break;
  5014. }
  5015. }
  5016. if (!result)
  5017. for (long ammo = 0; ammo < numAmmoTypes; ammo++)
  5018. if (ammoTypeTotal[ammo].curAmount < ammoTypeTotal[ammo].maxAmount)
  5019. {
  5020. result = true;
  5021. break;
  5022. }
  5023. }
  5024. return result;
  5025. }
  5026. //---------------------------------------------------------------------------
  5027. long Mover::reduceAmmo (long ammoMasterId, long amount) {
  5028. long amountUsed = amount;
  5029. //----------------------------------------------------------
  5030. // Deduct it from the first available ammo critical space...
  5031. long firstAmmo = numOther + numWeapons;
  5032. for (long item = firstAmmo; item < firstAmmo + numAmmos; item++) {
  5033. if (inventory[item].masterID == ammoMasterId)
  5034. if (inventory[item].amount > amount) {
  5035. inventory[item].amount -= amount;
  5036. break;
  5037. }
  5038. else {
  5039. amount -= inventory[item].amount;
  5040. inventory[item].amount = 0;
  5041. }
  5042. }
  5043. //-------------------------------------------------------------------
  5044. // Make sure the ammoTypeTotal list, in which we track total ammo for
  5045. // all of the ammo types this mover uses, is adjusted...
  5046. for (long ammo = 0; ammo < numAmmoTypes; ammo++)
  5047. if (ammoTypeTotal[ammo].masterId == ammoMasterId) {
  5048. ammoTypeTotal[ammo].curAmount -= amountUsed;
  5049. if (ammoTypeTotal[ammo].curAmount <= 0) {
  5050. //-----------------------------------------------------
  5051. // Ammo for this weapon type is spent, which may effect
  5052. // our weapon effectiveness and optimal range...
  5053. ammoTypeTotal[ammo].curAmount = 0;
  5054. calcWeaponEffectiveness(false);
  5055. calcFireRanges();
  5056. pilot->radioMessage(RADIO_AMMO_OUT);
  5057. }
  5058. break;
  5059. }
  5060. return(amountUsed);
  5061. }
  5062. //---------------------------------------------------------------------------
  5063. /*
  5064. long Mover::refillAmmo (long ammoMasterId, long amount) {
  5065. long amountUsed = amount;
  5066. //----------------------------------------------------------
  5067. // Deduct it from the first available ammo critical space...
  5068. long firstAmmo = numOther + numWeapons;
  5069. for (long item = firstAmmo; item < firstAmmo + numAmmos; item++) {
  5070. if (inventory[item].masterID == ammoMasterId)
  5071. if (inventory[item].amount > amount) {
  5072. inventory[item].amount -= amount;
  5073. break;
  5074. }
  5075. else {
  5076. amount -= inventory[item].amount;
  5077. inventory[item].amount = 0;
  5078. }
  5079. }
  5080. //-------------------------------------------------------------------
  5081. // Make sure the ammoTypeTotal list, in which we track total ammo for
  5082. // all of the ammo types this mover uses, is adjusted...
  5083. for (long ammo = 0; ammo < numAmmoTypes; ammo++)
  5084. if (ammoTypeTotal[ammo].masterId == ammoMasterId) {
  5085. ammoTypeTotal[ammo].curAmount -= amountUsed;
  5086. if (ammoTypeTotal[ammo].curAmount <= 0) {
  5087. //-----------------------------------------------------
  5088. // Ammo for this weapon type is spent, which may effect
  5089. // our weapon effectiveness and optimal range...
  5090. ammoTypeTotal[ammo].curAmount = 0;
  5091. calcLongestRangeWeapon();
  5092. calcWeaponEffectiveness(false);
  5093. calcOptimalRange(NULL);
  5094. }
  5095. break;
  5096. }
  5097. return(amountUsed);
  5098. }
  5099. */
  5100. //---------------------------------------------------------------------------
  5101. void Mover::deductWeaponShot (long weaponIndex, long ammoAmount) {
  5102. if (ammoAmount > 0)
  5103. reduceAmmo(MasterComponent::masterList[inventory[weaponIndex].masterID].getWeaponAmmoMasterId(), ammoAmount);
  5104. }
  5105. //---------------------------------------------------------------------------
  5106. long Mover::sortWeapons (long* weaponList, long* valueList, long listSize, long sortType, bool skillCheck) {
  5107. //------------------------------------------
  5108. // Make sure list is >= the number of locked
  5109. // weapons!
  5110. GameObjectPtr target = pilot->getCurrentTarget();
  5111. if (!target)
  5112. return(-2);
  5113. long aimLocation = -1;
  5114. if (pilot && pilot->getCurTacOrder()->isCombatOrder())
  5115. aimLocation = pilot->getCurTacOrder()->attackParams.aimLocation;
  5116. //----------------------------------------------------------------------
  5117. // BIG ASSUMPTION HERE: That a mech will not have more than 100 weapons.
  5118. sortList->clear();
  5119. if (listSize == -1) {
  5120. for (long item = numOther; item < (numOther + numWeapons); item++) {
  5121. sortList->setId(item - numOther, item);
  5122. switch (sortType) {
  5123. case WEAPONSORT_ATTACKCHANCE:
  5124. sortList->setValue(item - numOther, calcAttackChance(target, aimLocation, scenarioTime, item, 0.0, NULL));
  5125. break;
  5126. default:
  5127. //-----------------
  5128. // Bad Sort Type...
  5129. return(-3);
  5130. }
  5131. }
  5132. sortList->sort();
  5133. for (item = 0; item < numWeapons; item++) {
  5134. weaponList[item] = sortList->getId(item);
  5135. valueList[item] = sortList->getValue(item);
  5136. }
  5137. }
  5138. else {
  5139. for (long item = 0; item < listSize; item++) {
  5140. sortList->setId(item, weaponList[item]);
  5141. float sortValue = 0.0;
  5142. if (weaponList[item] == -1)
  5143. sortValue = -999.0;
  5144. else
  5145. switch (sortType) {
  5146. case WEAPONSORT_ATTACKCHANCE:
  5147. sortValue = calcAttackChance(target, aimLocation, scenarioTime, weaponList[item], 0.0, NULL);
  5148. break;
  5149. default:
  5150. //-----------------
  5151. // Bad Sort Type...
  5152. return(-3);
  5153. }
  5154. sortList->setValue(item, sortValue);
  5155. }
  5156. sortList->sort();
  5157. for (item = 0; item < listSize; item++) {
  5158. weaponList[item] = sortList->getId(item);
  5159. valueList[item] = sortList->getValue(item);
  5160. }
  5161. }
  5162. return(NO_ERR);
  5163. }
  5164. //---------------------------------------------------------------------------
  5165. bool Mover::hasNonAreaWeapon (void)
  5166. {
  5167. for (long i=numOther;i<numOther+numWeapons;i++)
  5168. {
  5169. if (!getWeaponAreaEffect(i))
  5170. return true;
  5171. }
  5172. return false;
  5173. }
  5174. #define MAX_SHORT_RANGE (60.0f * worldUnitsPerMeter)
  5175. #define MIN_MEDIUM_RANGE (31.0f * worldUnitsPerMeter)
  5176. #define MAX_MEDIUM_RANGE (120.0f * worldUnitsPerMeter)
  5177. #define MIN_LONG_RANGE (61.0f * worldUnitsPerMeter)
  5178. #define MAX_LONG_RANGE (180.0f * worldUnitsPerMeter)
  5179. extern float applyDifficultySkill (float chance, bool isPlayer);
  5180. //---------------------------------------------------------------------------
  5181. float Mover::calcAttackChance (GameObjectPtr target, long aimLocation, float targetTime, long weaponIndex, float modifiers, long* range, Stuff::Vector3D* targetPoint)
  5182. {
  5183. if ((weaponIndex < numOther) || (weaponIndex >= numOther + numWeapons))
  5184. return(-9999.0);
  5185. //-------------------------------------------------------------
  5186. // First, let's find out what kind of object we're targeting...
  5187. Stuff::Vector3D targetPosition;
  5188. BattleMechPtr mech = NULL;
  5189. GroundVehiclePtr vehicle = NULL;
  5190. if (target)
  5191. {
  5192. long targetObjectClass = target->getObjectClass();
  5193. switch (targetObjectClass)
  5194. {
  5195. case BATTLEMECH:
  5196. mech = (BattleMechPtr)target;
  5197. break;
  5198. case GROUNDVEHICLE:
  5199. vehicle = (GroundVehiclePtr)target;
  5200. break;
  5201. }
  5202. targetPosition = target->getPosition();
  5203. }
  5204. else if (targetPoint)
  5205. targetPosition = *targetPoint;
  5206. else
  5207. return(-9999.0);
  5208. float attackChance = pilot->getSkill(MWS_GUNNERY);
  5209. if (!MPlayer && (getCommanderId() == Commander::home->getId())) //Up the gunnery Skill by the difficulty modifier
  5210. attackChance = applyDifficultySkill(attackChance,TRUE);
  5211. else if (!MPlayer && (getCommanderId() == Commander::home->getId())) //Up the gunnery Skill by the difficulty modifier
  5212. attackChance = applyDifficultySkill(attackChance,FALSE);
  5213. //----------------------
  5214. // General fire range...
  5215. float distanceToTarget = distanceFrom(targetPosition);
  5216. if (range)
  5217. {
  5218. if (distanceToTarget <= WeaponRange[FIRERANGE_SHORT])
  5219. *range = FIRERANGE_SHORT;
  5220. else if (distanceToTarget <= WeaponRange[FIRERANGE_MEDIUM])
  5221. *range = FIRERANGE_MEDIUM;
  5222. else
  5223. *range = FIRERANGE_LONG;
  5224. }
  5225. //----------------------
  5226. // Range (Per Weapon)...
  5227. float weaponMinRange = WeaponRanges[MasterComponent::masterList[inventory[weaponIndex].masterID].getWeaponRange()][0];
  5228. float weaponLongRange = WeaponRanges[MasterComponent::masterList[inventory[weaponIndex].masterID].getWeaponRange()][1];
  5229. if (distanceToTarget <= (weaponMinRange - MapCellDiagonal))
  5230. return(-1.0);
  5231. if (distanceToTarget > (weaponLongRange + MapCellDiagonal)) {
  5232. //----------------
  5233. // Out of range...
  5234. return(-1.0);
  5235. }
  5236. //Non-movers get 100% attack chance. Look for modifications for turrets soon!
  5237. // Nothing is better then 100%, no need to modify. Exit here!!
  5238. // -fs 9/8/2000
  5239. if (!target || (target && !target->isMover()) || (target && target->isMover() && target->isDisabled()))
  5240. {
  5241. attackChance = 100.0f;
  5242. return attackChance;
  5243. }
  5244. //Mech specialist modifiers
  5245. if (mech && mech->getPilot())
  5246. {
  5247. if ((mech->tonnage < 40) && mech->getPilot()->isLightMechSpecialist())
  5248. attackChance -= 30.0f;
  5249. else if ((mech->tonnage >= 40) && (mech->tonnage < 60) && mech->getPilot()->isMediumMechSpecialist())
  5250. attackChance -= 20.0f;
  5251. else if ((mech->tonnage >= 60) && (mech->tonnage < 80) && mech->getPilot()->isHevayMechSpecialist())
  5252. attackChance -= 10.0f;
  5253. else if ((mech->tonnage >= 80) && (mech->tonnage < 100) && mech->getPilot()->isAssaultMechSpecialist())
  5254. attackChance -= 10.0f;
  5255. }
  5256. //Weapon Specialist Modifiers
  5257. if (pilot && pilot->isLaserSpecialist() && ((inventory[weaponIndex].masterID == 139) ||
  5258. (inventory[weaponIndex].masterID == 140) ||
  5259. (inventory[weaponIndex].masterID == 147) ||
  5260. (inventory[weaponIndex].masterID == 156)))
  5261. {
  5262. attackChance += WeaponSpecialistModifier;
  5263. }
  5264. if (pilot && pilot->isPulseLaserSpecialist() && ((inventory[weaponIndex].masterID == 142) ||
  5265. (inventory[weaponIndex].masterID == 144) ||
  5266. (inventory[weaponIndex].masterID == 153) ||
  5267. (inventory[weaponIndex].masterID == 151)))
  5268. {
  5269. attackChance += WeaponSpecialistModifier;
  5270. }
  5271. if (pilot && pilot->isERLaserSpecialist() && ((inventory[weaponIndex].masterID == 141) ||
  5272. (inventory[weaponIndex].masterID == 143) ||
  5273. (inventory[weaponIndex].masterID == 150) ||
  5274. (inventory[weaponIndex].masterID == 152)))
  5275. {
  5276. attackChance += WeaponSpecialistModifier;
  5277. }
  5278. if (pilot && pilot->isLightACSpecialist() && ((inventory[weaponIndex].masterID == 100) ||
  5279. (inventory[weaponIndex].masterID == 103) ||
  5280. (inventory[weaponIndex].masterID == 110)))
  5281. {
  5282. attackChance += WeaponSpecialistModifier;
  5283. }
  5284. if (pilot && pilot->isMediumACSpecialist() && ((inventory[weaponIndex].masterID == 101) ||
  5285. (inventory[weaponIndex].masterID == 108) ||
  5286. (inventory[weaponIndex].masterID == 111)))
  5287. {
  5288. attackChance += WeaponSpecialistModifier;
  5289. }
  5290. if (pilot && pilot->isHeavyACSpecialist() && ((inventory[weaponIndex].masterID == 102) ||
  5291. (inventory[weaponIndex].masterID == 109) ||
  5292. (inventory[weaponIndex].masterID == 112)))
  5293. {
  5294. attackChance += WeaponSpecialistModifier;
  5295. }
  5296. if (pilot && pilot->isLRMSpecialist() && ((inventory[weaponIndex].masterID == 120) ||
  5297. (inventory[weaponIndex].masterID == 123)))
  5298. {
  5299. attackChance += WeaponSpecialistModifier;
  5300. }
  5301. if (pilot && pilot->isSRMSpecialist() && ((inventory[weaponIndex].masterID == 124) ||
  5302. (inventory[weaponIndex].masterID == 125)))
  5303. {
  5304. attackChance += WeaponSpecialistModifier;
  5305. }
  5306. if (pilot && pilot->isPPCSpecialist() && ((inventory[weaponIndex].masterID == 145) ||
  5307. (inventory[weaponIndex].masterID == 146) ||
  5308. (inventory[weaponIndex].masterID == 154)))
  5309. {
  5310. attackChance += WeaponSpecialistModifier;
  5311. }
  5312. if (pilot && pilot->isGaussSpecialist() && (inventory[weaponIndex].masterID == 99))
  5313. {
  5314. attackChance += WeaponSpecialistModifier;
  5315. }
  5316. if (pilot && pilot->isSmallArmsSpecialist() && ((inventory[weaponIndex].masterID == 114) ||
  5317. (inventory[weaponIndex].masterID == 155)))
  5318. {
  5319. attackChance += WeaponSpecialistModifier;
  5320. }
  5321. //Range Specialist Modifiers
  5322. if ((distanceToTarget <= MAX_SHORT_RANGE) && pilot && pilot->isShortRangeSpecialist())
  5323. {
  5324. attackChance += 15.0f;
  5325. }
  5326. if ((distanceToTarget >= MIN_MEDIUM_RANGE) && (distanceToTarget <= MAX_MEDIUM_RANGE) && pilot && pilot->isMediumRangeSpecialist())
  5327. {
  5328. attackChance += 15.0f;
  5329. }
  5330. if ((distanceToTarget >= MIN_LONG_RANGE) && (distanceToTarget <= MAX_LONG_RANGE) && pilot && pilot->isLongRangeSpecialist())
  5331. {
  5332. attackChance += 15.0f;
  5333. }
  5334. //Aimed shots go here.
  5335. // Aimed or called shots are percentage of the actual attack chance
  5336. // Therefore, must go here after all modifiers...
  5337. if ((aimLocation > -1) && (mech))
  5338. {
  5339. switch (aimLocation)
  5340. {
  5341. case MECH_BODY_LOCATION_HEAD:
  5342. attackChance *= HeadShotModifier;
  5343. break;
  5344. case MECH_BODY_LOCATION_CTORSO:
  5345. case MECH_BODY_LOCATION_LTORSO:
  5346. case MECH_BODY_LOCATION_RTORSO:
  5347. break;
  5348. case MECH_BODY_LOCATION_LARM:
  5349. case MECH_BODY_LOCATION_RARM:
  5350. attackChance *= ArmShotModifier;
  5351. break;
  5352. case MECH_BODY_LOCATION_LLEG:
  5353. case MECH_BODY_LOCATION_RLEG:
  5354. attackChance *= LegShotModifier;
  5355. break;
  5356. }
  5357. if (pilot && pilot->isSharpShooter())
  5358. attackChance += 20.0f;
  5359. }
  5360. //No more modifiers EXCEPT what is in here.
  5361. if (attackChance > 95.0f)
  5362. attackChance = 95.0f;
  5363. if (attackChance < 5.0f)
  5364. attackChance = 5.0f;
  5365. return(attackChance);
  5366. }
  5367. //---------------------------------------------------------------------------
  5368. /*
  5369. void Mover::buildAttackChanceTable (void) {
  5370. for (long i = 0; i <
  5371. }
  5372. */
  5373. //---------------------------------------------------------------------------
  5374. void Mover::ammoExplosion (long ammoIndex) {
  5375. pilot->injure(2);
  5376. Assert(ammoIndex < (numOther + numWeapons + numAmmos),ammoIndex," Ammo Index out of range ");
  5377. Assert(ammoIndex >= (numOther + numWeapons),ammoIndex," Ammo Index too low ");
  5378. long ammoMasterId = inventory[ammoIndex].masterID;
  5379. long bodyLocation = inventory[ammoIndex].bodyLocation;
  5380. long remainingAmmo = inventory[ammoIndex].amount;
  5381. float damage = float(remainingAmmo) *
  5382. MasterComponent::masterList[ammoMasterId].getAmmoExplosiveDamage();
  5383. inventory[ammoIndex].amount = 0;
  5384. //-------------------------------------------------------------------
  5385. // Make sure the ammoTypeTotal list, in which we track total ammo for
  5386. // all of the ammo types this mover uses, is adjusted...
  5387. if (inventory[ammoIndex].ammoIndex == -1)
  5388. Fatal(-1," Bad Ammo Index in Ammo Explosion ");
  5389. Assert(inventory[ammoIndex].ammoIndex < numAmmoTypes,inventory[ammoIndex].ammoIndex," Too Many Ammo Types ");
  5390. Assert(inventory[ammoIndex].ammoIndex >= 0,inventory[ammoIndex].ammoIndex," not enough Ammo Types ");
  5391. ammoTypeTotal[inventory[ammoIndex].ammoIndex].curAmount -= remainingAmmo;
  5392. //------------------------------------------------------------------------
  5393. // We can use the bodyLocation as the hitLocation since the location codes
  5394. // for all body locations are the same as the armor location codes.
  5395. // MULTIPLAYER NOTE: Since this is called ONLY by Mech.cpp from hitInventoryItem,
  5396. // there should be no need to call it from the server since it's triggered
  5397. // by a weaponhit already.
  5398. WeaponShotInfo shotInfo;
  5399. //-------------------------------------------------------
  5400. // This is big ass damage for an ammo explosion!!!!
  5401. // Happens with the Hunchback right now.
  5402. if (damage > 255.0f)
  5403. damage = 255.0f;
  5404. shotInfo.init(NULL, inventory[ammoIndex].masterID, damage, bodyLocation, 0.0);
  5405. if (MPlayer) {
  5406. if (MPlayer->isServer())
  5407. handleWeaponHit(&shotInfo, true);
  5408. }
  5409. else
  5410. handleWeaponHit(&shotInfo);
  5411. }
  5412. //---------------------------------------------------------------------------
  5413. void Mover::disable (unsigned long cause)
  5414. {
  5415. //-------------------------------------------------
  5416. // Immediately begin shutting down, then disable...
  5417. if (!isDisabled())
  5418. {
  5419. if (pilot)
  5420. pilot->handleAlarm(PILOT_ALARM_VEHICLE_INCAPACITATED, cause);
  5421. if (getObjectClass() == BATTLEMECH)
  5422. if (MPlayer && MPlayer->isServer() && !((BattleMech*)this)->lost) {
  5423. MPlayer->addKillLossChunk(MAX_MC_PLAYERS, getCommanderId());
  5424. ((BattleMech*)this)->lost = true;
  5425. }
  5426. setStatus(OBJECT_STATUS_DISABLED);
  5427. disableThisFrame = true;
  5428. if (Team::home->isFriendly(getTeam()))
  5429. friendlyDestroyed = true;
  5430. else
  5431. {
  5432. enemyDestroyed = true;
  5433. }
  5434. //-----------------------------
  5435. // Immediately lose contacts...
  5436. if (sensorSystem)
  5437. sensorSystem->disable();
  5438. causeOfDeath = cause;
  5439. if (isMech() && (getMoveType() != MOVETYPE_AIR))
  5440. {
  5441. //We now want to play a BUNCH of critical Hit Explosions on the mech
  5442. // To indicate that we are going down.
  5443. Stuff::Vector3D explosionLoc;
  5444. explosionLoc = appearance->getNodeNamePosition("cockpit");
  5445. ObjectManager->createExplosion(MECH_CRITICAL_HIT_ID,NULL,explosionLoc,0.0f,0.0f);
  5446. explosionLoc = appearance->getNodeNamePosition("hit_left");
  5447. ObjectManager->createExplosion(MECH_CRITICAL_HIT_ID,NULL,explosionLoc,0.0f,0.0f);
  5448. explosionLoc = appearance->getNodeNamePosition("hit_right");
  5449. ObjectManager->createExplosion(MECH_CRITICAL_HIT_ID,NULL,explosionLoc,0.0f,0.0f);
  5450. explosionLoc = appearance->getNodeNamePosition("weapon_righttorso");
  5451. ObjectManager->createExplosion(MECH_CRITICAL_HIT_ID,NULL,explosionLoc,0.0f,0.0f);
  5452. explosionLoc = appearance->getNodeNamePosition("weapon_lefttorso");
  5453. ObjectManager->createExplosion(MECH_CRITICAL_HIT_ID,NULL,explosionLoc,0.0f,0.0f);
  5454. }
  5455. }
  5456. }
  5457. //---------------------------------------------------------------------------
  5458. void Mover::shutDown (void) {
  5459. if (isDisabled())
  5460. return;
  5461. if ((status == OBJECT_STATUS_SHUTDOWN) || (status == OBJECT_STATUS_SHUTTING_DOWN))
  5462. return;
  5463. //-----------------------------------
  5464. // Immediately begin shutting down...
  5465. setStatus(OBJECT_STATUS_SHUTTING_DOWN);
  5466. shutDownThisFrame = true;
  5467. }
  5468. //---------------------------------------------------------------------------
  5469. void Mover::startUp (void) {
  5470. if (isDisabled())
  5471. return;
  5472. if ((status == OBJECT_STATUS_STARTING_UP) || (status == OBJECT_STATUS_NORMAL))
  5473. return;
  5474. //---------------------------------
  5475. // Immediately begin starting up...
  5476. setStatus(OBJECT_STATUS_STARTING_UP);
  5477. startUpThisFrame = true;
  5478. }
  5479. //---------------------------------------------------------------------------
  5480. void Mover::tradeRefresh (void) {
  5481. timeLeft = 15.0;
  5482. exploding = false;
  5483. withdrawing = false;
  5484. engineBlowTime = -1.0;
  5485. ejectOrderGiven = false;
  5486. // if (statusChunk.ejectOrderGiven)
  5487. // PAUSE(("whoops"));
  5488. statusChunk.ejectOrderGiven = false;
  5489. }
  5490. //---------------------------------------------------------------------------
  5491. bool Mover::isWithdrawing (void) {
  5492. return(pilot->getCurTacOrder()->code == TACTICAL_ORDER_WITHDRAW);
  5493. }
  5494. //---------------------------------------------------------------------------
  5495. bool Mover::refit (float pointsAvailable, float& pointsUsed, bool ammoOnly) {
  5496. bool result = false;
  5497. float refitPoints = pointsAvailable;
  5498. if (refitPoints > 0.0) {
  5499. float areaMax = refitAmount / 3.0; // size of pool for each section (armor, internals, and ammo) before depletions
  5500. long rearmCount = 0;
  5501. long repairCount = 0;
  5502. // how many locations need armor or IS repaired?
  5503. if (!ammoOnly) {
  5504. for (long i = 0; i < numArmorLocations; i++) {
  5505. //-------------------------------
  5506. // don't repair destroyed arms...
  5507. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5508. continue;
  5509. if ((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure))
  5510. repairCount++;
  5511. if (armor[i].curArmor < armor[i].maxArmor)
  5512. repairCount++;
  5513. }
  5514. }
  5515. // how many ammo types need refilling, and how much?
  5516. for (long i = 0; i < numAmmoTypes; i++)
  5517. if (ammoTypeTotal[i].curAmount < ammoTypeTotal[i].maxAmount)
  5518. rearmCount++;
  5519. if (pilot && pilot->getRadio())
  5520. pilot->getRadio()->resetAmmoMessage();
  5521. // repair each armor & IS
  5522. for (i = 0; i < numArmorLocations; i++) {
  5523. if ((repairCount == 0) || (refitPoints <= 0.0))
  5524. continue;
  5525. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5526. continue;
  5527. if (armor[i].curArmor < armor[i].maxArmor) {
  5528. float pointsToUse = areaMax / repairCount;
  5529. if (pointsToUse > refitPoints)
  5530. pointsToUse = refitPoints;
  5531. float repairValue = pointsToUse * refitCostArray[ARMOR_REFIT_COST][REFIT_VEHICLE];
  5532. if ((armor[i].curArmor + repairValue) > armor[i].maxArmor) {
  5533. repairValue = armor[i].maxArmor - armor[i].curArmor;
  5534. pointsToUse = repairValue / refitCostArray[ARMOR_REFIT_COST][REFIT_VEHICLE];
  5535. }
  5536. armor[i].curArmor += repairValue;
  5537. refitPoints -= pointsToUse;
  5538. }
  5539. if ((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure)) {
  5540. float pointsToUse = areaMax / repairCount;
  5541. if (pointsToUse > refitPoints)
  5542. pointsToUse = refitPoints;
  5543. float repairValue = pointsToUse * refitCostArray[INTERNAL_REFIT_COST][REFIT_VEHICLE];
  5544. if ((body[i].curInternalStructure + repairValue) > body[i].maxInternalStructure) {
  5545. repairValue = body[i].maxInternalStructure - body[i].curInternalStructure;
  5546. pointsToUse = repairValue / refitCostArray[INTERNAL_REFIT_COST][REFIT_VEHICLE];
  5547. }
  5548. body[i].curInternalStructure += repairValue;
  5549. long newDamageState = IS_DAMAGE_NONE;
  5550. if (body[i].curInternalStructure <= 0.0)
  5551. newDamageState = IS_DAMAGE_DESTROYED;
  5552. else if ((body[i].curInternalStructure / body[i].maxInternalStructure) <= 0.5)
  5553. newDamageState = IS_DAMAGE_PARTIAL;
  5554. if ((getObjectClass() == BATTLEMECH) /*&& (newDamageState != body[i].damageState)*/) {
  5555. if ((i == MECH_BODY_LOCATION_LLEG) || (i == MECH_BODY_LOCATION_RLEG))
  5556. ((BattleMechPtr)this)->calcLegStatus();
  5557. if (i == MECH_BODY_LOCATION_CTORSO)
  5558. ((BattleMechPtr)this)->calcTorsoStatus();
  5559. }
  5560. body[i].damageState = newDamageState;
  5561. refitPoints -= pointsToUse;
  5562. }
  5563. }
  5564. // refit each ammo that needs it
  5565. for (i=0; i < numAmmoTypes; i++) {
  5566. if ((rearmCount > 0) && (refitPoints > 0.0) && (ammoTypeTotal[i].curAmount < ammoTypeTotal[i].maxAmount)) {
  5567. bool recalc = (ammoTypeTotal[i].curAmount == 0);
  5568. float pointsToUse = areaMax / rearmCount;
  5569. if (pointsToUse > refitPoints)
  5570. pointsToUse = refitPoints;
  5571. float repairValue = pointsToUse * refitCostArray[AMMO_REFIT_COST][REFIT_VEHICLE] * MasterComponent::masterList[ammoTypeTotal[i].masterId].getAmmoPerTon();
  5572. if ((ammoTypeTotal[i].curAmount + repairValue) > ammoTypeTotal[i].maxAmount) {
  5573. repairValue = ammoTypeTotal[i].maxAmount - ammoTypeTotal[i].curAmount;
  5574. pointsToUse = repairValue / (refitCostArray[AMMO_REFIT_COST][REFIT_VEHICLE] * MasterComponent::masterList[ammoTypeTotal[i].masterId].getAmmoPerTon());
  5575. }
  5576. ammoTypeTotal[i].curAmount += repairValue;
  5577. refitPoints -= pointsToUse;
  5578. if (recalc) {
  5579. calcWeaponEffectiveness(false);
  5580. calcFireRanges();
  5581. }
  5582. }
  5583. }
  5584. // do any locations still need repair?
  5585. rearmCount = 0;
  5586. repairCount = 0;
  5587. if (!ammoOnly) {
  5588. for (i = 0; i < numArmorLocations; i++) {
  5589. // don't repair destroyed arms
  5590. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5591. continue;
  5592. if (((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure)) || (armor[i].curArmor < armor[i].maxArmor)) {
  5593. repairCount++;
  5594. break;
  5595. }
  5596. }
  5597. }
  5598. // if there's no more repairing, do any ammo types need refilling?
  5599. if (repairCount == 0)
  5600. for (i = 0; i < numAmmoTypes; i++)
  5601. if (ammoTypeTotal[i].curAmount < ammoTypeTotal[i].maxAmount) {
  5602. rearmCount++;
  5603. break;
  5604. }
  5605. if ((repairCount == 0) && (rearmCount == 0)) {
  5606. // done fixing, move on
  5607. if (getCommanderId() == Commander::home->getId())
  5608. {
  5609. getPilot()->radioMessage(RADIO_REFIT_DONE, TRUE);
  5610. soundSystem->playBettySample(BETTY_REPAIR_COMPLETE);
  5611. if (appearance)
  5612. appearance->startSmoking(-1); //Turn the smoke off if we successfully repaired.
  5613. }
  5614. result = true;
  5615. }
  5616. }
  5617. else {
  5618. // no points left, so move on
  5619. if (getCommanderId() == Commander::home->getId())
  5620. {
  5621. getPilot()->radioMessage(RADIO_REFIT_INCOMPLETE, TRUE);
  5622. soundSystem->playBettySample( BETTY_REPAIR_GONE );
  5623. }
  5624. result = true;
  5625. }
  5626. pointsUsed = pointsAvailable - refitPoints;
  5627. //-----------------------------------------------------------------------
  5628. // Points used should be rounded to nearest 1/4 point (for consistency in
  5629. // MultiPlayer)...
  5630. if ((pointsUsed > 0.0) && (pointsUsed < 0.25))
  5631. pointsUsed = 0.25;
  5632. pointsUsed = (float)((long)((pointsUsed + 0.125) / 0.25)) / 4.0;
  5633. return(result);
  5634. }
  5635. //---------------------------------------------------------------------------
  5636. float Mover::calcRecoverPrice (void) {
  5637. float repairValue = 0;
  5638. for (long i = 0; i < numBodyLocations; i++) {
  5639. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5640. continue;
  5641. if ((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure))
  5642. repairValue += (body[i].maxInternalStructure - body[i].curInternalStructure);
  5643. }
  5644. float pointsToUse = repairValue / recoverCost;
  5645. //-----------------------------------------------------------------------
  5646. // Points used should be rounded to nearest 1/4 point (for consistency in
  5647. // MultiPlayer)...
  5648. if ((pointsToUse > 0.0) && (pointsToUse < 0.25))
  5649. pointsToUse = 0.25;
  5650. pointsToUse = (float)((long)((pointsToUse + 0.125) / 0.25)) / 4.0;
  5651. return(pointsToUse);
  5652. }
  5653. //---------------------------------------------------------------------------
  5654. bool Mover::recover (void) {
  5655. inRecoverUpdate = true;
  5656. bool result = false;
  5657. float recoverPoints = 100000.0;
  5658. if (recoverPoints > 0.0) {
  5659. long repairCount = 0;
  5660. // how many locations need IS repaired?
  5661. for (long i = 0; i < numBodyLocations; i++) {
  5662. //-------------------------------
  5663. // don't repair destroyed arms...
  5664. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5665. continue;
  5666. if ((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure))
  5667. repairCount++;
  5668. }
  5669. // repair each IS
  5670. for (i = 0; i < numBodyLocations; i++)
  5671. {
  5672. if ((repairCount == 0) || (recoverPoints <= 0.0))
  5673. continue;
  5674. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5675. continue;
  5676. if ((i < numBodyLocations) && (body[i].curInternalStructure < body[i].maxInternalStructure))
  5677. {
  5678. float pointsToUse = recoverAmount / repairCount;
  5679. if (pointsToUse > recoverPoints)
  5680. pointsToUse = recoverPoints;
  5681. float repairValue = pointsToUse * recoverCost;
  5682. if ((body[i].curInternalStructure + repairValue) > body[i].maxInternalStructure)
  5683. {
  5684. repairValue = body[i].maxInternalStructure - body[i].curInternalStructure;
  5685. pointsToUse = repairValue / recoverCost;
  5686. }
  5687. body[i].curInternalStructure += repairValue;
  5688. long newDamageState = IS_DAMAGE_NONE;
  5689. if (body[i].curInternalStructure <= 0.0)
  5690. newDamageState = IS_DAMAGE_DESTROYED;
  5691. else if ((body[i].curInternalStructure / body[i].maxInternalStructure) <= 0.5)
  5692. newDamageState = IS_DAMAGE_PARTIAL;
  5693. if ((getObjectClass() == BATTLEMECH) /*&& (newDamageState != body[i].damageState)*/)
  5694. {
  5695. if ((i == MECH_BODY_LOCATION_LLEG) || (i == MECH_BODY_LOCATION_RLEG))
  5696. ((BattleMechPtr)this)->calcLegStatus();
  5697. if (i == MECH_BODY_LOCATION_CTORSO)
  5698. ((BattleMechPtr)this)->calcTorsoStatus();
  5699. }
  5700. body[i].damageState = newDamageState;
  5701. recoverPoints -= pointsToUse;
  5702. }
  5703. }
  5704. // do any locations still need repair?
  5705. repairCount = 0;
  5706. for (i = 0; i < numBodyLocations; i++)
  5707. {
  5708. // don't repair destroyed arms
  5709. if (((i == MECH_BODY_LOCATION_LARM) || (i == MECH_BODY_LOCATION_RARM)) && (body[i].damageState == IS_DAMAGE_DESTROYED))
  5710. continue;
  5711. if (body[i].curInternalStructure < body[i].maxInternalStructure) {
  5712. repairCount++;
  5713. break;
  5714. }
  5715. }
  5716. if (repairCount == 0)
  5717. {
  5718. // done fixing, move on
  5719. for (long i = 0; i < numOther; i++)
  5720. {
  5721. MasterComponentPtr component = &MasterComponent::masterList[inventory[i].masterID];
  5722. switch (component->getForm()) {
  5723. case COMPONENT_FORM_WEAPON:
  5724. case COMPONENT_FORM_WEAPON_ENERGY:
  5725. case COMPONENT_FORM_WEAPON_BALLISTIC:
  5726. case COMPONENT_FORM_WEAPON_MISSILE:
  5727. inventory[i].health = component->getHealth();
  5728. inventory[i].disabled = false;
  5729. calcWeaponEffectiveness(false);
  5730. calcFireRanges();
  5731. break;
  5732. default:
  5733. inventory[i].health = component->getHealth();
  5734. inventory[i].disabled = false;
  5735. }
  5736. }
  5737. for (i = 0; i < numBodyLocations; i++)
  5738. for (long j = 0; j < NumLocationCriticalSpaces[i]; j++)
  5739. if (body[i].criticalSpaces[j].inventoryID < 255)
  5740. body[i].criticalSpaces[j].hit = inventory[body[i].criticalSpaces[j].inventoryID].disabled;
  5741. if (getCommanderId() == Commander::home->getId())
  5742. {
  5743. getPilot()->radioMessage(RADIO_REFIT_DONE, TRUE);
  5744. soundSystem->playBettySample(BETTY_REPAIR_COMPLETE);
  5745. }
  5746. result = true;
  5747. }
  5748. }
  5749. else
  5750. {
  5751. // no points left, so move on
  5752. if (getCommanderId() == Commander::home->getId())
  5753. {
  5754. getPilot()->radioMessage(RADIO_REFIT_INCOMPLETE, TRUE);
  5755. soundSystem->playBettySample( BETTY_REPAIR_GONE );
  5756. soundSystem->playBettySample(BETTY_REPAIR_INCOMPLETE);
  5757. }
  5758. result = true;
  5759. }
  5760. inRecoverUpdate = false;
  5761. return(result);
  5762. }
  5763. //---------------------------------------------------------------------------
  5764. void Mover::drawSensorTextHelp (float screenX, float screenY, long resID, DWORD color, bool drawBOLD)
  5765. {
  5766. DWORD width, height;
  5767. Stuff::Vector4D moveHere;
  5768. moveHere.x = screenX;
  5769. moveHere.y = screenY;
  5770. char buffer[256];
  5771. cLoadString( resID, buffer, 255 );
  5772. FloatHelp::getTextStringLength(buffer,color,1.0f,true,drawBOLD,true,false,width,height);
  5773. moveHere.x -= width / 2;
  5774. moveHere.z = width;
  5775. moveHere.w = height;
  5776. globalFloatHelp->setFloatHelp(buffer,moveHere,color,0x0,1.0f,true,drawBOLD,true,false);
  5777. }
  5778. //---------------------------------------------------------------------------
  5779. void Mover::drawWaypointPath()
  5780. {
  5781. getPilot()->drawWaypointPath();
  5782. }
  5783. //---------------------------------------------------------------------------
  5784. void Mover::updateDrawWaypointPath()
  5785. {
  5786. getPilot()->updateDrawWaypointPath();
  5787. }
  5788. //---------------------------------------------------------------------------
  5789. void Mover::initOptimalCells (long numIncrements) {
  5790. numOptimalIncrements = numIncrements;
  5791. double degreesToRadians = (double)DEGREES_TO_RADS;
  5792. double incrementDegrees = 360.0 / numOptimalIncrements;
  5793. double doptimalCells[MAX_ATTACK_CELLRANGE][MAX_ATTACK_INCREMENTS][2];
  5794. for (long r = 0; r < MAX_ATTACK_CELLRANGE; r++)
  5795. for (long i = 0; i < numIncrements; i++) {
  5796. double incrementRadians = incrementDegrees * (double)i * degreesToRadians;
  5797. doptimalCells[r][i][0] = sin(incrementRadians) * (double)r;
  5798. doptimalCells[r][i][1] = cos(incrementRadians) * (double)r;
  5799. }
  5800. //--------------------------------------------------------------------
  5801. // Due to double rounding errors, we munge the numbers a little before
  5802. // casting to chars. NOTE: due to this rounding, it's possible that
  5803. // an "optimal" cell may be a cell or more further from the target than
  5804. // the desired range (i.e. there's an error of 10 or so meters in these
  5805. // calcs, in some situations). Therefore, it's imperative that this is
  5806. // acceptable (re: max fire range for the mover), otherwise the mover
  5807. // may select an "optimal" cell from which it's out of range. As long
  5808. // as these optimal ranges fall well within their weapon fire ranges,
  5809. // this should be no problem.
  5810. for (r = 0; r < MAX_ATTACK_CELLRANGE; r++)
  5811. for (long i = 0; i < numIncrements; i++) {
  5812. if (doptimalCells[r][i][0] < 0.00)
  5813. optimalCells[r][i][0] = (char)(doptimalCells[r][i][0] - 0.50);
  5814. else
  5815. optimalCells[r][i][0] = (char)(doptimalCells[r][i][0] + 0.50);
  5816. if (doptimalCells[r][i][1] < 0.00)
  5817. optimalCells[r][i][1] = (char)(doptimalCells[r][i][1] - 0.50);
  5818. else
  5819. optimalCells[r][i][1] = (char)(doptimalCells[r][i][1] + 0.50);
  5820. }
  5821. //-----------------------------------------------
  5822. // Now, initialize the tables for Ranged Areas...
  5823. char rangedMap[MAX_ATTACK_CELLRANGE * 2 + 1][MAX_ATTACK_CELLRANGE * 2 + 1];
  5824. for (r = 0; r < (MAX_ATTACK_CELLRANGE * 2 + 1); r++)
  5825. for (long c = 0; c < (MAX_ATTACK_CELLRANGE * 2 + 1); c++) {
  5826. Stuff::Vector3D start, goal;
  5827. start.x = c;
  5828. start.y = r;
  5829. start.z = 0;
  5830. goal.x = MAX_ATTACK_CELLRANGE;
  5831. goal.y = MAX_ATTACK_CELLRANGE;
  5832. goal.z = 0.0;
  5833. double dist = distance_from(start, goal);
  5834. rangedMap[r][c] = (char)(dist + 0.5);
  5835. }
  5836. long index = 0;
  5837. for (long range = 0; range < MAX_ATTACK_CELLRANGE; range++) {
  5838. rangedCellsIndices[range][0] = index;
  5839. for (long r = 0; r < (MAX_ATTACK_CELLRANGE * 2 + 1); r++)
  5840. for (long c = 0; c < (MAX_ATTACK_CELLRANGE * 2 + 1); c++)
  5841. if (rangedMap[r][c] == range) {
  5842. rangedCells[index][0] = r - MAX_ATTACK_CELLRANGE;
  5843. rangedCells[index][1] = c - MAX_ATTACK_CELLRANGE;
  5844. index++;
  5845. }
  5846. rangedCellsIndices[range][1] = index - 1;
  5847. }
  5848. }
  5849. //***************************************************************************
  5850. // MOVE ROUTINES CALLBACKS
  5851. //***************************************************************************
  5852. void GetBlockedDoorCells (long moveLevel, long door, char* openCells) {
  5853. Assert((door > -1) && (door < GlobalMoveMap[moveLevel]->numDoors), 0, " FUDGE 1");
  5854. Assert((GlobalMoveMap[moveLevel]->doors[door].direction[0] == 1) || (GlobalMoveMap[moveLevel]->doors[door].direction[0] == 2), 0, " FUDGE 2");
  5855. Assert((GlobalMoveMap[moveLevel]->doors[door].length > 0) && (GlobalMoveMap[moveLevel]->doors[door].length < 1024), 0, " FUDGE 3");
  5856. long doorWorldCellRec[4] = {-1, -1, -1, -1};
  5857. if (GlobalMoveMap[moveLevel]->doors[door].direction[0] == 1) {
  5858. doorWorldCellRec[0] = GlobalMoveMap[moveLevel]->doors[door].row;
  5859. doorWorldCellRec[1] = GlobalMoveMap[moveLevel]->doors[door].col;
  5860. doorWorldCellRec[2] = doorWorldCellRec[0] + GlobalMoveMap[moveLevel]->doors[door].length;
  5861. doorWorldCellRec[3] = doorWorldCellRec[1] + 2;
  5862. //NOTE: DO NOT set cells for elementals--only mechs and vehicles.
  5863. long numMovers = ObjectManager->getNumMovers();
  5864. for (long i = 0; i < numMovers; i++) {
  5865. MoverPtr mover = ObjectManager->getMover(i);
  5866. if ((mover->getWatchID() != PathFindMap[SECTOR_PATHMAP]->moverWID) && (mover->getWatchID() != RamObjectWID) && !mover->isDisabled()) {
  5867. long cellPos[2];
  5868. mover->getCellPosition (cellPos[0], cellPos[1]);
  5869. long worldCellRow = cellPos[0];
  5870. long worldCellCol = cellPos[1];
  5871. if ((worldCellRow >= doorWorldCellRec[0]) && (worldCellRow < doorWorldCellRec[2]))
  5872. if ((worldCellCol >= doorWorldCellRec[1]) && (worldCellCol < doorWorldCellRec[3])) {
  5873. long cellIndex = worldCellRow - doorWorldCellRec[0];
  5874. Assert((cellIndex > -1) && (cellIndex < GlobalMoveMap[moveLevel]->doors[door].length), 0, " Bad Cell Index ");
  5875. openCells[cellIndex] = 0;
  5876. }
  5877. }
  5878. }
  5879. }
  5880. else {
  5881. doorWorldCellRec[0] = GlobalMoveMap[moveLevel]->doors[door].row;
  5882. doorWorldCellRec[1] = GlobalMoveMap[moveLevel]->doors[door].col;
  5883. doorWorldCellRec[2] = doorWorldCellRec[0] + 2;
  5884. doorWorldCellRec[3] = doorWorldCellRec[1] + GlobalMoveMap[moveLevel]->doors[door].length;
  5885. long numMovers = ObjectManager->getNumMovers();
  5886. for (long i = 0; i < numMovers; i++) {
  5887. MoverPtr mover = ObjectManager->getMover(i);
  5888. if ((mover->getObjectClass() != ELEMENTAL) && (mover->getWatchID() != PathFindMap[SECTOR_PATHMAP]->moverWID) && (mover->getWatchID() != RamObjectWID) && !mover->isDisabled()) {
  5889. long cellPos[2];
  5890. mover->getCellPosition (cellPos[0], cellPos[1]);
  5891. long worldCellRow = cellPos[0];
  5892. long worldCellCol = cellPos[1];
  5893. if ((worldCellRow >= doorWorldCellRec[0]) && (worldCellRow < doorWorldCellRec[2]))
  5894. if ((worldCellCol >= doorWorldCellRec[1]) && (worldCellCol < doorWorldCellRec[3])) {
  5895. long cellIndex = worldCellCol - doorWorldCellRec[1];
  5896. Assert((cellIndex > -1) && (cellIndex < GlobalMoveMap[moveLevel]->doors[door].length), 0, " Bad Cell Index ");
  5897. openCells[cellIndex] = 0;
  5898. }
  5899. }
  5900. }
  5901. }
  5902. }
  5903. //---------------------------------------------------------------------------
  5904. void PlaceMovers (void) {
  5905. long numMovers = ObjectManager->getNumMovers();
  5906. for (long i = 0; i < numMovers; i++) {
  5907. MoverPtr mover = (MoverPtr)ObjectManager->getMover(i);
  5908. if (mover->getUseMe()) {
  5909. long cellRow, cellCol;
  5910. mover->getCellPosition(cellRow, cellCol);
  5911. if (!GameMap->getPreserved(cellRow, cellCol))
  5912. GameMap->setPreserved(cellRow, cellCol, true);
  5913. GameMap->placeObject(mover->getPosition(), mover->getExtentRadius());
  5914. GameMap->setPassable(cellRow, cellCol, false);
  5915. }
  5916. }
  5917. }
  5918. //---------------------------------------------------------------------------
  5919. void adjustMoveMapCellCost (MoveMapNodePtr cell, long costAdj);
  5920. void PlaceStationaryMovers (MoveMap* map) {
  5921. long numMovers = ObjectManager->getNumMovers();
  5922. for (long i = 0; i < numMovers; i++) {
  5923. MoverPtr mover = ObjectManager->getMover(i);
  5924. if ((mover->getObjectClass() != ELEMENTAL) && (mover->getWatchID() != map->moverWID) && (mover->getWatchID() != RamObjectWID) && !mover->isDisabled()) {
  5925. long cellRow, cellCol;
  5926. mover->getCellPosition(cellRow, cellCol);
  5927. if ((cellRow >= 0) && (cellRow < map->height) && (cellCol >= 0) && (cellCol < map->width)) {
  5928. if ((cellRow != map->startR) || (cellCol != map->startC)) {
  5929. if ((map->map[cellRow * map->maxWidth + cellCol].flags & MOVEFLAG_GOAL) == 0) {
  5930. MovePathPtr path = mover->getPilot()->getMovePath();
  5931. if (path && (path->numSteps == 0)) {
  5932. map->map[cellRow * map->maxWidth + cellCol].setFlag(MOVEFLAG_MOVER_HERE);
  5933. #ifdef USE_OVERLAYS
  5934. long bridgeCost = COST_BLOCKED / 3;
  5935. long overlay = GameMap->getOverlay(cellRow, cellCol);
  5936. if (OverlayIsBridge[overlay])
  5937. map->adjustCellCost(cellRow, cellCol, bridgeCost);
  5938. else
  5939. #endif
  5940. map->adjustCost(cellRow, cellCol, COST_BLOCKED * 2);
  5941. }
  5942. }
  5943. }
  5944. }
  5945. }
  5946. }
  5947. }
  5948. bool Mover::isCloseToFirstTacOrder( Stuff::Vector3D& pos )
  5949. {
  5950. return getPilot()->isCloseToFirstTacOrder( pos );
  5951. }
  5952. void Mover::removeFromUnitGroup( int id )
  5953. {
  5954. unitGroup = -1;
  5955. //unitGroup &= (~(1<<id));
  5956. }
  5957. void Mover::addToUnitGroup( int id )
  5958. {
  5959. unitGroup = (1 << id); // now you only get to be in one at a time
  5960. }
  5961. bool Mover::isInUnitGroup( int id )
  5962. {
  5963. if ( unitGroup == -1 )
  5964. return false;
  5965. return unitGroup & ( 1 << id ) ? true : false;
  5966. }
  5967. bool Mover::handleEjection()
  5968. {
  5969. if (pilot && ((pilot->getStatus() == WARRIOR_STATUS_NORMAL) || (pilot->getStatus() == WARRIOR_STATUS_WITHDRAWING))) {
  5970. //----------------
  5971. // First, eject...
  5972. getPilot()->eject();
  5973. ejectOrderGiven = true;
  5974. //------------------
  5975. // The head blows...
  5976. // destroyBodyLocation(MECH_BODY_LOCATION_HEAD);
  5977. //---------------------
  5978. // Create the Eject FX
  5979. // WHEN READY -fs
  5980. //---------------------------------------------
  5981. // If we aren't already disabled, we are now...
  5982. disable(EJECTION_DEATH);
  5983. #ifdef USE_IFACE
  5984. //------------------------------------------------------
  5985. // The interface needs to know I'm not around anymore...
  5986. theInterface->RemoveMech(getPartId());
  5987. #endif
  5988. #ifdef USE_MOODMUSIC
  5989. //------------------------------------
  5990. // What heroic music should be played?
  5991. if (alignment == homeTeam->getAlignment())
  5992. friendlyDestroyed = true;
  5993. else
  5994. enemyDestroyed = true;
  5995. #endif
  5996. }
  5997. return(true);
  5998. }
  5999. bool Mover::isWeaponWorking (long weaponIndex)
  6000. {
  6001. if (inventory[weaponIndex].disabled)
  6002. return(FALSE);
  6003. else if (getWeaponShots(weaponIndex) == 0)
  6004. return (FALSE);
  6005. return(TRUE);
  6006. }
  6007. //***************************************************************************
  6008. void Mover::Save (PacketFilePtr file, long packetNum)
  6009. {
  6010. MoverData data;
  6011. CopyTo(&data);
  6012. //PacketNum incremented in ObjectManager!!
  6013. file->writePacket(packetNum,(MemoryPtr)&data,sizeof(MoverData),STORAGE_TYPE_ZLIB);
  6014. }
  6015. //***************************************************************************
  6016. void Mover::CopyTo (MoverData *data)
  6017. {
  6018. data->killed = killed;
  6019. data->lost = lost;
  6020. data->positionNormal = positionNormal;
  6021. data->velocity = velocity;
  6022. memcpy(data->name,name, sizeof(char) * MAXLEN_MOVER_NAME);
  6023. data->chassis = chassis;
  6024. data->startDisabled = startDisabled;
  6025. data->creationTime = creationTime;
  6026. data->moveType = moveType;
  6027. data->moveLevel = moveLevel;
  6028. data->followRoads = followRoads;
  6029. memcpy(data->lastMapCell,lastMapCell,sizeof(long) * 2);
  6030. data->damageRateTally = damageRateTally;
  6031. data->damageRateCheckTime = damageRateCheckTime;
  6032. data->pilotCheckDamageTally = pilotCheckDamageTally;
  6033. memcpy(data->body,body,sizeof(BodyLocation) * MAX_MOVER_BODY_LOCATIONS);
  6034. data->numBodyLocations = numBodyLocations;
  6035. data->fieldedCV = fieldedCV;
  6036. data->attackRange = attackRange;
  6037. memcpy(data->armor,armor, sizeof(ArmorLocation) * MAX_MOVER_ARMOR_LOCATIONS);
  6038. data->numArmorLocations = numArmorLocations;
  6039. memcpy(data->longName,longName, sizeof(char) * MAXLEN_MECH_LONGNAME);
  6040. memcpy(data->inventory,inventory,sizeof(InventoryItem) * MAX_MOVER_INVENTORY_ITEMS);
  6041. data->numOther = numOther;
  6042. data->numWeapons = numWeapons;
  6043. data->numAmmos = numAmmos;
  6044. memcpy(data->ammoTypeTotal,ammoTypeTotal,sizeof(AmmoTally) * MAX_AMMO_TYPES);
  6045. data->numAmmoTypes = numAmmoTypes;
  6046. data->pilotHandle = pilotHandle;
  6047. data->cockpit = cockpit;
  6048. data->engine = engine;
  6049. data->lifeSupport = lifeSupport;
  6050. data->sensor = sensor;
  6051. data->ecm = ecm;
  6052. data->probe = probe;
  6053. data->jumpJets = jumpJets;
  6054. data->nullSignature = nullSignature;
  6055. data->maxWeaponEffectiveness = maxWeaponEffectiveness;
  6056. data->weaponEffectiveness = weaponEffectiveness;
  6057. data->minRange = minRange;
  6058. data->maxRange = maxRange;
  6059. data->optimalRange = optimalRange;
  6060. data->numFunctionalWeapons = numFunctionalWeapons;
  6061. data->numAntiMissileSystems = numAntiMissileSystems;
  6062. memcpy(data->antiMissileSystem,antiMissileSystem,sizeof(unsigned char) * MAX_ANTI_MISSILE_SYSTEMS);
  6063. data->engineBlowTime = engineBlowTime;
  6064. data->maxMoveSpeed = maxMoveSpeed;
  6065. data->shutDownThisFrame = shutDownThisFrame;
  6066. data->startUpThisFrame = startUpThisFrame;
  6067. data->disableThisFrame = disableThisFrame;
  6068. data->teamId = teamId;
  6069. data->groupId = groupId;
  6070. data->squadId = squadId;
  6071. data->selectionIndex = selectionIndex;
  6072. data->teamRosterIndex = teamRosterIndex;
  6073. data->commanderId = commanderId;
  6074. data->unitGroup = unitGroup;
  6075. data->iconPictureIndex = iconPictureIndex;
  6076. data->suppressionFire = suppressionFire;
  6077. data->pilotCheckModifier = pilotCheckModifier;
  6078. data->prevPilotCheckModifier = prevPilotCheckModifier;
  6079. data->prevPilotCheckDelta = prevPilotCheckDelta;
  6080. data->prevPilotCheckUpdate = prevPilotCheckUpdate;
  6081. data->failedPilotingCheck = failedPilotingCheck;
  6082. data->lastWeaponEffectivenessCalc = lastWeaponEffectivenessCalc;
  6083. data->lastOptimalRangeCalc = lastOptimalRangeCalc;
  6084. data->challengerWID = challengerWID;
  6085. data->lastGesture = lastGesture;
  6086. data->control = control;
  6087. data->dynamics = dynamics;
  6088. data->numWeaponHitsHandled = numWeaponHitsHandled;
  6089. data->timeLeft = timeLeft;
  6090. data->exploding = exploding;
  6091. data->withdrawing = withdrawing;
  6092. data->yieldTimeLeft = yieldTimeLeft;
  6093. data->lastValidPosition = lastValidPosition;
  6094. data->pivotDirection = pivotDirection;
  6095. data->lastHustleTime = lastHustleTime;
  6096. data->salvageVehicle = salvageVehicle;
  6097. data->markDistanceMoved = markDistanceMoved;
  6098. data->refitBuddyWID = refitBuddyWID;
  6099. data->recoverBuddyWID = recoverBuddyWID;
  6100. data->crashAvoidSelf = crashAvoidSelf;
  6101. data->crashAvoidPath = crashAvoidPath;
  6102. data->crashBlockSelf = crashBlockSelf;
  6103. data->crashBlockPath = crashBlockPath;
  6104. data->crashYieldTime = crashYieldTime;
  6105. data->pathLockLength = pathLockLength;
  6106. memcpy(data->pathLockList,pathLockList, sizeof(long) * MAX_LOCK_RANGE * 2);
  6107. data->moveCenter = moveCenter;
  6108. data->moveRadius = moveRadius;
  6109. data->overlayWeightClass = overlayWeightClass;
  6110. data->timeToClearSelection = timeToClearSelection;
  6111. data->timeSinceMoving = timeSinceMoving;
  6112. data->timeSinceFiredLast = timeSinceFiredLast;
  6113. data->lastMovingTargetWID = lastMovingTargetWID;
  6114. data->mechSalvage = mechSalvage;
  6115. data->teleportPosition = teleportPosition;
  6116. data->debugPage = debugPage;
  6117. data->pathLocks = pathLocks;
  6118. data->isOnGui = isOnGui;
  6119. data->conStat = conStat;
  6120. data->fadeTime = fadeTime;
  6121. data->alphaValue = alphaValue;
  6122. data->causeOfDeath = causeOfDeath;
  6123. data->lowestWeaponNodeID = lowestWeaponNodeID;
  6124. if (getAppearance())
  6125. {
  6126. getAppearance()->getPaintScheme(data->psRed,data->psGreen,data->psBlue);
  6127. }
  6128. GameObject::CopyTo(dynamic_cast<GameObjectData *>(data));
  6129. }
  6130. //---------------------------------------------------------------------------
  6131. void Mover::Load (MoverData *data)
  6132. {
  6133. GameObject::Load(dynamic_cast<GameObjectData *>(data));
  6134. killed = data->killed;
  6135. lost = data->lost;
  6136. positionNormal = data->positionNormal;
  6137. velocity = data->velocity;
  6138. memcpy(name,data->name, sizeof(char) * MAXLEN_MOVER_NAME);
  6139. chassis = data->chassis;
  6140. startDisabled = data->startDisabled;
  6141. creationTime = data->creationTime;
  6142. moveType = data->moveType;
  6143. moveLevel = data->moveLevel;
  6144. followRoads = data->followRoads;
  6145. memcpy(lastMapCell,data->lastMapCell,sizeof(long) * 2);
  6146. damageRateTally = data->damageRateTally;
  6147. damageRateCheckTime = data->damageRateCheckTime;
  6148. pilotCheckDamageTally = data->pilotCheckDamageTally;
  6149. memcpy(body,data->body,sizeof(BodyLocation) * MAX_MOVER_BODY_LOCATIONS);
  6150. numBodyLocations = data->numBodyLocations;
  6151. fieldedCV = data->fieldedCV;
  6152. attackRange = data->attackRange;
  6153. memcpy(armor,data->armor, sizeof(ArmorLocation) * MAX_MOVER_ARMOR_LOCATIONS);
  6154. numArmorLocations = data->numArmorLocations;
  6155. memcpy(longName,data->longName, sizeof(char) * MAXLEN_MECH_LONGNAME);
  6156. memcpy(inventory,data->inventory,sizeof(InventoryItem) * MAX_MOVER_INVENTORY_ITEMS);
  6157. numOther = data->numOther;
  6158. numWeapons = data->numWeapons;
  6159. numAmmos = data->numAmmos;
  6160. memcpy(ammoTypeTotal,data->ammoTypeTotal,sizeof(AmmoTally) * MAX_AMMO_TYPES);
  6161. numAmmoTypes = data->numAmmoTypes;
  6162. pilotHandle = data->pilotHandle;
  6163. cockpit = data->cockpit;
  6164. engine = data->engine;
  6165. lifeSupport = data->lifeSupport;
  6166. sensor = data->sensor;
  6167. ecm = data->ecm;
  6168. probe = data->probe;
  6169. jumpJets = data->jumpJets;
  6170. nullSignature = data->nullSignature;
  6171. maxWeaponEffectiveness = data->maxWeaponEffectiveness;
  6172. weaponEffectiveness = data->weaponEffectiveness;
  6173. minRange = data->minRange;
  6174. maxRange = data->maxRange;
  6175. optimalRange = data->optimalRange;
  6176. numFunctionalWeapons = data->numFunctionalWeapons;
  6177. numAntiMissileSystems = data->numAntiMissileSystems;
  6178. memcpy(antiMissileSystem,data->antiMissileSystem,sizeof(unsigned char) * MAX_ANTI_MISSILE_SYSTEMS);
  6179. engineBlowTime = data->engineBlowTime;
  6180. maxMoveSpeed = data->maxMoveSpeed;
  6181. shutDownThisFrame = data->shutDownThisFrame;
  6182. startUpThisFrame = data->startUpThisFrame;
  6183. disableThisFrame = data->disableThisFrame;
  6184. teamId = data->teamId;
  6185. groupId = data->groupId;
  6186. squadId = data->squadId;
  6187. selectionIndex = data->selectionIndex;
  6188. teamRosterIndex = data->teamRosterIndex;
  6189. commanderId = data->commanderId;
  6190. unitGroup = data->unitGroup;
  6191. iconPictureIndex = data->iconPictureIndex;
  6192. suppressionFire = data->suppressionFire;
  6193. pilotCheckModifier = data->pilotCheckModifier;
  6194. prevPilotCheckModifier = data->prevPilotCheckModifier;
  6195. prevPilotCheckDelta = data->prevPilotCheckDelta;
  6196. prevPilotCheckUpdate = data->prevPilotCheckUpdate;
  6197. failedPilotingCheck = data->failedPilotingCheck;
  6198. lastWeaponEffectivenessCalc = data->lastWeaponEffectivenessCalc;
  6199. lastOptimalRangeCalc = data->lastOptimalRangeCalc;
  6200. challengerWID = data->challengerWID;
  6201. lastGesture = data->lastGesture;
  6202. control = data->control;
  6203. dynamics = data->dynamics;
  6204. numWeaponHitsHandled = data->numWeaponHitsHandled;
  6205. timeLeft = data->timeLeft;
  6206. exploding = data->exploding;
  6207. withdrawing = data->withdrawing;
  6208. yieldTimeLeft = data->yieldTimeLeft;
  6209. lastValidPosition = data->lastValidPosition;
  6210. pivotDirection = data->pivotDirection;
  6211. lastHustleTime = data->lastHustleTime;
  6212. salvageVehicle = data->salvageVehicle;
  6213. markDistanceMoved = data->markDistanceMoved;
  6214. refitBuddyWID = data->refitBuddyWID;
  6215. recoverBuddyWID = data->recoverBuddyWID;
  6216. crashAvoidSelf = data->crashAvoidSelf;
  6217. crashAvoidPath = data->crashAvoidPath;
  6218. crashBlockSelf = data->crashBlockSelf;
  6219. crashBlockPath = data->crashBlockPath;
  6220. crashYieldTime = data->crashYieldTime;
  6221. pathLockLength = data->pathLockLength;
  6222. memcpy(pathLockList,data->pathLockList, sizeof(long) * MAX_LOCK_RANGE * 2);
  6223. moveCenter = data->moveCenter;
  6224. moveRadius = data->moveRadius;
  6225. overlayWeightClass = data->overlayWeightClass;
  6226. timeToClearSelection = data->timeToClearSelection;
  6227. timeSinceMoving = data->timeSinceMoving;
  6228. timeSinceFiredLast = data->timeSinceFiredLast;
  6229. lastMovingTargetWID = data->lastMovingTargetWID;
  6230. mechSalvage = data->mechSalvage;
  6231. teleportPosition = data->teleportPosition;
  6232. debugPage = data->debugPage;
  6233. pathLocks = data->pathLocks;
  6234. isOnGui = data->isOnGui;
  6235. conStat = data->conStat;
  6236. fadeTime = data->fadeTime;
  6237. alphaValue = data->alphaValue;
  6238. causeOfDeath = data->causeOfDeath;
  6239. lowestWeaponNodeID = data->lowestWeaponNodeID;
  6240. //OK. Must get pilotPtr back.
  6241. pilot = MechWarrior::warriorList[pilotHandle];
  6242. if (!pilot)
  6243. STOP(("Could not find pilot for mover after in-mission load"));
  6244. //Set the PaintScheme
  6245. if (getAppearance())
  6246. {
  6247. getAppearance()->resetPaintScheme(data->psRed,data->psGreen,data->psBlue);
  6248. }
  6249. }
  6250. //***************************************************************************