1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 RWS Inc, All Rights Reserved
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of version 2 of the GNU General Public License as published by
- // the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along
- // with this program; if not, write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- // doofus.cpp
- // Project: Postal
- //
- // This module implements the CDoofus class which is the class of enemy
- // guys for the game.
- //
- // History:
- // 01/13/97 BRH Started this file from CDude and modified it
- // to do some enemy logic using the same assets
- // as the sample 2D guy.
- //
- // 01/15/97 BRH Changed the render to draw the guy with his
- // hotspot between his feet.
- //
- // 02/04/97 JMI Changed LoadDib() call to Load() (which now supports
- // loading of DIBs).
- //
- // 02/05/97 BRH Fixed problem loading Instance ID. Also fixed some other
- // problems with the run routine. He now will follow the
- // bouys to get to the bouy nearest the CDude.
- //
- // 02/23/97 BRH Changed coordinate system to x, -z in Find Direction.
- //
- // 02/25/97 BRH Fixed problem with the Startup where the height was not
- // being multiplied by 4, so enemies would start too low
- // if placed on a roof.
- //
- // 03/04/97 BRH Derived this from CCharacter instead of CThing
- //
- // 03/05/97 BRH Added SelectRandomBouy function which picks a random
- // bouy number (a valid one) or returns 0 if there are
- // no bouys.
- //
- // 03/06/97 BRH Added realignment timer and function so that the
- // direction to the bouy can be recalculated every
- // so often to avoid missing it.
- //
- // 03/13/97 JMI Load now takes a version number.
- //
- // 03/14/97 JMI SelectDude() now chooses the closest dude on the X/Z plane.
- //
- // 04/04/97 JMI SelectDude() no longer chooses dead dudes.
- // Also, last update to make SelectDude() find the closest
- // CDude was not comparing the distance to the CDude from this
- // guy, but instead was comparing the distance to the CDude from
- // (0, 0, 0).
- //
- // 04/16/97 BRH Changed references to the realm's list of CThings to use
- // the new non-STL methods.
- //
- // 04/22/97 BRH Moved common code and some variables like the animations
- // to the base class. Put common logic routines like the
- // reactions to weapons in this base class.
- //
- // 04/24/97 BRH Added TryClearDirection function that uses the
- // IsPathClear() funciton to try 3 directions to see if they
- // are clear.
- //
- // 05/02/97 JMI Added check to make sure not already in shot state or
- // writhing state before changing to shot state in OnShotMsg.
- // Also, now OnBurnMsg() sets him into the m_animOnFire in-
- // stead of m_animRun.
- //
- // 05/04/97 BRH Removed #ifdef code sections referring to STL lists.
- //
- // 05/06/97 BRH Added Popout logic, detection smash.
- //
- // 05/09/97 BRH Added Writhing and Shot logic from CPerson.
- //
- // 05/11/97 BRH Fixed problem with Logic_Guard.
- //
- // 05/15/97 BRH In the popout and run & shoot wait states, changed from
- // the temporary timeout method to checking for the
- // triggered pylon before running the logic.
- //
- // 05/18/97 BRH Added some logic functions for the victims to use.
- //
- // 05/20/97 BRH Added the hiding states. Also added calls to
- // ReevaluateState() at times in the action cycles
- // where the action can be changed. Changed Logic_PylonDetect
- // to set flags for use in ReevaluateState() and
- // in the logic table variables evaluation.
- //
- // 05/21/97 BRH Added m_dAnimRot to specify the direction the guy is
- // facing and separating that from the direction he is
- // moving. This will be used for the run and shoot
- // so that he can run and face sideways.
- //
- // 05/22/97 BRH Fixed typo bug in Logic_Shoot.
- //
- // 05/25/97 BRH Added the m_ShootAngle variable and an override for
- // ShootWeapon that uses this angle to aim the weapon.
- //
- // 05/26/97 BRH Changed ShootWeapon so that CSmash bits are passed in
- // so that enemy bullets don't hit other enemies.
- //
- // 05/26/97 BRH Added a timer for shooting to limit the number of shots
- // for each type of gun. Also added some avoidance
- // of obstacles during MoveNext in case the guy gets stuck
- // trying to get to the next bouy.
- //
- // 05/27/97 BRH Fixed a few problems with logic table transitions.
- //
- // 05/27/97 BRH Added some avoidance of obstacles to Logic_PopBegin in
- // case he gets stuck on walls while trying to find the
- // first pylon.
- //
- // 05/31/97 JMI Replaced m_pDude with m_idDude. The problem was that, by
- // just using a pointer to the dude, we never found out when
- // the dude was gone (deleted). Although this is rare for
- // CDudes, it does happen. For example, in the beginning of
- // a level all CDudes that do not have an associated player
- // are sent a Delete msg. They do not process this message
- // until their respective Update() calls. If a CDoofus
- // derived guy happened to be placed in the level before a
- // CDude (that is, the CDoofus' Update() got called before the
- // CDude's), and the CDoofus happened to point its m_pDude at
- // this CDude (that was destined to soon be deleted), later
- // when referencing the pointer to the freed and/or reallocated
- // memory, the CDoofus could cause a protection fault or math
- // overflow (due to invalid values returned by
- // m_pDude->GetX, Y, Z() with the non-CDude 'this' pointer).
- // Also, in the case that SelectDude() did not select a dude,
- // SQDistanceToDude() was returning an unitialized double
- // which, in some cases, could be a totally illegal double
- // value.
- //
- // 06/02/97 BRH Added AdvanceHold action and state so that once he reaches
- // the end of the advancement, he goes into this hold state
- // rather than Engage automatically. This way the logic
- // table can have more control over the next state.
- //
- // 06/02/97 BRH Changed TryClearShot to use the CRealm version of
- // IsPathClear which just checks for terrain obstacles.
- //
- // 06/05/97 JMI Changed m_sHitPoints to m_stockpile.m_sHitPoints to
- // accommodate new m_stockpile in base class, CThing3d (which
- // used to contain the m_sHitPoints).
- //
- // 06/10/97 BRH Now sends messages to CDemon for Explosion, and burning.
- //
- // 06/10/97 BRH Added RunIdleAnimation() function which monitors the
- // idle timer and controls which of 3 idle animations to
- // use. This should be called when in some kind of wait
- // state.
- //
- // 06/10/97 BRH Fixed "YMCA" bug where the enemy guy would cycle
- // each frame between Advance and AdvanceHold.
- //
- // 06/11/97 BRH Fixed the crouch and search animations since the
- // search is done from the crouch posiiton. It was
- // previously backwards because I thought the search
- // animation was done from a standing position.
- //
- // 06/13/97 JMI Changed FindDirection() to return m_dRot if we cannot find
- // a CDude.
- // Also, changed Logic_Shoot() over to using events.
- //
- // 06/17/97 BRH Changed NEAR_DEATH_HITPOINTS to a higher value since the
- // machine gun bullets were increased, it was diffucult to
- // get a writhing person.
- //
- // 06/17/97 BRH Attempted to make enemies stop shooting dead CDudes.
- //
- // 06/17/97 JMI Now doubles smash radius when in writhing state.
- //
- // 06/18/97 JMI Changed PlaySoundWrithing() to return the duration of the
- // played sample.
- //
- // 06/18/97 BRH Changed over to using GetRandom()
- //
- // 06/24/97 JMI Added intialization of m_sRotateDir.
- //
- // 06/26/97 BRH Added a special case for Writhers who get burned.
- // Previously they didn't react because their smash bits
- // were changed when they went into writhing. Then we wanted
- // them to get killed by fire so included the AlmostDead bits
- // but they they would jump to their feet and run around
- // once they got burned. So now they will just lie there
- // and die.
- //
- // 06/26/97 BRH When a doofus is killed after preparing a weapon but
- // before shooting it, he will drop it if it was a throwing
- // weapon, but will just delete it if it was a launched
- // weapon.
- //
- // 06/27/97 BRH Added a flag for recently stuck so that when a character
- // gets stuck on a wall, he sets the flag, then when he gets
- // free of the obstacle, it will get the closest bouy rather
- // than trying to align to the one it was trying to get to
- // previously which many times caused him to get stuck in
- // the same manner.
- //
- // 06/28/97 BRH Changed the RegisterBirth and RegisterDeath calls to pass
- // m_bCivilian so that the scoring can updated for hostiles
- // or civilians.
- //
- // 07/09/97 BRH Added logic for walking and running around on a bouy
- // network - used for victims.
- //
- // 07/11/97 BRH Added call to inline Cheater() to disable game if necessary
- //
- // 07/12/97 BRH Added addional text strings for the new actions that were
- // added. Also changed PylonDetect function to use
- // QuickCheckClosest rather than QuickCheck so that enemies
- // will detect the pylon you put them closest to and not
- // just a marker pylon.
- //
- // 07/12/97 BRH Changed macro MAX_STEPUP_THRESHOLD to use the CThing3d
- // definitino MaxStepUpThreshold so that the detecting and
- // ability to move will be the same.
- //
- // 07/15/97 BRH Added a few more calls to Cheater.
- //
- // 07/20/97 BRH Effectively canceled the DelayShoot state by setting
- // the timeout to zero since it caused the guys to
- // not shoot very often on levels that were difficult
- // to move in.
- //
- // 07/21/97 JMI Now Update() calls base class version.
- //
- // 07/23/97 BRH Added tunable values for different timeouts which will get
- // set in doofus to the default static values that they were
- // before, and can be set in Person to the personatorium values
- // so that different people have different traits.
- //
- // 08/01/97 BRH Changed the aiming so that it is based on the game
- // difficulty setting.
- //
- // 08/02/97 BRH Added YellForHelp funciton that enemies can call when they
- // get shot to alert others in the are that they need help.
- //
- // 08/05/97 BRH Fixed the problem with the doofus leaving the Run&Shoot
- // state at higher difficulty settings due to re-aligning
- // the angles with the shoot angle.
- //
- // 08/06/97 BRH Changed TryClearShot to first do the point translation
- // to the rigid body where the weapon is shot from, in order
- // to get the correct height. This will give the guys more
- // opportunities to shoot, and will work when the guys
- // are scaled larger or smaller.
- //
- // 08/06/97 JMI Added m_ptransExecutionTarget link point for execution
- // sphere. Also, added PositionSmash() to provide overridable
- // method for updating the collision sphere.
- //
- // 08/06/97 JMI Now TryClearPath() returns false if there's no weapon
- // link point.
- //
- // 08/07/97 JMI Added ms_awdWeapons[], ms_apszWeaponResNames[], and
- // GetResources() and FreeResources() for loading these anims.
- // Also, added ms_lWeaponResRefCount so we could know when the
- // weapons were no longer needed.
- //
- // 08/08/97 JMI Now Logic_Shoot() handles flamer.
- //
- // 08/08/97 JMI Now Logic_Shoot() handles AutoRifle, Uzi, and SmallPistol.
- //
- // 08/08/97 BRH Added start and end bouy ID's for special cases like
- // marching. Added these to load and save. They are set
- // by the dialog in the CPerson. Also added marching logic.
- //
- // 08/09/97 BRH Changed panic to be vicinity based so only the nearby
- // people panic. Also added checks for prepared weapons
- // when guys get blown up or burned, where they should
- // either randomly discard their weapon, or delete it.
- //
- // 08/10/97 JMI Moved CDoofus() and ~CDoofus() into doofus.cpp from
- // doofus.h.
- // Was going to move the registering of the birth into the
- // constructor but I realized that m_bCivilian probably won't
- // get set until Load() so I left it in Startup() (it could
- // probably also get set via EditModify() but this doesn't
- // matter b/c that's in edit mode only).
- // Now Registers death with the realm in the destructor if
- // m_bRegisteredBirth is true.
- // Also, now OnShotMsg() calls base class version even if
- // other states don't permit a state change b/c this gives
- // better feedback to the user (base class OnShotMsg() creates
- // blood).
- //
- // 08/10/97 JMI Moved NoWeapon up to enum value 0 and created a new one
- // to take its -1 place as an invalid weapon (InvalidWeapon).
- // Also, added block in PrepareWeapon() for the NoWeapon case.
- // Also, moved prepare weapon from the .H to the .CPP.
- //
- // 08/11/97 BRH Found the problem with the parade level victims where they
- // were flipping around one bouy. The problem was that they
- // asked how to get to a particular bouy which was unreachable
- // because the network was not fully connected, so when it
- // got the unreachable flag, it kept retrying the same bouy.
- // I fixed the problem so they will pick a new bouy, but also
- // we are deleting the unconnected nodes in the parade level.
- //
- // 08/11/97 JMI Now sets backup weapon to default 'none' and uses the
- // backup weapon when there's no animation for the current
- // weapon type.
- // Also, changed incorrectly name ms_awtType2Id to
- // ms_awtId2Type mapping.
- //
- // 08/11/97 BRH Fixed problem with run & shoot not using the correct
- // angle.
- //
- // 08/12/97 JMI Now hides weapon if current event is 10 or more.
- //
- // 08/12/97 BRH Checks for events channel in render so that people
- // without animation events like the band guys will still
- // work. Also made shooting timing based on game
- // difficulty.
- //
- // 08/13/97 JMI Changed so OnShotMsg() calls base class to generate blood
- // no matter what.
- //
- // 08/14/97 JMI Switched references to g_GameSettings.m_sDifficulty to
- // m_pRealm->m_flags.sDifficulty.
- //
- // 08/14/97 BRH Added static default bits to pass to weapons for their
- // collision testing. Changed call to WhileHoldingWeapon
- // to include these defaults.
- //
- // 08/15/97 BRH Changed the check for available pylons to check for a
- // clear path to the pylon before saying it is available.
- // Some guys were getting stuck on fences between themselves
- // an an available pylon. Also fixed the writhing to
- // executed transition so that the guys don't flip around
- // 180 degrees. Also fixed the smash bits that get passed
- // to the missile weapons.
- //
- // 08/16/97 BRH Changed the person's PlaySound functions so that comments
- // are only made when the CDude is alive. So in this module, I
- // made sure that the victims were also calling SelectDude()
- // for the logic that they were doing, just to keep track of
- // whether the CDude was alive or dead. Also tuned the
- // shooting accuracy for the levels to make them more accurate
- // on the easier levels since the easy dudes were missing too
- // much.
- //
- // 08/18/97 BRH Added virtual override for CCharacter's WhileHoldingWeapon
- // so that for higher difficulty settings where the enemies
- // re-aim after preparing the weapon, they could do it
- // every frame between PrepareWeapon and ShootWeapon so that
- // they turned smoothy and didn't just flip around when they
- // got to ShootWeapon. This was a problem with the Rocketman
- // who had a long shoot animation. If the target moved a
- // quite a bit between PrepareWeapon and ShootWeapon, he would
- // spin around just before releasing the shot. Also fixed
- // a problem with the ShootWeapon adjustment of the
- // random shooting innacuracy when I changed the values
- // last time, I adjusted the max left swing, but forgot to
- // adjust the random amount.
- //
- // 08/18/97 JMI Now sends doofuses who are writhing to the back most sprite
- // layer. As people pointed out this is another of two evils.
- // It is, however, a lessor. It appears a little wierd in one
- // case but better in all others I've seen so far.
- //
- // 08/19/97 JMI Now sends doofuses who are dying to the back as well.
- //
- // 08/19/97 JMI No longer shoots CSmash::Misc items.
- //
- // 08/20/97 JMI Now Logic_Shot() does not wait to the end of the animation
- // to check for death. This way they don't seem to kick back
- // from the shot, shoot forward, and then die.
- //
- // 08/20/97 BRH Trying a smaller tolerance on the Bouys so that the
- // enemies must get closer to the hotspot before
- // moving to the next one. We wanted to see if this helps
- // certain situations where guys are getting stuck. Changed
- // from 10 pixel radius to 5.
- //
- // 08/21/97 JMI Now does deluxe reporting on whether this doofus finds his
- // NavNet.
- //
- // 08/21/97 JMI Now after reporting that the doofus did not find its NavNet
- // it sets him to the current NavNet.
- //
- // 08/21/97 BRH Added a blooc counter so that the number of blood pools
- // created while writhing could be cut down a little bit.
- //
- // 08/24/97 BRH Changed the TryClearShot to use the Character version of
- // IsPathClear which also checks for people in the way. Added
- // an additional call to TryClearShot in ShootWeapon which is
- // after the weapon has been re-aimed to make sure that the
- // path is still clear. It was a problem with the rocketman
- // who has the slowest animation and may turn completely
- // around before shooting.
- //
- // 08/24/97 BRH Aborts sample that was playing when the guy gets executed
- // so that he doesn't keep making noises after he is dead.
- //
- // 08/25/97 BRH Undid a bunch of changes made yesterday which changed the
- // logic too much which threw off the tuning. Added two new
- // escape routes for the Engage mode instead. Fixed the
- // shoot timer which was being set to two different values
- // in two different places. Made the shooting more accurate
- // and more of the difficulty tuning will be with the
- // shooting times.
- //
- // 08/27/97 BRH Victims now panic when they are shot.
- //
- // 09/03/97 JMI Sentries now exclude CSmash::Bads and CSmash::Civilians.
- //
- // 09/07/97 BRH As Steve requested, the medium difficulty will now
- // re-adjust aiming just like hard difficulty in ShootWeapon.
- //
- // 12/18/97 JMI Changed SelectRandomBouy() to return 0 if a the number of
- // bouys is less than or equal to one. It used to be less
- // than one but, for some reason, GetNumNodes() returns 1 when
- // there's no bouys. This caused it to lock up when no bouys
- // for some lgk files.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #define DOOFUS_CPP
- #include "RSPiX.h"
- #include <math.h>
- #include "doofus.h"
- #include "dude.h"
- #include "SampleMaster.h"
- #include "grenade.h"
- #include "pylon.h"
- ////////////////////////////////////////////////////////////////////////////////
- // Macros/types/etc.
- ////////////////////////////////////////////////////////////////////////////////
- #define NEAR_DEATH_HITPOINTS 31 // Below this, start writhing
- #define MS_BETWEEN_SAMPLES 100 // Time between pain groans.
- #define BURNT_BRIGHTNESS -40 // -128 to 127.
- ////////////////////////////////////////////////////////////////////////////////
- // Variables/data
- ////////////////////////////////////////////////////////////////////////////////
- // These are default values -- actually values are set using the editor!
- double CDoofus::ms_dAccUser = 150.0; // Acceleration due to user
- double CDoofus::ms_dAccDrag = 80.0; // Acceleration due to drag
- double CDoofus::ms_dMaxVelFore = 80.0; // Maximum forward velocity
- double CDoofus::ms_dMaxVelBack = -60.0; // Maximum backward velocity
- double CDoofus::ms_dDegPerSec = 150.0; // Degrees of rotation per second
- double CDoofus::ms_dOffScreenDistance = 500*500; // Square distance off screen
- double CDoofus::ms_dGuardDistance = 300*300; // Square distance before attacking
- double CDoofus::ms_dThrowHorizVel = 200; // Throw out at this velocity
- double CDoofus::ms_dMinFightDistance = 80.0; // Closest you want to get
- double CDoofus::ms_dMedFightDistance = 200.0; // Median distance for fighting
- double CDoofus::ms_dMaxFightDistance = 400.0; // Farthest distance for fighting
- double CDoofus::ms_dMinFightDistanceSQ = 80.0*80.0;// Square distance for
- double CDoofus::ms_dMedFightDistanceSQ = 200.0*200.0;
- double CDoofus::ms_dMaxFightDistanceSQ = 400.0*400.0;
- double CDoofus::ms_dMarchVelocity = 20.0; // Speed at which to march
- long CDoofus::ms_lDefaultAlignTime = 100; //2000; // Time to realign to bouy position
- long CDoofus::ms_lGuardTimeoutMin = 4000; // Time to check for CDudes again
- long CDoofus::ms_lGuardTimeoutInc = 500; // Interval to add for each easier difficulty level
- long CDoofus::ms_lShootTimeoutMin = 1000; // Min time between shots, adjused for difficulty
- long CDoofus::ms_lShootTimeoutInc = 200; // Adjustment time for difficulty level between shots
- long CDoofus::ms_lDetectionRadius = 100;//80 // Radius of detection smash sphere
- long CDoofus::ms_lRunShootInterval = 2000; // Time between shots while running
- long CDoofus::ms_lReseekTime = 15 * 1000; // Seek the dude again after this time
- long CDoofus::ms_lShotTimeout = 3000; // Time between full shot reaction animations
- // which gives him a chance to attack or run
- long CDoofus::ms_lStuckRecoveryTime = 5000; // Time to stay in recoverys state
- long CDoofus::ms_lAvoidRadius = 40; // Radius of fire detection smash
- long CDoofus::ms_lYellRadius = 150; // Yell for help in this vicinity
- long CDoofus::ms_lHelpTimeout = 3000; // Time to react to a call for help.
- long CDoofus::ms_lDelayShootTimeout = 0; //2000; // Time before shooting
- long CDoofus::ms_lHelpingTimeout = 1000; // Only shoot every this often
- // Note that these seem to apply to all weapons except bullet weapons. That is, these are
- // passed to WhileHoldingWeapon() which passes them on to ShootWeapon(), but WhileHoldingWeapon()
- // is only used for non-bullet weapons. In the bullet weapons case, it uses the default parameters
- // to ShootWeapon().
- U32 CDoofus::ms_u32CollideBitsInclude = CSmash::Character | CSmash::Barrel;
- U32 CDoofus::ms_u32CollideBitsDontcare = CSmash::Good | CSmash::Bad;
- U32 CDoofus::ms_u32CollideBitsExclude = CSmash::SpecialBarrel | CSmash::Ducking | CSmash::Bad | CSmash::Civilian;
- short CDoofus::ms_sStuckLimit = 3; // Number of times to retry before attempting to
- // get free of whatever you are stuck on.
- // Weapon animations.
- CAnim3D CDoofus::ms_aanimWeapons[NumWeaponTypes];
- // Current ref count on ms_aanimWeapons[].
- long CDoofus::ms_lWeaponResRefCount = 0;
- // Weapon details (descriptions, res names, etc.).
- CDoofus::WeaponDetails CDoofus::ms_awdWeapons[NumWeaponTypes] =
- {
- //////// NoWeapon
- { // pszName, pszResName, CThing ID
- "Verbal Abuse",
- NULL,
- TotalIDs,
- },
- //////// Rocket
- { // pszName, pszResName, CThing ID
- "Rocket",
- "3d/launcher",
- CRocketID,
- },
- //////// Grenade
- { // pszName, pszResName, CThing ID
- "Grenade",
- NULL,
- CGrenadeID,
- },
- //////// Napalm
- { // pszName, pszResName, CThing ID
- "Napalm",
- "3d/napalmer",
- CNapalmID,
- },
- //////// Firebomb
- { // pszName, pszResName, CThing ID
- "Cocktail",
- NULL,
- CFirebombID,
- },
- //////// ProximityMine
- { // pszName, pszResName, CThing ID
- "ProximityMine",
- NULL,
- CProximityMineID,
- },
- //////// TimedMine
- { // pszName, pszResName, CThing ID
- "TimedMine",
- NULL,
- CTimedMineID,
- },
- //////// RemoteMine
- { // pszName, pszResName, CThing ID
- "RemoteMine",
- NULL,
- CRemoteControlMineID,
- },
- //////// BouncingBetty
- { // pszName, pszResName, CThing ID
- "BouncingBetty",
- NULL,
- CBouncingBettyMineID,
- },
- //////// Flamer
- { // pszName, pszResName, CThing ID
- "Flamer",
- "3d/flmthrower",
- CFirestreamID,
- },
- //////// Pistol
- { // pszName, pszResName, CThing ID
- "Pistol",
- "3d/bigpistol",
- CPistolID,
- },
- //////// MachineGun
- { // pszName, pszResName, CThing ID
- "MachineGun",
- "3d/submachine",
- CMachineGunID,
- },
- //////// ShotGun
- { // pszName, pszResName, CThing ID
- "ShotGun",
- "3d/ShotGun",
- CShotGunID,
- },
- //////// Heatseeker
- { // pszName, pszResName, CThing ID
- "Heatseeker",
- "3d/launcher",
- CHeatseekerID,
- },
- //////// Assault
- { // pszName, pszResName, CThing ID
- "Assault",
- "3d/SprayGun",
- CAssaultWeaponID,
- },
- //////// DeathWad
- { // pszName, pszResName, CThing ID
- "DeathWad",
- "3d/napalmer",
- CDeathWadID,
- },
- //////// DoubleBarrel
- { // pszName, pszResName, CThing ID
- "DoubleBarrel",
- "3d/shotgun",
- CDoubleBarrelID,
- },
- //////// Uzi
- { // pszName, pszResName, CThing ID
- "Uzi",
- "3d/Uzi",
- CUziID,
- },
- //////// AutoRifle
- { // pszName, pszResName, CThing ID
- "AutoRifle",
- "3d/AutoRifle",
- CAutoRifleID,
- },
- //////// SmallPistol
- { // pszName, pszResName, CThing ID
- "SmallPistol",
- "3d/Pistol",
- CSmallPistolID,
- },
- //////// Dynamite
- { // pszName, pszResName, CThing ID
- "Dynamite",
- NULL,
- CDynamiteID,
- },
- };
- // Maps a CThing ID to a WeaponType enum.
- CDoofus::WeaponType CDoofus::ms_awtId2Type[TotalIDs] =
- {
- NoWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- Rocket,
- Grenade,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- Napalm,
- InvalidWeapon,
- InvalidWeapon,
- Firebomb,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- ProximityMine,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- Pistol,
- MachineGun,
- ShotGun,
- InvalidWeapon,
- TimedMine,
- BouncingBettyMine,
- RemoteControlMine,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- Heatseeker,
- InvalidWeapon,
- Assault,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- InvalidWeapon,
- Flamer,
- DeathWad,
- DoubleBarrel,
- Uzi,
- AutoRifle,
- SmallPistol,
- Dynamite,
- };
- char* CDoofus::ms_apszActionNames[] =
- {
- "Guard",
- "Advance",
- "Retreat",
- "Engage",
- "Popout",
- "Run&Shoot",
- "Hide",
- "Advance-Hold",
- "Walk",
- "Panic",
- "March",
- "Madness",
- "Help",
- };
- // Let this auto-init to 0
- short CDoofus::ms_sFileCount;
- //
- //
- ////////////////////////////////////////////////////////////////////////////////
- // Constructor
- ////////////////////////////////////////////////////////////////////////////////
- CDoofus::CDoofus(CRealm* pRealm, ClassIDType id)
- : CCharacter(pRealm, id)
- {
- m_sSuspend = 0;
- m_pNavNet = NULL;
- m_u16NavNetID = 0;
- m_idDude = CIdBank::IdNil;
- m_pNextBouy = NULL;
- m_sNextX = m_sNextZ = 0;
- m_ucDestBouyID = m_ucNextBouyID = 0;
- m_lAlignTimer = 0;
- m_lEvalTimer = 0;
- m_lShootTimer = 0;
- m_lShotTimeout = 0;
- m_pPylonStart = NULL;
- m_pPylonEnd = NULL;
- m_lCommentTimer = 0;
- m_usCommentCounter = 0;
- m_lLastHelpCallTime = 0;
- m_lSampleTimeIsPlaying = 0;
- m_bRecentlyStuck = false;
- m_bCivilian = false;
- m_ptransExecutionTarget = NULL;
- m_spriteWeapon.m_pthing = this;
- m_ucSpecialBouy0ID = 0;
- m_ucSpecialBouy1ID = 0;
- m_bPanic = false;
- m_bRegisteredBirth = false;
- // Default to no fallback weapon.
- m_eFallbackWeaponType = TotalIDs;
- m_sStuckCounter = 0;
- m_usBloodCounter = 0;
- m_siPlaying = 0;
- // explicitly initialize to stop Valgrind whining. --ryan.
- m_lIdleTimer = pRealm->m_time.GetGameTime() + 2000 + GetRandom() % 2000;
- m_lStuckTimeout = 0;
- m_u16KillerId = 0;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Destructor
- ////////////////////////////////////////////////////////////////////////////////
- CDoofus::~CDoofus()
- {
- // If we've been born . . .
- if (m_bRegisteredBirth == true)
- {
- // See who killed us, if anyone
- bool bPlayerKill = false;
- CThing* pthing;
- if (m_u16KillerId != 0 && m_pRealm->m_idbank.GetThingByID(&pthing, m_u16KillerId) == 0)
- if (pthing->GetClassID() == CDudeID)
- bPlayerKill = true; // Dude killed us.
- // Good bye.
- m_pRealm->RegisterDeath(m_bCivilian, bPlayerKill);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Load object (should call base class version!)
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::Load( // Returns 0 if successfull, non-zero otherwise
- RFile* pFile, // In: File to load from
- bool bEditMode, // In: True for edit mode, false otherwise
- short sFileCount, // In: File count (unique per file, never 0)
- ULONG ulFileVersion) // In: Version of file format to load.
- {
- short sResult = CCharacter::Load(pFile, bEditMode, sFileCount, ulFileVersion);
- if (sResult == SUCCESS)
- {
- // Load common data just once per file (not with each object)
- if (ms_sFileCount != sFileCount)
- {
- ms_sFileCount = sFileCount;
- // Load static data
- switch (ulFileVersion)
- {
- default:
- case 1:
- pFile->Read(&ms_dAccUser);
- pFile->Read(&ms_dAccDrag);
- pFile->Read(&ms_dMaxVelFore);
- pFile->Read(&ms_dMaxVelBack);
- pFile->Read(&ms_dDegPerSec);
- // Clear the panic flag at the load of a level.
- m_bPanic = false;
- break;
- }
- }
- // Load object data
- switch (ulFileVersion)
- {
- default:
- case 43:
- pFile->Read(&m_ucSpecialBouy0ID);
- pFile->Read(&m_ucSpecialBouy1ID);
- case 42:
- case 41:
- case 40:
- case 39:
- case 38:
- case 37:
- case 36:
- case 35:
- case 34:
- case 33:
- case 32:
- case 31:
- case 30:
- case 29:
- case 28:
- case 27:
- case 26:
- case 25:
- case 24:
- case 23:
- case 22:
- case 21:
- case 20:
- case 19:
- case 18:
- case 17:
- case 16:
- case 15:
- case 14:
- case 13:
- case 12:
- case 11:
- case 10:
- case 9:
- case 8:
- case 7:
- case 6:
- case 5:
- case 4:
- case 3:
- case 2:
- case 1:
- // Get the instance ID for the NavNet
- U16 u16Data;
- short sres = pFile->Read(&u16Data);
- m_u16NavNetID = u16Data;
- break;
- }
-
- // Make sure there were no file errors
- if (!pFile->Error())
- {
- // Get resources
- }
- else
- {
- sResult = -1;
- TRACE("CDoofus::Load(): Error reading from file!\n");
- }
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Save object (should call base class version!)
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::Save( // Returns 0 if successfull, non-zero otherwise
- RFile* pFile, // In: File to save to
- short sFileCount) // In: File count (unique per file, never 0)
- {
- // Call the base class save to save the u16InstanceID
- short sResult = CCharacter::Save(pFile, sFileCount);
- if (sResult == SUCCESS)
- {
- // Save common data just once per file (not with each object)
- if (ms_sFileCount != sFileCount)
- {
- ms_sFileCount = sFileCount;
- // Save static data
- pFile->Write(&ms_dAccUser);
- pFile->Write(&ms_dAccDrag);
- pFile->Write(&ms_dMaxVelFore);
- pFile->Write(&ms_dMaxVelBack);
- pFile->Write(&ms_dDegPerSec);
- }
- // Save object data
- // Save the instance ID for the parent NavNet so it can be connected
- // again after load
- pFile->Write(&m_ucSpecialBouy0ID);
- pFile->Write(&m_ucSpecialBouy1ID);
- U16 u16Data = CIdBank::IdNil; // Safety.
- if (m_pNavNet)
- u16Data = m_pNavNet->GetInstanceID();
- pFile->Write(&u16Data);
- sResult = pFile->Error();
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Startup object
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::Startup(void) // Returns 0 if successfull, non-zero otherwise
- {
- CCharacter::Startup();
- // If we don't have a pointer to the Nav Net yet, get it from the ID
- if (m_pNavNet == NULL)
- {
- if (m_pRealm->m_idbank.GetThingByID((CThing**) &m_pNavNet, m_u16NavNetID) == 0)
- {
- // Verify it's a NavNet . . .
- if (m_pNavNet->GetClassID() == CNavigationNetID)
- {
- // Wahoo.
- }
- else
- {
- m_pNavNet = NULL;
- }
- }
- // If still no nav net . . .
- if (m_pNavNet == NULL)
- {
- // Message depends on user mode.
- if (m_pRealm->m_flags.bEditing)
- {
- rspMsgBox(
- RSP_MB_ICN_STOP | RSP_MB_BUT_OK,
- g_pszAppName,
- g_pszDoofusCannotFindNavNet_EditMode_hu_hu,
- m_u16InstanceId,
- m_u16NavNetID);
- }
- else
- {
- rspMsgBox(
- RSP_MB_ICN_INFO | RSP_MB_BUT_OK,
- g_pszAppName,
- g_pszDoofusCannotFindNavNet_PlayMode_hu_hu,
- m_u16InstanceId,
- m_u16NavNetID);
- }
- // Use the current NavNet.
- m_pNavNet = m_pRealm->GetCurrentNavNet();
- if (m_pNavNet)
- {
- // Remember to reset our ID so this doesn't happen again.
- m_u16NavNetID = m_pNavNet->GetInstanceID();
- }
- }
- }
- // Clear stuck counter
- m_sStuckCounter = 0;
- // Init other stuff
- m_dVel = 0.0;
- m_dRot = 0.0;
- m_dAnimRot = 0.0;
- m_sPrevHeight = (short) m_dY;
- m_sRotateDir = 0;
- // Set up the detection smash
- m_smashDetect.m_pThing = this;
- m_smashDetect.m_bits = 0;
- m_smashDetect.m_sphere.sphere.lRadius = ms_lDetectionRadius;
- // Set up the fire avoidance smash
- m_smashAvoid.m_sphere.sphere.X = m_dX + (rspCos(m_dRot) * ms_lAvoidRadius);
- m_smashAvoid.m_sphere.sphere.Y = m_dY;
- m_smashAvoid.m_sphere.sphere.Z = m_dZ - (rspSin(m_dRot) * ms_lAvoidRadius);
- m_smashAvoid.m_sphere.sphere.lRadius = ms_lAvoidRadius;
- m_smashAvoid.m_bits = 0;
- m_smashAvoid.m_pThing = this;
- // Setup weapon sprite.
- m_spriteWeapon.m_sInFlags = CSprite::InHidden;
- m_sprite.AddChild(&m_spriteWeapon);
- // Start in a neutral state
- m_state = State_Idle;
- // Hello, world.
- m_pRealm->RegisterBirth(m_bCivilian);
- // Note that we've registered our birth so we know later that we need to
- // register our death.
- m_bRegisteredBirth = true;
- // Set tunable values to their doofus defaults
- m_lShootTimeout = ms_lShootTimeoutMin + ((11-m_pRealm->m_flags.sDifficulty) * ms_lShootTimeoutInc);
- m_lRunShootInterval = ms_lRunShootInterval;
- m_lShotReactionTimeout = ms_lShotTimeout;
- return 0;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Called by editor to init new object at specified position
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::EditNew( // Returns 0 if successfull, non-zero otherwise
- short sX, // In: New x coord
- short sY, // In: New y coord
- short sZ) // In: New z coord
- {
- short sResult = 0;
- CCharacter::EditNew(sX, sY, sZ);
- // Since we were just created in the editor, set our nav net to the
- // current one for this realm.
- m_pNavNet = m_pRealm->GetCurrentNavNet();
- m_u16NavNetID = m_pNavNet->GetInstanceID();
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Render - This override version temporarily replaces the m_dRot with the
- // m_dAnimRot so that it draws the person in the desired facing
- // direction.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Render(void)
- {
- double dSaveRotation = m_dRot;
- m_dRot = m_dAnimRot;
- CCharacter::Render();
-
- m_dRot = dSaveRotation;
- CAnim3D* panimWeapon = NULL;
- if (m_panimCur->m_pevent)
- {
- // Get current event.
- U8 u8Event = *( (U8*)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime) ) );
- // If gun not hidden by Randy . . .
- if (u8Event < 10)
- {
- panimWeapon = GetWeaponAnim(m_eWeaponType);
- // If we got an anim . . .
- if (panimWeapon)
- {
- // If this anim is empty (and not intentional) . . .
- if (panimWeapon->m_pmeshes == NULL && m_eWeaponType != TotalIDs)
- {
- // Get fallback weapon, if any.
- panimWeapon = GetWeaponAnim(m_eFallbackWeaponType);
- }
- }
- }
- }
- // If we have a visible weapon . . .
- if (panimWeapon)
- {
- // If we have the necessary components . . .
- if (panimWeapon->m_pmeshes && m_panimCur->m_ptransWeapon)
- {
- // Show weapon sprite.
- m_spriteWeapon.m_sInFlags &= ~CSprite::InHidden;
- m_spriteWeapon.m_pmesh = panimWeapon->m_pmeshes->GetAtTime(m_lAnimTime);
- m_spriteWeapon.m_psop = panimWeapon->m_psops->GetAtTime(m_lAnimTime);
- m_spriteWeapon.m_ptex = panimWeapon->m_ptextures->GetAtTime(m_lAnimTime);
- m_spriteWeapon.m_psphere = panimWeapon->m_pbounds->GetAtTime(m_lAnimTime);
- m_spriteWeapon.m_ptrans = m_panimCur->m_ptransWeapon->GetAtTime(m_lAnimTime);
- }
- else
- {
- // Hide weapon sprite.
- m_spriteWeapon.m_sInFlags |= CSprite::InHidden;
- }
- }
- else
- {
- // Hide weapon sprite.
- m_spriteWeapon.m_sInFlags |= CSprite::InHidden;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Called by editor to render object
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::EditRender(void)
- {
- // In some cases, object's might need to do a special-case render in edit
- // mode because Startup() isn't called. In this case it doesn't matter, so
- // we can call the normal Render().
- Render();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SelectDude
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::SelectDudeBouy(void)
- {
- short sReturn = SUCCESS;
- // CDude* pDude;
- // CBouy* pBouytest;
- if (SelectDude() == SUCCESS)
- {
- CDude* pdude;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
- {
- m_ucDestBouyID = m_pNavNet->FindNearestBouy(pdude->GetX(), pdude->GetZ());
- }
- }
- else
- sReturn = FAILURE;
- /*
- if (m_pRealm->m_asClassNumThings[CThing::CDudeID] > 0)
- {
- pDude = (CDude*) m_pRealm->m_aclassHeads[CThing::CDudeID].GetNext();
- m_ucDestBouyID = m_pNavNet->FindNearestBouy(pDude->GetX(), pDude->GetZ());
- }
- else
- {
- if (m_pNavNet->GetNumNodes() < 1)
- sReturn = FAILURE;
- else
- {
- pBouytest = NULL;
- while (pBouytest == NULL)
- {
- m_ucDestBouyID = GetRandom() % m_pNavNet->GetNumNodes();
- pBouytest = m_pNavNet->GetBouy(m_ucDestBouyID);
- }
- }
- }
- */
- return sReturn;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SelectRandomBouy - make sure it exists before setting it
- ////////////////////////////////////////////////////////////////////////////////
- UCHAR CDoofus::SelectRandomBouy(void)
- {
- UCHAR ucSelect = 0;
- CBouy* pBouy = NULL;
- if (m_pNavNet->GetNumNodes() <= 1)
- return 0;
- while (pBouy == NULL)
- {
- ucSelect = GetRandom() % m_pNavNet->GetNumNodes();
- pBouy = m_pNavNet->GetBouy(ucSelect);
- }
- return ucSelect;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SelectDude - Picks the closest dude from the dude list and assignes it to
- // this enemy's CDude pointer.
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::SelectDude(void)
- {
- // Things::iterator i;
- // Things* pDudes;
- m_idDude = CIdBank::IdNil;
- ULONG ulSqrDistance;
- ULONG ulCurSqrDistance = 0xFFFFFFFF;
- ULONG ulDistX;
- ULONG ulDistZ;
- // pDudes = m_pRealm->m_apthings[CThing::CDudeID];
- CDude* pdude;
- CListNode<CThing>* pDudeList = m_pRealm->m_aclassHeads[CThing::CDudeID].m_pnNext;
- while (pDudeList && pDudeList->m_powner)
- {
- pdude = (CDude*) pDudeList->m_powner;
- // If this dude is not dead . . .
- if (pdude->m_state != State_Dead)
- {
- ulDistX = pdude->m_dX - m_dX;
- ulDistZ = pdude->m_dZ - m_dZ;
- ulSqrDistance = ulDistX * ulDistX + ulDistZ * ulDistZ;
- if (ulSqrDistance < ulCurSqrDistance)
- {
- // This one is closer.
- ulCurSqrDistance = ulSqrDistance;
- m_idDude = pdude->GetInstanceID();
- }
- }
- pDudeList = pDudeList->m_pnNext;
- }
- return (m_idDude != CIdBank::IdNil) ? SUCCESS : FAILURE;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // FindDirection - gives the direction toward the cDude
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::FindDirection()
- {
- short sAngle;
- double dDudeX;
- double dDudeZ;
- double dX;
- double dZ;
- if (m_idDude == CIdBank::IdNil)
- SelectDude();
- if (m_idDude != CIdBank::IdNil)
- {
- CDude* pdude;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
- {
- dDudeX = pdude->GetX();
- dDudeZ = pdude->GetZ();
- dX = dDudeX - m_dX;
- dZ = m_dZ - dDudeZ;
- sAngle = rspATan(dZ, dX);
- }
- else
- sAngle = m_dRot;
- }
- else
- sAngle = m_dRot;
- return sAngle;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Return the square of the distance to the guy (to test for proximity
- ////////////////////////////////////////////////////////////////////////////////
- double CDoofus::SQDistanceToDude()
- {
- double dSquareDistance = 0.0;
- double dX;
- double dZ;
-
- if (m_idDude == CIdBank::IdNil)
- SelectDude();
- if (m_idDude != CIdBank::IdNil)
- {
- CDude* pdude;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
- {
- dX = pdude->GetX() - m_dX;
- dZ = pdude->GetZ() - m_dZ;
- dSquareDistance = (dX * dX) + (dZ * dZ);
- }
- }
- return dSquareDistance;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // AlignToBouy - When timer is up, recalculate direction to bouy
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::AlignToBouy(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- if (lThisTime > m_lAlignTimer || m_bRecentlyStuck)
- {
- // If he got stuck and is now free, pick a new bouy
- if (m_bRecentlyStuck)
- {
- m_bRecentlyStuck = false;
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- }
- }
- }
- m_lAlignTimer = lThisTime + ms_lDefaultAlignTime;
- m_dAnimRot = m_dRot = rspATan(m_dZ - m_sNextZ, m_sNextX - m_dX);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // TryClearDirection - Given an angle, and a variance, checks the given angle
- // to see if it is clear. If it is blocked by walls or fire
- // it will try the max variance in either direction for a
- // clear path and set the angle if it finds one. If these
- // three angles fail, it will return false.
- //
- // Enemy guys can use this to attempt several paths before moving. If it fails
- // then they can fall back on using a random direction, or they could try again
- // with a completely different angle and variance.
- ////////////////////////////////////////////////////////////////////////////////
- bool CDoofus::TryClearDirection(double* pdRot, short sVariance)
- {
- bool bFoundPath = false;
- short sTries = 0;
- short sX, sY, sZ;
- double dRotAttempt = *pdRot;
- CThing* pthing;
- while (!bFoundPath && sTries < 2)
- {
- // Check clear path
- if (IsPathClear( // Returns true, if the entire path is clear.
- // Returns false, if only a portion of the path is clear.
- // (see *psX, *psY, *psZ, *ppthing).
- m_dX, // In: Starting X.
- m_dY, // In: Starting Y.
- m_dZ, // In: Starting Z.
- dRotAttempt, // In: Rotation around y axis (direction on X/Z plane).
- 10, // In: Rate at which to scan ('crawl') path in pixels per
- // iteration.
- // NOTE: We scan terrain using GetFloorAttributes()
- // so small values of sCrawl are not necessary.
- // NOTE: We could change this to a speed in pixels per second
- // where we'd assume a certain frame rate.
- 200, // In: Range on X/Z plane.
- m_smash.m_sphere.sphere.lRadius, // In: Radius of path traverser.
- MaxStepUpThreshold, // In: Max traverser can step up.
- CSmash::Fire, // In: Mask of CSmash bits that would terminate path.
- CSmash::Bad | CSmash::Good, // In: Mask of CSmash bits that would not affect path.
- CSmash::Dead, // In: Mask of CSmash bits that cannot affect path.
- &sX, // Out: Point of intercept, if any, on path.
- &sY, // Out: Point of intercept, if any, on path.
- &sZ, // Out: Point of intercept, if any, on path.
- &pthing, // Out: Thing that intercepted us or NULL, if none.
- &m_smash) // In: Optional CSmash to exclude or NULL, if none.
- == false)
- {
- switch (sTries)
- {
- case 0:
- dRotAttempt = rspMod360(dRotAttempt + (sVariance/2));
- break;
- case 1:
- dRotAttempt = rspMod360(dRotAttempt - sVariance);
- break;
- }
- }
- else
- {
- bFoundPath = true;
- *pdRot = dRotAttempt;
- }
- sTries++;
- }
- return bFoundPath;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // TryClearShot - Works like the TryClearDirection, but doesn't check for
- // fire, only walls. So it knows if it could hit the target
- // from here.
- ////////////////////////////////////////////////////////////////////////////////
- bool CDoofus::TryClearShot(double dRot, short sVariance)
- {
- bool bFoundPath = false;
- // If we have a weapon transform . . .
- if (m_panimCur->m_ptransRigid)
- {
- short sTries = 0;
- short sX, sY, sZ;
- CThing* pthing = NULL;
- double dRotAttempt = dRot;
- // Do a translation to the weapon position so that it is at the correct
- // height for checking
- double dMuzzleX, dMuzzleY, dMuzzleZ;
- GetLinkPoint( // Returns nothing.
- m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime), // In: Transform specifying point.
- &dMuzzleX, // Out: Point speicfied.
- &dMuzzleY, // Out: Point speicfied.
- &dMuzzleZ); // Out: Point speicfied. // Update execution point via link point.
- while (!bFoundPath && sTries < 2)
- {
- // Check clear path
- /*
- // This one checks terrain and for people in the way
- if (IsPathClear(
- (short) (m_dX + dMuzzleX), // Start x position
- (short) (m_dY + dMuzzleY), // Start y position
- (short) (m_dZ + dMuzzleZ), // Start z position
- (short) dRotAttempt, // Angle of shot
- 4, // Crawl rate
- rspSqrt(CDoofus::SQDistanceToDude()),
- 2, // radius of traverser
- 2, // vertical tolerance
- CSmash::Bad, // Bits that would terminate path
- 0, // Bits that would not affect path
- 0, // Bits that cannot affect path
- &sX, // pointer to terminating point
- &sY, // pointer to terminating point
- &sZ, // pointer to terminating point
- &pthing, // handle to terminating target
- &m_smash) // exclude your own smash
- == false)
- */
- // This one only checks terrain
- if (m_pRealm->IsPathClear(
- m_dX + dMuzzleX,
- m_dY + dMuzzleY,
- m_dZ + dMuzzleZ,
- (short) dRotAttempt,
- 3,
- rspSqrt(CDoofus::SQDistanceToDude()),
- 0,
- &sX,
- &sY,
- &sZ)
- == false)
- {
- switch (sTries)
- {
- case 0:
- dRotAttempt = rspMod360(dRotAttempt + (sVariance/2));
- break;
- case 1:
- dRotAttempt = rspMod360(dRotAttempt - sVariance);
- break;
- }
- }
- else
- {
- bFoundPath = true;
- }
- sTries++;
- }
- }
- return bFoundPath;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Update - default implementation of logic for enemy characters
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Update(void)
- {
- if (!m_sSuspend)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- // Check for new messages that may change the state
- ProcessMessages();
- // If he has no network, then he should stick to Guard mode
- // if (m_pNavNet == NULL)
- // m_state = State_Guard;
- // See if there are any pylons nearby that he wants to use.
- if (m_state == State_Idle ||
- m_state == State_Wait ||
- m_state == State_Hunt)
- Logic_PylonDetect();
-
- switch (m_state)
- {
- case State_Guard:
- Logic_Guard();
- break;
- case State_PopBegin:
- Logic_PopBegin();
- break;
- case State_PopWait:
- Logic_PopWait();
- break;
- case State_Popout:
- Logic_Popout();
- break;
- case State_RunShootBegin:
- Logic_RunShootBegin();
- break;
- case State_RunShoot:
- Logic_RunShoot();
- break;
- case State_RunShootWait:
- Logic_RunShootWait();
- break;
- case State_Shoot:
- Logic_Shoot();
- break;
- case State_Hunt:
- Logic_Hunt();
- break;
- case State_HuntNext:
- Logic_MoveNext();
- break;
- case State_Shot:
- Logic_Shot();
- break;
- case State_BlownUp:
- Logic_BlownUp();
- break;
- case State_Burning:
- Logic_Burning();
- break;
- case State_Die:
- Logic_Die();
- break;
- case State_Dead:
- CCharacter::OnDead();
- delete this;
- return;
- break;
- }
- // Determine appropriate position for main smash.
- PositionSmash();
- // Update the smash.
- m_pRealm->m_smashatorium.Update(&m_smash);
-
- m_lPrevTime = lThisTime;
- // Call base class //////////////////////////////////////////////////////
- CCharacter::Update();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Shot - You got shot so check your damage
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Shot(void)
- {
- if (m_stockpile.m_sHitPoints > NEAR_DEATH_HITPOINTS)
- // Restore previous state & go back to what you were doing
- {
- // If your shot animation is over...
- if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
- {
- // Add this check for low health -> retreat
- // if (m_stockpile.m_sHitPoints < DefHitPoints / 2)
- //{
- // m_state = State_Retreat;
- // m_panimCur = &m_animRun;
- // m_lAnimTime = 0;
- // m_dRot = rspMod360(CDoofus::FindDirection() + 180);
- // if (CDoofus::TryClearDirection(&m_dRot, 90) == false)
- // m_dRot = rspMod360(GetRandom());
- //}
- //
- // else // go back to previous state
- m_state = m_ePreviousState;
- m_panimCur = m_panimPrev;
- m_lAnimTime = 0;
- }
- else
- // Else continue shot animation
- {
- m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;
- }
- Cheater();
- }
- else
- // else you are dead or writhing so die
- {
- m_state = State_Die;
- m_panimCur = &m_animDie;
- m_lAnimTime = 0;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_BlownUp - You were blown up so pop up into the air and come down again
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_BlownUp(void)
- {
- // Make him animate
- m_lAnimTime += (m_pRealm->m_time.GetGameTime() - m_lPrevTime);
- if (!WhileBlownUp())
- m_state = State_Dead;
- else
- UpdateFirePosition();
- Cheater();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Burning - You are on fire, so run aroudn burning until you are dead
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Burning(void)
- {
- m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;
- if (!CCharacter::WhileBurning())
- {
- m_state = State_Die;
- m_lAnimTime = 0;
- m_panimCur = &m_animDie;
- }
- Cheater();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Die - You are about to die, so look like it!
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Die(void)
- {
- // Change our smashatorium bits
- // enable this if you want them to always writhe when dead - for testing
- #if 0
- if ((m_smash.m_bits & CSmash::AlmostDead) == 0)
- {
- m_smash.m_bits |= CSmash::AlmostDead;
- m_stockpile.m_sHitPoints = 30;
- }
- #else
- m_smash.m_bits |= CSmash::AlmostDead;
- #endif
- // Send to back.
- m_sLayerOverride = CRealm::LayerSprite1;
- // When the die animation is finsihed, so are you.
- if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
- {
- if (m_stockpile.m_sHitPoints > 0)
- {
- m_state = State_Writhing;
- m_panimCur = &m_animWrithing;
- m_lAnimTime = 0;
- GameMessage msg;
- msg.msg_Writhing.eType = typeWrithing;
- msg.msg_Writhing.sPriority = 0;
- CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
- if (pDemon)
- SendThingMessage(&msg, pDemon);
- }
- else
- {
- m_state = State_Dead;
- }
- }
- else
- {
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // It should drop like a rock if its a throwing weapon or just delete it if it
- // is a launched weapon
- if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
- {
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pweapon);
- }
- else
- {
- pweapon->m_dHorizVel = (GetRandom() % (short) CGrenade::ms_dThrowHorizVel);
- m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
- ShootWeapon();
- }
- }
- m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;
- #if 0 // causes people to spin when they die. --ryan.
- // Rotate him so that he doesn't fall the same way
- // Don't rotate again if he was writhing and has been executed
- if (m_panimCur != &m_animExecuted)
- {
- short sRot = GetRandom() % 2;
- if (sRot & 0x01)
- sRot++;
- if (((short) m_dRot) & 0x01)
- m_dAnimRot = m_dRot = rspMod360(m_dRot + sRot);
- else
- m_dAnimRot = m_dRot = rspMod360(m_dRot - sRot);
- }
- #endif
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Writhing - Rolling around on the ground in pain, waiting to be
- // executed or until the pain is no longer tolerable.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Writhing(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lTimeDifference = lThisTime - m_lPrevTime;
- // Send to back.
- m_sLayerOverride = CRealm::LayerSprite1;
- if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
- {
- if (--m_stockpile.m_sHitPoints <= 0)
- {
- // Die. - Run the executed anim first to transition between the
- // writhing animation and being dead, otherwise some characters will
- // die on their knees.
- m_state = State_Die;
- m_panimCur = &m_animExecuted;
- m_lAnimTime = 0;
- // m_state = State_Dead;
- }
- else
- {
- // Whoa! MOD EQUALS is deep, man!
- m_lAnimTime %= m_panimCur->m_psops->TotalTime();
- // Now you can tune the amount of blood if it gets too thick.
- if ((++m_usBloodCounter % 2) == 0)
- MakeBloodPool();
- if (lThisTime >= m_lTimer)
- {
- // Launch a sample and get the duration of the sample.
- long lSampleDuration;
- PlaySoundWrithing(&lSampleDuration);
- m_lTimer = lThisTime + lSampleDuration + MS_BETWEEN_SAMPLES;
- }
- }
- }
- else
- {
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // It should drop like a rock if its a throwing weapon or just delete it if it
- // is a launched weapon
- if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
- {
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pweapon);
- }
- else
- {
- pweapon->m_dHorizVel = (GetRandom() % (short) CGrenade::ms_dThrowHorizVel);
- m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
- ShootWeapon();
- }
- }
- m_lAnimTime += lTimeDifference;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Guard - Guard the area until a CDude is nearby
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Guard(void)
- {
- long lThisTime;
- long lTimeDifference;
- double dSeconds;
- // Get new time
- lThisTime = m_pRealm->m_time.GetGameTime();
- lTimeDifference = lThisTime - m_lPrevTime;
- m_eCurrentAction = Action_Guard;
- // Calculate the elapsed time in seconds
- dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;
- // if (m_panimCur != &m_animStand)
- // {
- // m_panimCur = &m_animStand;
- // m_lAnimTime = 0;
- // }
- m_dRot = m_dAnimRot = m_dShootAngle = FindDirection();
- RunIdleAnimation();
- // We assume at first that nothing much is going on in the area, so he
- // only evaluates CDude position every once in a while to cut down on
- // the processing.
- if (lThisTime > m_lTimer)
- {
- m_eCurrentAction = Action_Guard;
- m_lTimer = lThisTime + m_lGuardTimeout;
- if (SelectDude() == SUCCESS && SQDistanceToDude() < ms_dGuardDistance)
- {
- if (CDoofus::TryClearShot(m_dRot, 20) == true)
- {
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
- }
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- SelectDude();
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- m_state = State_Shoot;
- m_eNextState = State_Guard;
- m_lTimer = lThisTime + m_lShootTimeout;
- }
- else
- {
- m_lTimer = lThisTime + m_lShootTimeout;
- }
- }
- }
- else
- ReevaluateState();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Patrol - Keep an eye on the CDude and look like you're doing something
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Patrol(void)
- {
- // Turn to face dude but do not attack yet
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Hunt - Find a CDude, use the bouy network to get to him wherever he is
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Hunt(void)
- {
- // Find the destination bouy (one closest to the Dude)
- m_eDestinationState = State_HuntHold;
- SelectDudeBouy();
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_state = State_HuntNext;
- m_lTimer = m_pRealm->m_time.GetGameTime() + ms_lReseekTime;
- }
- else
- {
- // Couldn't get bouy position, so stay put
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- }
- }
- else
- {
- // Couldn't find a bouy so stay put
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_HuntHold - This state means that you were trying to advance to the
- // Dude and have got to the bouy closest to the Dude. The
- // Logic table can either break you out into Engage or
- // whatever, or you will stay in this state where you will
- // be in a guard mode, but always looking to see if you should
- // move to a closer bouy once the Dude changes his position.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_HuntHold(void)
- {
- // Evaluate the logic table suggestion to see if you should change states
- m_eCurrentAction = Action_AdvanceHold;
- if (!ReevaluateState())
- {
- // Check to see if the dude has moved and you should move
- long lThisTime = m_pRealm->m_time.GetGameTime();
- if (lThisTime > m_lTimer)
- {
- m_lTimer = lThisTime + 1000;
- SelectDudeBouy();
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- // See if he should move closer.
- if (m_ucNextBouyID != m_ucDestBouyID)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_lTimer = lThisTime + ms_lReseekTime;
- m_state = State_HuntNext;
- m_eCurrentAction = Action_Advance;
- }
- }
- // Else see if he is close enough to take a shot.
- else
- {
- if (SelectDude() == SUCCESS && SQDistanceToDude() < ms_dGuardDistance)
- {
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
- }
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- SelectDude();
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- m_state = State_Shoot;
- m_eNextState = State_HuntHold;
- m_lTimer = lThisTime + m_lGuardTimeout;
- }
- }
- }
- // Check to see if the dude is nearby and you should shoot at him.
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_HuntNext - going to next bouy in network en route to final destination
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_MoveNext(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lElapsedTime = lThisTime - m_lPrevTime;
- double dSeconds = lElapsedTime / 1000.0;
- double dStartX = m_dX;
- double dStartZ = m_dZ;
- if (!ReevaluateState())
- {
- // Make sure its using the correct animation
- if (m_state == State_MarchNext || m_state == State_WalkNext)
- {
- if (m_panimCur != &m_animWalk)
- {
- m_panimCur = &m_animWalk;
- m_lAnimTime = 0;
- }
- else
- m_lAnimTime += lElapsedTime;
- // Cheat and make him walk slower by continuously
- // resetting the velocity.
- m_dVel = ms_dMarchVelocity;
- }
- // Normally use run
- else
- {
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- else
- m_lAnimTime += lElapsedTime;
- }
- AlignToBouy();
- m_dAcc = ms_dAccUser;
- DeluxeUpdatePosVel(dSeconds);
- // If not moving when you are trying to, rotate
- if (m_dX == dStartX && m_dZ == dStartZ)
- {
- if (m_sRotateDir)
- m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
- else
- m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
- // Reset alignment timer so he doesn't reseek the bouy direction immediately
- m_lAlignTimer = lThisTime + ms_lDefaultAlignTime;
- // Set a flag indicating that you were stuck, so we know what bouy to look
- // for once you get free
- m_bRecentlyStuck = true;
- }
- else
- {
- m_sRotateDir = GetRandom() % 2;
- }
- // See if we are at the next bouy yet
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 5*5) // Was 10*10 for a long time, trying smaller to see if it keeps guys from getting stuck
- {
- UCHAR ucNext = m_pNextBouy->NextRouteNode(m_ucDestBouyID);
- if (ucNext == 0 || ucNext == 255) // you are here or you are lost
- {
- m_state = m_eDestinationState;
- switch (m_state)
- {
- case State_Engage:
- m_eCurrentAction = Action_Engage;
- break;
- case State_Guard:
- m_eCurrentAction = Action_Guard;
- break;
- case State_March:
- m_eCurrentAction = Action_March;
- break;
- case State_Walk:
- m_eCurrentAction = Action_Walk;
- break;
- }
- }
- else
- {
- // If the reseek timer has expired, find the Dude bouy
- // again since he may have moved
- if (lThisTime > m_lTimer || ucNext == 255)
- {
- if (m_state == State_HuntNext)
- m_state = State_Hunt;
- else
- m_lTimer = lThisTime + ms_lReseekTime;
- }
- else
- {
- m_ucNextBouyID = ucNext;
- m_pNextBouy = m_pNavNet->GetBouy(ucNext);
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- }
- }
- }
- }
- // Check for fire in your path so you don't keep moving
- AvoidFire();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PositionSet - Get yourself set up to get into range for shooting
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PositionSet(void)
- {
- if (!ReevaluateState())
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- short sVarRot = GetRandom() % 40;
- double dTargetDist = SQDistanceToDude();
- double dTestAngle;
- double dAngleTurn;
- // If he is out of range now, reset desired range to the middle value.
- if (dTargetDist < ms_dMinFightDistanceSQ || dTargetDist > ms_dMaxFightDistanceSQ)
- dTargetDist = ms_dMedFightDistanceSQ;
- bool bFoundDirection = false;
- short sAttempts = 0;
- while (!bFoundDirection && sAttempts < 8)
- {
- switch (sAttempts)
- {
- case 0:
- if (sVarRot & 0x01)
- dTestAngle = rspMod360(m_dRot + 40 + sVarRot);
- else
- dTestAngle = rspMod360(m_dRot - 40 - sVarRot);
- dAngleTurn = 40 + sVarRot;
- break;
-
- case 1:
- if (sVarRot & 0x01)
- dTestAngle = rspMod360(m_dRot - 40 - sVarRot);
- else
- dTestAngle = rspMod360(m_dRot + 40 + sVarRot);
- // Same dAngleTurn as last time
- break;
-
- case 2:
- if (sVarRot & 0x01)
- dTestAngle = rspMod360(m_dRot + 20 + sVarRot);
- else
- dTestAngle = rspMod360(m_dRot - 20 - sVarRot);
- dAngleTurn = 20 + sVarRot;
- break;
-
- case 3:
- if (sVarRot & 0x01)
- dTestAngle = rspMod360(m_dRot - 20 - sVarRot);
- else
- dTestAngle = rspMod360(m_dRot + 20 + sVarRot);
- // Same dAngleTurn as last time
- break;
-
- case 4:
- dTestAngle = rspMod360(m_dRot + 95);
- break;
-
- case 5:
- dTestAngle = rspMod360(m_dRot - 95);
- break;
- case 6:
- dTestAngle = m_dRot;
- break;
- case 7:
- dTestAngle = rspMod360(m_dRot + 180);
- break;
- }
- if (CDoofus::TryClearDirection(&dTestAngle, 30) == true)
- {
- bFoundDirection = true;
- if (sAttempts < 4)
- {
- m_sDistRemaining = 2 * 3.1415 * rspSqrt(dTargetDist) * ((180.0-(2.0*(dAngleTurn)))/360.0);
- m_lTimer = lThisTime + (1000 * (m_sDistRemaining / ms_dMaxVelFore));
- }
- else
- {
- m_lTimer = 800 + GetRandom() % 400;
- }
- }
- else
- {
- sAttempts++;
- }
- }
- // If you found a clear direction, take it
- if (bFoundDirection)
- {
- m_dAnimRot = m_dRot = dTestAngle;
- // Set new animation
- m_dAcc = ms_dAccUser;
- m_state = State_PositionMove;
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- else
- // else stay where you are.
- {
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- if (m_idDude != CIdBank::IdNil)
- {
- m_state = State_DelayShoot;
- m_lTimer = lThisTime + ms_lDelayShootTimeout;
- m_eNextState = State_PositionSet;
- m_sStuckCounter++;
- m_lAnimTime = 0;
- if (m_sStuckCounter > ms_sStuckLimit)
- {
- m_sStuckCounter = 0;
- m_lStuckTimeout = lThisTime + ms_lStuckRecoveryTime;
- ReevaluateState();
- }
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_DelayShoot - wait for timer to expire then shoot if clear
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_DelayShoot(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- // See if its time to shoot yet
- if (lThisTime > m_lTimer)
- {
- if (TryClearShot(m_dRot, 20) == true && SelectDude() == SUCCESS)
- {
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
- }
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- m_state = State_Shoot;
- }
- else
- // Skip the shot and go back to the correct state
- {
- m_state = m_eNextState;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PositionMove - Move into position then shoot
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PositionMove(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lTimeDifference = lThisTime - m_lPrevTime;
- double dSeconds = lTimeDifference / 1000.0;
- if (lThisTime > m_lTimer)
- {
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- // Check for clear shot before shooting. If not clear, go back to moving
- if (CDoofus::TryClearShot(m_dRot, 20) == true && SelectDude() == SUCCESS)
- {
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
- }
- m_state = State_Shoot;
- m_eNextState = State_PositionSet;
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- }
- // If path is blocked, go on the move again and don't shoot
- else
- {
- m_state = State_PositionSet;
- }
- }
- else
- {
- double dLastPosX = m_dX;
- double dLastPosZ = m_dZ;
- DeluxeUpdatePosVel(dSeconds);
- m_lAnimTime += lTimeDifference;
- // If not moving when intending to, rotate one way or the other
- // to try to avoid the obstacle
- if (m_dVel != 0.0 && dLastPosX == m_dX && dLastPosZ == m_dZ)
- {
- if (((short) m_dRot) & 0x01)
- m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
- else
- m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
- }
- // Avoid fire in your path
- AvoidFire();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Engage - engage the Dude and start fighting at close range
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Engage(void)
- {
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- m_state = State_PositionSet;
- m_sStuckCounter = 0;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_BeSafe - Stay behind the SafeSpot bouy if the Dude is facing you
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_BeSafe(void)
- {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Firefight - Shoot and move
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Firefight(void)
- {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PylonDetect - If you detect that a pylon is near, find out what kind
- // it is and decide if you will use it based on the
- // suggested action. This function is only called when
- // the suggested action requires the use of a pylon.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PylonDetect(void)
- {
- CSmash* pSmashPylon;
- CPylon* pPylon = NULL;
- double dSmashedX = 0.0;
- double dSmashedZ = 0.0;
- m_bPylonSafeAvailable = false;
- m_bPylonPopoutAvailable = false;
- m_bPylonRunShootAvailable = false;
- // Update the detection smash.
- m_smashDetect.m_sphere.sphere.X = m_dX;
- m_smashDetect.m_sphere.sphere.Y = m_dY;
- m_smashDetect.m_sphere.sphere.Z = m_dZ;
- // For now when we detect a pylon we are assuming that this
- // character should use that logic. Later we will have to
- // decide based on the personality numbers and current state.
- if (m_pRealm->m_smashatorium.QuickCheckClosest(&m_smashDetect,
- CSmash::Pylon,
- 0,
- 0,
- &pSmashPylon))
- {
- ASSERT(pSmashPylon);
- pPylon = (CPylon*) pSmashPylon->m_pThing;
- ASSERT(pPylon);
- if (pSmashPylon->m_pThing != this)
- {
- dSmashedX = pSmashPylon->m_sphere.sphere.X;
- dSmashedZ = pSmashPylon->m_sphere.sphere.Z;
- // Check IsPathClear to that thing
- if (m_pRealm->IsPathClear( // Returns true, if the entire path is clear.
- // Returns false, if only a portion of the path is clear.
- // (see *psX, *psY, *psZ).
- (short) m_dX, // In: Starting X.
- (short) m_dY, // In: Starting Y.
- (short) m_dZ, // In: Starting Z.
- 3.0, // In: Rate at which to scan ('crawl') path in pixels per
- // iteration.
- // NOTE: Values less than 1.0 are inefficient.
- // NOTE: We scan terrain using GetHeight()
- // at only one pixel.
- // NOTE: We could change this to a speed in pixels per second
- // where we'd assume a certain frame rate.
- (short) dSmashedX, // In: Destination X.
- (short) dSmashedZ, // In: Destination Z.
- 0, // In: Max traverser can step up.
- NULL, // Out: If not NULL, last clear point on path.
- NULL, // Out: If not NULL, last clear point on path.
- NULL, // Out: If not NULL, last clear point on path.
- true) ) // In: If true, will consider the edge of the realm a path
- // inhibitor. If false, reaching the edge of the realm
- // indicates a clear path.
- {
- switch (pPylon->m_msg.msg_Generic.eType)
- {
- case typeSafeSpot:
- m_bPylonSafeAvailable = true;
- m_pPylonStart = pPylon;
- break;
- case typePopout:
- m_bPylonPopoutAvailable = true;
- m_pPylonStart = pPylon;
- break;
- case typeShootCycle:
- m_bPylonRunShootAvailable = true;
- m_pPylonStart = pPylon;
- break;
- default:
- break;
- }
- }
- }
- }
- else
- {
- // If no pylons were nearby where he started, and he was just idle,
- // then set him up as a stationary guard for now.
- if (m_state == State_Idle || m_state == State_Wait)
- {
- // See if there are bouys, if there aren't any, then stay in Guard mode
- if (m_pNavNet->GetNumNodes() > 1)
- {
- m_state = State_Hunt;
- m_eCurrentAction = Action_Advance;
- }
- else
- {
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_HideBegin - Go to the hiding pylon and then wait once you get there
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_HideBegin(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- // If we'renot using the run animation yet then switch to it
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- else
- m_lAnimTime += lThisTime - m_lPrevTime;
- // Set angle to pylon.
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_dAcc = ms_dAccUser;
- long lElapsedTime = m_pRealm->m_time.GetGameTime() - m_lPrevTime;
- double dSeconds = lElapsedTime / 1000.0;
- DeluxeUpdatePosVel(dSeconds);
- // if close to pylon, go to next state
- // for now just check the square distance, but later, probably use
- // QuickCheckCloses in smash to see if you are there yet.
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 300)
- {
- m_state = State_Hide;
- }
- // Check for fire in your path
- AvoidFire();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Hide - Wait here and hope nobody sees you and blows you away.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Hide(void)
- {
- ReevaluateState();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PopBegin - Once you have detected a nearby Pylon, begin the popout
- // phase here by going to that pylon.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PopBegin(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- double dStartX = m_dX;
- double dStartZ = m_dZ;
- // If we're not using the run animation yet then switch to it
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- else
- m_lAnimTime += lThisTime - m_lPrevTime;
- m_eCurrentAction = Action_Popout;
- // Set angle to pylon.
- if (lThisTime > m_lAlignTimer)
- {
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_lAlignTimer = lThisTime + 100;
- m_sRotateDir = GetRandom() % 2;
- }
- m_dAcc = ms_dAccUser;
- long lElapsedTime = m_pRealm->m_time.GetGameTime() - m_lPrevTime;
- double dSeconds = lElapsedTime / 1000.0;
- DeluxeUpdatePosVel(dSeconds);
- // Check for fire in your path
- AvoidFire();
- // If not moving when meaning to, rotate to avoid obstacle
- if (m_dX == dStartX && m_dZ == dStartZ)
- {
- if (m_sRotateDir)
- m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
- else
- m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
- m_lAlignTimer = lThisTime + 100;
- }
- // if close to pylon, go to next state
- // for now just check the square distance, but later, probably use
- // QuickCheckCloses in smash to see if you are there yet.
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 300)
- {
- m_state = State_PopWait;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PopWait - Wait at covered pylon until a CDude steps on the trigger
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PopWait(void)
- {
- // If the stand animation is not being used yet then switch to it.
- // if (m_panimCur != &m_animStand)
- // {
- // m_panimCur = &m_animStand;
- // m_lAnimTime = 0;
- // }
- RunIdleAnimation();
- if (!ReevaluateState())
- {
- m_eCurrentAction = Action_Popout;
- if (m_pRealm->m_time.GetGameTime() > m_lTimer && m_pPylonStart->Triggered())
- {
- // Set next pylon to go to
- m_sNextX = m_pPylonEnd->GetX();
- m_sNextZ = m_pPylonEnd->GetZ();
- m_state = State_Popout;
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- }
- else
- {
- m_pPylonStart = NULL;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Popout - When at a popout bouy, if the guy is close, popout, shoot,
- // and duck back behind the bouy. If he is not in range, stay
- // behind the bouy and wait for some amount of time. Reevaluate
- // logic choice based on enemy attributes.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Popout(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lElapsedTime = lThisTime - m_lPrevTime;
- double dSeconds = lElapsedTime / 1000.0;
- m_lAnimTime += lElapsedTime;
- m_eCurrentAction = Action_Popout;
- // Go to next pylon
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_dAcc = ms_dAccUser;
- DeluxeUpdatePosVel(dSeconds);
- // Check for fire in your path
- AvoidFire();
- // If you are at the pylon, then go to the next state
- // if close to pylon, go to next state
- // for now just check the square distance, but later, probably use
- // QuickCheckCloses in smash to see if you are there yet.
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq;
- dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 300)
- {
- m_state = State_Shoot;
- // After shooting, go back to beginning
- m_eNextState = State_PopBegin;
- m_lTimer = lThisTime + 2000 + GetRandom() % 2000;
- m_sNextX = m_pPylonStart->GetX();
- m_sNextZ = m_pPylonStart->GetZ();
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- }
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- SelectDude();
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Shoot - run the animation to the proper time and then shoot the weapon
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Shoot(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lTimeDifference = lThisTime - m_lPrevTime;
- // Switch behavior on weapon type.
- switch (m_eWeaponType)
- {
- case CThing::CFirestreamID:
- // If out of fire streams . . .
- if (m_u16IdWeapon == CIdBank::IdNil)
- {
- // Must prepare more fire.
- if (PrepareWeapon() == NULL)
- break;
- }
- // Intentional fall through.
- case CThing::CShotGunID:
- case CThing::CMachineGunID:
- case CThing::CPistolID:
- case CThing::CAssaultWeaponID:
- case CThing::CDoubleBarrelID:
- case CUziID:
- case CAutoRifleID:
- case CSmallPistolID:
- {
- // Get event.
- U8 u8Event = *( (U8*)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime) ) );
- // We don't care about show point for these non-object weapon types.
- // If it's time to fire the weapon . . .
- if (u8Event > 0 && lThisTime > m_lShootTimer)
- {
- // Fire!
- ShootWeapon();
- switch (m_eWeaponType)
- {
- case CThing::CShotGunID:
- case CThing::CDoubleBarrelID:
- m_lShootTimer = lThisTime + 2000;
- break;
- case CUziID:
- case CAutoRifleID:
- case CThing::CMachineGunID:
- case CThing::CAssaultWeaponID:
- case CThing::CFirestreamID:
- m_lShootTimer = lThisTime + 100;
- break;
- case CSmallPistolID:
- case CThing::CPistolID:
- m_lShootTimer = lThisTime + 300;
- break;
- }
- }
- break;
- }
-
- // This is the "I don't have a weapon" case (i.e., NoWeapon).
- case TotalIDs:
- break;
- default:
- if (m_u16IdWeapon != CIdBank::IdNil)
- {
- if (WhileHoldingWeapon(ms_u32CollideBitsInclude, ms_u32CollideBitsDontcare, ms_u32CollideBitsExclude) == true)
- {
- // Note we are done with the child. Now we're just finishing the anim.
- ASSERT(m_u16IdWeapon == CIdBank::IdNil);
- }
- }
- break;
- }
- // See if we are at the end of the animation or we have no weapon . . .
- if (m_lAnimTime > m_panimCur->m_psops->TotalTime() || m_eWeaponType == TotalIDs)
- {
- m_state = m_eNextState;
- }
- // Else advance the animation timer
- else
- {
- m_lAnimTime += lTimeDifference;
- }
-
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_ShootRun - Shoot while running - use the Shoot logic to control
- // the firing of the shot and the animation, and just add on
- // the motion update
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_ShootRun(void)
- {
- double dSeconds = (m_pRealm->m_time.GetGameTime() - m_lPrevTime) / 1000.0;
- Logic_Shoot();
- DeluxeUpdatePosVel(dSeconds);
- // Check for fire in your path
- AvoidFire();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_RunShootBegin - Set up the destination pylon and shooting interval
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_RunShootBegin(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lElapsedTime = lThisTime - m_lPrevTime;
- double dStartX = m_dX;
- double dStartZ = m_dZ;
- // If we're not using the run animation yet, then switch to it
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- else
- m_lAnimTime += lElapsedTime;
- // Set angle to pylon.
- if (lThisTime > m_lAlignTimer)
- {
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_lAlignTimer = lThisTime + 100;
- m_sRotateDir = GetRandom() % 2;
- }
- m_dAcc = ms_dAccUser;
- double dSeconds = lElapsedTime / 1000.0;
- DeluxeUpdatePosVel(dSeconds);
- // Check for fire in your path
- AvoidFire();
- // If not moving when you meant to, start to rotate to avoid obstacles
- if (m_dX == dStartX && m_dZ == dStartZ)
- {
- if (m_sRotateDir)
- m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
- else
- m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
- m_lAlignTimer = lThisTime + 100;
- }
- // If close to pylon, go to nex state
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 300)
- {
- // Find position of destinaiton pylon
- m_sNextX = m_pPylonEnd->GetX();
- m_sNextZ = m_pPylonEnd->GetZ();
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_lTimer = lThisTime + m_lRunShootInterval;
- // m_state = State_RunShoot;
- m_state = State_RunShootWait;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_RunShoot - Run and Shoot logic. When near a ShootCycle bouy, hide,
- // run out and shoot at a Dude, run towards other bouy, stop
- // and shoot again, then duck behind the second bouy. Repeat
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_RunShoot(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- long lElapsedTime = lThisTime - m_lPrevTime;
- // If its not using the run animation, then switch to it
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- // m_lAnimTime = 0;
- m_dAnimRot = m_dRot;
- }
- if (!ReevaluateState())
- {
- // Advance the run animation
- m_lAnimTime += lElapsedTime;
- // If its time to shoot then do it.
- if (lThisTime > m_lTimer)
- {
- m_state = State_ShootRun;
- // After shooting, go back to beginning
- m_eNextState = State_RunShoot;
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- }
- SelectDude();
- short sTargetAngle = FindDirection();
- m_dShootAngle = sTargetAngle;
- short sAngleCCL = rspMod360(sTargetAngle - m_dRot);
- short sAngleCL = rspMod360((360 - sTargetAngle) + m_dRot);
- short sAngleDistance = MIN(sAngleCCL, sAngleCL);
- if (sAngleCCL < sAngleCL)
- // Rotate Counter Clockwise - Use left animations
- {
- if (sAngleDistance > 150)
- {
- m_panimCur = &m_animShootRunBack;
- m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 180));
- }
- else
- {
- if (sAngleDistance > 68)
- {
- m_panimCur = &m_animShootRunL1;
- m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 90));
- }
- else
- {
- if (sAngleDistance > 23)
- {
- m_panimCur = &m_animShootRunL0;
- m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 45));
- }
- else
- {
- m_panimCur = &m_animShootRun;
- m_dAnimRot = rspMod360(m_dRot + sAngleDistance);
- }
- }
- }
- }
- else
- // Rotate Clockwise - Use right animations
- {
- if (sAngleDistance > 150)
- {
- m_panimCur = &m_animShootRunBack;
- m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 180));
- }
- else
- {
- if (sAngleDistance > 68)
- {
- m_panimCur = &m_animShootRunR1;
- m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 90));
- }
- else
- {
- if (sAngleDistance > 23)
- {
- m_panimCur = &m_animShootRunR0;
- m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 45));
- }
- else
- {
- m_panimCur = &m_animShootRun;
- m_dAnimRot = rspMod360(m_dRot - sAngleDistance);
- }
- }
- }
- }
- m_lTimer = lThisTime + m_lRunShootInterval;
- m_lAnimTime = 0;
- }
- else
- {
- // Set angle to pylon.
- m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
- m_dAcc = ms_dAccUser;
- double dSeconds = lElapsedTime / 1000.0;
- DeluxeUpdatePosVel(dSeconds);
- // Check for fire in your path
- AvoidFire();
- // if close to pylon, go to next state
- // for now just check the square distance, but later, probably use
- // QuickCheckCloses in smash to see if you are there yet.
- double dX = m_dX - (double) m_sNextX;
- double dZ = m_dZ - (double) m_sNextZ;
- double dsq = (dX * dX) + (dZ * dZ);
- if (dsq < 300)
- {
- m_state = State_RunShootWait;
- m_lTimer = lThisTime + m_lGuardTimeout;
- // Make him face the other way.
- m_dAnimRot = m_dRot = FindAngleTo(m_pPylonEnd->GetX(), m_pPylonEnd->GetZ());
- }
- }
- }
- else
- {
- m_pPylonStart = NULL;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_RunShootWait - Wait at the endpoint before going again.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_RunShootWait(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
-
- RunIdleAnimation();
- // If the waiting is over and its triggered
- if (lThisTime > m_lTimer && m_pPylonStart->Triggered())
- {
- CPylon* pPylonTemp = m_pPylonStart;
- m_pPylonStart = m_pPylonEnd;
- m_pPylonEnd = pPylonTemp;
- m_state = State_RunShoot;
- m_sNextX = m_pPylonStart->GetX();
- m_sNextZ = m_pPylonStart->GetZ();
- m_dVel = 0.0;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Retreat - Once you are low on health, you may choose this logic to
- // run away from the Dude, taking shelter wherever you can.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Retreat(void)
- {
- // Get your closest bouy, pick a random destination bouy, then
- // start traversing 3 away from your current one and set that
- // as the destination bouy, then switch to State_MoveNext
- m_eDestinationState = State_Guard;
- m_ucDestBouyID = SelectRandomBouy();
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_state = State_MoveNext;
- m_eCurrentAction = Action_Retreat;
- }
- else
- {
- // Couldn't find bouy so stay engaged
- m_state = State_Engage;
- m_eCurrentAction = Action_Engage;
- }
- }
- else
- {
- // Couldn't find a bouy so just stay in engage.
- m_state = State_Engage;
- m_eCurrentAction = Action_Engage;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PanicBegin - Pick a random bouy to run to and set animation
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PanicBegin(void)
- {
- // See if dude is still alive
- SelectDude();
- // Switch to correct animation if not already.
- if (m_panimCur != &m_animRun)
- {
- m_panimCur = &m_animRun;
- m_lAnimTime = 0;
- }
- m_eDestinationState = State_PanicContinue;
- m_bPanic = true;
- m_ucDestBouyID = SelectRandomBouy();
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_state = State_MoveNext;
- }
- else
- {
- // Couldn't find bouy so stay engaged
- m_state = State_PanicBegin;
- }
- }
- else
- {
- // Couldn't find a bouy so just stay in engage.
- m_state = State_PanicBegin;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_PanicContinue - End of panic state - pick another bouy
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_PanicContinue(void)
- {
- m_state = State_PanicBegin;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_MarchBegin - Pick an endpoint and march to it.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_MarchBegin(void)
- {
- // See if Dude is still alive
- SelectDude();
- // Switch to correct animation if not already.
- if (m_panimCur != &m_animWalk)
- {
- m_panimCur = &m_animWalk;
- m_lAnimTime = 0;
- }
- m_eDestinationState = State_March;
- m_eCurrentAction = Action_March;
- // Pick an endpoint that we are not already at.
- if (m_ucDestBouyID == m_ucSpecialBouy0ID)
- m_ucDestBouyID = m_ucSpecialBouy1ID;
- else
- m_ucDestBouyID = m_ucSpecialBouy0ID;
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_state = State_MarchNext;
- }
- // Couldn't find a bouy - try guard mode
- else
- {
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- }
- }
- // Couldn't find a bouy - try guard mode
- else
- {
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_WalkBegin - Pick a random bouy to run to and set animation
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_WalkBegin(void)
- {
- // See if dude is still alive
- SelectDude();
- m_eDestinationState = State_WalkContinue;
- m_ucDestBouyID = SelectRandomBouy();
- m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
- if (m_ucNextBouyID > 0)
- {
- m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
- if (m_pNextBouy != NULL)
- {
- m_sNextX = m_pNextBouy->GetX();
- m_sNextZ = m_pNextBouy->GetZ();
- m_state = State_WalkNext;
- }
- else
- {
- // Couldn't find bouy so stay engaged
- m_state = State_Walk;
- }
- }
- else
- {
- // Couldn't find a bouy so just stay in engage.
- m_state = State_Walk;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_WalkContinue - End of walk state - pick another bouy
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_WalkContinue(void)
- {
- m_state = State_WalkBegin;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_Helping - Help out your buddies without moving around and screwing
- // up your positioning
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_Helping(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- if (lThisTime > m_lTimer)
- {
- m_eCurrentAction = Action_Help;
- m_lTimer = lThisTime + ms_lHelpingTimeout;
- if (SelectDude() == SUCCESS && SQDistanceToDude() <= ms_lYellRadius)
- {
- if (CDoofus::TryClearShot(m_dRot, 20) == true)
- {
- CWeapon* pweapon = PrepareWeapon();
- if (pweapon != NULL)
- {
- // Keep it hidden, for now.
- pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
- pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
- }
- m_panimCur = &m_animShoot;
- m_lAnimTime = 0;
- SelectDude();
- m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
- m_state = State_Shoot;
- m_eNextState = State_Helping;
- m_lTimer = lThisTime + ms_lHelpingTimeout;
- }
- else
- {
- m_lTimer = lThisTime + ms_lHelpingTimeout;
- }
- }
- }
- else
- ReevaluateState();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Logic_AvoidFire - Wait for fire danger to pass before going back to your
- // previous state. This state is set by AvoidFire function
- // when there is a fire in your path.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::Logic_AvoidFire(void)
- {
- if (AvoidFire())
- {
- // Check for a different state
- ReevaluateState();
- }
- else
- // Go back to your previous state
- {
- m_state = m_ePreviousState;
- m_panimCur = m_panimPrev;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // AvoidFire - checks for fire in your path, returns true if there is a fire
- // danger. If you are not already in the fire avoidance state,
- // it will save your previous state info and change your state.
- ////////////////////////////////////////////////////////////////////////////////
- bool CDoofus::AvoidFire(void)
- {
- bool bFireDanger = false;
- CSmash* pSmashed = NULL;
- // Change this to quick check closest
- if (m_pRealm->m_smashatorium.QuickCheck(&m_smashAvoid,
- CSmash::Fire,
- CSmash::Good | CSmash::Bad,
- 0,
- &pSmashed))
- {
- bFireDanger = true;
- // If its not already avoiding fire, switch to this safe state
- // and save the previous state
- if (m_state != State_AvoidFire)
- {
- m_ePreviousState = m_state;
- m_panimPrev = m_panimCur;
- m_state = State_AvoidFire;
- m_panimCur = &m_animStand;
- }
- }
- return bFireDanger;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // YellForHelp - Call this when you get shot and it will alert other enemies in
- // the area within line of sight that you are in trouble. Then
- // they can choose to react.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::YellForHelp(void)
- {
- CSmash* pSmashed = NULL;
- CSmash smashYell;
- double dSmashedX;
- double dSmashedZ;
- GameMessage msg;
- msg.msg_Help.eType = typeHelp;
- msg.msg_Help.sPriority = 0;
- // Set up the yell smash
- smashYell.m_sphere.sphere.X = m_dX;
- smashYell.m_sphere.sphere.Y = m_dY;
- smashYell.m_sphere.sphere.Z = m_dZ;
- smashYell.m_sphere.sphere.lRadius = ms_lYellRadius;
- smashYell.m_bits = 0;
- smashYell.m_pThing = this;
- m_pRealm->m_smashatorium.QuickCheckReset(&smashYell,
- CSmash::Character | CSmash::Bad,
- 0,
- CSmash::Good);
- while (m_pRealm->m_smashatorium.QuickCheckNext(&pSmashed))
- {
- ASSERT(pSmashed->m_pThing);
- if (pSmashed->m_pThing != this)
- {
- dSmashedX = pSmashed->m_sphere.sphere.X;
- dSmashedZ = pSmashed->m_sphere.sphere.Z;
- // Check IsPathClear to that thing
- if (m_pRealm->IsPathClear( // Returns true, if the entire path is clear.
- // Returns false, if only a portion of the path is clear.
- // (see *psX, *psY, *psZ).
- (short) m_dX, // In: Starting X.
- (short) m_dY, // In: Starting Y.
- (short) m_dZ, // In: Starting Z.
- 3.0, // In: Rate at which to scan ('crawl') path in pixels per
- // iteration.
- // NOTE: Values less than 1.0 are inefficient.
- // NOTE: We scan terrain using GetHeight()
- // at only one pixel.
- // NOTE: We could change this to a speed in pixels per second
- // where we'd assume a certain frame rate.
- (short) dSmashedX, // In: Destination X.
- (short) dSmashedZ, // In: Destination Z.
- 0, // In: Max traverser can step up.
- NULL, // Out: If not NULL, last clear point on path.
- NULL, // Out: If not NULL, last clear point on path.
- NULL, // Out: If not NULL, last clear point on path.
- true) ) // In: If true, will consider the edge of the realm a path
- // inhibitor. If false, reaching the edge of the realm
- // indicates a clear path.
- {
- SendThingMessage(&msg, pSmashed->m_pThing);
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ReevaluateState - Based on parameters, some randomness, current state etc,
- // come up with a new state.
- //
- // Returns true if the state changed
- ////////////////////////////////////////////////////////////////////////////////
- bool CDoofus::ReevaluateState(void)
- {
- bool bChanged = false;
- // If its not already doing the suggested Action, then attempt to switch to it.
- // and if its not in one of the final death states
- if (m_eSuggestedAction != m_eCurrentAction &&
- m_state != State_Burning &&
- m_state != State_BlownUp &&
- m_state != State_Die &&
- m_state != State_Dead &&
- m_state != State_Writhing)
- {
- switch (m_eSuggestedAction)
- {
- case Action_Guard:
- if (m_panimCur != &m_animStand &&
- m_panimCur != &m_animCrouch &&
- m_panimCur != &m_animSearch)
- {
- m_panimCur = &m_animStand;
- m_lAnimTime = 0;
- }
- m_state = State_Guard;
- m_eCurrentAction = Action_Guard;
- bChanged = true;
- break;
- case Action_Advance:
- if (m_eCurrentAction != Action_AdvanceHold)
- {
- m_state = State_Hunt;
- m_eCurrentAction = Action_Advance;
- bChanged = true;
- }
- break;
- case Action_Retreat:
- m_state = State_Retreat;
- m_eCurrentAction = Action_Retreat;
- bChanged = true;
- break;
- case Action_Engage:
- m_state = State_Engage;
- m_eCurrentAction = Action_Engage;
- bChanged = true;
- break;
- case Action_Walk:
- m_state = State_WalkBegin;
- m_eCurrentAction = Action_Walk;
- bChanged = true;
- break;
- case Action_March:
- m_state = State_March;
- m_eCurrentAction = Action_March;
- bChanged = true;
- break;
- case Action_Panic:
- m_state = State_PanicBegin;
- m_eCurrentAction = Action_Panic;
- bChanged = true;
- break;
- case Action_Help:
- m_state = State_Helping;
- m_eCurrentAction = Action_Help;
- bChanged = true;
- // Force it to shoot right away.
- m_lTimer = 0;
- break;
- // In these cases, if a pylon of their type is detected, it
- // will switch them to the correct state, otherwise it will leave
- // the action and the state unchanged.
- case Action_Popout:
- Logic_PylonDetect();
- if (m_bPylonPopoutAvailable)
- {
- m_pPylonEnd = m_pPylonStart->GetPylon(m_pPylonStart->m_msg.msg_Popout.ucIDNext);
- ASSERT(m_pPylonEnd != NULL);
- m_sNextX = m_pPylonStart->GetX();
- m_sNextZ = m_pPylonStart->GetZ();
- m_state = State_PopBegin;
- m_eCurrentAction = Action_Popout;
- bChanged = true;
- }
- break;
- case Action_RunShoot:
- Logic_PylonDetect();
- if (m_bPylonRunShootAvailable)
- {
- m_pPylonEnd = m_pPylonStart->GetPylon(m_pPylonStart->m_msg.msg_ShootCycle.ucIDNext);
- ASSERT(m_pPylonEnd != NULL);
- m_sNextX = m_pPylonStart->GetX();
- m_sNextZ = m_pPylonStart->GetZ();
- m_state = State_RunShootBegin;
- m_eCurrentAction = Action_RunShoot;
- bChanged = true;
- }
- break;
- case Action_Hide:
- Logic_PylonDetect();
- if (m_bPylonSafeAvailable)
- {
- m_sNextX = m_pPylonStart->GetX();
- m_sNextZ = m_pPylonStart->GetZ();
- m_state = State_HideBegin;
- m_eCurrentAction = Action_Hide;
- bChanged = true;
- }
- break;
- }
- }
- return bChanged;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Message handlers
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- // Shot Message
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnShotMsg(Shot_Message* pMessage)
- {
- // If he is already shot, just deduct hit points for
- // the additional bullets
- m_stockpile.m_sHitPoints -= pMessage->sDamage;
- if (m_state != State_Burning &&
- m_state != State_BlownUp &&
- m_state != State_Die &&
- m_state != State_Dead)
- {
- // Alert other in the area that you are being attacked.
- YellForHelp();
- // Set panic so that victims will panic after being shot
- m_bPanic = true;
- // If he got shot while he was writhing, then he was executed
- // so switch to that animation.
- if (m_state == State_Writhing)
- {
- m_stockpile.m_sHitPoints = 0;
- m_panimCur = &m_animExecuted;
- m_lAnimTime = 0;
- AbortSample(m_siPlaying);
- }
- else
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- if (lThisTime > m_lShotTimeout)
- {
- m_lShotTimeout = lThisTime + m_lShotReactionTimeout;
- PlaySoundShot();
- if ( m_state != State_Shot &&
- m_state != State_Writhing)
- {
- m_ePreviousState = m_state;
- m_panimPrev = m_panimCur;
- m_state = State_Shot;
- m_panimCur = &m_animShot;
- m_lAnimTime = 0;
- }
- }
- // Give him time to escape and move without starting the full
- // shot animation again
- else
- {
- PlaySoundShot();
- }
- }
- }
- // Let's have some blood no matter what!
- CCharacter::OnShotMsg(pMessage);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Explosion message
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnExplosionMsg(Explosion_Message* pMessage)
- {
- if (
- m_state != State_BlownUp &&
- m_state != State_Die &&
- m_state != State_Dead)
- {
- CCharacter::OnExplosionMsg(pMessage);
-
- // PlaySample(g_smidBlownupYell);
- PlaySoundBlownup();
- m_ePreviousState = m_state;
- m_state = State_BlownUp;
- m_panimPrev = m_panimCur;
- m_panimCur = &m_animDie;
- m_lAnimTime = 0;
- m_stockpile.m_sHitPoints = 0;
- m_lTimer = m_pRealm->m_time.GetGameTime() + 6000;
- GameMessage msg;
- msg.msg_Explosion.eType = typeExplosion;
- msg.msg_Explosion.sPriority = 0;
- CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
- if (pDemon)
- SendThingMessage(&msg, pDemon);
- // If he has a weapon ready, either get rid of it or shoot it off randomly
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // It should drop like a rock if its a throwing weapon or just delete it if it
- // is a launched weapon
- if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
- {
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pweapon);
- }
- else
- {
- pweapon->m_dHorizVel = (GetRandom() % (short) CGrenade::ms_dThrowHorizVel);
- m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
- ShootWeapon();
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Burning message
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnBurnMsg(Burn_Message* pMessage)
- {
- CCharacter::OnBurnMsg(pMessage);
- m_stockpile.m_sHitPoints -= pMessage->sDamage;
- if (m_state != State_Burning &&
- m_state != State_BlownUp &&
- m_state != State_Die &&
- m_state != State_Dead)
- {
- PlaySoundBurning();
- if (m_state == State_Writhing)
- {
- m_sBrightness = BURNT_BRIGHTNESS;
- // May also have to create a small fire at this position
- m_stockpile.m_sHitPoints = 0;
- m_state = State_Dead;
- }
- else
- {
- m_ePreviousState = m_state;
- m_state = State_Burning;
- // If he's not on fire already, switch to on fire animation
- if (m_panimCur != &m_animOnfire)
- {
- m_panimPrev = m_panimCur;
- m_panimCur = &m_animOnfire;
- m_lAnimTime = 0;
- GameMessage msg;
- msg.msg_Burn.eType = typeBurn;
- msg.msg_Burn.sPriority = 0;
- CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
- if (pDemon)
- SendThingMessage(&msg, pDemon);
- }
- }
- // If he has a weapon ready, either get rid of it or shoot it off randomly.
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // It should drop like a rock if its a throwing weapon or just delete it if it
- // is a launched weapon
- if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
- {
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pweapon);
- }
- else
- {
- pweapon->m_dHorizVel = (GetRandom() % (short) CGrenade::ms_dThrowHorizVel);
- m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
- ShootWeapon();
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles an ObjectDelete_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnDeleteMsg( // Returns nothing.
- ObjectDelete_Message* pdeletemsg) // In: Message to handle.
- {
- CCharacter::OnDeleteMsg(pdeletemsg);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a Help_Message
- // (virtual)
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnHelpMsg( // Returns nothing
- Help_Message* phelpmsg) // In: Message to handle
- {
- m_lLastHelpCallTime = m_pRealm->m_time.GetGameTime();
- // Victium take the yell for help as a indication to panic.
- m_bPanic = true;
-
- }
- ////////////////////////////////////////////////////////////////////////////////
- // OnDead - override of CCharacter version
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::OnDead(void)
- {
- CCharacter::OnDead();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Prepare current weapon (ammo).
- // This should be done when the character starts its shoot animation.
- // (virtual (overriden here) ).
- ////////////////////////////////////////////////////////////////////////////////
- CWeapon* CDoofus::PrepareWeapon(void) // Returns the weapon ptr or NULL.
- {
- // Play sound even if we have no weapon...seems like they're threatening him.
- PlaySoundShooting();
- CWeapon* pweapon;
- // If we have a weapon . . .
- if (m_eWeaponType != TotalIDs)
- {
- pweapon = CCharacter::PrepareWeapon();
- }
- else
- {
- pweapon = NULL;
- }
- return pweapon;
- };
- ////////////////////////////////////////////////////////////////////////////////
- // ShootWeapon - Overloaded version to use the Shooting Angle
- ////////////////////////////////////////////////////////////////////////////////
- CWeapon* CDoofus::ShootWeapon(CSmash::Bits bitsInclude,
- CSmash::Bits bitsDontcare,
- CSmash::Bits bitsExclude)
- {
- CWeapon* pWeapon = NULL;
- double dSaveDirection = m_dRot;
- // Don't adjust shooting angle if shooting on the run
- if (m_state != State_ShootRun)
- {
- // Tuned for difficulty level - if the game is in the harder
- // levels, the enemies will tune their aim at the last second
- // before shooting, and then also depending on the difficulty,
- // they will add different amounts of random misses
- // Easy levels = 1, 2, 3
- // Medium levels = 4, 5, 6
- // Hard levels = 7, 8, 9, 10, 11
- switch (m_pRealm->m_flags.sDifficulty)
- {
- case 0:
- case 1:
- m_dRot = rspMod360(m_dShootAngle - 8 + (GetRandom() % 17));
- break;
- case 2:
- m_dRot = rspMod360(m_dShootAngle - 7 + (GetRandom() % 15));
- break;
- case 3:
- m_dRot = rspMod360(m_dShootAngle - 6 + (GetRandom() % 13));
- break;
- case 4:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 6 + (GetRandom() % 13));
- break;
- case 5:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 5 + (GetRandom() % 11));
- break;
- case 6:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 4 + (GetRandom() % 9));
- break;
- case 7:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 6 + (GetRandom() % 13));
- break;
- case 8:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 5 + (GetRandom() % 11));
- break;
- case 9:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 4 + (GetRandom() % 9));
- break;
- case 10:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 3 + (GetRandom() % 7));
- break;
- case 11:
- default:
- m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = FindDirection();
- break;
- }
- }
- else
- {
- m_dRot = m_dShootAngle;
- }
- /*
- // Allow rockets to hit other enemies, but still not the special barrel type
- if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID)
- {
- bitsExclude &= ~CSmash::Bad;
- }
- */
- pWeapon = CCharacter::ShootWeapon(bitsInclude, bitsDontcare, bitsExclude);
- if (pWeapon)
- pWeapon->SetDetectionBits(CSmash::Character | CSmash::Fire,
- 0,
- CSmash::Bad | CSmash::Ducking | CSmash::AlmostDead);
- m_dRot = dSaveDirection;
- return pWeapon;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // RunIdleAnimation - Check the idle timer and pick an idle animation to run
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::RunIdleAnimation(void)
- {
- long lThisTime = m_pRealm->m_time.GetGameTime();
- if (m_panimCur == &m_animStand)
- {
- if (lThisTime > m_lIdleTimer)
- {
- if (GetRandom() % 3 == 0)
- {
- // Stay with current animation
- m_lIdleTimer = lThisTime + 2000 + GetRandom() % 2000;
- }
- else
- {
- // Go to the Crouch animaiton
- m_panimCur = &m_animCrouch;
- m_lAnimTime = 0;
- m_bAnimUp = true;
- m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
- }
- }
- }
- else
- {
- if (m_panimCur == &m_animCrouch)
- {
- // Animate forward or backward depending on if he is standing up or
- if (m_bAnimUp)
- {
- m_lAnimTime += lThisTime - m_lPrevTime;
- // See if it is time to switch yet
- if (lThisTime > m_lIdleTimer)
- {
- if ((GetRandom() % 3 ) == 0)
- // Start standing up to use the stand animation
- {
- // Start to stand up - run the animation backwards
- m_lAnimTime = m_panimCur->m_psops->TotalTime();
- m_bAnimUp = false;
- }
- else
- {
- // Pick either crouch still or search
- if (GetRandom() % 2)
- {
- // Start search animation
- m_panimCur = &m_animSearch;
- m_lAnimTime = 0;
- m_lIdleTimer = lThisTime + 1000 + GetRandom() % 1500;
- }
- else
- {
- // Stay with still crouch animation
- m_lIdleTimer = lThisTime + 2000 + GetRandom() % 2000;
- }
- }
- }
- }
- else
- {
- m_lAnimTime -= lThisTime - m_lPrevTime;
- // See if it is done standing yet. If so, you have
- // already previously decided to end the crouch animation
- // so now pick the stand animation
- if (m_lAnimTime < 0)
- {
- m_panimCur = &m_animStand;
- m_lAnimTime = 0;
- m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
- }
- }
- }
- else
- {
- if (m_panimCur == &m_animSearch)
- {
- // Animate the search
- m_lAnimTime += lThisTime - m_lPrevTime;
- // See if its time to switch yet
- if (lThisTime > m_lIdleTimer)
- {
- switch (GetRandom() % 3)
- {
- case 0: // Stay with current animation
- m_lIdleTimer = lThisTime + 1000 + GetRandom() % 1000;
- break;
- case 1: // Go to Crouch animation
- m_panimCur = &m_animCrouch;
- m_bAnimUp = true;
- m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
- break;
- case 2: // Go to stand - use Crouch backwards to get up
- m_panimCur = &m_animCrouch;
- m_bAnimUp = false;
- m_lAnimTime = m_panimCur->m_psops->TotalTime();
- m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2500;
- break;
- }
- }
- }
- // Start the stand animation since it isn't currently using any idle animations.
- else
- {
- m_panimCur = &m_animStand;
- m_lIdleTimer = lThisTime + 3000;
- m_lAnimTime = 0;
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Position our smash approriately.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::PositionSmash(void)
- {
- // If not in writhing state . . .
- if (m_state != State_Writhing)
- {
- // Update sphere.
- m_smash.m_sphere.sphere.X = m_dX;
- m_smash.m_sphere.sphere.Y = m_dY + m_sprite.m_sRadius;
- m_smash.m_sphere.sphere.Z = m_dZ;
- m_smash.m_sphere.sphere.lRadius = m_sprite.m_sRadius;
- }
- else
- {
- // If we have the execution points . . .
- if (m_ptransExecutionTarget)
- {
- // Compute link point for execution target.
- double dVitalOrganX;
- double dVitalOrganY;
- double dVitalOrganZ;
- GetLinkPoint( // Returns nothing.
- m_ptransExecutionTarget->GetAtTime(m_lAnimTime), // In: Transform specifying point.
- &dVitalOrganX, // Out: Point speicfied.
- &dVitalOrganY, // Out: Point speicfied.
- &dVitalOrganZ); // Out: Point speicfied. // Update execution point via link point.
- // Offset from hotspot to set collision sphere position.
- m_smash.m_sphere.sphere.X = m_dX + dVitalOrganX;
- m_smash.m_sphere.sphere.Y = m_dY + dVitalOrganY;
- m_smash.m_sphere.sphere.Z = m_dZ + dVitalOrganZ;
- m_smash.m_sphere.sphere.lRadius = m_sprite.m_sRadius;
- }
- else
- {
- ASSERT(m_dRot >= 0);
- ASSERT(m_dRot < 360);
- // Try to find center.
- // Let's go a radius up their torso. Say... .
- // This only looks decent if m_dRot is the direction they fell which is
- // not always the case.
- short sPseudoCenter = m_sprite.m_sRadius;
- m_smash.m_sphere.sphere.X = m_dX + COSQ[short(m_dRot)] * sPseudoCenter;
- m_smash.m_sphere.sphere.Y = m_dY + m_sprite.m_sRadius;
- m_smash.m_sphere.sphere.Z = m_dZ - SINQ[short(m_dRot)] * sPseudoCenter;
- m_smash.m_sphere.sphere.lRadius = m_sprite.m_sRadius;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // WhileHoldingWeapon
- //
- // Override for the character version which will just re-aim the guy during the
- // shoot-prepare frames. Otherwise, guys like the rocketman which has a long
- // prepare animation, quickly flip around in ShootWeapon if the target has moved
- // between PrepareWeapon and ShootWeapon.
- //
- ////////////////////////////////////////////////////////////////////////////////
- bool CDoofus::WhileHoldingWeapon( // Returns true when weapon is released.
- U32 u32BitsInclude, // In: Collision bits passed to ShootWeapon
- U32 u32BitsDontcare, // In: Collision bits passed to ShootWeapon
- U32 u32BitsExclude) // In: Collision bits passed to ShootWeapon
- {
- bool bResult = true;
- // If they haven't shot the weapon yet, adjust their aiming if the difficulty
- // level allows them to re-aim after preparing the weapon
- if (!(CCharacter::WhileHoldingWeapon(u32BitsInclude, u32BitsDontcare, u32BitsExclude)))
- {
- bResult = false;
- // Don't adjust shooting angle if shooting on the run
- if (m_state != State_ShootRun)
- {
- // Tuned for difficulty level - if the game is in the harder
- // levels, the enemies will tune their aim at the last second
- // before shooting, and then also depending on the difficulty,
- // they will add different amounts of random misses
- // Easy levels = 1, 2, 3
- // Medium levels = 4, 5, 6
- // Hard levels = 7, 8, 9, 10, 11
- switch (m_pRealm->m_flags.sDifficulty)
- {
- // Easy levels don't re-aim after preparing weapon, so leve it alone
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- break;
- // More difficult levels allow constant re-aiming.
- case 7:
- case 8:
- case 9:
- case 10:
- case 11:
- default:
- m_dRot = m_dAnimRot = m_dShootAngle = rspMod360(FindDirection());
- break;
- }
- }
- }
- return bResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // A way for the base class to get resources. If you are going to use
- // any of this class's resources (e.g., ms_aanimWeapons[]), call this
- // when getting your resources.
- ////////////////////////////////////////////////////////////////////////////////
- short CDoofus::GetResources(void)
- {
- short sResult = 0;
- // If the ref count was 0 . . .
- if (ms_lWeaponResRefCount++ == 0)
- {
- // Get the actual resources.
- short i;
- short sLoadResult;
- for (i = 0; i < NumWeaponTypes; i++)
- {
- // If this weapon has a visible resource . . .
- if (ms_awdWeapons[i].pszResName)
- {
- sLoadResult = ms_aanimWeapons[i].Get(
- ms_awdWeapons[i].pszResName,
- NULL,
- NULL,
- NULL,
- RChannel_LoopAtStart | RChannel_LoopAtEnd);
- if (sLoadResult != 0)
- {
- TRACE("GetResources(): Failed to load weapon resource \"%s\".\n",
- ms_awdWeapons[i].pszResName);
- sResult = -1;
- }
- }
- }
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // A way for the base class to release resources. If you are going to use
- // any of this class's resources (e.g., ms_aanimWeapons[]), call this
- // when releasing your resources.
- ////////////////////////////////////////////////////////////////////////////////
- void CDoofus::ReleaseResources(void)
- {
- ASSERT(ms_lWeaponResRefCount > 0);
- // If the ref count hits zero with this release . . .
- if (--ms_lWeaponResRefCount == 0)
- {
- // Release the actual resources.
- short i;
- for (i = 0; i < NumWeaponTypes; i++)
- {
- if (ms_aanimWeapons[i].m_pmeshes)
- {
- ms_aanimWeapons[i].Release();
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|