Mover.cpp 111 KB

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