Mover.cpp 109 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. // a mover will update any gui entities in it's target list with
  24. // a key/val pair of "mover" "state" from below.. guis can represent
  25. // realtime info like this
  26. // binary only
  27. static const char *guiBinaryMoverStates[] = {
  28. "1", // pos 1
  29. "2", // pos 2
  30. "3", // moving 1 to 2
  31. "4" // moving 2 to 1
  32. };
  33. /*
  34. ===============================================================================
  35. idMover
  36. ===============================================================================
  37. */
  38. const idEventDef EV_FindGuiTargets( "<FindGuiTargets>", NULL );
  39. const idEventDef EV_TeamBlocked( "<teamblocked>", "ee" );
  40. const idEventDef EV_PartBlocked( "<partblocked>", "e" );
  41. const idEventDef EV_ReachedPos( "<reachedpos>", NULL );
  42. const idEventDef EV_ReachedAng( "<reachedang>", NULL );
  43. const idEventDef EV_PostRestore( "<postrestore>", "ddddd" );
  44. const idEventDef EV_StopMoving( "stopMoving", NULL );
  45. const idEventDef EV_StopRotating( "stopRotating", NULL );
  46. const idEventDef EV_Speed( "speed", "f" );
  47. const idEventDef EV_Time( "time", "f" );
  48. const idEventDef EV_AccelTime( "accelTime", "f" );
  49. const idEventDef EV_DecelTime( "decelTime", "f" );
  50. const idEventDef EV_MoveTo( "moveTo", "e" );
  51. const idEventDef EV_MoveToPos( "moveToPos", "v" );
  52. const idEventDef EV_Move( "move", "ff" );
  53. const idEventDef EV_MoveAccelerateTo( "accelTo", "ff" );
  54. const idEventDef EV_MoveDecelerateTo( "decelTo", "ff" );
  55. const idEventDef EV_RotateDownTo( "rotateDownTo", "df" );
  56. const idEventDef EV_RotateUpTo( "rotateUpTo", "df" );
  57. const idEventDef EV_RotateTo( "rotateTo", "v" );
  58. const idEventDef EV_Rotate( "rotate", "v" );
  59. const idEventDef EV_RotateOnce( "rotateOnce", "v" );
  60. const idEventDef EV_Bob( "bob", "ffv" );
  61. const idEventDef EV_Sway( "sway", "ffv" );
  62. const idEventDef EV_Mover_OpenPortal( "openPortal" );
  63. const idEventDef EV_Mover_ClosePortal( "closePortal" );
  64. const idEventDef EV_AccelSound( "accelSound", "s" );
  65. const idEventDef EV_DecelSound( "decelSound", "s" );
  66. const idEventDef EV_MoveSound( "moveSound", "s" );
  67. const idEventDef EV_Mover_InitGuiTargets( "<initguitargets>", NULL );
  68. const idEventDef EV_EnableSplineAngles( "enableSplineAngles", NULL );
  69. const idEventDef EV_DisableSplineAngles( "disableSplineAngles", NULL );
  70. const idEventDef EV_RemoveInitialSplineAngles( "removeInitialSplineAngles", NULL );
  71. const idEventDef EV_StartSpline( "startSpline", "e" );
  72. const idEventDef EV_StopSpline( "stopSpline", NULL );
  73. const idEventDef EV_IsMoving( "isMoving", NULL, 'd' );
  74. const idEventDef EV_IsRotating( "isRotating", NULL, 'd' );
  75. CLASS_DECLARATION( idEntity, idMover )
  76. EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets )
  77. EVENT( EV_Thread_SetCallback, idMover::Event_SetCallback )
  78. EVENT( EV_TeamBlocked, idMover::Event_TeamBlocked )
  79. EVENT( EV_PartBlocked, idMover::Event_PartBlocked )
  80. EVENT( EV_ReachedPos, idMover::Event_UpdateMove )
  81. EVENT( EV_ReachedAng, idMover::Event_UpdateRotation )
  82. EVENT( EV_PostRestore, idMover::Event_PostRestore )
  83. EVENT( EV_StopMoving, idMover::Event_StopMoving )
  84. EVENT( EV_StopRotating, idMover::Event_StopRotating )
  85. EVENT( EV_Speed, idMover::Event_SetMoveSpeed )
  86. EVENT( EV_Time, idMover::Event_SetMoveTime )
  87. EVENT( EV_AccelTime, idMover::Event_SetAccellerationTime )
  88. EVENT( EV_DecelTime, idMover::Event_SetDecelerationTime )
  89. EVENT( EV_MoveTo, idMover::Event_MoveTo )
  90. EVENT( EV_MoveToPos, idMover::Event_MoveToPos )
  91. EVENT( EV_Move, idMover::Event_MoveDir )
  92. EVENT( EV_MoveAccelerateTo, idMover::Event_MoveAccelerateTo )
  93. EVENT( EV_MoveDecelerateTo, idMover::Event_MoveDecelerateTo )
  94. EVENT( EV_RotateDownTo, idMover::Event_RotateDownTo )
  95. EVENT( EV_RotateUpTo, idMover::Event_RotateUpTo )
  96. EVENT( EV_RotateTo, idMover::Event_RotateTo )
  97. EVENT( EV_Rotate, idMover::Event_Rotate )
  98. EVENT( EV_RotateOnce, idMover::Event_RotateOnce )
  99. EVENT( EV_Bob, idMover::Event_Bob )
  100. EVENT( EV_Sway, idMover::Event_Sway )
  101. EVENT( EV_Mover_OpenPortal, idMover::Event_OpenPortal )
  102. EVENT( EV_Mover_ClosePortal, idMover::Event_ClosePortal )
  103. EVENT( EV_AccelSound, idMover::Event_SetAccelSound )
  104. EVENT( EV_DecelSound, idMover::Event_SetDecelSound )
  105. EVENT( EV_MoveSound, idMover::Event_SetMoveSound )
  106. EVENT( EV_Mover_InitGuiTargets, idMover::Event_InitGuiTargets )
  107. EVENT( EV_EnableSplineAngles, idMover::Event_EnableSplineAngles )
  108. EVENT( EV_DisableSplineAngles, idMover::Event_DisableSplineAngles )
  109. EVENT( EV_RemoveInitialSplineAngles, idMover::Event_RemoveInitialSplineAngles )
  110. EVENT( EV_StartSpline, idMover::Event_StartSpline )
  111. EVENT( EV_StopSpline, idMover::Event_StopSpline )
  112. EVENT( EV_Activate, idMover::Event_Activate )
  113. EVENT( EV_IsMoving, idMover::Event_IsMoving )
  114. EVENT( EV_IsRotating, idMover::Event_IsRotating )
  115. END_CLASS
  116. /*
  117. ================
  118. idMover::idMover
  119. ================
  120. */
  121. idMover::idMover( void ) {
  122. memset( &move, 0, sizeof( move ) );
  123. memset( &rot, 0, sizeof( rot ) );
  124. move_thread = 0;
  125. rotate_thread = 0;
  126. dest_angles.Zero();
  127. angle_delta.Zero();
  128. dest_position.Zero();
  129. move_delta.Zero();
  130. move_speed = 0.0f;
  131. move_time = 0;
  132. deceltime = 0;
  133. acceltime = 0;
  134. stopRotation = false;
  135. useSplineAngles = true;
  136. lastCommand = MOVER_NONE;
  137. damage = 0.0f;
  138. areaPortal = 0;
  139. fl.networkSync = true;
  140. }
  141. /*
  142. ================
  143. idMover::Save
  144. ================
  145. */
  146. void idMover::Save( idSaveGame *savefile ) const {
  147. int i;
  148. savefile->WriteStaticObject( physicsObj );
  149. savefile->WriteInt( move.stage );
  150. savefile->WriteInt( move.acceleration );
  151. savefile->WriteInt( move.movetime );
  152. savefile->WriteInt( move.deceleration );
  153. savefile->WriteVec3( move.dir );
  154. savefile->WriteInt( rot.stage );
  155. savefile->WriteInt( rot.acceleration );
  156. savefile->WriteInt( rot.movetime );
  157. savefile->WriteInt( rot.deceleration );
  158. savefile->WriteFloat( rot.rot.pitch );
  159. savefile->WriteFloat( rot.rot.yaw );
  160. savefile->WriteFloat( rot.rot.roll );
  161. savefile->WriteInt( move_thread );
  162. savefile->WriteInt( rotate_thread );
  163. savefile->WriteAngles( dest_angles );
  164. savefile->WriteAngles( angle_delta );
  165. savefile->WriteVec3( dest_position );
  166. savefile->WriteVec3( move_delta );
  167. savefile->WriteFloat( move_speed );
  168. savefile->WriteInt( move_time );
  169. savefile->WriteInt( deceltime );
  170. savefile->WriteInt( acceltime );
  171. savefile->WriteBool( stopRotation );
  172. savefile->WriteBool( useSplineAngles );
  173. savefile->WriteInt( lastCommand );
  174. savefile->WriteFloat( damage );
  175. savefile->WriteInt( areaPortal );
  176. if ( areaPortal > 0 ) {
  177. savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
  178. }
  179. savefile->WriteInt( guiTargets.Num() );
  180. for( i = 0; i < guiTargets.Num(); i++ ) {
  181. guiTargets[ i ].Save( savefile );
  182. }
  183. if ( splineEnt.GetEntity() && splineEnt.GetEntity()->GetSpline() ) {
  184. idCurve_Spline<idVec3> *spline = physicsObj.GetSpline();
  185. savefile->WriteBool( true );
  186. splineEnt.Save( savefile );
  187. savefile->WriteInt( spline->GetTime( 0 ) );
  188. savefile->WriteInt( spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) );
  189. savefile->WriteInt( physicsObj.GetSplineAcceleration() );
  190. savefile->WriteInt( physicsObj.GetSplineDeceleration() );
  191. savefile->WriteInt( (int)physicsObj.UsingSplineAngles() );
  192. } else {
  193. savefile->WriteBool( false );
  194. }
  195. }
  196. /*
  197. ================
  198. idMover::Restore
  199. ================
  200. */
  201. void idMover::Restore( idRestoreGame *savefile ) {
  202. int i, num;
  203. bool hasSpline = false;
  204. savefile->ReadStaticObject( physicsObj );
  205. RestorePhysics( &physicsObj );
  206. savefile->ReadInt( (int&)move.stage );
  207. savefile->ReadInt( move.acceleration );
  208. savefile->ReadInt( move.movetime );
  209. savefile->ReadInt( move.deceleration );
  210. savefile->ReadVec3( move.dir );
  211. savefile->ReadInt( (int&)rot.stage );
  212. savefile->ReadInt( rot.acceleration );
  213. savefile->ReadInt( rot.movetime );
  214. savefile->ReadInt( rot.deceleration );
  215. savefile->ReadFloat( rot.rot.pitch );
  216. savefile->ReadFloat( rot.rot.yaw );
  217. savefile->ReadFloat( rot.rot.roll );
  218. savefile->ReadInt( move_thread );
  219. savefile->ReadInt( rotate_thread );
  220. savefile->ReadAngles( dest_angles );
  221. savefile->ReadAngles( angle_delta );
  222. savefile->ReadVec3( dest_position );
  223. savefile->ReadVec3( move_delta );
  224. savefile->ReadFloat( move_speed );
  225. savefile->ReadInt( move_time );
  226. savefile->ReadInt( deceltime );
  227. savefile->ReadInt( acceltime );
  228. savefile->ReadBool( stopRotation );
  229. savefile->ReadBool( useSplineAngles );
  230. savefile->ReadInt( (int &)lastCommand );
  231. savefile->ReadFloat( damage );
  232. savefile->ReadInt( areaPortal );
  233. if ( areaPortal > 0 ) {
  234. int portalState = 0;
  235. savefile->ReadInt( portalState );
  236. gameLocal.SetPortalState( areaPortal, portalState );
  237. }
  238. guiTargets.Clear();
  239. savefile->ReadInt( num );
  240. guiTargets.SetNum( num );
  241. for( i = 0; i < num; i++ ) {
  242. guiTargets[ i ].Restore( savefile );
  243. }
  244. savefile->ReadBool( hasSpline );
  245. if ( hasSpline ) {
  246. int starttime;
  247. int totaltime;
  248. int accel;
  249. int decel;
  250. int useAngles;
  251. splineEnt.Restore( savefile );
  252. savefile->ReadInt( starttime );
  253. savefile->ReadInt( totaltime );
  254. savefile->ReadInt( accel );
  255. savefile->ReadInt( decel );
  256. savefile->ReadInt( useAngles );
  257. PostEventMS( &EV_PostRestore, 0, starttime, totaltime, accel, decel, useAngles );
  258. }
  259. }
  260. /*
  261. ================
  262. idMover::Event_PostRestore
  263. ================
  264. */
  265. void idMover::Event_PostRestore( int start, int total, int accel, int decel, int useSplineAng ) {
  266. idCurve_Spline<idVec3> *spline;
  267. idEntity *splineEntity = splineEnt.GetEntity();
  268. if ( !splineEntity ) {
  269. // We should never get this event if splineEnt is invalid
  270. common->Warning( "Invalid spline entity during restore\n" );
  271. return;
  272. }
  273. spline = splineEntity->GetSpline();
  274. spline->MakeUniform( total );
  275. spline->ShiftTime( start - spline->GetTime( 0 ) );
  276. physicsObj.SetSpline( spline, accel, decel, ( useSplineAng != 0 ) );
  277. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
  278. }
  279. /*
  280. ================
  281. idMover::Spawn
  282. ================
  283. */
  284. void idMover::Spawn( void ) {
  285. move_thread = 0;
  286. rotate_thread = 0;
  287. stopRotation = false;
  288. lastCommand = MOVER_NONE;
  289. acceltime = 1000.0f * spawnArgs.GetFloat( "accel_time", "0" );
  290. deceltime = 1000.0f * spawnArgs.GetFloat( "decel_time", "0" );
  291. move_time = 1000.0f * spawnArgs.GetFloat( "move_time", "1" ); // safe default value
  292. move_speed = spawnArgs.GetFloat( "move_speed", "0" );
  293. spawnArgs.GetFloat( "damage" , "0", damage );
  294. dest_position = GetPhysics()->GetOrigin();
  295. dest_angles = GetPhysics()->GetAxis().ToAngles();
  296. physicsObj.SetSelf( this );
  297. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  298. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  299. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  300. physicsObj.SetClipMask( MASK_SOLID );
  301. if ( !spawnArgs.GetBool( "solid", "1" ) ) {
  302. physicsObj.SetContents( 0 );
  303. }
  304. if ( !renderEntity.hModel || !spawnArgs.GetBool( "nopush" ) ) {
  305. physicsObj.SetPusher( 0 );
  306. }
  307. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
  308. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
  309. SetPhysics( &physicsObj );
  310. // see if we are on an areaportal
  311. areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
  312. if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
  313. if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
  314. PostEventMS( &EV_FindGuiTargets, 0 );
  315. } else {
  316. // not during spawn, so it's ok to get the targets
  317. FindGuiTargets();
  318. }
  319. }
  320. health = spawnArgs.GetInt( "health" );
  321. if ( health ) {
  322. fl.takedamage = true;
  323. }
  324. }
  325. /*
  326. ================
  327. idMover::Hide
  328. ================
  329. */
  330. void idMover::Hide( void ) {
  331. idEntity::Hide();
  332. physicsObj.SetContents( 0 );
  333. }
  334. /*
  335. ================
  336. idMover::Show
  337. ================
  338. */
  339. void idMover::Show( void ) {
  340. idEntity::Show();
  341. if ( spawnArgs.GetBool( "solid", "1" ) ) {
  342. physicsObj.SetContents( CONTENTS_SOLID );
  343. }
  344. SetPhysics( &physicsObj );
  345. }
  346. /*
  347. ============
  348. idMover::Killed
  349. ============
  350. */
  351. void idMover::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  352. fl.takedamage = false;
  353. ActivateTargets( this );
  354. }
  355. /*
  356. ================
  357. idMover::Event_SetCallback
  358. ================
  359. */
  360. void idMover::Event_SetCallback( void ) {
  361. if ( ( lastCommand == MOVER_ROTATING ) && !rotate_thread ) {
  362. lastCommand = MOVER_NONE;
  363. rotate_thread = idThread::CurrentThreadNum();
  364. idThread::ReturnInt( true );
  365. } else if ( ( lastCommand == MOVER_MOVING || lastCommand == MOVER_SPLINE ) && !move_thread ) {
  366. lastCommand = MOVER_NONE;
  367. move_thread = idThread::CurrentThreadNum();
  368. idThread::ReturnInt( true );
  369. } else {
  370. idThread::ReturnInt( false );
  371. }
  372. }
  373. /*
  374. ================
  375. idMover::VectorForDir
  376. ================
  377. */
  378. void idMover::VectorForDir( float angle, idVec3 &vec ) {
  379. idAngles ang;
  380. switch( ( int )angle ) {
  381. case DIR_UP :
  382. vec.Set( 0, 0, 1 );
  383. break;
  384. case DIR_DOWN :
  385. vec.Set( 0, 0, -1 );
  386. break;
  387. case DIR_LEFT :
  388. physicsObj.GetLocalAngles( ang );
  389. ang.pitch = 0;
  390. ang.roll = 0;
  391. ang.yaw += 90;
  392. vec = ang.ToForward();
  393. break;
  394. case DIR_RIGHT :
  395. physicsObj.GetLocalAngles( ang );
  396. ang.pitch = 0;
  397. ang.roll = 0;
  398. ang.yaw -= 90;
  399. vec = ang.ToForward();
  400. break;
  401. case DIR_FORWARD :
  402. physicsObj.GetLocalAngles( ang );
  403. ang.pitch = 0;
  404. ang.roll = 0;
  405. vec = ang.ToForward();
  406. break;
  407. case DIR_BACK :
  408. physicsObj.GetLocalAngles( ang );
  409. ang.pitch = 0;
  410. ang.roll = 0;
  411. ang.yaw += 180;
  412. vec = ang.ToForward();
  413. break;
  414. case DIR_REL_UP :
  415. vec.Set( 0, 0, 1 );
  416. break;
  417. case DIR_REL_DOWN :
  418. vec.Set( 0, 0, -1 );
  419. break;
  420. case DIR_REL_LEFT :
  421. physicsObj.GetLocalAngles( ang );
  422. ang.ToVectors( NULL, &vec );
  423. vec *= -1;
  424. break;
  425. case DIR_REL_RIGHT :
  426. physicsObj.GetLocalAngles( ang );
  427. ang.ToVectors( NULL, &vec );
  428. break;
  429. case DIR_REL_FORWARD :
  430. physicsObj.GetLocalAngles( ang );
  431. vec = ang.ToForward();
  432. break;
  433. case DIR_REL_BACK :
  434. physicsObj.GetLocalAngles( ang );
  435. vec = ang.ToForward() * -1;
  436. break;
  437. default:
  438. ang.Set( 0, angle, 0 );
  439. vec = GetWorldVector( ang.ToForward() );
  440. break;
  441. }
  442. }
  443. /*
  444. ================
  445. idMover::FindGuiTargets
  446. ================
  447. */
  448. void idMover::FindGuiTargets( void ) {
  449. gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
  450. }
  451. /*
  452. ==============================
  453. idMover::SetGuiState
  454. key/val will be set to any renderEntity->gui's on the list
  455. ==============================
  456. */
  457. void idMover::SetGuiState( const char *key, const char *val ) const {
  458. gameLocal.Printf( "Setting %s to %s\n", key, val );
  459. for( int i = 0; i < guiTargets.Num(); i++ ) {
  460. idEntity *ent = guiTargets[ i ].GetEntity();
  461. if ( ent ) {
  462. for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
  463. if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
  464. ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
  465. ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
  466. }
  467. }
  468. ent->UpdateVisuals();
  469. }
  470. }
  471. }
  472. /*
  473. ================
  474. idMover::Event_InitGuiTargets
  475. ================
  476. */
  477. void idMover::Event_FindGuiTargets( void ) {
  478. FindGuiTargets();
  479. }
  480. /*
  481. ================
  482. idMover::SetGuiStates
  483. ================
  484. */
  485. void idMover::SetGuiStates( const char *state ) {
  486. int i;
  487. if ( guiTargets.Num() ) {
  488. SetGuiState( "movestate", state );
  489. }
  490. for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
  491. if ( renderEntity.gui[ i ] ) {
  492. renderEntity.gui[ i ]->SetStateString( "movestate", state );
  493. renderEntity.gui[ i ]->StateChanged( gameLocal.time, true );
  494. }
  495. }
  496. }
  497. /*
  498. ================
  499. idMover::Event_InitGuiTargets
  500. ================
  501. */
  502. void idMover::Event_InitGuiTargets( void ) {
  503. SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
  504. }
  505. /***********************************************************************
  506. Translation control functions
  507. ***********************************************************************/
  508. /*
  509. ================
  510. idMover::Event_StopMoving
  511. ================
  512. */
  513. void idMover::Event_StopMoving( void ) {
  514. physicsObj.GetLocalOrigin( dest_position );
  515. DoneMoving();
  516. }
  517. /*
  518. ================
  519. idMover::DoneMoving
  520. ================
  521. */
  522. void idMover::DoneMoving( void ) {
  523. if ( lastCommand != MOVER_SPLINE ) {
  524. // set our final position so that we get rid of any numerical inaccuracy
  525. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
  526. }
  527. lastCommand = MOVER_NONE;
  528. idThread::ObjectMoveDone( move_thread, this );
  529. move_thread = 0;
  530. StopSound( SND_CHANNEL_BODY, false );
  531. }
  532. /*
  533. ================
  534. idMover::UpdateMoveSound
  535. ================
  536. */
  537. void idMover::UpdateMoveSound( moveStage_t stage ) {
  538. switch( stage ) {
  539. case ACCELERATION_STAGE: {
  540. StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
  541. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  542. break;
  543. }
  544. case LINEAR_STAGE: {
  545. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  546. break;
  547. }
  548. case DECELERATION_STAGE: {
  549. StopSound( SND_CHANNEL_BODY, false );
  550. StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
  551. break;
  552. }
  553. case FINISHED_STAGE: {
  554. StopSound( SND_CHANNEL_BODY, false );
  555. break;
  556. }
  557. }
  558. }
  559. /*
  560. ================
  561. idMover::Event_UpdateMove
  562. ================
  563. */
  564. void idMover::Event_UpdateMove( void ) {
  565. idVec3 org;
  566. physicsObj.GetLocalOrigin( org );
  567. UpdateMoveSound( move.stage );
  568. switch( move.stage ) {
  569. case ACCELERATION_STAGE: {
  570. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, move.acceleration, org, move.dir, vec3_origin );
  571. if ( move.movetime > 0 ) {
  572. move.stage = LINEAR_STAGE;
  573. } else if ( move.deceleration > 0 ) {
  574. move.stage = DECELERATION_STAGE;
  575. } else {
  576. move.stage = FINISHED_STAGE;
  577. }
  578. break;
  579. }
  580. case LINEAR_STAGE: {
  581. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, move.movetime, org, move.dir, vec3_origin );
  582. if ( move.deceleration ) {
  583. move.stage = DECELERATION_STAGE;
  584. } else {
  585. move.stage = FINISHED_STAGE;
  586. }
  587. break;
  588. }
  589. case DECELERATION_STAGE: {
  590. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, move.deceleration, org, move.dir, vec3_origin );
  591. move.stage = FINISHED_STAGE;
  592. break;
  593. }
  594. case FINISHED_STAGE: {
  595. if ( g_debugMover.GetBool() ) {
  596. gameLocal.Printf( "%d: '%s' move done\n", gameLocal.time, name.c_str() );
  597. }
  598. DoneMoving();
  599. break;
  600. }
  601. }
  602. }
  603. /*
  604. ================
  605. idMover::BeginMove
  606. ================
  607. */
  608. void idMover::BeginMove( idThread *thread ) {
  609. moveStage_t stage;
  610. idVec3 org;
  611. float dist;
  612. float acceldist;
  613. int totalacceltime;
  614. int at;
  615. int dt;
  616. lastCommand = MOVER_MOVING;
  617. move_thread = 0;
  618. physicsObj.GetLocalOrigin( org );
  619. move_delta = dest_position - org;
  620. if ( move_delta.Compare( vec3_zero ) ) {
  621. DoneMoving();
  622. return;
  623. }
  624. // scale times up to whole physics frames
  625. at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
  626. move_time += at - acceltime;
  627. acceltime = at;
  628. dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
  629. move_time += dt - deceltime;
  630. deceltime = dt;
  631. // if we're moving at a specific speed, we need to calculate the move time
  632. if ( move_speed ) {
  633. dist = move_delta.Length();
  634. totalacceltime = acceltime + deceltime;
  635. // calculate the distance we'll move during acceleration and deceleration
  636. acceldist = totalacceltime * 0.5f * 0.001f * move_speed;
  637. if ( acceldist >= dist ) {
  638. // going too slow for this distance to move at a constant speed
  639. move_time = totalacceltime;
  640. } else {
  641. // calculate move time taking acceleration into account
  642. move_time = totalacceltime + 1000.0f * ( dist - acceldist ) / move_speed;
  643. }
  644. }
  645. // scale time up to a whole physics frames
  646. move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
  647. if ( acceltime ) {
  648. stage = ACCELERATION_STAGE;
  649. } else if ( move_time <= deceltime ) {
  650. stage = DECELERATION_STAGE;
  651. } else {
  652. stage = LINEAR_STAGE;
  653. }
  654. at = acceltime;
  655. dt = deceltime;
  656. if ( at + dt > move_time ) {
  657. // there's no real correct way to handle this, so we just scale
  658. // the times to fit into the move time in the same proportions
  659. at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
  660. dt = move_time - at;
  661. }
  662. move_delta = move_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
  663. move.stage = stage;
  664. move.acceleration = at;
  665. move.movetime = move_time - at - dt;
  666. move.deceleration = dt;
  667. move.dir = move_delta;
  668. ProcessEvent( &EV_ReachedPos );
  669. }
  670. /***********************************************************************
  671. Rotation control functions
  672. ***********************************************************************/
  673. /*
  674. ================
  675. idMover::Event_StopRotating
  676. ================
  677. */
  678. void idMover::Event_StopRotating( void ) {
  679. physicsObj.GetLocalAngles( dest_angles );
  680. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
  681. DoneRotating();
  682. }
  683. /*
  684. ================
  685. idMover::DoneRotating
  686. ================
  687. */
  688. void idMover::DoneRotating( void ) {
  689. lastCommand = MOVER_NONE;
  690. idThread::ObjectMoveDone( rotate_thread, this );
  691. rotate_thread = 0;
  692. StopSound( SND_CHANNEL_BODY, false );
  693. }
  694. /*
  695. ================
  696. idMover::UpdateRotationSound
  697. ================
  698. */
  699. void idMover::UpdateRotationSound( moveStage_t stage ) {
  700. switch( stage ) {
  701. case ACCELERATION_STAGE: {
  702. StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
  703. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  704. break;
  705. }
  706. case LINEAR_STAGE: {
  707. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  708. break;
  709. }
  710. case DECELERATION_STAGE: {
  711. StopSound( SND_CHANNEL_BODY, false );
  712. StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
  713. break;
  714. }
  715. case FINISHED_STAGE: {
  716. StopSound( SND_CHANNEL_BODY, false );
  717. break;
  718. }
  719. }
  720. }
  721. /*
  722. ================
  723. idMover::Event_UpdateRotation
  724. ================
  725. */
  726. void idMover::Event_UpdateRotation( void ) {
  727. idAngles ang;
  728. physicsObj.GetLocalAngles( ang );
  729. UpdateRotationSound( rot.stage );
  730. switch( rot.stage ) {
  731. case ACCELERATION_STAGE: {
  732. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, rot.acceleration, ang, rot.rot, ang_zero );
  733. if ( rot.movetime > 0 ) {
  734. rot.stage = LINEAR_STAGE;
  735. } else if ( rot.deceleration > 0 ) {
  736. rot.stage = DECELERATION_STAGE;
  737. } else {
  738. rot.stage = FINISHED_STAGE;
  739. }
  740. break;
  741. }
  742. case LINEAR_STAGE: {
  743. if ( !stopRotation && !rot.deceleration ) {
  744. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, rot.movetime, ang, rot.rot, ang_zero );
  745. } else {
  746. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, rot.movetime, ang, rot.rot, ang_zero );
  747. }
  748. if ( rot.deceleration ) {
  749. rot.stage = DECELERATION_STAGE;
  750. } else {
  751. rot.stage = FINISHED_STAGE;
  752. }
  753. break;
  754. }
  755. case DECELERATION_STAGE: {
  756. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, rot.deceleration, ang, rot.rot, ang_zero );
  757. rot.stage = FINISHED_STAGE;
  758. break;
  759. }
  760. case FINISHED_STAGE: {
  761. lastCommand = MOVER_NONE;
  762. if ( stopRotation ) {
  763. // set our final angles so that we get rid of any numerical inaccuracy
  764. dest_angles.Normalize360();
  765. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
  766. stopRotation = false;
  767. } else if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_ACCELLINEAR ) {
  768. // keep our angular velocity constant
  769. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, ang, rot.rot, ang_zero );
  770. }
  771. if ( g_debugMover.GetBool() ) {
  772. gameLocal.Printf( "%d: '%s' rotation done\n", gameLocal.time, name.c_str() );
  773. }
  774. DoneRotating();
  775. break;
  776. }
  777. }
  778. }
  779. /*
  780. ================
  781. idMover::BeginRotation
  782. ================
  783. */
  784. void idMover::BeginRotation( idThread *thread, bool stopwhendone ) {
  785. moveStage_t stage;
  786. idAngles ang;
  787. int at;
  788. int dt;
  789. lastCommand = MOVER_ROTATING;
  790. rotate_thread = 0;
  791. // rotation always uses move_time so that if a move was started before the rotation,
  792. // the rotation will take the same amount of time as the move. If no move has been
  793. // started and no time is set, the rotation takes 1 second.
  794. if ( !move_time ) {
  795. move_time = 1;
  796. }
  797. physicsObj.GetLocalAngles( ang );
  798. angle_delta = dest_angles - ang;
  799. if ( angle_delta == ang_zero ) {
  800. // set our final angles so that we get rid of any numerical inaccuracy
  801. dest_angles.Normalize360();
  802. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_angles, ang_zero, ang_zero );
  803. stopRotation = false;
  804. DoneRotating();
  805. return;
  806. }
  807. // scale times up to whole physics frames
  808. at = idPhysics::SnapTimeToPhysicsFrame( acceltime );
  809. move_time += at - acceltime;
  810. acceltime = at;
  811. dt = idPhysics::SnapTimeToPhysicsFrame( deceltime );
  812. move_time += dt - deceltime;
  813. deceltime = dt;
  814. move_time = idPhysics::SnapTimeToPhysicsFrame( move_time );
  815. if ( acceltime ) {
  816. stage = ACCELERATION_STAGE;
  817. } else if ( move_time <= deceltime ) {
  818. stage = DECELERATION_STAGE;
  819. } else {
  820. stage = LINEAR_STAGE;
  821. }
  822. at = acceltime;
  823. dt = deceltime;
  824. if ( at + dt > move_time ) {
  825. // there's no real correct way to handle this, so we just scale
  826. // the times to fit into the move time in the same proportions
  827. at = idPhysics::SnapTimeToPhysicsFrame( at * move_time / ( at + dt ) );
  828. dt = move_time - at;
  829. }
  830. angle_delta = angle_delta * ( 1000.0f / ( (float) move_time - ( at + dt ) * 0.5f ) );
  831. stopRotation = stopwhendone || ( dt != 0 );
  832. rot.stage = stage;
  833. rot.acceleration = at;
  834. rot.movetime = move_time - at - dt;
  835. rot.deceleration = dt;
  836. rot.rot = angle_delta;
  837. ProcessEvent( &EV_ReachedAng );
  838. }
  839. /***********************************************************************
  840. Script callable routines
  841. ***********************************************************************/
  842. /*
  843. ===============
  844. idMover::Event_TeamBlocked
  845. ===============
  846. */
  847. void idMover::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
  848. if ( g_debugMover.GetBool() ) {
  849. gameLocal.Printf( "%d: '%s' stopped due to team member '%s' blocked by '%s'\n", gameLocal.time, name.c_str(), blockedEntity->name.c_str(), blockingEntity->name.c_str() );
  850. }
  851. }
  852. /*
  853. ===============
  854. idMover::Event_PartBlocked
  855. ===============
  856. */
  857. void idMover::Event_PartBlocked( idEntity *blockingEntity ) {
  858. if ( damage > 0.0f ) {
  859. blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
  860. }
  861. if ( g_debugMover.GetBool() ) {
  862. gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.time, name.c_str(), blockingEntity->name.c_str() );
  863. }
  864. }
  865. /*
  866. ================
  867. idMover::Event_SetMoveSpeed
  868. ================
  869. */
  870. void idMover::Event_SetMoveSpeed( float speed ) {
  871. if ( speed <= 0 ) {
  872. gameLocal.Error( "Cannot set speed less than or equal to 0." );
  873. }
  874. move_speed = speed;
  875. move_time = 0; // move_time is calculated for each move when move_speed is non-0
  876. }
  877. /*
  878. ================
  879. idMover::Event_SetMoveTime
  880. ================
  881. */
  882. void idMover::Event_SetMoveTime( float time ) {
  883. if ( time <= 0 ) {
  884. gameLocal.Error( "Cannot set time less than or equal to 0." );
  885. }
  886. move_speed = 0;
  887. move_time = SEC2MS( time );
  888. }
  889. /*
  890. ================
  891. idMover::Event_SetAccellerationTime
  892. ================
  893. */
  894. void idMover::Event_SetAccellerationTime( float time ) {
  895. if ( time < 0 ) {
  896. gameLocal.Error( "Cannot set acceleration time less than 0." );
  897. }
  898. acceltime = SEC2MS( time );
  899. }
  900. /*
  901. ================
  902. idMover::Event_SetDecelerationTime
  903. ================
  904. */
  905. void idMover::Event_SetDecelerationTime( float time ) {
  906. if ( time < 0 ) {
  907. gameLocal.Error( "Cannot set deceleration time less than 0." );
  908. }
  909. deceltime = SEC2MS( time );
  910. }
  911. /*
  912. ================
  913. idMover::Event_MoveTo
  914. ================
  915. */
  916. void idMover::Event_MoveTo( idEntity *ent ) {
  917. if ( !ent ) {
  918. gameLocal.Warning( "Entity not found" );
  919. }
  920. dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() );
  921. BeginMove( idThread::CurrentThread() );
  922. }
  923. /*
  924. ================
  925. idMover::MoveToPos
  926. ================
  927. */
  928. void idMover::MoveToPos( const idVec3 &pos ) {
  929. dest_position = GetLocalCoordinates( pos );
  930. BeginMove( NULL );
  931. }
  932. /*
  933. ================
  934. idMover::Event_MoveToPos
  935. ================
  936. */
  937. void idMover::Event_MoveToPos( idVec3 &pos ) {
  938. MoveToPos( pos );
  939. }
  940. /*
  941. ================
  942. idMover::Event_MoveDir
  943. ================
  944. */
  945. void idMover::Event_MoveDir( float angle, float distance ) {
  946. idVec3 dir;
  947. idVec3 org;
  948. physicsObj.GetLocalOrigin( org );
  949. VectorForDir( angle, dir );
  950. dest_position = org + dir * distance;
  951. BeginMove( idThread::CurrentThread() );
  952. }
  953. /*
  954. ================
  955. idMover::Event_MoveAccelerateTo
  956. ================
  957. */
  958. void idMover::Event_MoveAccelerateTo( float speed, float time ) {
  959. float v;
  960. idVec3 org, dir;
  961. int at;
  962. if ( time < 0 ) {
  963. gameLocal.Error( "idMover::Event_MoveAccelerateTo: cannot set acceleration time less than 0." );
  964. }
  965. dir = physicsObj.GetLinearVelocity();
  966. v = dir.Normalize();
  967. // if not moving already
  968. if ( v == 0.0f ) {
  969. gameLocal.Error( "idMover::Event_MoveAccelerateTo: not moving." );
  970. }
  971. // if already moving faster than the desired speed
  972. if ( v >= speed ) {
  973. return;
  974. }
  975. at = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
  976. lastCommand = MOVER_MOVING;
  977. physicsObj.GetLocalOrigin( org );
  978. move.stage = ACCELERATION_STAGE;
  979. move.acceleration = at;
  980. move.movetime = 0;
  981. move.deceleration = 0;
  982. StartSound( "snd_accel", SND_CHANNEL_BODY2, 0, false, NULL );
  983. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  984. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_ACCELLINEAR, gameLocal.time, move.acceleration, org, dir * ( speed - v ), dir * v );
  985. }
  986. /*
  987. ================
  988. idMover::Event_MoveDecelerateTo
  989. ================
  990. */
  991. void idMover::Event_MoveDecelerateTo( float speed, float time ) {
  992. float v;
  993. idVec3 org, dir;
  994. int dt;
  995. if ( time < 0 ) {
  996. gameLocal.Error( "idMover::Event_MoveDecelerateTo: cannot set deceleration time less than 0." );
  997. }
  998. dir = physicsObj.GetLinearVelocity();
  999. v = dir.Normalize();
  1000. // if not moving already
  1001. if ( v == 0.0f ) {
  1002. gameLocal.Error( "idMover::Event_MoveDecelerateTo: not moving." );
  1003. }
  1004. // if already moving slower than the desired speed
  1005. if ( v <= speed ) {
  1006. return;
  1007. }
  1008. dt = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( time ) );
  1009. lastCommand = MOVER_MOVING;
  1010. physicsObj.GetLocalOrigin( org );
  1011. move.stage = DECELERATION_STAGE;
  1012. move.acceleration = 0;
  1013. move.movetime = 0;
  1014. move.deceleration = dt;
  1015. StartSound( "snd_decel", SND_CHANNEL_BODY2, 0, false, NULL );
  1016. StartSound( "snd_move", SND_CHANNEL_BODY, 0, false, NULL );
  1017. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_DECELLINEAR, gameLocal.time, move.deceleration, org, dir * ( v - speed ), dir * speed );
  1018. }
  1019. /*
  1020. ================
  1021. idMover::Event_RotateDownTo
  1022. ================
  1023. */
  1024. void idMover::Event_RotateDownTo( int axis, float angle ) {
  1025. idAngles ang;
  1026. if ( ( axis < 0 ) || ( axis > 2 ) ) {
  1027. gameLocal.Error( "Invalid axis" );
  1028. }
  1029. physicsObj.GetLocalAngles( ang );
  1030. dest_angles[ axis ] = angle;
  1031. if ( dest_angles[ axis ] > ang[ axis ] ) {
  1032. dest_angles[ axis ] -= 360;
  1033. }
  1034. BeginRotation( idThread::CurrentThread(), true );
  1035. }
  1036. /*
  1037. ================
  1038. idMover::Event_RotateUpTo
  1039. ================
  1040. */
  1041. void idMover::Event_RotateUpTo( int axis, float angle ) {
  1042. idAngles ang;
  1043. if ( ( axis < 0 ) || ( axis > 2 ) ) {
  1044. gameLocal.Error( "Invalid axis" );
  1045. }
  1046. physicsObj.GetLocalAngles( ang );
  1047. dest_angles[ axis ] = angle;
  1048. if ( dest_angles[ axis ] < ang[ axis ] ) {
  1049. dest_angles[ axis ] += 360;
  1050. }
  1051. BeginRotation( idThread::CurrentThread(), true );
  1052. }
  1053. /*
  1054. ================
  1055. idMover::Event_RotateTo
  1056. ================
  1057. */
  1058. void idMover::Event_RotateTo( idAngles &angles ) {
  1059. dest_angles = angles;
  1060. BeginRotation( idThread::CurrentThread(), true );
  1061. }
  1062. /*
  1063. ================
  1064. idMover::Event_Rotate
  1065. ================
  1066. */
  1067. void idMover::Event_Rotate( idAngles &angles ) {
  1068. idAngles ang;
  1069. if ( rotate_thread ) {
  1070. DoneRotating();
  1071. }
  1072. physicsObj.GetLocalAngles( ang );
  1073. dest_angles = ang + angles * ( move_time - ( acceltime + deceltime ) / 2 ) * 0.001f;
  1074. BeginRotation( idThread::CurrentThread(), false );
  1075. }
  1076. /*
  1077. ================
  1078. idMover::Event_RotateOnce
  1079. ================
  1080. */
  1081. void idMover::Event_RotateOnce( idAngles &angles ) {
  1082. idAngles ang;
  1083. if ( rotate_thread ) {
  1084. DoneRotating();
  1085. }
  1086. physicsObj.GetLocalAngles( ang );
  1087. dest_angles = ang + angles;
  1088. BeginRotation( idThread::CurrentThread(), true );
  1089. }
  1090. /*
  1091. ================
  1092. idMover::Event_Bob
  1093. ================
  1094. */
  1095. void idMover::Event_Bob( float speed, float phase, idVec3 &depth ) {
  1096. idVec3 org;
  1097. physicsObj.GetLocalOrigin( org );
  1098. physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), speed * 1000 * phase, speed * 500, org, depth * 2.0f, vec3_origin );
  1099. }
  1100. /*
  1101. ================
  1102. idMover::Event_Sway
  1103. ================
  1104. */
  1105. void idMover::Event_Sway( float speed, float phase, idAngles &depth ) {
  1106. idAngles ang, angSpeed;
  1107. float duration;
  1108. physicsObj.GetLocalAngles( ang );
  1109. assert ( speed > 0.0f );
  1110. duration = idMath::Sqrt( depth[0] * depth[0] + depth[1] * depth[1] + depth[2] * depth[2] ) / speed;
  1111. angSpeed = depth / ( duration * idMath::SQRT_1OVER2 );
  1112. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), duration * 1000.0f * phase, duration * 1000.0f, ang, angSpeed, ang_zero );
  1113. }
  1114. /*
  1115. ================
  1116. idMover::Event_OpenPortal
  1117. Sets the portal associtated with this mover to be open
  1118. ================
  1119. */
  1120. void idMover::Event_OpenPortal( void ) {
  1121. if ( areaPortal ) {
  1122. SetPortalState( true );
  1123. }
  1124. }
  1125. /*
  1126. ================
  1127. idMover::Event_ClosePortal
  1128. Sets the portal associtated with this mover to be closed
  1129. ================
  1130. */
  1131. void idMover::Event_ClosePortal( void ) {
  1132. if ( areaPortal ) {
  1133. SetPortalState( false );
  1134. }
  1135. }
  1136. /*
  1137. ================
  1138. idMover::Event_SetAccelSound
  1139. ================
  1140. */
  1141. void idMover::Event_SetAccelSound( const char *sound ) {
  1142. // refSound.SetSound( "accel", sound );
  1143. }
  1144. /*
  1145. ================
  1146. idMover::Event_SetDecelSound
  1147. ================
  1148. */
  1149. void idMover::Event_SetDecelSound( const char *sound ) {
  1150. // refSound.SetSound( "decel", sound );
  1151. }
  1152. /*
  1153. ================
  1154. idMover::Event_SetMoveSound
  1155. ================
  1156. */
  1157. void idMover::Event_SetMoveSound( const char *sound ) {
  1158. // refSound.SetSound( "move", sound );
  1159. }
  1160. /*
  1161. ================
  1162. idMover::Event_EnableSplineAngles
  1163. ================
  1164. */
  1165. void idMover::Event_EnableSplineAngles( void ) {
  1166. useSplineAngles = true;
  1167. }
  1168. /*
  1169. ================
  1170. idMover::Event_DisableSplineAngles
  1171. ================
  1172. */
  1173. void idMover::Event_DisableSplineAngles( void ) {
  1174. useSplineAngles = false;
  1175. }
  1176. /*
  1177. ================
  1178. idMover::Event_RemoveInitialSplineAngles
  1179. ================
  1180. */
  1181. void idMover::Event_RemoveInitialSplineAngles( void ) {
  1182. idCurve_Spline<idVec3> *spline;
  1183. idAngles ang;
  1184. spline = physicsObj.GetSpline();
  1185. if ( !spline ) {
  1186. return;
  1187. }
  1188. ang = spline->GetCurrentFirstDerivative( 0 ).ToAngles();
  1189. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, -ang, ang_zero, ang_zero );
  1190. }
  1191. /*
  1192. ================
  1193. idMover::Event_StartSpline
  1194. ================
  1195. */
  1196. void idMover::Event_StartSpline( idEntity *splineEntity ) {
  1197. idCurve_Spline<idVec3> *spline;
  1198. if ( !splineEntity ) {
  1199. return;
  1200. }
  1201. // Needed for savegames
  1202. splineEnt = splineEntity;
  1203. spline = splineEntity->GetSpline();
  1204. if ( !spline ) {
  1205. return;
  1206. }
  1207. lastCommand = MOVER_SPLINE;
  1208. move_thread = 0;
  1209. if ( acceltime + deceltime > move_time ) {
  1210. acceltime = move_time / 2;
  1211. deceltime = move_time - acceltime;
  1212. }
  1213. move.stage = FINISHED_STAGE;
  1214. move.acceleration = acceltime;
  1215. move.movetime = move_time;
  1216. move.deceleration = deceltime;
  1217. spline->MakeUniform( move_time );
  1218. spline->ShiftTime( gameLocal.time - spline->GetTime( 0 ) );
  1219. physicsObj.SetSpline( spline, move.acceleration, move.deceleration, useSplineAngles );
  1220. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, dest_position, vec3_origin, vec3_origin );
  1221. }
  1222. /*
  1223. ================
  1224. idMover::Event_StopSpline
  1225. ================
  1226. */
  1227. void idMover::Event_StopSpline( void ) {
  1228. physicsObj.SetSpline( NULL, 0, 0, useSplineAngles );
  1229. splineEnt = NULL;
  1230. }
  1231. /*
  1232. ================
  1233. idMover::Event_Activate
  1234. ================
  1235. */
  1236. void idMover::Event_Activate( idEntity *activator ) {
  1237. Show();
  1238. Event_StartSpline( this );
  1239. }
  1240. /*
  1241. ================
  1242. idMover::Event_IsMoving
  1243. ================
  1244. */
  1245. void idMover::Event_IsMoving( void ) {
  1246. if ( physicsObj.GetLinearExtrapolationType() == EXTRAPOLATION_NONE ) {
  1247. idThread::ReturnInt( false );
  1248. } else {
  1249. idThread::ReturnInt( true );
  1250. }
  1251. }
  1252. /*
  1253. ================
  1254. idMover::Event_IsRotating
  1255. ================
  1256. */
  1257. void idMover::Event_IsRotating( void ) {
  1258. if ( physicsObj.GetAngularExtrapolationType() == EXTRAPOLATION_NONE ) {
  1259. idThread::ReturnInt( false );
  1260. } else {
  1261. idThread::ReturnInt( true );
  1262. }
  1263. }
  1264. /*
  1265. ================
  1266. idMover::WriteToSnapshot
  1267. ================
  1268. */
  1269. void idMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1270. physicsObj.WriteToSnapshot( msg );
  1271. msg.WriteBits( move.stage, 3 );
  1272. msg.WriteBits( rot.stage, 3 );
  1273. WriteBindToSnapshot( msg );
  1274. WriteGUIToSnapshot( msg );
  1275. }
  1276. /*
  1277. ================
  1278. idMover::ReadFromSnapshot
  1279. ================
  1280. */
  1281. void idMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1282. moveStage_t oldMoveStage = move.stage;
  1283. moveStage_t oldRotStage = rot.stage;
  1284. physicsObj.ReadFromSnapshot( msg );
  1285. move.stage = (moveStage_t) msg.ReadBits( 3 );
  1286. rot.stage = (moveStage_t) msg.ReadBits( 3 );
  1287. ReadBindFromSnapshot( msg );
  1288. ReadGUIFromSnapshot( msg );
  1289. if ( msg.HasChanged() ) {
  1290. if ( move.stage != oldMoveStage ) {
  1291. UpdateMoveSound( oldMoveStage );
  1292. }
  1293. if ( rot.stage != oldRotStage ) {
  1294. UpdateRotationSound( oldRotStage );
  1295. }
  1296. UpdateVisuals();
  1297. }
  1298. }
  1299. /*
  1300. ================
  1301. idMover::SetPortalState
  1302. ================
  1303. */
  1304. void idMover::SetPortalState( bool open ) {
  1305. assert( areaPortal );
  1306. gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
  1307. }
  1308. /*
  1309. ===============================================================================
  1310. idSplinePath, holds a spline path to be used by an idMover
  1311. ===============================================================================
  1312. */
  1313. CLASS_DECLARATION( idEntity, idSplinePath )
  1314. END_CLASS
  1315. /*
  1316. ================
  1317. idSplinePath::idSplinePath
  1318. ================
  1319. */
  1320. idSplinePath::idSplinePath() {
  1321. }
  1322. /*
  1323. ================
  1324. idSplinePath::Spawn
  1325. ================
  1326. */
  1327. void idSplinePath::Spawn( void ) {
  1328. }
  1329. /*
  1330. ===============================================================================
  1331. idElevator
  1332. ===============================================================================
  1333. */
  1334. const idEventDef EV_PostArrival( "postArrival", NULL );
  1335. const idEventDef EV_GotoFloor( "gotoFloor", "d" );
  1336. CLASS_DECLARATION( idMover, idElevator )
  1337. EVENT( EV_Activate, idElevator::Event_Activate )
  1338. EVENT( EV_TeamBlocked, idElevator::Event_TeamBlocked )
  1339. EVENT( EV_PartBlocked, idElevator::Event_PartBlocked )
  1340. EVENT( EV_PostArrival, idElevator::Event_PostFloorArrival )
  1341. EVENT( EV_GotoFloor, idElevator::Event_GotoFloor )
  1342. EVENT( EV_Touch, idElevator::Event_Touch )
  1343. END_CLASS
  1344. /*
  1345. ================
  1346. idElevator::idElevator
  1347. ================
  1348. */
  1349. idElevator::idElevator( void ) {
  1350. state = INIT;
  1351. floorInfo.Clear();
  1352. currentFloor = 0;
  1353. pendingFloor = 0;
  1354. lastFloor = 0;
  1355. controlsDisabled = false;
  1356. lastTouchTime = 0;
  1357. returnFloor = 0;
  1358. returnTime = 0;
  1359. }
  1360. /*
  1361. ================
  1362. idElevator::Save
  1363. ================
  1364. */
  1365. void idElevator::Save( idSaveGame *savefile ) const {
  1366. int i;
  1367. savefile->WriteInt( (int)state );
  1368. savefile->WriteInt( floorInfo.Num() );
  1369. for ( i = 0; i < floorInfo.Num(); i++ ) {
  1370. savefile->WriteVec3( floorInfo[ i ].pos );
  1371. savefile->WriteString( floorInfo[ i ].door );
  1372. savefile->WriteInt( floorInfo[ i ].floor );
  1373. }
  1374. savefile->WriteInt( currentFloor );
  1375. savefile->WriteInt( pendingFloor );
  1376. savefile->WriteInt( lastFloor );
  1377. savefile->WriteBool( controlsDisabled );
  1378. savefile->WriteFloat( returnTime );
  1379. savefile->WriteInt( returnFloor );
  1380. savefile->WriteInt( lastTouchTime );
  1381. }
  1382. /*
  1383. ================
  1384. idElevator::Restore
  1385. ================
  1386. */
  1387. void idElevator::Restore( idRestoreGame *savefile ) {
  1388. int i, num;
  1389. savefile->ReadInt( (int &)state );
  1390. savefile->ReadInt( num );
  1391. for ( i = 0; i < num; i++ ) {
  1392. floorInfo_s floor;
  1393. savefile->ReadVec3( floor.pos );
  1394. savefile->ReadString( floor.door );
  1395. savefile->ReadInt( floor.floor );
  1396. floorInfo.Append( floor );
  1397. }
  1398. savefile->ReadInt( currentFloor );
  1399. savefile->ReadInt( pendingFloor );
  1400. savefile->ReadInt( lastFloor );
  1401. savefile->ReadBool( controlsDisabled );
  1402. savefile->ReadFloat( returnTime );
  1403. savefile->ReadInt( returnFloor );
  1404. savefile->ReadInt( lastTouchTime );
  1405. }
  1406. /*
  1407. ================
  1408. idElevator::Spawn
  1409. ================
  1410. */
  1411. void idElevator::Spawn( void ) {
  1412. idStr str;
  1413. int len1;
  1414. lastFloor = 0;
  1415. currentFloor = 0;
  1416. pendingFloor = spawnArgs.GetInt( "floor", "1" );
  1417. SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1]);
  1418. returnTime = spawnArgs.GetFloat( "returnTime" );
  1419. returnFloor = spawnArgs.GetInt( "returnFloor" );
  1420. len1 = strlen( "floorPos_" );
  1421. const idKeyValue *kv = spawnArgs.MatchPrefix( "floorPos_", NULL );
  1422. while( kv ) {
  1423. str = kv->GetKey().Right( kv->GetKey().Length() - len1 );
  1424. floorInfo_s fi;
  1425. fi.floor = atoi( str );
  1426. fi.door = spawnArgs.GetString( va( "floorDoor_%i", fi.floor ) );
  1427. fi.pos = spawnArgs.GetVector( kv->GetKey() );
  1428. floorInfo.Append( fi );
  1429. kv = spawnArgs.MatchPrefix( "floorPos_", kv );
  1430. }
  1431. lastTouchTime = 0;
  1432. state = INIT;
  1433. BecomeActive( TH_THINK | TH_PHYSICS );
  1434. PostEventMS( &EV_Mover_InitGuiTargets, 0 );
  1435. controlsDisabled = false;
  1436. }
  1437. /*
  1438. ==============
  1439. idElevator::Event_Touch
  1440. ===============
  1441. */
  1442. void idElevator::Event_Touch( idEntity *other, trace_t *trace ) {
  1443. if ( gameLocal.time < lastTouchTime + 2000 ) {
  1444. return;
  1445. }
  1446. if ( !other->IsType( idPlayer::Type ) ) {
  1447. return;
  1448. }
  1449. lastTouchTime = gameLocal.time;
  1450. if ( thinkFlags & TH_PHYSICS ) {
  1451. return;
  1452. }
  1453. int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
  1454. if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
  1455. PostEventSec( &EV_GotoFloor, 0.25f, triggerFloor );
  1456. }
  1457. }
  1458. /*
  1459. ================
  1460. idElevator::Think
  1461. ================
  1462. */
  1463. void idElevator::Think( void ) {
  1464. idVec3 masterOrigin;
  1465. idMat3 masterAxis;
  1466. idDoor *doorent = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1467. if ( state == INIT ) {
  1468. state = IDLE;
  1469. if ( doorent ) {
  1470. doorent->BindTeam( this );
  1471. doorent->spawnArgs.Set( "snd_open", "" );
  1472. doorent->spawnArgs.Set( "snd_close", "" );
  1473. doorent->spawnArgs.Set( "snd_opened", "" );
  1474. }
  1475. for ( int i = 0; i < floorInfo.Num(); i++ ) {
  1476. idDoor *door = GetDoor( floorInfo[i].door );
  1477. if ( door ) {
  1478. door->SetCompanion( doorent );
  1479. }
  1480. }
  1481. Event_GotoFloor( pendingFloor );
  1482. DisableAllDoors();
  1483. SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
  1484. } else if ( state == WAITING_ON_DOORS ) {
  1485. if ( doorent ) {
  1486. state = doorent->IsOpen() ? WAITING_ON_DOORS : IDLE;
  1487. } else {
  1488. state = IDLE;
  1489. }
  1490. if ( state == IDLE ) {
  1491. lastFloor = currentFloor;
  1492. currentFloor = pendingFloor;
  1493. floorInfo_s *fi = GetFloorInfo( currentFloor );
  1494. if ( fi ) {
  1495. MoveToPos( fi->pos );
  1496. }
  1497. }
  1498. }
  1499. RunPhysics();
  1500. Present();
  1501. }
  1502. /*
  1503. ================
  1504. idElevator::Event_Activate
  1505. ================
  1506. */
  1507. void idElevator::Event_Activate( idEntity *activator ) {
  1508. int triggerFloor = spawnArgs.GetInt( "triggerFloor" );
  1509. if ( spawnArgs.GetBool( "trigger" ) && triggerFloor != currentFloor ) {
  1510. Event_GotoFloor( triggerFloor );
  1511. }
  1512. }
  1513. /*
  1514. ================
  1515. idElevator::Event_TeamBlocked
  1516. ================
  1517. */
  1518. void idElevator::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
  1519. if ( blockedEntity == this ) {
  1520. Event_GotoFloor( lastFloor );
  1521. } else if ( blockedEntity && blockedEntity->IsType( idDoor::Type ) ) {
  1522. // open the inner doors if one is blocked
  1523. idDoor *blocked = static_cast<idDoor *>( blockedEntity );
  1524. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1525. if ( door && blocked->GetMoveMaster() == door->GetMoveMaster() ) {
  1526. door->SetBlocked(true);
  1527. OpenInnerDoor();
  1528. OpenFloorDoor( currentFloor );
  1529. }
  1530. }
  1531. }
  1532. /*
  1533. ===============
  1534. idElevator::HandleSingleGuiCommand
  1535. ===============
  1536. */
  1537. bool idElevator::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
  1538. idToken token;
  1539. if ( controlsDisabled ) {
  1540. return false;
  1541. }
  1542. if ( !src->ReadToken( &token ) ) {
  1543. return false;
  1544. }
  1545. if ( token == ";" ) {
  1546. return false;
  1547. }
  1548. if ( token.Icmp( "changefloor" ) == 0 ) {
  1549. if ( src->ReadToken( &token ) ) {
  1550. int newFloor = atoi( token );
  1551. if ( newFloor == currentFloor ) {
  1552. // open currentFloor and interior doors
  1553. OpenInnerDoor();
  1554. OpenFloorDoor( currentFloor );
  1555. } else {
  1556. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1557. if ( door && door->IsOpen() ) {
  1558. PostEventSec( &EV_GotoFloor, 0.5f, newFloor );
  1559. } else {
  1560. ProcessEvent( &EV_GotoFloor, newFloor );
  1561. }
  1562. }
  1563. return true;
  1564. }
  1565. }
  1566. src->UnreadToken( &token );
  1567. return false;
  1568. }
  1569. /*
  1570. ================
  1571. idElevator::OpenFloorDoor
  1572. ================
  1573. */
  1574. void idElevator::OpenFloorDoor( int floor ) {
  1575. floorInfo_s *fi = GetFloorInfo( floor );
  1576. if ( fi ) {
  1577. idDoor *door = GetDoor( fi->door );
  1578. if ( door ) {
  1579. door->Open();
  1580. }
  1581. }
  1582. }
  1583. /*
  1584. ================
  1585. idElevator::OpenInnerDoor
  1586. ================
  1587. */
  1588. void idElevator::OpenInnerDoor( void ) {
  1589. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1590. if ( door ) {
  1591. door->Open();
  1592. }
  1593. }
  1594. /*
  1595. ================
  1596. idElevator::GetFloorInfo
  1597. ================
  1598. */
  1599. floorInfo_s *idElevator::GetFloorInfo( int floor ) {
  1600. for ( int i = 0; i < floorInfo.Num(); i++ ) {
  1601. if ( floorInfo[i].floor == floor ) {
  1602. return &floorInfo[i];
  1603. }
  1604. }
  1605. return NULL;
  1606. }
  1607. /*
  1608. ================
  1609. idElevator::Event_GotoFloor
  1610. ================
  1611. */
  1612. void idElevator::Event_GotoFloor( int floor ) {
  1613. floorInfo_s *fi = GetFloorInfo( floor );
  1614. if ( fi ) {
  1615. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1616. if ( door ) {
  1617. if ( door->IsBlocked() || door->IsOpen() ) {
  1618. PostEventSec( &EV_GotoFloor, 0.5f, floor );
  1619. return;
  1620. }
  1621. }
  1622. DisableAllDoors();
  1623. CloseAllDoors();
  1624. state = WAITING_ON_DOORS;
  1625. pendingFloor = floor;
  1626. }
  1627. }
  1628. /*
  1629. ================
  1630. idElevator::BeginMove
  1631. ================
  1632. */
  1633. void idElevator::BeginMove( idThread *thread ) {
  1634. controlsDisabled = true;
  1635. CloseAllDoors();
  1636. DisableAllDoors();
  1637. const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
  1638. while( kv ) {
  1639. idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
  1640. if ( ent ) {
  1641. for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
  1642. if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
  1643. ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", "" );
  1644. ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
  1645. }
  1646. }
  1647. ent->UpdateVisuals();
  1648. }
  1649. kv = spawnArgs.MatchPrefix( "statusGui", kv );
  1650. }
  1651. SetGuiStates( ( pendingFloor == 1 ) ? guiBinaryMoverStates[3] : guiBinaryMoverStates[2] );
  1652. idMover::BeginMove( thread );
  1653. }
  1654. /*
  1655. ================
  1656. idElevator::GetDoor
  1657. ================
  1658. */
  1659. idDoor *idElevator::GetDoor( const char *name ) {
  1660. idEntity *ent;
  1661. idEntity *master;
  1662. idDoor *doorEnt;
  1663. doorEnt = NULL;
  1664. if ( name && *name ) {
  1665. ent = gameLocal.FindEntity( name );
  1666. if ( ent && ent->IsType( idDoor::Type ) ) {
  1667. doorEnt = static_cast<idDoor*>( ent );
  1668. master = doorEnt->GetMoveMaster();
  1669. if ( master != doorEnt ) {
  1670. if ( master->IsType( idDoor::Type ) ) {
  1671. doorEnt = static_cast<idDoor*>( master );
  1672. } else {
  1673. doorEnt = NULL;
  1674. }
  1675. }
  1676. }
  1677. }
  1678. return doorEnt;
  1679. }
  1680. /*
  1681. ================
  1682. idElevator::Event_PostFloorArrival
  1683. ================
  1684. */
  1685. void idElevator::Event_PostFloorArrival() {
  1686. OpenFloorDoor( currentFloor );
  1687. OpenInnerDoor();
  1688. SetGuiStates( ( currentFloor == 1 ) ? guiBinaryMoverStates[0] : guiBinaryMoverStates[1] );
  1689. controlsDisabled = false;
  1690. if ( returnTime > 0.0f && returnFloor != currentFloor ) {
  1691. PostEventSec( &EV_GotoFloor, returnTime, returnFloor );
  1692. }
  1693. }
  1694. /*
  1695. ================
  1696. idElevator::DoneMoving
  1697. ================
  1698. */
  1699. void idElevator::DoneMoving( void ) {
  1700. idMover::DoneMoving();
  1701. EnableProperDoors();
  1702. const idKeyValue *kv = spawnArgs.MatchPrefix( "statusGui" );
  1703. while( kv ) {
  1704. idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
  1705. if ( ent ) {
  1706. for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
  1707. if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
  1708. ent->GetRenderEntity()->gui[ j ]->SetStateString( "floor", va( "%i", currentFloor ) );
  1709. ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
  1710. }
  1711. }
  1712. ent->UpdateVisuals();
  1713. }
  1714. kv = spawnArgs.MatchPrefix( "statusGui", kv );
  1715. }
  1716. if ( spawnArgs.GetInt( "pauseOnFloor", "-1" ) == currentFloor ) {
  1717. PostEventSec( &EV_PostArrival, spawnArgs.GetFloat( "pauseTime" ) );
  1718. } else {
  1719. Event_PostFloorArrival();
  1720. }
  1721. }
  1722. /*
  1723. ================
  1724. idElevator::CloseAllDoors
  1725. ================
  1726. */
  1727. void idElevator::CloseAllDoors( void ) {
  1728. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1729. if ( door ) {
  1730. door->Close();
  1731. }
  1732. for ( int i = 0; i < floorInfo.Num(); i++ ) {
  1733. door = GetDoor( floorInfo[i].door );
  1734. if ( door ) {
  1735. door->Close();
  1736. }
  1737. }
  1738. }
  1739. /*
  1740. ================
  1741. idElevator::DisableAllDoors
  1742. ================
  1743. */
  1744. void idElevator::DisableAllDoors( void ) {
  1745. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1746. if ( door ) {
  1747. door->Enable( false );
  1748. }
  1749. for ( int i = 0; i < floorInfo.Num(); i++ ) {
  1750. door = GetDoor( floorInfo[i].door );
  1751. if ( door ) {
  1752. door->Enable( false );
  1753. }
  1754. }
  1755. }
  1756. /*
  1757. ================
  1758. idElevator::EnableProperDoors
  1759. ================
  1760. */
  1761. void idElevator::EnableProperDoors( void ) {
  1762. idDoor *door = GetDoor( spawnArgs.GetString( "innerdoor" ) );
  1763. if ( door ) {
  1764. door->Enable( true );
  1765. }
  1766. for ( int i = 0; i < floorInfo.Num(); i++ ) {
  1767. if ( floorInfo[i].floor == currentFloor ) {
  1768. door = GetDoor( floorInfo[i].door );
  1769. if ( door ) {
  1770. door->Enable( true );
  1771. break;
  1772. }
  1773. }
  1774. }
  1775. }
  1776. /*
  1777. ===============================================================================
  1778. idMover_Binary
  1779. Doors, plats, and buttons are all binary (two position) movers
  1780. Pos1 is "at rest", pos2 is "activated"
  1781. ===============================================================================
  1782. */
  1783. const idEventDef EV_Mover_ReturnToPos1( "<returntopos1>", NULL );
  1784. const idEventDef EV_Mover_MatchTeam( "<matchteam>", "dd" );
  1785. const idEventDef EV_Mover_Enable( "enable", NULL );
  1786. const idEventDef EV_Mover_Disable( "disable", NULL );
  1787. CLASS_DECLARATION( idEntity, idMover_Binary )
  1788. EVENT( EV_FindGuiTargets, idMover_Binary::Event_FindGuiTargets )
  1789. EVENT( EV_Thread_SetCallback, idMover_Binary::Event_SetCallback )
  1790. EVENT( EV_Mover_ReturnToPos1, idMover_Binary::Event_ReturnToPos1 )
  1791. EVENT( EV_Activate, idMover_Binary::Event_Use_BinaryMover )
  1792. EVENT( EV_ReachedPos, idMover_Binary::Event_Reached_BinaryMover )
  1793. EVENT( EV_Mover_MatchTeam, idMover_Binary::Event_MatchActivateTeam )
  1794. EVENT( EV_Mover_Enable, idMover_Binary::Event_Enable )
  1795. EVENT( EV_Mover_Disable, idMover_Binary::Event_Disable )
  1796. EVENT( EV_Mover_OpenPortal, idMover_Binary::Event_OpenPortal )
  1797. EVENT( EV_Mover_ClosePortal, idMover_Binary::Event_ClosePortal )
  1798. EVENT( EV_Mover_InitGuiTargets, idMover_Binary::Event_InitGuiTargets )
  1799. END_CLASS
  1800. /*
  1801. ================
  1802. idMover_Binary::idMover_Binary()
  1803. ================
  1804. */
  1805. idMover_Binary::idMover_Binary() {
  1806. pos1.Zero();
  1807. pos2.Zero();
  1808. moverState = MOVER_POS1;
  1809. moveMaster = NULL;
  1810. activateChain = NULL;
  1811. soundPos1 = 0;
  1812. sound1to2 = 0;
  1813. sound2to1 = 0;
  1814. soundPos2 = 0;
  1815. soundLoop = 0;
  1816. wait = 0.0f;
  1817. damage = 0.0f;
  1818. duration = 0;
  1819. accelTime = 0;
  1820. decelTime = 0;
  1821. activatedBy = this;
  1822. stateStartTime = 0;
  1823. team.Clear();
  1824. enabled = false;
  1825. move_thread = 0;
  1826. updateStatus = 0;
  1827. areaPortal = 0;
  1828. blocked = false;
  1829. fl.networkSync = true;
  1830. }
  1831. /*
  1832. ================
  1833. idMover_Binary::~idMover_Binary
  1834. ================
  1835. */
  1836. idMover_Binary::~idMover_Binary() {
  1837. idMover_Binary *mover;
  1838. // if this is the mover master
  1839. if ( this == moveMaster ) {
  1840. // make the next mover in the chain the move master
  1841. for ( mover = moveMaster; mover; mover = mover->activateChain ) {
  1842. mover->moveMaster = this->activateChain;
  1843. }
  1844. }
  1845. else {
  1846. // remove mover from the activate chain
  1847. for ( mover = moveMaster; mover; mover = mover->activateChain ) {
  1848. if ( mover->activateChain == this ) {
  1849. mover->activateChain = this->activateChain;
  1850. break;
  1851. }
  1852. }
  1853. }
  1854. }
  1855. /*
  1856. ================
  1857. idMover_Binary::Save
  1858. ================
  1859. */
  1860. void idMover_Binary::Save( idSaveGame *savefile ) const {
  1861. int i;
  1862. savefile->WriteVec3( pos1 );
  1863. savefile->WriteVec3( pos2 );
  1864. savefile->WriteInt( (moverState_t)moverState );
  1865. savefile->WriteObject( moveMaster );
  1866. savefile->WriteObject( activateChain );
  1867. savefile->WriteInt( soundPos1 );
  1868. savefile->WriteInt( sound1to2 );
  1869. savefile->WriteInt( sound2to1 );
  1870. savefile->WriteInt( soundPos2 );
  1871. savefile->WriteInt( soundLoop );
  1872. savefile->WriteFloat( wait );
  1873. savefile->WriteFloat( damage );
  1874. savefile->WriteInt( duration );
  1875. savefile->WriteInt( accelTime );
  1876. savefile->WriteInt( decelTime );
  1877. activatedBy.Save( savefile );
  1878. savefile->WriteInt( stateStartTime );
  1879. savefile->WriteString( team );
  1880. savefile->WriteBool( enabled );
  1881. savefile->WriteInt( move_thread );
  1882. savefile->WriteInt( updateStatus );
  1883. savefile->WriteInt( buddies.Num() );
  1884. for ( i = 0; i < buddies.Num(); i++ ) {
  1885. savefile->WriteString( buddies[ i ] );
  1886. }
  1887. savefile->WriteStaticObject( physicsObj );
  1888. savefile->WriteInt( areaPortal );
  1889. if ( areaPortal ) {
  1890. savefile->WriteInt( gameRenderWorld->GetPortalState( areaPortal ) );
  1891. }
  1892. savefile->WriteBool( blocked );
  1893. savefile->WriteInt( guiTargets.Num() );
  1894. for( i = 0; i < guiTargets.Num(); i++ ) {
  1895. guiTargets[ i ].Save( savefile );
  1896. }
  1897. }
  1898. /*
  1899. ================
  1900. idMover_Binary::Restore
  1901. ================
  1902. */
  1903. void idMover_Binary::Restore( idRestoreGame *savefile ) {
  1904. int i, num, portalState;
  1905. idStr temp;
  1906. savefile->ReadVec3( pos1 );
  1907. savefile->ReadVec3( pos2 );
  1908. savefile->ReadInt( (int &)moverState );
  1909. savefile->ReadObject( reinterpret_cast<idClass *&>( moveMaster ) );
  1910. savefile->ReadObject( reinterpret_cast<idClass *&>( activateChain ) );
  1911. savefile->ReadInt( soundPos1 );
  1912. savefile->ReadInt( sound1to2 );
  1913. savefile->ReadInt( sound2to1 );
  1914. savefile->ReadInt( soundPos2 );
  1915. savefile->ReadInt( soundLoop );
  1916. savefile->ReadFloat( wait );
  1917. savefile->ReadFloat( damage );
  1918. savefile->ReadInt( duration );
  1919. savefile->ReadInt( accelTime );
  1920. savefile->ReadInt( decelTime );
  1921. activatedBy.Restore( savefile );
  1922. savefile->ReadInt( stateStartTime );
  1923. savefile->ReadString( team );
  1924. savefile->ReadBool( enabled );
  1925. savefile->ReadInt( move_thread );
  1926. savefile->ReadInt( updateStatus );
  1927. savefile->ReadInt( num );
  1928. for ( i = 0; i < num; i++ ) {
  1929. savefile->ReadString( temp );
  1930. buddies.Append( temp );
  1931. }
  1932. savefile->ReadStaticObject( physicsObj );
  1933. RestorePhysics( &physicsObj );
  1934. savefile->ReadInt( areaPortal );
  1935. if ( areaPortal ) {
  1936. savefile->ReadInt( portalState );
  1937. gameLocal.SetPortalState( areaPortal, portalState );
  1938. }
  1939. savefile->ReadBool( blocked );
  1940. guiTargets.Clear();
  1941. savefile->ReadInt( num );
  1942. guiTargets.SetNum( num );
  1943. for( i = 0; i < num; i++ ) {
  1944. guiTargets[ i ].Restore( savefile );
  1945. }
  1946. }
  1947. /*
  1948. ================
  1949. idMover_Binary::Spawn
  1950. Base class for all movers.
  1951. "wait" wait before returning (3 default, -1 = never return)
  1952. "speed" movement speed
  1953. ================
  1954. */
  1955. void idMover_Binary::Spawn( void ) {
  1956. idEntity *ent;
  1957. const char *temp;
  1958. move_thread = 0;
  1959. enabled = true;
  1960. areaPortal = 0;
  1961. activateChain = NULL;
  1962. spawnArgs.GetFloat( "wait", "0", wait );
  1963. spawnArgs.GetInt( "updateStatus", "0", updateStatus );
  1964. const idKeyValue *kv = spawnArgs.MatchPrefix( "buddy", NULL );
  1965. while( kv ) {
  1966. buddies.Append( kv->GetValue() );
  1967. kv = spawnArgs.MatchPrefix( "buddy", kv );
  1968. }
  1969. spawnArgs.GetString( "team", "", &temp );
  1970. team = temp;
  1971. if ( !team.Length() ) {
  1972. ent = this;
  1973. } else {
  1974. // find the first entity spawned on this team (which could be us)
  1975. for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  1976. if ( ent->IsType( idMover_Binary::Type ) && !idStr::Icmp( static_cast<idMover_Binary *>(ent)->team.c_str(), temp ) ) {
  1977. break;
  1978. }
  1979. }
  1980. if ( !ent ) {
  1981. ent = this;
  1982. }
  1983. }
  1984. moveMaster = static_cast<idMover_Binary *>(ent);
  1985. // create a physics team for the binary mover parts
  1986. if ( ent != this ) {
  1987. JoinTeam( ent );
  1988. }
  1989. physicsObj.SetSelf( this );
  1990. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  1991. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  1992. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  1993. physicsObj.SetClipMask( MASK_SOLID );
  1994. if ( !spawnArgs.GetBool( "solid", "1" ) ) {
  1995. physicsObj.SetContents( 0 );
  1996. }
  1997. if ( !spawnArgs.GetBool( "nopush" ) ) {
  1998. physicsObj.SetPusher( 0 );
  1999. }
  2000. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
  2001. physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
  2002. SetPhysics( &physicsObj );
  2003. if ( moveMaster != this ) {
  2004. JoinActivateTeam( moveMaster );
  2005. }
  2006. idBounds soundOrigin;
  2007. idMover_Binary *slave;
  2008. soundOrigin.Clear();
  2009. for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2010. soundOrigin += slave->GetPhysics()->GetAbsBounds();
  2011. }
  2012. moveMaster->refSound.origin = soundOrigin.GetCenter();
  2013. if ( spawnArgs.MatchPrefix( "guiTarget" ) ) {
  2014. if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
  2015. PostEventMS( &EV_FindGuiTargets, 0 );
  2016. } else {
  2017. // not during spawn, so it's ok to get the targets
  2018. FindGuiTargets();
  2019. }
  2020. }
  2021. }
  2022. /*
  2023. ===============
  2024. idMover_Binary::GetMovedir
  2025. The editor only specifies a single value for angles (yaw),
  2026. but we have special constants to generate an up or down direction.
  2027. Angles will be cleared, because it is being used to represent a direction
  2028. instead of an orientation.
  2029. ===============
  2030. */
  2031. void idMover_Binary::GetMovedir( float angle, idVec3 &movedir ) {
  2032. if ( angle == -1 ) {
  2033. movedir.Set( 0, 0, 1 );
  2034. } else if ( angle == -2 ) {
  2035. movedir.Set( 0, 0, -1 );
  2036. } else {
  2037. movedir = idAngles( 0, angle, 0 ).ToForward();
  2038. }
  2039. }
  2040. /*
  2041. ================
  2042. idMover_Binary::Event_SetCallback
  2043. ================
  2044. */
  2045. void idMover_Binary::Event_SetCallback( void ) {
  2046. if ( ( moverState == MOVER_1TO2 ) || ( moverState == MOVER_2TO1 ) ) {
  2047. move_thread = idThread::CurrentThreadNum();
  2048. idThread::ReturnInt( true );
  2049. } else {
  2050. idThread::ReturnInt( false );
  2051. }
  2052. }
  2053. /*
  2054. ===============
  2055. idMover_Binary::UpdateMoverSound
  2056. ===============
  2057. */
  2058. void idMover_Binary::UpdateMoverSound( moverState_t state ) {
  2059. if ( moveMaster == this ) {
  2060. switch( state ) {
  2061. case MOVER_POS1:
  2062. break;
  2063. case MOVER_POS2:
  2064. break;
  2065. case MOVER_1TO2:
  2066. StartSound( "snd_open", SND_CHANNEL_ANY, 0, false, NULL );
  2067. break;
  2068. case MOVER_2TO1:
  2069. StartSound( "snd_close", SND_CHANNEL_ANY, 0, false, NULL );
  2070. break;
  2071. }
  2072. }
  2073. }
  2074. /*
  2075. ===============
  2076. idMover_Binary::SetMoverState
  2077. ===============
  2078. */
  2079. void idMover_Binary::SetMoverState( moverState_t newstate, int time ) {
  2080. idVec3 delta;
  2081. moverState = newstate;
  2082. move_thread = 0;
  2083. UpdateMoverSound( newstate );
  2084. stateStartTime = time;
  2085. switch( moverState ) {
  2086. case MOVER_POS1: {
  2087. Signal( SIG_MOVER_POS1 );
  2088. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos1, vec3_origin, vec3_origin );
  2089. break;
  2090. }
  2091. case MOVER_POS2: {
  2092. Signal( SIG_MOVER_POS2 );
  2093. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, time, 0, pos2, vec3_origin, vec3_origin );
  2094. break;
  2095. }
  2096. case MOVER_1TO2: {
  2097. Signal( SIG_MOVER_1TO2 );
  2098. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos1, ( pos2 - pos1 ) * 1000.0f / duration, vec3_origin );
  2099. if ( accelTime != 0 || decelTime != 0 ) {
  2100. physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos1, pos2 );
  2101. } else {
  2102. physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
  2103. }
  2104. break;
  2105. }
  2106. case MOVER_2TO1: {
  2107. Signal( SIG_MOVER_2TO1 );
  2108. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, time, duration, pos2, ( pos1 - pos2 ) * 1000.0f / duration, vec3_origin );
  2109. if ( accelTime != 0 || decelTime != 0 ) {
  2110. physicsObj.SetLinearInterpolation( time, accelTime, decelTime, duration, pos2, pos1 );
  2111. } else {
  2112. physicsObj.SetLinearInterpolation( 0, 0, 0, 0, pos1, pos2 );
  2113. }
  2114. break;
  2115. }
  2116. }
  2117. }
  2118. /*
  2119. ================
  2120. idMover_Binary::MatchActivateTeam
  2121. All entities in a mover team will move from pos1 to pos2
  2122. in the same amount of time
  2123. ================
  2124. */
  2125. void idMover_Binary::MatchActivateTeam( moverState_t newstate, int time ) {
  2126. idMover_Binary *slave;
  2127. for ( slave = this; slave != NULL; slave = slave->activateChain ) {
  2128. slave->SetMoverState( newstate, time );
  2129. }
  2130. }
  2131. /*
  2132. ================
  2133. idMover_Binary::Enable
  2134. ================
  2135. */
  2136. void idMover_Binary::Enable( bool b ) {
  2137. enabled = b;
  2138. }
  2139. /*
  2140. ================
  2141. idMover_Binary::Event_MatchActivateTeam
  2142. ================
  2143. */
  2144. void idMover_Binary::Event_MatchActivateTeam( moverState_t newstate, int time ) {
  2145. MatchActivateTeam( newstate, time );
  2146. }
  2147. /*
  2148. ================
  2149. idMover_Binary::BindTeam
  2150. All entities in a mover team will be bound
  2151. ================
  2152. */
  2153. void idMover_Binary::BindTeam( idEntity *bindTo ) {
  2154. idMover_Binary *slave;
  2155. for ( slave = this; slave != NULL; slave = slave->activateChain ) {
  2156. slave->Bind( bindTo, true );
  2157. }
  2158. }
  2159. /*
  2160. ================
  2161. idMover_Binary::JoinActivateTeam
  2162. Set all entities in a mover team to be enabled
  2163. ================
  2164. */
  2165. void idMover_Binary::JoinActivateTeam( idMover_Binary *master ) {
  2166. this->activateChain = master->activateChain;
  2167. master->activateChain = this;
  2168. }
  2169. /*
  2170. ================
  2171. idMover_Binary::Event_Enable
  2172. Set all entities in a mover team to be enabled
  2173. ================
  2174. */
  2175. void idMover_Binary::Event_Enable( void ) {
  2176. idMover_Binary *slave;
  2177. for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2178. slave->Enable( false );
  2179. }
  2180. }
  2181. /*
  2182. ================
  2183. idMover_Binary::Event_Disable
  2184. Set all entities in a mover team to be disabled
  2185. ================
  2186. */
  2187. void idMover_Binary::Event_Disable( void ) {
  2188. idMover_Binary *slave;
  2189. for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2190. slave->Enable( false );
  2191. }
  2192. }
  2193. /*
  2194. ================
  2195. idMover_Binary::Event_OpenPortal
  2196. Sets the portal associtated with this mover to be open
  2197. ================
  2198. */
  2199. void idMover_Binary::Event_OpenPortal( void ) {
  2200. idMover_Binary *slave;
  2201. for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2202. if ( slave->areaPortal ) {
  2203. slave->SetPortalState( true );
  2204. }
  2205. }
  2206. }
  2207. /*
  2208. ================
  2209. idMover_Binary::Event_ClosePortal
  2210. Sets the portal associtated with this mover to be closed
  2211. ================
  2212. */
  2213. void idMover_Binary::Event_ClosePortal( void ) {
  2214. idMover_Binary *slave;
  2215. for ( slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2216. if ( !slave->IsHidden() ) {
  2217. if ( slave->areaPortal ) {
  2218. slave->SetPortalState( false );
  2219. }
  2220. }
  2221. }
  2222. }
  2223. /*
  2224. ================
  2225. idMover_Binary::Event_ReturnToPos1
  2226. ================
  2227. */
  2228. void idMover_Binary::Event_ReturnToPos1( void ) {
  2229. MatchActivateTeam( MOVER_2TO1, gameLocal.time );
  2230. }
  2231. /*
  2232. ================
  2233. idMover_Binary::Event_Reached_BinaryMover
  2234. ================
  2235. */
  2236. void idMover_Binary::Event_Reached_BinaryMover( void ) {
  2237. if ( moverState == MOVER_1TO2 ) {
  2238. // reached pos2
  2239. idThread::ObjectMoveDone( move_thread, this );
  2240. move_thread = 0;
  2241. if ( moveMaster == this ) {
  2242. StartSound( "snd_opened", SND_CHANNEL_ANY, 0, false, NULL );
  2243. }
  2244. SetMoverState( MOVER_POS2, gameLocal.time );
  2245. SetGuiStates( guiBinaryMoverStates[MOVER_POS2] );
  2246. UpdateBuddies( 1 );
  2247. if ( enabled && wait >= 0 && !spawnArgs.GetBool( "toggle" ) ) {
  2248. // return to pos1 after a delay
  2249. PostEventSec( &EV_Mover_ReturnToPos1, wait );
  2250. }
  2251. // fire targets
  2252. ActivateTargets( moveMaster->GetActivator() );
  2253. SetBlocked(false);
  2254. } else if ( moverState == MOVER_2TO1 ) {
  2255. // reached pos1
  2256. idThread::ObjectMoveDone( move_thread, this );
  2257. move_thread = 0;
  2258. SetMoverState( MOVER_POS1, gameLocal.time );
  2259. SetGuiStates( guiBinaryMoverStates[MOVER_POS1] );
  2260. UpdateBuddies( 0 );
  2261. // close areaportals
  2262. if ( moveMaster == this ) {
  2263. ProcessEvent( &EV_Mover_ClosePortal );
  2264. }
  2265. if ( enabled && wait >= 0 && spawnArgs.GetBool( "continuous" ) ) {
  2266. PostEventSec( &EV_Activate, wait, this );
  2267. }
  2268. SetBlocked(false);
  2269. } else {
  2270. gameLocal.Error( "Event_Reached_BinaryMover: bad moverState" );
  2271. }
  2272. }
  2273. /*
  2274. ================
  2275. idMover_Binary::GotoPosition1
  2276. ================
  2277. */
  2278. void idMover_Binary::GotoPosition1( void ) {
  2279. idMover_Binary *slave;
  2280. int partial;
  2281. // only the master should control this
  2282. if ( moveMaster != this ) {
  2283. moveMaster->GotoPosition1();
  2284. return;
  2285. }
  2286. SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
  2287. if ( ( moverState == MOVER_POS1 ) || ( moverState == MOVER_2TO1 ) ) {
  2288. // already there, or on the way
  2289. return;
  2290. }
  2291. if ( moverState == MOVER_POS2 ) {
  2292. for ( slave = this; slave != NULL; slave = slave->activateChain ) {
  2293. slave->CancelEvents( &EV_Mover_ReturnToPos1 );
  2294. }
  2295. if ( !spawnArgs.GetBool( "toggle" ) ) {
  2296. ProcessEvent( &EV_Mover_ReturnToPos1 );
  2297. }
  2298. return;
  2299. }
  2300. // only partway up before reversing
  2301. if ( moverState == MOVER_1TO2 ) {
  2302. // use the physics times because this might be executed during the physics simulation
  2303. partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
  2304. assert( partial >= 0 );
  2305. if ( partial < 0 ) {
  2306. partial = 0;
  2307. }
  2308. MatchActivateTeam( MOVER_2TO1, physicsObj.GetTime() - partial );
  2309. // if already at at position 1 (partial == duration) execute the reached event
  2310. if ( partial >= duration ) {
  2311. Event_Reached_BinaryMover();
  2312. }
  2313. }
  2314. }
  2315. /*
  2316. ================
  2317. idMover_Binary::GotoPosition2
  2318. ================
  2319. */
  2320. void idMover_Binary::GotoPosition2( void ) {
  2321. int partial;
  2322. // only the master should control this
  2323. if ( moveMaster != this ) {
  2324. moveMaster->GotoPosition2();
  2325. return;
  2326. }
  2327. SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
  2328. if ( ( moverState == MOVER_POS2 ) || ( moverState == MOVER_1TO2 ) ) {
  2329. // already there, or on the way
  2330. return;
  2331. }
  2332. if ( moverState == MOVER_POS1 ) {
  2333. MatchActivateTeam( MOVER_1TO2, gameLocal.time );
  2334. // open areaportal
  2335. ProcessEvent( &EV_Mover_OpenPortal );
  2336. return;
  2337. }
  2338. // only partway up before reversing
  2339. if ( moverState == MOVER_2TO1 ) {
  2340. // use the physics times because this might be executed during the physics simulation
  2341. partial = physicsObj.GetLinearEndTime() - physicsObj.GetTime();
  2342. assert( partial >= 0 );
  2343. if ( partial < 0 ) {
  2344. partial = 0;
  2345. }
  2346. MatchActivateTeam( MOVER_1TO2, physicsObj.GetTime() - partial );
  2347. // if already at at position 2 (partial == duration) execute the reached event
  2348. if ( partial >= duration ) {
  2349. Event_Reached_BinaryMover();
  2350. }
  2351. }
  2352. }
  2353. /*
  2354. ================
  2355. idMover_Binary::UpdateBuddies
  2356. ================
  2357. */
  2358. void idMover_Binary::UpdateBuddies( int val ) {
  2359. int i, c;
  2360. if ( updateStatus == 2 ) {
  2361. c = buddies.Num();
  2362. for ( i = 0; i < c; i++ ) {
  2363. idEntity *buddy = gameLocal.FindEntity( buddies[i] );
  2364. if ( buddy ) {
  2365. buddy->SetShaderParm( SHADERPARM_MODE, val );
  2366. buddy->UpdateVisuals();
  2367. }
  2368. }
  2369. }
  2370. }
  2371. /*
  2372. ================
  2373. idMover_Binary::SetGuiStates
  2374. ================
  2375. */
  2376. void idMover_Binary::SetGuiStates( const char *state ) {
  2377. if ( guiTargets.Num() ) {
  2378. SetGuiState( "movestate", state );
  2379. }
  2380. idMover_Binary *mb = activateChain;
  2381. while( mb ) {
  2382. if ( mb->guiTargets.Num() ) {
  2383. mb->SetGuiState( "movestate", state );
  2384. }
  2385. mb = mb->activateChain;
  2386. }
  2387. }
  2388. /*
  2389. ================
  2390. idMover_Binary::Use_BinaryMover
  2391. ================
  2392. */
  2393. void idMover_Binary::Use_BinaryMover( idEntity *activator ) {
  2394. // only the master should be used
  2395. if ( moveMaster != this ) {
  2396. moveMaster->Use_BinaryMover( activator );
  2397. return;
  2398. }
  2399. if ( !enabled ) {
  2400. return;
  2401. }
  2402. activatedBy = activator;
  2403. if ( moverState == MOVER_POS1 ) {
  2404. // FIXME: start moving USERCMD_MSEC later, because if this was player
  2405. // triggered, gameLocal.time hasn't been advanced yet
  2406. MatchActivateTeam( MOVER_1TO2, gameLocal.time + USERCMD_MSEC );
  2407. SetGuiStates( guiBinaryMoverStates[MOVER_1TO2] );
  2408. // open areaportal
  2409. ProcessEvent( &EV_Mover_OpenPortal );
  2410. return;
  2411. }
  2412. // if all the way up, just delay before coming down
  2413. if ( moverState == MOVER_POS2 ) {
  2414. idMover_Binary *slave;
  2415. if ( wait == -1 ) {
  2416. return;
  2417. }
  2418. SetGuiStates( guiBinaryMoverStates[MOVER_2TO1] );
  2419. for ( slave = this; slave != NULL; slave = slave->activateChain ) {
  2420. slave->CancelEvents( &EV_Mover_ReturnToPos1 );
  2421. slave->PostEventSec( &EV_Mover_ReturnToPos1, spawnArgs.GetBool( "toggle" ) ? 0 : wait );
  2422. }
  2423. return;
  2424. }
  2425. // only partway down before reversing
  2426. if ( moverState == MOVER_2TO1 ) {
  2427. GotoPosition2();
  2428. return;
  2429. }
  2430. // only partway up before reversing
  2431. if ( moverState == MOVER_1TO2 ) {
  2432. GotoPosition1();
  2433. return;
  2434. }
  2435. }
  2436. /*
  2437. ================
  2438. idMover_Binary::Event_Use_BinaryMover
  2439. ================
  2440. */
  2441. void idMover_Binary::Event_Use_BinaryMover( idEntity *activator ) {
  2442. Use_BinaryMover( activator );
  2443. }
  2444. /*
  2445. ================
  2446. idMover_Binary::PreBind
  2447. ================
  2448. */
  2449. void idMover_Binary::PreBind( void ) {
  2450. pos1 = GetWorldCoordinates( pos1 );
  2451. pos2 = GetWorldCoordinates( pos2 );
  2452. }
  2453. /*
  2454. ================
  2455. idMover_Binary::PostBind
  2456. ================
  2457. */
  2458. void idMover_Binary::PostBind( void ) {
  2459. pos1 = GetLocalCoordinates( pos1 );
  2460. pos2 = GetLocalCoordinates( pos2 );
  2461. }
  2462. /*
  2463. ================
  2464. idMover_Binary::FindGuiTargets
  2465. ================
  2466. */
  2467. void idMover_Binary::FindGuiTargets( void ) {
  2468. gameLocal.GetTargets( spawnArgs, guiTargets, "guiTarget" );
  2469. }
  2470. /*
  2471. ==============================
  2472. idMover_Binary::SetGuiState
  2473. key/val will be set to any renderEntity->gui's on the list
  2474. ==============================
  2475. */
  2476. void idMover_Binary::SetGuiState( const char *key, const char *val ) const {
  2477. int i;
  2478. for( i = 0; i < guiTargets.Num(); i++ ) {
  2479. idEntity *ent = guiTargets[ i ].GetEntity();
  2480. if ( ent ) {
  2481. for ( int j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
  2482. if ( ent->GetRenderEntity() && ent->GetRenderEntity()->gui[ j ] ) {
  2483. ent->GetRenderEntity()->gui[ j ]->SetStateString( key, val );
  2484. ent->GetRenderEntity()->gui[ j ]->StateChanged( gameLocal.time, true );
  2485. }
  2486. }
  2487. ent->UpdateVisuals();
  2488. }
  2489. }
  2490. }
  2491. /*
  2492. ================
  2493. idMover_Binary::Event_InitGuiTargets
  2494. ================
  2495. */
  2496. void idMover_Binary::Event_FindGuiTargets( void ) {
  2497. FindGuiTargets();
  2498. }
  2499. /*
  2500. ================
  2501. idMover_Binary::Event_InitGuiTargets
  2502. ================
  2503. */
  2504. void idMover_Binary::Event_InitGuiTargets( void ) {
  2505. if ( guiTargets.Num() ) {
  2506. SetGuiState( "movestate", guiBinaryMoverStates[MOVER_POS1] );
  2507. }
  2508. }
  2509. /*
  2510. ================
  2511. idMover_Binary::InitSpeed
  2512. pos1, pos2, and speed are passed in so the movement delta can be calculated
  2513. ================
  2514. */
  2515. void idMover_Binary::InitSpeed( idVec3 &mpos1, idVec3 &mpos2, float mspeed, float maccelTime, float mdecelTime ) {
  2516. idVec3 move;
  2517. float distance;
  2518. float speed;
  2519. pos1 = mpos1;
  2520. pos2 = mpos2;
  2521. accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
  2522. decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
  2523. speed = mspeed ? mspeed : 100;
  2524. // calculate time to reach second position from speed
  2525. move = pos2 - pos1;
  2526. distance = move.Length();
  2527. duration = idPhysics::SnapTimeToPhysicsFrame( distance * 1000 / speed );
  2528. if ( duration <= 0 ) {
  2529. duration = 1;
  2530. }
  2531. moverState = MOVER_POS1;
  2532. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
  2533. physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
  2534. SetOrigin( pos1 );
  2535. PostEventMS( &EV_Mover_InitGuiTargets, 0 );
  2536. }
  2537. /*
  2538. ================
  2539. idMover_Binary::InitTime
  2540. pos1, pos2, and time are passed in so the movement delta can be calculated
  2541. ================
  2542. */
  2543. void idMover_Binary::InitTime( idVec3 &mpos1, idVec3 &mpos2, float mtime, float maccelTime, float mdecelTime ) {
  2544. pos1 = mpos1;
  2545. pos2 = mpos2;
  2546. accelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( maccelTime ) );
  2547. decelTime = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mdecelTime ) );
  2548. duration = idPhysics::SnapTimeToPhysicsFrame( SEC2MS( mtime ) );
  2549. if ( duration <= 0 ) {
  2550. duration = 1;
  2551. }
  2552. moverState = MOVER_POS1;
  2553. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, pos1, vec3_origin, vec3_origin );
  2554. physicsObj.SetLinearInterpolation( 0, 0, 0, 0, vec3_origin, vec3_origin );
  2555. SetOrigin( pos1 );
  2556. PostEventMS( &EV_Mover_InitGuiTargets, 0 );
  2557. }
  2558. /*
  2559. ================
  2560. idMover_Binary::SetBlocked
  2561. ================
  2562. */
  2563. void idMover_Binary::SetBlocked( bool b ) {
  2564. for ( idMover_Binary *slave = moveMaster; slave != NULL; slave = slave->activateChain ) {
  2565. slave->blocked = b;
  2566. if ( b ) {
  2567. const idKeyValue *kv = slave->spawnArgs.MatchPrefix( "triggerBlocked" );
  2568. while( kv ) {
  2569. idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
  2570. if ( ent ) {
  2571. ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
  2572. }
  2573. kv = slave->spawnArgs.MatchPrefix( "triggerBlocked", kv );
  2574. }
  2575. }
  2576. }
  2577. }
  2578. /*
  2579. ================
  2580. idMover_Binary::IsBlocked
  2581. ================
  2582. */
  2583. bool idMover_Binary::IsBlocked( void ) {
  2584. return blocked;
  2585. }
  2586. /*
  2587. ================
  2588. idMover_Binary::GetActivator
  2589. ================
  2590. */
  2591. idEntity *idMover_Binary::GetActivator( void ) const {
  2592. return activatedBy.GetEntity();
  2593. }
  2594. /*
  2595. ================
  2596. idMover_Binary::WriteToSnapshot
  2597. ================
  2598. */
  2599. void idMover_Binary::WriteToSnapshot( idBitMsgDelta &msg ) const {
  2600. physicsObj.WriteToSnapshot( msg );
  2601. msg.WriteBits( moverState, 3 );
  2602. WriteBindToSnapshot( msg );
  2603. }
  2604. /*
  2605. ================
  2606. idMover_Binary::ReadFromSnapshot
  2607. ================
  2608. */
  2609. void idMover_Binary::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  2610. moverState_t oldMoverState = moverState;
  2611. physicsObj.ReadFromSnapshot( msg );
  2612. moverState = (moverState_t) msg.ReadBits( 3 );
  2613. ReadBindFromSnapshot( msg );
  2614. if ( msg.HasChanged() ) {
  2615. if ( moverState != oldMoverState ) {
  2616. UpdateMoverSound( moverState );
  2617. }
  2618. UpdateVisuals();
  2619. }
  2620. }
  2621. /*
  2622. ================
  2623. idMover_Binary::SetPortalState
  2624. ================
  2625. */
  2626. void idMover_Binary::SetPortalState( bool open ) {
  2627. assert( areaPortal );
  2628. gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL );
  2629. }
  2630. /*
  2631. ===============================================================================
  2632. idDoor
  2633. A use can be triggered either by a touch function, by being shot, or by being
  2634. targeted by another entity.
  2635. ===============================================================================
  2636. */
  2637. const idEventDef EV_Door_StartOpen( "<startOpen>", NULL );
  2638. const idEventDef EV_Door_SpawnDoorTrigger( "<spawnDoorTrigger>", NULL );
  2639. const idEventDef EV_Door_SpawnSoundTrigger( "<spawnSoundTrigger>", NULL );
  2640. const idEventDef EV_Door_Open( "open", NULL );
  2641. const idEventDef EV_Door_Close( "close", NULL );
  2642. const idEventDef EV_Door_Lock( "lock", "d" );
  2643. const idEventDef EV_Door_IsOpen( "isOpen", NULL, 'f' );
  2644. const idEventDef EV_Door_IsLocked( "isLocked", NULL, 'f' );
  2645. CLASS_DECLARATION( idMover_Binary, idDoor )
  2646. EVENT( EV_TeamBlocked, idDoor::Event_TeamBlocked )
  2647. EVENT( EV_PartBlocked, idDoor::Event_PartBlocked )
  2648. EVENT( EV_Touch, idDoor::Event_Touch )
  2649. EVENT( EV_Activate, idDoor::Event_Activate )
  2650. EVENT( EV_Door_StartOpen, idDoor::Event_StartOpen )
  2651. EVENT( EV_Door_SpawnDoorTrigger, idDoor::Event_SpawnDoorTrigger )
  2652. EVENT( EV_Door_SpawnSoundTrigger, idDoor::Event_SpawnSoundTrigger )
  2653. EVENT( EV_Door_Open, idDoor::Event_Open )
  2654. EVENT( EV_Door_Close, idDoor::Event_Close )
  2655. EVENT( EV_Door_Lock, idDoor::Event_Lock )
  2656. EVENT( EV_Door_IsOpen, idDoor::Event_IsOpen )
  2657. EVENT( EV_Door_IsLocked, idDoor::Event_Locked )
  2658. EVENT( EV_ReachedPos, idDoor::Event_Reached_BinaryMover )
  2659. EVENT( EV_SpectatorTouch, idDoor::Event_SpectatorTouch )
  2660. EVENT( EV_Mover_OpenPortal, idDoor::Event_OpenPortal )
  2661. EVENT( EV_Mover_ClosePortal, idDoor::Event_ClosePortal )
  2662. END_CLASS
  2663. /*
  2664. ================
  2665. idDoor::idDoor
  2666. ================
  2667. */
  2668. idDoor::idDoor( void ) {
  2669. triggersize = 1.0f;
  2670. crusher = false;
  2671. noTouch = false;
  2672. aas_area_closed = false;
  2673. buddyStr.Clear();
  2674. trigger = NULL;
  2675. sndTrigger = NULL;
  2676. nextSndTriggerTime = 0;
  2677. localTriggerOrigin.Zero();
  2678. localTriggerAxis.Identity();
  2679. requires.Clear();
  2680. removeItem = 0;
  2681. syncLock.Clear();
  2682. companionDoor = NULL;
  2683. normalAxisIndex = 0;
  2684. }
  2685. /*
  2686. ================
  2687. idDoor::~idDoor
  2688. ================
  2689. */
  2690. idDoor::~idDoor( void ) {
  2691. if ( trigger ) {
  2692. delete trigger;
  2693. }
  2694. if ( sndTrigger ) {
  2695. delete sndTrigger;
  2696. }
  2697. }
  2698. /*
  2699. ================
  2700. idDoor::Save
  2701. ================
  2702. */
  2703. void idDoor::Save( idSaveGame *savefile ) const {
  2704. savefile->WriteFloat( triggersize );
  2705. savefile->WriteBool( crusher );
  2706. savefile->WriteBool( noTouch );
  2707. savefile->WriteBool( aas_area_closed );
  2708. savefile->WriteString( buddyStr );
  2709. savefile->WriteInt( nextSndTriggerTime );
  2710. savefile->WriteVec3( localTriggerOrigin );
  2711. savefile->WriteMat3( localTriggerAxis );
  2712. savefile->WriteString( requires );
  2713. savefile->WriteInt( removeItem );
  2714. savefile->WriteString( syncLock );
  2715. savefile->WriteInt( normalAxisIndex );
  2716. savefile->WriteClipModel( trigger );
  2717. savefile->WriteClipModel( sndTrigger );
  2718. savefile->WriteObject( companionDoor );
  2719. }
  2720. /*
  2721. ================
  2722. idDoor::Restore
  2723. ================
  2724. */
  2725. void idDoor::Restore( idRestoreGame *savefile ) {
  2726. savefile->ReadFloat( triggersize );
  2727. savefile->ReadBool( crusher );
  2728. savefile->ReadBool( noTouch );
  2729. savefile->ReadBool( aas_area_closed );
  2730. SetAASAreaState( aas_area_closed );
  2731. savefile->ReadString( buddyStr );
  2732. savefile->ReadInt( nextSndTriggerTime );
  2733. savefile->ReadVec3( localTriggerOrigin );
  2734. savefile->ReadMat3( localTriggerAxis );
  2735. savefile->ReadString( requires );
  2736. savefile->ReadInt( removeItem );
  2737. savefile->ReadString( syncLock );
  2738. savefile->ReadInt( normalAxisIndex );
  2739. savefile->ReadClipModel( trigger );
  2740. savefile->ReadClipModel( sndTrigger );
  2741. savefile->ReadObject( reinterpret_cast<idClass *&>( companionDoor ) );
  2742. }
  2743. /*
  2744. ================
  2745. idDoor::Spawn
  2746. ================
  2747. */
  2748. void idDoor::Spawn( void ) {
  2749. idVec3 abs_movedir;
  2750. float distance;
  2751. idVec3 size;
  2752. idVec3 movedir;
  2753. float dir;
  2754. float lip;
  2755. bool start_open;
  2756. float time;
  2757. float speed;
  2758. // get the direction to move
  2759. if ( !spawnArgs.GetFloat( "movedir", "0", dir ) ) {
  2760. // no movedir, so angle defines movement direction and not orientation,
  2761. // a la oldschool Quake
  2762. SetAngles( ang_zero );
  2763. spawnArgs.GetFloat( "angle", "0", dir );
  2764. }
  2765. GetMovedir( dir, movedir );
  2766. // default speed of 400
  2767. spawnArgs.GetFloat( "speed", "400", speed );
  2768. // default wait of 2 seconds
  2769. spawnArgs.GetFloat( "wait", "3", wait );
  2770. // default lip of 8 units
  2771. spawnArgs.GetFloat( "lip", "8", lip );
  2772. // by default no damage
  2773. spawnArgs.GetFloat( "damage", "0", damage );
  2774. // trigger size
  2775. spawnArgs.GetFloat( "triggersize", "120", triggersize );
  2776. spawnArgs.GetBool( "crusher", "0", crusher );
  2777. spawnArgs.GetBool( "start_open", "0", start_open );
  2778. spawnArgs.GetBool( "no_touch", "0", noTouch );
  2779. // expects syncLock to be a door that must be closed before this door will open
  2780. spawnArgs.GetString( "syncLock", "", syncLock );
  2781. spawnArgs.GetString( "buddy", "", buddyStr );
  2782. spawnArgs.GetString( "requires", "", requires );
  2783. spawnArgs.GetInt( "removeItem", "0", removeItem );
  2784. // ever separate piece of a door is considered solid when other team mates push entities
  2785. fl.solidForTeam = true;
  2786. // first position at start
  2787. pos1 = GetPhysics()->GetOrigin();
  2788. // calculate second position
  2789. abs_movedir[0] = idMath::Fabs( movedir[ 0 ] );
  2790. abs_movedir[1] = idMath::Fabs( movedir[ 1 ] );
  2791. abs_movedir[2] = idMath::Fabs( movedir[ 2 ] );
  2792. size = GetPhysics()->GetAbsBounds()[1] - GetPhysics()->GetAbsBounds()[0];
  2793. distance = ( abs_movedir * size ) - lip;
  2794. pos2 = pos1 + distance * movedir;
  2795. // if "start_open", reverse position 1 and 2
  2796. if ( start_open ) {
  2797. // post it after EV_SpawnBind
  2798. PostEventMS( &EV_Door_StartOpen, 1 );
  2799. }
  2800. if ( spawnArgs.GetFloat( "time", "1", time ) ) {
  2801. InitTime( pos1, pos2, time, 0, 0 );
  2802. } else {
  2803. InitSpeed( pos1, pos2, speed, 0, 0 );
  2804. }
  2805. if ( moveMaster == this ) {
  2806. if ( health ) {
  2807. fl.takedamage = true;
  2808. }
  2809. if ( noTouch || health ) {
  2810. // non touch/shoot doors
  2811. PostEventMS( &EV_Mover_MatchTeam, 0, moverState, gameLocal.time );
  2812. const char *sndtemp = spawnArgs.GetString( "snd_locked" );
  2813. if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
  2814. PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
  2815. }
  2816. } else {
  2817. // spawn trigger
  2818. PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
  2819. }
  2820. }
  2821. // see if we are on an areaportal
  2822. areaPortal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds() );
  2823. if ( !start_open ) {
  2824. // start closed
  2825. ProcessEvent( &EV_Mover_ClosePortal );
  2826. }
  2827. int locked = spawnArgs.GetInt( "locked" );
  2828. if ( locked ) {
  2829. // make sure all members of the team get locked
  2830. PostEventMS( &EV_Door_Lock, 0, locked );
  2831. }
  2832. if ( spawnArgs.GetBool( "continuous" ) ) {
  2833. PostEventSec( &EV_Activate, spawnArgs.GetFloat( "delay" ), this );
  2834. }
  2835. // sounds have a habit of stuttering when portals close, so make them unoccluded
  2836. refSound.parms.soundShaderFlags |= SSF_NO_OCCLUSION;
  2837. companionDoor = NULL;
  2838. enabled = true;
  2839. blocked = false;
  2840. }
  2841. /*
  2842. ================
  2843. idDoor::Think
  2844. ================
  2845. */
  2846. void idDoor::Think( void ) {
  2847. idVec3 masterOrigin;
  2848. idMat3 masterAxis;
  2849. idMover_Binary::Think();
  2850. if ( thinkFlags & TH_PHYSICS ) {
  2851. // update trigger position
  2852. if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
  2853. if ( trigger ) {
  2854. trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
  2855. }
  2856. if ( sndTrigger ) {
  2857. sndTrigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
  2858. }
  2859. }
  2860. }
  2861. }
  2862. /*
  2863. ================
  2864. idDoor::PreBind
  2865. ================
  2866. */
  2867. void idDoor::PreBind( void ) {
  2868. idMover_Binary::PreBind();
  2869. }
  2870. /*
  2871. ================
  2872. idDoor::PostBind
  2873. ================
  2874. */
  2875. void idDoor::PostBind( void ) {
  2876. idMover_Binary::PostBind();
  2877. GetLocalTriggerPosition( trigger ? trigger : sndTrigger );
  2878. }
  2879. /*
  2880. ================
  2881. idDoor::SetAASAreaState
  2882. ================
  2883. */
  2884. void idDoor::SetAASAreaState( bool closed ) {
  2885. aas_area_closed = closed;
  2886. gameLocal.SetAASAreaState( physicsObj.GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL|AREACONTENTS_OBSTACLE, closed );
  2887. }
  2888. /*
  2889. ================
  2890. idDoor::Hide
  2891. ================
  2892. */
  2893. void idDoor::Hide( void ) {
  2894. idMover_Binary *slave;
  2895. idMover_Binary *master;
  2896. idDoor *slaveDoor;
  2897. idDoor *companion;
  2898. master = GetMoveMaster();
  2899. if ( this != master ) {
  2900. master->Hide();
  2901. } else {
  2902. for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
  2903. if ( slave->IsType( idDoor::Type ) ) {
  2904. slaveDoor = static_cast<idDoor *>( slave );
  2905. companion = slaveDoor->companionDoor;
  2906. if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
  2907. companion->Hide();
  2908. }
  2909. if ( slaveDoor->trigger ) {
  2910. slaveDoor->trigger->Disable();
  2911. }
  2912. if ( slaveDoor->sndTrigger ) {
  2913. slaveDoor->sndTrigger->Disable();
  2914. }
  2915. if ( slaveDoor->areaPortal ) {
  2916. slaveDoor->SetPortalState( true );
  2917. }
  2918. slaveDoor->SetAASAreaState( false );
  2919. }
  2920. slave->GetPhysics()->GetClipModel()->Disable();
  2921. slave->idMover_Binary::Hide();
  2922. }
  2923. }
  2924. }
  2925. /*
  2926. ================
  2927. idDoor::Show
  2928. ================
  2929. */
  2930. void idDoor::Show( void ) {
  2931. idMover_Binary *slave;
  2932. idMover_Binary *master;
  2933. idDoor *slaveDoor;
  2934. idDoor *companion;
  2935. master = GetMoveMaster();
  2936. if ( this != master ) {
  2937. master->Show();
  2938. } else {
  2939. for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
  2940. if ( slave->IsType( idDoor::Type ) ) {
  2941. slaveDoor = static_cast<idDoor *>( slave );
  2942. companion = slaveDoor->companionDoor;
  2943. if ( companion && ( companion != master ) && ( companion->GetMoveMaster() != master ) ) {
  2944. companion->Show();
  2945. }
  2946. if ( slaveDoor->trigger ) {
  2947. slaveDoor->trigger->Enable();
  2948. }
  2949. if ( slaveDoor->sndTrigger ) {
  2950. slaveDoor->sndTrigger->Enable();
  2951. }
  2952. if ( slaveDoor->areaPortal && ( slaveDoor->moverState == MOVER_POS1 ) ) {
  2953. slaveDoor->SetPortalState( false );
  2954. }
  2955. slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
  2956. }
  2957. slave->GetPhysics()->GetClipModel()->Enable();
  2958. slave->idMover_Binary::Show();
  2959. }
  2960. }
  2961. }
  2962. /*
  2963. ================
  2964. idDoor::GetLocalTriggerPosition
  2965. ================
  2966. */
  2967. void idDoor::GetLocalTriggerPosition( const idClipModel *trigger ) {
  2968. idVec3 origin;
  2969. idMat3 axis;
  2970. if ( !trigger ) {
  2971. return;
  2972. }
  2973. GetMasterPosition( origin, axis );
  2974. localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
  2975. localTriggerAxis = trigger->GetAxis() * axis.Transpose();
  2976. }
  2977. /*
  2978. ================
  2979. idDoor::Use
  2980. ================
  2981. */
  2982. void idDoor::Use( idEntity *other, idEntity *activator ) {
  2983. if ( gameLocal.RequirementMet( activator, requires, removeItem ) ) {
  2984. if ( syncLock.Length() ) {
  2985. idEntity *sync = gameLocal.FindEntity( syncLock );
  2986. if ( sync && sync->IsType( idDoor::Type ) ) {
  2987. if ( static_cast<idDoor *>( sync )->IsOpen() ) {
  2988. return;
  2989. }
  2990. }
  2991. }
  2992. ActivateTargets( activator );
  2993. Use_BinaryMover( activator );
  2994. }
  2995. }
  2996. /*
  2997. ================
  2998. idDoor::Open
  2999. ================
  3000. */
  3001. void idDoor::Open( void ) {
  3002. GotoPosition2();
  3003. }
  3004. /*
  3005. ================
  3006. idDoor::Close
  3007. ================
  3008. */
  3009. void idDoor::Close( void ) {
  3010. GotoPosition1();
  3011. }
  3012. /*
  3013. ================
  3014. idDoor::Lock
  3015. ================
  3016. */
  3017. void idDoor::Lock( int f ) {
  3018. idMover_Binary *other;
  3019. // lock all the doors on the team
  3020. for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
  3021. if ( other->IsType( idDoor::Type ) ) {
  3022. idDoor *door = static_cast<idDoor *>( other );
  3023. if ( other == moveMaster ) {
  3024. if ( door->sndTrigger == NULL ) {
  3025. // in this case the sound trigger never got spawned
  3026. const char *sndtemp = door->spawnArgs.GetString( "snd_locked" );
  3027. if ( sndtemp && *sndtemp ) {
  3028. door->PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
  3029. }
  3030. }
  3031. if ( !f && ( door->spawnArgs.GetInt( "locked" ) != 0 ) ) {
  3032. door->StartSound( "snd_unlocked", SND_CHANNEL_ANY, 0, false, NULL );
  3033. }
  3034. }
  3035. door->spawnArgs.SetInt( "locked", f );
  3036. if ( ( f == 0 ) || ( !IsHidden() && ( door->moverState == MOVER_POS1 ) ) ) {
  3037. door->SetAASAreaState( f != 0 );
  3038. }
  3039. }
  3040. }
  3041. if ( f ) {
  3042. Close();
  3043. }
  3044. }
  3045. /*
  3046. ================
  3047. idDoor::IsLocked
  3048. ================
  3049. */
  3050. int idDoor::IsLocked( void ) {
  3051. return spawnArgs.GetInt( "locked" );
  3052. }
  3053. /*
  3054. ================
  3055. idDoor::IsOpen
  3056. ================
  3057. */
  3058. bool idDoor::IsOpen( void ) {
  3059. return ( moverState != MOVER_POS1 );
  3060. }
  3061. /*
  3062. ================
  3063. idDoor::IsNoTouch
  3064. ================
  3065. */
  3066. bool idDoor::IsNoTouch( void ) {
  3067. return noTouch;
  3068. }
  3069. /*
  3070. ======================
  3071. idDoor::CalcTriggerBounds
  3072. Calcs bounds for a trigger.
  3073. ======================
  3074. */
  3075. void idDoor::CalcTriggerBounds( float size, idBounds &bounds ) {
  3076. idMover_Binary *other;
  3077. int i;
  3078. int best;
  3079. // find the bounds of everything on the team
  3080. bounds = GetPhysics()->GetAbsBounds();
  3081. fl.takedamage = true;
  3082. for( other = activateChain; other != NULL; other = other->GetActivateChain() ) {
  3083. if ( other->IsType( idDoor::Type ) ) {
  3084. // find the bounds of everything on the team
  3085. bounds.AddBounds( other->GetPhysics()->GetAbsBounds() );
  3086. // set all of the slaves as shootable
  3087. other->fl.takedamage = true;
  3088. }
  3089. }
  3090. // find the thinnest axis, which will be the one we expand
  3091. best = 0;
  3092. for ( i = 1 ; i < 3 ; i++ ) {
  3093. if ( bounds[1][ i ] - bounds[0][ i ] < bounds[1][ best ] - bounds[0][ best ] ) {
  3094. best = i;
  3095. }
  3096. }
  3097. normalAxisIndex = best;
  3098. bounds[0][ best ] -= size;
  3099. bounds[1][ best ] += size;
  3100. bounds[0] -= GetPhysics()->GetOrigin();
  3101. bounds[1] -= GetPhysics()->GetOrigin();
  3102. }
  3103. /*
  3104. ======================
  3105. idDoor::Event_StartOpen
  3106. if "start_open", reverse position 1 and 2
  3107. ======================
  3108. */
  3109. void idDoor::Event_StartOpen( void ) {
  3110. float time;
  3111. float speed;
  3112. // if "start_open", reverse position 1 and 2
  3113. pos1 = pos2;
  3114. pos2 = GetPhysics()->GetOrigin();
  3115. spawnArgs.GetFloat( "speed", "400", speed );
  3116. if ( spawnArgs.GetFloat( "time", "1", time ) ) {
  3117. InitTime( pos1, pos2, time, 0, 0 );
  3118. } else {
  3119. InitSpeed( pos1, pos2, speed, 0, 0 );
  3120. }
  3121. }
  3122. /*
  3123. ======================
  3124. idDoor::Event_SpawnDoorTrigger
  3125. All of the parts of a door have been spawned, so create
  3126. a trigger that encloses all of them.
  3127. ======================
  3128. */
  3129. void idDoor::Event_SpawnDoorTrigger( void ) {
  3130. idBounds bounds;
  3131. idMover_Binary *other;
  3132. bool toggle;
  3133. if ( trigger ) {
  3134. // already have a trigger, so don't spawn a new one.
  3135. return;
  3136. }
  3137. // check if any of the doors are marked as toggled
  3138. toggle = false;
  3139. for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
  3140. if ( other->IsType( idDoor::Type ) && other->spawnArgs.GetBool( "toggle" ) ) {
  3141. toggle = true;
  3142. break;
  3143. }
  3144. }
  3145. if ( toggle ) {
  3146. // mark them all as toggled
  3147. for( other = moveMaster; other != NULL; other = other->GetActivateChain() ) {
  3148. if ( other->IsType( idDoor::Type ) ) {
  3149. other->spawnArgs.Set( "toggle", "1" );
  3150. }
  3151. }
  3152. // don't spawn trigger
  3153. return;
  3154. }
  3155. const char *sndtemp = spawnArgs.GetString( "snd_locked" );
  3156. if ( spawnArgs.GetInt( "locked" ) && sndtemp && *sndtemp ) {
  3157. PostEventMS( &EV_Door_SpawnSoundTrigger, 0 );
  3158. }
  3159. CalcTriggerBounds( triggersize, bounds );
  3160. // create a trigger clip model
  3161. trigger = new idClipModel( idTraceModel( bounds ) );
  3162. trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
  3163. trigger->SetContents( CONTENTS_TRIGGER );
  3164. GetLocalTriggerPosition( trigger );
  3165. MatchActivateTeam( moverState, gameLocal.time );
  3166. }
  3167. /*
  3168. ======================
  3169. idDoor::Event_SpawnSoundTrigger
  3170. Spawn a sound trigger to activate locked sound if it exists.
  3171. ======================
  3172. */
  3173. void idDoor::Event_SpawnSoundTrigger( void ) {
  3174. idBounds bounds;
  3175. if ( sndTrigger ) {
  3176. return;
  3177. }
  3178. CalcTriggerBounds( triggersize * 0.5f, bounds );
  3179. // create a trigger clip model
  3180. sndTrigger = new idClipModel( idTraceModel( bounds ) );
  3181. sndTrigger->Link( gameLocal.clip, this, 254, GetPhysics()->GetOrigin(), mat3_identity );
  3182. sndTrigger->SetContents( CONTENTS_TRIGGER );
  3183. GetLocalTriggerPosition( sndTrigger );
  3184. }
  3185. /*
  3186. ================
  3187. idDoor::Event_Reached_BinaryMover
  3188. ================
  3189. */
  3190. void idDoor::Event_Reached_BinaryMover( void ) {
  3191. if ( moverState == MOVER_2TO1 ) {
  3192. SetBlocked( false );
  3193. const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerClosed" );
  3194. while( kv ) {
  3195. idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
  3196. if ( ent ) {
  3197. ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
  3198. }
  3199. kv = spawnArgs.MatchPrefix( "triggerClosed", kv );
  3200. }
  3201. } else if ( moverState == MOVER_1TO2 ) {
  3202. const idKeyValue *kv = spawnArgs.MatchPrefix( "triggerOpened" );
  3203. while( kv ) {
  3204. idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
  3205. if ( ent ) {
  3206. ent->PostEventMS( &EV_Activate, 0, moveMaster->GetActivator() );
  3207. }
  3208. kv = spawnArgs.MatchPrefix( "triggerOpened", kv );
  3209. }
  3210. }
  3211. idMover_Binary::Event_Reached_BinaryMover();
  3212. }
  3213. /*
  3214. ================
  3215. idDoor::Blocked_Door
  3216. ================
  3217. */
  3218. void idDoor::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
  3219. SetBlocked( true );
  3220. if ( crusher ) {
  3221. return; // crushers don't reverse
  3222. }
  3223. // reverse direction
  3224. Use_BinaryMover( moveMaster->GetActivator() );
  3225. if ( companionDoor ) {
  3226. companionDoor->ProcessEvent( &EV_TeamBlocked, blockedEntity, blockingEntity );
  3227. }
  3228. }
  3229. /*
  3230. ===============
  3231. idDoor::SetCompanion
  3232. ===============
  3233. */
  3234. void idDoor::SetCompanion( idDoor *door ) {
  3235. companionDoor = door;
  3236. }
  3237. /*
  3238. ===============
  3239. idDoor::Event_PartBlocked
  3240. ===============
  3241. */
  3242. void idDoor::Event_PartBlocked( idEntity *blockingEntity ) {
  3243. if ( damage > 0.0f ) {
  3244. blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
  3245. }
  3246. }
  3247. /*
  3248. ================
  3249. idDoor::Event_Touch
  3250. ================
  3251. */
  3252. void idDoor::Event_Touch( idEntity *other, trace_t *trace ) {
  3253. idVec3 contact, translate;
  3254. idVec3 planeaxis1, planeaxis2, normal;
  3255. idBounds bounds;
  3256. if ( !enabled ) {
  3257. return;
  3258. }
  3259. if ( trigger && trace->c.id == trigger->GetId() ) {
  3260. if ( !IsNoTouch() && !IsLocked() && GetMoverState() != MOVER_1TO2 ) {
  3261. Use( this, other );
  3262. }
  3263. } else if ( sndTrigger && trace->c.id == sndTrigger->GetId() ) {
  3264. if ( other && other->IsType( idPlayer::Type ) && IsLocked() && gameLocal.time > nextSndTriggerTime ) {
  3265. StartSound( "snd_locked", SND_CHANNEL_ANY, 0, false, NULL );
  3266. nextSndTriggerTime = gameLocal.time + 10000;
  3267. }
  3268. }
  3269. }
  3270. /*
  3271. ================
  3272. idDoor::Event_SpectatorTouch
  3273. ================
  3274. */
  3275. void idDoor::Event_SpectatorTouch( idEntity *other, trace_t *trace ) {
  3276. idVec3 contact, translate, normal;
  3277. idBounds bounds;
  3278. idPlayer *p;
  3279. assert( other && other->IsType( idPlayer::Type ) && static_cast< idPlayer * >( other )->spectating );
  3280. p = static_cast< idPlayer * >( other );
  3281. // avoid flicker when stopping right at clip box boundaries
  3282. if ( p->lastSpectateTeleport > gameLocal.time - 1000 ) {
  3283. return;
  3284. }
  3285. if ( trigger && !IsOpen() ) {
  3286. // teleport to the other side, center to the middle of the trigger brush
  3287. bounds = trigger->GetAbsBounds();
  3288. contact = trace->endpos - bounds.GetCenter();
  3289. translate = bounds.GetCenter();
  3290. normal.Zero();
  3291. normal[ normalAxisIndex ] = 1.0f;
  3292. if ( normal * contact > 0 ) {
  3293. translate[ normalAxisIndex ] += ( bounds[ 0 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
  3294. } else {
  3295. translate[ normalAxisIndex ] += ( bounds[ 1 ][ normalAxisIndex ] - translate[ normalAxisIndex ] ) * 0.5f;
  3296. }
  3297. p->SetOrigin( translate );
  3298. p->lastSpectateTeleport = gameLocal.time;
  3299. }
  3300. }
  3301. /*
  3302. ================
  3303. idDoor::Event_Activate
  3304. ================
  3305. */
  3306. void idDoor::Event_Activate( idEntity *activator ) {
  3307. int old_lock;
  3308. if ( spawnArgs.GetInt( "locked" ) ) {
  3309. if ( !trigger ) {
  3310. PostEventMS( &EV_Door_SpawnDoorTrigger, 0 );
  3311. }
  3312. if ( buddyStr.Length() ) {
  3313. idEntity *buddy = gameLocal.FindEntity( buddyStr );
  3314. if ( buddy ) {
  3315. buddy->SetShaderParm( SHADERPARM_MODE, 1 );
  3316. buddy->UpdateVisuals();
  3317. }
  3318. }
  3319. old_lock = spawnArgs.GetInt( "locked" );
  3320. Lock( 0 );
  3321. if ( old_lock == 2 ) {
  3322. return;
  3323. }
  3324. }
  3325. if ( syncLock.Length() ) {
  3326. idEntity *sync = gameLocal.FindEntity( syncLock );
  3327. if ( sync && sync->IsType( idDoor::Type ) ) {
  3328. if ( static_cast<idDoor *>( sync )->IsOpen() ) {
  3329. return;
  3330. }
  3331. }
  3332. }
  3333. ActivateTargets( activator );
  3334. renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
  3335. UpdateVisuals();
  3336. Use_BinaryMover( activator );
  3337. }
  3338. /*
  3339. ================
  3340. idDoor::Event_Open
  3341. ================
  3342. */
  3343. void idDoor::Event_Open( void ) {
  3344. Open();
  3345. }
  3346. /*
  3347. ================
  3348. idDoor::Event_Close
  3349. ================
  3350. */
  3351. void idDoor::Event_Close( void ) {
  3352. Close();
  3353. }
  3354. /*
  3355. ================
  3356. idDoor::Event_Lock
  3357. ================
  3358. */
  3359. void idDoor::Event_Lock( int f ) {
  3360. Lock( f );
  3361. }
  3362. /*
  3363. ================
  3364. idDoor::Event_IsOpen
  3365. ================
  3366. */
  3367. void idDoor::Event_IsOpen( void ) {
  3368. bool state;
  3369. state = IsOpen();
  3370. idThread::ReturnFloat( state );
  3371. }
  3372. /*
  3373. ================
  3374. idDoor::Event_Locked
  3375. ================
  3376. */
  3377. void idDoor::Event_Locked( void ) {
  3378. idThread::ReturnFloat( spawnArgs.GetInt("locked") );
  3379. }
  3380. /*
  3381. ================
  3382. idDoor::Event_OpenPortal
  3383. Sets the portal associtated with this door to be open
  3384. ================
  3385. */
  3386. void idDoor::Event_OpenPortal( void ) {
  3387. idMover_Binary *slave;
  3388. idDoor *slaveDoor;
  3389. for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
  3390. if ( slave->IsType( idDoor::Type ) ) {
  3391. slaveDoor = static_cast<idDoor *>( slave );
  3392. if ( slaveDoor->areaPortal ) {
  3393. slaveDoor->SetPortalState( true );
  3394. }
  3395. slaveDoor->SetAASAreaState( false );
  3396. }
  3397. }
  3398. }
  3399. /*
  3400. ================
  3401. idDoor::Event_ClosePortal
  3402. Sets the portal associtated with this door to be closed
  3403. ================
  3404. */
  3405. void idDoor::Event_ClosePortal( void ) {
  3406. idMover_Binary *slave;
  3407. idDoor *slaveDoor;
  3408. for ( slave = this; slave != NULL; slave = slave->GetActivateChain() ) {
  3409. if ( !slave->IsHidden() ) {
  3410. if ( slave->IsType( idDoor::Type ) ) {
  3411. slaveDoor = static_cast<idDoor *>( slave );
  3412. if ( slaveDoor->areaPortal ) {
  3413. slaveDoor->SetPortalState( false );
  3414. }
  3415. slaveDoor->SetAASAreaState( IsLocked() || IsNoTouch() );
  3416. }
  3417. }
  3418. }
  3419. }
  3420. /*
  3421. ===============================================================================
  3422. idPlat
  3423. ===============================================================================
  3424. */
  3425. CLASS_DECLARATION( idMover_Binary, idPlat )
  3426. EVENT( EV_Touch, idPlat::Event_Touch )
  3427. EVENT( EV_TeamBlocked, idPlat::Event_TeamBlocked )
  3428. EVENT( EV_PartBlocked, idPlat::Event_PartBlocked )
  3429. END_CLASS
  3430. /*
  3431. ===============
  3432. idPlat::idPlat
  3433. ===============
  3434. */
  3435. idPlat::idPlat( void ) {
  3436. trigger = NULL;
  3437. localTriggerOrigin.Zero();
  3438. localTriggerAxis.Identity();
  3439. }
  3440. /*
  3441. ===============
  3442. idPlat::~idPlat
  3443. ===============
  3444. */
  3445. idPlat::~idPlat( void ) {
  3446. if ( trigger ) {
  3447. delete trigger;
  3448. }
  3449. }
  3450. /*
  3451. ===============
  3452. idPlat::Save
  3453. ===============
  3454. */
  3455. void idPlat::Save( idSaveGame *savefile ) const {
  3456. savefile->WriteClipModel( trigger );
  3457. savefile->WriteVec3( localTriggerOrigin );
  3458. savefile->WriteMat3( localTriggerAxis );
  3459. }
  3460. /*
  3461. ===============
  3462. idPlat::Restore
  3463. ===============
  3464. */
  3465. void idPlat::Restore( idRestoreGame *savefile ) {
  3466. savefile->ReadClipModel( trigger );
  3467. savefile->ReadVec3( localTriggerOrigin );
  3468. savefile->ReadMat3( localTriggerAxis );
  3469. }
  3470. /*
  3471. ===============
  3472. idPlat::Spawn
  3473. ===============
  3474. */
  3475. void idPlat::Spawn( void ) {
  3476. float lip;
  3477. float height;
  3478. float time;
  3479. float speed;
  3480. float accel;
  3481. float decel;
  3482. bool noTouch;
  3483. spawnArgs.GetFloat( "speed", "100", speed );
  3484. spawnArgs.GetFloat( "damage", "0", damage );
  3485. spawnArgs.GetFloat( "wait", "1", wait );
  3486. spawnArgs.GetFloat( "lip", "8", lip );
  3487. spawnArgs.GetFloat( "accel_time", "0.25", accel );
  3488. spawnArgs.GetFloat( "decel_time", "0.25", decel );
  3489. // create second position
  3490. if ( !spawnArgs.GetFloat( "height", "0", height ) ) {
  3491. height = ( GetPhysics()->GetBounds()[1][2] - GetPhysics()->GetBounds()[0][2] ) - lip;
  3492. }
  3493. spawnArgs.GetBool( "no_touch", "0", noTouch );
  3494. // pos1 is the rest (bottom) position, pos2 is the top
  3495. pos2 = GetPhysics()->GetOrigin();
  3496. pos1 = pos2;
  3497. pos1[2] -= height;
  3498. if ( spawnArgs.GetFloat( "time", "1", time ) ) {
  3499. InitTime( pos1, pos2, time, accel, decel );
  3500. } else {
  3501. InitSpeed( pos1, pos2, speed, accel, decel );
  3502. }
  3503. SetMoverState( MOVER_POS1, gameLocal.time );
  3504. UpdateVisuals();
  3505. // spawn the trigger if one hasn't been custom made
  3506. if ( !noTouch ) {
  3507. // spawn trigger
  3508. SpawnPlatTrigger( pos1 );
  3509. }
  3510. }
  3511. /*
  3512. ================
  3513. idPlat::Think
  3514. ================
  3515. */
  3516. void idPlat::Think( void ) {
  3517. idVec3 masterOrigin;
  3518. idMat3 masterAxis;
  3519. idMover_Binary::Think();
  3520. if ( thinkFlags & TH_PHYSICS ) {
  3521. // update trigger position
  3522. if ( GetMasterPosition( masterOrigin, masterAxis ) ) {
  3523. if ( trigger ) {
  3524. trigger->Link( gameLocal.clip, this, 0, masterOrigin + localTriggerOrigin * masterAxis, localTriggerAxis * masterAxis );
  3525. }
  3526. }
  3527. }
  3528. }
  3529. /*
  3530. ================
  3531. idPlat::PreBind
  3532. ================
  3533. */
  3534. void idPlat::PreBind( void ) {
  3535. idMover_Binary::PreBind();
  3536. }
  3537. /*
  3538. ================
  3539. idPlat::PostBind
  3540. ================
  3541. */
  3542. void idPlat::PostBind( void ) {
  3543. idMover_Binary::PostBind();
  3544. GetLocalTriggerPosition( trigger );
  3545. }
  3546. /*
  3547. ================
  3548. idPlat::GetLocalTriggerPosition
  3549. ================
  3550. */
  3551. void idPlat::GetLocalTriggerPosition( const idClipModel *trigger ) {
  3552. idVec3 origin;
  3553. idMat3 axis;
  3554. if ( !trigger ) {
  3555. return;
  3556. }
  3557. GetMasterPosition( origin, axis );
  3558. localTriggerOrigin = ( trigger->GetOrigin() - origin ) * axis.Transpose();
  3559. localTriggerAxis = trigger->GetAxis() * axis.Transpose();
  3560. }
  3561. /*
  3562. ==============
  3563. idPlat::SpawnPlatTrigger
  3564. ===============
  3565. */
  3566. void idPlat::SpawnPlatTrigger( idVec3 &pos ) {
  3567. idBounds bounds;
  3568. idVec3 tmin;
  3569. idVec3 tmax;
  3570. // the middle trigger will be a thin trigger just
  3571. // above the starting position
  3572. bounds = GetPhysics()->GetBounds();
  3573. tmin[0] = bounds[0][0] + 33;
  3574. tmin[1] = bounds[0][1] + 33;
  3575. tmin[2] = bounds[0][2];
  3576. tmax[0] = bounds[1][0] - 33;
  3577. tmax[1] = bounds[1][1] - 33;
  3578. tmax[2] = bounds[1][2] + 8;
  3579. if ( tmax[0] <= tmin[0] ) {
  3580. tmin[0] = ( bounds[0][0] + bounds[1][0] ) * 0.5f;
  3581. tmax[0] = tmin[0] + 1;
  3582. }
  3583. if ( tmax[1] <= tmin[1] ) {
  3584. tmin[1] = ( bounds[0][1] + bounds[1][1] ) * 0.5f;
  3585. tmax[1] = tmin[1] + 1;
  3586. }
  3587. trigger = new idClipModel( idTraceModel( idBounds( tmin, tmax ) ) );
  3588. trigger->Link( gameLocal.clip, this, 255, GetPhysics()->GetOrigin(), mat3_identity );
  3589. trigger->SetContents( CONTENTS_TRIGGER );
  3590. }
  3591. /*
  3592. ==============
  3593. idPlat::Event_Touch
  3594. ===============
  3595. */
  3596. void idPlat::Event_Touch( idEntity *other, trace_t *trace ) {
  3597. if ( !other->IsType( idPlayer::Type ) ) {
  3598. return;
  3599. }
  3600. if ( ( GetMoverState() == MOVER_POS1 ) && trigger && ( trace->c.id == trigger->GetId() ) && ( other->health > 0 ) ) {
  3601. Use_BinaryMover( other );
  3602. }
  3603. }
  3604. /*
  3605. ================
  3606. idPlat::Event_TeamBlocked
  3607. ================
  3608. */
  3609. void idPlat::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
  3610. // reverse direction
  3611. Use_BinaryMover( activatedBy.GetEntity() );
  3612. }
  3613. /*
  3614. ===============
  3615. idPlat::Event_PartBlocked
  3616. ===============
  3617. */
  3618. void idPlat::Event_PartBlocked( idEntity *blockingEntity ) {
  3619. if ( damage > 0.0f ) {
  3620. blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
  3621. }
  3622. }
  3623. /*
  3624. ===============================================================================
  3625. idMover_Periodic
  3626. ===============================================================================
  3627. */
  3628. CLASS_DECLARATION( idEntity, idMover_Periodic )
  3629. EVENT( EV_TeamBlocked, idMover_Periodic::Event_TeamBlocked )
  3630. EVENT( EV_PartBlocked, idMover_Periodic::Event_PartBlocked )
  3631. END_CLASS
  3632. /*
  3633. ===============
  3634. idMover_Periodic::idMover_Periodic
  3635. ===============
  3636. */
  3637. idMover_Periodic::idMover_Periodic( void ) {
  3638. damage = 0.0f;
  3639. fl.neverDormant = false;
  3640. }
  3641. /*
  3642. ===============
  3643. idMover_Periodic::Spawn
  3644. ===============
  3645. */
  3646. void idMover_Periodic::Spawn( void ) {
  3647. spawnArgs.GetFloat( "damage", "0", damage );
  3648. if ( !spawnArgs.GetBool( "solid", "1" ) ) {
  3649. GetPhysics()->SetContents( 0 );
  3650. }
  3651. }
  3652. /*
  3653. ===============
  3654. idMover_Periodic::Save
  3655. ===============
  3656. */
  3657. void idMover_Periodic::Save( idSaveGame *savefile ) const {
  3658. savefile->WriteFloat( damage );
  3659. savefile->WriteStaticObject( physicsObj );
  3660. }
  3661. /*
  3662. ===============
  3663. idMover_Periodic::Restore
  3664. ===============
  3665. */
  3666. void idMover_Periodic::Restore( idRestoreGame *savefile ) {
  3667. savefile->ReadFloat( damage );
  3668. savefile->ReadStaticObject( physicsObj );
  3669. RestorePhysics( &physicsObj );
  3670. }
  3671. /*
  3672. ================
  3673. idMover_Periodic::Think
  3674. ================
  3675. */
  3676. void idMover_Periodic::Think( void ) {
  3677. // if we are completely closed off from the player, don't do anything at all
  3678. if ( CheckDormant() ) {
  3679. return;
  3680. }
  3681. RunPhysics();
  3682. Present();
  3683. }
  3684. /*
  3685. ===============
  3686. idMover_Periodic::Event_TeamBlocked
  3687. ===============
  3688. */
  3689. void idMover_Periodic::Event_TeamBlocked( idEntity *blockedEntity, idEntity *blockingEntity ) {
  3690. }
  3691. /*
  3692. ===============
  3693. idMover_Periodic::Event_PartBlocked
  3694. ===============
  3695. */
  3696. void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) {
  3697. if ( damage > 0.0f ) {
  3698. blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
  3699. }
  3700. }
  3701. /*
  3702. ================
  3703. idMover_Periodic::WriteToSnapshot
  3704. ================
  3705. */
  3706. void idMover_Periodic::WriteToSnapshot( idBitMsgDelta &msg ) const {
  3707. physicsObj.WriteToSnapshot( msg );
  3708. WriteBindToSnapshot( msg );
  3709. }
  3710. /*
  3711. ================
  3712. idMover_Periodic::ReadFromSnapshot
  3713. ================
  3714. */
  3715. void idMover_Periodic::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  3716. physicsObj.ReadFromSnapshot( msg );
  3717. ReadBindFromSnapshot( msg );
  3718. if ( msg.HasChanged() ) {
  3719. UpdateVisuals();
  3720. }
  3721. }
  3722. /*
  3723. ===============================================================================
  3724. idRotater
  3725. ===============================================================================
  3726. */
  3727. CLASS_DECLARATION( idMover_Periodic, idRotater )
  3728. EVENT( EV_Activate, idRotater::Event_Activate )
  3729. END_CLASS
  3730. /*
  3731. ===============
  3732. idRotater::idRotater
  3733. ===============
  3734. */
  3735. idRotater::idRotater( void ) {
  3736. activatedBy = this;
  3737. }
  3738. /*
  3739. ===============
  3740. idRotater::Spawn
  3741. ===============
  3742. */
  3743. void idRotater::Spawn( void ) {
  3744. physicsObj.SetSelf( this );
  3745. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  3746. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  3747. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  3748. physicsObj.SetClipMask( MASK_SOLID );
  3749. if ( !spawnArgs.GetBool( "nopush" ) ) {
  3750. physicsObj.SetPusher( 0 );
  3751. }
  3752. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, gameLocal.time, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
  3753. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, GetPhysics()->GetAxis().ToAngles(), ang_zero, ang_zero );
  3754. SetPhysics( &physicsObj );
  3755. if ( spawnArgs.GetBool( "start_on" ) ) {
  3756. ProcessEvent( &EV_Activate, this );
  3757. }
  3758. }
  3759. /*
  3760. ===============
  3761. idRotater::Save
  3762. ===============
  3763. */
  3764. void idRotater::Save( idSaveGame *savefile ) const {
  3765. activatedBy.Save( savefile );
  3766. }
  3767. /*
  3768. ===============
  3769. idRotater::Restore
  3770. ===============
  3771. */
  3772. void idRotater::Restore( idRestoreGame *savefile ) {
  3773. activatedBy.Restore( savefile );
  3774. }
  3775. /*
  3776. ===============
  3777. idRotater::Event_Activate
  3778. ===============
  3779. */
  3780. void idRotater::Event_Activate( idEntity *activator ) {
  3781. float speed;
  3782. bool x_axis;
  3783. bool y_axis;
  3784. idAngles delta;
  3785. activatedBy = activator;
  3786. delta.Zero();
  3787. if ( !spawnArgs.GetBool( "rotate" ) ) {
  3788. spawnArgs.Set( "rotate", "1" );
  3789. spawnArgs.GetFloat( "speed", "100", speed );
  3790. spawnArgs.GetBool( "x_axis", "0", x_axis );
  3791. spawnArgs.GetBool( "y_axis", "0", y_axis );
  3792. // set the axis of rotation
  3793. if ( x_axis ) {
  3794. delta[2] = speed;
  3795. } else if ( y_axis ) {
  3796. delta[0] = speed;
  3797. } else {
  3798. delta[1] = speed;
  3799. }
  3800. } else {
  3801. spawnArgs.Set( "rotate", "0" );
  3802. }
  3803. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero );
  3804. }
  3805. /*
  3806. ===============================================================================
  3807. idBobber
  3808. ===============================================================================
  3809. */
  3810. CLASS_DECLARATION( idMover_Periodic, idBobber )
  3811. END_CLASS
  3812. /*
  3813. ===============
  3814. idBobber::idBobber
  3815. ===============
  3816. */
  3817. idBobber::idBobber( void ) {
  3818. }
  3819. /*
  3820. ===============
  3821. idBobber::Spawn
  3822. ===============
  3823. */
  3824. void idBobber::Spawn( void ) {
  3825. float speed;
  3826. float height;
  3827. float phase;
  3828. bool x_axis;
  3829. bool y_axis;
  3830. idVec3 delta;
  3831. spawnArgs.GetFloat( "speed", "4", speed );
  3832. spawnArgs.GetFloat( "height", "32", height );
  3833. spawnArgs.GetFloat( "phase", "0", phase );
  3834. spawnArgs.GetBool( "x_axis", "0", x_axis );
  3835. spawnArgs.GetBool( "y_axis", "0", y_axis );
  3836. // set the axis of bobbing
  3837. delta = vec3_origin;
  3838. if ( x_axis ) {
  3839. delta[ 0 ] = height;
  3840. } else if ( y_axis ) {
  3841. delta[ 1 ] = height;
  3842. } else {
  3843. delta[ 2 ] = height;
  3844. }
  3845. physicsObj.SetSelf( this );
  3846. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  3847. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  3848. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  3849. physicsObj.SetClipMask( MASK_SOLID );
  3850. if ( !spawnArgs.GetBool( "nopush" ) ) {
  3851. physicsObj.SetPusher( 0 );
  3852. }
  3853. physicsObj.SetLinearExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, speed * 500, GetPhysics()->GetOrigin(), delta * 2.0f, vec3_origin );
  3854. SetPhysics( &physicsObj );
  3855. }
  3856. /*
  3857. ===============================================================================
  3858. idPendulum
  3859. ===============================================================================
  3860. */
  3861. CLASS_DECLARATION( idMover_Periodic, idPendulum )
  3862. END_CLASS
  3863. /*
  3864. ===============
  3865. idPendulum::idPendulum
  3866. ===============
  3867. */
  3868. idPendulum::idPendulum( void ) {
  3869. }
  3870. /*
  3871. ===============
  3872. idPendulum::Spawn
  3873. ===============
  3874. */
  3875. void idPendulum::Spawn( void ) {
  3876. float speed;
  3877. float freq;
  3878. float length;
  3879. float phase;
  3880. spawnArgs.GetFloat( "speed", "30", speed );
  3881. spawnArgs.GetFloat( "phase", "0", phase );
  3882. if ( spawnArgs.GetFloat( "freq", "", freq ) ) {
  3883. if ( freq <= 0.0f ) {
  3884. gameLocal.Error( "Invalid frequency on entity '%s'", GetName() );
  3885. }
  3886. } else {
  3887. // find pendulum length
  3888. length = idMath::Fabs( GetPhysics()->GetBounds()[0][2] );
  3889. if ( length < 8 ) {
  3890. length = 8;
  3891. }
  3892. freq = 1 / ( idMath::TWO_PI ) * idMath::Sqrt( g_gravity.GetFloat() / ( 3 * length ) );
  3893. }
  3894. physicsObj.SetSelf( this );
  3895. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  3896. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  3897. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  3898. physicsObj.SetClipMask( MASK_SOLID );
  3899. if ( !spawnArgs.GetBool( "nopush" ) ) {
  3900. physicsObj.SetPusher( 0 );
  3901. }
  3902. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
  3903. physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase * 1000, 500/freq, GetPhysics()->GetAxis().ToAngles(), idAngles( 0, 0, speed * 2.0f ), ang_zero );
  3904. SetPhysics( &physicsObj );
  3905. }
  3906. /*
  3907. ===============================================================================
  3908. idBobber
  3909. ===============================================================================
  3910. */
  3911. CLASS_DECLARATION( idMover_Periodic, idRiser )
  3912. EVENT( EV_Activate, idRiser::Event_Activate )
  3913. END_CLASS
  3914. /*
  3915. ===============
  3916. idRiser::idRiser
  3917. ===============
  3918. */
  3919. idRiser::idRiser( void ) {
  3920. }
  3921. /*
  3922. ===============
  3923. idRiser::Spawn
  3924. ===============
  3925. */
  3926. void idRiser::Spawn( void ) {
  3927. physicsObj.SetSelf( this );
  3928. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  3929. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  3930. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  3931. physicsObj.SetClipMask( MASK_SOLID );
  3932. if ( !spawnArgs.GetBool( "solid", "1" ) ) {
  3933. physicsObj.SetContents( 0 );
  3934. }
  3935. if ( !spawnArgs.GetBool( "nopush" ) ) {
  3936. physicsObj.SetPusher( 0 );
  3937. }
  3938. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_NONE, 0, 0, GetPhysics()->GetOrigin(), vec3_origin, vec3_origin );
  3939. SetPhysics( &physicsObj );
  3940. }
  3941. /*
  3942. ================
  3943. idRiser::Event_Activate
  3944. ================
  3945. */
  3946. void idRiser::Event_Activate( idEntity *activator ) {
  3947. if ( !IsHidden() && spawnArgs.GetBool("hide") ) {
  3948. Hide();
  3949. } else {
  3950. Show();
  3951. float time;
  3952. float height;
  3953. idVec3 delta;
  3954. spawnArgs.GetFloat( "time", "4", time );
  3955. spawnArgs.GetFloat( "height", "32", height );
  3956. delta = vec3_origin;
  3957. delta[ 2 ] = height;
  3958. physicsObj.SetLinearExtrapolation( EXTRAPOLATION_LINEAR, gameLocal.time, time * 1000, physicsObj.GetOrigin(), delta, vec3_origin );
  3959. }
  3960. }