Mover.cpp 115 KB

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