play.cpp 184 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // play.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // This module deals with the high-level aspects of setting up and running the
  22. // game.
  23. //
  24. // History:
  25. // 11/19/96 MJR Started.
  26. //
  27. // A huge number of changes occurred, and then the entire module was
  28. // reorganized, to the point where the previous history was no longer
  29. // relevant. This history was was purged on 8/3/97 -- if you need to
  30. // refer back to it, simply go back before this date in SourceSafe.
  31. //
  32. // 08/03/97 MJR A total reorganization occurs.
  33. //
  34. // 08/05/97 JMI Changed uses of CRealm::m_bMultiplayer to
  35. // CRealm::m_flags.bMultiplayer.
  36. //
  37. // 08/06/97 MJR Fixed bug when going to next level or restarting.
  38. //
  39. // 08/06/97 JMI Now Play_VerifyQuitMenuChoice() plays the appropriate sound
  40. // as to whether there was a selection change or an item was
  41. // chosen.
  42. // Also, changed uses of InitLocalInput() to ClearLocalInput().
  43. //
  44. // 08/08/97 MJR Moved background/foreground callbacks to game.cpp.
  45. //
  46. // 08/08/97 MJR Fixed multiplayer go-to-next-level bug.
  47. // Got the abort message working properly.
  48. //
  49. // 08/08/97 JMI CPlayRealm::EndRealm() now only updates players stockpiles
  50. // if we are not restarting the current level. This way, in
  51. // single player mode, when we restart the level, you don't
  52. // get a combo of the ammo you had when you died and the warp
  53. // but rather a combo of the ammo you had when you entered the
  54. // level and the warp.
  55. //
  56. // 08/08/97 JMI After a realm play when 'Just one realm' was specified,
  57. // the 'Game Over' flag would get set regardless of whether
  58. // the player had chosen to restart the realm. Fixed.
  59. //
  60. // 08/09/97 JMI CoreLoopRender() and CoreLoopUserInput() were checking
  61. // m_bCheckForAbortKey without first checking if we're in
  62. // network mode. This flag is not used in non-net mode so we
  63. // must check before using it.
  64. //
  65. // 08/09/97 JRD Changed play to call the new toolbar render, and modified the
  66. // score render to include the background bitmap.
  67. //
  68. // 08/11/97 JMI Changed two occurrences of sRealNum to info.m_sRealNum.
  69. // sRealmNum is the passed in start realm and info.m_sRealNum
  70. // is the current realm.
  71. //
  72. // 08/11/97 MJR Fixed a bug where time wasn't being updated properly (and
  73. // thereby was at least one reason for sync problems.)
  74. //
  75. // 08/12/97 JMI Now that cheats require an input event, we only pass it to
  76. // GetLocalInput() in singel player mode. Since the two ways
  77. // of getting input for are so different, it makes it difficult
  78. // to hack cheats into multiplayer mode.
  79. //
  80. // 08/13/97 MJR Cleaned up use of info flags to try to simplify and
  81. // make sure no race conditions exist.
  82. //
  83. // Fixed bug when trying to resume paused game (wasn't
  84. // filtering out modifier keys -- now it does).
  85. //
  86. // 08/13/97 JMI Fixed positioning macros so they are nearly constant (i.e,
  87. // changes in the g_pimScreenBuf could cause it to be non-
  88. // constant).
  89. // Fixed portions of the code that updated the realm status
  90. // using the INFO_STATUS_* macros.
  91. // Moved the initial drawing of the toolbar into
  92. // CPlayStatus::StartRealm().
  93. // Now utilizes the return value from ToolbarRender() to de-
  94. // termine whether to update that area of the display.
  95. //
  96. // 08/14/97 JMI Took 'again' out of "Hit <pause> key again to resume"
  97. // paused message.
  98. // Also, RespondToMenuRequest() now clears all events before
  99. // starting menu.
  100. // Made XRay All key a toggle.
  101. // Changed name of difficulty parameter to Play() from
  102. // bDifficulty to sDifficulty.
  103. // Now uses sDifficulty paramter to Play().
  104. // Added sDifficulty paramter to
  105. // Play_GetRealmSectionAndEntry().
  106. // Converted ms_bQuitVerified to ms_menuaction and added two
  107. // actions: MenuActionQuit and MenuActionSaveGame.
  108. // Now passes difficulty to Game_SavePlayersGame() which is
  109. // now called from RespondToMenuRequest().
  110. //
  111. // 08/14/97 JMI Converted Play_VerifyQuitMenuChoice() to returning true to
  112. // accept or false to deny.
  113. //
  114. // 08/17/97 JMI Now disables postal organ option from within the game.
  115. //
  116. // 08/17/97 MJR Now loads abort gui from g_resmgrShell.
  117. //
  118. // 08/18/97 JMI Was still clearing KEY_RESTART as a left over from when we
  119. // would use KEY_RESTART to flag restarting a level in single
  120. // player (nowadays uses INPUT_REVIVE).
  121. // Also, was able to get rid of INPUT_JUMP which was left over
  122. // from when we converted to INPUT_REVIVE but play.cpp was
  123. // under different construction.
  124. //
  125. // 08/18/97 JMI Now turns on XRay all when the local dude dies.
  126. //
  127. // 08/18/97 JMI Added variable that, when true, allows advancing to the next
  128. // level without meeting the level goal.
  129. // Also, now in multiplayer mode, the server can advance the
  130. // level without meeting the level goal.
  131. //
  132. // 08/19/97 MJR Added supoprt for new MP parameters.
  133. //
  134. // 08/20/97 JMI Now responds to INPUT_CHEAT_29 by advancing the level if
  135. // NOT a sales demo.
  136. //
  137. // 08/20/97 BRH In the Play function, I used the flags passed in to
  138. // determine and set the scoring mode in the realm.
  139. //
  140. // 08/21/97 JMI Now keeps the global savable stockpile up to date.
  141. //
  142. // 08/21/97 JMI Changed call to Update() to UpdateSystem() and occurrences
  143. // of rspUpdateDisplay() to UpdateDisplay().
  144. //
  145. // 08/22/97 JMI Changed calls to UpdateDisplay() back to rspUpdateDisplay()
  146. // since we no longer need UpdateDisplay() now that we are
  147. // using rspLock/Unlock* functions properly.
  148. // Also, now locks the composite buffer before accessing it
  149. // and unlocks it before updating the screen. This required
  150. // breaking CoreLoopRender() into CoreLoopRender() and
  151. // CoreLoopDraw().
  152. //
  153. // 08/23/97 JMI Now 'Save' menu option is disabled in multiplayer mode.
  154. //
  155. // 08/24/97 JMI Added a timeout to the abortion of playing samples just in
  156. // case there's a bug or a sound driver problem (no need to
  157. // to taunt infinite loopage).
  158. //
  159. // 08/24/97 JMI Moved code to stop all samples into a function so we could
  160. // call it in two places.
  161. // Now used before starting the load b/c playing samples sound
  162. // too shitty during loads.
  163. //
  164. // 08/24/97 JMI Check for INPUT_CHEAT_29 was incorrectly using
  165. // INPUT_CHEAT_29 as a mask instead of INPUT_WEAPONS_MASK so
  166. // other cheats that included all the same mask bits could
  167. // cause 29 to be activated (there was only one, of course,
  168. // INPUT_CHEAT_30).
  169. //
  170. // 08/25/97 JMI Now uses toolbar initialized score font colors for debug
  171. // display info text.
  172. //
  173. // 08/26/97 BRH Added special cases for the final ending demo level.
  174. // Now when it is determined that the player won, it sets
  175. // the global g_bLastLevelDemo so that the ending demo will
  176. // be shown after the final game level which is the air
  177. // force base. Also made a few special cases so that the
  178. // Cutscene shown is the one loaded from the RealmEnd section
  179. // of the realms.ini file, and that the toolbars are not
  180. // shown during the final level demo.
  181. //
  182. // 08/26/97 JMI Moved m_bXRayAll to CPlayInfo so it could be accessed from
  183. // anywhere.
  184. // Fixed problem where, when you come back to life in MP mode
  185. // or via cheat, the XRay would stay on even if the user
  186. // setting was off.
  187. //
  188. // 08/27/97 JMI Changed PAUSED_FONT_HEIGHT to 48 (was 50). Apparently, we
  189. // cannot use a size that is larger than the largest cached
  190. // font size. So all font sizes for the Smash font must be
  191. // less than or equal to 48.
  192. //
  193. // 08/27/97 MJR Updated to use new union name in NetMsg.
  194. // Now sets dude ID for all players in MP mode.
  195. // Now sends and receives special peer data.
  196. //
  197. // 08/28/97 MJR Merged CPlayClient and CPlayServer into CPlayNet.
  198. //
  199. // xx/xx/97 MJR HUGE CHANGES to incorporate new network scheme.
  200. //
  201. // ==========================================================
  202. // 09/05/97 MJR MERGED ALL THE CHANGES FROM THE SEPARATE BRANCH OF PLAY.CPP
  203. // WHICH IS WHERE THE FOLLOWING CHANGES CAME FROM
  204. // ==========================================================
  205. //
  206. // 08/30/97 BRH Fixed paths for installer. The levels were still trying
  207. // to load from the HD path but they should load from the CD
  208. // path.
  209. //
  210. // 08/30/97 JMI If the player hits space to restart, we check if the goal
  211. // was met and, if so, show the high score dialogs.
  212. //
  213. // 09/02/97 JMI Now Purges all resources from g_resmgrGame, Samples, and
  214. // Res on certain systems.
  215. //
  216. // 09/03/97 JMI I realized that the last change would cause an
  217. // unnecessarily long load for restarting a realm so now it
  218. // only does the purging (on the MAC) if we're not restarting
  219. // the realm.
  220. //
  221. // 09/03/97 JMI Changed the check for the end of the demo to use IsDead()
  222. // instead of State_Dead for determining whether the dude is
  223. // dead. Also, now checks InputIsDemoOver().
  224. //
  225. // 09/03/97 JMI Now checks to make sure we're in SP mode before pausing
  226. // while in the background.
  227. //
  228. // 09/04/97 BRH Play no longer sets the full path to the realm file to
  229. // load. It is done in Realm::Load instead so that we can
  230. // try several paths. This way the realms can be loaded
  231. // from the HD path, or if not there, loaded from the CD
  232. // path. Then if someone wants to insert their level, or
  233. // we want to provide an updated level, they can copy it
  234. // to the mirror path on their HD and it will attempt to
  235. // load that one first.
  236. //
  237. // ==========================================================
  238. // Finished merging separate branches of PLAY.CPP.
  239. // ==========================================================
  240. //
  241. // 09/06/97 MJR Fixed bug in SetupDudes() that caused crash in single
  242. // player mode.
  243. //
  244. // 09/06/97 MJR Now allows menu to be used in MP mode.
  245. // Cleaned up how local user quits are handled in MP mode.
  246. // Properly uses abort gui thing.
  247. //
  248. // 09/07/97 JMI Now displays the high scores at the end of each MP level.
  249. // Also, now defaults to 99 (instead of 10) kills when neither
  250. // a time or a kill limit is specified.
  251. //
  252. // 09/07/97 MJR Fixed bug that prevented end-of-game sequence from working.
  253. // Now ignores keyboard input during end-of-game sequence.
  254. //
  255. // 09/08/97 MJR Centered net prog gui thingy.
  256. //
  257. // 09/11/97 JMI Added support for ENABLE_PLAY_SPECIFIC_REALMS_ONLY which
  258. // only allows you to play a realm whose name is jumbled in
  259. // ms_szSingleRealmPostFix[].
  260. //
  261. // 09/12/97 MJR In MP game, if a realm can't be loaded, we either abort
  262. // the game if we're the server or we drop out of the game
  263. // if we're a client.
  264. //
  265. // Also removed the ASSERT() from CInfo.GameOver(), which
  266. // used to not get called in MP mode, but now does due to
  267. // our sudden use of "just one realm" mode in cases where
  268. // the server only has one realm available.
  269. //
  270. // 09/16/97 MJR Removed the JUMBLE stuff, which was made obsolete when we
  271. // switched to embedding the realm files in the executable.
  272. //
  273. // 09/29/97 JMI Now updates areas of the display that were blanked by
  274. // ScaleFilm() (called from CPlayRealm::CoreLoopRender() ) in
  275. // CPlayInfo::UpdateBlankedAreas() (called from
  276. // CPlayRealm::CoreLoopDraw() ). Since, when ScaleFilm() is
  277. // called, we are inside a rspLock/UnlockBuffer() pair, we
  278. // cannot call rspUpdateDisplay() there.
  279. //
  280. // 10/30/97 JMI Used to use a flag to indicate whether CInfo::m_rc* needed
  281. // to be updated. Now we simply check whether m_rc*.sW & sH
  282. // are greater than 0 so we need to make sure they're
  283. // initialized to zero. It didn't show up on the PC b/c Blue
  284. // does not allow negative widths/heights to be drawn but on
  285. // the Mac it seems to cause a rather bizarre mess.
  286. //
  287. // 11/19/97 JMI The m_bDrawFrame flag was not being set to false when
  288. // bDoFrame (in CPlayRealm::CoreLoopRender() ) was false. The
  289. // result was that while a net game was idle of input, the
  290. // display was still being updated. Once this was changed and
  291. // m_bDrawFrame was moved into CPlayInfo (so all CPlayXxxx's
  292. // could utilize it), the idle looping increased in speed by
  293. // approximately 10 times on my machine. The next logical
  294. // step would be to use this flag to reduce the number of
  295. // calls to ToolBarRender() and ScoreUpdateDisplay(). There's
  296. // a possible order problem with simply checking m_bDrawFrame
  297. // since it is set to false or true in
  298. // CPlayRealm::CoreLoopRender() and ToolBarRender() and
  299. // ScoreUpdateDisplay() are called in
  300. // CPlayStatus::CoreLoopRender().
  301. //
  302. // 11/20/97 JMI Added net chat and dirty rects. Now most things don't have to
  303. // bother implementing an CoreLoopRender() just for the sake of
  304. // updating an area they already processed. Now, in
  305. // CoreLoopRender(), just do a pinfo->m_drl.Add(x, y, w, h) of the
  306. // area dirtied and it will be combined with everyone else's area
  307. // and updated to the screen (usually in one chunk if the film
  308. // size has not been altered).
  309. // More testing needs to be done, though. Playing against all
  310. // P200s, the game ran fine. But with a P120, it ran poorly.
  311. // We only tried once though...not sure there's really a
  312. // problem (also the P120 was the only machine with Win95...).
  313. //
  314. // 11/20/97 JMI Added bCoopLevels & bCoopMode parameters to
  315. // Play_GetRealmInfo() and Play_GetRealmSectionAndEntry()
  316. // calls.
  317. // Also, added sCoopLevels & sCoopMode to Play() call.
  318. // Also, fixed a bug in Play_GetRealmInfo() where it would
  319. // write one byte off the end of the pszTitle parameter.
  320. //
  321. // 11/25/97 JMI Changed the chats' .GUIs to be loaded from the HD
  322. // instead of from the VD so we can guarantee the new assets
  323. // get loaded (since they'll use their old Postal disc, we
  324. // cannot load the .GUIs from the CD).
  325. //
  326. // 06/04/98 BRH Set the cutscene mode to simple mode if this is a spawn
  327. // build, since the spawn version only has 1 default cutscene
  328. // bitmap, it has to use this for all cutscenes.
  329. //
  330. // 10/07/99 JMI Changed play loop to get the number of single player levels
  331. // from the INI. Previously, it was 16.
  332. //
  333. ////////////////////////////////////////////////////////////////////////////////
  334. #define PLAY_CPP
  335. #include "RSPiX.h"
  336. #include "main.h"
  337. #include "input.h"
  338. #include "game.h"
  339. #include "update.h"
  340. #include "realm.h"
  341. #include "camera.h"
  342. #include "grip.h"
  343. #include "thing.h"
  344. #include "dude.h"
  345. #include "hood.h"
  346. #include "input.h"
  347. #include "menus.h"
  348. #include "SampleMaster.h"
  349. #include "reality.h"
  350. #include "NetDlg.h"
  351. #include "cutscene.h"
  352. #include "play.h"
  353. #include "warp.h"
  354. #include "scene.h"
  355. #include "score.h"
  356. #include "person.h"
  357. #include "InputSettingsDlg.h"
  358. #include "toolbar.h"
  359. #include "title.h"
  360. #include "credits.h"
  361. #ifdef WIN32
  362. #include "log.h"
  363. #endif
  364. #if defined(WIN32)
  365. // For file timestamp.
  366. #include <windows.h>
  367. #include <time.h>
  368. #include <sys/types.h>
  369. #include <sys/stat.h>
  370. #endif
  371. #if WITH_STEAMWORKS
  372. #include "steam/steam_api.h"
  373. #endif
  374. //#define RSP_PROFILE_ON
  375. //#include "ORANGE/Debug/profile.h"
  376. ////////////////////////////////////////////////////////////////////////////////
  377. // Macros/types/etc.
  378. ////////////////////////////////////////////////////////////////////////////////
  379. #define DEMO_FRAMES_PER_SECOND 15
  380. #define DEMO_TIME_PER_FRAME (1000 / DEMO_FRAMES_PER_SECOND)
  381. #define DEMO_MAX_SEQUENTIAL_SKIPPED_FRAMES 1
  382. #define DEMO_MAX_LAG (DEMO_TIME_PER_FRAME / 2)
  383. #define DEMO_MAX_DEAD_TIME 5000
  384. #define DEMO_MULTIALPHA_FILE "2d/school.mlp"
  385. #define DISP_INFO_INTERVAL 1000 // NEVER EVER MAKE THIS LESS THAN 1!!!!
  386. #define DISP_INFO_FONT_HEIGHT 15
  387. #define VIEW_X 0
  388. #define VIEW_Y 0
  389. #define VIEW_W wideScreenWidth
  390. #define VIEW_H 400
  391. #define FILM_X 0
  392. #define FILM_Y 40
  393. // Scaling values
  394. #define FILM_INCDEC_SCALE 0.05
  395. #define FILM_MAX_SCALE 1.00
  396. #define FILM_MIN_SCALE 0.30
  397. #define INFO_STATUS_RECT_X ((VIEW_W - 640)/2)
  398. #define INFO_STATUS_RECT_Y (FILM_Y - (INFO_STATUS_RECT_H + 3) )
  399. #define INFO_STATUS_RECT_W (g_pimScreenBuf->m_sWidth - INFO_STATUS_RECT_X)
  400. #define INFO_STATUS_RECT_H DISP_INFO_FONT_HEIGHT
  401. #define DUDE_STATUS_RECT_X 0
  402. #define DUDE_STATUS_RECT_Y (FILM_Y + VIEW_H)
  403. #define DUDE_STATUS_RECT_W (g_pimScreenBuf->m_sWidth - DUDE_STATUS_RECT_X)
  404. #define DUDE_STATUS_RECT_H (g_pimScreenBuf->m_sHeight - DUDE_STATUS_RECT_Y)
  405. #define REALM_STATUS_RECT_X 0
  406. #define REALM_STATUS_RECT_Y 0
  407. #define REALM_STATUS_RECT_W (FILM_X + VIEW_W - REALM_STATUS_RECT_X)
  408. #define REALM_STATUS_RECT_H 40
  409. // No less than this even after scaling.
  410. #define MIN_GRIP_ZONE_RADIUS 30
  411. // Grip movement parameters
  412. #define GRIP_MIN_MOVE_X 1
  413. #define GRIP_MIN_MOVE_Y 1
  414. #define GRIP_MAX_MOVE_X 8
  415. #define GRIP_MAX_MOVE_Y 8
  416. #define GRIP_ALIGN_X 1
  417. #define GRIP_ALIGN_Y 1
  418. // Time for black screen between cutscene and game screen
  419. #define BLACK_HOLD_TIME 250
  420. // Default message in case app's time stamp is not available. MUST be 25 characters or less!!!
  421. #define DEFAULT_APP_TIMESTAMP "No time stamp available"
  422. #define DEBUG_STR " Debug"
  423. #define RELEASE_STR " Release"
  424. #define TRACENASSERT_STR " Trace & Assert"
  425. // Number of kills limit if they specified no kills limit and no time limit.
  426. #define KILLS_LIMIT_DEFAULT 0
  427. // Default value for "final frame" in network mode (6.8 years at 10fps)
  428. #define DEFAULT_FINAL_FRAME LONG_MAX
  429. #if WITH_STEAMWORKS
  430. extern bool EnableSteamCloud;
  431. #define SAVEGAME_DIR (EnableSteamCloud ? "steamcloud" : "savegame")
  432. #else
  433. #define SAVEGAME_DIR ("savegame")
  434. #endif
  435. #define SAVEGAME_EXT "gme"
  436. #define ABORT_GUI_FILE "menu/abort.gui"
  437. #define CHAT_GUI "res/shell/chat.gui"
  438. #define CHAT_IN_GUI "res/shell/chatin.gui"
  439. #define KEY_MENU 27
  440. #define KEY_PAUSE RSP_GK_PAUSE
  441. #define KEY_NEXT_LEVEL RSP_GK_F1
  442. #define KEY_TOGGLE_TARGETING RSP_GK_F2
  443. // NOTE THAT F3 IS IN USE: DONT USE RSP_GK_F3.
  444. #define KEY_TOGGLE_DISP_INFO RSP_GK_F4
  445. #define KEY_SHOW_MISSION RSP_GK_F5
  446. #define KEY_ENLARGE_FILM1 RSP_GK_NUMPAD_PLUS
  447. #define KEY_ENLARGE_FILM2 '+'
  448. #define KEY_ENLARGE_FILM3 '='
  449. #define KEY_REDUCE_FILM1 RSP_GK_NUMPAD_MINUS
  450. #define KEY_REDUCE_FILM2 '-'
  451. #define KEY_TALK1 'T'
  452. #define KEY_TALK2 't'
  453. #define KEY_ACCEPT_CHAT '\r'
  454. #define KEY_ABORT_CHAT 27
  455. // Note that this uses RSP_SK_* macros for use the rspGetKeyStatusArray() key interface.
  456. #define KEY_XRAY_ALL RSP_SK_F3
  457. #define KEY_SNAP_PICTURE RSP_SK_ENTER
  458. #define PAUSED_FONT_HEIGHT 48
  459. #define PAUSED_FONT_SHADOW_X 5 // In pixels.
  460. #define PAUSED_FONT_SHADOW_Y PAUSED_FONT_SHADOW_X // In pixels.
  461. #define PAUSED_BASE_PAL_INDEX 64
  462. #define PAUSED_FONT_SHADOW_COLOR_R 0
  463. #define PAUSED_FONT_SHADOW_COLOR_G 0
  464. #define PAUSED_FONT_SHADOW_COLOR_B 0
  465. #define PAUSED_FONT_COLOR_R 0
  466. #define PAUSED_FONT_COLOR_G 15
  467. #define PAUSED_FONT_COLOR_B 255
  468. #define PAUSED_MSG_FONT_HEIGHT 29
  469. #define PAUSED_MSG_FONT_SHADOW_X 3 // In pixels.
  470. #define PAUSED_MSG_FONT_SHADOW_Y PAUSED_MSG_FONT_SHADOW_X // In pixels.
  471. #define PAUSED_MSG_FONT_SHADOW_COLOR_R PAUSED_FONT_SHADOW_COLOR_R
  472. #define PAUSED_MSG_FONT_SHADOW_COLOR_G PAUSED_FONT_SHADOW_COLOR_G
  473. #define PAUSED_MSG_FONT_SHADOW_COLOR_B PAUSED_FONT_SHADOW_COLOR_B
  474. #define PAUSED_MSG_FONT_COLOR_R PAUSED_FONT_COLOR_R
  475. #define PAUSED_MSG_FONT_COLOR_G PAUSED_FONT_COLOR_G
  476. #define PAUSED_MSG_FONT_COLOR_B PAUSED_FONT_COLOR_B
  477. #define TIME_OUT_FOR_ABORT_SOUNDS 3000 // In ms.
  478. #define MP_HIGH_SCORES_MAX_TIME 7000 // In ms.
  479. #define NUM_CHATS 4
  480. #define CHAT_DELAY 5000 // In ms.
  481. #define CHAT_IN_LENGTH 46
  482. ////////////////////////////////////////////////////////////////////////////////
  483. // Types.
  484. ////////////////////////////////////////////////////////////////////////////////
  485. // Game states
  486. typedef enum
  487. {
  488. // These are defined in a SPECIFIC ORDER!!! We sometimes check for specific
  489. // values, but other times we check for less than or greater than a value!!!
  490. // Note that you can think of the values as a PROGRESSION of states.
  491. Game_Ok, // Base state, must be 0
  492. Game_RedoRealm, // Redo the current realm
  493. Game_NextRealm, // Go to the next realm
  494. Game_GameOver, // Game is over
  495. Game_GameAborted, // Game is over because user aborted it
  496. } GameState;
  497. // Menu actions
  498. typedef enum
  499. {
  500. MenuActionNone,
  501. MenuActionQuit, // Quit game.
  502. MenuActionSaveGame, // Save user's game.
  503. MenuActionEndMenu // End the menu
  504. } MenuAction;
  505. ////////////////////////////////////////////////////////////////////////////////
  506. // Variables/data
  507. ////////////////////////////////////////////////////////////////////////////////
  508. // Quit flag used by menu callbacks
  509. static MenuAction ms_menuaction = MenuActionNone;
  510. // Number used in filename for snapshots
  511. static long ms_lCurPicture = 0;
  512. #ifdef SALES_DEMO
  513. // When true, one can advance to the next level without meeting the goal.
  514. extern bool g_bEnableLevelAdvanceWithoutGoal = false;
  515. #endif
  516. extern SampleMaster::SoundInstance g_siFinalScene; // should be in game
  517. extern SampleMaster::SoundInstance g_siFinalSceneCredits; // should be in game
  518. SampleMaster::SoundInstance g_siFinalScene; // should be in game
  519. SampleMaster::SoundInstance g_siFinalSceneCredits; // should be in game
  520. //#ifdef MOBILE
  521. extern bool demoCompat; //Try to make demos not go out of sync
  522. //#endif
  523. ////////////////////////////////////////////////////////////////////////////////
  524. // Function prototypes
  525. ////////////////////////////////////////////////////////////////////////////////
  526. ////////////////////////////////////////////////////////////////////////////////
  527. //
  528. // Info needed by virtually everything in play
  529. //
  530. ////////////////////////////////////////////////////////////////////////////////
  531. class CPlayInfo
  532. {
  533. friend short Play( // Returns 0 if successfull, non-zero otherwise
  534. CNetClient* pclient, // In: Client object or NULL if not network game
  535. CNetServer* pserver, // In: Server object or NULL if not server or not network game
  536. INPUT_MODE inputMode, // In: Input mode
  537. const short sRealmNum, // In: Realm number to start on or -1 to use specified realm file
  538. const char* pszRealmFile, // In: Realm file to play (ignored if sRealmNum >= 0)
  539. const bool bJustOneRealm, // In: Play just this one realm (ignored if sRealmNum < 0)
  540. const bool bGauntlet, // In: Play challenge levels gauntlet - as selected on menu
  541. const bool bAddOn, // In: Play new single player Add On levels
  542. const short sDifficulty, // In: Difficulty level
  543. const bool bRejuvenate, // In: Whether to allow players to rejuvenate (MP only)
  544. const short sTimeLimit, // In: Time limit for MP games (0 or negative if none)
  545. const short sKillLimit, // In: Kill limit for MP games (0 or negative if none)
  546. const short sCoopLevels, // In: Zero for deathmatch levels, non-zero for cooperative levels.
  547. const short sCoopMode, // In: Zero for deathmatch mode, non-zero for cooperative mode.
  548. const short sFrameTime, // In: Milliseconds per frame (MP only)
  549. RFile* pfileDemoModeDebugMovie); // In: File for loading/saving demo mode debug movie
  550. //------------------------------------------------------------------------------
  551. // Types, enums, etc.
  552. //------------------------------------------------------------------------------
  553. //------------------------------------------------------------------------------
  554. // Variables
  555. //------------------------------------------------------------------------------
  556. private:
  557. CNetClient* m_pclient; // Client object or NULL if not network game
  558. CNetServer* m_pserver; // Server object or NULL if not server or not network game
  559. short m_sRealmNum; // Realm number
  560. char m_szRealm[RSP_MAX_PATH+1]; // Realm file
  561. bool m_bJustOneRealm; // Play just this one realm (ignored if sRealmNum < 0)
  562. CRealm* m_prealm;
  563. CCamera* m_pcamera;
  564. CGrip* m_pgrip;
  565. bool m_bGauntlet; // Play challenge levels gauntlet
  566. bool m_bAddOn; // Play new Add On levels
  567. bool m_bRejuvenate; // Whether to allow players to rejuvenate (MP only)
  568. short m_sTimeLimit; // Time limit for MP games (0 or negative if none)
  569. short m_sKillLimit; // Kill limit for MP games (0 or negative if none)
  570. short m_sCoopLevels; // Zero for deathmatch levels, non-zero for cooperative levels.
  571. short m_sFrameTime; // Milliseconds per frame (MP only)
  572. RFile* m_pfileDemoModeDebugMovie; // File for loading/saving demo mode debug movie
  573. GameState m_gamestate;
  574. bool m_bPurgeSaks; // Purge the SAKS if true
  575. public:
  576. U16 m_idLocalDude; // Local dude's ID
  577. U16 m_idGripTarget; // Grip target's ID
  578. bool m_bDoRealmFrame; // Whether to do a realm frame
  579. long m_lSumUpdateDisplayTimes;
  580. bool m_bXRayAll; // X Ray all status.
  581. bool m_bInMenu; // Whether we're in the menu
  582. bool m_bUserQuitMP; // Whether local user wants to quit MP game
  583. bool m_bNextRealmMP; // Whether local user wants next level of MP game
  584. bool m_bBadRealmMP; // Whether MP realm was unable to load
  585. bool m_bChatting; // true, when typing in chat messages.
  586. // false, otherwise.
  587. bool m_bDrawFrame; // true, if we need to draw a frame.
  588. RDirtyRects m_drl; // Any areas of the composite buffer that is
  589. // altered should be added to this list so it can
  590. // be updated on CoreLoopDraw().
  591. //------------------------------------------------------------------------------
  592. // Functions
  593. //------------------------------------------------------------------------------
  594. public:
  595. ////////////////////////////////////////////////////////////////////////////////
  596. // Constructor
  597. ////////////////////////////////////////////////////////////////////////////////
  598. CPlayInfo(void)
  599. {
  600. m_pclient = 0;
  601. m_pserver = 0;
  602. m_sRealmNum = 0;
  603. m_szRealm[0] = 0;
  604. m_bJustOneRealm = false;
  605. m_prealm = new CRealm;
  606. m_pcamera = new CCamera;
  607. m_pgrip = new CGrip;
  608. m_bGauntlet = false;
  609. m_bAddOn = false;
  610. m_bRejuvenate = false;
  611. m_sTimeLimit = 0;
  612. m_sKillLimit = 0;
  613. m_sCoopLevels = 0;
  614. m_sFrameTime = 0;
  615. m_pfileDemoModeDebugMovie = 0;
  616. m_gamestate = Game_Ok;
  617. m_idLocalDude = CIdBank::IdNil;
  618. m_idGripTarget = CIdBank::IdNil;
  619. m_bDoRealmFrame = false;
  620. m_lSumUpdateDisplayTimes = 0;
  621. m_bXRayAll = false; // Always default to no XRay all.
  622. m_bPurgeSaks = false; // Assume no purging
  623. m_bInMenu = false;
  624. m_bUserQuitMP = false;
  625. m_bNextRealmMP = false;
  626. m_bBadRealmMP = false;
  627. m_bChatting = false;
  628. }
  629. ////////////////////////////////////////////////////////////////////////////////
  630. // Destructor
  631. ////////////////////////////////////////////////////////////////////////////////
  632. ~CPlayInfo()
  633. {
  634. delete m_prealm;
  635. delete m_pcamera;
  636. delete m_pgrip;
  637. }
  638. ////////////////////////////////////////////////////////////////////////////////
  639. // Simple wrappers that allow "read-only" access to member variables
  640. ////////////////////////////////////////////////////////////////////////////////
  641. CNetClient* Client(void) { return m_pclient; }
  642. CNetServer* Server(void) { return m_pserver; }
  643. short RealmNum(void) { return m_sRealmNum; }
  644. const char* RealmName(void) { return m_szRealm; }
  645. bool JustOneRealm(void) { return m_bJustOneRealm; }
  646. CRealm* Realm(void) { return m_prealm; }
  647. CCamera* Camera(void) { return m_pcamera; }
  648. CGrip* Grip(void) { return m_pgrip; }
  649. bool Gauntlet(void) { return m_bGauntlet; }
  650. bool AddOn(void) { return m_bAddOn; }
  651. bool Rejuvenate(void) { return m_bRejuvenate; }
  652. short TimeLimit(void) { return m_sTimeLimit > 0 ? m_sTimeLimit : 0; }
  653. short KillLimit(void) { return m_sKillLimit > 0 ? m_sKillLimit : 0; }
  654. short CoopLevels(void) { return m_sCoopLevels; }
  655. short FrameTime(void) { return m_sFrameTime; }
  656. RFile* DemoModeDebugMovie(void) { return m_pfileDemoModeDebugMovie; }
  657. ////////////////////////////////////////////////////////////////////////////////
  658. // Change the frame time (MP only)
  659. ////////////////////////////////////////////////////////////////////////////////
  660. void SetFrameTime(
  661. short sFrameTime)
  662. {
  663. m_sFrameTime = sFrameTime;
  664. }
  665. ////////////////////////////////////////////////////////////////////////////////
  666. // Set the SAK purge flag.
  667. ////////////////////////////////////////////////////////////////////////////////
  668. void SetPurgeSaks(void)
  669. { m_bPurgeSaks = true; }
  670. ////////////////////////////////////////////////////////////////////////////////
  671. // Clear the SAK purge flag.
  672. ////////////////////////////////////////////////////////////////////////////////
  673. void ClearPurgeSaks(void)
  674. { m_bPurgeSaks = false; }
  675. ////////////////////////////////////////////////////////////////////////////////
  676. // Query the SAK purge flag status.
  677. ////////////////////////////////////////////////////////////////////////////////
  678. bool PurgeSaks(void)
  679. { return m_bPurgeSaks; }
  680. ////////////////////////////////////////////////////////////////////////////////
  681. // Get pointer to local dude if one exists, otherwise returns 0.
  682. ////////////////////////////////////////////////////////////////////////////////
  683. CDude* LocalDudePointer(void)
  684. {
  685. CDude* pdudeLocal;
  686. if (m_prealm->m_idbank.GetThingByID((CThing**)&pdudeLocal, m_idLocalDude) != 0)
  687. m_idLocalDude = CIdBank::IdNil;
  688. return pdudeLocal;
  689. }
  690. ////////////////////////////////////////////////////////////////////////////////
  691. // Query the game mode
  692. ////////////////////////////////////////////////////////////////////////////////
  693. bool IsMP(void)
  694. { return (m_pclient) ? true : false; }
  695. bool IsServer(void)
  696. { return (m_pserver) ? true : false; }
  697. ////////////////////////////////////////////////////////////////////////////////
  698. // Set the game state
  699. ////////////////////////////////////////////////////////////////////////////////
  700. void SetGameState_Ok(void)
  701. {
  702. m_gamestate = Game_Ok;
  703. }
  704. void SetGameState_RestartRealm(void)
  705. {
  706. // This should NEVER occur in MP mode
  707. ASSERT(!IsMP());
  708. m_gamestate = Game_RedoRealm;
  709. }
  710. void SetGameState_NextRealm(
  711. bool bServerToldMe = false)
  712. {
  713. m_gamestate = Game_NextRealm;
  714. }
  715. void SetGameState_GameOver(
  716. bool bServerToldMe = false)
  717. {
  718. // This should NEVER occur in MP mode
  719. // 09/12/97 MJR -- This USED TO BE TRUE, but now that we re-enabled the
  720. // use of the "just one realm" mode in MP in the case where the server
  721. // only has a single realm available, we need this again in MP mode,
  722. // so I commented it out.
  723. // ASSERT(!IsMP());
  724. m_gamestate = Game_GameOver;
  725. }
  726. void SetGameState_GameAborted(
  727. bool bServerToldMe = false)
  728. {
  729. m_gamestate = Game_GameAborted;
  730. }
  731. ////////////////////////////////////////////////////////////////////////////////
  732. // Query the game state
  733. ////////////////////////////////////////////////////////////////////////////////
  734. bool IsRealmDone(void)
  735. { return (m_gamestate >= Game_RedoRealm) ? true : false; }
  736. bool IsRestartingRealm(void)
  737. { return (m_gamestate == Game_RedoRealm) ? true : false; }
  738. bool IsNextRealm(void)
  739. { return (m_gamestate == Game_NextRealm) ? true : false; }
  740. bool IsGameOver(void)
  741. { return (m_gamestate >= Game_GameOver) ? true : false; }
  742. bool IsGameAborted(void)
  743. { return (m_gamestate == Game_GameAborted) ? true : false; }
  744. };
  745. ////////////////////////////////////////////////////////////////////////////////
  746. //
  747. // Base class for all "Play Modules"
  748. //
  749. ////////////////////////////////////////////////////////////////////////////////
  750. class CPlay
  751. {
  752. //------------------------------------------------------------------------------
  753. // Types, enums, etc.
  754. //------------------------------------------------------------------------------
  755. private:
  756. //------------------------------------------------------------------------------
  757. // Variables
  758. //------------------------------------------------------------------------------
  759. private:
  760. //------------------------------------------------------------------------------
  761. // Functions
  762. //------------------------------------------------------------------------------
  763. public:
  764. ////////////////////////////////////////////////////////////////////////////////
  765. // Constructor
  766. ////////////////////////////////////////////////////////////////////////////////
  767. CPlay(void)
  768. {
  769. }
  770. ////////////////////////////////////////////////////////////////////////////////
  771. // Destructor
  772. ////////////////////////////////////////////////////////////////////////////////
  773. virtual
  774. ~CPlay()
  775. {
  776. }
  777. ////////////////////////////////////////////////////////////////////////////////
  778. // Prepare game
  779. ////////////////////////////////////////////////////////////////////////////////
  780. virtual
  781. short PrepareGame( // Returns 0 if successfull, non-zero otherwise
  782. CPlayInfo* pinfo) // I/O: Play info
  783. {
  784. return 0;
  785. }
  786. ////////////////////////////////////////////////////////////////////////////////
  787. // Determine if game is ready
  788. ////////////////////////////////////////////////////////////////////////////////
  789. virtual
  790. short IsGameReady( // Returns 0 if successfull, non-zero otherwise
  791. CPlayInfo* pinfo, // I/O: Play info
  792. bool* pbGameReady) // Out: Whether game is ready
  793. {
  794. *pbGameReady = true;
  795. return 0;
  796. }
  797. ////////////////////////////////////////////////////////////////////////////////
  798. // Start game
  799. ////////////////////////////////////////////////////////////////////////////////
  800. virtual
  801. short StartGame( // Returns 0 if successfull, non-zero otherwise
  802. CPlayInfo* pinfo) // I/O: Play info
  803. {
  804. return 0;
  805. }
  806. ////////////////////////////////////////////////////////////////////////////////
  807. // Start cutscene
  808. ////////////////////////////////////////////////////////////////////////////////
  809. virtual
  810. void StartCutscene(
  811. CPlayInfo* pinfo) // I/O: Play info
  812. {
  813. }
  814. ////////////////////////////////////////////////////////////////////////////////
  815. // Prepare realm
  816. ////////////////////////////////////////////////////////////////////////////////
  817. virtual
  818. short PrepareRealm( // Returns 0 if successfull, non-zero otherwise
  819. CPlayInfo* pinfo) // I/O: Play info
  820. {
  821. return 0;
  822. }
  823. ////////////////////////////////////////////////////////////////////////////////
  824. // Determine if realm is ready
  825. ////////////////////////////////////////////////////////////////////////////////
  826. virtual
  827. short IsRealmReady( // Returns 0 if successfull, non-zero otherwise
  828. CPlayInfo* pinfo, // I/O: Play info
  829. bool* pbRealmReady) // Out: Whether realm is ready
  830. {
  831. *pbRealmReady = true;
  832. return 0;
  833. }
  834. ////////////////////////////////////////////////////////////////////////////////
  835. // Do cutscene
  836. ////////////////////////////////////////////////////////////////////////////////
  837. virtual
  838. void DoCutscene(
  839. CPlayInfo* pinfo) // I/O: Play info
  840. {
  841. }
  842. ////////////////////////////////////////////////////////////////////////////////
  843. // End cutscene
  844. ////////////////////////////////////////////////////////////////////////////////
  845. virtual
  846. void EndCutscene(
  847. CPlayInfo* pinfo) // I/O: Play info
  848. {
  849. }
  850. ////////////////////////////////////////////////////////////////////////////////
  851. // Start realm
  852. ////////////////////////////////////////////////////////////////////////////////
  853. virtual
  854. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  855. CPlayInfo* pinfo) // I/O: Play info
  856. {
  857. return 0;
  858. }
  859. ////////////////////////////////////////////////////////////////////////////////
  860. // Core loop user input
  861. ////////////////////////////////////////////////////////////////////////////////
  862. virtual
  863. void CoreLoopUserInput(
  864. CPlayInfo* pinfo, // I/O: Play info
  865. RInputEvent* pie) // I/O: Input event
  866. {
  867. }
  868. ////////////////////////////////////////////////////////////////////////////////
  869. // Core loop update
  870. ////////////////////////////////////////////////////////////////////////////////
  871. virtual
  872. void CoreLoopUpdate(
  873. CPlayInfo* pinfo) // I/O: Play info
  874. {
  875. }
  876. ////////////////////////////////////////////////////////////////////////////////
  877. // Core loop render -- create and update images to the composite buffer but do
  878. // NOT update the screen.
  879. ////////////////////////////////////////////////////////////////////////////////
  880. virtual
  881. void CoreLoopRender(
  882. CPlayInfo* pinfo) // I/O: Play info
  883. {
  884. }
  885. ////////////////////////////////////////////////////////////////////////////////
  886. // Core loop render on top -- create and update images to the composite buffer
  887. // on top of things rendered in CoreLoopRender() but do NOT update the screen.
  888. ////////////////////////////////////////////////////////////////////////////////
  889. virtual
  890. void CoreLoopRenderOnTop(
  891. CPlayInfo* pinfo) // I/O: Play info
  892. {
  893. }
  894. ////////////////////////////////////////////////////////////////////////////////
  895. // Core loop draw -- Draw CoreLoopRender[OnTop]() results to the screen.
  896. ////////////////////////////////////////////////////////////////////////////////
  897. virtual
  898. void CoreLoopDraw(
  899. CPlayInfo* pinfo) // I/O: Play info
  900. {
  901. }
  902. ////////////////////////////////////////////////////////////////////////////////
  903. // Determine if core loop is done
  904. ////////////////////////////////////////////////////////////////////////////////
  905. virtual
  906. bool IsCoreLoopDone( // Returns true if done, false otherwise
  907. CPlayInfo* pinfo) // I/O: Play info
  908. {
  909. return pinfo->IsRealmDone();
  910. }
  911. ////////////////////////////////////////////////////////////////////////////////
  912. // End realm
  913. ////////////////////////////////////////////////////////////////////////////////
  914. virtual
  915. void EndRealm(
  916. CPlayInfo* pinfo) // I/O: Play info
  917. {
  918. }
  919. ////////////////////////////////////////////////////////////////////////////////
  920. // Unprepare game
  921. ////////////////////////////////////////////////////////////////////////////////
  922. virtual
  923. void UnprepareGame(
  924. CPlayInfo* pinfo) // I/O: Play info
  925. {
  926. }
  927. ////////////////////////////////////////////////////////////////////////////////
  928. // Start realm error handler
  929. ////////////////////////////////////////////////////////////////////////////////
  930. virtual
  931. void StartRealmErr(
  932. CPlayInfo* pinfo) // I/O: Play info
  933. {
  934. }
  935. ////////////////////////////////////////////////////////////////////////////////
  936. // Is realm ready error handler
  937. ////////////////////////////////////////////////////////////////////////////////
  938. virtual
  939. void IsRealmReadyErr(
  940. CPlayInfo* pinfo) // I/O: Play info
  941. {
  942. }
  943. ////////////////////////////////////////////////////////////////////////////////
  944. // Prepare realm error handler
  945. ////////////////////////////////////////////////////////////////////////////////
  946. virtual
  947. void PrepareRealmErr(
  948. CPlayInfo* pinfo) // I/O: Play info
  949. {
  950. }
  951. ////////////////////////////////////////////////////////////////////////////////
  952. // Start game error handler
  953. ////////////////////////////////////////////////////////////////////////////////
  954. virtual
  955. void StartGameErr(
  956. CPlayInfo* pinfo) // I/O: Play info
  957. {
  958. }
  959. ////////////////////////////////////////////////////////////////////////////////
  960. // Is game ready error handler
  961. ////////////////////////////////////////////////////////////////////////////////
  962. virtual
  963. void IsGameReadyErr(
  964. CPlayInfo* pinfo) // I/O: Play info
  965. {
  966. }
  967. ////////////////////////////////////////////////////////////////////////////////
  968. // Prepare game error handler
  969. ////////////////////////////////////////////////////////////////////////////////
  970. virtual
  971. void PrepareGameErr(
  972. CPlayInfo* pinfo) // I/O: Play info
  973. {
  974. }
  975. };
  976. ////////////////////////////////////////////////////////////////////////////////
  977. //
  978. // Group of "Play Modules"
  979. //
  980. ////////////////////////////////////////////////////////////////////////////////
  981. class CPlayGroup
  982. {
  983. //------------------------------------------------------------------------------
  984. // Types, enums, etc.
  985. //------------------------------------------------------------------------------
  986. public:
  987. typedef RFList<CPlay*> Plays;
  988. //------------------------------------------------------------------------------
  989. // Variables
  990. //------------------------------------------------------------------------------
  991. private:
  992. Plays m_Plays; // List of play modules
  993. //------------------------------------------------------------------------------
  994. // Functions
  995. //------------------------------------------------------------------------------
  996. public:
  997. ////////////////////////////////////////////////////////////////////////////////
  998. // Constructor
  999. ////////////////////////////////////////////////////////////////////////////////
  1000. CPlayGroup(void)
  1001. {
  1002. }
  1003. ////////////////////////////////////////////////////////////////////////////////
  1004. // Destructor
  1005. ////////////////////////////////////////////////////////////////////////////////
  1006. ~CPlayGroup()
  1007. {
  1008. m_Plays.Reset();
  1009. }
  1010. ////////////////////////////////////////////////////////////////////////////////
  1011. // Add a module
  1012. ////////////////////////////////////////////////////////////////////////////////
  1013. void AddModule(
  1014. CPlay* pPlay)
  1015. {
  1016. m_Plays.InsertHead(pPlay);
  1017. }
  1018. ////////////////////////////////////////////////////////////////////////////////
  1019. // Remove a module
  1020. ////////////////////////////////////////////////////////////////////////////////
  1021. void RemoveModule(
  1022. CPlay* pPlay)
  1023. {
  1024. // Search for the specific pointer and remove it. This is inefficient, but
  1025. // but it happens so rarely that it doesn't matter. The alternative was for
  1026. // AddModule() to return a Plays::Pointer, which the caller would pass to
  1027. // this function so it could remove the object without searching for it.
  1028. // This sound good, but then CPlay would have to know what a Plays::Pointer
  1029. // is so it could hold on to it. Unfortunately, the pointer is defined by
  1030. // this class, and this class must follow the definition of CPlay, so you
  1031. // end up with a classic which-comes-first-the-chicken-or-the-egg problem.
  1032. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1033. {
  1034. if (m_Plays.GetData(p) == pPlay)
  1035. {
  1036. m_Plays.Remove(p);
  1037. break;
  1038. }
  1039. }
  1040. }
  1041. ////////////////////////////////////////////////////////////////////////////////
  1042. // Prepare game
  1043. ////////////////////////////////////////////////////////////////////////////////
  1044. short PrepareGame( // Returns 0 if successfull, non-zero otherwise
  1045. CPlayInfo* pinfo) // I/O: Play info
  1046. {
  1047. short sResult = 0;
  1048. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1049. sResult |= m_Plays.GetData(p)->PrepareGame(pinfo);
  1050. return sResult;
  1051. }
  1052. ////////////////////////////////////////////////////////////////////////////////
  1053. // Determine if game is ready
  1054. ////////////////////////////////////////////////////////////////////////////////
  1055. short IsGameReady( // Returns 0 if successfull, non-zero otherwise
  1056. CPlayInfo* pinfo, // I/O: Play info
  1057. bool* pbGameReady) // Out: Whether game is ready
  1058. {
  1059. short sResult = 0;
  1060. *pbGameReady = true;
  1061. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1062. {
  1063. bool bGameReady = false;
  1064. sResult |= m_Plays.GetData(p)->IsGameReady(pinfo, &bGameReady);
  1065. *pbGameReady &= bGameReady;
  1066. }
  1067. return sResult;
  1068. }
  1069. ////////////////////////////////////////////////////////////////////////////////
  1070. // Start game
  1071. ////////////////////////////////////////////////////////////////////////////////
  1072. short StartGame( // Returns 0 if successfull, non-zero otherwise
  1073. CPlayInfo* pinfo) // I/O: Play info
  1074. {
  1075. short sResult = 0;
  1076. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1077. sResult |= m_Plays.GetData(p)->StartGame(pinfo);
  1078. return sResult;
  1079. }
  1080. ////////////////////////////////////////////////////////////////////////////////
  1081. // Start cutscene
  1082. ////////////////////////////////////////////////////////////////////////////////
  1083. void StartCutscene(
  1084. CPlayInfo* pinfo) // I/O: Play info
  1085. {
  1086. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1087. m_Plays.GetData(p)->StartCutscene(pinfo);
  1088. }
  1089. ////////////////////////////////////////////////////////////////////////////////
  1090. // Prepare realm
  1091. ////////////////////////////////////////////////////////////////////////////////
  1092. short PrepareRealm( // Returns 0 if successfull, non-zero otherwise
  1093. CPlayInfo* pinfo) // I/O: Play info
  1094. {
  1095. short sResult = 0;
  1096. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1097. sResult |= m_Plays.GetData(p)->PrepareRealm(pinfo);
  1098. return sResult;
  1099. }
  1100. ////////////////////////////////////////////////////////////////////////////////
  1101. // Determine if realm is ready
  1102. ////////////////////////////////////////////////////////////////////////////////
  1103. short IsRealmReady( // Returns 0 if successfull, non-zero otherwise
  1104. CPlayInfo* pinfo, // I/O: Play info
  1105. bool* pbRealmReady) // Out: Whether realm is ready
  1106. {
  1107. short sResult = 0;
  1108. *pbRealmReady = true;
  1109. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1110. {
  1111. bool bRealmReady = false;
  1112. sResult |= m_Plays.GetData(p)->IsRealmReady(pinfo, &bRealmReady);
  1113. *pbRealmReady &= bRealmReady;
  1114. }
  1115. return sResult;
  1116. }
  1117. ////////////////////////////////////////////////////////////////////////////////
  1118. // Do cutscene
  1119. ////////////////////////////////////////////////////////////////////////////////
  1120. void DoCutscene(
  1121. CPlayInfo* pinfo) // I/O: Play info
  1122. {
  1123. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1124. m_Plays.GetData(p)->DoCutscene(pinfo);
  1125. }
  1126. ////////////////////////////////////////////////////////////////////////////////
  1127. // End cutscene
  1128. ////////////////////////////////////////////////////////////////////////////////
  1129. void EndCutscene(
  1130. CPlayInfo* pinfo) // I/O: Play info
  1131. {
  1132. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1133. m_Plays.GetData(p)->EndCutscene(pinfo);
  1134. }
  1135. ////////////////////////////////////////////////////////////////////////////////
  1136. // Start realm
  1137. ////////////////////////////////////////////////////////////////////////////////
  1138. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  1139. CPlayInfo* pinfo) // I/O: Play info
  1140. {
  1141. short sResult = 0;
  1142. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1143. sResult |= m_Plays.GetData(p)->StartRealm(pinfo);
  1144. return sResult;
  1145. }
  1146. ////////////////////////////////////////////////////////////////////////////////
  1147. // Core loop user input
  1148. ////////////////////////////////////////////////////////////////////////////////
  1149. void CoreLoopUserInput(
  1150. CPlayInfo* pinfo, // I/O: Play info
  1151. RInputEvent* pie) // I/O: Input event
  1152. {
  1153. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1154. m_Plays.GetData(p)->CoreLoopUserInput(pinfo, pie);
  1155. }
  1156. ////////////////////////////////////////////////////////////////////////////////
  1157. // Core loop update
  1158. ////////////////////////////////////////////////////////////////////////////////
  1159. void CoreLoopUpdate(
  1160. CPlayInfo* pinfo) // I/O: Play info
  1161. {
  1162. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1163. m_Plays.GetData(p)->CoreLoopUpdate(pinfo);
  1164. }
  1165. ////////////////////////////////////////////////////////////////////////////////
  1166. // Core loop render -- create and update images to the composite buffer but do
  1167. // NOT update the screen.
  1168. ////////////////////////////////////////////////////////////////////////////////
  1169. void CoreLoopRender(
  1170. CPlayInfo* pinfo) // I/O: Play info
  1171. {
  1172. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1173. m_Plays.GetData(p)->CoreLoopRender(pinfo);
  1174. }
  1175. ////////////////////////////////////////////////////////////////////////////////
  1176. // Core loop render on top -- create and update images to the composite buffer
  1177. // on top of things rendered in CoreLoopRender() but do NOT update the screen.
  1178. ////////////////////////////////////////////////////////////////////////////////
  1179. virtual
  1180. void CoreLoopRenderOnTop(
  1181. CPlayInfo* pinfo) // I/O: Play info
  1182. {
  1183. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1184. m_Plays.GetData(p)->CoreLoopRenderOnTop(pinfo);
  1185. }
  1186. ////////////////////////////////////////////////////////////////////////////////
  1187. // Core loop draw -- Draw CoreLoopRender[OnTop]() results to the screen.
  1188. ////////////////////////////////////////////////////////////////////////////////
  1189. virtual
  1190. void CoreLoopDraw(
  1191. CPlayInfo* pinfo) // I/O: Play info
  1192. {
  1193. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1194. m_Plays.GetData(p)->CoreLoopDraw(pinfo);
  1195. // Update the display in the dirtied areas defined by m_drl.
  1196. RDRect* pdr = pinfo->m_drl.GetHead();
  1197. while (pdr)
  1198. {
  1199. long lTime = rspGetMilliseconds();
  1200. // Update the portion of the display.
  1201. rspCacheDirtyRect(pdr->sX, pdr->sY, pdr->sW, pdr->sH);
  1202. pinfo->m_lSumUpdateDisplayTimes += (rspGetMilliseconds() - lTime);
  1203. // Remove the current rectangle from the list and delete it.
  1204. pinfo->m_drl.Remove();
  1205. delete pdr;
  1206. // Get the next one.
  1207. pdr = pinfo->m_drl.GetNext();
  1208. }
  1209. rspUpdateDisplayRects();
  1210. }
  1211. ////////////////////////////////////////////////////////////////////////////////
  1212. // Determine if core loop is done
  1213. ////////////////////////////////////////////////////////////////////////////////
  1214. bool IsCoreLoopDone( // Returns true if done, false otherwise
  1215. CPlayInfo* pinfo) // I/O: Play info
  1216. {
  1217. bool bDone = true;
  1218. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1219. bDone &= m_Plays.GetData(p)->IsCoreLoopDone(pinfo);
  1220. return bDone;
  1221. }
  1222. ////////////////////////////////////////////////////////////////////////////////
  1223. // End realm
  1224. ////////////////////////////////////////////////////////////////////////////////
  1225. void EndRealm(
  1226. CPlayInfo* pinfo) // I/O: Play info
  1227. {
  1228. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1229. m_Plays.GetData(p)->EndRealm(pinfo);
  1230. // Any dirty rects left over, we don't care about.
  1231. pinfo->m_drl.Empty();
  1232. }
  1233. ////////////////////////////////////////////////////////////////////////////////
  1234. // Unprepare game
  1235. ////////////////////////////////////////////////////////////////////////////////
  1236. void UnprepareGame(
  1237. CPlayInfo* pinfo) // I/O: Play info
  1238. {
  1239. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1240. m_Plays.GetData(p)->UnprepareGame(pinfo);
  1241. }
  1242. ////////////////////////////////////////////////////////////////////////////////
  1243. // Start realm error handler
  1244. ////////////////////////////////////////////////////////////////////////////////
  1245. void StartRealmErr(
  1246. CPlayInfo* pinfo) // I/O: Play info
  1247. {
  1248. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1249. m_Plays.GetData(p)->StartRealmErr(pinfo);
  1250. }
  1251. ////////////////////////////////////////////////////////////////////////////////
  1252. // Is realm ready error handler
  1253. ////////////////////////////////////////////////////////////////////////////////
  1254. void IsRealmReadyErr(
  1255. CPlayInfo* pinfo) // I/O: Play info
  1256. {
  1257. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1258. m_Plays.GetData(p)->IsRealmReadyErr(pinfo);
  1259. }
  1260. ////////////////////////////////////////////////////////////////////////////////
  1261. // Prepare realm error handler
  1262. ////////////////////////////////////////////////////////////////////////////////
  1263. void PrepareRealmErr(
  1264. CPlayInfo* pinfo) // I/O: Play info
  1265. {
  1266. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1267. m_Plays.GetData(p)->PrepareRealmErr(pinfo);
  1268. }
  1269. ////////////////////////////////////////////////////////////////////////////////
  1270. // Start game error handler
  1271. ////////////////////////////////////////////////////////////////////////////////
  1272. void StartGameErr(
  1273. CPlayInfo* pinfo) // I/O: Play info
  1274. {
  1275. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1276. m_Plays.GetData(p)->StartGameErr(pinfo);
  1277. }
  1278. ////////////////////////////////////////////////////////////////////////////////
  1279. // Is game ready error handler
  1280. ////////////////////////////////////////////////////////////////////////////////
  1281. void IsGameReadyErr(
  1282. CPlayInfo* pinfo) // I/O: Play info
  1283. {
  1284. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1285. m_Plays.GetData(p)->IsGameReadyErr(pinfo);
  1286. }
  1287. ////////////////////////////////////////////////////////////////////////////////
  1288. // Prepare game error handler
  1289. ////////////////////////////////////////////////////////////////////////////////
  1290. void PrepareGameErr(
  1291. CPlayInfo* pinfo) // I/O: Play info
  1292. {
  1293. for (Plays::Pointer p = m_Plays.GetHead(); p != 0; p = m_Plays.GetNext(p))
  1294. m_Plays.GetData(p)->PrepareGameErr(pinfo);
  1295. }
  1296. };
  1297. ////////////////////////////////////////////////////////////////////////////////
  1298. //
  1299. // Client Play Module
  1300. //
  1301. ////////////////////////////////////////////////////////////////////////////////
  1302. class CPlayNet : public CPlay
  1303. {
  1304. //------------------------------------------------------------------------------
  1305. // Types, enums, etc.
  1306. //------------------------------------------------------------------------------
  1307. private:
  1308. //------------------------------------------------------------------------------
  1309. // Variables
  1310. //------------------------------------------------------------------------------
  1311. private:
  1312. bool m_bEndServerCleanly;
  1313. bool m_bServerDone;
  1314. bool m_bEndClientCleanly;
  1315. bool m_bClientDone;
  1316. bool m_bHandledUserQuit;
  1317. bool m_bHandledNextRealm;
  1318. bool m_bAbortNow; // Ultimate abort flag
  1319. bool m_bCheckForAbortKey; // Whether to check for user abort
  1320. bool m_bTimeBombActive; // Whether time bomb is active
  1321. long m_lTimeBomb; // Time when bomb explodes
  1322. bool m_bShowNetFeedback; // Whether to show net feedback thingy
  1323. bool m_bFirstCoreLoopUserInput;
  1324. REdit* m_apeditChats[NUM_CHATS]; // Received chat edit fields.
  1325. long m_lLastChatMoveTime; // Last time chats were adjusted.
  1326. //------------------------------------------------------------------------------
  1327. // Functions
  1328. //------------------------------------------------------------------------------
  1329. public:
  1330. ////////////////////////////////////////////////////////////////////////////////
  1331. // Constructor
  1332. ////////////////////////////////////////////////////////////////////////////////
  1333. CPlayNet(void)
  1334. {
  1335. // Note that if any of this fails, we don't care (we just won't use them).
  1336. short sIndex;
  1337. for (sIndex = 0; sIndex < NUM_CHATS; sIndex++)
  1338. {
  1339. m_apeditChats[sIndex] = (REdit*)RGuiItem::LoadInstantiate(FullPathHD(CHAT_GUI) );
  1340. if (m_apeditChats[sIndex])
  1341. {
  1342. // Recreate in the correct spot and dimensions . . .
  1343. if (m_apeditChats[sIndex]->Create(
  1344. REALM_STATUS_RECT_X,
  1345. REALM_STATUS_RECT_Y + REALM_STATUS_RECT_H + (m_apeditChats[sIndex]->m_im.m_sHeight * sIndex),
  1346. REALM_STATUS_RECT_W,
  1347. m_apeditChats[sIndex]->m_im.m_sHeight,
  1348. g_pimScreenBuf->m_sDepth) == 0)
  1349. {
  1350. m_apeditChats[sIndex]->SetText("");
  1351. m_apeditChats[sIndex]->SetVisible(FALSE);
  1352. }
  1353. else
  1354. {
  1355. delete m_apeditChats[sIndex];
  1356. m_apeditChats[sIndex] = NULL;
  1357. }
  1358. }
  1359. }
  1360. }
  1361. ////////////////////////////////////////////////////////////////////////////////
  1362. // Destructor
  1363. ////////////////////////////////////////////////////////////////////////////////
  1364. /* virtual */
  1365. ~CPlayNet()
  1366. {
  1367. short sIndex;
  1368. for (sIndex = 0; sIndex < NUM_CHATS; sIndex++)
  1369. {
  1370. delete m_apeditChats[sIndex];
  1371. m_apeditChats[sIndex] = NULL;
  1372. }
  1373. }
  1374. ////////////////////////////////////////////////////////////////////////////////
  1375. // Move all the chat texts up one field. Hides emptied fields.
  1376. ////////////////////////////////////////////////////////////////////////////////
  1377. void MoveChatsUp(
  1378. CPlayInfo* pinfo) // In: Info object.
  1379. {
  1380. short sIndex = 0;
  1381. // Goto last chat that is filled moving them up as we go.
  1382. while (sIndex < NUM_CHATS)
  1383. {
  1384. if (m_apeditChats[sIndex]->m_szText[0])
  1385. {
  1386. m_apeditChats[sIndex]->SetText("%s", (sIndex >= NUM_CHATS - 1) ? "" : m_apeditChats[sIndex + 1]->m_szText);
  1387. m_apeditChats[sIndex]->Compose();
  1388. // If this emptied the field . . .
  1389. if (m_apeditChats[sIndex]->m_szText[0] == '\0')
  1390. {
  1391. m_apeditChats[sIndex]->SetVisible(FALSE);
  1392. // If we're at all off of the view edge . . .
  1393. if (pinfo->Camera()->m_sFilmViewX > 0)
  1394. {
  1395. // Erase now.
  1396. rspGeneralLock(g_pimScreenBuf);
  1397. rspRect(
  1398. RSP_BLACK_INDEX,
  1399. g_pimScreenBuf,
  1400. m_apeditChats[sIndex]->m_sX,
  1401. m_apeditChats[sIndex]->m_sY,
  1402. m_apeditChats[sIndex]->m_im.m_sWidth,
  1403. m_apeditChats[sIndex]->m_im.m_sHeight);
  1404. rspGeneralUnlock(g_pimScreenBuf);
  1405. // Add dirty rectangle to update erased area.
  1406. pinfo->m_drl.Add(
  1407. m_apeditChats[sIndex]->m_sX,
  1408. m_apeditChats[sIndex]->m_sY,
  1409. m_apeditChats[sIndex]->m_im.m_sWidth,
  1410. m_apeditChats[sIndex]->m_im.m_sHeight);
  1411. }
  1412. }
  1413. }
  1414. else
  1415. {
  1416. break;
  1417. }
  1418. sIndex++;
  1419. }
  1420. m_lLastChatMoveTime = rspGetMilliseconds();
  1421. }
  1422. ////////////////////////////////////////////////////////////////////////////////
  1423. // Draw visible chats.
  1424. ////////////////////////////////////////////////////////////////////////////////
  1425. void DrawChats(
  1426. CPlayInfo* pinfo) // In: Info object.
  1427. {
  1428. short sIndex;
  1429. for (sIndex = 0; sIndex < NUM_CHATS; sIndex++)
  1430. {
  1431. if (m_apeditChats[sIndex]->m_sVisible != FALSE)
  1432. {
  1433. m_apeditChats[sIndex]->Draw(g_pimScreenBuf);
  1434. // Make sure this gets to the display.
  1435. pinfo->m_drl.Add(
  1436. m_apeditChats[sIndex]->m_sX,
  1437. m_apeditChats[sIndex]->m_sY,
  1438. m_apeditChats[sIndex]->m_im.m_sWidth,
  1439. m_apeditChats[sIndex]->m_im.m_sHeight);
  1440. }
  1441. }
  1442. }
  1443. ////////////////////////////////////////////////////////////////////////////////
  1444. // Prepare realm
  1445. ////////////////////////////////////////////////////////////////////////////////
  1446. /* virtual */
  1447. short PrepareRealm( // Returns 0 if successfull, non-zero otherwise
  1448. CPlayInfo* pinfo) // I/O: Play info
  1449. {
  1450. CNetClient* pclient = pinfo->Client();
  1451. if (pclient)
  1452. {
  1453. // Clear server flags even if there's no server so we can rely on these flags
  1454. m_bEndServerCleanly = false;
  1455. m_bServerDone = false;
  1456. m_bEndClientCleanly = false;
  1457. m_bClientDone = false;
  1458. m_bHandledUserQuit = false;
  1459. m_bHandledNextRealm = false;
  1460. m_bAbortNow = false;
  1461. m_bCheckForAbortKey = false;
  1462. m_bTimeBombActive = false;
  1463. m_lTimeBomb = 0;
  1464. m_bShowNetFeedback = false;
  1465. m_bFirstCoreLoopUserInput = true;
  1466. m_lLastChatMoveTime = 0;
  1467. pinfo->m_bUserQuitMP = false;
  1468. pinfo->m_bNextRealmMP = false;
  1469. pinfo->m_bDoRealmFrame = false;
  1470. // Call this periodically to let it know we're not locked up
  1471. NetBlockingWatchdog();
  1472. // 09/12/97 MJR - Having this here is a MAJOR MISTAKE, but it happened
  1473. // to work out okay. The order in which the CPlay-derived objects
  1474. // were added to the CPlayGroup HAPPENS TO WORK OUT so that this
  1475. // function is called AFTER the same function in the CPlayRealm object,
  1476. // which is what actually loads the realm. Therefore, this message is
  1477. // only sent AFTER the realm was loaded, which is good. However, the
  1478. // release version also had this hardwired to "true", which means that
  1479. // regardless of whether the realm file loaded or not, it always told
  1480. // the server that it was ready. That's bad.
  1481. //
  1482. // But it gets worse. The original intention of this, assuming it had
  1483. // been done correctly, was for the client to either say "yes, this worked,
  1484. // let's play", or "I had a problem, abort the game". That sounded good
  1485. // at the time, but unfortunately we didn't realize until too late that
  1486. // in the case where some users have added new levels or some users had
  1487. // a limited version of the game, not all the players would have all the
  1488. // same levels available. The preferred approach, in retrospect, would
  1489. // have been for the server to make sure all the clients had all the
  1490. // necessary levels ahead of time. Instead, we're now stuck trying to
  1491. // patch it after-the-fact.
  1492. //
  1493. // The new idea is that if we try to load a realm and we fail, then
  1494. // it will most likely be due to the fact that we don't have that
  1495. // realm, as opposed to the very rare file reading error. And instead
  1496. // of telling the host of the problem, which would cause the host to
  1497. // end the game, we instead lie to the host and say everything is fine,
  1498. // but then follow that up immediately with a request to drop from the
  1499. // game. This way, everyone else can go on playing.
  1500. //
  1501. // So, after all that, we leave the original bad line of code as is!
  1502. // Tell the server we've got the realm ready to go
  1503. pclient->SendRealmStatus(true);
  1504. }
  1505. return 0;
  1506. }
  1507. ////////////////////////////////////////////////////////////////////////////////
  1508. // Start realm
  1509. ////////////////////////////////////////////////////////////////////////////////
  1510. virtual
  1511. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  1512. CPlayInfo* pinfo) // I/O: Play info
  1513. {
  1514. if (pinfo->IsMP())
  1515. {
  1516. // Most of the players will likely end up waiting for at least one other
  1517. // player to start, which means they are staring at a blank screen. To
  1518. // alleviate their fears of a lockup, we show this message.
  1519. RTxt* ptxt = GetNetProbGUI();
  1520. if (ptxt)
  1521. {
  1522. ptxt->SetText(
  1523. "Waiting for other\n"
  1524. "players to get ready...");
  1525. ptxt->Compose();
  1526. ptxt->Move(
  1527. (g_pimScreenBuf->m_sWidth / 2) - (ptxt->m_im.m_sWidth / 2),
  1528. (g_pimScreenBuf->m_sHeight / 2) - (ptxt->m_im.m_sHeight / 2));
  1529. ptxt->SetVisible(TRUE);
  1530. ptxt->Draw(g_pimScreenBuf);
  1531. rspUpdateDisplay(ptxt->m_sX, ptxt->m_sY, ptxt->m_im.m_sWidth, ptxt->m_im.m_sHeight);
  1532. m_bShowNetFeedback = true;
  1533. }
  1534. }
  1535. return 0;
  1536. }
  1537. ////////////////////////////////////////////////////////////////////////////////
  1538. // Core loop user input
  1539. ////////////////////////////////////////////////////////////////////////////////
  1540. void CoreLoopUserInput(
  1541. CPlayInfo* pinfo, // I/O: Play info
  1542. RInputEvent* pie) // I/O: Input event
  1543. {
  1544. if (pinfo->IsMP())
  1545. {
  1546. // Get pointers to make this more readable
  1547. CNetServer* pserver = pinfo->Server();
  1548. CNetClient* pclient = pinfo->Client();
  1549. // If this is the first time this function is being called, then turn off
  1550. // the feedback we displayed in StartRealm().
  1551. if (m_bFirstCoreLoopUserInput)
  1552. {
  1553. m_bFirstCoreLoopUserInput = false;
  1554. m_bShowNetFeedback = false;
  1555. }
  1556. // Check if user aborted the net blocking (meaning we were stuck and the
  1557. // user decided he had waited long enough).
  1558. if (NetBlockingWasAborted())
  1559. pinfo->m_bUserQuitMP = true;
  1560. // Check if the realm is bad (meaning it couldn't be loaded) and we've
  1561. // started playing, and if so, abort the game. If we're the server, this
  1562. // will end the entire game, and if we're a client we'll just drop out
  1563. // and let everyone else continue. Note that we need to wait until we're
  1564. // playing because we don't currently support dropping players during
  1565. // while waiting for the realm to start.
  1566. if (pinfo->m_bBadRealmMP && pclient->IsPlaying())
  1567. {
  1568. // Quit game
  1569. pinfo->m_bUserQuitMP = true;
  1570. // Tell user what's going on
  1571. RTxt* ptxt = GetNetProbGUI();
  1572. if (ptxt)
  1573. {
  1574. ptxt->SetText(
  1575. "You don't have\n"
  1576. "the selected hood.\n"
  1577. "Dropping out...");
  1578. ptxt->Compose();
  1579. ptxt->Move(
  1580. (g_pimScreenBuf->m_sWidth / 2) - (ptxt->m_im.m_sWidth / 2),
  1581. (g_pimScreenBuf->m_sHeight / 2) - (ptxt->m_im.m_sHeight / 2));
  1582. ptxt->SetVisible(TRUE);
  1583. ptxt->Draw(g_pimScreenBuf);
  1584. m_bShowNetFeedback = true;
  1585. }
  1586. }
  1587. // If user wants to quit the MP game, try to do it cleanly. It is
  1588. // possible that the network links are down, so we also set a timer
  1589. // that quits the game if it takes too long to do it the "right" way.
  1590. // Note that a second flag keeps us from doing this more than once.
  1591. if (pinfo->m_bUserQuitMP && !m_bHandledUserQuit)
  1592. {
  1593. m_bHandledUserQuit = true;
  1594. if (pserver)
  1595. {
  1596. // Abort game and end server (local client will end when it gets the message)
  1597. pserver->AbortGame(NetMsg::UserAbortedGame);
  1598. m_bEndServerCleanly = true;
  1599. }
  1600. else
  1601. {
  1602. // Tell server we're dropping and then disconnect from it (cleanly, which means
  1603. // it waits until all messages have been sent). We then set the flag so we
  1604. // do essentially the same thing at this level -- wait for all messages to be sent.
  1605. pclient->Drop();
  1606. pinfo->SetGameState_GameAborted();
  1607. m_bEndClientCleanly = true;
  1608. }
  1609. // Set time bomb in case the "nice" way doesn't work
  1610. m_lTimeBomb = rspGetMilliseconds() + g_GameSettings.m_lNetForceAbortTime;
  1611. m_bTimeBombActive = true;
  1612. }
  1613. // Once the time bomb goes off, we display a message telling the user that something
  1614. // has gone very wrong, and they can either wait a little longer or hit a key to abort.
  1615. // Once that message is displayed, we check if the user has hit the abort key, and if
  1616. // so, we set the ultimate abort flag.
  1617. if (m_bCheckForAbortKey)
  1618. {
  1619. if ((pie->type == RInputEvent::Key) && (pie->lKey == NET_PROB_GUI_ABORT_GK_KEY))
  1620. {
  1621. pinfo->SetGameState_GameAborted();
  1622. m_bAbortNow = true;
  1623. }
  1624. }
  1625. else
  1626. {
  1627. if (m_bTimeBombActive)
  1628. {
  1629. if (rspGetMilliseconds() > m_lTimeBomb)
  1630. {
  1631. RTxt* ptxt = GetNetProbGUI();
  1632. if (ptxt)
  1633. {
  1634. ptxt->SetText("%s", g_pszNetProb_General);
  1635. ptxt->Compose();
  1636. ptxt->SetVisible(TRUE);
  1637. ptxt->Move(
  1638. (g_pimScreenBuf->m_sWidth / 2) - (ptxt->m_im.m_sWidth / 2),
  1639. (g_pimScreenBuf->m_sHeight / 2) - (ptxt->m_im.m_sHeight / 2));
  1640. // Set flag to indicate that we're waiting for user to abort
  1641. m_bCheckForAbortKey = true;
  1642. }
  1643. else
  1644. {
  1645. // If we can't display the message for the user, we don't have much
  1646. // choice but to abort
  1647. TRACE("CPlayNet::CoreLoopUpdate(): Unable to show abort GUI. Aborting.\n");
  1648. pinfo->SetGameState_GameAborted();
  1649. m_bAbortNow = true;
  1650. }
  1651. }
  1652. }
  1653. }
  1654. // Try doing the next frame, which involves setting all the players' inputs.
  1655. // We must NEVER try to do another frame if we haven't done the previous one,
  1656. // because skipping a frame would screw up network sync!
  1657. if (!pinfo->m_bDoRealmFrame)
  1658. {
  1659. // If we can do a frame, all player's inputs will be returned
  1660. UINPUT aInputs[Net::MaxNumIDs];
  1661. /** SPA **/
  1662. short sFrameTime = 0;
  1663. if (pclient->CanDoFrame(aInputs, &sFrameTime)) // Get frame time as well *SPA
  1664. {
  1665. pinfo->SetFrameTime(sFrameTime);
  1666. /** SPA **/
  1667. // Copy inputs into the input module
  1668. for (Net::ID id = 0; id < Net::MaxNumIDs; id++)
  1669. SetInput(id, aInputs[id]);
  1670. // Set flag so we'll do the frame
  1671. pinfo->m_bDoRealmFrame = true;
  1672. }
  1673. else
  1674. {
  1675. //------------------------------------------------------------------------------
  1676. // Server section *SPA 1/13/98
  1677. //------------------------------------------------------------------------------
  1678. if (pserver && !m_bServerDone)
  1679. {
  1680. // Check to see if we have lost a peer through a timeout
  1681. Net::ID peerID = pclient->CheckForLostPeer();
  1682. if (peerID < Net::MaxNumIDs)
  1683. // Lost one, so drop him so the rest of us can go on
  1684. pserver->DropClient(peerID);
  1685. }
  1686. }
  1687. }
  1688. }
  1689. }
  1690. ////////////////////////////////////////////////////////////////////////////////
  1691. // Core loop update
  1692. ////////////////////////////////////////////////////////////////////////////////
  1693. /* virtual */
  1694. void CoreLoopUpdate(
  1695. CPlayInfo* pinfo) // I/O: Play info
  1696. {
  1697. // If we're in MP mode, then there's always a client and there may be a server
  1698. if (pinfo->IsMP())
  1699. {
  1700. // Get pointers to make this more readable
  1701. CNetServer* pserver = pinfo->Server();
  1702. CNetClient* pclient = pinfo->Client();
  1703. // Call this periodically to let it know we're not locked up
  1704. NetBlockingWatchdog();
  1705. //------------------------------------------------------------------------------
  1706. // Server section
  1707. //------------------------------------------------------------------------------
  1708. if (pserver && !m_bServerDone)
  1709. {
  1710. // Update server (send and receive messages)
  1711. pserver->Update();
  1712. // Check if we're trying to end
  1713. if (m_bEndServerCleanly)
  1714. {
  1715. // If there isn't anything more to send, then we're done. It's safe to
  1716. // ignore incoming messages because they will get bufferred and we'll
  1717. // handle them in the next realm if there is one, and if the game is
  1718. // over, they don't matter anyway!!!
  1719. if (!pserver->IsMoreToSend())
  1720. m_bServerDone = true;
  1721. }
  1722. else
  1723. {
  1724. // If the realm is done, tell the clients about it. Only the server ever
  1725. // checks for the end of the realm -- the clients rely on the server to
  1726. // tell them when to end. In case this flag somehow gets set multiple
  1727. // times, we only want to handle it once per realm, hence the purpose
  1728. // of the second flag.
  1729. if (pinfo->m_bNextRealmMP && !m_bHandledNextRealm)
  1730. {
  1731. //------------------------------------------------------------------------------
  1732. // DIRECT BREACH OF CLIENT/SERVER SEPARATION!
  1733. //
  1734. // In order to get all the clients to stop on the same frame, the server
  1735. // needs to choose a frame that no client has yet reached. Unfortunately,
  1736. // it has no way of knowing this (having the clients constantly report
  1737. // their frame seq's would not help because the information would always
  1738. // be out of date due to latencies. Instead, we violate the client/server
  1739. // separation and ask the local client for the most recent input seq that
  1740. // it sent to any of the other clients. Since no client can possibly be
  1741. // ahead of that (it can't do a frame unless it has all the inputs), we
  1742. // can safely use that as the frame to stop on.
  1743. //
  1744. // But that's not all!!! If we were to send the "stop on frame" message
  1745. // to all the clients, including the local one, there is still the
  1746. // latency to deal with, and even message to local clients have some
  1747. // latnecy. During that time, if the local client sends out input beyond
  1748. // the stop frame, all is lost. So, we again violate the client/server
  1749. // separation and DIRECTLY tell the local client what frame to stop on.
  1750. // This ensures that it will not send out any excess inputs.
  1751. //------------------------------------------------------------------------------
  1752. // Note that we add one to the client's sequence as an extra safey measure.
  1753. Net::SEQ seqHalt = (Net::SEQ)(pclient->GetInputSeqNotYetSent() + (Net::SEQ)1);
  1754. if (pserver->NextRealm(seqHalt))
  1755. {
  1756. // Tell local client (see above explanation)
  1757. pclient->SetHaltFrame(seqHalt);
  1758. // Note that we handled this
  1759. m_bHandledNextRealm = true;
  1760. // End the server cleanly
  1761. m_bEndServerCleanly = true;
  1762. }
  1763. }
  1764. else
  1765. {
  1766. // Process messages
  1767. NetMsg msg;
  1768. pserver->GetMsg(&msg);
  1769. switch (msg.msg.nothing.ucType)
  1770. {
  1771. case NetMsg::ERR:
  1772. // We'll learn about any really bad errors via the client!
  1773. break;
  1774. case NetMsg::NOTHING:
  1775. break;
  1776. case NetMsg::CHAT_REQ:
  1777. // Handled at lower level.
  1778. break;
  1779. default:
  1780. TRACE("CPlayNet::CoreLoopUpdate(): Unhandled message: %hd\n", (short)msg.msg.nothing.ucType);
  1781. break;
  1782. }
  1783. }
  1784. }
  1785. }
  1786. //------------------------------------------------------------------------------
  1787. // Client section
  1788. //------------------------------------------------------------------------------
  1789. // Check if local input is needed, and if so, hand it over
  1790. if (!pinfo->m_bBadRealmMP)
  1791. {
  1792. if (pclient->IsLocalInputNeeded())
  1793. {
  1794. if (!pinfo->m_bInMenu && !pinfo->m_bChatting)
  1795. pclient->SetLocalInput(GetLocalInput(pinfo->Realm()) );
  1796. else
  1797. pclient->SetLocalInput(INPUT_IDLE);
  1798. }
  1799. }
  1800. // Handle network communiciation stuff
  1801. if (!m_bClientDone)
  1802. {
  1803. pclient->Update();
  1804. // Check if we're trying to end
  1805. if (m_bEndClientCleanly)
  1806. {
  1807. // If there isn't anything more to send, then we're done
  1808. if (!pclient->IsMoreToSend())
  1809. m_bClientDone = true;
  1810. }
  1811. else
  1812. {
  1813. // Process messages from server
  1814. NetMsg msg;
  1815. pclient->GetMsg(&msg);
  1816. switch (msg.msg.nothing.ucType)
  1817. {
  1818. case NetMsg::JOINED:
  1819. break;
  1820. case NetMsg::DROPPED:
  1821. // If I am dropped, abort the game and end immediately
  1822. if (msg.msg.dropped.id == pclient->GetID())
  1823. {
  1824. pinfo->SetGameState_GameAborted();
  1825. m_bClientDone = true;
  1826. }
  1827. break;
  1828. case NetMsg::CHAT:
  1829. {
  1830. short sIndex = 0;
  1831. while (m_apeditChats[sIndex]->m_szText[0])
  1832. {
  1833. if (++sIndex >= NUM_CHATS) // Should never be greater but...
  1834. {
  1835. MoveChatsUp(pinfo);
  1836. sIndex = NUM_CHATS - 1;
  1837. break;
  1838. }
  1839. }
  1840. ASSERT(sIndex < NUM_CHATS);
  1841. m_apeditChats[sIndex]->SetText("%s", msg.msg.chat.acText);
  1842. m_apeditChats[sIndex]->Compose();
  1843. m_apeditChats[sIndex]->SetVisible(TRUE);
  1844. m_lLastChatMoveTime = rspGetMilliseconds();
  1845. break;
  1846. }
  1847. case NetMsg::ERR:
  1848. break;
  1849. case NetMsg::NOTHING:
  1850. break;
  1851. case NetMsg::ABORT_GAME:
  1852. // Abort game and end client immediately
  1853. pinfo->SetGameState_GameAborted();
  1854. m_bClientDone = true;
  1855. break;
  1856. case NetMsg::NEXT_REALM:
  1857. // Go on to the next realm (in case there's a server we end it cleanly,
  1858. // too, because it was waiting for it's local client to get this message)
  1859. pinfo->SetGameState_NextRealm();
  1860. m_bEndServerCleanly = true;
  1861. m_bEndClientCleanly = true;
  1862. break;
  1863. case NetMsg::PROGRESS_REALM:
  1864. // This was disabled for reasons explained at length in NetServer.cpp -- suffice it to say it's
  1865. // far from an easy fix.
  1866. #if 0
  1867. m_bShowNetFeedback = false;
  1868. if (msg.msg.progressRealm.sNumNotReady > 0)
  1869. {
  1870. // Show the player how many other players we're waiting for
  1871. RTxt* ptxt = GetNetProbGUI();
  1872. if (ptxt)
  1873. {
  1874. ptxt->SetText("Waiting for %hd\nplayer(s) to get ready...", msg.msg.progressRealm.sNumNotReady);
  1875. ptxt->Compose();
  1876. ptxt->Move(
  1877. (g_pimScreenBuf->m_sWidth / 2) - (ptxt->m_im.m_sWidth / 2),
  1878. (g_pimScreenBuf->m_sHeight / 2) - (ptxt->m_im.m_sHeight / 2));
  1879. ptxt->SetVisible(TRUE);
  1880. m_bShowNetFeedback = true;
  1881. }
  1882. }
  1883. #endif
  1884. break;
  1885. case NetMsg::PROCEED:
  1886. break;
  1887. default:
  1888. TRACE("CPlayNet::CoreLoopUpdate(): Unhandled message: %hd\n", (short)msg.msg.nothing.ucType);
  1889. break;
  1890. }
  1891. }
  1892. }
  1893. // Govern loop to a maximum of frames-per-second rate
  1894. //
  1895. // This is definitely not done. It's a waste to sit here in a dead loop, when we could be checking
  1896. // for more network input/output/whatever. Perhaps this whole function should be inside a do/while
  1897. // loop that loops as long until we've slowed to the proper frame rate.
  1898. //
  1899. // Actually, I think the real answer might be to put timers on everything in Play. There's no
  1900. // point in checking for keyboard input 100 times per second. There's no point in doing many of
  1901. // the things we do at that speed, but we DO want to loop through everything as quickly as possible
  1902. // to give an opportunity to everyone to do whatever DOES need to be done.
  1903. //
  1904. // The frames-per-second governor would be done at the point were we actually render the frame
  1905. // and increment the frame seq. That portion would say "If we're ready to render the frame AND
  1906. // it's been 1/50th of a second since we rendered the last frame, then do it."
  1907. // while (rspGetMilliseconds() < m_lGovernTime)
  1908. // ;
  1909. // m_lGovernTime = rspGetMilliseconds() + Net::MinFrameTime;
  1910. if (m_lLastChatMoveTime + CHAT_DELAY < rspGetMilliseconds() )
  1911. {
  1912. // Move the chats up -- this updates m_lLastChatMoveTime.
  1913. MoveChatsUp(pinfo);
  1914. }
  1915. }
  1916. }
  1917. ////////////////////////////////////////////////////////////////////////////////
  1918. // Core loop render -- create and update images to the composite buffer but do
  1919. // NOT update the screen.
  1920. ////////////////////////////////////////////////////////////////////////////////
  1921. void CoreLoopRender(
  1922. CPlayInfo* pinfo) // I/O: Play info
  1923. {
  1924. if (pinfo->IsMP())
  1925. {
  1926. // Draw chats, if any.
  1927. DrawChats(pinfo);
  1928. // If we're waiting for user to abort or there was a net problem
  1929. if (m_bCheckForAbortKey || IsNetProb() || m_bShowNetFeedback)
  1930. {
  1931. // What we want to do is put it in the composite buffer. It will be
  1932. // copied to the screen in CoreLoopDraw().
  1933. RTxt* ptxt = GetNetProbGUI();
  1934. if (ptxt)
  1935. {
  1936. // Draw locks the screen as is necessary for the current buffer type.
  1937. // Even though it's already locked, this should be fine.
  1938. ptxt->Draw(g_pimScreenBuf);
  1939. // Add a rectangle representing this GUI so it gets updated to the screen.
  1940. pinfo->m_drl.Add(ptxt->m_sX, ptxt->m_sY, ptxt->m_im.m_sWidth, ptxt->m_im.m_sHeight);
  1941. }
  1942. }
  1943. }
  1944. }
  1945. ////////////////////////////////////////////////////////////////////////////////
  1946. // Determine if core loop is done
  1947. ////////////////////////////////////////////////////////////////////////////////
  1948. /* virtual */
  1949. bool IsCoreLoopDone( // Returns true if done, false otherwise
  1950. CPlayInfo* pinfo) // I/O: Play info
  1951. {
  1952. bool bDone;
  1953. if (pinfo->IsMP())
  1954. {
  1955. // This is the ultimate abort flag
  1956. if (m_bAbortNow)
  1957. bDone = true;
  1958. else
  1959. {
  1960. // Check server (if there is one)
  1961. bDone = pinfo->Server() ? m_bServerDone : true;
  1962. // There's always a client in MP mode
  1963. bDone &= m_bClientDone;
  1964. }
  1965. }
  1966. else
  1967. {
  1968. // If client doesn't exist, use base-class implimentation
  1969. bDone = CPlay::IsCoreLoopDone(pinfo);
  1970. }
  1971. return bDone;
  1972. }
  1973. };
  1974. ////////////////////////////////////////////////////////////////////////////////
  1975. //
  1976. // Display API
  1977. //
  1978. ////////////////////////////////////////////////////////////////////////////////
  1979. class CPlayStatus : public CPlay
  1980. {
  1981. //------------------------------------------------------------------------------
  1982. // Types, enums, etc.
  1983. //------------------------------------------------------------------------------
  1984. private:
  1985. //------------------------------------------------------------------------------
  1986. // Variables
  1987. //------------------------------------------------------------------------------
  1988. private:
  1989. long m_lLastFrameTime;
  1990. long m_lSumFrameTimes;
  1991. long m_lNumFrames;
  1992. long m_lLastIterationTime;
  1993. /* 12/3/97 AJC */
  1994. Net::SEQ m_seqPrevFrameSeq;
  1995. Net::SEQ m_seqCurrFrameSeq;
  1996. long m_lFramePerSecond;
  1997. long m_lPrevSeqTime;
  1998. /* 12/3/97 AJC */
  1999. long m_lSumIterationTimes;
  2000. long m_lNumIterations;
  2001. RRect m_rectDude;
  2002. RRect m_rectRealm;
  2003. RRect m_rectInfo;
  2004. char m_szFileDescriptor[256];
  2005. RPrint m_print;
  2006. bool m_bFirstUpdate;
  2007. bool m_bUpdateDude; // true, if dude status area was updated.
  2008. bool m_bUpdateRealm; // true, if realm status area was updated.
  2009. //------------------------------------------------------------------------------
  2010. // Functions
  2011. //------------------------------------------------------------------------------
  2012. public:
  2013. ////////////////////////////////////////////////////////////////////////////////
  2014. // Constructor
  2015. ////////////////////////////////////////////////////////////////////////////////
  2016. CPlayStatus(void)
  2017. : m_bUpdateRealm(true) // valgrind fix. --ryan.
  2018. {
  2019. }
  2020. ////////////////////////////////////////////////////////////////////////////////
  2021. // Destructor
  2022. ////////////////////////////////////////////////////////////////////////////////
  2023. /* virtual */
  2024. ~CPlayStatus()
  2025. {
  2026. }
  2027. ////////////////////////////////////////////////////////////////////////////////
  2028. // Start realm
  2029. ////////////////////////////////////////////////////////////////////////////////
  2030. /* virtual */
  2031. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  2032. CPlayInfo* pinfo) // I/O: Play info
  2033. {
  2034. if (!pinfo->m_bBadRealmMP)
  2035. {
  2036. // Initialize times and counts.
  2037. m_lLastFrameTime = rspGetMilliseconds();
  2038. m_lLastIterationTime = m_lLastFrameTime;
  2039. m_lSumFrameTimes = 0;
  2040. m_lNumFrames = 0;
  2041. pinfo->m_lSumUpdateDisplayTimes = 0;
  2042. /*** 12/3/97 AJC ***/
  2043. m_seqCurrFrameSeq = 0;
  2044. m_lFramePerSecond = 0;
  2045. if (pinfo->IsMP())
  2046. m_seqPrevFrameSeq = pinfo->Client()->GetInputSeqNotYetSent();
  2047. m_lPrevSeqTime = rspGetMilliseconds();
  2048. /*** 12/3/97 AJC ***/
  2049. m_lSumIterationTimes = 0;
  2050. m_lNumIterations = 0;
  2051. // Get app's descriptor.
  2052. Play_GetApplicationDescriptor(m_szFileDescriptor, sizeof(m_szFileDescriptor));
  2053. // Setup rects for various status areas
  2054. m_rectDude.sX = DUDE_STATUS_RECT_X;
  2055. m_rectDude.sY = DUDE_STATUS_RECT_Y;
  2056. m_rectDude.sW = DUDE_STATUS_RECT_W;
  2057. m_rectDude.sH = DUDE_STATUS_RECT_H;
  2058. m_rectRealm.sX = REALM_STATUS_RECT_X;
  2059. m_rectRealm.sY = REALM_STATUS_RECT_Y;
  2060. m_rectRealm.sW = REALM_STATUS_RECT_W;
  2061. m_rectRealm.sH = REALM_STATUS_RECT_H;
  2062. m_rectInfo.sX = INFO_STATUS_RECT_X;
  2063. m_rectInfo.sY = INFO_STATUS_RECT_Y;
  2064. m_rectInfo.sW = INFO_STATUS_RECT_W;
  2065. m_rectInfo.sH = INFO_STATUS_RECT_H;
  2066. // We must lock the buffer before accessing it.
  2067. rspLockBuffer();
  2068. // Init the tool bar
  2069. ToolBarInit(pinfo->Realm()->m_phood);
  2070. // Setup print utilizing some values initialized by ToolBarInit().
  2071. m_print.SetFont(DISP_INFO_FONT_HEIGHT, &g_fontBig);
  2072. m_print.SetColor(gsStatusFontForeIndex, gsStatusFontBackIndex, gsStatusFontShadowIndex);
  2073. m_print.SetEffectAbs(RPrint::SHADOW_X, 1);
  2074. m_print.SetEffectAbs(RPrint::SHADOW_Y, 1);
  2075. m_print.SetDestination(g_pimScreenBuf);
  2076. // Render it now so it appears in tandem with the top bar: unless
  2077. // its the last demo level for the game where we don't want the
  2078. // bars to let the player know that they are not under control.
  2079. if (!g_bLastLevelDemo)
  2080. {
  2081. ToolBarRender(
  2082. pinfo->Realm()->m_phood,
  2083. g_pimScreenBuf,
  2084. m_rectDude.sX,
  2085. m_rectDude.sY,
  2086. pinfo->LocalDudePointer(),TRUE);
  2087. // Draw the top bar just once. I'm assuming all the rest of the updates are
  2088. // partial and, therefore, this only needs to be done once.
  2089. rspBlit(
  2090. pinfo->Realm()->m_phood->m_pimTopBar,
  2091. g_pimScreenBuf,
  2092. 0,
  2093. 0,
  2094. REALM_STATUS_RECT_X,
  2095. REALM_STATUS_RECT_Y,
  2096. pinfo->Realm()->m_phood->m_pimTopBar->m_sWidth,
  2097. pinfo->Realm()->m_phood->m_pimTopBar->m_sHeight);
  2098. }
  2099. // Unlock once done.
  2100. rspUnlockBuffer();
  2101. // Make sure this these get reflected on the screen intially.
  2102. rspUpdateDisplay(m_rectDude.sX, m_rectDude.sY, m_rectDude.sW, m_rectDude.sH);
  2103. rspUpdateDisplay(
  2104. REALM_STATUS_RECT_X,
  2105. REALM_STATUS_RECT_Y,
  2106. pinfo->Realm()->m_phood->m_pimTopBar->m_sWidth,
  2107. pinfo->Realm()->m_phood->m_pimTopBar->m_sHeight);
  2108. }
  2109. return 0;
  2110. }
  2111. ////////////////////////////////////////////////////////////////////////////////
  2112. // Core loop update
  2113. ////////////////////////////////////////////////////////////////////////////////
  2114. /* virtual */
  2115. void CoreLoopUpdate(
  2116. CPlayInfo* pinfo) // I/O: Play info
  2117. {
  2118. if (!pinfo->m_bBadRealmMP)
  2119. {
  2120. /**** 12/3/97 AJC ****/
  2121. if (pinfo->IsMP())
  2122. {
  2123. // Get current frame sequence number
  2124. m_seqCurrFrameSeq = pinfo->Client()->GetInputSeqNotYetSent();
  2125. // Check if it's the next frame, if it is, calculate frame per sec
  2126. if (m_seqCurrFrameSeq != m_seqPrevFrameSeq)
  2127. {
  2128. long lCurrSeqTime = rspGetMilliseconds();
  2129. m_lFramePerSecond = 1000.0 * (m_seqCurrFrameSeq - m_seqPrevFrameSeq) / (lCurrSeqTime - m_lPrevSeqTime);
  2130. m_seqPrevFrameSeq = m_seqCurrFrameSeq;
  2131. m_lPrevSeqTime = lCurrSeqTime;
  2132. }
  2133. }
  2134. /**** 12/3/97 AJC ****/
  2135. //==============================================================================
  2136. // Check for death stuff
  2137. //==============================================================================
  2138. CDude* pdudeLocal = pinfo->LocalDudePointer();
  2139. // If the local dude is dead . . .
  2140. if (pdudeLocal != NULL)
  2141. {
  2142. if (pdudeLocal->IsDead() )
  2143. {
  2144. // Make sure X Ray is on so we can see him.
  2145. pinfo->Realm()->m_scene.SetXRayAll(TRUE);
  2146. }
  2147. else
  2148. {
  2149. // If alive, use user setting.
  2150. pinfo->Realm()->m_scene.SetXRayAll( (pinfo->m_bXRayAll == true) ? TRUE : FALSE);
  2151. }
  2152. }
  2153. }
  2154. }
  2155. ////////////////////////////////////////////////////////////////////////////////
  2156. // Core loop render on top -- create and update images to the composite buffer
  2157. // but do NOT update the screen.
  2158. // NOTE that we use CoreLoopRenderOnTop() not b/c these go on top but instead as
  2159. // a cheat to make sure that the m_bDrawFrame flag has already been set.
  2160. ////////////////////////////////////////////////////////////////////////////////
  2161. /* virtual */
  2162. void CoreLoopRenderOnTop(
  2163. CPlayInfo* pinfo) // I/O: Play info
  2164. {
  2165. if (!pinfo->m_bBadRealmMP)
  2166. {
  2167. // Only if we're not on the menu
  2168. if (!pinfo->m_bInMenu)
  2169. {
  2170. // If display info is enabled . . .
  2171. if (g_GameSettings.m_sDisplayInfo)
  2172. {
  2173. // Add this iteration's elapsed time to total (sum).
  2174. m_lSumIterationTimes += (rspGetMilliseconds() - m_lLastIterationTime);
  2175. // Increment number of iterations.
  2176. m_lNumIterations++;
  2177. }
  2178. m_lLastIterationTime = rspGetMilliseconds();
  2179. // No need for this unless we're going to draw . . .
  2180. if (pinfo->m_bDrawFrame)
  2181. {
  2182. bool bUpdateRealm = false;
  2183. // If its the last level then don't draw the top and bottom, for
  2184. // a letterbox look that lets the player know they don't have
  2185. // control for this demo
  2186. if (!g_bLastLevelDemo)
  2187. {
  2188. // Update the realm (or Score) status
  2189. bUpdateRealm = ScoreUpdateDisplay(
  2190. g_pimScreenBuf,
  2191. &m_rectRealm,
  2192. pinfo->Realm(),
  2193. pinfo->Client(),
  2194. REALM_STATUS_RECT_X,
  2195. REALM_STATUS_RECT_Y,
  2196. pinfo->Realm()->m_phood);
  2197. if (bUpdateRealm == true)
  2198. {
  2199. pinfo->m_drl.Add(m_rectRealm.sX, m_rectRealm.sY, m_rectRealm.sW, m_rectRealm.sH);
  2200. }
  2201. // Generate TooL Bar
  2202. if (ToolBarRender(
  2203. pinfo->Realm()->m_phood,
  2204. g_pimScreenBuf,
  2205. m_rectDude.sX,
  2206. m_rectDude.sY,
  2207. pinfo->LocalDudePointer()) == true)
  2208. {
  2209. pinfo->m_drl.Add(m_rectDude.sX, m_rectDude.sY, m_rectDude.sW, m_rectDude.sH);
  2210. }
  2211. }
  2212. // If display info is enabled, draw the info
  2213. if (g_GameSettings.m_sDisplayInfo)
  2214. {
  2215. // Add this frame's elapsed time to total (sum)
  2216. m_lSumFrameTimes += (rspGetMilliseconds() - m_lLastFrameTime);
  2217. // Increment number of frames
  2218. m_lNumFrames++;
  2219. // Just update whenever the realm is updating so we can cash in on their
  2220. // update display all.
  2221. // If were not in MP then calculate the FPS. If we are in MP, FPS is calculated elsewhere *SPA
  2222. if (!pinfo->IsMP())
  2223. m_lFramePerSecond = (1000 * m_lNumFrames) / m_lSumFrameTimes;
  2224. if (m_bUpdateRealm)
  2225. {
  2226. m_print.print(
  2227. m_rectInfo.sX, m_rectInfo.sY,
  2228. "FPS: %ld Video H/W Update: %ld%% %s",
  2229. m_lFramePerSecond,
  2230. (pinfo->m_lSumUpdateDisplayTimes * 100) / m_lSumFrameTimes,
  2231. m_szFileDescriptor);
  2232. // Reset.
  2233. m_lSumFrameTimes = 0;
  2234. m_lNumFrames = 0;
  2235. pinfo->m_lSumUpdateDisplayTimes = 0;
  2236. }
  2237. }
  2238. // By resetting this here, we ignore any time spent in the above code
  2239. m_lLastFrameTime = rspGetMilliseconds();
  2240. }
  2241. }
  2242. }
  2243. }
  2244. };
  2245. #if 1 //PLATFORM_UNIX
  2246. #include <sys/stat.h>
  2247. static void EnumSaveGamesSlots(Menu *menu)
  2248. {
  2249. char gamename[RSP_MAX_PATH];
  2250. int Max = (sizeof(menu->ami) / sizeof(menu->ami[0])) - 1;
  2251. if (Max > MAX_SAVE_SLOTS)
  2252. Max = MAX_SAVE_SLOTS;
  2253. for (int i = 0; i < Max; i++)
  2254. {
  2255. snprintf(gamename, sizeof (gamename), "%s/%d.gme", SAVEGAME_DIR, i);
  2256. const char *fname = FindCorrectFile(gamename, "w");
  2257. struct stat statbuf;
  2258. char timebuf[32];
  2259. const char *str = "unknown";
  2260. if (stat(fname, &statbuf) == -1)
  2261. str = "available";
  2262. else
  2263. {
  2264. struct tm *tm;
  2265. if ((tm = localtime((const time_t*)&statbuf.st_mtime)) != NULL)
  2266. {
  2267. strftime(timebuf, sizeof (timebuf), "%m/%d/%y %H:%M", tm);
  2268. str = timebuf;
  2269. }
  2270. }
  2271. #ifdef MOBILE
  2272. snprintf(gamename, sizeof (gamename), "%d - [%s]", i, str);
  2273. menu->ami[i].pszText = strdup(gamename);
  2274. #else
  2275. snprintf(gamename, sizeof (gamename), "%s/%d.gme [%s]", SAVEGAME_DIR, i, str);
  2276. menu->ami[i].pszText = strdup(gamename);
  2277. #endif
  2278. menu->ami[i].sEnabled = (menu->ami[i].pszText != NULL);
  2279. if (!menu->ami[i].sEnabled)
  2280. break;
  2281. }
  2282. }
  2283. #endif
  2284. #ifdef MOBILE
  2285. static bool continueIsRestart; //This is to make the continue button actually restart the level
  2286. #endif
  2287. ////////////////////////////////////////////////////////////////////////////////
  2288. //
  2289. // Input API
  2290. //
  2291. ////////////////////////////////////////////////////////////////////////////////
  2292. class CPlayInput : public CPlay
  2293. {
  2294. //------------------------------------------------------------------------------
  2295. // Types, enums, etc.
  2296. //------------------------------------------------------------------------------
  2297. private:
  2298. //------------------------------------------------------------------------------
  2299. // Variables
  2300. //------------------------------------------------------------------------------
  2301. private:
  2302. long m_lDemoDeadTime; // Time dude has been dead for
  2303. U8* m_pau8KeyStatus; // Key status array
  2304. REdit* m_peditChatIn; // Outgoing chat.
  2305. //------------------------------------------------------------------------------
  2306. // Functions
  2307. //------------------------------------------------------------------------------
  2308. public:
  2309. ////////////////////////////////////////////////////////////////////////////////
  2310. // Constructor
  2311. ////////////////////////////////////////////////////////////////////////////////
  2312. CPlayInput(void)
  2313. {
  2314. // We don't care if this fails.
  2315. m_peditChatIn = (REdit*)RGuiItem::LoadInstantiate(FullPathHD(CHAT_IN_GUI) );
  2316. if (m_peditChatIn)
  2317. {
  2318. // Limit to chat length minus some room for our name.
  2319. m_peditChatIn->m_sMaxText = MIN(CHAT_IN_LENGTH, Net::MaxChatSize - 1);
  2320. // Recreate in the correct spot and dimensions . . .
  2321. if (m_peditChatIn->Create(
  2322. DUDE_STATUS_RECT_X,
  2323. DUDE_STATUS_RECT_Y - m_peditChatIn->m_im.m_sHeight,
  2324. DUDE_STATUS_RECT_W,
  2325. m_peditChatIn->m_im.m_sHeight,
  2326. g_pimScreenBuf->m_sDepth) == 0)
  2327. {
  2328. m_peditChatIn->SetVisible(FALSE);
  2329. }
  2330. else
  2331. {
  2332. delete m_peditChatIn;
  2333. m_peditChatIn = NULL;
  2334. }
  2335. }
  2336. #ifdef MOBILE
  2337. continueIsRestart = false;
  2338. #endif
  2339. }
  2340. ////////////////////////////////////////////////////////////////////////////////
  2341. // Destructor
  2342. ////////////////////////////////////////////////////////////////////////////////
  2343. /* virtual */
  2344. ~CPlayInput()
  2345. {
  2346. delete m_peditChatIn;
  2347. m_peditChatIn = NULL;
  2348. }
  2349. ////////////////////////////////////////////////////////////////////////////////
  2350. // Start realm
  2351. ////////////////////////////////////////////////////////////////////////////////
  2352. virtual
  2353. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  2354. CPlayInfo* pinfo) // I/O: Play info
  2355. {
  2356. // Reset time he's been dead ('cause he isn't dead yet)
  2357. m_lDemoDeadTime = -1;
  2358. // Get the key status array
  2359. m_pau8KeyStatus = rspGetKeyStatusArray();
  2360. return 0;
  2361. }
  2362. ////////////////////////////////////////////////////////////////////////////////
  2363. // End realm
  2364. ////////////////////////////////////////////////////////////////////////////////
  2365. /* virtual */
  2366. void EndRealm(
  2367. CPlayInfo* pinfo) // I/O: Play info
  2368. {
  2369. }
  2370. ////////////////////////////////////////////////////////////////////////////////
  2371. // Core loop user input
  2372. ////////////////////////////////////////////////////////////////////////////////
  2373. virtual
  2374. void CoreLoopUserInput(
  2375. CPlayInfo* pinfo, // I/O: Play info
  2376. RInputEvent* pie) // I/O: Input event
  2377. {
  2378. // If we're in the menu, then just do that
  2379. if (pinfo->m_bInMenu)
  2380. {
  2381. DoMenu(pinfo, pie);
  2382. }
  2383. else
  2384. {
  2385. // Get usefull pointers
  2386. CDude* pdudeLocal = pinfo->LocalDudePointer();
  2387. CRealm* prealm = pinfo->Realm();
  2388. //==============================================================================
  2389. // A system-specific quit ends the game (MP mode is handled elsewhere)
  2390. //==============================================================================
  2391. if (rspGetQuitStatus())
  2392. {
  2393. if (pinfo->IsMP())
  2394. pinfo->m_bUserQuitMP = true;
  2395. else
  2396. pinfo->SetGameState_GameAborted();
  2397. }
  2398. if (!pinfo->m_bBadRealmMP)
  2399. {
  2400. //==============================================================================
  2401. // Handle all the local input stuff.
  2402. //==============================================================================
  2403. // Always default to "not pressed" for the "end-the-level-now" key
  2404. bool bEndLevelKey = false;
  2405. // If not in special end-of-the-game-demo-mode...
  2406. if (!g_bLastLevelDemo)
  2407. {
  2408. // If playing back a demo and there's any input, then ignore the input and end the demo
  2409. //if ((GetInputMode() == INPUT_MODE_PLAYBACK) && (pie->type != RInputEvent::None))
  2410. if ((GetInputMode() == INPUT_MODE_PLAYBACK) && (pie->type == RInputEvent::Key))
  2411. {
  2412. pinfo->SetGameState_GameAborted();
  2413. }
  2414. else
  2415. {
  2416. // MUST NOT DO THIS IN SALES MODE! Normally, this cheat just takes
  2417. // you to the next level. In sales mode, though, this cheat gives
  2418. // you lots o stuff as well. Going to the next level in Sales Mode
  2419. // is handled separately.
  2420. #if !defined(SALES_DEMO)
  2421. // Cheats are disabled in MP mode by other code, but it still seems better to
  2422. // clarify that this is NOT allowed in MP mode.
  2423. if (!pinfo->IsMP())
  2424. {
  2425. if ((GetInput(0) & INPUT_WEAPONS_MASK) == INPUT_CHEAT_29)
  2426. {
  2427. // End the realm without checking whether goal has been met
  2428. pinfo->SetGameState_NextRealm();
  2429. }
  2430. }
  2431. #endif // !SALES_DEMO
  2432. // Handle pause button on joystick
  2433. if (g_InputSettings.m_sUseJoy)
  2434. {
  2435. XInputState xis;
  2436. GetXInputStateNoUpdate(&xis);
  2437. if (xis.ButtonState[g_InputSettings.m_sJoyStartButton] == XInputState::Press)
  2438. {
  2439. // Start the menu. I have no idea why the check to make sure the game
  2440. // is not over was necessary, but it doesn't seem like it could hurt,
  2441. // so it was safer to leave it in.
  2442. if (!pinfo->IsGameOver())
  2443. StartMenu(pinfo);
  2444. }
  2445. }
  2446. // Process local key events
  2447. if (pie->type == RInputEvent::Key && pie->sUsed == FALSE)
  2448. {
  2449. // This is the switch for things that don't want specific RSP_GKF_* modifier keys
  2450. switch (pie->lKey & 0x0000FFFF)
  2451. {
  2452. case KEY_NEXT_LEVEL:
  2453. if (pinfo->IsMP())
  2454. {
  2455. // Only the server's local user can advance to the next level, but even
  2456. // then only when the game is playing. The actual handling of this
  2457. // is done elsewhere -- we just set the flag here.
  2458. if (pinfo->IsServer() && pinfo->Client()->IsPlaying())
  2459. pinfo->m_bNextRealmMP = true;
  2460. }
  2461. else
  2462. {
  2463. // If sales demo cheat is enabled, we can go to the next level
  2464. #if defined(SALES_DEMO)
  2465. if (g_bEnableLevelAdvanceWithoutGoal)
  2466. pinfo->SetGameState_NextRealm();
  2467. #endif // SALES_DEMO
  2468. // Normally, all that happens when you press this key is that we
  2469. // set a flag that gets passed to the function that determines whether
  2470. // the end-of-level-goal has been reached, which may or may not depend
  2471. // on the player actually pressing the key.
  2472. bEndLevelKey = true;
  2473. pie->sUsed = TRUE;
  2474. }
  2475. break;
  2476. case KEY_PAUSE:
  2477. // Only allow pause if we're NOT in MP mode and we're in live mode
  2478. if (!pinfo->IsMP() && (GetInputMode() == INPUT_MODE_LIVE))
  2479. {
  2480. PauseGame(pinfo->Realm(), "Press <Pause> key to resume", KEY_PAUSE);
  2481. pie->sUsed = TRUE;
  2482. }
  2483. break;
  2484. case KEY_MENU:
  2485. // If not chatting . . .
  2486. if (pinfo->m_bChatting == false)
  2487. {
  2488. // Start the menu. I have no idea why the check to make sure the game
  2489. // is not over was necessary, but it doesn't seem like it could hurt,
  2490. // so it was safer to leave it in.
  2491. if (!pinfo->IsGameOver())
  2492. StartMenu(pinfo);
  2493. pie->sUsed = TRUE;
  2494. }
  2495. break;
  2496. case KEY_TOGGLE_TARGETING:
  2497. // Toggle game settings flag
  2498. g_GameSettings.m_sCrossHair = !g_GameSettings.m_sCrossHair;
  2499. // Toggle local dude's flag (if he exists)
  2500. if (pdudeLocal != NULL)
  2501. pdudeLocal->m_bTargetingHelpEnabled = (g_GameSettings.m_sCrossHair != FALSE) ? true : false;
  2502. pie->sUsed = TRUE;
  2503. break;
  2504. case KEY_SHOW_MISSION:
  2505. // Show the mission goal line again for about 5 seconds.
  2506. ScoreDisplayStatus(prealm);
  2507. pie->sUsed = TRUE;
  2508. break;
  2509. case KEY_ENLARGE_FILM1:
  2510. case KEY_ENLARGE_FILM2:
  2511. case KEY_ENLARGE_FILM3:
  2512. // Increase film scale
  2513. g_GameSettings.m_dGameFilmScale += FILM_INCDEC_SCALE;
  2514. pie->sUsed = TRUE;
  2515. break;
  2516. case KEY_REDUCE_FILM1:
  2517. case KEY_REDUCE_FILM2:
  2518. // Decrease film scale
  2519. g_GameSettings.m_dGameFilmScale -= FILM_INCDEC_SCALE;
  2520. pie->sUsed = TRUE;
  2521. break;
  2522. case KEY_TOGGLE_DISP_INFO:
  2523. // Toggle display info flag.
  2524. if (g_GameSettings.m_sDisplayInfo == FALSE)
  2525. g_GameSettings.m_sDisplayInfo = TRUE;
  2526. else
  2527. g_GameSettings.m_sDisplayInfo = FALSE;
  2528. pie->sUsed = TRUE;
  2529. break;
  2530. case KEY_TALK1:
  2531. case KEY_TALK2:
  2532. if (m_peditChatIn && pinfo->IsMP() && pinfo->m_bChatting == false)
  2533. {
  2534. // Activate talk mode.
  2535. pinfo->m_bChatting = true;
  2536. m_peditChatIn->SetVisible(TRUE);
  2537. pie->sUsed = TRUE;
  2538. }
  2539. break;
  2540. }
  2541. // If in talk mode . . .
  2542. if (pinfo->m_bChatting == true && m_peditChatIn && pinfo->IsMP() )
  2543. {
  2544. switch (pie->lKey)
  2545. {
  2546. case KEY_ACCEPT_CHAT:
  2547. // If anything was typed . . .
  2548. if (m_peditChatIn->m_szText[0])
  2549. {
  2550. // Send the chat text.
  2551. pinfo->Client()->SendChat(m_peditChatIn->m_szText);
  2552. }
  2553. // Intentional fall through.
  2554. case KEY_ABORT_CHAT: // Cancel jumps in here to clean up but not send anything.
  2555. // If we're at all off of the view edge . . .
  2556. if (pinfo->Camera()->m_sFilmViewX > 0)
  2557. {
  2558. // Erase now.
  2559. rspGeneralLock(g_pimScreenBuf);
  2560. rspRect(
  2561. RSP_BLACK_INDEX,
  2562. g_pimScreenBuf,
  2563. m_peditChatIn->m_sX,
  2564. m_peditChatIn->m_sY,
  2565. m_peditChatIn->m_im.m_sWidth,
  2566. m_peditChatIn->m_im.m_sHeight);
  2567. rspGeneralUnlock(g_pimScreenBuf);
  2568. // Add dirty rectangle to update erased area.
  2569. pinfo->m_drl.Add(
  2570. m_peditChatIn->m_sX,
  2571. m_peditChatIn->m_sY,
  2572. m_peditChatIn->m_im.m_sWidth,
  2573. m_peditChatIn->m_im.m_sHeight);
  2574. }
  2575. // Clear the chat text.
  2576. m_peditChatIn->SetText("");
  2577. m_peditChatIn->Compose();
  2578. m_peditChatIn->SetVisible(FALSE);
  2579. // Reset the input.
  2580. ClearLocalInput();
  2581. pinfo->m_bChatting = false;
  2582. break;
  2583. default:
  2584. m_peditChatIn->Do(pie);
  2585. break;
  2586. }
  2587. }
  2588. }
  2589. else
  2590. {
  2591. // If we're in chat mode, even if there's no input for the chat box,
  2592. // we need to call the Edit's Do() so it can flash the caret and stuff.
  2593. if (pinfo->m_bChatting == true && m_peditChatIn && pinfo->IsMP() )
  2594. {
  2595. m_peditChatIn->Do(pie);
  2596. }
  2597. }
  2598. // Note that this key's status element in the key status array could be determined once and
  2599. // stored statically, but, if we do this, the key cannot be changed during gameplay. That
  2600. // is, if the user changed the key to say Caps Lock, it would not work until the next
  2601. // run of the game.
  2602. // If xray all pressed . . .
  2603. if (m_pau8KeyStatus[KEY_XRAY_ALL])
  2604. {
  2605. // Toggle user choice for XRay all.
  2606. pinfo->m_bXRayAll = !pinfo->m_bXRayAll;
  2607. // Set new value to the scene.
  2608. prealm->m_scene.SetXRayAll( (pinfo->m_bXRayAll == true) ? TRUE : FALSE);
  2609. // Clear key's status.
  2610. m_pau8KeyStatus[KEY_XRAY_ALL] = 0;
  2611. }
  2612. // If snap picture pressed . . .
  2613. if (m_pau8KeyStatus[KEY_SNAP_PICTURE])
  2614. {
  2615. // If not chatting . . .
  2616. if (pinfo->m_bChatting == false)
  2617. {
  2618. // If snap shots are enabled, take one
  2619. if (g_GameSettings.m_sCanTakeSnapShots != 0)
  2620. Play_SnapPicture();
  2621. // Clear the key.
  2622. m_pau8KeyStatus[KEY_SNAP_PICTURE] = 0;
  2623. }
  2624. }
  2625. // If app goes to background, we put up a "pause game" message and wait for it to return
  2626. // to the foreground. In MP mode, we can't do this or all the other players will freeze!
  2627. if (rspIsBackground() && !pinfo->IsMP())
  2628. PauseGame(prealm, "Make Postal the foreground app to resume", 0);
  2629. // If "live" and not MP . . .
  2630. if ((GetInputMode() == INPUT_MODE_LIVE) && !pinfo->IsMP())
  2631. {
  2632. // If revive key pressed . . .
  2633. #ifdef MOBILE
  2634. if (continueIsRestart)
  2635. #else
  2636. if (GetInput(0) & INPUT_REVIVE)
  2637. #endif
  2638. {
  2639. #ifdef MOBILE
  2640. continueIsRestart = false; //Reset this flag for next time
  2641. #endif
  2642. bool bRestart = false;
  2643. // If there's a local dude . . .
  2644. if (pdudeLocal)
  2645. {
  2646. // If he's dead . . .
  2647. if (pdudeLocal->IsDead() == true)
  2648. {
  2649. // Restart the realm
  2650. bRestart = true;
  2651. }
  2652. }
  2653. else
  2654. {
  2655. // Restart the realm
  2656. bRestart = true;
  2657. }
  2658. if (bRestart)
  2659. {
  2660. // Restart the realm
  2661. pinfo->SetGameState_RestartRealm();
  2662. // Check the goal (since the user pressed revive, we'll
  2663. // say the 'end the level' key was pressed.
  2664. if (prealm->IsEndOfLevelGoalMet(true))
  2665. {
  2666. // The goal was met, show the dialog(s).
  2667. ScoreDisplayHighScores(prealm);
  2668. }
  2669. }
  2670. }
  2671. }
  2672. }
  2673. }
  2674. //==============================================================================
  2675. // Check if end of level goal has been met. Depending on the game type, this
  2676. // may require that the user pressed the end-of-level key.
  2677. //==============================================================================
  2678. if (pinfo->IsMP())
  2679. {
  2680. if (pinfo->IsServer() && pinfo->Client()->IsPlaying())
  2681. {
  2682. if (prealm->IsEndOfLevelGoalMet(bEndLevelKey))
  2683. pinfo->m_bNextRealmMP = true;
  2684. }
  2685. }
  2686. else
  2687. {
  2688. if (prealm->IsEndOfLevelGoalMet(bEndLevelKey))
  2689. {
  2690. // Set so we'll go to the next realm
  2691. pinfo->SetGameState_NextRealm();
  2692. #ifdef MOBILE //Disble scores for now, don't work
  2693. // Display high scoers
  2694. ScoreDisplayHighScores(prealm);
  2695. #endif
  2696. }
  2697. }
  2698. //==============================================================================
  2699. // If NOT multiplayer mode, get player's input here. Otherwise, player input
  2700. // is handled by the network stuff.
  2701. //==============================================================================
  2702. if (!pinfo->IsMP())
  2703. {
  2704. // Set controls for the one-and-only dude now (allow cheats).
  2705. SetInput(0, GetLocalInput(prealm, pie));
  2706. }
  2707. //==============================================================================
  2708. // Demo mode stuff
  2709. //==============================================================================
  2710. if (GetInputMode() != INPUT_MODE_LIVE)
  2711. {
  2712. // If the local dude dies, we wait a short period of time and then end the game.
  2713. if (pdudeLocal != NULL)
  2714. {
  2715. if (pdudeLocal->IsDead() == true)
  2716. {
  2717. // If this is the first time here, reset the timer
  2718. if (m_lDemoDeadTime < 0)
  2719. m_lDemoDeadTime = prealm->m_time.GetGameTime();
  2720. // If he's been dead long enough, end the game
  2721. if (prealm->m_time.GetGameTime() > m_lDemoDeadTime + DEMO_MAX_DEAD_TIME)
  2722. pinfo->SetGameState_GameOver();
  2723. }
  2724. }
  2725. else
  2726. {
  2727. // If the dude goes away, end the game right away
  2728. pinfo->SetGameState_GameOver();
  2729. }
  2730. // If we're in Playback mode . . .
  2731. if (GetInputMode() == INPUT_MODE_PLAYBACK)
  2732. {
  2733. // If the input is done . . .
  2734. if (InputIsDemoOver() == true)
  2735. {
  2736. // Game be done now.
  2737. pinfo->SetGameState_GameOver();
  2738. }
  2739. }
  2740. // Govern the speed of the loop
  2741. while (prealm->m_time.GetRealTime() - prealm->m_time.GetGameTime() < 0)
  2742. ;
  2743. }
  2744. }
  2745. }
  2746. }
  2747. ////////////////////////////////////////////////////////////////////////////////
  2748. // Unprepare game
  2749. ////////////////////////////////////////////////////////////////////////////////
  2750. virtual
  2751. void UnprepareGame(
  2752. CPlayInfo* pinfo) // I/O: Play info
  2753. {
  2754. // If we're still on the menu, end it now
  2755. if (pinfo->m_bInMenu)
  2756. StopMenu(pinfo);
  2757. }
  2758. ////////////////////////////////////////////////////////////////////////////////
  2759. // Core loop render -- create and update images to the composite buffer but do
  2760. // NOT update the screen.
  2761. ////////////////////////////////////////////////////////////////////////////////
  2762. /* virtual */
  2763. void CoreLoopRenderOnTop(
  2764. CPlayInfo* pinfo) // I/O: Play info
  2765. {
  2766. if (!pinfo->m_bBadRealmMP)
  2767. {
  2768. // Only if we're not on the menu
  2769. if (!pinfo->m_bInMenu)
  2770. {
  2771. // If in chat input mode . . .
  2772. if (pinfo->m_bChatting && m_peditChatIn)
  2773. {
  2774. m_peditChatIn->Draw(g_pimScreenBuf);
  2775. // Add dirty rectangle.
  2776. pinfo->m_drl.Add(
  2777. m_peditChatIn->m_sX,
  2778. m_peditChatIn->m_sY,
  2779. m_peditChatIn->m_im.m_sWidth,
  2780. m_peditChatIn->m_im.m_sHeight);
  2781. }
  2782. }
  2783. }
  2784. }
  2785. ////////////////////////////////////////////////////////////////////////////////
  2786. //
  2787. // Pause the specified realm, fade colors to red, display specified message,
  2788. // and wait for specified key or focus.
  2789. // When the key is hit, or foreground status is regained, the color are faded
  2790. // back from red, the realm is resumed, and the function returns.
  2791. //
  2792. ////////////////////////////////////////////////////////////////////////////////
  2793. void PauseGame(
  2794. CRealm* prealm, // In: Realm to pause or NULL.
  2795. char* pszMsg, // In: Message to be displayed.
  2796. long lKey) // In: Key to continue or 0 to wait for foreground status
  2797. {
  2798. // Suspend realm.
  2799. if (prealm)
  2800. {
  2801. prealm->Suspend();
  2802. }
  2803. // Fade colors to red.
  2804. PalTranOn();
  2805. // Create colors.
  2806. // 'PAUSED' fore color.
  2807. rspSetPaletteEntry(
  2808. PAUSED_BASE_PAL_INDEX + 0, // Palette entry (0 to 255)
  2809. PAUSED_FONT_COLOR_R, // Red component (0 to 255)
  2810. PAUSED_FONT_COLOR_G, // Green component (0 to 255)
  2811. PAUSED_FONT_COLOR_B); // Blue component (0 to 255)
  2812. // 'PAUSED' shadow color.
  2813. rspSetPaletteEntry(
  2814. PAUSED_BASE_PAL_INDEX + 1, // Palette entry (0 to 255)
  2815. PAUSED_FONT_SHADOW_COLOR_R, // Red component (0 to 255)
  2816. PAUSED_FONT_SHADOW_COLOR_G, // Green component (0 to 255)
  2817. PAUSED_FONT_SHADOW_COLOR_B); // Blue component (0 to 255)
  2818. // Message fore color.
  2819. rspSetPaletteEntry(
  2820. PAUSED_BASE_PAL_INDEX + 2, // Palette entry (0 to 255)
  2821. PAUSED_MSG_FONT_COLOR_R, // Red component (0 to 255)
  2822. PAUSED_MSG_FONT_COLOR_G, // Green component (0 to 255)
  2823. PAUSED_MSG_FONT_COLOR_B); // Blue component (0 to 255)
  2824. // Message shadow color.
  2825. rspSetPaletteEntry(
  2826. PAUSED_BASE_PAL_INDEX + 3, // Palette entry (0 to 255)
  2827. PAUSED_MSG_FONT_SHADOW_COLOR_R, // Red component (0 to 255)
  2828. PAUSED_MSG_FONT_SHADOW_COLOR_G, // Green component (0 to 255)
  2829. PAUSED_MSG_FONT_SHADOW_COLOR_B); // Blue component (0 to 255)
  2830. // Update hardware palette.
  2831. rspUpdatePalette();
  2832. // Lock the buffer before drawing to it.
  2833. rspLockBuffer();
  2834. // Draw text.
  2835. RPrint print;
  2836. print.SetFont(PAUSED_FONT_HEIGHT, &g_fontPostal);
  2837. print.SetEffectAbs(RPrint::SHADOW_X, PAUSED_FONT_SHADOW_X);
  2838. print.SetEffectAbs(RPrint::SHADOW_Y, PAUSED_FONT_SHADOW_Y);
  2839. print.SetColor(
  2840. PAUSED_BASE_PAL_INDEX + 0,
  2841. 0,
  2842. PAUSED_BASE_PAL_INDEX + 1);
  2843. print.SetDestination(g_pimScreenBuf);
  2844. print.SetJustifyCenter();
  2845. short sTotalH = PAUSED_FONT_HEIGHT + PAUSED_FONT_SHADOW_Y;
  2846. if (pszMsg)
  2847. {
  2848. // Include message height as well.
  2849. sTotalH += PAUSED_MSG_FONT_HEIGHT + PAUSED_MSG_FONT_SHADOW_Y;
  2850. }
  2851. short sPosY = g_pimScreenBuf->m_sHeight / 2 - sTotalH; // / 2;
  2852. print.print(
  2853. 0,
  2854. sPosY,
  2855. "PAUSED");
  2856. if (pszMsg)
  2857. {
  2858. sPosY += PAUSED_FONT_HEIGHT + PAUSED_FONT_SHADOW_Y;
  2859. print.SetFont(PAUSED_MSG_FONT_HEIGHT);
  2860. print.SetEffectAbs(RPrint::SHADOW_X, PAUSED_MSG_FONT_SHADOW_X);
  2861. print.SetEffectAbs(RPrint::SHADOW_Y, PAUSED_MSG_FONT_SHADOW_Y);
  2862. print.SetColor(
  2863. PAUSED_BASE_PAL_INDEX + 2,
  2864. 0,
  2865. PAUSED_BASE_PAL_INDEX + 3);
  2866. print.print(
  2867. 0,
  2868. sPosY,
  2869. "%s",
  2870. pszMsg);
  2871. }
  2872. // Unlock the buffer now that we're done drawing to it.
  2873. rspUnlockBuffer();
  2874. // Update the screen with the text.
  2875. rspUpdateDisplay();
  2876. // Loop until signaled to continue.
  2877. bool bResume = false;
  2878. RInputEvent ie;
  2879. while (bResume == false)
  2880. {
  2881. UpdateSystem();
  2882. if (lKey)
  2883. {
  2884. ie.type = RInputEvent::None;
  2885. rspGetNextInputEvent(&ie);
  2886. if (ie.type == RInputEvent::Key)
  2887. {
  2888. if ((ie.lKey & 0x0000FFFF) == lKey)
  2889. {
  2890. bResume = true;
  2891. }
  2892. }
  2893. }
  2894. else
  2895. {
  2896. if (rspIsBackground() == FALSE)
  2897. {
  2898. bResume = true;
  2899. }
  2900. }
  2901. if (rspGetQuitStatus() )
  2902. {
  2903. bResume = true;
  2904. }
  2905. }
  2906. // Fade colors back from red.
  2907. PalTranOff();
  2908. // Resum realm.
  2909. if (prealm)
  2910. {
  2911. prealm->Resume();
  2912. }
  2913. // Clear queues.
  2914. rspClearAllInputEvents();
  2915. // Re-init input.
  2916. ClearLocalInput();
  2917. }
  2918. ////////////////////////////////////////////////////////////////////////////////
  2919. //
  2920. // Start the menu
  2921. //
  2922. ////////////////////////////////////////////////////////////////////////////////
  2923. void StartMenu(
  2924. CPlayInfo* pinfo) // I/O: Play info
  2925. {
  2926. // If not in multiplayer mode, suspend the realm while on the menu
  2927. if (!pinfo->IsMP())
  2928. pinfo->Realm()->Suspend();
  2929. // Fade out colors -- for MP do it fast to avoid hanging the game up
  2930. if (pinfo->IsMP())
  2931. PalTranOn(0);
  2932. else
  2933. PalTranOn();
  2934. // Clear input events.
  2935. rspClearAllInputEvents();
  2936. #ifdef MOBILE
  2937. if (pinfo->LocalDudePointer()->IsDead()) //Only enable RETRY if player is dead
  2938. menuClientGame.ami[0].sEnabled = TRUE;
  2939. else
  2940. menuClientGame.ami[0].sEnabled = FALSE;
  2941. #endif
  2942. // Disable 'Play Options' on 'Options' menu.
  2943. menuOptions.ami[5].sEnabled = FALSE;
  2944. // Disable 'Organ' on 'Audio Options' menu.
  2945. menuAudioOptions.ami[1].sEnabled = FALSE;
  2946. // Disable 'Save' IF in multiplayer.
  2947. menuClientGame.ami[1].sEnabled = (pinfo->IsMP() == true) ? FALSE : TRUE;
  2948. // Start the menu
  2949. if (::StartMenu(&menuClientGame, &g_resmgrShell, g_pimScreenBuf) == 0)
  2950. {
  2951. // Disable autopump.
  2952. RMix::SetAutoPump(FALSE);
  2953. // Disable Camera's screen access by making the view really friggin small.
  2954. pinfo->Camera()->SetViewSize(0, 0);
  2955. // Set flag to indicate we're in the menu
  2956. pinfo->m_bInMenu = true;
  2957. #ifdef MOBILE
  2958. AndroidSetScreenMode(TOUCH_SCREEN_MENU);
  2959. #endif
  2960. }
  2961. else
  2962. {
  2963. // Clean up
  2964. StopMenu(pinfo);
  2965. }
  2966. }
  2967. ////////////////////////////////////////////////////////////////////////////////
  2968. //
  2969. // Do one iteration of the menu (must have called StartMenu() previously!!!)
  2970. //
  2971. ////////////////////////////////////////////////////////////////////////////////
  2972. void DoMenu(
  2973. CPlayInfo* pinfo, // I/O: Play info
  2974. RInputEvent* pie) // I/O: Input event
  2975. {
  2976. // Make sure StartMenu() was called
  2977. ASSERT(pinfo->m_bInMenu);
  2978. // Run the menu
  2979. ms_menuaction = MenuActionNone;
  2980. DoMenuInput(pie, g_InputSettings.m_sUseJoy);
  2981. switch (ms_menuaction)
  2982. {
  2983. // Nothing in particular.
  2984. case MenuActionNone:
  2985. break;
  2986. // User quit choice.
  2987. case MenuActionQuit:
  2988. StopMenu(pinfo);
  2989. // User hit "Quit" in the menu; end the game.
  2990. if (pinfo->IsMP())
  2991. pinfo->m_bUserQuitMP = true;
  2992. else
  2993. pinfo->SetGameState_GameAborted();
  2994. break;
  2995. // User save choice.
  2996. case MenuActionSaveGame:
  2997. {
  2998. short sResult;
  2999. // Static so dialog will "remember" the previously-used name
  3000. static char szFile[RSP_MAX_PATH] = "";
  3001. // If not yet used, start out in appropriate directory
  3002. if (szFile[0] == '\0')
  3003. strcpy(szFile, FullPathHD(SAVEGAME_DIR));
  3004. // Display open dialog to let user choose a file
  3005. #if 1 //PLATFORM_UNIX
  3006. if (PickFile("Choose Game Slot", EnumSaveGamesSlots, szFile, sizeof(szFile)) == 0)
  3007. {
  3008. #ifdef MOBILE
  3009. //Android we have the format "1 - date"
  3010. //Need to create the filename
  3011. char number = szFile[0];
  3012. snprintf(szFile, sizeof (szFile), "%s/%c.gme", SAVEGAME_DIR,number);
  3013. #else
  3014. char *ptr = strrchr(szFile, '[');
  3015. if (ptr) *(ptr-1) = '\0';
  3016. #endif
  3017. // This function will open the saved game file and set the correct game mode
  3018. // and settings. Note that this modifies the m_action (that's how we get
  3019. // out this state...this confused me for a while but it seems like a good
  3020. // way to choose the appropriate original action).
  3021. if (Game_SavePlayersGame(szFile, pinfo->Realm()->m_flags.sDifficulty) == SUCCESS)
  3022. {
  3023. #if WITH_STEAMWORKS
  3024. if ((EnableSteamCloud) && (strncmp(szFile, "steamcloud/", 11) == 0))
  3025. {
  3026. char fname[64];
  3027. snprintf(fname, sizeof (fname), "savegame_%s", szFile + 11);
  3028. ISteamRemoteStorage *cloud = SteamRemoteStorage();
  3029. if (cloud)
  3030. {
  3031. FILE *io = fopen(FindCorrectFile(szFile, "rb"), "rb");
  3032. if (io)
  3033. {
  3034. char buf[1024];
  3035. const size_t br = fread(buf, 1, sizeof (buf), io);
  3036. fclose(io);
  3037. if (br > 0)
  3038. cloud->FileWrite(fname, buf, (int32) br);
  3039. }
  3040. }
  3041. }
  3042. #endif
  3043. }
  3044. }
  3045. #else
  3046. #if WITH_STEAMWORKS
  3047. #error You need to switch over from this code to the in-game file UI first.
  3048. #endif
  3049. sResult = rspSaveBox(g_pszSaveGameTitle, szFile, szFile, sizeof(szFile), SAVEGAME_EXT);
  3050. if (sResult == 0)
  3051. {
  3052. if (Game_SavePlayersGame(szFile, pinfo->Realm()->m_flags.sDifficulty) != SUCCESS)
  3053. {
  3054. rspMsgBox(RSP_MB_ICN_EXCLAIM | RSP_MB_BUT_OK, g_pszSaveGameErrorTitle,
  3055. g_pszSaveGameErrorText);
  3056. }
  3057. }
  3058. #endif
  3059. break;
  3060. }
  3061. case MenuActionEndMenu:
  3062. StopMenu(pinfo);
  3063. break;
  3064. default:
  3065. TRACE("RespondToMenuRequest(): Unhandled action %d.\n", ms_menuaction);
  3066. break;
  3067. }
  3068. DoMenuOutput(pinfo->Camera()->m_pimFilm);
  3069. ms_menuaction = MenuActionNone;
  3070. // This is CHEEZY AS HELL but the normal menu callback calls
  3071. // game.cpp which sets its action flag telling it to call this
  3072. // function. Not sure how to do it here. Will we need to call
  3073. // game.cpp, play.cpp, and gameedit.cpp whenever this menu is
  3074. // activated?
  3075. Menu* pmenu = GetCurrentMenu();
  3076. if (pmenu == &menuJoystick || pmenu == &menuMouse || pmenu == &menuKeyboard)
  3077. {
  3078. // Do the input settings.
  3079. EditInputSettings();
  3080. }
  3081. rspUpdateDisplay();
  3082. }
  3083. ////////////////////////////////////////////////////////////////////////////////
  3084. //
  3085. // End the menu
  3086. //
  3087. ////////////////////////////////////////////////////////////////////////////////
  3088. void StopMenu(
  3089. CPlayInfo* pinfo) // I/O: Play info
  3090. {
  3091. // End the menu
  3092. ::StopMenu();
  3093. // Update the display reflecting the erasure of the menu.
  3094. rspUpdateDisplay();
  3095. // Re-enable autopump.
  3096. RMix::SetAutoPump(TRUE);
  3097. // Set the local dude's color in case the user changed it on the menu. In MP
  3098. // mode we have to ignore this because we currently don't support the messages that
  3099. // would be necessary to tell all the other players about the color change.
  3100. if (!pinfo->IsMP())
  3101. {
  3102. CDude* pdudeLocal = pinfo->LocalDudePointer();
  3103. if (pdudeLocal)
  3104. pdudeLocal->m_sTextureIndex = MAX((short)0, MIN((short)(CDude::MaxTextures - 1), g_GameSettings.m_sPlayerColorIndex));
  3105. }
  3106. // Re-enable 'Play Options' on 'Options' menu.
  3107. menuOptions.ami[5].sEnabled = TRUE;
  3108. // Re-enable 'Organ' on 'Audio Options' menu.
  3109. menuAudioOptions.ami[1].sEnabled = TRUE;
  3110. // Fade colors back in
  3111. PalTranOff();
  3112. // Clear queues.
  3113. rspClearAllInputEvents();
  3114. // Clear the local input.
  3115. ClearLocalInput();
  3116. // If not in multiplayer mode, resume the realm
  3117. if (!pinfo->IsMP())
  3118. pinfo->Realm()->Resume();
  3119. // Restore camera's screen access.
  3120. pinfo->Camera()->SetViewSize(
  3121. VIEW_W * g_GameSettings.m_dGameFilmScale,
  3122. VIEW_H * g_GameSettings.m_dGameFilmScale);
  3123. // Clear flag
  3124. pinfo->m_bInMenu = false;
  3125. #ifdef MOBILE
  3126. AndroidSetScreenMode(TOUCH_SCREEN_GAME);
  3127. #endif
  3128. }
  3129. };
  3130. ////////////////////////////////////////////////////////////////////////////////
  3131. //
  3132. // Realm API
  3133. //
  3134. ////////////////////////////////////////////////////////////////////////////////
  3135. class CPlayRealm : public CPlay
  3136. {
  3137. //------------------------------------------------------------------------------
  3138. // Types, enums, etc.
  3139. //------------------------------------------------------------------------------
  3140. private:
  3141. typedef struct
  3142. {
  3143. CStockPile stockpile;
  3144. CDude::WeaponType weapon;
  3145. } LevelPersist;
  3146. //------------------------------------------------------------------------------
  3147. // Variables
  3148. //------------------------------------------------------------------------------
  3149. private:
  3150. LevelPersist m_alevelpersist[Net::MaxNumIDs]; // Index by CDude::m_sDudeNum.
  3151. bool m_bMakeDemoMovie_WaitForClick; // Flag used when making demo movies
  3152. double m_dCurrentFilmScale;
  3153. short m_sCurrentGripZoneRadius;
  3154. long m_lNumSeqSkippedFrames;
  3155. //------------------------------------------------------------------------------
  3156. // Functions
  3157. //------------------------------------------------------------------------------
  3158. public:
  3159. ////////////////////////////////////////////////////////////////////////////////
  3160. // Constructor
  3161. ////////////////////////////////////////////////////////////////////////////////
  3162. CPlayRealm(void)
  3163. {
  3164. }
  3165. ////////////////////////////////////////////////////////////////////////////////
  3166. // Destructor
  3167. ////////////////////////////////////////////////////////////////////////////////
  3168. /* virtual */
  3169. ~CPlayRealm()
  3170. {
  3171. }
  3172. ////////////////////////////////////////////////////////////////////////////////
  3173. // Prepare game
  3174. ////////////////////////////////////////////////////////////////////////////////
  3175. /* virtual */
  3176. short PrepareGame( // Returns 0 if successfull, non-zero otherwise
  3177. CPlayInfo* pinfo) // I/O: Play info
  3178. {
  3179. // Note whether multiplayer.
  3180. pinfo->Realm()->m_flags.bMultiplayer = pinfo->IsMP();
  3181. // Array of LevelPersist to carry players' ammo, health, kevlar, current
  3182. // weapon, etc. from level to level. Using CDudes in this manner was
  3183. // another idea, but when I tried carrying them from level to level, many,
  3184. // many possibilities for error were revealed. For example, various realm
  3185. // dependent things in the CDude and his base classes are cleaned up by
  3186. // those respective classes in the destructor (e.g., his smash and his
  3187. // sprite and, in some cases, a child object).
  3188. // Initialize to appropriate values.
  3189. short sDudeIndex;
  3190. for (sDudeIndex = 0; sDudeIndex < Net::MaxNumIDs; sDudeIndex++)
  3191. {
  3192. // Clear stockpile.
  3193. m_alevelpersist[sDudeIndex].stockpile.Zero();
  3194. // Make machine gun the default weapon.
  3195. m_alevelpersist[sDudeIndex].weapon = CDude::SemiAutomatic;
  3196. }
  3197. // Debug demo mode stuff (always active -- takes essentially no time unless enabled from game.cpp)
  3198. m_bMakeDemoMovie_WaitForClick = true;
  3199. return 0;
  3200. }
  3201. ////////////////////////////////////////////////////////////////////////////////
  3202. // Prepare realm
  3203. ////////////////////////////////////////////////////////////////////////////////
  3204. /* virtual */
  3205. short PrepareRealm( // Returns 0 if successfull, non-zero otherwise
  3206. CPlayInfo* pinfo) // I/O: Play info
  3207. {
  3208. short sResult = 0;
  3209. CRealm* prealm = pinfo->Realm();
  3210. // Clear realm (in case there's any crap left over from last realm)
  3211. prealm->Clear();
  3212. // If we're supposed to purge the SAKs . . .
  3213. if (pinfo->PurgeSaks() == true)
  3214. {
  3215. // We must do this after the Clear() to make sure all the objects
  3216. // have been deleted so that they'll release their resources.
  3217. // Also, we must, of course, do this before the Load().
  3218. g_resmgrGame.Purge();
  3219. g_resmgrSamples.Purge();
  3220. g_resmgrRes.Purge();
  3221. g_resmgrShell.Purge();
  3222. // Clear the flag.
  3223. pinfo->ClearPurgeSaks();
  3224. }
  3225. // Reset time here so that objects can use it when they are loaded
  3226. prealm->m_time.Reset();
  3227. // If there's already a realm error, then don't bother with this
  3228. if (!pinfo->m_bBadRealmMP)
  3229. {
  3230. // Check if specified file exists
  3231. if (prealm->DoesFileExist((char*)pinfo->RealmName()))
  3232. {
  3233. // Load realm (false indicates NOT edit mode)
  3234. if (prealm->Load((char*)pinfo->RealmName(), false) == 0)
  3235. {
  3236. // Startup the realm
  3237. if (prealm->Startup() == 0)
  3238. {
  3239. //==============================================================================
  3240. // Set up dudes. This can take a while (dude's need lots of resources) so it
  3241. // should be done before the cutscene stuff.
  3242. //==============================================================================
  3243. // If this game was loaded from a saved game file, then copy the
  3244. // global stockpile into the level persistent data array.
  3245. if (g_bTransferStockpile)
  3246. m_alevelpersist[0].stockpile.Copy(&g_stockpile);
  3247. // Otherwise, keep the global savable stockpile up to date.
  3248. else
  3249. g_stockpile.Copy(&m_alevelpersist[0].stockpile);
  3250. g_bTransferStockpile = false;
  3251. // Set up as many dudes as needed and get pointer to local dude
  3252. sResult = SetupDudes(pinfo, m_alevelpersist);
  3253. }
  3254. else
  3255. {
  3256. sResult = -1;
  3257. TRACE("CPlayRealm::PrepareRealm(): Error starting-up realm!\n");
  3258. }
  3259. }
  3260. else
  3261. {
  3262. sResult = -1;
  3263. TRACE("CPlayRealm::PrepareRealm(): Error loading realm!\n");
  3264. }
  3265. }
  3266. else
  3267. {
  3268. sResult = -1;
  3269. TRACE("CPlayRealm::PrepareRealm(): File does not exist: %s\n", (char*)pinfo->RealmName());
  3270. // If we're in the specific realm mode, then display a message telling the user that
  3271. // this version only handles one specific realm. Otherwise, this shouldn't happen
  3272. // to a user of a normal version, so we don't say anything.
  3273. #if defined(ENABLE_PLAY_SPECIFIC_REALMS_ONLY)
  3274. // MP is a special case that is handled below
  3275. if (!pinfo->IsMP())
  3276. {
  3277. rspMsgBox(
  3278. RSP_MB_ICN_INFO | RSP_MB_BUT_OK,
  3279. g_pszAppName,
  3280. g_pszPlayOneRealmOnlyMessage);
  3281. }
  3282. #endif // ENABLE_PLAY_SPECIFIC_REALMS_ONLY
  3283. }
  3284. // If there was an error, and this is an MP game, then we ignore the error for now,
  3285. // and instead we set a flag saying the realm is bad. This is done so we can handle
  3286. // the error as part of the core loop, which is where similar errors are already handled.
  3287. if ((sResult != 0) && pinfo->IsMP())
  3288. {
  3289. sResult = 0;
  3290. pinfo->m_bBadRealmMP = true;
  3291. }
  3292. }
  3293. return sResult;
  3294. }
  3295. ////////////////////////////////////////////////////////////////////////////////
  3296. // Start realm
  3297. ////////////////////////////////////////////////////////////////////////////////
  3298. /* virtual */
  3299. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  3300. CPlayInfo* pinfo) // I/O: Play info
  3301. {
  3302. if (!pinfo->m_bBadRealmMP)
  3303. {
  3304. // Get realm
  3305. CRealm* prealm = pinfo->Realm();
  3306. // Setup camera
  3307. pinfo->Camera()->SetScene(&(prealm->m_scene));
  3308. pinfo->Camera()->SetHood((CHood*)(prealm->m_aclassHeads[CThing::CHoodID].GetNext() ) );
  3309. pinfo->Camera()->SetView(VIEW_X, VIEW_Y, VIEW_W, VIEW_H);
  3310. // Set grip to control camera.
  3311. pinfo->Grip()->SetCamera(pinfo->Camera());
  3312. // Set hood's palette.
  3313. prealm->m_phood->SetPalette();
  3314. // Setup initial film scaling
  3315. ScaleFilm(pinfo, false);
  3316. // Reset time so that the first time update doesn't show (much) elapsed time.
  3317. prealm->m_time.Reset();
  3318. // Reset
  3319. m_lNumSeqSkippedFrames = 0;
  3320. }
  3321. return 0;
  3322. }
  3323. ////////////////////////////////////////////////////////////////////////////////
  3324. // Core loop render -- create and update images to the composite buffer but do
  3325. // NOT update the screen.
  3326. ////////////////////////////////////////////////////////////////////////////////
  3327. /* virtual */
  3328. void CoreLoopRender(
  3329. CPlayInfo* pinfo) // I/O: Play info
  3330. {
  3331. if (!pinfo->m_bBadRealmMP)
  3332. {
  3333. // If scale or radius have changed from our current values, then we need
  3334. // to redo everything now.
  3335. if ((m_dCurrentFilmScale != g_GameSettings.m_dGameFilmScale) ||
  3336. (m_sCurrentGripZoneRadius != g_GameSettings.m_sGripZoneRadius))
  3337. {
  3338. ScaleFilm(pinfo);
  3339. }
  3340. // Figure out whether to do a frame or not
  3341. bool bDoFrame = true;
  3342. if (pinfo->IsMP())
  3343. {
  3344. // In MP mode, we simply follow the special MP flag. We don't care about
  3345. // the menu, because we must let the game continue to run so we stay in
  3346. // sync with everyone else.
  3347. bDoFrame = pinfo->m_bDoRealmFrame;
  3348. }
  3349. else
  3350. {
  3351. // If we're in the menu, then DON'T do the frame
  3352. if (pinfo->m_bInMenu)
  3353. bDoFrame = false;
  3354. }
  3355. if (bDoFrame)
  3356. {
  3357. // Get realm pointer
  3358. CRealm* prealm = pinfo->Realm();
  3359. // Adjust realm time. How we do it depends on the mode we're in.
  3360. if (GetInputMode() == INPUT_MODE_LIVE)
  3361. {
  3362. if (pinfo->IsMP())
  3363. {
  3364. // In multiplayer mode, time moves in hardwired increments
  3365. prealm->m_time.Update(pinfo->FrameTime());
  3366. }
  3367. else
  3368. {
  3369. // In non-network mode, time flows freely
  3370. prealm->m_time.Update();
  3371. }
  3372. }
  3373. else
  3374. {
  3375. // In demo mode, time moves in hardwired increments
  3376. prealm->m_time.Update(DEMO_TIME_PER_FRAME);
  3377. }
  3378. // Update Realm
  3379. prealm->Update();
  3380. // Prepare Realm for rendering (Snap()).
  3381. prealm->Render();
  3382. // In demo mode (record or playback) we don't draw the results of the frame if
  3383. // we're falling behind. However, we always draw when doing a demo-mode-movie .
  3384. pinfo->m_bDrawFrame = true;
  3385. if ((pinfo->DemoModeDebugMovie() == 0) && (GetInputMode() != INPUT_MODE_LIVE))
  3386. {
  3387. // If we've fallen behind the demo frame rate by our max lag . . .
  3388. if (prealm->m_time.GetRealTime() - prealm->m_time.GetGameTime() > DEMO_MAX_LAG)
  3389. {
  3390. // If we haven't already skipped too many frames . . .
  3391. if (m_lNumSeqSkippedFrames < DEMO_MAX_SEQUENTIAL_SKIPPED_FRAMES)
  3392. {
  3393. m_lNumSeqSkippedFrames++;
  3394. pinfo->m_bDrawFrame = false;
  3395. }
  3396. else
  3397. m_lNumSeqSkippedFrames = 0;
  3398. }
  3399. else
  3400. m_lNumSeqSkippedFrames = 0;
  3401. }
  3402. // Track the local dude with the grip/camera and adjust the sound, too
  3403. CDude* pdudeLocal = pinfo->LocalDudePointer();
  3404. if (pdudeLocal != NULL)
  3405. {
  3406. // Update grip/camera
  3407. short sX, sY;
  3408. prealm->Map3Dto2D(pdudeLocal->GetX(), pdudeLocal->GetY(), pdudeLocal->GetZ(), &sX, &sY);
  3409. pinfo->Grip()->TrackTarget(sX, sY, 30);
  3410. // Set coordinates for the "ear"
  3411. SetSoundLocation(pdudeLocal->GetX(), pdudeLocal->GetY(), pdudeLocal->GetZ());
  3412. }
  3413. // Snap picture of scene. Even if we DON'T want to draw this frame, we still
  3414. // have to allow a certain amount of work to get done (we still need things like
  3415. // collision areas to be updated via the 3D scene rendered). The scene flag tells
  3416. // the scene whether or not to do BLiT's (and anything else that's purely cosmetic.)
  3417. g_bSceneDontBlit = !pinfo->m_bDrawFrame;
  3418. pinfo->Camera()->Snap();
  3419. g_bSceneDontBlit = false;
  3420. // If in MP mode, clear the flag
  3421. if (pinfo->IsMP())
  3422. pinfo->m_bDoRealmFrame = false;
  3423. }
  3424. else
  3425. {
  3426. // 11/18/97 JMI This didn't seem to get cleared in the case bDoFrame
  3427. // is false but I didn't see why we'd need to update the
  3428. // screen in this case (perhaps this is part of our net
  3429. // slow down?).
  3430. pinfo->m_bDrawFrame = false;
  3431. }
  3432. // If not in menu . . .
  3433. if (pinfo->m_bInMenu == false)
  3434. {
  3435. // If we should draw the frame . . .
  3436. if (pinfo->m_bDrawFrame)
  3437. {
  3438. CCamera* pcamera = pinfo->Camera();
  3439. pinfo->m_drl.Add(
  3440. pcamera->m_sFilmViewX,
  3441. pcamera->m_sFilmViewY,
  3442. pcamera->m_sViewW,
  3443. pcamera->m_sViewH);
  3444. }
  3445. }
  3446. }
  3447. }
  3448. ////////////////////////////////////////////////////////////////////////////////
  3449. // Core loop draw -- Draw CoreLoopRender() results to the screen.
  3450. ////////////////////////////////////////////////////////////////////////////////
  3451. virtual
  3452. void CoreLoopDraw(
  3453. CPlayInfo* pinfo) // I/O: Play info
  3454. {
  3455. // Only if we're not on the menu and not a bad realm
  3456. if (!pinfo->m_bInMenu && !pinfo->m_bBadRealmMP)
  3457. {
  3458. // If we did draw the frame, we need to copy the results to the screen
  3459. if (pinfo->m_bDrawFrame)
  3460. {
  3461. // Special demo-mode debug stuff
  3462. if (pinfo->DemoModeDebugMovie() && (GetInputMode() != INPUT_MODE_LIVE))
  3463. MakeDemoMovie(pinfo->DemoModeDebugMovie());
  3464. }
  3465. }
  3466. }
  3467. ////////////////////////////////////////////////////////////////////////////////
  3468. // End realm
  3469. ////////////////////////////////////////////////////////////////////////////////
  3470. /* virtual */
  3471. void EndRealm(
  3472. CPlayInfo* pinfo) // I/O: Play info
  3473. {
  3474. if (!pinfo->m_bBadRealmMP)
  3475. {
  3476. CRealm* prealm = pinfo->Realm();
  3477. // If we're not simply restarting the level . . .
  3478. if (pinfo->IsRestartingRealm() == false)
  3479. {
  3480. // Update players' stockpiles.
  3481. CListNode<CThing>* plnDude = prealm->m_aclassHeads[CThing::CDudeID].m_pnNext;
  3482. CListNode<CThing>* plnDudeTail = &(prealm->m_aclassTails[CThing::CDudeID]);
  3483. while (plnDude != plnDudeTail)
  3484. {
  3485. CDude* pdude = (CDude*)plnDude->m_powner;
  3486. m_alevelpersist[pdude->m_sDudeNum].stockpile.Copy( &(pdude->m_stockpile) );
  3487. m_alevelpersist[pdude->m_sDudeNum].weapon = pdude->GetCurrentWeapon();
  3488. plnDude = plnDude->m_pnNext;
  3489. }
  3490. }
  3491. // Shutdown realm
  3492. prealm->Shutdown();
  3493. }
  3494. }
  3495. private:
  3496. ////////////////////////////////////////////////////////////////////////////////
  3497. // Setup local dude
  3498. ////////////////////////////////////////////////////////////////////////////////
  3499. void SetupLocalDude(
  3500. CPlayInfo* pinfo, // I/O: Play info
  3501. CDude* pdude) // In: Dude to setup
  3502. {
  3503. // Get local dude's ID
  3504. pinfo->m_idLocalDude = pdude->GetInstanceID();
  3505. // Turn on local dude's xray effect
  3506. pdude->m_sprite.m_sInFlags |= CSprite::InXrayee;
  3507. // Set local dude's targeting status
  3508. pdude->m_bTargetingHelpEnabled = (g_GameSettings.m_sCrossHair != FALSE) ? true : false;
  3509. }
  3510. ////////////////////////////////////////////////////////////////////////////////
  3511. // Setup general dude
  3512. ////////////////////////////////////////////////////////////////////////////////
  3513. void SetupGeneralDude(
  3514. CDude* pdude, // In: Dude to setup
  3515. short sColor, // In: Player's color
  3516. LevelPersist* palevelpersist) // In: Players' level persistent data.
  3517. {
  3518. // Union player's pre-existing stockpile with warped-in dude and give him his prior weapon
  3519. ASSERT(pdude != NULL);
  3520. pdude->m_stockpile.Union(&(palevelpersist[pdude->m_sDudeNum].stockpile));
  3521. pdude->SetWeapon(palevelpersist[pdude->m_sDudeNum].weapon, true);
  3522. // Set dude's color
  3523. pdude->m_sTextureIndex = sColor;
  3524. if (pdude->m_sTextureIndex < 0)
  3525. pdude->m_sTextureIndex = 0;
  3526. if (pdude->m_sTextureIndex >= CDude::MaxTextures)
  3527. pdude->m_sTextureIndex = CDude::MaxTextures - 1;
  3528. }
  3529. ////////////////////////////////////////////////////////////////////////////////
  3530. //
  3531. // Setup dudes in this realm based on the specified parameters.
  3532. //
  3533. // This function is designed to work properly with (1) levels that have CDude's
  3534. // but no CWarps, (2) levels that have CWarp's but no CDude's, and (3) levels
  3535. // that have a combination of CWarp's and CDude's.
  3536. //
  3537. // If this function completes successfully, the specified number of CDude's
  3538. // will exist (no more, no less).
  3539. //
  3540. ////////////////////////////////////////////////////////////////////////////////
  3541. short SetupDudes(
  3542. CPlayInfo* pinfo, // I/O: Play info
  3543. LevelPersist* palevelpersist) // In: Players' level persistent data.
  3544. {
  3545. short sResult = 0;
  3546. CRealm* prealm = pinfo->Realm();
  3547. // Always default to nil for safety!
  3548. pinfo->m_idLocalDude = CIdBank::IdNil;
  3549. //------------------------------------------------------------------------------
  3550. // This is for backwards compatibility with VERY OLD realms that used CDude's
  3551. // to determine where dude's started out and what they got. This is completely
  3552. // obsolete, since we now use CWarp's instead.
  3553. //
  3554. // If there are no CDude's, then we don't do anything. If there are, we
  3555. // convert them into CWarp's. The CWarp's get their settings from the first
  3556. // CDude we find, and all subsequent CWarp's use those same settings.
  3557. //
  3558. // After this point, there will be NO DUDE'S, either because there weren't any
  3559. // to start with or because we converted them into warps.
  3560. //------------------------------------------------------------------------------
  3561. CListNode<CThing>* pln = prealm->m_aclassHeads[CThing::CDudeID].m_pnNext;
  3562. CListNode<CThing>* plnTail = &(prealm->m_aclassTails[CThing::CDudeID]);
  3563. CDude* pdude;
  3564. CWarp* pwarp;
  3565. bool bFirst = true;
  3566. while (pln != plnTail)
  3567. {
  3568. CListNode<CThing>* plnNext = pln->m_pnNext;
  3569. pdude = (CDude*)pln->m_powner;
  3570. if (CWarp::CreateWarpFromDude(prealm, pdude, &pwarp, bFirst) == 0)
  3571. bFirst = false;
  3572. else
  3573. TRACE("SetupDudes(): CWarp::CreateWarpFromDude() failed.\n");
  3574. delete pdude;
  3575. pdude = 0;
  3576. pln = plnNext;
  3577. }
  3578. //------------------------------------------------------------------------------
  3579. // Here, we warp-in as many dude's as we need. If there are no warps, it
  3580. // probably means the realm wasn't designed correctecly, and we bail out.
  3581. //------------------------------------------------------------------------------
  3582. if (prealm->m_asClassNumThings[CThing::CWarpID] > 0)
  3583. {
  3584. // Setup warp pointers
  3585. CListNode<CThing>* plnWarpHead = &(prealm->m_aclassHeads[CThing::CWarpID]);
  3586. CListNode<CThing>* plnWarp = plnWarpHead->m_pnNext;
  3587. CListNode<CThing>* plnWarpTail = &(prealm->m_aclassTails[CThing::CWarpID]);
  3588. // Multiplayer mode is handled separately
  3589. if (pinfo->IsMP())
  3590. {
  3591. // Get convenient pointer
  3592. CNetClient* pclient = pinfo->Client();
  3593. // Find a random starter. Pick a number from 0 to n - 1 where n is the
  3594. // number of CWarps in the realm. Next, iterate to that warp so we start
  3595. // creating dudes at a 'random' warp.
  3596. short sStartWarpNum = GetRand() % prealm->m_asClassNumThings[CThing::CWarpID];
  3597. short i;
  3598. for (i = 0; i < sStartWarpNum; i++, plnWarp = plnWarp->m_pnNext)
  3599. ;
  3600. // Warp in as many dude's as we need
  3601. for (Net::ID id = 0; (sResult == 0) && (id < Net::MaxNumIDs); id++)
  3602. {
  3603. // If this player needs a dude
  3604. if (pclient->DoesPlayerNeedDude(id))
  3605. {
  3606. // If we've hit the tail of the warps, wrap around
  3607. if (plnWarp == plnWarpTail)
  3608. plnWarp = plnWarpHead->m_pnNext;
  3609. pwarp = (CWarp*)plnWarp->m_powner;
  3610. ASSERT(pwarp != NULL);
  3611. // Warp in dude (creates a new dude since the pointer starts out NULL)
  3612. pdude = NULL;
  3613. if (pwarp->WarpIn(&pdude, CWarp::CopyStockPile) == 0)
  3614. {
  3615. // SPECIAL CASE!!! In multiplayer mode, we overwrite the dude numbers
  3616. // that are assigned by the CDude constructor, instead using the
  3617. // corresponding network ID. This isn't a great solution, but it
  3618. // was the best we could do given the little time we have left.
  3619. ASSERT(pdude != NULL);
  3620. pdude->m_sDudeNum = (short)id;
  3621. // Set general dude stuff
  3622. SetupGeneralDude(pdude, (short)pclient->GetPlayerColor(id), palevelpersist);
  3623. // Set dude's instance ID (not to be confused with network ID)
  3624. pclient->SetPlayerDudeID(id, pdude->GetInstanceID());
  3625. // Special stuff for local dude
  3626. if (id == pclient->GetID())
  3627. SetupLocalDude(pinfo, pdude);
  3628. }
  3629. else
  3630. {
  3631. sResult = -1;
  3632. TRACE("SetupDudes(): pwarp->WarpIn() failed.\n");
  3633. }
  3634. plnWarp = plnWarp->m_pnNext;
  3635. }
  3636. }
  3637. }
  3638. else
  3639. {
  3640. // Use the first warp
  3641. pwarp = (CWarp*)plnWarp->m_powner;
  3642. ASSERT(pwarp != NULL);
  3643. // Warp in dude (creates a new dude since the pointer starts out NULL)
  3644. pdude = NULL;
  3645. if (pwarp->WarpIn(&pdude, CWarp::CopyStockPile) == 0)
  3646. {
  3647. // Set general dude stuff
  3648. SetupGeneralDude(pdude, g_GameSettings.m_sPlayerColorIndex, palevelpersist);
  3649. // Special stuff just for local dude
  3650. SetupLocalDude(pinfo, pdude);
  3651. }
  3652. else
  3653. {
  3654. sResult = -1;
  3655. TRACE("SetupDudes(): pwarp->WarpIn() failed.\n");
  3656. }
  3657. }
  3658. }
  3659. else
  3660. {
  3661. TRACE("SetupDudes(): No warps!!!\n");
  3662. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK,
  3663. "Realm Error",
  3664. "There are no warps in this realm! There must be at least one warp in a realm!\n");
  3665. sResult = -1;
  3666. }
  3667. return sResult;
  3668. }
  3669. ////////////////////////////////////////////////////////////////////////////////
  3670. //
  3671. // Blanks the specified area of the display.
  3672. //
  3673. ////////////////////////////////////////////////////////////////////////////////
  3674. void BlankDisplay( // Returns nothing.
  3675. short sX, // In: X start position.
  3676. short sY, // In: Y start position.
  3677. short sW, // In: Width.
  3678. short sH, // In: Height
  3679. CPlayInfo* pinfo) // Out: Dimensions to update to the display later.
  3680. {
  3681. if (sW > 0 && sH > 0)
  3682. {
  3683. rspLockBuffer();
  3684. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, sX, sY, sW, sH);
  3685. rspUnlockBuffer();
  3686. pinfo->m_drl.Add(sX, sY, sW, sH);
  3687. }
  3688. }
  3689. ////////////////////////////////////////////////////////////////////////////////
  3690. //
  3691. // Scale the film to g_GameSettings.m_dGameFilmScale.
  3692. //
  3693. ////////////////////////////////////////////////////////////////////////////////
  3694. void ScaleFilm(
  3695. CPlayInfo* pinfo, // I/O: Play info
  3696. bool bRedraw = true) // In: true to clear any newly created dirty areas.
  3697. {
  3698. // Get pointers to camera and grip
  3699. CCamera* pcamera = pinfo->Camera();
  3700. CGrip* pgrip = pinfo->Grip();
  3701. // Remember previous values so we know what portion of the screen needs to be cleared
  3702. short sOldFilmX = pcamera->m_sFilmViewX;
  3703. short sOldFilmY = pcamera->m_sFilmViewY;
  3704. short sOldViewW = pcamera->m_sViewW;
  3705. short sOldViewH = pcamera->m_sViewH;
  3706. // Clamp the scale to fit the valid range
  3707. if (g_GameSettings.m_dGameFilmScale > FILM_MAX_SCALE)
  3708. g_GameSettings.m_dGameFilmScale = FILM_MAX_SCALE;
  3709. if (g_GameSettings.m_dGameFilmScale < FILM_MIN_SCALE)
  3710. g_GameSettings.m_dGameFilmScale = FILM_MIN_SCALE;
  3711. // Scale the actual film.
  3712. short sViewW = VIEW_W * g_GameSettings.m_dGameFilmScale;
  3713. short sViewH = VIEW_H * g_GameSettings.m_dGameFilmScale;
  3714. short sFilmX = FILM_X + (VIEW_W - sViewW) / 2;
  3715. short sFilmY = FILM_Y + (VIEW_H - sViewH) / 2;
  3716. // Update the camera to the new film size.
  3717. pcamera->m_sViewW = sViewW;
  3718. pcamera->m_sViewH = sViewH;
  3719. pcamera->SetFilm(g_pimScreenBuf, sFilmX, sFilmY);
  3720. // Update the grip to the new film scaling.
  3721. pgrip->SetParms(
  3722. MAX(short(g_GameSettings.m_sGripZoneRadius * g_GameSettings.m_dGameFilmScale), short(MIN_GRIP_ZONE_RADIUS) ),
  3723. GRIP_MIN_MOVE_X,
  3724. GRIP_MIN_MOVE_Y,
  3725. GRIP_MAX_MOVE_X,
  3726. GRIP_MAX_MOVE_Y,
  3727. GRIP_ALIGN_X,
  3728. GRIP_ALIGN_Y,
  3729. true);
  3730. // If local dude exists, reset the grip's targetting
  3731. CDude* pdudeLocal = pinfo->LocalDudePointer();
  3732. if (pdudeLocal)
  3733. pgrip->ResetTarget(pdudeLocal->GetX(), pdudeLocal->GetZ(), 30);
  3734. // Clear any portion of the screen that was revealed by the change in scale
  3735. if (bRedraw == true)
  3736. {
  3737. // Update revealed zones.
  3738. // ________
  3739. // |xxxxxxxx|
  3740. // |xxxxxxxx|
  3741. // |**| |++|
  3742. // |**|__|++|
  3743. // |--------|
  3744. // |--------|
  3745. // Top strip.
  3746. // ________
  3747. // |xxxxxxxx|
  3748. // |xxxxxxxx|
  3749. // | | | |
  3750. // | |__| |
  3751. // | |
  3752. // |________|
  3753. BlankDisplay(sOldFilmX, sOldFilmY, sOldViewW, sFilmY - sOldFilmY, pinfo);
  3754. // Bottom strip.
  3755. // ________
  3756. // | |
  3757. // | __ |
  3758. // | | | |
  3759. // | |__| |
  3760. // |xxxxxxxx|
  3761. // |xxxxxxxx|
  3762. BlankDisplay( sOldFilmX, sFilmY + sViewH, sOldViewW, (sOldFilmY + sOldViewH) - (sFilmY + sViewH), pinfo);
  3763. // Left strip.
  3764. // ________
  3765. // | |
  3766. // | __ |
  3767. // |xx| | |
  3768. // |xx|__| |
  3769. // | |
  3770. // |________|
  3771. BlankDisplay(sOldFilmX, sFilmY, sFilmX - sOldFilmX, sViewH, pinfo);
  3772. // Right strip.
  3773. // ________
  3774. // | |
  3775. // | __ |
  3776. // | | |xx|
  3777. // | |__|xx|
  3778. // | |
  3779. // |________|
  3780. BlankDisplay(sFilmX + sViewW, sFilmY, (sOldFilmX + sOldViewW) - (sFilmX + sViewW), sViewH, pinfo);
  3781. }
  3782. // Save new settings so we'll know when they change
  3783. m_dCurrentFilmScale = g_GameSettings.m_dGameFilmScale;
  3784. m_sCurrentGripZoneRadius = g_GameSettings.m_sGripZoneRadius;
  3785. }
  3786. ////////////////////////////////////////////////////////////////////////////////
  3787. //
  3788. // Make a demo mode movie for debugging purposes.
  3789. //
  3790. ////////////////////////////////////////////////////////////////////////////////
  3791. void MakeDemoMovie(
  3792. RFile* pfileDemoModeDebugMovie) // In: File for loading/saving demo mode debug movie
  3793. {
  3794. // The basic idea is that in demo record mode, we save every frame of the
  3795. // game being recorded to a file. Then, in demo playback mode, we compare
  3796. // each frame of the game as it plays back to the recorded frames, and if
  3797. // there's a difference between the frames, we highlight that difference.
  3798. // From that, we hope that the programmer can figure out his stupid mistake
  3799. // that somehow caused such a difference. :) Naturally, the actual bug may
  3800. // not be directly related to the visual difference, but it should help.
  3801. if (pfileDemoModeDebugMovie && (GetInputMode() != INPUT_MODE_LIVE))
  3802. {
  3803. if (pfileDemoModeDebugMovie->IsOpen())
  3804. {
  3805. bool bDemoErr = false;
  3806. RImage im;
  3807. if (GetInputMode() == INPUT_MODE_RECORD)
  3808. {
  3809. // In record mode, we create an image, copy the screen buffer to it, and save it
  3810. if (im.CreateImage(VIEW_W, VIEW_H, RImage::BMP8) == 0)
  3811. {
  3812. // Must lock the buffer before reading from it.
  3813. rspLockBuffer();
  3814. rspBlit(g_pimScreenBuf, &im, FILM_X, FILM_Y, 0, 0, VIEW_W, VIEW_H);
  3815. // Done with the composite buffer.
  3816. rspUnlockBuffer();
  3817. if (im.Save(pfileDemoModeDebugMovie) != 0)
  3818. {
  3819. TRACE("PlayRealm(): Error writing demo movie!\n");
  3820. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK, g_pszAppName, "Error writing demo movie!\n");
  3821. bDemoErr = true;
  3822. }
  3823. }
  3824. else
  3825. {
  3826. TRACE("PlayRealm(): Error create demo image!\n");
  3827. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK, g_pszAppName, "Error creating demo image!\n");
  3828. bDemoErr = true;
  3829. }
  3830. }
  3831. else
  3832. {
  3833. // In playback mode, we load the previously saved image and compare it to the screen bufer
  3834. if (im.Load(pfileDemoModeDebugMovie) == 0)
  3835. {
  3836. // Must lock the buffer before reading from it.
  3837. rspLockBuffer();
  3838. bool bMatch = true;
  3839. int i;
  3840. U8* pSrcLine = im.m_pData;
  3841. U8* pDstLine = g_pimScreenBuf->m_pData + ((long)FILM_Y * g_pimScreenBuf->m_lPitch) + (long)FILM_X;
  3842. short sHeight = im.m_sHeight;
  3843. U8* pSrc;
  3844. U8* pDst;
  3845. while (sHeight--)
  3846. {
  3847. pSrc = pSrcLine;
  3848. pDst = pDstLine;
  3849. i = im.m_sWidth;
  3850. while (i--)
  3851. {
  3852. if (*pSrc != *pDst) bMatch = false;
  3853. pDst++;pSrc++;
  3854. }
  3855. pSrcLine += im.m_lPitch;
  3856. pDstLine += g_pimScreenBuf->m_lPitch;
  3857. }
  3858. // If there's a mismatch, highlight the differences between the two frames
  3859. if (!bMatch)
  3860. {
  3861. int i;
  3862. U8* pSrcLine = im.m_pData;
  3863. U8* pDstLine = g_pimScreenBuf->m_pData + ((long)FILM_Y * g_pimScreenBuf->m_lPitch) + (long)FILM_X;
  3864. short sHeight = im.m_sHeight;
  3865. U8* pSrc;
  3866. U8* pDst;
  3867. while (sHeight--)
  3868. {
  3869. pSrc = pSrcLine;
  3870. pDst = pDstLine;
  3871. i = im.m_sWidth;
  3872. while (i--)
  3873. {
  3874. if (*pSrc == *pDst)
  3875. *pSrc = 0;
  3876. pDst++;pSrc++;
  3877. }
  3878. pSrcLine += im.m_lPitch;
  3879. pDstLine += g_pimScreenBuf->m_lPitch;
  3880. }
  3881. // Copy modified image to screen buffer and update the screen
  3882. rspBlit(&im, g_pimScreenBuf, 0, 0, FILM_X, FILM_Y, VIEW_W, VIEW_H);
  3883. // Done with the composite buffer.
  3884. rspUnlockBuffer();
  3885. rspUpdateDisplay();
  3886. // If wait-for-click is enabled, wait for click. Otherwise, don't.
  3887. // It will always wait for a click on the first different frame, and thereafter
  3888. // the user can disable the waiting by clicking the right mouse button.
  3889. if (m_bMakeDemoMovie_WaitForClick)
  3890. {
  3891. short sButtons;
  3892. do {
  3893. rspGetMouse(NULL, NULL, &sButtons);
  3894. UpdateSystem();
  3895. } while (sButtons);
  3896. do {
  3897. rspGetMouse(NULL, NULL, &sButtons);
  3898. UpdateSystem();
  3899. } while (!sButtons);
  3900. if (sButtons & 2)
  3901. m_bMakeDemoMovie_WaitForClick = false;
  3902. do {
  3903. rspGetMouse(NULL, NULL, &sButtons);
  3904. UpdateSystem();
  3905. } while (sButtons);
  3906. rspClearMouseInputEvents();
  3907. }
  3908. }
  3909. else
  3910. {
  3911. // Done with the composite buffer.
  3912. rspUnlockBuffer();
  3913. }
  3914. }
  3915. else
  3916. {
  3917. TRACE("PlayRealm(): Error reading demo movie!\n");
  3918. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK, g_pszAppName, "Error reading demo movie!\n");
  3919. bDemoErr = true;
  3920. }
  3921. }
  3922. // If there was an error, close file to turn off demo movie mode
  3923. if (bDemoErr)
  3924. pfileDemoModeDebugMovie->Close();
  3925. }
  3926. }
  3927. }
  3928. };
  3929. ////////////////////////////////////////////////////////////////////////////////
  3930. //
  3931. // Score play module
  3932. //
  3933. ////////////////////////////////////////////////////////////////////////////////
  3934. class CPlayScore : public CPlay
  3935. {
  3936. //------------------------------------------------------------------------------
  3937. // Types, enums, etc.
  3938. //------------------------------------------------------------------------------
  3939. private:
  3940. //------------------------------------------------------------------------------
  3941. // Variables
  3942. //------------------------------------------------------------------------------
  3943. private:
  3944. //------------------------------------------------------------------------------
  3945. // Functions
  3946. //------------------------------------------------------------------------------
  3947. public:
  3948. ////////////////////////////////////////////////////////////////////////////////
  3949. // Constructor
  3950. ////////////////////////////////////////////////////////////////////////////////
  3951. CPlayScore(void)
  3952. {
  3953. }
  3954. ////////////////////////////////////////////////////////////////////////////////
  3955. // Destructor
  3956. ////////////////////////////////////////////////////////////////////////////////
  3957. virtual
  3958. ~CPlayScore()
  3959. {
  3960. }
  3961. ////////////////////////////////////////////////////////////////////////////////
  3962. // Prepare game
  3963. ////////////////////////////////////////////////////////////////////////////////
  3964. /* virtual */
  3965. short PrepareGame( // Returns 0 if successfull, non-zero otherwise
  3966. CPlayInfo* pinfo) // I/O: Play info
  3967. {
  3968. // Init and reset score module
  3969. ScoreInit();
  3970. ScoreReset();
  3971. return 0;
  3972. }
  3973. ////////////////////////////////////////////////////////////////////////////////
  3974. // Start realm
  3975. ////////////////////////////////////////////////////////////////////////////////
  3976. /* virtual */
  3977. short StartRealm( // Returns 0 if successfull, non-zero otherwise
  3978. CPlayInfo* pinfo) // I/O: Play info
  3979. {
  3980. if (!pinfo->m_bBadRealmMP)
  3981. {
  3982. // Reset the display timer
  3983. ScoreResetDisplay();
  3984. // Set the scoring type
  3985. if (pinfo->IsMP())
  3986. {
  3987. ScoreSetMode(CScoreboard::MultiPlayer);
  3988. if (pinfo->Realm()->m_ScoringMode == 0)
  3989. pinfo->Realm()->m_ScoringMode = CRealm::MPFrag;
  3990. }
  3991. else
  3992. {
  3993. ScoreSetMode(CScoreboard::SinglePlayer);
  3994. }
  3995. }
  3996. return 0;
  3997. }
  3998. ////////////////////////////////////////////////////////////////////////////////
  3999. // End realm
  4000. ////////////////////////////////////////////////////////////////////////////////
  4001. /* virtual */
  4002. void EndRealm(
  4003. CPlayInfo* pinfo) // I/O: Play info
  4004. {
  4005. if (!pinfo->m_bBadRealmMP)
  4006. {
  4007. // If MP mode, check if score should be reset after each level
  4008. if (pinfo->IsMP() && g_GameSettings.m_sHostResetScoresEachLevel)
  4009. ScoreReset();
  4010. }
  4011. }
  4012. };
  4013. ////////////////////////////////////////////////////////////////////////////////
  4014. //
  4015. // Base class for all "Play Modules"
  4016. //
  4017. ////////////////////////////////////////////////////////////////////////////////
  4018. class CPlayCutscene : public CPlay
  4019. {
  4020. //------------------------------------------------------------------------------
  4021. // Types, enums, etc.
  4022. //------------------------------------------------------------------------------
  4023. private:
  4024. //------------------------------------------------------------------------------
  4025. // Variables
  4026. //------------------------------------------------------------------------------
  4027. private:
  4028. bool m_bSimple;
  4029. //------------------------------------------------------------------------------
  4030. // Functions
  4031. //------------------------------------------------------------------------------
  4032. public:
  4033. ////////////////////////////////////////////////////////////////////////////////
  4034. // Constructor
  4035. ////////////////////////////////////////////////////////////////////////////////
  4036. CPlayCutscene(void)
  4037. {
  4038. }
  4039. ////////////////////////////////////////////////////////////////////////////////
  4040. // Destructor
  4041. ////////////////////////////////////////////////////////////////////////////////
  4042. /* virtual */
  4043. ~CPlayCutscene()
  4044. {
  4045. }
  4046. ////////////////////////////////////////////////////////////////////////////////
  4047. // Start cutscene
  4048. ////////////////////////////////////////////////////////////////////////////////
  4049. /* virtual */
  4050. void StartCutscene(
  4051. CPlayInfo* pinfo) // I/O: Play info
  4052. {
  4053. // Clear input events (don't let user press anything before the cutscene appears)
  4054. rspClearAllInputEvents();
  4055. // For demo and specific file modes, use simple cutscenes. Otherwise, use real cutscenes.
  4056. m_bSimple = ((GetInputMode() != INPUT_MODE_LIVE) || (pinfo->RealmNum() < 0)) ? true : false;
  4057. // If this is the spawn version, it only has 1 cutscene image, so it should use
  4058. // simple mode to display that one.
  4059. #ifdef SPAWN
  4060. m_bSimple = true;
  4061. #endif
  4062. // Special case for the last level demo
  4063. if (g_bLastLevelDemo)
  4064. m_bSimple = false;
  4065. // Start cutscene
  4066. RString strSection;
  4067. RString strEntry;
  4068. Play_GetRealmSectionAndEntry(pinfo->IsMP(), pinfo->CoopLevels(), pinfo->Gauntlet(), pinfo->AddOn(), pinfo->RealmNum(), pinfo->Realm()->m_flags.sDifficulty, &strSection, &strEntry);
  4069. CutSceneStart(m_bSimple, &strSection, &strEntry, 24, 24);
  4070. }
  4071. ////////////////////////////////////////////////////////////////////////////////
  4072. // Do cutscene
  4073. ////////////////////////////////////////////////////////////////////////////////
  4074. /* virtual */
  4075. void DoCutscene(
  4076. CPlayInfo* pinfo) // I/O: Play info
  4077. {
  4078. // If this is NOT simple and NOT multiplayer, do the effect while waiting for user input
  4079. if (!m_bSimple && !pinfo->IsMP())
  4080. {
  4081. // Configure cutscene effect
  4082. CutSceneConfig(
  4083. 3600,
  4084. -24,24,10000L,
  4085. -24,24,10000L,
  4086. 0.6,0.6,4000L,
  4087. 0,0,g_pimScreenBuf->m_sWidth,g_pimScreenBuf->m_sHeight);
  4088. // Insert effects into this loop!
  4089. RInputEvent ie;
  4090. ie.type = RInputEvent::None;
  4091. rspClearAllInputEvents();
  4092. while (rspGetQuitStatus() == 0)
  4093. {
  4094. CutSceneUpdate();
  4095. UpdateSystem();
  4096. if (((rspGetNextInputEvent(&ie) == 1) && (ie.type == RInputEvent::Key))
  4097. || IsXInputButtonPressed())
  4098. break;
  4099. }
  4100. }
  4101. }
  4102. ////////////////////////////////////////////////////////////////////////////////
  4103. // End cutscene
  4104. ////////////////////////////////////////////////////////////////////////////////
  4105. /* virtual */
  4106. void EndCutscene(
  4107. CPlayInfo* pinfo) // I/O: Play info
  4108. {
  4109. // End cutscene
  4110. CutSceneEnd();
  4111. // Clear any excess inputs
  4112. rspClearAllInputEvents();
  4113. rspLockBuffer();
  4114. // Clear screen (to avoid palette flash when the hood sets its palette)
  4115. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, 0, 0, g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight);
  4116. rspUnlockBuffer();
  4117. rspUpdateDisplay();
  4118. // A quick delay while on the black screen looks better than no delay
  4119. long lBlackTime = rspGetMilliseconds();
  4120. while (rspGetMilliseconds() - lBlackTime < BLACK_HOLD_TIME)
  4121. ;
  4122. }
  4123. };
  4124. ////////////////////////////////////////////////////////////////////////////////
  4125. //
  4126. // Abort all currently playing sounds and do not return until they are gone
  4127. // unless timed out for safety.
  4128. //
  4129. ////////////////////////////////////////////////////////////////////////////////
  4130. inline void SynchronousSampleAbortion(void)
  4131. {
  4132. // Stop all currently playing samples abruptly.
  4133. AbortAllSamples();
  4134. // We should never need a timeout but I don't want to risk a Muppets
  4135. // scenario where a shitty sound driver causes us to think a sound is always
  4136. // playing.
  4137. // Wait for all samples to finish.
  4138. long lTimeOutTime = rspGetMilliseconds() + TIME_OUT_FOR_ABORT_SOUNDS;
  4139. // Wait for them to stop.
  4140. while (IsSamplePlaying() == true && rspGetMilliseconds() < lTimeOutTime)
  4141. {
  4142. // Always do periodic updates.
  4143. // Crucial to sound completing.
  4144. UpdateSystem();
  4145. }
  4146. }
  4147. ////////////////////////////////////////////////////////////////////////////////
  4148. //
  4149. // Play game using specified settings.
  4150. //
  4151. ////////////////////////////////////////////////////////////////////////////////
  4152. extern short Play( // Returns 0 if successfull, non-zero otherwise
  4153. CNetClient* pclient, // In: Client object or NULL if not network game
  4154. CNetServer* pserver, // In: Server object or NULL if not server or not network game
  4155. INPUT_MODE inputMode, // In: Input mode
  4156. const short sRealmNum, // In: Realm number to start on or -1 to use specified realm file
  4157. const char* pszRealmFile, // In: Realm file to play (ignored if sRealmNum >= 0)
  4158. const bool bJustOneRealm, // In: Play just this one realm (ignored if sRealmNum < 0)
  4159. const bool bGauntlet, // In: Play challenge levels gauntlet - as selected on menu
  4160. const bool bAddOn, // In: Play add on levels
  4161. const short sDifficulty, // In: Difficulty level
  4162. const bool bRejuvenate, // In: Whether to allow players to rejuvenate (MP only)
  4163. const short sTimeLimit, // In: Time limit for MP games (0 or negative if none)
  4164. const short sKillLimit, // In: Kill limit for MP games (0 or negative if none)
  4165. const short sCoopLevels, // In: Zero for deathmatch levels, non-zero for cooperative levels.
  4166. const short sCoopMode, // In: Zero for deathmatch mode, non-zero for cooperative mode.
  4167. const short sFrameTime, // In: Milliseconds per frame (MP only)
  4168. RFile* pfileDemoModeDebugMovie) // In: File for loading/saving demo mode debug movie
  4169. {
  4170. short sResult = 0;
  4171. //#ifdef MOBILE
  4172. if (inputMode == INPUT_MODE_PLAYBACK)
  4173. demoCompat = true; //DEMO playback
  4174. else
  4175. demoCompat = false;
  4176. //#endif
  4177. // If this is the last demo level, then load the mult alpha needed for the ending
  4178. RMultiAlpha* pDemoMultiAlpha = NULL;
  4179. if (g_bLastLevelDemo)
  4180. {
  4181. sResult = rspGetResource(&g_resmgrGame, DEMO_MULTIALPHA_FILE, &pDemoMultiAlpha, RFile::LittleEndian);
  4182. if (sResult != SUCCESS)
  4183. TRACE("Play - Error loading multialpha mask for the ending demo\n");
  4184. }
  4185. // Enable RMix's autopump.
  4186. RMix::SetAutoPump(TRUE);
  4187. // Clear any events that might be in the queue
  4188. rspClearAllInputEvents();
  4189. // Lock the composite buffer before accessing it.
  4190. rspLockBuffer();
  4191. // Clear screen
  4192. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, 0, 0, g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight);
  4193. // Lock the composite buffer now that we're done.
  4194. rspUnlockBuffer();
  4195. rspUpdateDisplay();
  4196. // Set input mode
  4197. SetInputMode(inputMode);
  4198. // Reset AI logging feature to avoid potential multiplayer/demo sync problems
  4199. CPerson::ResetLogAI();
  4200. // Reseed random number generator to keep multiplayer/demo games sync'ed.
  4201. SeedRand(1);
  4202. // Create all the play modules
  4203. CPlayNet playNet;
  4204. CPlayStatus playStatus;
  4205. CPlayRealm playRealm;
  4206. CPlayInput playInput;
  4207. CPlayCutscene playCutscene;
  4208. CPlayScore playScore;
  4209. // Create play group and add all the modules to it
  4210. CPlayGroup playgroup;
  4211. playgroup.AddModule(&playNet);
  4212. playgroup.AddModule(&playStatus);
  4213. playgroup.AddModule(&playRealm);
  4214. playgroup.AddModule(&playInput);
  4215. playgroup.AddModule(&playScore);
  4216. playgroup.AddModule(&playCutscene);
  4217. // Create and fill in play info
  4218. CPlayInfo info;
  4219. info.m_pclient = pclient;
  4220. info.m_pserver = pserver;
  4221. info.m_sRealmNum = sRealmNum;
  4222. info.m_bGauntlet = bGauntlet;
  4223. info.m_bAddOn = bAddOn;
  4224. info.m_sFrameTime = sFrameTime;
  4225. info.m_sCoopLevels = sCoopLevels;
  4226. info.Realm()->m_flags.bCoopMode = sCoopMode ? true : false;
  4227. info.Realm()->m_flags.sDifficulty = sDifficulty; // MUST be set before Play_GetRealmInfo() calls.
  4228. if (info.m_sRealmNum < 0)
  4229. {
  4230. strncpy(info.m_szRealm, pszRealmFile, sizeof(info.m_szRealm));
  4231. info.m_szRealm[sizeof(info.m_szRealm)-1] = 0;
  4232. info.m_bJustOneRealm = true;
  4233. }
  4234. else
  4235. {
  4236. if (Play_GetRealmInfo(info.IsMP(), info.CoopLevels(), info.Gauntlet(), info.AddOn(), info.m_sRealmNum, info.Realm()->m_flags.sDifficulty, info.m_szRealm, sizeof(info.m_szRealm)) == 0)
  4237. {
  4238. info.m_bJustOneRealm = bJustOneRealm;
  4239. }
  4240. else
  4241. {
  4242. // 09/12/97 MJR - Clear the string. The CPlayInfo constructor actually does this, but this
  4243. // makes it more obvious.
  4244. info.m_szRealm[0] = 0;
  4245. sResult = -1;
  4246. TRACE("Play(): Couldn't get info for realm #%hd!\n", (short)sRealmNum);
  4247. }
  4248. }
  4249. info.m_pfileDemoModeDebugMovie = pfileDemoModeDebugMovie;
  4250. // 09/12/97 MJR - Here exists yet another error in the release version, but thankfully,
  4251. // it works out okay. Note how completely ignore the value in sResult and simply
  4252. // overwrite it with the return value from PrepareGame(). This should, in general,
  4253. // fail further along the way when we try to load this realm.
  4254. // Open the realm prefs file
  4255. RPrefs prefsRealm;
  4256. // Try opening the realms.ini file on the HD path first, if that fails go to the CD
  4257. sResult = prefsRealm.Open(FullPathHD(g_GameSettings.m_pszRealmPrefsFile), "rt");
  4258. if (sResult != 0)
  4259. sResult = prefsRealm.Open(FullPathCD(g_GameSettings.m_pszRealmPrefsFile), "rt");
  4260. if (sResult == 0)
  4261. {
  4262. short sNumLevels;
  4263. prefsRealm.GetVal("Info", "NumSinglePlayerLevels", 16, &sNumLevels);
  4264. prefsRealm.Close();
  4265. // Prepare game
  4266. sResult = playgroup.PrepareGame(&info);
  4267. if (!sResult)
  4268. {
  4269. // Wait until game is ready
  4270. bool bGameReady = false;
  4271. do {
  4272. sResult = playgroup.IsGameReady(&info, &bGameReady);
  4273. } while (!sResult && !bGameReady);
  4274. if (!sResult && bGameReady)
  4275. {
  4276. // Start game
  4277. sResult = playgroup.StartGame(&info);
  4278. if (sResult == 0)
  4279. {
  4280. /*** 12/5/97 AJC ***/
  4281. #ifdef WIN32
  4282. if (info.IsMP())
  4283. OpenLogFile();
  4284. #endif
  4285. #ifdef MOBILE
  4286. bool doAutoSaveGame = false; //This is set to true when you complete a level, so it's auto saved when the next realm starts
  4287. #endif
  4288. /*** 12/5/97 AJC ***/
  4289. // Outer loop keeps playing one realm after another
  4290. do {
  4291. long startRealmMS = -1;
  4292. // Clear game status
  4293. info.SetGameState_Ok();
  4294. // Update global realm number so the "save game" mechanism knows what realm we're on
  4295. g_sRealmNumToSave = info.m_sRealmNum;
  4296. // Sounds playing during the load suck.
  4297. SynchronousSampleAbortion();
  4298. // Start the cutscene
  4299. playgroup.StartCutscene(&info);
  4300. // Prepare realm
  4301. sResult = playgroup.PrepareRealm(&info);
  4302. if (!sResult)
  4303. {
  4304. // Wait until realm is ready
  4305. bool bRealmReady = false;
  4306. do {
  4307. sResult = playgroup.IsRealmReady(&info, &bRealmReady);
  4308. } while (!sResult && !bRealmReady);
  4309. if (!sResult && bRealmReady)
  4310. {
  4311. if ((!info.IsMP()) && (info.m_sRealmNum == 1))
  4312. UnlockAchievement(ACHIEVEMENT_START_SECOND_LEVEL);
  4313. #ifdef MOBILE//Tap screen to get past
  4314. AndroidSetScreenMode(TOUCH_SCREEN_BLANK_TAP);
  4315. #endif
  4316. // do the cutscene
  4317. playgroup.DoCutscene(&info);
  4318. #ifdef MOBILE
  4319. if (inputMode == INPUT_MODE_PLAYBACK)
  4320. AndroidSetScreenMode(TOUCH_SCREEN_BLANK_TAP); //DEMO playback
  4321. else
  4322. AndroidSetScreenMode(TOUCH_SCREEN_GAME);
  4323. #endif
  4324. // End the cutscene
  4325. playgroup.EndCutscene(&info);
  4326. // If multiplayer mode, set up the scoring mode from
  4327. // the flags passed into play.
  4328. if (pclient)
  4329. {
  4330. info.Realm()->m_sKillsGoal = sKillLimit;
  4331. info.Realm()->m_lScoreInitialTime = info.Realm()->m_lScoreTimeDisplay = (long)sTimeLimit * (long)60000;
  4332. // If Rejuvenate is allowed, then its not last man standing
  4333. if (bRejuvenate)
  4334. {
  4335. if (sKillLimit > 0 && sTimeLimit > 0)
  4336. info.Realm()->m_ScoringMode = CRealm::MPTimedFrag;
  4337. if (sKillLimit <= 0 && sTimeLimit > 0)
  4338. info.Realm()->m_ScoringMode = CRealm::MPTimed;
  4339. if (sKillLimit > 0 && sTimeLimit <= 0)
  4340. info.Realm()->m_ScoringMode = CRealm::MPFrag;
  4341. if (sKillLimit <= 0 && sTimeLimit <= 0)
  4342. {
  4343. info.Realm()->m_sKillsGoal = KILLS_LIMIT_DEFAULT;
  4344. info.Realm()->m_ScoringMode = CRealm::MPFrag;
  4345. }
  4346. }
  4347. // Last man standing mode
  4348. else
  4349. {
  4350. if (sKillLimit > 0 && sTimeLimit > 0)
  4351. info.Realm()->m_ScoringMode = CRealm::MPLastManTimedFrag;
  4352. if (sKillLimit > 0 && sTimeLimit <= 0)
  4353. info.Realm()->m_ScoringMode = CRealm::MPLastManFrag;
  4354. if (sKillLimit <= 0 && sTimeLimit > 0)
  4355. info.Realm()->m_ScoringMode = CRealm::MPLastManTimed;
  4356. if (sKillLimit <= 0 && sTimeLimit <= 0)
  4357. info.Realm()->m_ScoringMode = CRealm::MPLastMan;
  4358. }
  4359. }
  4360. // Start realm
  4361. sResult = playgroup.StartRealm(&info);
  4362. if (sResult == 0)
  4363. {
  4364. // Init local input
  4365. ClearLocalInput();
  4366. // Set the resource managers to trace uncached loads
  4367. g_resmgrGame.TraceUncachedLoads(true);
  4368. g_resmgrSamples.TraceUncachedLoads(true);
  4369. g_resmgrRes.TraceUncachedLoads(true);
  4370. // Start the music:
  4371. if (g_bLastLevelDemo)
  4372. {
  4373. // Begin Final Scene Music:
  4374. PlaySample(
  4375. g_smidFinalScene,
  4376. SampleMaster::Unspecified,
  4377. 255,
  4378. &g_siFinalScene,
  4379. NULL,
  4380. 0,
  4381. 0,
  4382. true);
  4383. }
  4384. StatsAreAllowed = !info.IsMP(); // !!! FIXME: we currently only track for single-player (because we don't check that kills belong to the local player, etc).
  4385. startRealmMS = rspGetMilliseconds();
  4386. #ifdef MOBILE
  4387. if (doAutoSaveGame)
  4388. {
  4389. TRACE("Doing autosave");
  4390. char szFile[256];
  4391. snprintf(szFile, sizeof (szFile), "%s/auto.gme", SAVEGAME_DIR);
  4392. if (Game_SavePlayersGame(szFile, info.Realm()->m_flags.sDifficulty) == SUCCESS)
  4393. {
  4394. TRACE("Auto Save success");
  4395. }
  4396. else
  4397. TRACE("Auto Save FAILED");
  4398. doAutoSaveGame= false; //reset
  4399. }
  4400. #endif
  4401. // Inner loop plays current realm until it's done
  4402. RInputEvent ie;
  4403. do {
  4404. if ((info.Realm()->m_flags.sDifficulty != 11) && (!g_bLastLevelDemo))
  4405. Flag_Achievements &= ~FLAG_HIGHEST_DIFFICULTY;
  4406. // As always...
  4407. UpdateSystem();
  4408. // User input
  4409. ie.type = RInputEvent::None;
  4410. rspGetNextInputEvent(&ie);
  4411. playgroup.CoreLoopUserInput(&info, &ie);
  4412. #ifdef MOBILE //Tap screen to show menu
  4413. if (info.LocalDudePointer()->IsDead())
  4414. {
  4415. if (!info.m_bInMenu)
  4416. AndroidSetScreenMode(TOUCH_SCREEN_BLANK_TAP);
  4417. }
  4418. #endif
  4419. // Update
  4420. playgroup.CoreLoopUpdate(&info);
  4421. // Render:
  4422. // This requires access to the composite buffer so lock it down.
  4423. rspLockBuffer();
  4424. playgroup.CoreLoopRender(&info);
  4425. playgroup.CoreLoopRenderOnTop(&info);
  4426. // Release the composite buffer now that we're done.
  4427. rspUnlockBuffer();
  4428. // Draw to the screen.
  4429. playgroup.CoreLoopDraw(&info);
  4430. // Check if core loop is done
  4431. } while (!playgroup.IsCoreLoopDone(&info));
  4432. // Set the resource managers to trace uncached loads
  4433. g_resmgrGame.TraceUncachedLoads(false);
  4434. g_resmgrSamples.TraceUncachedLoads(false);
  4435. g_resmgrRes.TraceUncachedLoads(false);
  4436. // If this was the last demo level, then do the martini effect
  4437. if (g_bLastLevelDemo)
  4438. {
  4439. RRect rect(0,40,640,400);
  4440. MartiniDo(g_pimScreenBuf,
  4441. 0,
  4442. 0,
  4443. pDemoMultiAlpha,
  4444. 15000,
  4445. 24,
  4446. 5000,
  4447. 9000,
  4448. &rect,
  4449. 5000,
  4450. g_siFinalScene // to dim out...
  4451. );
  4452. // End the sound:
  4453. if (g_siFinalScene)
  4454. {
  4455. // Cut it off.
  4456. AbortSample(g_siFinalScene);
  4457. // Clear.
  4458. g_siFinalScene = 0;
  4459. // Play final sample that completes the cut off sound. ***
  4460. }
  4461. TRACE("Stop here before clearing screen");
  4462. }
  4463. // *** MP Score display ///////////////
  4464. if (info.IsMP() && !rspGetQuitStatus() && !info.IsGameAborted())
  4465. {
  4466. // Display the high scores. Currently, this is MODAL (but has a timeout)!
  4467. ScoreDisplayHighScores(info.Realm(), info.Client(), MP_HIGH_SCORES_MAX_TIME );
  4468. }
  4469. // End realm
  4470. const bool tmpStatsAreAllowed = StatsAreAllowed;
  4471. StatsAreAllowed = false;
  4472. playgroup.EndRealm(&info);
  4473. StatsAreAllowed = tmpStatsAreAllowed;
  4474. }
  4475. else
  4476. playgroup.StartRealmErr(&info);
  4477. }
  4478. else
  4479. playgroup.IsRealmReadyErr(&info);
  4480. }
  4481. else
  4482. playgroup.PrepareRealmErr(&info);
  4483. const long endRealmMS = rspGetMilliseconds();
  4484. const long timePlayedMS = ((startRealmMS > 0) && (endRealmMS > 0) && (endRealmMS > startRealmMS)) ? endRealmMS - startRealmMS : -1;
  4485. const long newPlaythroughMS = playthroughMS + timePlayedMS;
  4486. if (!g_bLastLevelDemo) // don't charge the last level demo to playthroughMS.
  4487. playthroughMS = ((playthroughMS < 0) || (timePlayedMS < 0) || (newPlaythroughMS < 0)) ? -1 : newPlaythroughMS;
  4488. // End the cutscene. It normally gets called above, but if an error
  4489. // occurs it doesn't, so this is the backup. Multiple calls are safe.
  4490. playgroup.EndCutscene(&info);
  4491. if (StatsAreAllowed)
  4492. {
  4493. Stat_LevelsPlayed++;
  4494. if ((!sResult) && (info.LocalDudePointer()->IsDead()))
  4495. Stat_Deaths++;
  4496. }
  4497. StatsAreAllowed = false;
  4498. #if WITH_STEAMWORKS
  4499. RequestSteamStatsStore(); // this is a good time to push any updated stats from the level.
  4500. #endif
  4501. // Figure out what to do next (same realm, next realm, game over, etc.)
  4502. if (!sResult)
  4503. {
  4504. if (info.JustOneRealm() == true && info.IsRestartingRealm() == false)
  4505. {
  4506. info.SetGameState_GameOver();
  4507. }
  4508. else if (info.IsNextRealm())
  4509. {
  4510. CDude *pDude = info.LocalDudePointer();
  4511. // this is how the toolbar display calculates health.
  4512. const double health = (pDude->GetHealth()*100/pDude->m_sOrigHitPoints);
  4513. if (health < 10)
  4514. UnlockAchievement(ACHIEVEMENT_COMPLETE_LEVEL_ON_LOW_HEALTH);
  4515. if (info.Realm()->m_sPopulation != 0)
  4516. Flag_Achievements &= ~FLAG_KILLED_EVERYTHING;
  4517. if (info.m_sRealmNum == 9)
  4518. UnlockAchievement(ACHIEVEMENT_COMPLETE_LEVEL_10);
  4519. info.m_sRealmNum++;
  4520. switch (Play_GetRealmInfo(info.IsMP(), info.CoopLevels(), info.Gauntlet(), info.AddOn(), info.m_sRealmNum, info.Realm()->m_flags.sDifficulty, info.m_szRealm, sizeof(info.m_szRealm)))
  4521. {
  4522. case 0: // Got info
  4523. #ifdef MOBILE
  4524. if (!bGauntlet) //Dont autosave if playing a challenge!
  4525. doAutoSaveGame = true;
  4526. #endif
  4527. break;
  4528. case 1: // No such realm number
  4529. if (info.IsMP())
  4530. {
  4531. // Multiplayer just keeps wrapping around
  4532. info.m_sRealmNum = 0;
  4533. if (Play_GetRealmInfo(info.IsMP(), info.CoopLevels(), info.Gauntlet(), info.AddOn(), info.m_sRealmNum, info.Realm()->m_flags.sDifficulty, info.m_szRealm, sizeof(info.m_szRealm)) != 0)
  4534. {
  4535. // 09/12/97 MJR - We don't want to exit the loop if this happens. Instead,
  4536. // we set the bad realm flag and let the core loop handle the abort process.
  4537. info.m_bBadRealmMP = true;
  4538. TRACE("Play(): Couldn't get info for realm #%hd!\n", (short)info.m_sRealmNum);
  4539. }
  4540. }
  4541. else
  4542. {
  4543. // This is a bit weird, but it works! If the player has reached the
  4544. // last level, either the game is over or the player has won the game,
  4545. // depending on what mode we're in. If the game is over, we just set
  4546. // the appropriate game state. If the player won the game, we set a
  4547. // special flag and allow the loop we're in to continue, even though
  4548. // there is no such realm (that's how we get to this point). Other
  4549. // special-case code handles everything that happens after that to do
  4550. // the actual ending scene for the game.
  4551. if (!info.Gauntlet() && !info.JustOneRealm() && info.RealmNum() == sNumLevels)
  4552. g_bLastLevelDemo = true;
  4553. else
  4554. info.SetGameState_GameOver();
  4555. }
  4556. break;
  4557. default: // Error
  4558. // 09/12/97 MJR - In MP, we don't want to exit the loop if this happens. Instead,
  4559. // we set the bad realm flag and let the core loop handle the abort process.
  4560. if (info.IsMP())
  4561. info.m_bBadRealmMP = true;
  4562. else
  4563. sResult = -1;
  4564. TRACE("Play(): Couldn't get info for realm #%hd!\n", (short)info.m_sRealmNum);
  4565. break;
  4566. }
  4567. }
  4568. }
  4569. } while (!sResult && !info.IsGameOver() && !g_bLastLevelDemo);
  4570. /*** 12/5/97 AJC ***/
  4571. #ifdef WIN32
  4572. if (info.IsMP())
  4573. CloseLogFile();
  4574. #endif
  4575. /*** 12/5/97 AJC ***/
  4576. }
  4577. else
  4578. playgroup.StartGameErr(&info);
  4579. }
  4580. else
  4581. playgroup.IsGameReadyErr(&info);
  4582. }
  4583. else
  4584. playgroup.PrepareGameErr(&info);
  4585. // Unprepare game
  4586. playgroup.UnprepareGame(&info);
  4587. }
  4588. rspLockBuffer();
  4589. // Clear screen
  4590. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, 0, 0, g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight);
  4591. rspUnlockBuffer();
  4592. rspUpdateDisplay();
  4593. // Disable autopump.
  4594. RMix::SetAutoPump(FALSE);
  4595. // Abort all playing sounds.
  4596. SynchronousSampleAbortion();
  4597. // Clear any events that might be in the queue
  4598. rspClearAllInputEvents();
  4599. return sResult;
  4600. }
  4601. ////////////////////////////////////////////////////////////////////////////////
  4602. //
  4603. // Snap picture to disk.
  4604. //
  4605. ////////////////////////////////////////////////////////////////////////////////
  4606. extern void Play_SnapPicture(void)
  4607. {
  4608. // Feedback is nice.
  4609. PlaySample(
  4610. g_smidClick,
  4611. SampleMaster::UserFeedBack);
  4612. // Set up palette for snap shots once.
  4613. RPal palPicture;
  4614. if (palPicture.CreatePalette(RPal::PDIB) == 0)
  4615. {
  4616. palPicture.m_sStartIndex = 0;
  4617. palPicture.m_sNumEntries = 256;
  4618. rspGetPaletteEntries(
  4619. palPicture.m_sStartIndex, // Palette entry to start copying to (has no effect on source!)
  4620. palPicture.m_sNumEntries, // Number of palette entries to do
  4621. palPicture.Red(0), // Pointer to first red component to copy to
  4622. palPicture.Green(0), // Pointer to first green component to copy to
  4623. palPicture.Blue(0), // Pointer to first blue component to copy to
  4624. palPicture.m_sPalEntrySize); // Number of bytes by which to increment pointers after each copy
  4625. // Store screen buffer's actual type and palette
  4626. RImage::Type typeOrig = g_pimScreenBuf->m_type;
  4627. RPal* ppalOrig = g_pimScreenBuf->m_pPalette;
  4628. // Temporarily change its type and palette
  4629. g_pimScreenBuf->m_type = RImage::BMP8;
  4630. g_pimScreenBuf->m_pPalette = &palPicture;
  4631. // Save picture to file
  4632. char szFileName[RSP_MAX_PATH];
  4633. sprintf(szFileName, "PostalShot%03ld.bmp", ms_lCurPicture++);
  4634. // This will require direct access to the composite buffer.
  4635. rspLockBuffer();
  4636. g_pimScreenBuf->SaveDib(szFileName);
  4637. rspUnlockBuffer();
  4638. // Restore original type and palette
  4639. g_pimScreenBuf->m_type = typeOrig;
  4640. g_pimScreenBuf->m_pPalette = ppalOrig;
  4641. }
  4642. }
  4643. ////////////////////////////////////////////////////////////////////////////////
  4644. //
  4645. // Callback from g_menuVerifyQuitGame with choice.
  4646. //
  4647. ////////////////////////////////////////////////////////////////////////////////
  4648. extern bool Play_VerifyQuitMenuChoice( // Returns true to accept, false to deny choice.
  4649. Menu* pmenuCurrent, // In: Current menu.
  4650. short sMenuItem) // In: Item chosen or -1 for change of focus.
  4651. {
  4652. bool bAcceptChoice = true; // Assume accepting choice.
  4653. switch (sMenuItem)
  4654. {
  4655. case 0: // Continue.
  4656. #ifdef MOBILE
  4657. continueIsRestart = true; //Now the continue button will restart the realm
  4658. #endif
  4659. ms_menuaction = MenuActionEndMenu;
  4660. break;
  4661. case 1: // Save game
  4662. ms_menuaction = MenuActionSaveGame;
  4663. break;
  4664. case 2: // Options.
  4665. break;
  4666. case 3: // Quit.
  4667. ms_menuaction = MenuActionQuit;
  4668. break;
  4669. #ifdef MOBILE
  4670. case 10:// Menu cancelled, set in menus_android.cpp
  4671. ms_menuaction = MenuActionEndMenu;
  4672. break;
  4673. #endif
  4674. }
  4675. // Audible Feedback.
  4676. if (sMenuItem == -1)
  4677. PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
  4678. else
  4679. PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
  4680. return bAcceptChoice;
  4681. }
  4682. ////////////////////////////////////////////////////////////////////////////////
  4683. //
  4684. // Get info about specified realm
  4685. //
  4686. ////////////////////////////////////////////////////////////////////////////////
  4687. extern short Play_GetRealmInfo( // Returns 0 if successfull, 1 if no such realm, negative on error
  4688. bool bNetwork, // In: true if network game, false otherwise
  4689. bool bCoop, // In: true if coop net game, false otherwise -- no effect if bNetwork is false.
  4690. bool bGauntlet, // In: true if playing challenge mode
  4691. bool bAddOn, // In: true if playing the new add on levels
  4692. short sRealmNum, // In: Realm number
  4693. short sDifficulty, // In: Realm difficulty.
  4694. char* pszFile, // Out: Realm's file name
  4695. short sMaxFileLen, // In: Max length of returned file name, including terminating null
  4696. char* pszTitle /*= 0*/, // Out: Realm's title
  4697. short sMaxTitleLen /*= NULL*/) // In: Max length of returned title, including terminating null
  4698. {
  4699. ASSERT(sRealmNum >= 0);
  4700. ASSERT(pszFile != NULL);
  4701. ASSERT(sMaxFileLen > 0);
  4702. short sResult = 0;
  4703. // Open the realm prefs file
  4704. RPrefs prefsRealm;
  4705. // Try opening the realms.ini file on the HD path first, if that fails go to the CD
  4706. sResult = prefsRealm.Open(FullPathHD(g_GameSettings.m_pszRealmPrefsFile), "rt");
  4707. if (sResult != 0)
  4708. sResult = prefsRealm.Open(FullPathCD(g_GameSettings.m_pszRealmPrefsFile), "rt");
  4709. if (sResult == 0)
  4710. {
  4711. // Get realm's section and entry strings
  4712. RString strSection;
  4713. RString strEntry;
  4714. Play_GetRealmSectionAndEntry(bNetwork, bCoop, bGauntlet, bAddOn, sRealmNum, sDifficulty, &strSection, &strEntry);
  4715. // Get realm file name from prefs file
  4716. char szText[RSP_MAX_PATH * 2];
  4717. prefsRealm.GetVal((char*)strSection, (char*)strEntry, "", szText);
  4718. if (strlen(szText) == 0)
  4719. {
  4720. // Realm not found
  4721. sResult = 1;
  4722. }
  4723. else if ((strlen(szText) + 1) <= sMaxFileLen)
  4724. {
  4725. // Return the file name
  4726. strcpy(pszFile, szText);
  4727. // Check if caller wants the title, too
  4728. if ((sMaxTitleLen > 0) && (pszTitle != NULL))
  4729. {
  4730. // Get title from prefs file
  4731. prefsRealm.GetVal((char*)strSection, "Title", "Untitled", szText);
  4732. // Copy amount that will fit
  4733. strncpy(pszTitle, szText, sMaxTitleLen - 2);
  4734. pszTitle[sMaxTitleLen - 1] = '\0';
  4735. }
  4736. }
  4737. else
  4738. {
  4739. // File name too long (and can't be truncated)
  4740. sResult = -1;
  4741. TRACE("Play_GetRealmInfo(): Realm file name/path too long!\n");
  4742. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK, g_pszCriticalErrorTitle, g_pszBadPath_s_s, "Realm", (char*)strSection);
  4743. }
  4744. prefsRealm.Close();
  4745. }
  4746. else
  4747. {
  4748. sResult = -1;
  4749. TRACE("Play_GetRealmInfo(): Error opening realm prefs file: '%s'!\n", FullPathCD(g_GameSettings.m_pszRealmPrefsFile));
  4750. rspMsgBox(RSP_MB_ICN_STOP | RSP_MB_BUT_OK, "", "Can't open realm prefs file '%s'.\n", FullPathCD(g_GameSettings.m_pszRealmPrefsFile));
  4751. }
  4752. return sResult;
  4753. }
  4754. ////////////////////////////////////////////////////////////////////////////////
  4755. //
  4756. // Get the section and entry that should be used when querying the realms prefs
  4757. // file for the described realm.
  4758. //
  4759. ////////////////////////////////////////////////////////////////////////////////
  4760. extern void Play_GetRealmSectionAndEntry(
  4761. bool bNetwork, // In: true if network game, false otherwise
  4762. bool bCoop, // In: true if coop net game, false otherwise -- no effect if bNetwork is false.
  4763. bool bGauntlet, // In: true if playing challenge mode
  4764. bool bAddOnLevels, // In: true if playing new add on levels
  4765. short sRealmNum, // In: Realm number
  4766. short sDifficulty, // In: Realm difficulty.
  4767. RString* pstrSection, // Out: Section is returned here
  4768. RString* pstrEntry) // Out: Entry is returned here
  4769. {
  4770. if (bNetwork)
  4771. {
  4772. if (bCoop == false)
  4773. {
  4774. // Deathmatch multiplayer sections are named "RealmNet1, "RealmNet2", etc.
  4775. *pstrSection = "RealmNet";
  4776. }
  4777. else
  4778. {
  4779. // Cooperative multiplayer sections are named "RealmCoopNet1, "RealmCoopNet2", etc.
  4780. *pstrSection = "RealmCoopNet";
  4781. }
  4782. *pstrSection += (short)(sRealmNum + 1);
  4783. // Multiplayer realm entry is always "Realm"
  4784. *pstrEntry = "Realm";
  4785. }
  4786. else if (bGauntlet)
  4787. {
  4788. // Challenge sections are named "Challenge1", "Challenge2", etc.
  4789. *pstrSection = "Gauntlet";
  4790. *pstrSection += (short)(sRealmNum + 1);
  4791. // Challen realm entry is always "Realm"
  4792. *pstrEntry = "Realm";
  4793. }
  4794. else
  4795. {
  4796. if (g_bLastLevelDemo)
  4797. {
  4798. *pstrSection = "RealmEnd";
  4799. *pstrEntry = "RealmHard";
  4800. }
  4801. else
  4802. {
  4803. // Single player sections are named "Realm1", "Realm2", etc.
  4804. // AddOn single player sections are named "AddOn1", "AddOn2", etc.
  4805. if (bAddOnLevels)
  4806. *pstrSection = "AddOn";
  4807. else
  4808. *pstrSection = "Realm";
  4809. *pstrSection += (short)(sRealmNum + 1);
  4810. // Single player entry depends on difficulty level
  4811. switch (sDifficulty)
  4812. {
  4813. case 0:
  4814. case 1:
  4815. case 2:
  4816. case 3:
  4817. *pstrEntry = "RealmEasy";
  4818. break;
  4819. case 4:
  4820. case 5:
  4821. case 6:
  4822. *pstrEntry = "RealmMedium";
  4823. break;
  4824. case 7:
  4825. case 8:
  4826. case 9:
  4827. case 10:
  4828. case 11:
  4829. default:
  4830. *pstrEntry = "RealmHard";
  4831. break;
  4832. }
  4833. }
  4834. }
  4835. }
  4836. ////////////////////////////////////////////////////////////////////////////////
  4837. //
  4838. // Creates descriptor including app's time stamp, debug status (debug or release)
  4839. // and, if defined, TRACENASSERT flag.
  4840. //
  4841. ////////////////////////////////////////////////////////////////////////////////
  4842. extern
  4843. void Play_GetApplicationDescriptor( // Returns nothing.
  4844. char* pszText, // Out: Text descriptor.
  4845. short sMaxBytes) // In: Amount of writable
  4846. // memory pointed to by pszText.
  4847. {
  4848. // Set default in case there's an error
  4849. ASSERT(strlen(DEFAULT_APP_TIMESTAMP) < sMaxBytes);
  4850. strcpy(pszText, DEFAULT_APP_TIMESTAMP);
  4851. #if defined(WIN32)
  4852. char szModuleFileName[RSP_MAX_PATH];
  4853. if (GetModuleFileName(NULL, szModuleFileName, sizeof(szModuleFileName)) > 0)
  4854. {
  4855. struct _stat statExe;
  4856. if (_stat(szModuleFileName, &statExe) == 0)
  4857. {
  4858. char* pszTime = ctime(&statExe.st_mtime);
  4859. if (pszTime)
  4860. {
  4861. if (strlen(pszText) + strlen(pszTime) < sMaxBytes)
  4862. {
  4863. // ctime() returns a string of exactly 26 characters, including /n and null.
  4864. strcpy(pszText, pszTime);
  4865. }
  4866. // Get rid of trailing '\n'.
  4867. pszText[strlen(pszText) - 1] = '\0';
  4868. }
  4869. }
  4870. }
  4871. #endif
  4872. #ifdef _DEBUG
  4873. if (strlen(pszText) + strlen(DEBUG_STR) < sMaxBytes)
  4874. {
  4875. strcat(pszText, DEBUG_STR);
  4876. }
  4877. #else
  4878. if (strlen(pszText) + strlen(RELEASE_STR) < sMaxBytes)
  4879. {
  4880. strcat(pszText, RELEASE_STR);
  4881. }
  4882. #endif
  4883. #ifdef TRACENASSERT
  4884. if (strlen(pszText) + strlen(TRACENASSERT_STR) < sMaxBytes)
  4885. {
  4886. strcat(pszText, TRACENASSERT_STR);
  4887. }
  4888. #endif
  4889. }
  4890. ////////////////////////////////////////////////////////////////////////////////
  4891. // EOF
  4892. ////////////////////////////////////////////////////////////////////////////////