WormGameState.java 146 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184
  1. /*
  2. * Copyright (c) 2003-onwards Shaven Puppy Ltd
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'Shaven Puppy' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package worm;
  33. import java.io.FileNotFoundException;
  34. import java.io.IOException;
  35. import java.io.ObjectInputStream;
  36. import java.io.ObjectOutputStream;
  37. import java.io.Serializable;
  38. import java.util.ArrayList;
  39. import java.util.Collections;
  40. import java.util.HashMap;
  41. import java.util.HashSet;
  42. import java.util.Iterator;
  43. import java.util.List;
  44. import java.util.Map;
  45. import java.util.Map.Entry;
  46. import java.util.Set;
  47. import net.puppygames.applet.Area;
  48. import net.puppygames.applet.Game;
  49. import net.puppygames.applet.GameInputStream;
  50. import net.puppygames.applet.GameOutputStream;
  51. import net.puppygames.applet.GameState;
  52. import net.puppygames.applet.MiniGame;
  53. import net.puppygames.applet.RoamingFile;
  54. import net.puppygames.applet.effects.LabelEffect;
  55. import net.puppygames.applet.screens.DialogScreen;
  56. import net.puppygames.steam.Steam;
  57. import net.puppygames.steam.SteamException;
  58. import org.lwjgl.input.Keyboard;
  59. import org.lwjgl.input.Mouse;
  60. import org.lwjgl.util.Color;
  61. import org.lwjgl.util.Point;
  62. import org.lwjgl.util.ReadableColor;
  63. import org.lwjgl.util.ReadablePoint;
  64. import org.lwjgl.util.ReadableRectangle;
  65. import org.lwjgl.util.Rectangle;
  66. import worm.buildings.BuildingFeature;
  67. import worm.effects.ArrowEffect;
  68. import worm.entities.Building;
  69. import worm.entities.Capacitor;
  70. import worm.entities.Factory;
  71. import worm.entities.Gidrah;
  72. import worm.entities.Saucer;
  73. import worm.entities.Smartbomb;
  74. import worm.entities.Turret;
  75. import worm.entities.Unit;
  76. import worm.features.GidrahFeature;
  77. import worm.features.HintFeature;
  78. import worm.features.LevelFeature;
  79. import worm.features.MedalFeature;
  80. import worm.features.RankFeature;
  81. import worm.features.ResearchFeature;
  82. import worm.features.StoryFeature;
  83. import worm.features.WorldFeature;
  84. import worm.generator.BaseMapGenerator;
  85. import worm.powerups.BatteryPowerupFeature;
  86. import worm.powerups.BezerkPowerupFeature;
  87. import worm.powerups.CapacitorPowerupFeature;
  88. import worm.powerups.CoolingTowerPowerupFeature;
  89. import worm.powerups.FreezePowerupFeature;
  90. import worm.powerups.MoneyPowerupFeature;
  91. import worm.powerups.PowerupFeature;
  92. import worm.powerups.ReactorPowerupFeature;
  93. import worm.powerups.RepairPowerupFeature;
  94. import worm.powerups.ResourcePowerupFeature;
  95. import worm.powerups.ScannerPowerupFeature;
  96. import worm.powerups.ShieldGeneratorPowerupFeature;
  97. import worm.powerups.ShieldPowerupFeature;
  98. import worm.powerups.SmartbombPowerupFeature;
  99. import worm.screens.CompleteGameScreen;
  100. import worm.screens.CompleteXmasScreen;
  101. import worm.screens.GameScreen;
  102. import worm.screens.IntermissionScreen;
  103. import worm.screens.MenuScreen;
  104. import worm.screens.NewWorldScreen;
  105. import worm.screens.ResearchScreen;
  106. import worm.screens.SelectEndlessLevelScreen;
  107. import worm.screens.SelectLevelScreen;
  108. import worm.screens.SelectSandboxLevelScreen;
  109. import worm.screens.SelectSurvivalLevelScreen;
  110. import worm.screens.SelectWorldScreen;
  111. import worm.screens.StoryScreen;
  112. import worm.screens.SurvivalMenuScreen;
  113. import worm.screens.XmasMenuScreen;
  114. import worm.tiles.Crystal;
  115. import worm.tiles.Exclude;
  116. import com.shavenpuppy.jglib.Resources;
  117. import com.shavenpuppy.jglib.interpolators.CosineInterpolator;
  118. import com.shavenpuppy.jglib.interpolators.LinearInterpolator;
  119. import com.shavenpuppy.jglib.interpolators.SineInterpolator;
  120. import com.shavenpuppy.jglib.resources.MappedColor;
  121. import com.shavenpuppy.jglib.resources.ResourceArray;
  122. import com.shavenpuppy.jglib.util.Util;
  123. /**
  124. * The entire game state during play
  125. */
  126. public class WormGameState extends GameState {
  127. private static final long serialVersionUID = 3L;
  128. private static final Rectangle TEMPBOUNDS = new Rectangle();
  129. private static final ArrayList<Entity> BUILD_COLLISIONS = new ArrayList<Entity>();
  130. public static final int GAME_MODE_CAMPAIGN = 0;
  131. public static final int GAME_MODE_ENDLESS = 1;
  132. public static final int GAME_MODE_SURVIVAL = 2;
  133. public static final int GAME_MODE_SANDBOX = 3;
  134. public static final int GAME_MODE_XMAS = 4;
  135. /** Mouse dragging speed */
  136. private static final float MOUSE_DRAG_SPEED = 2.0f;
  137. /** Absolute max map size in tiles */
  138. public static final int ABS_MAX_SIZE = 96;
  139. /** Absolute max map width in pixels */
  140. public static final int MAP_WIDTH = ABS_MAX_SIZE * MapRenderer.TILE_SIZE;
  141. /** Absolute max map height in pixels */
  142. public static final int MAP_HEIGHT = MAP_WIDTH;
  143. /** Map resolution - defines the grid size upon which we place buildings */
  144. public static final int MAP_RESOLUTION = MapRenderer.TILE_SIZE / 4;
  145. /** Maximum number of gidrahs allowed */
  146. public static final int MAX_GIDRAHS = 160;
  147. /** Number of levels in a world */
  148. public static final int LEVELS_IN_WORLD = 10;
  149. /** Number of worlds */
  150. public static final int NUM_WORLDS = 5;
  151. /** Number of levels */
  152. public static final int MAX_LEVELS = LEVELS_IN_WORLD * NUM_WORLDS;
  153. /** Duration in ticks that we wait after save is initiated */
  154. private static final int SAVE_DURATION = 45;
  155. /** End of level duration, in ticks */
  156. private static final int END_OF_LEVEL_DURATION = 30 * 60;
  157. /** Wait a few seconds delay, after a boss is killed */
  158. private static final int WAIT_A_FEW_SECONDS_DELAY = 5 * 60;
  159. /** Endgame duration, ticks */
  160. private static final int END_OF_GAME_DURATION = 420;
  161. /** After this interval (ticks), we warn about crystals that aren't being mined */
  162. private static final int UNMINED_WARNING_TIME = 600;
  163. /** After this interval (ticks) of nothing happening, we mention the fast forward button */
  164. private static final int INTERESTING_INTERVAL = 500;
  165. /** Right mouse button drag sensitivity */
  166. private static final int RMB_DRAG_SENSITIVITY = 3;
  167. /** Game configuration */
  168. private final GameConfiguration config;
  169. private static final ArrayList<Capacitor> TEMP_CAPACITORS = new ArrayList<Capacitor>();
  170. /*
  171. * Transient data
  172. */
  173. /** Save game stuff */
  174. private transient int saveTick;
  175. private transient boolean saving;
  176. /** Xmas reset */
  177. private transient boolean xmasReset;
  178. /**
  179. * Meta state - holds all the information that should be saved on a level-by-level basis, not the actual in-play state, in
  180. * a campaign game
  181. */
  182. public static class MetaState implements Serializable {
  183. private static final long serialVersionUID = 3L;
  184. private static final int MAX_RANDOM = 30;
  185. private static final int MAX_DEFAULT_RANDOM = 24;
  186. private static final int MAX_EXOTIC_RANDOM = 6;
  187. private static final int MAX_CRAPPY_RANDOM = 10;
  188. /** Game mode */
  189. private final int gameMode;
  190. /** A map of stats (Strings to Integers) */
  191. private final Map<String, Integer> stats = new HashMap<String, Integer>();
  192. /** Powerups: a Map of PowerupFeatures to Integers, which is the number of each of that sort of powerup the player has. Or null for 0. */
  193. private final Map<PowerupFeature, Integer> powerups = new HashMap<PowerupFeature, Integer>();
  194. /** Medals: a Map of MedalFeatures to Integers */
  195. private final Map<MedalFeature, Integer> medals = new HashMap<MedalFeature, Integer>();
  196. /** Total score for all medals */
  197. private int score;
  198. /** Current rank */
  199. private RankFeature rank;
  200. /** Which BuildingFeatures have been researched (Strings - from BuildingFeature.getID()) */
  201. private final HashSet<String> researched = new HashSet<String>();
  202. /** Money */
  203. private int money;
  204. /** Number of attempts at current level so far */
  205. private int attempts;
  206. /** Level index */
  207. private int level = 0;
  208. /** World */
  209. private WorldFeature world;
  210. /** Level */
  211. private LevelFeature levelFeature;
  212. /** Base difficulty, usually negative as player starts to lose */
  213. private float difficulty = 0.0f;
  214. /** Survival params */
  215. private SurvivalParams survivalParams;
  216. /** Sandbox params */
  217. private SandboxParams sandboxParams;
  218. /** Powerups shuffle */
  219. private ArrayList<Integer> shuffle;
  220. private ArrayList<Integer> crappyShuffle;
  221. private ArrayList<Integer> randomShuffle;
  222. private ArrayList<Integer> exoticShuffle;
  223. /** Resources */
  224. private Map<BuildingFeature, Integer> availableStock;
  225. public MetaState(int gameMode) {
  226. this.gameMode = gameMode;
  227. reset();
  228. }
  229. public void bonusMoney(int amount) {
  230. money += amount;
  231. }
  232. public int getAvailableStock(BuildingFeature bf) {
  233. if (availableStock == null) {
  234. availableStock = new HashMap<BuildingFeature, Integer>();
  235. }
  236. Integer ret = availableStock.get(bf);
  237. if (ret == null) {
  238. return 0;
  239. } else {
  240. return ret.intValue();
  241. }
  242. }
  243. public void addAvailableStock(BuildingFeature bf, int n) {
  244. int current = getAvailableStock(bf);
  245. if (current == -1) {
  246. // Hmm.
  247. assert false;
  248. return;
  249. }
  250. current = Math.min(bf.getMaxAvailable(), Math.max(0, current + n));
  251. availableStock.put(bf, new Integer(current));
  252. }
  253. void reset() {
  254. stats.clear();
  255. powerups.clear();
  256. medals.clear();
  257. researched.clear();
  258. if (availableStock == null) {
  259. availableStock = new HashMap<BuildingFeature, Integer>();
  260. } else {
  261. availableStock.clear();
  262. }
  263. switch (gameMode) {
  264. case GAME_MODE_SURVIVAL:
  265. if (survivalParams != null) {
  266. money = GameConfiguration.getInstance().getSurvivalInitialMoney()[survivalParams.getWorld().getIndex()];
  267. }
  268. break;
  269. case GAME_MODE_XMAS:
  270. money = GameConfiguration.getInstance().getXmasInitialMoney();
  271. break;
  272. default:
  273. money = GameConfiguration.getInstance().getNormalInitialMoney();
  274. break;
  275. }
  276. score = 0;
  277. rank = RankFeature.getRank(0);
  278. shuffle = new ArrayList<Integer>(MAX_RANDOM);
  279. randomShuffle = new ArrayList<Integer>(MAX_DEFAULT_RANDOM);
  280. crappyShuffle = new ArrayList<Integer>(MAX_CRAPPY_RANDOM);
  281. exoticShuffle = new ArrayList<Integer>(MAX_EXOTIC_RANDOM);
  282. }
  283. private int addStat(String stat, int n) {
  284. Integer current = stats.get(stat);
  285. if (current == null) {
  286. current = Integer.valueOf(n);
  287. } else {
  288. current = Integer.valueOf(current.intValue() + n);
  289. }
  290. stats.put(stat, current);
  291. return current.intValue();
  292. }
  293. private static String getPath(int level, int gameMode) {
  294. return Game.getPlayerDirectoryPrefix() + "metastate_" + level + "_"+gameMode+".ser";
  295. }
  296. public static MetaState load(int level, int gameMode) throws Exception {
  297. GameInputStream gis = null;
  298. ObjectInputStream ois = null;
  299. try {
  300. String path = getPath(level, gameMode);
  301. RoamingFile file = new RoamingFile(path);
  302. if (!file.exists()) {
  303. throw new FileNotFoundException("The save game file "+path+" was not found");
  304. }
  305. gis = new GameInputStream(path);
  306. ois = new ObjectInputStream(gis);
  307. MetaState ret = (MetaState) ois.readObject();
  308. Resources.dequeue();
  309. if (ret.shuffle == null || ret.exoticShuffle == null || ret.randomShuffle == null || ret.crappyShuffle == null) {
  310. ret.shuffle = new ArrayList<Integer>(MAX_RANDOM);
  311. ret.randomShuffle = new ArrayList<Integer>(MAX_DEFAULT_RANDOM);
  312. ret.crappyShuffle = new ArrayList<Integer>(MAX_CRAPPY_RANDOM);
  313. ret.exoticShuffle = new ArrayList<Integer>(MAX_EXOTIC_RANDOM);
  314. }
  315. if (ret.availableStock == null) {
  316. ret.availableStock = new HashMap<BuildingFeature, Integer>();
  317. }
  318. return ret;
  319. } finally {
  320. try {
  321. if (gis != null) {
  322. gis.close();
  323. }
  324. } catch (IOException e) {
  325. }
  326. }
  327. }
  328. public void save() throws Exception {
  329. GameOutputStream gos = null;
  330. ObjectOutputStream oos = null;
  331. try {
  332. gos = new GameOutputStream(getPath(level, gameMode));
  333. oos = new ObjectOutputStream(gos);
  334. oos.writeObject(this);
  335. oos.flush();
  336. gos.flush();
  337. } finally {
  338. try {
  339. gos.close();
  340. } catch (IOException e) {
  341. }
  342. }
  343. }
  344. public int getMoney() {
  345. return money;
  346. }
  347. public void bonusPowerup(PowerupFeature powerup, int amount) {
  348. Integer current = powerups.get(powerup);
  349. if (current == null) {
  350. current = Integer.valueOf(amount);
  351. } else {
  352. current = Integer.valueOf(current.intValue() + amount);
  353. }
  354. powerups.put(powerup, current);
  355. }
  356. private PowerupFeature getCrappyPowerup() {
  357. if (crappyShuffle.size() == 0) {
  358. for (int i = 0; i < MAX_CRAPPY_RANDOM; i ++) {
  359. crappyShuffle.add(new Integer(i));
  360. }
  361. Collections.shuffle(crappyShuffle);
  362. }
  363. Integer i = crappyShuffle.remove(crappyShuffle.size() - 1);
  364. switch (i.intValue()) {
  365. case 0:
  366. case 1:
  367. case 2:
  368. case 3:
  369. case 4:
  370. case 5:
  371. case 6:
  372. case 7:
  373. case 8:
  374. return MoneyPowerupFeature.getInstance(50);
  375. default:
  376. return getPowerup();
  377. }
  378. }
  379. private PowerupFeature getRandomPowerup() {
  380. if (randomShuffle.size() == 0) {
  381. for (int i = 0; i < MAX_DEFAULT_RANDOM; i ++) {
  382. randomShuffle.add(new Integer(i));
  383. }
  384. Collections.shuffle(randomShuffle);
  385. }
  386. Integer i = randomShuffle.remove(randomShuffle.size() - 1);
  387. switch (i.intValue()) {
  388. case 0:
  389. case 1:
  390. case 2:
  391. case 3:
  392. case 4:
  393. case 5:
  394. case 6:
  395. return MoneyPowerupFeature.getInstance(50);
  396. case 7:
  397. case 8:
  398. case 9:
  399. case 10:
  400. case 11:
  401. return MoneyPowerupFeature.getInstance(100);
  402. case 12:
  403. case 13:
  404. case 14:
  405. case 15:
  406. return MoneyPowerupFeature.getInstance(250);
  407. case 16:
  408. case 17:
  409. case 18:
  410. return MoneyPowerupFeature.getInstance(500);
  411. case 19:
  412. case 20:
  413. case 21:
  414. return getPowerup();
  415. default:
  416. return getExoticPowerup();
  417. }
  418. }
  419. /**
  420. * @return an exotic powerup
  421. */
  422. private PowerupFeature getExoticPowerup() {
  423. if (gameMode == GAME_MODE_SURVIVAL || gameMode == GAME_MODE_XMAS || level < 4) {
  424. return getPowerup();
  425. }
  426. PowerupFeature ret = null;
  427. do {
  428. if (exoticShuffle.size() == 0) {
  429. for (int i = 0; i < MAX_EXOTIC_RANDOM; i ++) {
  430. exoticShuffle.add(new Integer(i));
  431. }
  432. Collections.shuffle(exoticShuffle);
  433. }
  434. Integer i = exoticShuffle.remove(exoticShuffle.size() - 1);
  435. WormGameState gameState = Worm.getGameState();
  436. switch (i.intValue()) {
  437. case 0:
  438. if (gameState.isResearched(ResearchFeature.BATTERY)) {
  439. ret = BatteryPowerupFeature.getInstance();
  440. } else {
  441. ret = getCrappyPowerup();
  442. }
  443. break;
  444. case 1:
  445. if (gameState.isResearched(ResearchFeature.CAPACITOR)) {
  446. ret = CapacitorPowerupFeature.getInstance();
  447. } else {
  448. ret = getCrappyPowerup();
  449. }
  450. break;
  451. case 2:
  452. if (gameState.isResearched(ResearchFeature.COOLINGTOWER)) {
  453. ret = CoolingTowerPowerupFeature.getInstance();
  454. } else {
  455. ret = getCrappyPowerup();
  456. }
  457. break;
  458. case 3:
  459. if (gameState.isResearched(ResearchFeature.REACTOR)) {
  460. ret = ReactorPowerupFeature.getInstance();
  461. } else {
  462. ret = getCrappyPowerup();
  463. }
  464. break;
  465. case 4:
  466. if (gameState.isResearched(ResearchFeature.SHIELDGENERATOR)) {
  467. ret = ShieldGeneratorPowerupFeature.getInstance();
  468. } else {
  469. ret = getCrappyPowerup();
  470. }
  471. break;
  472. case 5:
  473. if (gameState.isResearched(ResearchFeature.SCANNER)) {
  474. ret = ScannerPowerupFeature.getInstance();
  475. } else {
  476. ret = getCrappyPowerup();
  477. }
  478. break;
  479. default:
  480. assert false;
  481. return null;
  482. }
  483. } while (!ret.isAvailable());
  484. return ret;
  485. }
  486. /**
  487. * @return a non-exotic powerup
  488. */
  489. private PowerupFeature getPowerup() {
  490. if (shuffle.size() == 0) {
  491. for (int i = 0; i < MAX_RANDOM; i ++) {
  492. shuffle.add(new Integer(i));
  493. }
  494. Collections.shuffle(shuffle);
  495. }
  496. Integer i = shuffle.remove(shuffle.size() - 1);
  497. WormGameState gameState = Worm.getGameState();
  498. switch (i.intValue()) {
  499. case 0:
  500. case 1:
  501. case 2:
  502. return BezerkPowerupFeature.getInstance();
  503. case 3:
  504. case 4:
  505. case 5:
  506. return RepairPowerupFeature.getInstance();
  507. case 6:
  508. case 7:
  509. case 8:
  510. return ShieldPowerupFeature.getInstance();
  511. case 9:
  512. case 10:
  513. case 11:
  514. return SmartbombPowerupFeature.getInstance();
  515. case 12:
  516. case 13:
  517. case 14:
  518. return FreezePowerupFeature.getInstance();
  519. case 15:
  520. case 16:
  521. case 17:
  522. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.MINES)) {
  523. return ResourcePowerupFeature.getInstance("mines.powerup");
  524. } else {
  525. return getPowerup(); // Recurse
  526. }
  527. case 18:
  528. case 19:
  529. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.CLUSTERMINES)) {
  530. return ResourcePowerupFeature.getInstance("clustermines.powerup");
  531. } else {
  532. return getPowerup(); // Recurse
  533. }
  534. case 20:
  535. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.BLASTMINES)) {
  536. return ResourcePowerupFeature.getInstance("blastmines.powerup");
  537. } else {
  538. return getPowerup(); // Recurse
  539. }
  540. case 21:
  541. case 22:
  542. case 23:
  543. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.CONCRETE)) {
  544. return ResourcePowerupFeature.getInstance("concrete.powerup");
  545. } else {
  546. return getPowerup(); // Recurse
  547. }
  548. case 24:
  549. case 25:
  550. case 26:
  551. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.STEEL)) {
  552. return ResourcePowerupFeature.getInstance("steel.powerup");
  553. } else {
  554. return getPowerup(); // Recurse
  555. }
  556. case 27:
  557. case 28:
  558. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.TITANIUM)) {
  559. return ResourcePowerupFeature.getInstance("titanium.powerup");
  560. } else {
  561. return getPowerup(); // Recurse
  562. }
  563. case 29:
  564. if ((gameState.getGameMode() == WormGameState.GAME_MODE_SURVIVAL || gameState.getGameMode() == WormGameState.GAME_MODE_XMAS) && gameState.isResearched(ResearchFeature.NANOMESH)) {
  565. return ResourcePowerupFeature.getInstance("nanomesh.powerup");
  566. } else {
  567. return getPowerup(); // Recurse
  568. }
  569. default:
  570. assert false;
  571. return null;
  572. }
  573. }
  574. }
  575. /*
  576. * Game state
  577. */
  578. /** Current metastate */
  579. private MetaState metaState;
  580. /** Current map */
  581. private GameMap map;
  582. /** Spawn points */
  583. private final ArrayList<SpawnPoint> spawnPoints = new ArrayList<SpawnPoint>();
  584. /** Debugging aid */
  585. private transient boolean forceDifficulty;
  586. /** Current total difficulty */
  587. private float currentDifficulty;
  588. /** Powerup difficulty */
  589. private float powerupDifficulty;
  590. /** Whether to begin the level */
  591. private boolean beginLevel;
  592. /** Money at start of level */
  593. private int startingMoney;
  594. /** Factories */
  595. private int factories, totalFactories;
  596. /** Warehouses */
  597. private int warehouses;
  598. /** Shield generators */
  599. private int shieldGenerators;
  600. /** Autoloaders */
  601. private int autoloaders;
  602. /** Spawners */
  603. private int spawners;
  604. /** Are we alive? */
  605. private boolean alive = true;
  606. /** Freeze tick */
  607. private int freezeTick;
  608. /** Shield tick */
  609. private int shieldTick;
  610. /** Capacitor boost */
  611. private int capacitorBoost;
  612. /** Cooling boost */
  613. private int coolingBoost;
  614. /** Battery boost */
  615. private int batteryBoost;
  616. /** Reactor boost */
  617. private int reactorBoost;
  618. /** Scanner boost */
  619. private int scannerBoost;
  620. /** Shield boost: extra hitpoints for all buildings */
  621. private int shieldBoost;
  622. /** Level duration */
  623. private int levelDuration;
  624. /** Aliens spawned */
  625. private int aliensSpawnedValue, aliensSpawnedAtLevelEnd;
  626. /** Aliens vanquished value */
  627. private int aliensVanquishedValue, aliensVanquishedSinceEndOfLevel;
  628. /** Last story index */
  629. private int currentStoryIndex;
  630. /** Something interesting happened this many ticks ago */
  631. private int somethingInterestingHappenedTick;
  632. /** Last hovered entity */
  633. private Entity lastHovered;
  634. /** Total value of buildings created on this level */
  635. private int valueOfBuiltBuildings;
  636. /** Number of buildings made this level */
  637. private int numberOfBuildingsMade;
  638. /** Total value of buildings destroyed by gidrahs on this level */
  639. private int valueOfDestroyedBuildings;
  640. /** Number of buildings destroyed this level */
  641. private int numberOfBuildingsDestroyed;
  642. /** Have any buildings been damaged on this level? (for Pristine medal) */
  643. private boolean anyDamaged;
  644. /** Number of crystal buildings */
  645. private int crystals, initialCrystals;
  646. /** The base */
  647. private Building base;
  648. /** Entities */
  649. private final ArrayList<Entity> entities = new ArrayList<Entity>();
  650. /** All the gidrahs currently on the level */
  651. private final ArrayList<Gidrah> gidrahs = new ArrayList<Gidrah>();
  652. /** All the bosses currently on the level */
  653. private final ArrayList<Gidrah> bosses = new ArrayList<Gidrah>();
  654. /** All the units currently on the level */
  655. private final ArrayList<Unit> units = new ArrayList<Unit>();
  656. /** All the buildings currently on the level */
  657. private final ArrayList<Building> buildings = new ArrayList<Building>();
  658. /** All the saucers currently on the level */
  659. private final ArrayList<Saucer> saucers = new ArrayList<Saucer>();
  660. /** Medals earned during play this level */
  661. private final Set<MedalFeature> medalsThisLevel = new HashSet<MedalFeature>();
  662. /** Armed capacitors */
  663. private final ArrayList<Capacitor> armedCapacitors = new ArrayList<Capacitor>();
  664. /** Event hint seq number */
  665. private int eventHintSeq;
  666. /** Unmined crystals warning */
  667. private int unminedTick;
  668. /** Game state interface */
  669. private final GameStateInterface gameStateInterface = new GameStateInterface() {
  670. private static final long serialVersionUID = 1L;
  671. @Override
  672. public void addToGidrahs(Gidrah gidrah) {
  673. gidrahs.add(gidrah);
  674. if (gidrah.getFeature().isBoss()) {
  675. bosses.add(gidrah);
  676. addStat(Stats.BOSSES_SPAWNED, 1);
  677. }
  678. if (gidrah.getFeature().isAngry()) {
  679. addStat(Stats.ANGRY_SPAWNED, 1);
  680. }
  681. if (gidrah.getFeature().isGidlet()) {
  682. addStat(Stats.GIDLETS_SPAWNED, 1);
  683. }
  684. aliensSpawnedValue += gidrah.getFeature().getValue();
  685. addStat(Stats.ALIENS_SPAWNED, 1);
  686. }
  687. @Override
  688. public void addToUnits(Unit unit) {
  689. units.add(unit);
  690. }
  691. @Override
  692. public void addToBuildings(Building building) {
  693. buildings.add(building);
  694. if (building.isCity()) {
  695. base = building;
  696. }
  697. onSomethingInterestingHappened();
  698. }
  699. @Override
  700. public void addToSaucers(Saucer saucer) {
  701. saucers.add(saucer);
  702. onSomethingInterestingHappened();
  703. }
  704. @Override
  705. public void removeFromBuildings(Building building) {
  706. buildings.remove(building);
  707. onSomethingInterestingHappened();
  708. }
  709. @Override
  710. public void removeFromGidrahs(Gidrah gidrah) {
  711. gidrahs.remove(gidrah);
  712. aliensVanquishedValue += gidrah.getFeature().getValue();
  713. if (!isLevelActive()) {
  714. aliensVanquishedSinceEndOfLevel += gidrah.getFeature().getValue();
  715. }
  716. onSomethingInterestingHappened();
  717. }
  718. @Override
  719. public void removeFromUnits(Unit unit) {
  720. units.remove(unit);
  721. }
  722. @Override
  723. public void removeFromSaucers(Saucer saucer) {
  724. saucers.remove(saucer);
  725. }
  726. @Override
  727. public void buffBatteries() {
  728. batteryBoost ++;
  729. onSomethingInterestingHappened();
  730. }
  731. @Override
  732. public void buffScanners() {
  733. scannerBoost ++;
  734. onSomethingInterestingHappened();
  735. }
  736. @Override
  737. public void buffCapacitors() {
  738. capacitorBoost ++;
  739. onSomethingInterestingHappened();
  740. }
  741. @Override
  742. public void buffCoolingTowers() {
  743. coolingBoost ++;
  744. onSomethingInterestingHappened();
  745. }
  746. @Override
  747. public void buffReactors() {
  748. reactorBoost ++;
  749. onSomethingInterestingHappened();
  750. }
  751. @Override
  752. public void buffShieldGenerators() {
  753. shieldBoost ++;
  754. for (Building building : buildings) {
  755. building.addShieldBoost();
  756. }
  757. onSomethingInterestingHappened();
  758. }
  759. @Override
  760. public void repairFully() {
  761. for (Building building : buildings) {
  762. building.repairFully();
  763. }
  764. if (metaState.addStat(Stats.REPAIRS_USED, 1) == 5) {
  765. awardMedal(Medals.JIMLL_FIX_IT);
  766. }
  767. onSomethingInterestingHappened();
  768. }
  769. @Override
  770. public void setSmartbombMode() {
  771. setBuilding(null);
  772. mode = Mode.MODE_SMARTBOMB;
  773. onSomethingInterestingHappened();
  774. }
  775. @Override
  776. public void freeze(int duration) {
  777. // Freeze all the gidrahs!
  778. freezeTick += duration;
  779. GameScreen.instance.onFreezeTimerIncreased(freezeTick);
  780. if (metaState.addStat(Stats.FREEZES_USED, 1) == 5) {
  781. awardMedal(Medals.TOILET_BREAK);
  782. }
  783. onSomethingInterestingHappened();
  784. }
  785. @Override
  786. public void bezerk(int duration) {
  787. bezerkTick += duration;
  788. GameScreen.getInstance().onBezerkTimerIncreased(bezerkTick);
  789. // Bezerk turrets reload instantly
  790. for (Building building : buildings) {
  791. building.onBezerk();
  792. }
  793. if (metaState.addStat(Stats.BEZERKS_USED, 1) == 5) {
  794. awardMedal(Medals.SHORT_TEMPERED);
  795. }
  796. onSomethingInterestingHappened();
  797. }
  798. @Override
  799. public void invulnerable(int duration) {
  800. shieldTick += duration;
  801. GameScreen.getInstance().onShieldTimerIncreased(shieldTick);
  802. if (metaState.addStat(Stats.SHIELDS_USED, 1) == 5) {
  803. awardMedal(Medals.BATFINK);
  804. }
  805. onSomethingInterestingHappened();
  806. }
  807. @Override
  808. public void addMoney(int delta) {
  809. WormGameState.this.addMoney(delta);
  810. onSomethingInterestingHappened();
  811. }
  812. @Override
  813. public int getAvailableStock(BuildingFeature bf) {
  814. return WormGameState.this.getAvailableStock(bf);
  815. }
  816. @Override
  817. public void addAvailableStock(BuildingFeature bf, int n) {
  818. WormGameState.this.addAvailableStock(bf, n);
  819. }
  820. };
  821. /** Ticks */
  822. private int tick, totalTicks, saucerTick, levelTick, bezerkTick, survivalSpawnPointTick, crystalTick;
  823. /** Next saucer at... */
  824. private int nextSaucer;
  825. /** Contains a shuffle bag of crystal sizes (Integers), valued between 1 and 3 inclusive */
  826. private final ArrayList<Integer> survivalCrystals = new ArrayList<Integer>();
  827. /** Survival mode: next boss at this tick */
  828. private int survivalNextBossTick;
  829. /** Survival mode: next crystal at this tick */
  830. private int survivalNextCrystalTick;
  831. /** Survival mode: next survival spawn point count increase at this tick */
  832. private int survivalNextSpawnPointTick;
  833. /** Survival mode: survival spawn point interval */
  834. private int survivalSpawnPointInterval;
  835. /** Number of survival spawnpoints that there should be */
  836. private int numSurvivalSpawnPoints;
  837. /** Survival mode gidrah unlock levels */
  838. private final int[] survivalGidrahUnlock = new int[] {1, 0, 0, 0};
  839. /** Survival mode gidrah unlock levels */
  840. private final int[] survivalGidrahUnlockNext = new int[] {0, 0, 0, 0};
  841. /** Survival mode gidrah kill counts */
  842. private final int[] survivalGidrahKills = new int[] {0, 0, 0, 0};
  843. /** Whether mouse button was down last tick */
  844. private transient boolean leftMouseWasDown, rightMouseWasDown;
  845. /** Wait for mouse button to be released */
  846. private transient boolean waitForMouse;
  847. /** Building we want to build */
  848. private transient BuildingFeature building;
  849. /** Build entity */
  850. private transient Building buildEntity;
  851. /** Things we've clicked on */
  852. private transient ArrayList<Entity> clicked;
  853. /** Back button runnable */
  854. private transient Runnable backButtonRunnable;
  855. /** RMB scroll */
  856. private transient boolean rmbScroll;
  857. /** Unmined crystals */
  858. private final ArrayList<Building> unminedCrystalList = new ArrayList<Building>();
  859. /** Game phase */
  860. private int phase;
  861. private static final int PHASE_NORMAL = 0;
  862. private static final int PHASE_END_OF_GAME = 1;
  863. private static final int PHASE_WAIT_FOR_GIDRAHS = 2;
  864. private static final int PHASE_WAIT_A_FEW_SECONDS = 3;
  865. /** Rush mode */
  866. private boolean rush;
  867. /** Whether we've expired the demo */
  868. private boolean demoExpired;
  869. /** Survival boss counter (earth, moon, mars, etc) */
  870. private int survivalBoss;
  871. /** Survival boss ticker */
  872. private int survivalBossTick;
  873. /** Input mode (see {@link Mode} */
  874. private int mode;
  875. /** Scroll origin */
  876. private transient int scrollOriginX, scrollOriginY;
  877. /** Are we in range of a capacitor? */
  878. private transient boolean capacitorRange;
  879. /** RMB drag sensor */
  880. private transient int rmbDragSensor;
  881. /** Hint map maps HintFeatures to Integers, which is the number of times that hint has been shown in sequence */
  882. private final Map<HintFeature, Integer> hintMap = new HashMap<HintFeature, Integer>();
  883. /** Stories for this level */
  884. private transient List<StoryFeature> currentStories;
  885. /** Survival init params */
  886. private SurvivalParams survivalParams;
  887. /** Sandbox init params */
  888. private SandboxParams sandboxParams;
  889. /** Suppress medals display on game screen */
  890. private boolean suppressMedals;
  891. /** Awesome awarded this level */
  892. private boolean awesome;
  893. /** Number of attempts */
  894. private transient int attempts;
  895. /**
  896. * Gidrah spawn point
  897. */
  898. private class SpawnPoint implements Serializable {
  899. private static final long serialVersionUID = 1L;
  900. int tileX, tileY, type, subType;
  901. final boolean edge;
  902. /** Gidrah generator tick */
  903. private int gidrahTick;
  904. /** Waiting for spawn point to clear */
  905. private int waitingToSpawn;
  906. /** Gidrah position indicator */
  907. private int gidrahsSpawned;
  908. /** Spawn boss */
  909. private GidrahFeature boss;
  910. /** Stop spawning */
  911. private boolean stop;
  912. /** More aliens please */
  913. private int moreAliens;
  914. /** Effect */
  915. private transient ArrowEffect arrowEffect;
  916. SpawnPoint(int tileX, int tileY, int type, boolean edge) {
  917. this.tileX = tileX;
  918. this.tileY = tileY;
  919. this.type = type;
  920. this.edge = edge;
  921. gidrahTick = Util.random(getInitialLevelDelay(map.getWidth(), map.getHeight()), getInitialLevelDelay(map.getWidth(), map.getHeight()) + 600);
  922. waitingToSpawn = 0;
  923. if (getGameMode() == GAME_MODE_SURVIVAL) {
  924. chooseType();
  925. }
  926. reinit();
  927. }
  928. void spawnBoss(GidrahFeature boss) {
  929. this.boss = boss;
  930. waitingToSpawn = 0;
  931. }
  932. @Override
  933. public boolean equals(Object obj) {
  934. return obj != null && obj instanceof SpawnPoint && ((SpawnPoint) obj).tileX == tileX && ((SpawnPoint) obj).tileY == tileY && ((SpawnPoint) obj).type == type;
  935. }
  936. @Override
  937. public int hashCode() {
  938. return (tileX ^ tileY << 16) * (type + 1);
  939. }
  940. /**
  941. * Tick the spawnpoint. Returns true if still active
  942. * @return
  943. */
  944. boolean tick() {
  945. if (!base.isAlive()) {
  946. // No base?
  947. return !stop;
  948. }
  949. if (getGameMode() == GAME_MODE_XMAS) {
  950. if (levelTick % Xmas.BOSS_INTERVAL == 0 && levelTick > 0) {
  951. // Every 5 minutes spawn a boss
  952. type = 2;
  953. } else if (levelTick % Xmas.ANGRY_INTERVAL == 0 && levelTick > 0 && type == 0) {
  954. type = 1;
  955. }
  956. }
  957. if (gidrahs.size() >= MAX_GIDRAHS) {
  958. return !stop;
  959. }
  960. if (freezeTick > 0) {
  961. return !stop;
  962. }
  963. if (getGameMode() != GAME_MODE_SURVIVAL) {
  964. if (crystals == 0 && !isLevelActive()) {
  965. return !stop;
  966. }
  967. }
  968. if (waitingToSpawn > 0) {
  969. waitingToSpawn --;
  970. if (waitingToSpawn == 0) {
  971. spawnGidrah();
  972. }
  973. } else {
  974. if (stop) {
  975. return false;
  976. }
  977. if (boss != null) {
  978. spawnGidrah();
  979. stop = true;
  980. arrowEffect.remove();
  981. arrowEffect = null;
  982. } else if (--gidrahTick <= 0) {
  983. // Get next item
  984. gidrahsSpawned ++;
  985. // xmas mode: continuous stream
  986. if (getGameMode() == GAME_MODE_XMAS) {
  987. spawnGidrah();
  988. } else {
  989. if (gidrahsSpawned >= getWaveLength()) {
  990. // We've got to the end. Go into a very long delay and start again at the beginning
  991. gidrahTick = getLongGidrahDelay();
  992. gidrahsSpawned = 0;
  993. // In Survival we move the spawnpoint around, and choose a new alien to spawn
  994. if (getGameMode() == GAME_MODE_SURVIVAL) {
  995. moveSpawnPoint();
  996. } else {
  997. moreAliens = Math.min(3, moreAliens + 1);
  998. }
  999. } else {
  1000. spawnGidrah();
  1001. }
  1002. }
  1003. }
  1004. }
  1005. return true;
  1006. }
  1007. void moveSpawnPoint() {
  1008. int ox = 0, oy = 0;
  1009. if (tileX == -1) {
  1010. tileY += Util.random(-2, 2);
  1011. ox = 1;
  1012. oy = 0;
  1013. } else if (tileX == getMap().getWidth()) {
  1014. tileY += Util.random(-2, 2);
  1015. ox = 1;
  1016. oy = 0;
  1017. } else if (tileY == -1) {
  1018. tileX += Util.random(-2, -2);
  1019. ox = 0;
  1020. oy = 1;
  1021. } else if (tileY == getMap().getHeight()) {
  1022. tileX += Util.random(-2, -2);
  1023. ox = 0;
  1024. oy = -1;
  1025. }
  1026. for (int z = 0; z < GameMap.LAYERS; z ++) {
  1027. Tile t = getMap().getTile(tileX + ox, tileY + oy, z);
  1028. if (t == null || t.isImpassable() || t.isSolid()) {
  1029. // Zap!
  1030. stop = true;
  1031. remove();
  1032. return;
  1033. }
  1034. }
  1035. arrowEffect.setSpawnLocation(tileX, tileY);
  1036. // Choose a new type
  1037. chooseType();
  1038. }
  1039. void chooseType() {
  1040. assert getGameMode() == GAME_MODE_SURVIVAL;
  1041. int maxType = 0;
  1042. for (int i = survivalGidrahUnlock.length; -- i >= 0; ) {
  1043. if (survivalGidrahUnlock[i] != 0) {
  1044. maxType = i;
  1045. break;
  1046. }
  1047. }
  1048. type = Util.random(0, maxType);
  1049. subType = Util.random(0, Math.min(getNumTypes(type) - 1, survivalGidrahUnlock[type] - 1));
  1050. }
  1051. int getNumTypes(int type) {
  1052. assert getGameMode() == GAME_MODE_SURVIVAL;
  1053. return Res.getSurvivalGidrahs(type).getNumResources();
  1054. }
  1055. int getWaveLength() {
  1056. switch (metaState.gameMode) {
  1057. case GAME_MODE_CAMPAIGN:
  1058. return getLevelInWorld() / 3 + 10 + moreAliens - Math.min(9, type * 3);
  1059. case GAME_MODE_ENDLESS:
  1060. return metaState.level / LEVELS_IN_WORLD + 10 + moreAliens - Math.min(9, type * 3);
  1061. case GAME_MODE_SURVIVAL:
  1062. return config.getSurvivalWaveLength() - Math.max(0, type * 3 - Math.max(0, getLevelTick() - config.getSurvivalWaveLengthTimeOffset()) / config.getSurvivalWaveLengthTimeAdjust());
  1063. case GAME_MODE_XMAS:
  1064. assert false;
  1065. return -1; // Constant stream
  1066. case GAME_MODE_SANDBOX:
  1067. return metaState.level / LEVELS_IN_WORLD + 10 + moreAliens - Math.min(9, type * 3);
  1068. default:
  1069. assert false : "Unknown game mode " + metaState.gameMode;
  1070. return 1;
  1071. }
  1072. }
  1073. void spawnGidrah() {
  1074. if (map.isOccupied(tileX, tileY)) {
  1075. if (metaState.gameMode == GAME_MODE_XMAS) {
  1076. waitingToSpawn = 1;
  1077. } else {
  1078. waitingToSpawn = config.getSpawnDelay();
  1079. }
  1080. } else {
  1081. waitingToSpawn = 0;
  1082. if (boss != null) {
  1083. boss.spawn(GameScreen.getInstance(), tileX, tileY, 0);
  1084. boss = null;
  1085. gidrahTick = Util.random(getInitialLevelDelay(map.getWidth(), map.getHeight()), getInitialLevelDelay(map.getWidth(), map.getHeight()) + 600);
  1086. } else {
  1087. GidrahFeature gf;
  1088. if (getGameMode() == GAME_MODE_XMAS) {
  1089. // Choose gidrah based on level duration so far
  1090. float ratio = levelTick == getLevelDuration() ? 0.99999f : (float) levelTick / (float) getLevelDuration();
  1091. int gidIndex = Util.random(0, (int) LinearInterpolator.instance.interpolate(0.0f, Res.getXmasGidrahs().getNumResources(), ratio));
  1092. switch (type) {
  1093. case 0: // Ordinary gid
  1094. gf = (GidrahFeature) Res.getXmasGidrahs().getResource(gidIndex);
  1095. break;
  1096. case 1: // Angry gid
  1097. gf = (GidrahFeature) Res.getXmasAngryGidrahs().getResource(gidIndex);
  1098. type = 0;
  1099. break;
  1100. case 2: // Boss
  1101. gf = Res.getXmasBoss(survivalBoss ++);
  1102. doBossWarning(1);
  1103. // Stop when last boss made
  1104. if (survivalBoss == Xmas.MAX_BOSSES) {
  1105. stop = true;
  1106. }
  1107. type = 0;
  1108. break;
  1109. default:
  1110. assert false;
  1111. return;
  1112. }
  1113. } else if ((getGameMode() == GAME_MODE_SURVIVAL || metaState.level > 2) && gidrahsSpawned == 1) {
  1114. // Maybe an angry gidrah at the head of the column?
  1115. if (Util.random() < getDifficulty()) {
  1116. gf = getAngryGidrah(type, subType);
  1117. } else {
  1118. gf = getGidrah(type, subType);
  1119. }
  1120. } else {
  1121. gf = getGidrah(type, subType);
  1122. }
  1123. gf.spawn(GameScreen.getInstance(), tileX, tileY, type);
  1124. gidrahTick = 1;
  1125. }
  1126. }
  1127. }
  1128. int getLongGidrahDelay() {
  1129. switch (getGameMode()) {
  1130. case GAME_MODE_SURVIVAL:
  1131. return config.getLongDelay() + (int) LinearInterpolator.instance.interpolate(config.getSpawnpointDelayAdjust() * 5.0f, 0.0f, getDifficulty());
  1132. case GAME_MODE_XMAS:
  1133. return 0; // No delay
  1134. default:
  1135. return getShortGidrahDelay() * 2;
  1136. }
  1137. }
  1138. int getShortGidrahDelay() {
  1139. return config.getLongDelay() + Math.max(0, spawnPoints.size() - metaState.level / LEVELS_IN_WORLD) * config.getSpawnpointDelayAdjust();
  1140. }
  1141. void reinit() {
  1142. arrowEffect = new ArrowEffect(tileX, tileY);
  1143. arrowEffect.spawn(GameScreen.getInstance());
  1144. arrowEffect.setOffset(null);
  1145. }
  1146. void remove() {
  1147. if (arrowEffect != null) {
  1148. arrowEffect.remove();
  1149. arrowEffect = null;
  1150. }
  1151. map.clearItem(tileX, tileY);
  1152. stop = true;
  1153. }
  1154. }
  1155. /**
  1156. * C'tor
  1157. */
  1158. public WormGameState(int mode) {
  1159. this.config = GameConfiguration.getInstance();
  1160. this.metaState = new MetaState(mode);
  1161. reset();
  1162. }
  1163. private GidrahFeature getAngryGidrah(int type, int subType) {
  1164. assert getGameMode() != GAME_MODE_XMAS;
  1165. switch (getGameMode()) {
  1166. case GAME_MODE_SURVIVAL:
  1167. return (GidrahFeature) Res.getSurvivalAngryGidrahs(type).getResource(subType);
  1168. default:
  1169. return getLevelFeature().getAngryGidrah(type);
  1170. }
  1171. }
  1172. private GidrahFeature getGidrah(int type, int subType) {
  1173. assert getGameMode() != GAME_MODE_XMAS;
  1174. switch (getGameMode()) {
  1175. case GAME_MODE_SURVIVAL:
  1176. return (GidrahFeature) Res.getSurvivalGidrahs(type).getResource(subType);
  1177. default:
  1178. return getLevelFeature().getGidrah(type);
  1179. }
  1180. }
  1181. /**
  1182. * Repair all the buildings by one shield
  1183. */
  1184. public void repair() {
  1185. for (Iterator<Building> i = buildings.iterator(); i.hasNext(); ) {
  1186. i.next().repair();
  1187. }
  1188. }
  1189. /**
  1190. * Get the player's money
  1191. * @return int
  1192. */
  1193. public int getMoney() {
  1194. return metaState.money;
  1195. }
  1196. /**
  1197. * Adjust the player's money
  1198. * @param moneyDelta The amount of money to adjust the player's purse by
  1199. */
  1200. public void addMoney(int moneyDelta) {
  1201. metaState.money += moneyDelta;
  1202. if (metaState.money < 0) {
  1203. metaState.money = 0;
  1204. }
  1205. }
  1206. /**
  1207. * Add an entity to the game
  1208. * @param entity
  1209. */
  1210. public void addEntity(Entity entity) {
  1211. entities.add(entity);
  1212. // Now get the entity to add itself to the appropriate list
  1213. entity.addToGameState(gameStateInterface);
  1214. }
  1215. /**
  1216. * Add a powerup
  1217. * @param powerup
  1218. * @param doSound TODO
  1219. */
  1220. public void addPowerup(PowerupFeature powerup, boolean doSound) {
  1221. Integer num = metaState.powerups.get(powerup);
  1222. int n;
  1223. if (num == null) {
  1224. n = 1;
  1225. } else {
  1226. n = num.intValue() + 1;
  1227. }
  1228. metaState.powerups.put(powerup, new Integer(n));
  1229. if (doSound && powerup != null) {
  1230. Game.allocateSound(powerup.getCollectSound());
  1231. }
  1232. calcPowerupDifficulty();
  1233. GameScreen.onPowerupsUpdated();
  1234. }
  1235. private void calcPowerupDifficulty() {
  1236. powerupDifficulty = 0.0f;
  1237. for (PowerupFeature pf : metaState.powerups.keySet()) {
  1238. powerupDifficulty += pf.getDifficulty();
  1239. }
  1240. }
  1241. /**
  1242. * Remove a powerup
  1243. * @param powerup
  1244. */
  1245. public void removePowerup(PowerupFeature powerup) {
  1246. Integer num = metaState.powerups.get(powerup);
  1247. int n;
  1248. if (num == null) {
  1249. assert false;
  1250. return;
  1251. } else {
  1252. n = num.intValue() - 1;
  1253. }
  1254. if (n == 0) {
  1255. metaState.powerups.remove(powerup);
  1256. } else {
  1257. metaState.powerups.put(powerup, new Integer(n));
  1258. }
  1259. calcPowerupDifficulty();
  1260. GameScreen.onPowerupsUpdated();
  1261. }
  1262. /**
  1263. * Return the number of powerups of this type the player has
  1264. * @param powerup
  1265. * @return int
  1266. */
  1267. public int getNumPowerups(PowerupFeature powerup) {
  1268. Integer num = metaState.powerups.get(powerup);
  1269. if (num == null) {
  1270. return 0;
  1271. } else {
  1272. return num.intValue();
  1273. }
  1274. }
  1275. /**
  1276. * Use a powerup
  1277. * @param powerup
  1278. */
  1279. public void usePowerup(PowerupFeature powerup) {
  1280. if (getNumPowerups(powerup) == 0) {
  1281. return;
  1282. }
  1283. removePowerup(powerup);
  1284. Game.allocateSound(powerup.getCollectSound());
  1285. powerup.activate(gameStateInterface);
  1286. }
  1287. /**
  1288. * Remove an entity from the game
  1289. * @param entity
  1290. */
  1291. public void removeEntity(Entity entity) {
  1292. // We don't actually remove the entity from entities list here; that occurs
  1293. // for us in checkCollisions().
  1294. // Now get the entity to remove itself from the appropriate list
  1295. entity.removeFromGameState(gameStateInterface);
  1296. }
  1297. /**
  1298. * Cleanup
  1299. */
  1300. public void cleanup() {
  1301. for (int i = 0; i < entities.size(); i ++) {
  1302. Entity e = entities.get(i);
  1303. if (e.isActive()) {
  1304. e.remove();
  1305. }
  1306. }
  1307. Entity.reset();
  1308. System.gc();
  1309. }
  1310. /**
  1311. * Adjust the number of factories in play
  1312. * @param n
  1313. */
  1314. public void addFactories(int n) {
  1315. factories += n;
  1316. if (n > 0) {
  1317. totalFactories += n;
  1318. }
  1319. beginLevel = true;
  1320. assert factories >= 0;
  1321. }
  1322. public void addCrystals(int n) {
  1323. crystals += n;
  1324. if (crystals == 0 && getGameMode() != GAME_MODE_SURVIVAL && getGameMode() != GAME_MODE_XMAS) {
  1325. // Mined all crystals before timer expires?
  1326. if (levelTick < getLevelDuration() && beginLevel) {
  1327. awardMedal(Medals.EFFICIENT);
  1328. }
  1329. }
  1330. }
  1331. public void addInitialCrystals(int n) {
  1332. initialCrystals += n;
  1333. }
  1334. /**
  1335. * @return the number of factories in play
  1336. */
  1337. public int getFactories() {
  1338. return factories;
  1339. }
  1340. public int getAutoloaders() {
  1341. return autoloaders;
  1342. }
  1343. public int getWarehouses() {
  1344. return warehouses;
  1345. }
  1346. public void addWarehouses(int n) {
  1347. warehouses += n;
  1348. }
  1349. public int getShieldGenerators() {
  1350. return shieldGenerators;
  1351. }
  1352. public void addShieldGenerators(int n) {
  1353. shieldGenerators += n;
  1354. }
  1355. public void addAutoloaders(int n) {
  1356. autoloaders += n;
  1357. }
  1358. /**
  1359. * Adjust the number of spawners in play
  1360. * @param n
  1361. */
  1362. public void addSpawners(int n) {
  1363. spawners += n;
  1364. beginLevel = true;
  1365. assert spawners >= 0;
  1366. }
  1367. /**
  1368. * @return the number of spawners in play
  1369. */
  1370. public int getSpawners() {
  1371. return spawners;
  1372. }
  1373. /**
  1374. * @return Returns the tick.
  1375. */
  1376. public int getTick() {
  1377. return tick;
  1378. }
  1379. /**
  1380. * @return Returns the totalTicks.
  1381. */
  1382. public int getTotalTicks() {
  1383. return totalTicks;
  1384. }
  1385. /**
  1386. * Reset the total tick count
  1387. */
  1388. public void resetTotalTicks() {
  1389. totalTicks = 0;
  1390. }
  1391. /**
  1392. * Is the player still "alive"?
  1393. * @return true if the player hasn't lost a base
  1394. */
  1395. public boolean isAlive() {
  1396. return alive;
  1397. }
  1398. /**
  1399. * Called when a base is destroyed or built
  1400. */
  1401. public void addBases(int n) {
  1402. if (n == -1) {
  1403. kill();
  1404. }
  1405. }
  1406. /**
  1407. * Are we playing the game?
  1408. * @return true if we're in PHASE_NORMAL or WAIT_FOR_GIDRAHS
  1409. */
  1410. public boolean isPlaying() {
  1411. return phase == PHASE_NORMAL || phase == PHASE_WAIT_FOR_GIDRAHS;
  1412. }
  1413. /**
  1414. * @return true if we're in rush mode
  1415. */
  1416. public boolean isRushActive() {
  1417. return rush;
  1418. }
  1419. /**
  1420. * End the level
  1421. */
  1422. private void endLevel() {
  1423. GameScreen.onEndLevel();
  1424. // Cancel build mode
  1425. setBuilding(null);
  1426. // Shut down etc.
  1427. shutdownFactories();
  1428. if (metaState.level == 19 && !Game.isRegistered()) {
  1429. expireDemo();
  1430. } else {
  1431. // And open the intermission screen
  1432. suppressMedals = true;
  1433. IntermissionScreen.show();
  1434. }
  1435. }
  1436. public static int getDifficultyAdjust(int level, int gameMode) {
  1437. return Worm.getExtraLevelData(Game.getPlayerSlot(), level, gameMode, "diff", 0);
  1438. }
  1439. public static void setDifficultyAdjust(int level, int gameMode, int diff) {
  1440. Worm.setExtraLevelData(level, gameMode, "diff", diff);
  1441. }
  1442. /**
  1443. * Checks for new medals at the end of the level. Right now, can create up to *9* medals, which will have to be listed on the
  1444. * {@link IntermissionScreen}. Check what got created with {@link #getMedalsEarnedThisLevel()}, which could have a whole bunch
  1445. * more medals in it!
  1446. */
  1447. public void checkForNewMedals() {
  1448. if (base.isAlive() && base.getHitPoints() <= 4) {
  1449. awardMedal(Medals.TAPE_AND_STRING);
  1450. }
  1451. if (metaState.gameMode == GAME_MODE_CAMPAIGN) {
  1452. // Award various medals
  1453. int diff = getDifficultyAdjust(metaState.level, GAME_MODE_CAMPAIGN);
  1454. if (diff == 0) {
  1455. // Gold!
  1456. awardMedal(Medals.GOLD);
  1457. } else if (diff == 1) {
  1458. // Silver!
  1459. awardMedal(Medals.SILVER);
  1460. } else if (diff == 2) {
  1461. // Bronze!
  1462. awardMedal(Medals.BRONZE);
  1463. }
  1464. boolean good = false;
  1465. if (getLevel() == 9) {
  1466. good = true;
  1467. awardMedal(Medals.EARTH_COMPLETE);
  1468. for (int i = 0; i < 10; i ++) {
  1469. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1470. good = false;
  1471. break;
  1472. }
  1473. }
  1474. } else if (getLevel() == 19) {
  1475. good = true;
  1476. awardMedal(Medals.MOON_COMPLETE);
  1477. for (int i = 10; i < 20; i ++) {
  1478. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1479. good = false;
  1480. break;
  1481. }
  1482. }
  1483. } else if (getLevel() == 29) {
  1484. good = true;
  1485. awardMedal(Medals.MARS_COMPLETE);
  1486. for (int i = 20; i < 30; i ++) {
  1487. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1488. good = false;
  1489. break;
  1490. }
  1491. }
  1492. } else if (getLevel() == 39) {
  1493. good = true;
  1494. awardMedal(Medals.SATURN_COMPLETE);
  1495. for (int i = 30; i < 40; i ++) {
  1496. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1497. good = false;
  1498. break;
  1499. }
  1500. }
  1501. } else if (getLevel() == 49) {
  1502. good = true;
  1503. awardMedal(Medals.TITAN_COMPLETE);
  1504. for (int i = 40; i < 50; i ++) {
  1505. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1506. good = false;
  1507. break;
  1508. }
  1509. }
  1510. if (good) {
  1511. boolean reallyGood = true;
  1512. for (int i = 0; i < 50; i ++) {
  1513. if (getDifficultyAdjust(i, metaState.gameMode) > 0) {
  1514. reallyGood = false;
  1515. break;
  1516. }
  1517. }
  1518. if (reallyGood) {
  1519. awardMedal(Medals.PERFECT_GAME);
  1520. }
  1521. }
  1522. }
  1523. if (good) {
  1524. awardMedal(Medals.PERFECT_WORLD);
  1525. }
  1526. }
  1527. if (!anyDamaged) {
  1528. awardMedal(Medals.PRISTINE);
  1529. } else if (valueOfDestroyedBuildings == 0) {
  1530. awardMedal(Medals.CAREFUL);
  1531. } else if ((float) numberOfBuildingsDestroyed / (float) numberOfBuildingsMade >= 0.9f) {
  1532. awardMedal(Medals.SKIN_OF_YOUR_TEETH);
  1533. }
  1534. if (getMoney() - startingMoney >= 15000) {
  1535. awardMedal(Medals.SHORT_ARMS_DEEP_POCKETS);
  1536. } else if (getMoney() - startingMoney >= 10000) {
  1537. awardMedal(Medals.TIGHT_FISTED);
  1538. } else if (getMoney() - startingMoney >= 5000) {
  1539. awardMedal(Medals.THRIFTY);
  1540. }
  1541. if (getMoney() >= 50000) {
  1542. awardMedal(Medals.HOARDED_$50000);
  1543. } else if (getMoney() >= 25000) {
  1544. awardMedal(Medals.HOARDED_$25000);
  1545. } else if (getMoney() >= 10000) {
  1546. awardMedal(Medals.HOARDED_$10000);
  1547. } else if (getMoney() >= 5000) {
  1548. awardMedal(Medals.HOARDED_$5000);
  1549. }
  1550. }
  1551. /**
  1552. * Store the state of play at the end of a level so it's ready to be used by the next level
  1553. */
  1554. public void checkPoint() {
  1555. metaState.level ++;
  1556. if (Worm.getMaxLevel(metaState.gameMode) < metaState.level) {
  1557. Worm.setMaxLevel(metaState.level, metaState.gameMode);
  1558. }
  1559. Worm.setExtraLevelData(metaState.level, metaState.gameMode, "money", metaState.money);
  1560. int diff = getDifficultyAdjust(metaState.level, metaState.gameMode);
  1561. if (diff > 0) {
  1562. setDifficultyAdjust(metaState.level, metaState.gameMode, diff - 1);
  1563. }
  1564. try {
  1565. metaState.save();
  1566. } catch (Exception e) {
  1567. e.printStackTrace(System.err);
  1568. }
  1569. // Just be sure...
  1570. Game.flushPrefs();
  1571. metaState.level --;
  1572. }
  1573. /**
  1574. * End the game
  1575. */
  1576. private void endGame() {
  1577. setBuilding(null);
  1578. phase = PHASE_END_OF_GAME;
  1579. tick = 0;
  1580. }
  1581. /**
  1582. * Sell a building
  1583. * @param building
  1584. */
  1585. public void sell(Building building) {
  1586. building.destroy(true);
  1587. SFX.sold();
  1588. LabelEffect le = new LabelEffect(net.puppygames.applet.Res.getTinyFont(), "$"+building.getSalePrice(), ReadableColor.WHITE, new Color(255, 255, 100), 30, 10);
  1589. le.setAcceleration(0.0f, -0.025f);
  1590. le.setVelocity(0.0f, 1.0f);
  1591. le.setLocation(building.getMapX() + building.getCollisionX(), building.getMapY() + building.getFeature().getBounds().getHeight());
  1592. le.spawn(GameScreen.getInstance());
  1593. addMoney(building.getSalePrice());
  1594. Worm.getGameState().flagHint(Hints.DRAGSELL);
  1595. if (building.isWorthAttacking()) {
  1596. addStat(Stats.SOLD, 1);
  1597. }
  1598. addAvailableStock(building.getFeature(), 1);
  1599. // If selling stuff when there are no gidrahs and level ended, there's no point...
  1600. if (beginLevel && gidrahs.size() == 0) {
  1601. flagHint(Hints.DONTSELL);
  1602. }
  1603. }
  1604. /**
  1605. * Get the level duration, in ticks
  1606. * @return int
  1607. */
  1608. public int getLevelDuration() {
  1609. return levelDuration;
  1610. }
  1611. private void calcLevelDuration(int w, int h) {
  1612. switch (metaState.gameMode) {
  1613. case GAME_MODE_CAMPAIGN:
  1614. float min, max; // In seconds
  1615. switch (getLevel() / LEVELS_IN_WORLD) {
  1616. case 0: // Earth
  1617. min = 30.0f;
  1618. max = 90.0f;
  1619. break;
  1620. case 1: // Moon
  1621. min = 90.0f;
  1622. max = 150.0f;
  1623. break;
  1624. case 2: // Mars
  1625. min = 150.0f;
  1626. max = 210.0f;
  1627. break;
  1628. case 3: // Saturn
  1629. min = 210.0f;
  1630. max = 270.0f;
  1631. break;
  1632. case 4: // Titan
  1633. min = 270.0f;
  1634. max = 300.0f;
  1635. break;
  1636. default:
  1637. assert false : "Bad world for campaign: "+metaState.level;
  1638. min = max = 300.0f;
  1639. }
  1640. levelDuration = (int) LinearInterpolator.instance.interpolate(min, max, getLevelInWorld() / (LEVELS_IN_WORLD - 1.0f)) * 60 + getInitialLevelDelay(w, h); // *60 converts seconds to ticks
  1641. return;
  1642. case GAME_MODE_ENDLESS:
  1643. levelDuration = getInitialLevelDelay(w, h) + getLevel() * 300 + 1800; // 5 seconds per level
  1644. return;
  1645. case GAME_MODE_SURVIVAL:
  1646. case GAME_MODE_SANDBOX:
  1647. levelDuration = -1;
  1648. return;
  1649. case GAME_MODE_XMAS:
  1650. levelDuration = Xmas.DURATION;
  1651. return;
  1652. default:
  1653. assert false : "Unknown game mode "+metaState.gameMode;
  1654. return;
  1655. }
  1656. }
  1657. /**
  1658. * @return time in ticks to delay before starting enemy spawning
  1659. */
  1660. private int getInitialLevelDelay(int w, int h) {
  1661. if (metaState.level < 4 && getGameMode() != GAME_MODE_SURVIVAL && getGameMode() != GAME_MODE_XMAS) {
  1662. return 0;
  1663. }
  1664. return (int) (Math.sqrt(w * h) * config.getInitialLevelDelayFactor());
  1665. }
  1666. /**
  1667. * Get the current level tick
  1668. * @return int
  1669. */
  1670. public int getLevelTick() {
  1671. return levelTick;
  1672. }
  1673. /**
  1674. * Quantize a coordinate to the map resolution
  1675. * @param coord
  1676. * @return a quantized coordinate
  1677. */
  1678. public static float quantize(float coord) {
  1679. return (float) ((int) coord / MAP_RESOLUTION) * MAP_RESOLUTION;
  1680. }
  1681. private void tickSurvivalBosses() {
  1682. survivalBossTick ++;
  1683. if (survivalBossTick > survivalNextBossTick) {
  1684. survivalBossTick = 0;
  1685. survivalNextBossTick += config.getSurvivalBossIntervalAdjust();
  1686. createSurvivalBosses();
  1687. }
  1688. }
  1689. /**
  1690. * Ticks all the spawnpoints.
  1691. * @return true if any spawnpoint is still active
  1692. */
  1693. private boolean tickSpawnPoints() {
  1694. // Survival mode: occasionally create a new spawnpoint
  1695. if (getGameMode() == GAME_MODE_SURVIVAL) {
  1696. survivalSpawnPointTick ++;
  1697. if (survivalSpawnPointTick >= survivalNextSpawnPointTick) {
  1698. survivalSpawnPointInterval += config.getSurvivalSpawnpointSpawnIntervalAdjust();
  1699. survivalNextSpawnPointTick += survivalSpawnPointInterval;
  1700. numSurvivalSpawnPoints = Math.min(config.getBaseMaxSurvivalSpawnpoints() + getMap().getWidth() / config.getSurvivalSpawnpointsPerMapSize(), numSurvivalSpawnPoints + 1);
  1701. }
  1702. while (spawnPoints.size() < numSurvivalSpawnPoints) {
  1703. createSurvivalSpawnPoint();
  1704. }
  1705. }
  1706. boolean active = false;
  1707. for (int i = 0; i < spawnPoints.size(); ) {
  1708. SpawnPoint sp = spawnPoints.get(i);
  1709. boolean wasActive = sp.tick();
  1710. active |= wasActive;
  1711. if (!wasActive) {
  1712. sp.remove();
  1713. spawnPoints.remove(i);
  1714. } else {
  1715. i ++;
  1716. }
  1717. }
  1718. return active;
  1719. }
  1720. /**
  1721. * Is the mouse pointer in range of a capacitor?
  1722. * @return boolean
  1723. */
  1724. public boolean inRangeOfCapacitor() {
  1725. return capacitorRange;
  1726. }
  1727. /**
  1728. * Check for clicking and set the mouse pointer to the right animation for whatever's going on
  1729. */
  1730. private void checkMouse() {
  1731. if (GameScreen.getInstance().isBlocked()) {
  1732. return;
  1733. }
  1734. if (GameScreen.isSomethingElseGrabbingMouse()) {
  1735. return;
  1736. }
  1737. // Ignore mouse on active areas, unless grabbed
  1738. int mouseX = GameScreen.getInstance().getMouseX();
  1739. int mouseY = GameScreen.getInstance().getMouseY();
  1740. List<Area> areasUnderMouse = GameScreen.getInstance().getAreasUnder(mouseX, mouseY);
  1741. if (GameScreen.getInstance().getGrabbed() == null) {
  1742. for (Area area : areasUnderMouse) {
  1743. if (area.isFocusable()) {
  1744. if (buildEntity != null) {
  1745. buildEntity.setVisible(false);
  1746. }
  1747. Worm.setMouseAppearance(Res.getMousePointer());
  1748. return;
  1749. }
  1750. }
  1751. }
  1752. if (buildEntity != null && !buildEntity.isVisible()) {
  1753. buildEntity.setVisible(true);
  1754. }
  1755. float x = mouseX - GameScreen.getSpriteOffset().getX();
  1756. float y = mouseY - GameScreen.getSpriteOffset().getY();
  1757. int n = entities.size();
  1758. Entity hovered = null;
  1759. if (clicked == null) {
  1760. clicked = new ArrayList<Entity>(8);
  1761. } else {
  1762. clicked.clear();
  1763. }
  1764. for (int i = 0; i < n; i ++) {
  1765. Entity entity = entities.get(i);
  1766. if (entity.isTouching(x, y)) {
  1767. if (entity.isClickable()) {
  1768. clicked.add(entity);
  1769. hovered = entity;
  1770. } else if (hovered == null && entity.isHoverable()) {
  1771. hovered = entity;
  1772. }
  1773. }
  1774. }
  1775. // Check range of capacitors
  1776. capacitorRange = false;
  1777. TEMP_CAPACITORS.clear();
  1778. for (int i = 0; i < buildings.size(); i ++) {
  1779. Building b = buildings.get(i);
  1780. if (b.isAlive() && b instanceof Capacitor) {
  1781. Capacitor capacitor = (Capacitor) b;
  1782. if (b.getDistanceTo(x, y) <= capacitor.getZapRadius()) {
  1783. capacitorRange = true;
  1784. TEMP_CAPACITORS.add(capacitor);
  1785. }
  1786. }
  1787. }
  1788. if (mode == Mode.MODE_BUILD) {
  1789. buildEntity.setLocation(quantize(x - building.getBounds().getX() - building.getBounds().getWidth() / 2 + MAP_RESOLUTION / 2), quantize(y - building.getBounds().getY() - building.getBounds().getHeight() / 2 + MAP_RESOLUTION / 2));
  1790. }
  1791. boolean auxDown = false;
  1792. int count = Mouse.getButtonCount();
  1793. for (int i = 2; i < count; i ++) {
  1794. if (Mouse.isButtonDown(i)) {
  1795. auxDown = true;
  1796. break;
  1797. }
  1798. }
  1799. if (Mouse.isButtonDown(0)) {
  1800. if (waitForMouse) {
  1801. return;
  1802. }
  1803. GameScreen.onMapGrabbedMouse(true);
  1804. switch (mode) {
  1805. case Mode.MODE_SELL:
  1806. case Mode.MODE_NORMAL:
  1807. case Mode.MODE_BUILD:
  1808. case Mode.MODE_DRAG:
  1809. // Left mouse down in Normal mode. If we're over something, we click on it unless in "no click" mode. If we're not over something, and in capacitor range,
  1810. // we enter zap mode. If we're outside capacitor range, we set "no click" mode.
  1811. if (clicked.size() > 0) {
  1812. if (leftMouseWasDown) {
  1813. // But we're in "no click" mode...
  1814. return;
  1815. }
  1816. // Ok, enter no click mode
  1817. if (mode != Mode.MODE_DRAG) {
  1818. leftMouseWasDown = true;
  1819. }
  1820. // Hovering over something. Pick the first one and let that decide what mouse pointer to use.
  1821. Worm.setMouseAppearance(clicked.get(0).getMousePointer(true));
  1822. boolean fire = false, maybeSelect = false;
  1823. for (Iterator<Entity> i = clicked.iterator(); i.hasNext(); ) {
  1824. Entity clickable = i.next();
  1825. switch (clickable.onClicked(mode)) {
  1826. case ClickAction.IGNORE:
  1827. maybeSelect = true;
  1828. continue;
  1829. case ClickAction.DRAG:
  1830. // Allow dragging over factories & turrets. Unless in build mode.
  1831. if (mode == Mode.MODE_BUILD) {
  1832. waitForMouse = true;
  1833. leftMouseWasDown = true;
  1834. } else {
  1835. mode = Mode.MODE_DRAG;
  1836. leftMouseWasDown = false;
  1837. }
  1838. return;
  1839. case ClickAction.CONSUME:
  1840. if (mode == Mode.MODE_SELL) {
  1841. // Allow drag
  1842. leftMouseWasDown = false;
  1843. waitForMouse = false;
  1844. } else {
  1845. waitForMouse = true;
  1846. leftMouseWasDown = true;
  1847. }
  1848. return;
  1849. case ClickAction.FIRE:
  1850. if (mode == Mode.MODE_NORMAL) {
  1851. fire = true;
  1852. } else {
  1853. // Build or drag mode; don't zap
  1854. }
  1855. break;
  1856. }
  1857. }
  1858. if (!fire && mode == Mode.MODE_NORMAL) {
  1859. if (maybeSelect) {
  1860. mode = Mode.MODE_SCROLL;
  1861. Worm.setMouseAppearance(Res.getMousePointerGrab());
  1862. scrollOriginX = mouseX;
  1863. scrollOriginY = mouseY;
  1864. }
  1865. return;
  1866. }
  1867. } else {
  1868. if (mode == Mode.MODE_DRAG) {
  1869. Worm.setMouseAppearance(Res.getMousePointer());
  1870. }
  1871. }
  1872. if (mode == Mode.MODE_DRAG || mode == Mode.MODE_SELL || mode == Mode.MODE_SCROLL || mode == Mode.MODE_SELECT) {
  1873. return;
  1874. }
  1875. if (mode == Mode.MODE_BUILD) {
  1876. build();
  1877. break;
  1878. }
  1879. if (!capacitorRange) {
  1880. leftMouseWasDown = true;
  1881. Worm.setMouseAppearance(Res.getMousePointer());
  1882. if (mode == Mode.MODE_NORMAL) {
  1883. // Go into scroll mode
  1884. mode = Mode.MODE_SCROLL;
  1885. scrollOriginX = mouseX;
  1886. scrollOriginY = mouseY;
  1887. leftMouseWasDown = true;
  1888. Worm.setMouseAppearance(Res.getMousePointerGrab());
  1889. }
  1890. return;
  1891. } else {
  1892. leftMouseWasDown = true;
  1893. mode = Mode.MODE_ZAP;
  1894. armedCapacitors.clear();
  1895. armedCapacitors.addAll(TEMP_CAPACITORS);
  1896. }
  1897. // Intentional fall-through
  1898. case Mode.MODE_ZAP:
  1899. // LMB down in ZAP mode. If we are inside capacitor range,
  1900. // fire. Otherwise do nothing.
  1901. for (int i = 0; i < armedCapacitors.size(); i ++) {
  1902. Capacitor capacitor = armedCapacitors.get(i);
  1903. if (capacitor.isAlive()) {
  1904. capacitor.zap(x, y);
  1905. }
  1906. }
  1907. if (armedCapacitors.size() == 0) {
  1908. mode = Mode.MODE_NORMAL;
  1909. }
  1910. if (clicked.size() > 0) {
  1911. Worm.setMouseAppearance(clicked.get(0).getMousePointer(true));
  1912. } else if (isBezerk()) {
  1913. Worm.setMouseAppearance(capacitorRange ? Res.getMousePointerBezerkOffTarget() : Res.getMousePointerBezerkOutOfRange());
  1914. } else {
  1915. Worm.setMouseAppearance(capacitorRange ? Res.getMousePointerOffTarget() : Res.getMousePointerOutOfRange());
  1916. }
  1917. break;
  1918. case Mode.MODE_SMARTBOMB:
  1919. // Boooooooooom!
  1920. Smartbomb bomb = new Smartbomb(x, y);
  1921. bomb.spawn(GameScreen.getInstance());
  1922. mode = Mode.MODE_NORMAL;
  1923. Worm.setMouseAppearance(Res.getMousePointerOffTarget()); // That'll probably change immediately next frame
  1924. waitForMouse = true;
  1925. if (metaState.addStat(Stats.SMARTBOMBS_USED, 1) == 5) {
  1926. awardMedal(Medals.THE_ONLY_WAY_TO_BE_SURE);
  1927. }
  1928. break;
  1929. case Mode.MODE_SCROLL:
  1930. // Scroll the screen around under the mouse
  1931. int dx = scrollOriginX - mouseX;
  1932. int dy = scrollOriginY - mouseY;
  1933. scrollOriginX = mouseX;
  1934. scrollOriginY = mouseY;
  1935. GameScreen.getInstance().scroll(dx * MOUSE_DRAG_SPEED, dy * MOUSE_DRAG_SPEED);
  1936. break;
  1937. case Mode.MODE_SELECT:
  1938. // Do nothing
  1939. return;
  1940. default:
  1941. assert false;
  1942. }
  1943. } else if (auxDown) {
  1944. // Sell!
  1945. GameScreen.onMapGrabbedMouse(true);
  1946. for (Iterator<Entity> i = clicked.iterator(); i.hasNext(); ) {
  1947. Entity entity = i.next();
  1948. if (entity instanceof Building) {
  1949. Building building = (Building) entity;
  1950. if (building.canSell()) {
  1951. sell(building);
  1952. }
  1953. }
  1954. }
  1955. } else {
  1956. GameScreen.onMapGrabbedMouse(false);
  1957. boolean rmbDown = Mouse.getButtonCount() > 1 && Mouse.isButtonDown(1) || Game.wasKeyPressed(Keyboard.KEY_ESCAPE);
  1958. if (rmbDown) {
  1959. if (waitForMouse) {
  1960. return;
  1961. }
  1962. if (!rightMouseWasDown) {
  1963. scrollOriginX = mouseX;
  1964. scrollOriginY = mouseY;
  1965. rmbDragSensor = 0;
  1966. }
  1967. leftMouseWasDown = false;
  1968. rightMouseWasDown = true;
  1969. int dx = scrollOriginX - mouseX;
  1970. int dy = scrollOriginY - mouseY;
  1971. scrollOriginX = mouseX;
  1972. scrollOriginY = mouseY;
  1973. if (dx != 0 || dy != 0) {
  1974. rmbDragSensor ++;
  1975. if (rmbDragSensor > RMB_DRAG_SENSITIVITY) {
  1976. GameScreen.getInstance().scroll(dx * MOUSE_DRAG_SPEED, dy * MOUSE_DRAG_SPEED);
  1977. rmbScroll = true;
  1978. Worm.setMouseAppearance(Res.getMousePointerGrab());
  1979. }
  1980. }
  1981. } else {
  1982. if (rightMouseWasDown && !rmbScroll) {
  1983. // It was a RMB click. Cancel building / selling / blowing stuff up. Or pick up whatever building is under the mouse to build.
  1984. switch (mode) {
  1985. case Mode.MODE_BUILD:
  1986. setBuilding(null);
  1987. rightMouseWasDown = false;
  1988. return;
  1989. case Mode.MODE_SELL:
  1990. setSellMode(false);
  1991. rightMouseWasDown = false;
  1992. return;
  1993. case Mode.MODE_DRAG:
  1994. case Mode.MODE_NORMAL:
  1995. case Mode.MODE_SELECT:
  1996. case Mode.MODE_ZAP:
  1997. for (Iterator<Entity> i = clicked.iterator(); i.hasNext(); ) {
  1998. Entity clickable = i.next();
  1999. if (clickable instanceof Building) {
  2000. BuildingFeature bf = ((Building) clickable).getFeature();
  2001. if (bf.isAvailable()) {
  2002. clickable.onLeave(mode);
  2003. lastHovered = null;
  2004. setBuilding(bf);
  2005. rightMouseWasDown = false;
  2006. return;
  2007. }
  2008. }
  2009. }
  2010. break;
  2011. case Mode.MODE_SMARTBOMB:
  2012. // Cancel the smartbomb and return it to the stores
  2013. addPowerup(SmartbombPowerupFeature.getInstance(), false);
  2014. mode = Mode.MODE_NORMAL;
  2015. rightMouseWasDown = false;
  2016. return;
  2017. case Mode.MODE_SCROLL:
  2018. break;
  2019. default:
  2020. assert false;
  2021. }
  2022. }
  2023. rmbScroll = false;
  2024. rightMouseWasDown = false;
  2025. leftMouseWasDown = false;
  2026. waitForMouse = false;
  2027. if (mode == Mode.MODE_BUILD) {
  2028. if (canBuild(false)) {
  2029. Worm.setMouseAppearance(Res.getMousePointer());
  2030. } else {
  2031. Worm.setMouseAppearance(Res.getMousePointerCantBuild());
  2032. }
  2033. } else if (hovered != null) {
  2034. Worm.setMouseAppearance(hovered.getMousePointer(false));
  2035. if (lastHovered != hovered) {
  2036. if (lastHovered != null) {
  2037. lastHovered.onLeave(mode);
  2038. }
  2039. lastHovered = hovered;
  2040. hovered.onHovered(mode);
  2041. }
  2042. } else {
  2043. if (lastHovered != null) {
  2044. lastHovered.onLeave(mode);
  2045. lastHovered = null;
  2046. }
  2047. if (mode == Mode.MODE_SMARTBOMB) {
  2048. Worm.setMouseAppearance(Res.getMousePointerSmartbomb());
  2049. } else if (mode == Mode.MODE_SELL) {
  2050. Worm.setMouseAppearance(Res.getMousePointerSellOff());
  2051. } else if (isBezerk()) {
  2052. Worm.setMouseAppearance(capacitorRange ? Res.getMousePointerBezerkOffTarget() : Res.getMousePointerBezerkOutOfRange());
  2053. } else {
  2054. Worm.setMouseAppearance(capacitorRange ? Res.getMousePointerOffTarget() : Res.getMousePointerOutOfRange());
  2055. }
  2056. }
  2057. if (mode == Mode.MODE_ZAP || mode == Mode.MODE_DRAG || mode == Mode.MODE_SCROLL || mode == Mode.MODE_SELECT) {
  2058. mode = Mode.MODE_NORMAL;
  2059. }
  2060. }
  2061. }
  2062. }
  2063. /**
  2064. * Can the build entity be drawn where it is?
  2065. * @param doingBuild If we're actually attempting to build
  2066. * @return true if we can build here
  2067. */
  2068. private boolean canBuild(boolean doingBuild) {
  2069. // Can we build?
  2070. if (!buildEntity.canBuild()) {
  2071. return false;
  2072. }
  2073. // Don't allow us to overlap with any solid entities
  2074. buildEntity.getBounds(TEMPBOUNDS);
  2075. // Ensure no part of the building is on a tile that can't be built upon or off the map
  2076. if (TEMPBOUNDS.getX() < 0 || TEMPBOUNDS.getY() < 0 || TEMPBOUNDS.getX() + TEMPBOUNDS.getWidth() >= map.getWidth() * MapRenderer.TILE_SIZE || TEMPBOUNDS.getY() + TEMPBOUNDS.getHeight() >= map.getHeight() * MapRenderer.TILE_SIZE) {
  2077. return false;
  2078. }
  2079. for (int y = TEMPBOUNDS.getY() / MapRenderer.TILE_SIZE; y <= (TEMPBOUNDS.getY() + TEMPBOUNDS.getHeight() - 1) / MapRenderer.TILE_SIZE; y ++) {
  2080. for (int x = TEMPBOUNDS.getX() / MapRenderer.TILE_SIZE; x <= (TEMPBOUNDS.getX() + TEMPBOUNDS.getWidth() - 1) / MapRenderer.TILE_SIZE; x ++) {
  2081. for (int z = 0; z < GameMap.LAYERS; z ++) {
  2082. Tile tile = map.getTile(x, y, z);
  2083. if (tile != null && (tile.isSolid() || tile.isImpassable())) {
  2084. return false;
  2085. }
  2086. }
  2087. }
  2088. }
  2089. Entity.getCollisions(TEMPBOUNDS, BUILD_COLLISIONS);
  2090. int n = BUILD_COLLISIONS.size();
  2091. for (int i = 0; i < n; i ++) {
  2092. Entity entity = BUILD_COLLISIONS.get(i);
  2093. if (entity.isActive() && entity.canCollide() && entity.isTouching(buildEntity) && !buildEntity.canBuildOnTopOf(entity)) {
  2094. return false;
  2095. }
  2096. }
  2097. return true;
  2098. }
  2099. /**
  2100. * Build a building at the specified location.
  2101. * @param x
  2102. * @param y
  2103. */
  2104. private void build() {
  2105. // Make all buildings paintable
  2106. //waitForMouse = !building.isPaintable();
  2107. if (!canBuild(true)) {
  2108. return;
  2109. }
  2110. // Unfortunately need to do these nice cheap checks after canBuild check, to prevent double labels being drawn
  2111. if (!isBuildingAvailable()) {
  2112. SFX.insufficientFunds();
  2113. waitForMouse = true;
  2114. LabelEffect le = new LabelEffect(net.puppygames.applet.Res.getTinyFont(), Game.getMessage("ultraworm.wormgamestate.no_longer_available"), ReadableColor.WHITE, ReadableColor.CYAN, 120, 60);
  2115. le.setAcceleration(0.0f, -0.003125f);
  2116. le.setVelocity(0.0f, 0.5f);
  2117. le.setLocation(buildEntity.getMapX() + buildEntity.getCollisionX(), buildEntity.getMapY() + buildEntity.getFeature().getBounds().getHeight());
  2118. le.spawn(GameScreen.getInstance());
  2119. setBuilding(null);
  2120. waitForMouse = true;
  2121. GameScreen.getInstance().enableBuildings();
  2122. return;
  2123. }
  2124. int cost = building.getShopValue();
  2125. if (getMoney() < cost) {
  2126. SFX.insufficientFunds();
  2127. waitForMouse = true;
  2128. LabelEffect le = new LabelEffect(net.puppygames.applet.Res.getTinyFont(), Game.getMessage("ultraworm.wormgamestate.insufficient_funds")+" $"+(cost - getMoney()), ReadableColor.WHITE, ReadableColor.CYAN, 30, 10);
  2129. le.setAcceleration(0.0f, -0.025f);
  2130. le.setVelocity(0.0f, 1.0f);
  2131. le.setLocation(buildEntity.getMapX() + buildEntity.getCollisionX(), buildEntity.getMapY() + buildEntity.getFeature().getBounds().getHeight());
  2132. le.spawn(GameScreen.getInstance());
  2133. waitForMouse = true;
  2134. return;
  2135. }
  2136. // Demolish any entities we're building on top of
  2137. int damage = 0;
  2138. ArrayList<Entity> ents = new ArrayList<Entity>(entities);
  2139. for (Iterator<Entity> i = ents.iterator(); i.hasNext(); ) {
  2140. Entity entity = i.next();
  2141. if (entity != buildEntity && entity.canBeCrushed() && entity.isTouching(buildEntity) && buildEntity.canBuildOnTopOf(entity)) {
  2142. damage += entity.crush();
  2143. }
  2144. }
  2145. // Create the building
  2146. Building b = building.build((int) buildEntity.getMapX(), (int) buildEntity.getMapY());
  2147. if (damage > 0) {
  2148. b.damage(damage);
  2149. }
  2150. // Record the cost
  2151. b.setCost(cost);
  2152. // Pay the price
  2153. addMoney(-cost);
  2154. if (!(b.isBarricade() || b.isMineField())) {
  2155. valueOfBuiltBuildings += cost;
  2156. numberOfBuildingsMade ++;
  2157. addStat(Stats.BUILDINGS_BUILT, 1);
  2158. addStat(Stats.VALUE_OF_BUILDINGS_BUILT, cost);
  2159. }
  2160. LabelEffect le = new LabelEffect(net.puppygames.applet.Res.getTinyFont(), "$"+cost, ReadableColor.WHITE, ReadableColor.CYAN, 30, 10);
  2161. le.setAcceleration(0.0f, -0.025f);
  2162. le.setVelocity(0.0f, 1.0f);
  2163. le.setLocation(buildEntity.getMapX() + buildEntity.getCollisionX(), buildEntity.getMapY() + buildEntity.getFeature().getBounds().getHeight());
  2164. le.spawn(GameScreen.getInstance());
  2165. Worm.getGameState().flagHint(Hints.DRAGBUILD);
  2166. SFX.build();
  2167. if (building.getNumAvailable() != 0) {
  2168. GameScreen.getInstance().enableBuildings();
  2169. }
  2170. }
  2171. /**
  2172. * Puts us in Build Mode and sets the building that we wish to build, or cancels build mode if building is null
  2173. * @param building
  2174. */
  2175. public void setBuilding(BuildingFeature building) {
  2176. if (buildEntity != null) {
  2177. buildEntity.remove();
  2178. buildEntity = null;
  2179. }
  2180. if (building == null) {
  2181. // Leave "build mode"
  2182. mode = Mode.MODE_NORMAL;
  2183. this.building = null;
  2184. } else {
  2185. if (mode == Mode.MODE_SMARTBOMB) {
  2186. // Return the active smartbomb to the stash
  2187. addPowerup(SmartbombPowerupFeature.getInstance(), false);
  2188. }
  2189. // Go into "build mode"
  2190. mode = Mode.MODE_BUILD;
  2191. this.building = building;
  2192. // Create the build entity
  2193. float x = quantize(GameScreen.getInstance().getMouseX() - GameScreen.getSpriteOffset().getX()), y = quantize(GameScreen.getInstance().getMouseY() - GameScreen.getSpriteOffset().getY());
  2194. buildEntity = building.ghost(quantize(x - building.getBounds().getX() - building.getBounds().getWidth() / 2 + MAP_RESOLUTION / 2), quantize(y - building.getBounds().getY() - building.getBounds().getHeight() / 2 + MAP_RESOLUTION / 2));
  2195. }
  2196. waitForMouse = true;
  2197. }
  2198. public void setSellMode(boolean sellMode) {
  2199. if (sellMode) {
  2200. if (mode == Mode.MODE_SMARTBOMB) {
  2201. // Cancel the smartbomb and return it to the stores
  2202. addPowerup(SmartbombPowerupFeature.getInstance(), true);
  2203. } else if (mode == Mode.MODE_BUILD) {
  2204. setBuilding(null);
  2205. }
  2206. mode = Mode.MODE_SELL;
  2207. } else {
  2208. mode = building == null ? Mode.MODE_NORMAL : Mode.MODE_BUILD;
  2209. }
  2210. }
  2211. public boolean isSelling() {
  2212. return mode == Mode.MODE_SELL;
  2213. }
  2214. private void tickEndOfGame() {
  2215. if (tick == END_OF_GAME_DURATION) {
  2216. // Game over! This puts up teh Game Over dialog. Or goes to the title screen if we've expired the demo.
  2217. if (demoExpired) {
  2218. MiniGame.endGame();
  2219. } else {
  2220. MiniGame.gameOver();
  2221. }
  2222. }
  2223. }
  2224. /**
  2225. * Expire the demo
  2226. */
  2227. private void expireDemo() {
  2228. if (demoExpired) {
  2229. return;
  2230. }
  2231. String msg =
  2232. getGameMode() == GAME_MODE_CAMPAIGN
  2233. ? Game.getMessage("ultraworm.wormgamestate.completed_campaign_demo")
  2234. : Game.getMessage("ultraworm.wormgamestate.completed_endless_demo");
  2235. Res.getResearchNagDialog().doModal("): DEMO EXPIRED :(", msg, new Runnable() {
  2236. @Override
  2237. public void run() {
  2238. if (Res.getResearchNagDialog().getOption() == DialogScreen.OK_OPTION) {
  2239. MiniGame.buy(true);
  2240. } else {
  2241. LabelEffect nagEffect = new LabelEffect(net.puppygames.applet.Res.getBigFont(), "): ): "+Game.getMessage("ultraworm.wormgamestate.demo_expired")+" :( :(", ReadableColor.YELLOW, ReadableColor.RED, 160, 240);
  2242. nagEffect.setLocation(Game.getWidth() / 2, Game.getHeight() / 2 + 32);
  2243. nagEffect.setVisible(true);
  2244. nagEffect.spawn(GameScreen.getInstance());
  2245. nagEffect.setOffset(null);
  2246. Game.getLocalPreferences().putBoolean("showregister", true);
  2247. phase = PHASE_END_OF_GAME;
  2248. tick = 0;
  2249. demoExpired = true;
  2250. }
  2251. }
  2252. });
  2253. }
  2254. /**
  2255. * @return true if player is using bezerk weapon
  2256. */
  2257. public boolean isBezerk() {
  2258. return bezerkTick > 0;
  2259. }
  2260. /**
  2261. * @return true if we've got a smartbomb loaded
  2262. */
  2263. public boolean isSmartbombMode() {
  2264. return mode == Mode.MODE_SMARTBOMB;
  2265. }
  2266. public void handleESC() {
  2267. if (isBuilding()) {
  2268. setBuilding(null);
  2269. } else if (isSelling()) {
  2270. setSellMode(false);
  2271. } else if (!saving && !GameScreen.getInstance().isBlocked()) {
  2272. // Open ingame menu
  2273. switch (getGameMode()) {
  2274. case GAME_MODE_SURVIVAL:
  2275. SurvivalMenuScreen.show(SurvivalMenuScreen.MENU_GAME_MODE);
  2276. break;
  2277. case GAME_MODE_XMAS:
  2278. XmasMenuScreen.show(SurvivalMenuScreen.MENU_GAME_MODE);
  2279. break;
  2280. default:
  2281. MenuScreen.show(MenuScreen.MENU_GAME_MODE);
  2282. break;
  2283. }
  2284. }
  2285. }
  2286. /**
  2287. * Tick, called every frame that the game is playing
  2288. */
  2289. public void tick() {
  2290. // // Check for escape to save the game and quit, or cancel building
  2291. // if (Game.wasKeyPressed(Keyboard.KEY_ESCAPE)) {
  2292. // }
  2293. if (saving && isAlive()) {
  2294. saveTick ++;
  2295. if (saveTick >= SAVE_DURATION) {
  2296. MiniGame.saveGame();
  2297. return;
  2298. }
  2299. }
  2300. // Do colours & animation syncs
  2301. LevelFeature lf = getLevelFeature();
  2302. if (lf == null) {
  2303. return;
  2304. }
  2305. getLevelFeature().getColors().tick();
  2306. // If the shops open do nothing else
  2307. if (GameScreen.getInstance().isBlocked() || GameScreen.getInstance().isShowingPausedHint()) {
  2308. return;
  2309. }
  2310. tick ++;
  2311. totalTicks ++;
  2312. calcCurrentDifficulty();
  2313. // Tick all entities, cull dead ones
  2314. tickEntities();
  2315. if (freezeTick > 0) {
  2316. freezeTick --;
  2317. }
  2318. if (bezerkTick > 0) {
  2319. bezerkTick --;
  2320. }
  2321. if (shieldTick > 0) {
  2322. shieldTick --;
  2323. }
  2324. if (isAlive()) {
  2325. checkMouse();
  2326. }
  2327. if (Game.DEBUG) {
  2328. if (Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
  2329. try {
  2330. Thread.sleep(20);
  2331. } catch (Exception e) {
  2332. }
  2333. }
  2334. if (Game.wasKeyPressed(Keyboard.KEY_M)) {
  2335. metaState.money = Util.random(1, 100) * 100;
  2336. // addMoney(10000);
  2337. }
  2338. if (Game.wasKeyPressed(Keyboard.KEY_R)) {
  2339. Map<String, ResearchFeature> research = ResearchFeature.getResearch();
  2340. for (String id : research.keySet()) {
  2341. setResearched(id);
  2342. }
  2343. GameScreen.getInstance().enableBuildings();
  2344. for (PowerupFeature pf : PowerupFeature.getPowerups()) {
  2345. addPowerup(pf, false);
  2346. }
  2347. for (Iterator<BuildingFeature> i = BuildingFeature.getBuildings().iterator(); i.hasNext(); ) {
  2348. BuildingFeature bf = i.next();
  2349. if (bf.isAvailable()) {
  2350. int num = bf.getNumAvailable();
  2351. if (num > 0) {
  2352. addAvailableStock(bf, num);
  2353. }
  2354. }
  2355. }
  2356. // chaz hack! powerup topup for testing
  2357. //for (int i = 0; i < 5; i ++) {
  2358. // addPowerup(PowerupFeature.getPowerup());
  2359. //}
  2360. }
  2361. if (Game.wasKeyPressed(Keyboard.KEY_PERIOD)) {
  2362. Saucer saucer = new Saucer();
  2363. saucer.spawn(GameScreen.getInstance());
  2364. }
  2365. // if (Worm.wasKeyPressed(Keyboard.KEY_S)) {
  2366. // gameStateInterface.invulnerable(200);
  2367. // }
  2368. if (Game.wasKeyPressed(Keyboard.KEY_L)) {
  2369. endLevel();
  2370. return;
  2371. }
  2372. if (Game.wasKeyPressed(Keyboard.KEY_C)) {
  2373. CompleteGameScreen.show();
  2374. return;
  2375. }
  2376. if (Game.wasKeyPressed(Keyboard.KEY_X)) {
  2377. CompleteXmasScreen.show();
  2378. return;
  2379. }
  2380. }
  2381. switch (phase) {
  2382. case PHASE_NORMAL:
  2383. tickNormal();
  2384. break;
  2385. case PHASE_WAIT_FOR_GIDRAHS:
  2386. tickWaitForGidrahs();
  2387. break;
  2388. case PHASE_END_OF_GAME:
  2389. tickEndOfGame();
  2390. break;
  2391. case PHASE_WAIT_A_FEW_SECONDS:
  2392. tickWaitAFewSeconds();
  2393. break;
  2394. default:
  2395. assert false;
  2396. }
  2397. Entity.checkCollisions();
  2398. // Now cull inactive entities
  2399. for (int i = 0; i < entities.size(); ) {
  2400. Entity src = entities.get(i);
  2401. if (!src.isActive()) {
  2402. entities.remove(i);
  2403. } else {
  2404. i ++;
  2405. }
  2406. }
  2407. updateEntities();
  2408. }
  2409. /**
  2410. * Clears the collision quadtree and then ticks each entity in turn, placing it back in the quadtree once it has
  2411. * been ticked.
  2412. */
  2413. private void tickEntities() {
  2414. Unit.resetTotalThinkTime();
  2415. Gidrah.resetTotalThinkTime();
  2416. for (int i = 0; i < entities.size(); ) {
  2417. Entity e = entities.get(i);
  2418. if (e.isActive()) {
  2419. e.tick();
  2420. }
  2421. if (e.isActive()) {
  2422. i ++;
  2423. } else {
  2424. entities.remove(i);
  2425. }
  2426. }
  2427. }
  2428. /**
  2429. * Update all the entities
  2430. */
  2431. private void updateEntities() {
  2432. for (int i = 0; i < entities.size(); i ++ ) {
  2433. Entity entity = entities.get(i);
  2434. try {
  2435. entity.update();
  2436. } catch (Exception ex) {
  2437. System.err.println("Error updating entity "+entity);
  2438. ex.printStackTrace(System.err);
  2439. entity.remove();
  2440. }
  2441. }
  2442. }
  2443. /**
  2444. * Normal gameplay: make the gidrahs spawn, and the occasional saucer.
  2445. */
  2446. private void tickNormal() {
  2447. // Dead? Game over!
  2448. if (!isAlive()) {
  2449. endGame();
  2450. return;
  2451. }
  2452. if (Game.DEBUG) {
  2453. if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD1)) {
  2454. forceDifficulty = true;
  2455. currentDifficulty = 0.0f;
  2456. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD2)) {
  2457. forceDifficulty = true;
  2458. currentDifficulty = 0.5f;
  2459. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD3)) {
  2460. forceDifficulty = true;
  2461. currentDifficulty = 1.0f;
  2462. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD0)) {
  2463. forceDifficulty = false;
  2464. calcCurrentDifficulty();
  2465. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD4)) {
  2466. createSurvivalBosses();
  2467. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD5)) {
  2468. crystalTick = 1;
  2469. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD6)) {
  2470. setBuilding((BuildingFeature) Resources.get("building.generic.tangleweb"));
  2471. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD7)) {
  2472. CompleteGameScreen.show();
  2473. return;
  2474. } else if (Game.wasKeyPressed(Keyboard.KEY_NUMPAD8)) {
  2475. levelTick += 3600;
  2476. }
  2477. }
  2478. boolean xmas = getGameMode() == GAME_MODE_XMAS;
  2479. // Has the level ended?
  2480. if (getGameMode() == GAME_MODE_SURVIVAL) {
  2481. // No, it's survival mode, and goes on forever and starts right away
  2482. levelTick ++;
  2483. tickSurvivalBosses();
  2484. tickSpawnPoints();
  2485. doCrystals();
  2486. doSaucers();
  2487. } else if (levelTick < getLevelDuration()) {
  2488. if (beginLevel) {
  2489. levelTick ++;
  2490. if (xmas) {
  2491. doCrystals();
  2492. }
  2493. checkUnminedCrystals();
  2494. tickSpawnPoints();
  2495. doSaucers();
  2496. if (levelTick == getLevelDuration()) {
  2497. aliensSpawnedAtLevelEnd = aliensSpawnedValue;
  2498. rush = true;
  2499. if (!xmas) {
  2500. removeSpawnPoints();
  2501. }
  2502. }
  2503. checkInterestingThingsHappening();
  2504. // Maybe do hints
  2505. HintFeature[] events = getLevelFeature().getEvents();
  2506. if (events != null) {
  2507. if (events.length > eventHintSeq) {
  2508. if (events[eventHintSeq].getSeconds() * 60 <= levelTick) {
  2509. flagHint(events[eventHintSeq ++]);
  2510. }
  2511. }
  2512. }
  2513. } else if (getMoney() < 250) {
  2514. beginLevel = true;
  2515. }
  2516. } else {
  2517. waitForGidrahs();
  2518. }
  2519. if (Game.DEBUG && tick % 240 == 0) {
  2520. System.out.println(gidrahs.size()+" gidrahs, difficulty "+getDifficulty()+", valueSpawned="+aliensSpawnedValue+" vanquished="+aliensVanquishedValue);
  2521. }
  2522. }
  2523. /**
  2524. * @return true if the gidrahs have started their advance
  2525. */
  2526. public boolean isLevelStarted() {
  2527. return beginLevel;
  2528. }
  2529. private void checkUnminedCrystals() {
  2530. if (unminedCrystalList.size() == 0) {
  2531. unminedTick = 0;
  2532. } else {
  2533. unminedTick ++;
  2534. if (unminedTick == UNMINED_WARNING_TIME) {
  2535. Worm.getGameState().flagHint(Hints.UNMINED);
  2536. unminedTick = 0;
  2537. }
  2538. }
  2539. }
  2540. /**
  2541. * @return the number of unmined crystals
  2542. */
  2543. public int getNumUnminedCrystals() {
  2544. return unminedCrystalList.size();
  2545. }
  2546. private void checkInterestingThingsHappening() {
  2547. // Interesting stuff happened?
  2548. somethingInterestingHappenedTick ++;
  2549. if (somethingInterestingHappenedTick > INTERESTING_INTERVAL && unminedCrystalList.size() == 0 ) {
  2550. flagHint(Hints.FASTFORWARD);
  2551. somethingInterestingHappenedTick = 0;
  2552. }
  2553. }
  2554. private void removeSpawnPoints() {
  2555. for (Iterator<SpawnPoint> i = spawnPoints.iterator(); i.hasNext(); ) {
  2556. SpawnPoint s = i.next();
  2557. s.remove();
  2558. }
  2559. spawnPoints.clear();
  2560. }
  2561. /**
  2562. * Wait for all the gidrahs to die at the end of a level.
  2563. */
  2564. private void tickWaitForGidrahs() {
  2565. // No bases left? Game over!
  2566. if (!isAlive()) {
  2567. endGame();
  2568. return;
  2569. }
  2570. if (gidrahs.size() == 0) {
  2571. if (getGameMode() == GAME_MODE_XMAS) {
  2572. // Wait until spawnpoint is gone
  2573. if (spawnPoints.size() > 0) {
  2574. tickSpawnPoints();
  2575. return;
  2576. }
  2577. }
  2578. // Wait until (all crystals are mined / factory not available) and gidrahs and bosses are dead
  2579. int n = bosses.size();
  2580. for (int i = 0; i < n; i ++) {
  2581. Gidrah boss = bosses.get(i);
  2582. if (boss.isActive()) {
  2583. return;
  2584. }
  2585. }
  2586. bosses.clear();
  2587. // Yay! level complete. Wait a few seconds.
  2588. waitAFewSeconds();
  2589. return;
  2590. } else {
  2591. if (Game.DEBUG && tick % 240 == 0) {
  2592. System.out.println(gidrahs.size()+" gidrahs, difficulty "+getDifficulty());
  2593. }
  2594. tickSpawnPoints();
  2595. checkInterestingThingsHappening();
  2596. }
  2597. }
  2598. private void waitAFewSeconds() {
  2599. tick = 0;
  2600. rush = false;
  2601. phase = PHASE_WAIT_A_FEW_SECONDS;
  2602. // Collect any powerups
  2603. for (Saucer saucer : new ArrayList<Saucer>(saucers)) {
  2604. saucer.onClicked(Mode.MODE_NORMAL);
  2605. }
  2606. }
  2607. private void tickWaitAFewSeconds() {
  2608. if (tick == WAIT_A_FEW_SECONDS_DELAY) {
  2609. endLevel();
  2610. }
  2611. }
  2612. /**
  2613. * Spawns the bosses
  2614. */
  2615. private void spawnBosses() {
  2616. // Spawn the boss at some of the spawnpoints.
  2617. int n = 0;
  2618. ArrayList<SpawnPoint> sp = new ArrayList<SpawnPoint>(spawnPoints);
  2619. Collections.shuffle(sp);
  2620. ResourceArray bosses = LevelFeature.getLevel(metaState.level).getBosses();
  2621. for (int i = 0; i < bosses.getNumResources() && n < sp.size(); ) {
  2622. SpawnPoint bossSpawnPoint = sp.get(n ++);
  2623. if (bossSpawnPoint.edge) {
  2624. bossSpawnPoint.spawnBoss((GidrahFeature) bosses.getResource(i));
  2625. i ++;
  2626. }
  2627. }
  2628. doBossWarning(bosses.getNumResources());
  2629. }
  2630. private void doBossWarning(int numBosses) {
  2631. // TODO: sfx
  2632. LabelEffect challengeEffect = new LabelEffect(net.puppygames.applet.Res.getBigFont(), Game.getMessage("ultraworm.wormgamestate.extreme_danger"), ReadableColor.YELLOW, ReadableColor.RED, 60, 240);
  2633. challengeEffect.setLocation(Game.getWidth() / 2, Game.getHeight() / 2 + 40);
  2634. challengeEffect.setVisible(true);
  2635. challengeEffect.spawn(GameScreen.getInstance());
  2636. challengeEffect.setOffset(null);
  2637. LabelEffect challengeEffect2 = new LabelEffect(net.puppygames.applet.Res.getSmallFont(), (numBosses > 1 ? Game.getMessage("ultraworm.wormgamestate.large_enemies_approaching") : Game.getMessage("ultraworm.wormgamestate.large_enemy_approaching")), ReadableColor.YELLOW, ReadableColor.RED,
  2638. 60, 240);
  2639. challengeEffect2.setLocation(Game.getWidth() / 2, Game.getHeight() / 2);
  2640. challengeEffect2.setVisible(true);
  2641. challengeEffect2.spawn(GameScreen.getInstance());
  2642. challengeEffect2.setDelay(120);
  2643. challengeEffect2.setOffset(null);
  2644. }
  2645. private void shutdownFactories() {
  2646. boolean doSound = false;
  2647. for (int i = 0; i < buildings.size(); i ++) {
  2648. Building b = buildings.get(i);
  2649. if (b.isActive()) {
  2650. if (b instanceof Factory) {
  2651. doSound |= ((Factory) b).isMining();
  2652. }
  2653. b.onEndLevel();
  2654. }
  2655. }
  2656. if (doSound) {
  2657. SFX.factoryShutdown();
  2658. }
  2659. }
  2660. private void waitForGidrahs() {
  2661. phase = PHASE_WAIT_FOR_GIDRAHS;
  2662. tick = 0;
  2663. }
  2664. /**
  2665. * Activate a powerup
  2666. * @param powerup
  2667. */
  2668. public void activatePowerup(PowerupFeature powerup) {
  2669. powerup.activate(gameStateInterface);
  2670. }
  2671. /**
  2672. * Every now and again, launch a saucer.
  2673. */
  2674. private void doSaucers() {
  2675. saucerTick ++;
  2676. if (saucerTick > nextSaucer) {
  2677. saucerTick = 0;
  2678. if (getGameMode() != GAME_MODE_SURVIVAL && getGameMode() != GAME_MODE_XMAS) {
  2679. nextSaucer += config.getNextSaucerInterval();
  2680. }
  2681. Saucer saucer = new Saucer();
  2682. saucer.spawn(GameScreen.getInstance());
  2683. }
  2684. }
  2685. /**
  2686. * Every now and again, spawn a crystal.
  2687. */
  2688. private void doCrystals() {
  2689. crystalTick --;
  2690. if (crystalTick <= 0) {
  2691. if (survivalCrystals.size() == 0) {
  2692. // Refill the shuffle bag
  2693. for (int i = 0; i < 3; i ++) {
  2694. survivalCrystals.add(new Integer(1));
  2695. }
  2696. for (int i = 0; i < 2; i ++) {
  2697. survivalCrystals.add(new Integer(2));
  2698. }
  2699. for (int i = 0; i < 1; i ++) {
  2700. survivalCrystals.add(new Integer(3));
  2701. }
  2702. Collections.shuffle(survivalCrystals);
  2703. }
  2704. Integer size = survivalCrystals.remove(survivalCrystals.size() - 1);
  2705. Crystal crystalDefinition = getLevelFeature().getScenery().getCrystal(size.intValue());
  2706. int width = crystalDefinition.getWidth();
  2707. int height = crystalDefinition.getHeight();
  2708. int tileX, tileY;
  2709. // Choose a spot some way from the base depending on difficulty
  2710. float minDistance = 10.0f;
  2711. float maxDistance = getMap().getWidth() * 0.45f;
  2712. float ratio = (float) Math.random();
  2713. float distance = LinearInterpolator.instance.interpolate
  2714. (
  2715. CosineInterpolator.instance.interpolate(minDistance, maxDistance, ratio),
  2716. SineInterpolator.instance.interpolate(minDistance, maxDistance, ratio),
  2717. getDifficulty()
  2718. );
  2719. double angle = Math.random() * Math.PI * 2.0;
  2720. tileX = getBase().getTileX() + (int) (Math.cos(angle) * distance);
  2721. tileY = getBase().getTileY() + (int) (Math.sin(angle) * distance);
  2722. for (int x = 0; x < width; x ++) {
  2723. for (int y = 0; y < height; y ++) {
  2724. if (getMap().isOccupied(x + tileX, y + tileY)) {
  2725. return;
  2726. }
  2727. if (getMap().isAttacking(x + tileX, y + tileY)) {
  2728. return;
  2729. }
  2730. for (int z = 0; z < GameMap.LAYERS; z ++) {
  2731. Tile t = getMap().getTile(x + tileX, y + tileY, z);
  2732. if (t == null || t.isImpassable() || t.isSolid()) {
  2733. return;
  2734. }
  2735. }
  2736. }
  2737. }
  2738. ReadableRectangle bounds = new Rectangle(tileX * MapRenderer.TILE_SIZE, tileY * MapRenderer.TILE_SIZE, width * MapRenderer.TILE_SIZE, height * MapRenderer.TILE_SIZE);
  2739. for (Iterator<Entity> i = entities.iterator(); i.hasNext(); ) {
  2740. Entity entity = i.next();
  2741. if (entity.isActive() && entity.isSolid() && entity.canCollide() && entity.isTouching(bounds)) {
  2742. return;
  2743. }
  2744. }
  2745. // Write excludes
  2746. for (int x = 0; x < width; x ++) {
  2747. for (int y = 0; y < height; y ++) {
  2748. Exclude.getInstance().toMap(getMap(), x + tileX, y + tileY, false);
  2749. }
  2750. }
  2751. EntitySpawningFeature esf = crystalDefinition.getCrystalFeature();
  2752. esf.spawn(tileX, tileY);
  2753. crystalTick = survivalNextCrystalTick * size.intValue();
  2754. survivalNextCrystalTick += getGameMode() == GAME_MODE_SURVIVAL ? config.getSurvivalCrystalIntervalAdjust() : config.getXmasCrystalIntervalAdjust();
  2755. }
  2756. }
  2757. /**
  2758. * @return the game mode
  2759. */
  2760. public int getGameMode() {
  2761. return metaState.gameMode;
  2762. }
  2763. /**
  2764. * Init for a new game
  2765. */
  2766. @Override
  2767. public void init() {
  2768. switch (metaState.gameMode) {
  2769. case GAME_MODE_CAMPAIGN:
  2770. if (Worm.getMaxWorld() == 0) {
  2771. SelectLevelScreen sls = (SelectLevelScreen) Resources.get("select.level.screen.0");
  2772. sls.open();
  2773. } else {
  2774. SelectWorldScreen.show();
  2775. }
  2776. break;
  2777. case GAME_MODE_ENDLESS:
  2778. SelectEndlessLevelScreen.show();
  2779. break;
  2780. case GAME_MODE_SURVIVAL:
  2781. SelectSurvivalLevelScreen.show();
  2782. break;
  2783. case GAME_MODE_SANDBOX:
  2784. SelectSandboxLevelScreen.show();
  2785. break;
  2786. case GAME_MODE_XMAS:
  2787. initXmas(true);
  2788. break;
  2789. default:
  2790. assert false : "Unknown game mode "+mode;
  2791. }
  2792. }
  2793. /**
  2794. * Initialise a Christmas mode game (instead of calling {@link #doInit(int)})
  2795. */
  2796. private void initXmas(boolean reset) {
  2797. System.out.println("Initialising Xmas Mode");
  2798. waitForMouse = true;
  2799. xmasReset = reset;
  2800. if (reset) {
  2801. metaState = null;
  2802. metaState = new MetaState(GAME_MODE_XMAS);
  2803. } else {
  2804. metaState.reset();
  2805. }
  2806. GameScreen.beginGame(this);
  2807. beginLevel();
  2808. initXmasResearch();
  2809. doInitMessages(Game.getMessage("ultraworm.wormgamestate.init_xmas1"), Game.getMessage("ultraworm.wormgamestate.init_xmas2"));
  2810. }
  2811. /**
  2812. * Initialise a survival mode game (instead of calling {@link #doInit(int)})
  2813. */
  2814. private void initSurvival(boolean reset) {
  2815. System.out.println("Initialising Survival Mode");
  2816. waitForMouse = true;
  2817. if (reset) {
  2818. metaState = null;
  2819. metaState = new MetaState(GAME_MODE_SURVIVAL);
  2820. } else {
  2821. metaState.reset();
  2822. }
  2823. GameScreen.beginGame(this);
  2824. beginLevel();
  2825. initSurvivalResearch();
  2826. doInitMessages(Game.getMessage("ultraworm.wormgamestate.init_survival1"), Game.getMessage("ultraworm.wormgamestate.init_survival2"));
  2827. }
  2828. /**
  2829. * Initialise a sandbox mode game (instead of calling {@link #doInit(int)})
  2830. */
  2831. private void initSandbox() {
  2832. System.out.println("Initialising Sandbox Mode");
  2833. waitForMouse = true;
  2834. metaState = null;
  2835. metaState = new MetaState(GAME_MODE_SANDBOX);
  2836. GameScreen.beginGame(this);
  2837. beginLevel();
  2838. //initSurvivalResearch();
  2839. //doInitMessages(Game.getMessage("ultraworm.wormgamestate.init_sandbox1"), Game.getMessage("ultraworm.wormgamestate.init_sandbox2"));
  2840. doInitMessages("SANDBOX TEST", "No really, just a test!");
  2841. }
  2842. /**
  2843. * Begin a new game at the specified level
  2844. * @param level
  2845. */
  2846. public void doInit(int level) {
  2847. System.out.println("DoInit: "+level);
  2848. waitForMouse = true;
  2849. if (level == 0) {
  2850. // Research the default researchy things
  2851. metaState = new MetaState(metaState.gameMode);
  2852. initDefaultResearch();
  2853. } else {
  2854. // Load research and powerups etc we had last time
  2855. try {
  2856. metaState = MetaState.load(level, metaState.gameMode);
  2857. } catch (Exception e) {
  2858. e.printStackTrace(System.err);
  2859. metaState = new MetaState(metaState.gameMode);
  2860. initDefaultResearch();
  2861. }
  2862. }
  2863. GameScreen.beginGame(this);
  2864. if (Game.isRegistered()) {
  2865. awardMedal(Medals.REGISTERED);
  2866. }
  2867. beginLevel();
  2868. doInitMessages(Game.getMessage("ultraworm.wormgamestate.init1"), Game.getMessage("ultraworm.wormgamestate.init2"));
  2869. }
  2870. /**
  2871. * Begin a Survival game with the specified characteristics
  2872. */
  2873. public void doInit(SurvivalParams survivalParams) {
  2874. this.survivalParams = survivalParams;
  2875. System.out.println("DoInit: "+survivalParams);
  2876. initSurvival(survivalParams.getGenerateNew());
  2877. if (Game.isRegistered()) {
  2878. awardMedal(Medals.REGISTERED);
  2879. }
  2880. }
  2881. /**
  2882. * @return the survival parameters, if we're in survival mode
  2883. */
  2884. public SurvivalParams getSurvivalParams() {
  2885. return survivalParams;
  2886. }
  2887. private void initDefaultResearch() {
  2888. for (ResearchFeature rf : ResearchFeature.getResearch().values()) {
  2889. if (rf.isDefaultAvailable()) {
  2890. setResearched(rf.getID());
  2891. }
  2892. }
  2893. }
  2894. private void initSurvivalResearch() {
  2895. MetaState tempMetaState;
  2896. try {
  2897. tempMetaState = MetaState.load(survivalParams.getWorld().getIndex() * LEVELS_IN_WORLD + LEVELS_IN_WORLD, GAME_MODE_CAMPAIGN);
  2898. for (String s : tempMetaState.researched) {
  2899. setResearched(s);
  2900. }
  2901. } catch (Exception e) {
  2902. e.printStackTrace(System.err);
  2903. initDefaultResearch();
  2904. return;
  2905. }
  2906. }
  2907. private void initXmasResearch() {
  2908. for (String s : Xmas.RESEARCH) {
  2909. setResearched(s);
  2910. }
  2911. }
  2912. /**
  2913. * Begin a Sandbox game with the specified characteristics
  2914. */
  2915. public void doInit(SandboxParams sandboxParams) {
  2916. this.sandboxParams = sandboxParams;
  2917. System.out.println("DoInit: "+sandboxParams);
  2918. initSandbox();
  2919. }
  2920. /**
  2921. * We've completed the game!
  2922. */
  2923. private void completeGame() {
  2924. if (getGameMode() == GAME_MODE_XMAS) {
  2925. CompleteXmasScreen.show();
  2926. } else {
  2927. CompleteGameScreen.show();
  2928. }
  2929. }
  2930. /**
  2931. * Go to the next level. If we run out of levels, we complete the game.
  2932. */
  2933. public void nextLevel() {
  2934. metaState.level ++;
  2935. if (getGameMode() == GAME_MODE_XMAS) {
  2936. completeGame();
  2937. return;
  2938. }
  2939. if (getGameMode() == GAME_MODE_ENDLESS || getLevel() < MAX_LEVELS) {
  2940. beginLevel();
  2941. } else {
  2942. completeGame();
  2943. }
  2944. }
  2945. /**
  2946. * Initialise a level. We need to load the game map, remove any entities, spawn the buildings, make a note of the spawn points on
  2947. * the map, etc.
  2948. */
  2949. public void beginLevel() {
  2950. System.out.println("Beginning level "+metaState.gameMode+"/"+metaState.level);
  2951. WorldFeature newWorld;
  2952. StoryFeature[] sf;
  2953. reset();
  2954. switch (metaState.gameMode) {
  2955. case GAME_MODE_CAMPAIGN:
  2956. // Set the level
  2957. metaState.levelFeature = LevelFeature.getLevel(metaState.level);
  2958. // We might have changed worlds
  2959. newWorld = getLevelFeature().getWorld();
  2960. if (newWorld != getWorld()) {
  2961. setWorld(newWorld);
  2962. return;
  2963. }
  2964. currentStoryIndex = 0;
  2965. sf = getLevelFeature().getStories();
  2966. currentStories = new ArrayList<StoryFeature>(sf.length);
  2967. for (StoryFeature element : sf) {
  2968. if (element.qualifies()) {
  2969. currentStories.add(element);
  2970. }
  2971. }
  2972. suppressMedals = false;
  2973. showStoryScreen();
  2974. break;
  2975. case GAME_MODE_ENDLESS:
  2976. // Generate a random level
  2977. metaState.levelFeature = LevelFeature.generateEndless(metaState.level);
  2978. // We might have changed worlds
  2979. newWorld = getLevelFeature().getWorld();
  2980. if (newWorld != getWorld()) {
  2981. setWorld(newWorld);
  2982. }
  2983. currentStoryIndex = 0;
  2984. sf = getLevelFeature().getStories();
  2985. currentStories = new ArrayList<StoryFeature>(sf.length);
  2986. for (StoryFeature element : sf) {
  2987. if (element.qualifies()) {
  2988. currentStories.add(element);
  2989. }
  2990. }
  2991. suppressMedals = false;
  2992. showStoryScreen();
  2993. break;
  2994. case GAME_MODE_SURVIVAL:
  2995. if (survivalParams.getGenerateNew()) {
  2996. // Generate a random level
  2997. metaState.levelFeature = LevelFeature.generateSurvival(survivalParams);
  2998. } else {
  2999. // Use last level
  3000. }
  3001. metaState.difficulty = survivalParams.getDifficulty();
  3002. metaState.survivalParams = survivalParams;
  3003. metaState.reset();
  3004. // We might have changed worlds
  3005. newWorld = getLevelFeature().getWorld();
  3006. if (newWorld != getWorld()) {
  3007. setWorld(newWorld);
  3008. }
  3009. currentStoryIndex = 0;
  3010. sf = getLevelFeature().getStories();
  3011. currentStories = new ArrayList<StoryFeature>(sf.length);
  3012. for (StoryFeature element : sf) {
  3013. if (element.qualifies()) {
  3014. currentStories.add(element);
  3015. }
  3016. }
  3017. suppressMedals = false;
  3018. showStoryScreen();
  3019. break;
  3020. case GAME_MODE_SANDBOX:
  3021. // Generate a random level
  3022. metaState.levelFeature = LevelFeature.generateEndless(metaState.level);
  3023. // We might have changed worlds
  3024. newWorld = getLevelFeature().getWorld();
  3025. if (newWorld != getWorld()) {
  3026. setWorld(newWorld);
  3027. }
  3028. suppressMedals = true;
  3029. beginLevel2();
  3030. break;
  3031. case GAME_MODE_XMAS:
  3032. // Generate a random level
  3033. metaState.levelFeature = LevelFeature.generateXmas();
  3034. metaState.difficulty = 0.0f;
  3035. metaState.reset();
  3036. // We might have changed worlds
  3037. newWorld = getLevelFeature().getWorld();
  3038. if (newWorld != getWorld()) {
  3039. setWorld(newWorld);
  3040. }
  3041. currentStoryIndex = 0;
  3042. sf = getLevelFeature().getStories();
  3043. currentStories = new ArrayList<StoryFeature>(sf.length);
  3044. for (StoryFeature element : sf) {
  3045. if (element.qualifies()) {
  3046. currentStories.add(element);
  3047. }
  3048. }
  3049. suppressMedals = false;
  3050. showStoryScreen();
  3051. break;
  3052. default:
  3053. assert false : "Unknown game mode "+metaState.gameMode;
  3054. }
  3055. }
  3056. private void nextStoryScreen() {
  3057. if (++currentStoryIndex >= currentStories.size()) {
  3058. StoryScreen.tidyUp("story.screen."+getWorld().getUntranslated());
  3059. // Open research screen, unless this is level 0 or survival mode or sandbox mode, in which case jump straight into the action
  3060. if (getLevel() == 0 || getGameMode() == GAME_MODE_SURVIVAL || getGameMode() == GAME_MODE_SANDBOX) {
  3061. beginLevel2();
  3062. } else {
  3063. showResearchScreen();
  3064. }
  3065. } else {
  3066. // Open next story screen
  3067. showStoryScreen();
  3068. }
  3069. }
  3070. private void previousStoryScreen() {
  3071. if (--currentStoryIndex == -1) {
  3072. switch (metaState.gameMode) {
  3073. case GAME_MODE_CAMPAIGN:
  3074. // Go to level select screen of current world
  3075. StoryScreen.tidyUp("story.screen."+getWorld().getUntranslated());
  3076. showLevelSelectScreen();
  3077. break;
  3078. case GAME_MODE_ENDLESS:
  3079. case GAME_MODE_SURVIVAL:
  3080. showLevelSelectScreen();
  3081. break;
  3082. case GAME_MODE_SANDBOX:
  3083. showLevelSelectScreen();
  3084. break;
  3085. case GAME_MODE_XMAS:
  3086. net.puppygames.applet.screens.TitleScreen.show();
  3087. break;
  3088. default:
  3089. assert false : "Shouldn't be here: "+metaState.gameMode;
  3090. }
  3091. } else {
  3092. // Open previous story
  3093. showStoryScreen();
  3094. }
  3095. }
  3096. public void showLevelSelectScreen() {
  3097. switch (metaState.gameMode) {
  3098. case GAME_MODE_CAMPAIGN:
  3099. SelectLevelScreen sls = (SelectLevelScreen) Resources.get("select.level.screen."+metaState.level / LEVELS_IN_WORLD);
  3100. sls.open();
  3101. break;
  3102. case GAME_MODE_ENDLESS:
  3103. SelectEndlessLevelScreen.show();
  3104. break;
  3105. case GAME_MODE_SURVIVAL:
  3106. SelectSurvivalLevelScreen.show();
  3107. break;
  3108. case GAME_MODE_SANDBOX:
  3109. SelectSandboxLevelScreen.show();
  3110. break;
  3111. case GAME_MODE_XMAS:
  3112. net.puppygames.applet.screens.TitleScreen.show();
  3113. break;
  3114. default:
  3115. assert false : "Shouldn't be here: "+metaState.gameMode;
  3116. }
  3117. }
  3118. public void showResearchScreen() {
  3119. ResearchScreen.show
  3120. (
  3121. new Runnable() {
  3122. @Override
  3123. public void run() {
  3124. previousStoryScreen();
  3125. }
  3126. },
  3127. new Runnable() {
  3128. @Override
  3129. public void run() {
  3130. Worm.getGameState().beginLevel2();
  3131. }
  3132. }
  3133. );
  3134. }
  3135. private void showStoryScreen() {
  3136. StoryScreen.show
  3137. (
  3138. "story.screen."+getWorld().getUntranslated(),
  3139. currentStoryIndex == 0,
  3140. currentStories.get(currentStoryIndex),
  3141. new Runnable() {
  3142. @Override
  3143. public void run() {
  3144. previousStoryScreen();
  3145. }
  3146. },
  3147. new Runnable() {
  3148. @Override
  3149. public void run() {
  3150. nextStoryScreen();
  3151. }
  3152. }
  3153. );
  3154. }
  3155. public void setMap(GameMap newMap) {
  3156. this.map = newMap;
  3157. }
  3158. /**
  3159. * Reinforcements!
  3160. */
  3161. private void reinforcements() {
  3162. for (Iterator<BuildingFeature> i = BuildingFeature.getBuildings().iterator(); i.hasNext(); ) {
  3163. BuildingFeature bf = i.next();
  3164. if (bf.isAvailable()) {
  3165. int num = bf.getNumAvailable();
  3166. if (num > 0) {
  3167. addAvailableStock(bf, num);
  3168. }
  3169. }
  3170. }
  3171. }
  3172. /**
  3173. * Begin the level with the specified map.
  3174. */
  3175. public void beginLevel2() {
  3176. // Increase available stock of everything (barricades and mines)
  3177. reinforcements();
  3178. // Ensure all entities are removed first
  3179. for (Iterator<Entity> i = entities.iterator(); i.hasNext(); ) {
  3180. Entity entity = i.next();
  3181. entity.remove();
  3182. }
  3183. entities.clear();
  3184. // Init gids stuff at start of level
  3185. Gidrah.init();
  3186. phase = PHASE_NORMAL;
  3187. rush = false;
  3188. levelTick = 0;
  3189. tick = 0;
  3190. beginLevel = !isResearched(ResearchFeature.FACTORY) || getMoney() < 250 || metaState.gameMode == GAME_MODE_SURVIVAL || metaState.gameMode == GAME_MODE_XMAS;
  3191. nextSaucer = config.getSaucerInterval();
  3192. System.out.println("MODE: "+metaState.gameMode+"\nPHASE: "+phase);
  3193. // Reset buffs
  3194. scannerBoost = 0;
  3195. batteryBoost = 0;
  3196. reactorBoost = 0;
  3197. coolingBoost = 0;
  3198. shieldBoost = isResearched(ResearchFeature.NANOHARDENING) ? 1 : 0;
  3199. capacitorBoost = isResearched(ResearchFeature.IONISATION) ? 1 : 0;
  3200. // Reset counters
  3201. factories = 0;
  3202. warehouses = 0;
  3203. autoloaders = 0;
  3204. shieldGenerators = 0;
  3205. spawners = 0;
  3206. crystals = 0;
  3207. initialCrystals = 0;
  3208. // Reset powerups
  3209. bezerkTick = 0;
  3210. shieldTick = 0;
  3211. freezeTick = 0;
  3212. // Reset buildings value
  3213. anyDamaged = false;
  3214. valueOfBuiltBuildings = 0;
  3215. valueOfDestroyedBuildings = 0;
  3216. // Reset aliens spawned value
  3217. aliensVanquishedValue = 0;
  3218. aliensSpawnedValue = 0;
  3219. // Clear medals earned this level
  3220. medalsThisLevel.clear();
  3221. awesome = false;
  3222. // Remember starting money
  3223. startingMoney = metaState.money;
  3224. attempts = Worm.getExtraLevelData(Game.getPlayerSlot(), metaState.level, metaState.gameMode, "attempts", 0);
  3225. System.out.println("Number of attempts at this level so far: "+attempts);
  3226. // Adjust difficulty by number of attempts
  3227. calcBasicDifficulty();
  3228. calcPowerupDifficulty();
  3229. System.out.println("Base difficulty "+metaState.difficulty);
  3230. // How long's the level?
  3231. calcLevelDuration(map.getWidth(), map.getHeight());
  3232. // Clear everything out
  3233. unminedCrystalList.clear();
  3234. Entity.reset();
  3235. System.gc();
  3236. // Sort out the map
  3237. initMap();
  3238. // Open the game screen
  3239. GameScreen.onBeginLevel();
  3240. // Maybe spawn bosses
  3241. ResourceArray bosses = metaState.levelFeature.getBosses();
  3242. if (bosses != null && bosses.getNumResources() > 0) {
  3243. spawnBosses();
  3244. }
  3245. // Reset event sequence
  3246. eventHintSeq = 0;
  3247. leftMouseWasDown = true;
  3248. waitForMouse = true;
  3249. // Research medals
  3250. if (metaState.gameMode != GAME_MODE_SURVIVAL && metaState.gameMode != GAME_MODE_XMAS) {
  3251. Map<String, List<String>> medalGroups = ResearchFeature.getMedalGroups();
  3252. for (Entry<String, List<String>> entry : medalGroups.entrySet()) {
  3253. String medal = entry.getKey();
  3254. List<String> researchRequired = entry.getValue();
  3255. synchronized (metaState.researched) {
  3256. if (metaState.researched.containsAll(researchRequired)) {
  3257. awardMedal(medal);
  3258. }
  3259. }
  3260. }
  3261. }
  3262. }
  3263. /**
  3264. * Calculates base difficulty and research difficulty
  3265. */
  3266. public void calcBasicDifficulty() {
  3267. if (metaState.gameMode == GAME_MODE_SURVIVAL) {
  3268. metaState.difficulty = survivalParams.getDifficulty();
  3269. return;
  3270. } else if (metaState.gameMode == GAME_MODE_XMAS) {
  3271. metaState.difficulty = 0.0f;
  3272. return;
  3273. }
  3274. int diff = getDifficultyAdjust(getLevel(), getGameMode());
  3275. metaState.difficulty = diff * config.getDifficultyAdjustmentFactor();
  3276. // Central base levels - offset difficulty
  3277. float offset = BaseMapGenerator.isBaseCentralForLevel(metaState.level, metaState.gameMode) ? config.getCentralDifficultyAdjustPerLevel() * metaState.level : 0.0f;
  3278. // Endless mode: gets a bit harder every level after level 50...
  3279. if (metaState.gameMode == GAME_MODE_ENDLESS && metaState.level > MAX_LEVELS) {
  3280. offset -= (metaState.level - MAX_LEVELS) * config.getEndlessDifficultyCreep();
  3281. }
  3282. metaState.difficulty -= offset;
  3283. System.out.println("BASIC DIFFICULTY "+getBasicDifficulty()+" (was offset by "+offset+")");
  3284. }
  3285. /**
  3286. * @return the basic level difficulty (&ge; 0.0f)
  3287. */
  3288. public float getBasicDifficulty() {
  3289. return Math.max(0.0f, metaState.difficulty + (config.getBankFactor() * getMoney()) / (config.getDifficultyFactor() + config.getDifficultyFactorPerLevel() * getLevel()));
  3290. }
  3291. /**
  3292. * Initialise the map generated in the story screen
  3293. */
  3294. private void initMap() {
  3295. // Clear away the old spawn points
  3296. spawnPoints.clear();
  3297. // Have we been here before?
  3298. MapProcessor processor = new MapProcessor() {
  3299. @Override
  3300. public void addSpawnPoint(int x, int y, int type, boolean edge) {
  3301. SpawnPoint sp = new SpawnPoint(x, y, type, edge);
  3302. assert !spawnPoints.contains(sp);
  3303. spawnPoints.add(sp);
  3304. }
  3305. @Override
  3306. public void createBase(int x, int y) {
  3307. getWorld().getBase().build(x * MapRenderer.TILE_SIZE, y * MapRenderer.TILE_SIZE);
  3308. }
  3309. @Override
  3310. public void spawnEntity(EntitySpawningFeature feature, int tileX, int tileY) {
  3311. // Spawn the entity...
  3312. Entity e = feature.spawn(tileX, tileY);
  3313. e.update();
  3314. // Remove from the map?
  3315. if (feature.removeAfterSpawn()) {
  3316. getMap().clearItem(tileX, tileY);
  3317. }
  3318. }
  3319. };
  3320. // Process the map one tile at a time.
  3321. for (int y = 0; y < map.getHeight(); y ++) {
  3322. for (int x = 0; x < map.getWidth(); x ++) {
  3323. for (int z = 0; z < GameMap.LAYERS; z ++) {
  3324. Tile t = map.getTile(x, y, z);
  3325. t.process(processor, x, y);
  3326. }
  3327. }
  3328. }
  3329. // Survival mode: create spawn point.
  3330. if (getGameMode() == GAME_MODE_SURVIVAL) {
  3331. createSurvivalSpawnPoint();
  3332. }
  3333. }
  3334. private ReadablePoint getEdgePoint() {
  3335. int edge = getGameMode() == GAME_MODE_XMAS ? 0 : Util.random(0, 9); // Bias away from south: only 1/10 edges are picked from south
  3336. boolean blocked;
  3337. int x, y, ox, oy, count = 0;
  3338. do {
  3339. count ++;
  3340. blocked = false;
  3341. switch (edge) {
  3342. case 0: // North
  3343. case 1:
  3344. case 2:
  3345. x = Util.random(1, getMap().getWidth() - 2);
  3346. y = getMap().getHeight() - 1;
  3347. ox = 0;
  3348. oy = 1;
  3349. break;
  3350. case 3: // East
  3351. case 4:
  3352. case 5:
  3353. y = Util.random(1, getMap().getHeight() - 2);
  3354. x = getMap().getWidth() - 1;
  3355. ox = 1;
  3356. oy = 0;
  3357. break;
  3358. case 6: // West
  3359. case 7:
  3360. case 8:
  3361. y = Util.random(1, getMap().getHeight() - 2);
  3362. x = 0;
  3363. ox = -1;
  3364. oy = 0;
  3365. break;
  3366. case 9: // South
  3367. x = Util.random(1, getMap().getWidth() - 2);
  3368. y = 0;
  3369. ox = 0;
  3370. oy = -1;
  3371. break;
  3372. default:
  3373. assert false;
  3374. return new Point();
  3375. }
  3376. for (int z = 0; z < GameMap.LAYERS; z ++) {
  3377. Tile t = getMap().getTile(x, y, z);
  3378. if (t.isImpassable() || t.isSolid()) {
  3379. blocked = true;
  3380. break;
  3381. }
  3382. }
  3383. } while (blocked && count < 50);
  3384. return new Point(x + ox, y + oy);
  3385. }
  3386. /**
  3387. * Creates a brand new survival mode spawnpoint by finding an empty tile on the edge of the map.
  3388. */
  3389. private void createSurvivalSpawnPoint() {
  3390. ReadablePoint edgePoint = getEdgePoint();
  3391. if (edgePoint == null) {
  3392. return;
  3393. }
  3394. spawnPoints.add(new SpawnPoint(edgePoint.getX(), edgePoint.getY(), 0, true));
  3395. }
  3396. /**
  3397. * Hack for Earth-only survival / xmas bosses
  3398. * @param n
  3399. * @return
  3400. */
  3401. private static String survivalBoss(int n) {
  3402. StringBuilder sb = new StringBuilder(n);
  3403. for (int i = 0; i < n; i ++) {
  3404. sb.append('0');
  3405. }
  3406. return sb.toString();
  3407. }
  3408. /**
  3409. * Spawns survival mode bosses
  3410. */
  3411. private void createSurvivalBosses() {
  3412. // Get best world player has seen...
  3413. int bestWorldIndex = Math.min(WorldFeature.getNumWorlds() - 1, Worm.getMaxLevel(GAME_MODE_CAMPAIGN) / LEVELS_IN_WORLD);
  3414. int numBossTypes = Math.min(WorldFeature.getWorld(bestWorldIndex).getSurvivalMaxBoss() + 1, Res.getNumSurvivalBosses());
  3415. String s = numBossTypes == 1 ? survivalBoss(survivalBoss) : Integer.toString(survivalBoss, numBossTypes);
  3416. for (int i = 0; i < s.length(); i ++) {
  3417. ReadablePoint edgePoint = getEdgePoint();
  3418. GidrahFeature bossFeature = Res.getSurvivalBoss(s.charAt(i) - '0');
  3419. if (bossFeature != null) {
  3420. bossFeature.spawn(GameScreen.getInstance(), edgePoint.getX(), edgePoint.getY(), 0);
  3421. }
  3422. }
  3423. survivalBoss ++;
  3424. doBossWarning(s.length());
  3425. reinforcements();
  3426. }
  3427. /**
  3428. * @return the current map
  3429. */
  3430. public GameMap getMap() {
  3431. return map;
  3432. }
  3433. private void setWorld(WorldFeature newWorld) {
  3434. assert newWorld != null;
  3435. if (metaState.world == newWorld) {
  3436. return;
  3437. }
  3438. metaState.world = newWorld;
  3439. // Put up a New World screen in campaign mode
  3440. if (metaState.gameMode == GAME_MODE_CAMPAIGN) {
  3441. NewWorldScreen nw = (NewWorldScreen) Resources.get("newworld.screen."+getWorld().getUntranslated());
  3442. nw.open();
  3443. }
  3444. }
  3445. private void doInitMessages(String big, String small) {
  3446. LabelEffect newLevelEffect = new LabelEffect(net.puppygames.applet.Res.getBigFont(), big, ReadableColor.WHITE, ReadableColor.RED, 40, 160);
  3447. newLevelEffect.setLocation(Game.getWidth() / 2, Game.getHeight() / 2 + 60);
  3448. newLevelEffect.setVisible(true);
  3449. newLevelEffect.spawn(GameScreen.getInstance());
  3450. newLevelEffect.setOffset(null);
  3451. LabelEffect hintEffect = new LabelEffect(net.puppygames.applet.Res.getSmallFont(), small, ReadableColor.WHITE, ReadableColor.RED, 40, 160);
  3452. hintEffect.setDelay(30);
  3453. hintEffect.setLocation(Game.getWidth() / 2, Game.getHeight() / 2 + 20);
  3454. hintEffect.setVisible(true);
  3455. hintEffect.spawn(GameScreen.getInstance());
  3456. hintEffect.setOffset(null);
  3457. }
  3458. @Override
  3459. public void reinit() {
  3460. GameScreen.beginGame(this);
  3461. doInitMessages(Game.getMessage("ultraworm.wormgamestate.reinit1"), Game.getMessage("ultraworm.wormgamestate.reinit2"));
  3462. waitForMouse = true;
  3463. if (mode == Mode.MODE_BUILD) {
  3464. setBuilding(building);
  3465. }
  3466. // Process entities
  3467. for (Iterator<Entity> i = entities.iterator(); i.hasNext(); ) {
  3468. Entity entity = i.next();
  3469. entity.respawn(GameScreen.getInstance());
  3470. }
  3471. // And spawnpoints
  3472. for (Iterator<SpawnPoint> i = spawnPoints.iterator(); i.hasNext(); ) {
  3473. SpawnPoint sp = i.next();
  3474. sp.reinit();
  3475. }
  3476. GameScreen.onBeginLevel();
  3477. if (!isAlive()) {
  3478. MiniGame.endGame();
  3479. }
  3480. }
  3481. /**
  3482. * @return Returns the entities.
  3483. */
  3484. public ArrayList<Entity> getEntities() {
  3485. return entities;
  3486. }
  3487. /**
  3488. * @return Returns the buildings
  3489. */
  3490. public ArrayList<Building> getBuildings() {
  3491. return buildings;
  3492. }
  3493. /**
  3494. * @return Returns the gidrahs
  3495. */
  3496. public ArrayList<Gidrah> getGidrahs() {
  3497. return gidrahs;
  3498. }
  3499. /**
  3500. * @return all the saucers
  3501. */
  3502. public ArrayList<Saucer> getSaucers() {
  3503. return saucers;
  3504. }
  3505. /**
  3506. * Called when it's game over time
  3507. */
  3508. public void onGameOver() {
  3509. if (phase == PHASE_END_OF_GAME) {
  3510. return;
  3511. }
  3512. phase = PHASE_END_OF_GAME;
  3513. tick = 0;
  3514. // Stop building
  3515. setBuilding(null);
  3516. }
  3517. /**
  3518. * Freeze the gidrahs!
  3519. * @param duration
  3520. */
  3521. public void freeze(int duration) {
  3522. freezeTick += duration;
  3523. GameScreen.getInstance().onFreezeTimerIncreased(freezeTick);
  3524. }
  3525. /**
  3526. * Called whenever a gidrah is killed. This removes the gidrah from the gidrahs list,
  3527. * updates stats, and awards medals appropriately
  3528. * @param gidrah The gidrah that was killed
  3529. * @param causeOfDeath For stats and medals purposes
  3530. */
  3531. public void onGidrahKilled(Gidrah gidrah, int causeOfDeath) {
  3532. gidrahs.remove(gidrah);
  3533. switch (causeOfDeath) {
  3534. case CauseOfDeath.ATTACK:
  3535. return;
  3536. case CauseOfDeath.DISRUPTOR:
  3537. case CauseOfDeath.BULLET:
  3538. case CauseOfDeath.LASER:
  3539. metaState.addStat(Stats.ALIENS_SHOT, 1);
  3540. break;
  3541. case CauseOfDeath.CAPACITOR:
  3542. int fried = metaState.addStat(Stats.ALIENS_FRIED, 1);
  3543. if (fried == 100) {
  3544. awardMedal(Medals.FRIED_100);
  3545. } else if (fried == 250) {
  3546. awardMedal(Medals.FRIED_250);
  3547. } else if (fried == 500) {
  3548. awardMedal(Medals.FRIED_500);
  3549. }
  3550. break;
  3551. case CauseOfDeath.CRUSHED:
  3552. int crushed = metaState.addStat(Stats.ALIENS_CRUSHED, 1);
  3553. if (crushed == 25) {
  3554. awardMedal(Medals.CRUSHED_25);
  3555. } else if (crushed == 50) {
  3556. awardMedal(Medals.CRUSHED_50);
  3557. } else if (crushed == 100) {
  3558. awardMedal(Medals.CRUSHED_100);
  3559. }
  3560. break;
  3561. case CauseOfDeath.EXPLOSION:
  3562. int exploded = metaState.addStat(Stats.ALIENS_BLOWN_UP, 1);
  3563. if (exploded == 100) {
  3564. awardMedal(Medals.EXPLODED_100);
  3565. } else if (exploded == 250) {
  3566. awardMedal(Medals.EXPLODED_250);
  3567. } else if (exploded == 500) {
  3568. awardMedal(Medals.EXPLODED_500);
  3569. }
  3570. break;
  3571. case CauseOfDeath.SMARTBOMB:
  3572. int nuked = metaState.addStat(Stats.ALIENS_SMARTBOMBED, 1);
  3573. if (nuked == 50) {
  3574. awardMedal(Medals.NUKED_50);
  3575. } else if (nuked == 100) {
  3576. awardMedal(Medals.NUKED_100);
  3577. } else if (nuked == 250) {
  3578. awardMedal(Medals.NUKED_250);
  3579. }
  3580. break;
  3581. case CauseOfDeath.GRID_BUG:
  3582. // Bah
  3583. return;
  3584. default:
  3585. assert false : "Unknown cause of death "+causeOfDeath;
  3586. break;
  3587. }
  3588. int vanquished = metaState.addStat(Stats.ALIENS_VANQUISHED, 1);
  3589. if (vanquished == 100) {
  3590. awardMedal(Medals.VANQUISHED_100);
  3591. } else if (vanquished == 250) {
  3592. awardMedal(Medals.VANQUISHED_250);
  3593. } else if (vanquished == 500) {
  3594. awardMedal(Medals.VANQUISHED_500);
  3595. } else if (vanquished == 1000) {
  3596. awardMedal(Medals.VANQUISHED_1000);
  3597. } else if (vanquished == 2500) {
  3598. awardMedal(Medals.VANQUISHED_2500);
  3599. } else if (vanquished == 5000) {
  3600. awardMedal(Medals.VANQUISHED_5000);
  3601. } else if (vanquished == 10000) {
  3602. awardMedal(Medals.VANQUISHED_10000);
  3603. }
  3604. // Survival mode: gidrah deaths unlocks more gidrahs
  3605. if (getGameMode() == GAME_MODE_SURVIVAL) {
  3606. if (gidrah.getFeature().isBoss() || gidrah.getFeature().isGidlet()) {
  3607. // Not bosses or gidlets
  3608. } else {
  3609. int type = gidrah.getType();
  3610. survivalGidrahKills[type] += gidrah.getFeature().isAngry() ? 10 : 1;
  3611. if (survivalGidrahKills[type] >= survivalGidrahUnlockNext[type]) {
  3612. int bestWorldIndex = Math.min(WorldFeature.getNumWorlds() - 1, Worm.getMaxLevel(GAME_MODE_CAMPAIGN) / LEVELS_IN_WORLD);
  3613. if (type < survivalGidrahUnlock.length - 2 && survivalGidrahUnlock[type + 1] == 0) {
  3614. // Unlock next type first. If there is one.
  3615. int max = WorldFeature.getWorld(bestWorldIndex).getSurvivalMaxType(type + 1);
  3616. if (max != -1) {
  3617. survivalGidrahUnlock[type + 1] = 1;
  3618. } else {
  3619. metaState.difficulty += config.getSurvivalDifficultyAdjuster();
  3620. }
  3621. } else {
  3622. int max = WorldFeature.getWorld(bestWorldIndex).getSurvivalMaxType(type);
  3623. if (max > survivalGidrahUnlock[type]) {
  3624. survivalGidrahUnlock[type] ++;
  3625. } else {
  3626. metaState.difficulty += config.getSurvivalDifficultyAdjuster();;
  3627. }
  3628. }
  3629. survivalGidrahUnlockNext[type] += survivalGidrahUnlockNext[type] + config.getSurvivalGidrahUnlockIntervalAdjust()[type];
  3630. }
  3631. }
  3632. }
  3633. }
  3634. /**
  3635. * Marks a building type as having been researched
  3636. * @param type
  3637. */
  3638. public void setResearched(String type) {
  3639. synchronized (metaState.researched) {
  3640. if (metaState.researched.add(type)) {
  3641. (ResearchFeature.getResearch().get(type)).onResearched();
  3642. }
  3643. }
  3644. }
  3645. /**
  3646. * Marks a building type as having NOT been researched
  3647. * @param type
  3648. */
  3649. public void setUnresearched(String type) {
  3650. synchronized (metaState.researched) {
  3651. if (metaState.researched.remove(type)) {
  3652. (ResearchFeature.getResearch().get(type)).onUnresearched();
  3653. }
  3654. }
  3655. }
  3656. /**
  3657. * Is the specified ResearchItem researched?
  3658. * @param type
  3659. * @return
  3660. */
  3661. public boolean isResearched(String type) {
  3662. return metaState.researched.contains(type);
  3663. }
  3664. /**
  3665. * @return
  3666. */
  3667. public boolean isBuilding() {
  3668. return building != null;
  3669. }
  3670. /**
  3671. * @return the building we're building, or null, if we're not building
  3672. */
  3673. public BuildingFeature getBuilding() {
  3674. return building;
  3675. }
  3676. /**
  3677. * @return the cost of the current building
  3678. */
  3679. public int getBuildingCost() {
  3680. return building.getShopValue();
  3681. }
  3682. /**
  3683. * @return true if the building we're trying to build is still available to build
  3684. */
  3685. public boolean isBuildingAvailable() {
  3686. return building.isAvailable() && building.isEnabledInShop();
  3687. }
  3688. /**
  3689. * @return the name of the current building
  3690. */
  3691. public String getBuildingName() {
  3692. return building.getTitle();
  3693. }
  3694. /**
  3695. * @return current level index, 0 based
  3696. */
  3697. public int getLevel() {
  3698. return metaState.level;
  3699. }
  3700. /**
  3701. * @return the level in the world (0..LEVELS_IN_WORLD or so)
  3702. */
  3703. public int getLevelInWorld() {
  3704. return metaState.level % LEVELS_IN_WORLD;
  3705. }
  3706. /**
  3707. * @return current world feature
  3708. */
  3709. public WorldFeature getWorld() {
  3710. return metaState.world;
  3711. }
  3712. /**
  3713. * @return current level feature
  3714. */
  3715. public LevelFeature getLevelFeature() {
  3716. return metaState.levelFeature;
  3717. }
  3718. /**
  3719. * Calculates and returns a difficulty value based on level number in the world.
  3720. * @return a float value between 0.0f (very easy) and 1.0f (very hard)
  3721. */
  3722. public float getDifficulty() {
  3723. return currentDifficulty;
  3724. }
  3725. private void calcCurrentDifficulty() {
  3726. if (forceDifficulty) {
  3727. System.out.println("Difficulty forced to "+currentDifficulty);
  3728. return;
  3729. }
  3730. float ret = 0.0f;
  3731. float attenuate = 1.0f;
  3732. float irate = 0.0f;
  3733. boolean survival = getGameMode() == GAME_MODE_SURVIVAL;
  3734. boolean xmas = getGameMode() == GAME_MODE_XMAS;
  3735. // And then attenuate this difficulty by how badly the player is being kicked in:
  3736. if (!survival && !xmas && valueOfDestroyedBuildings > 0 && valueOfBuiltBuildings > config.getBuiltBuildingsValueThreshold()) {
  3737. float slaughterAttenuation = 1.0f;
  3738. // Attenuate the attenuation by the aliens losses when level is ended
  3739. if (!isLevelActive() && aliensSpawnedAtLevelEnd > 0 && aliensVanquishedSinceEndOfLevel > 0) {
  3740. slaughterAttenuation = 1.0f - Math.min(1.0f, Math.max(0.0f, (float) aliensVanquishedSinceEndOfLevel / (float ) aliensSpawnedAtLevelEnd));
  3741. }
  3742. attenuate = 1.0f - Math.min(1.0f, Math.max(0.0f, (float) valueOfDestroyedBuildings / (float ) valueOfBuiltBuildings)) * slaughterAttenuation;
  3743. }
  3744. // Calculate total money in-play
  3745. float total = getMoney() * config.getBankFactor();
  3746. for (int i = buildings.size(); -- i >= 0; ) {
  3747. Building b = buildings.get(i);
  3748. if (b.isAlive()) {
  3749. total += b.getCost();
  3750. irate += b.getAgitation();
  3751. }
  3752. }
  3753. // For every $DIFFICULTY_FACTOR in play add 1.0 difficulty
  3754. switch (getGameMode()) {
  3755. case GAME_MODE_SURVIVAL:
  3756. total += valueOfDestroyedBuildings; // It never gets easier :)
  3757. ret += total / config.getSurvivalModeDifficultyFactors()[survivalParams.getWorld().getIndex()];
  3758. break;
  3759. case GAME_MODE_XMAS:
  3760. total += valueOfDestroyedBuildings;
  3761. ret += total / config.getXmasDifficultyFactor();
  3762. break;
  3763. default:
  3764. ret += total / (config.getDifficultyFactor() + config.getDifficultyFactorPerLevel() * getLevel());
  3765. ret *= attenuate;
  3766. break;
  3767. }
  3768. // Add difficulty for powerups...
  3769. currentDifficulty += powerupDifficulty * attenuate;
  3770. // Finally, adjust by base difficulty offset
  3771. float attemptsDifficulty = Worm.getAutoDifficulty() ? config.getDifficultyAttempts()[Math.min(config.getDifficultyAttempts().length -1, attempts)] : 0.0f;
  3772. currentDifficulty = Math.max(0.0f, irate + ret + metaState.difficulty + attemptsDifficulty);
  3773. }
  3774. public int getResearchHash() {
  3775. return metaState.researched.hashCode();
  3776. }
  3777. /**
  3778. * Kill the player
  3779. */
  3780. public void kill() {
  3781. alive = false;
  3782. }
  3783. /**
  3784. * Quit the game
  3785. */
  3786. public void quit() {
  3787. MiniGame.endGame();
  3788. }
  3789. private void reset() {
  3790. alive = true;
  3791. rush = false;
  3792. mode = Mode.MODE_NORMAL;
  3793. beginLevel = false;
  3794. startingMoney = 500;
  3795. somethingInterestingHappenedTick = 0;
  3796. crystals = 0;
  3797. for (int i = entities.size(); --i >= 0; ) {
  3798. (entities.get(i)).remove();
  3799. }
  3800. entities.clear();
  3801. for (int i = spawnPoints.size(); --i >= 0; ) {
  3802. (spawnPoints.get(i)).remove();
  3803. }
  3804. spawnPoints.clear();
  3805. gidrahs.clear();
  3806. bosses.clear();
  3807. units.clear();
  3808. buildings.clear();
  3809. saucers.clear();
  3810. medalsThisLevel.clear();
  3811. armedCapacitors.clear();
  3812. unminedCrystalList.clear();
  3813. tick = 0;
  3814. totalTicks = 0;
  3815. saucerTick = 0;
  3816. levelTick = 0;
  3817. bezerkTick = 0;
  3818. survivalSpawnPointTick = 0;
  3819. crystalTick = 0;
  3820. survivalCrystals.clear();
  3821. survivalNextBossTick = config.getSurvivalBossInterval();
  3822. survivalNextCrystalTick = 0;
  3823. survivalNextSpawnPointTick = 0;
  3824. survivalSpawnPointInterval = config.getSurvivalSpawnpointSpawnInterval();
  3825. numSurvivalSpawnPoints = 1;
  3826. survivalGidrahUnlock[0] = 1;
  3827. survivalGidrahUnlock[1] = 0;
  3828. survivalGidrahUnlock[2] = 0;
  3829. survivalGidrahUnlock[3] = 0;
  3830. survivalGidrahUnlockNext[0] = config.getSurvivalGidrahUnlockInterval()[0];
  3831. survivalGidrahUnlockNext[1] = config.getSurvivalGidrahUnlockInterval()[1];
  3832. survivalGidrahUnlockNext[2] = config.getSurvivalGidrahUnlockInterval()[2];
  3833. survivalGidrahUnlockNext[3] = config.getSurvivalGidrahUnlockInterval()[3];
  3834. survivalGidrahKills[0] = 0;
  3835. survivalGidrahKills[1] = 0;
  3836. survivalGidrahKills[2] = 0;
  3837. survivalGidrahKills[3] = 0;
  3838. survivalBoss = 0;
  3839. survivalBossTick = 0;
  3840. GameScreen.getInstance().removeAllTickables();
  3841. }
  3842. /**
  3843. * Restart the level
  3844. */
  3845. public void restart() {
  3846. if (metaState.gameMode != GAME_MODE_SURVIVAL && metaState.gameMode != GAME_MODE_XMAS) {
  3847. int attempts = Worm.getExtraLevelData(Game.getPlayerSlot(), metaState.level, metaState.gameMode, "attempts", 0);
  3848. Worm.setExtraLevelData(metaState.level, metaState.gameMode, "attempts", attempts + 1);
  3849. }
  3850. reset();
  3851. doInit(metaState.level);
  3852. }
  3853. private void removeCrystals() {
  3854. // Remove crystals properly so the exclude tiles are removed
  3855. for (Building b : buildings) {
  3856. if (b instanceof CrystalResource) {
  3857. ((CrystalResource) b).clearMap();
  3858. }
  3859. }
  3860. }
  3861. public void restartSurvival(boolean generateNew) {
  3862. removeCrystals();
  3863. reset();
  3864. survivalParams.setGenerateNew(generateNew);
  3865. doInit(survivalParams);
  3866. }
  3867. public void restartXmas(boolean generateNew) {
  3868. removeCrystals();
  3869. reset();
  3870. initXmas(generateNew);
  3871. }
  3872. /**
  3873. * Make the current level easier next time it's played
  3874. */
  3875. public void easier() {
  3876. int diff = getDifficultyAdjust(metaState.level, metaState.gameMode);
  3877. setDifficultyAdjust(metaState.level, metaState.gameMode, diff + 1);
  3878. if (metaState.gameMode == GAME_MODE_CAMPAIGN || metaState.gameMode == GAME_MODE_ENDLESS) {
  3879. // Reset attempts count
  3880. Worm.setExtraLevelData(metaState.level, metaState.gameMode, "attempts", 0);
  3881. }
  3882. }
  3883. /**
  3884. * Save and quit
  3885. */
  3886. public void save() {
  3887. saving = true;
  3888. saveTick = 0;
  3889. LabelEffect saveEffect = new LabelEffect(net.puppygames.applet.Res.getBigFont(), Game.getMessage("ultraworm.wormgamestate.saving_game"), new MappedColor("gui-bright"), new MappedColor("gui-dark"), SAVE_DURATION / 2, SAVE_DURATION / 2);
  3890. saveEffect.setLocation(Game.getWidth() / 2, Game.getHeight() / 2);
  3891. saveEffect.setVisible(true);
  3892. saveEffect.spawn(GameScreen.getInstance());
  3893. saveEffect.setOffset(null);
  3894. }
  3895. public int getCapacitorBoost() {
  3896. return capacitorBoost;
  3897. }
  3898. public int getBatteryBoost() {
  3899. return batteryBoost;
  3900. }
  3901. public int getShieldBoost() {
  3902. return shieldBoost;
  3903. }
  3904. public int getReactorBoost() {
  3905. return reactorBoost;
  3906. }
  3907. public int getScannerBoost() {
  3908. return scannerBoost;
  3909. }
  3910. public int getCoolingBoost() {
  3911. return coolingBoost;
  3912. }
  3913. public void flagHint(String hintName) {
  3914. if (getGameMode() == GAME_MODE_SURVIVAL) {
  3915. // No hints in survival mode
  3916. return;
  3917. }
  3918. HintFeature hint = HintFeature.getHint(hintName);
  3919. if (hint == null) {
  3920. return;
  3921. }
  3922. flagHint(hint);
  3923. }
  3924. private void flagHint(HintFeature hintFeature) {
  3925. if (hintFeature.getMinLevel() > 0 && metaState.level <= hintFeature.getMinLevel()) {
  3926. return;
  3927. }
  3928. if (hintFeature.getMaxLevel() > 0 && metaState.level > hintFeature.getMaxLevel()) {
  3929. return;
  3930. }
  3931. GameScreen.getInstance().showHint(hintFeature);
  3932. }
  3933. public int getHintSequence(HintFeature hintFeature) {
  3934. if (hintFeature.getText() != null) {
  3935. // It's a medal hint
  3936. return 0;
  3937. }
  3938. Integer seq = hintMap.get(hintFeature);
  3939. if (seq == null) {
  3940. seq = new Integer(0);
  3941. }
  3942. int seqValue = seq.intValue();
  3943. if (seqValue == -1) {
  3944. // Hint suppressed
  3945. return -1;
  3946. }
  3947. if (hintFeature.isRandom()) {
  3948. return Util.random(0, hintFeature.getHints().getNumResources() - 1);
  3949. }
  3950. if (seqValue == hintFeature.getHints().getNumResources()) {
  3951. // No hints left
  3952. return -1;
  3953. }
  3954. hintMap.put(hintFeature, new Integer(seqValue + 1));
  3955. return seqValue;
  3956. }
  3957. public void suppressHint(String hint) {
  3958. HintFeature hint2 = HintFeature.getHint(hint);
  3959. if (hint2 == null) {
  3960. System.out.println("Hint "+hint+" not defined");
  3961. return;
  3962. }
  3963. suppressHint(hint2);
  3964. }
  3965. public void suppressHint(HintFeature hintFeature) {
  3966. hintMap.put(hintFeature, new Integer(-1));
  3967. GameScreen.getInstance().dequeueHint(hintFeature);
  3968. }
  3969. /**
  3970. * @return the number of active Units
  3971. */
  3972. public int getNumUnits() {
  3973. return units.size();
  3974. }
  3975. /**
  3976. * Called when a building is destroyed by aliens
  3977. * @param b
  3978. */
  3979. public void onBuildingDestroyed(Building b, boolean deliberate) {
  3980. if (b.isWorthAttacking()) {
  3981. if (!deliberate) {
  3982. valueOfDestroyedBuildings += b.getFeature().getInitialValue();
  3983. numberOfBuildingsDestroyed ++;
  3984. if (b.isCity()) {
  3985. GameScreen.instance.zoom(b.getMapX() + b.getCollisionX() - Game.getWidth() / 2, b.getMapY() + b.getCollisionY() - Game.getHeight() / 2);
  3986. }
  3987. int totalBuildingsDestroyed = addStat(Stats.BUILDINGS_DESTROYED, 1);
  3988. if (totalBuildingsDestroyed == 10) {
  3989. awardMedal(Medals.CARELESS);
  3990. } else if (totalBuildingsDestroyed == 25) {
  3991. awardMedal(Medals.RASH);
  3992. } else if (totalBuildingsDestroyed == 50) {
  3993. awardMedal(Medals.RECKLESS);
  3994. } else if (totalBuildingsDestroyed == 100) {
  3995. awardMedal(Medals.NEGLIGENT);
  3996. }
  3997. addStat(Stats.VALUE_OF_BUILDINGS_DESTROYED, b.getFeature().getInitialValue());
  3998. } else {
  3999. valueOfBuiltBuildings -= b.getFeature().getInitialValue();
  4000. }
  4001. }
  4002. }
  4003. /**
  4004. * Called when any building worth attacking takes actual damage
  4005. */
  4006. public void onBuildingDamaged() {
  4007. anyDamaged = true;
  4008. }
  4009. /**
  4010. * Is the level still active? This is the case when we're in {@link #PHASE_NORMAL} phase and either it's a survival mode game
  4011. * (the level is always active), or the timer's still timing.
  4012. * @return true if the level is still active
  4013. */
  4014. public boolean isLevelActive() {
  4015. return phase == PHASE_NORMAL && (metaState.gameMode == GAME_MODE_SURVIVAL || levelTick < getLevelDuration());
  4016. }
  4017. public Building getBase() {
  4018. return base;
  4019. }
  4020. public Building getNextUnminedCrystal() {
  4021. if (unminedCrystalList.size() == 0) {
  4022. return null;
  4023. }
  4024. Building ret = unminedCrystalList.remove(0);
  4025. unminedCrystalList.add(ret);
  4026. return ret;
  4027. }
  4028. public void addUnminedCrystal(Building crystal) {
  4029. unminedCrystalList.add(crystal);
  4030. }
  4031. public void removeUnminedCrystal(Building crystal) {
  4032. unminedCrystalList.remove(crystal);
  4033. }
  4034. public int getBezerkTick() {
  4035. return bezerkTick;
  4036. }
  4037. public int getFreezeTick() {
  4038. return freezeTick;
  4039. }
  4040. public int getShieldTick() {
  4041. return shieldTick;
  4042. }
  4043. public int getAvailableStock(BuildingFeature bf) {
  4044. return metaState.getAvailableStock(bf);
  4045. }
  4046. public void addAvailableStock(BuildingFeature bf, int n) {
  4047. metaState.addAvailableStock(bf, n);
  4048. GameScreen.getInstance().enableBuildings();
  4049. }
  4050. public float getGidrahDeathRatio() {
  4051. if (aliensSpawnedAtLevelEnd == 0) {
  4052. return 0.0f;
  4053. }
  4054. return (float) aliensVanquishedSinceEndOfLevel / (float) aliensSpawnedAtLevelEnd;
  4055. }
  4056. private void onSomethingInterestingHappened() {
  4057. somethingInterestingHappenedTick = 0;
  4058. }
  4059. /**
  4060. * @return an unmodifiable map of the medals
  4061. */
  4062. public Map<MedalFeature, Integer> getMedals() {
  4063. return Collections.unmodifiableMap(metaState.medals);
  4064. }
  4065. /**
  4066. * Award a medal
  4067. * @param medal
  4068. * @return a MedalFeature if the medal was awarded, null if not
  4069. */
  4070. public MedalFeature awardMedal(String medal) {
  4071. MedalFeature mf = MedalFeature.getMedals().get(medal);
  4072. if (mf == null) {
  4073. if (Game.DEBUG) {
  4074. System.out.println("Warning: medal "+medal+" not found");
  4075. }
  4076. return null;
  4077. }
  4078. Integer n = metaState.medals.get(mf);
  4079. if (n == null) {
  4080. n = Integer.valueOf(1);
  4081. } else {
  4082. if (mf.isRepeatable()) {
  4083. n = Integer.valueOf(n.intValue() + 1);
  4084. } else {
  4085. return null;
  4086. }
  4087. }
  4088. metaState.score += mf.getPoints();
  4089. addMoney(mf.getMoney());
  4090. RankFeature newRank = RankFeature.getRank(metaState.score);
  4091. boolean storeSteamStats = false;
  4092. if (newRank != metaState.rank) {
  4093. System.out.println("New Rank: "+newRank.getTitle());
  4094. metaState.rank = newRank;
  4095. addMoney(newRank.getPoints() / 10);
  4096. if (!suppressMedals) {
  4097. GameScreen.getInstance().showHint(newRank.getHint());
  4098. }
  4099. SFX.newRank();
  4100. if (Game.isUsingSteam() && Steam.isCreated() && Steam.isSteamRunning()) {
  4101. try {
  4102. Steam.getUserStats().setAchievement(newRank.getName());
  4103. } catch (SteamException e) {
  4104. System.err.println("Failed to set achievement "+newRank.getName()+" due to "+e);
  4105. }
  4106. storeSteamStats = true;
  4107. }
  4108. }
  4109. metaState.medals.put(mf, n);
  4110. System.out.println("Awarded "+medal+" ("+n+")");
  4111. if (mf.isSteam() && Game.isUsingSteam() && Steam.isCreated() && Steam.isSteamRunning()) {
  4112. try {
  4113. Steam.getUserStats().setAchievement(mf.getName());
  4114. storeSteamStats = true;
  4115. } catch (SteamException e) {
  4116. System.err.println("Failed to set achievement "+mf.getName()+" due to "+e);
  4117. }
  4118. }
  4119. // Update medals earned this level too
  4120. if (!mf.getSuppressHint()) {
  4121. medalsThisLevel.add(mf);
  4122. }
  4123. // Now, if we're currently playing a level, pop up a hint saying what they just got
  4124. if (phase != PHASE_END_OF_GAME && !mf.getSuppressHint() && !suppressMedals) {
  4125. if (mf.getHint() != null) {
  4126. if (Game.DEBUG) {
  4127. System.out.println("Queued hint "+mf.getHint());
  4128. }
  4129. GameScreen.getInstance().showHint(mf.getHint());
  4130. if (mf.isRepeatable()) {
  4131. SFX.medalAwarded();
  4132. } else {
  4133. SFX.achievementUnlocked();
  4134. }
  4135. } else {
  4136. System.out.println("Medal "+mf+" doesn't have a hint!");
  4137. }
  4138. }
  4139. // Store steam stats
  4140. if (storeSteamStats) {
  4141. try {
  4142. Steam.getUserStats().storeStats();
  4143. } catch (SteamException e) {
  4144. System.err.println("Failed to store steam stats due to "+e);
  4145. }
  4146. }
  4147. return mf;
  4148. }
  4149. /**
  4150. * @return the total score for all awarded medals
  4151. */
  4152. public int getScore() {
  4153. return metaState.score;
  4154. }
  4155. /**
  4156. * @return an unmodifiable map of all the medals earned in this level
  4157. */
  4158. public Set<MedalFeature> getMedalsEarnedThisLevel() {
  4159. return Collections.unmodifiableSet(medalsThisLevel);
  4160. }
  4161. public RankFeature getRank() {
  4162. return metaState.rank;
  4163. }
  4164. /**
  4165. * Gets the value of a stat
  4166. * @param stat
  4167. * @return
  4168. */
  4169. public int getStat(String stat) {
  4170. return addStat(stat, 0);
  4171. }
  4172. /**
  4173. * Add a value to a stat
  4174. * @param stat
  4175. * @param n
  4176. * @return the new stat value
  4177. */
  4178. public int addStat(String stat, int n) {
  4179. return metaState.addStat(stat, n);
  4180. }
  4181. /**
  4182. * @return text for the story screen in Endless mode
  4183. */
  4184. public String getStatsText() {
  4185. boolean showAllStats = false;
  4186. StringBuilder sb = new StringBuilder(256);
  4187. if (getGameMode() == GAME_MODE_SURVIVAL) {
  4188. sb.append(Game.getMessage("ultraworm.wormgamestate.stats_survival"));
  4189. } else if (getGameMode() == GAME_MODE_XMAS) {
  4190. if (getLevel() == 0) {
  4191. // Goes in the ZX bot at the start...
  4192. sb.append(Game.getMessage("ultraworm.wormgamestate.stats_xmas"));
  4193. } else {
  4194. // Goes on the Xmas victory screen at the end
  4195. sb.append("{font:smallfont.glfont color:text-bold}");
  4196. sb.append(Game.getMessage("ultraworm.wormgamestate.xmas.battle_statistics"));
  4197. sb.append("{font:tinyfont.glfont}\n\n");
  4198. int angry = getStat(Stats.ANGRY_SPAWNED);
  4199. int bosses = getStat(Stats.BOSSES_SPAWNED);
  4200. int gidlets = getStat(Stats.GIDLETS_SPAWNED);
  4201. int spawned = getStat(Stats.ALIENS_SPAWNED);
  4202. sb.append("{color:text-bold}"+spawned+" "+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_spawned"));
  4203. if (angry > 0 || bosses > 0 || gidlets > 0 || showAllStats) {
  4204. sb.append("\n\t\t {color:text}");
  4205. int normal = spawned - (angry + gidlets + bosses);
  4206. boolean addSpace = false;
  4207. if (normal > 0 || showAllStats) {
  4208. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.normal_sized_aliens")+": "+normal);
  4209. addSpace = true;
  4210. }
  4211. if (angry > 0 || showAllStats) {
  4212. if (addSpace) {
  4213. sb.append(' ');
  4214. }
  4215. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.large_sized_aliens")+": "+angry);
  4216. addSpace = true;
  4217. }
  4218. if (gidlets > 0 || showAllStats) {
  4219. if (addSpace) {
  4220. sb.append(' ');
  4221. }
  4222. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.tiny_sized_aliens")+": "+gidlets);
  4223. addSpace = true;
  4224. }
  4225. if (bosses > 0 || showAllStats) {
  4226. if (addSpace) {
  4227. sb.append(' ');
  4228. }
  4229. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.bosses")+": "+bosses);
  4230. }
  4231. }
  4232. int vanquished = getStat(Stats.ALIENS_VANQUISHED);
  4233. sb.append("\n{color:text-bold}"+vanquished+" "+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_vanquished"));
  4234. int shot = getStat(Stats.ALIENS_SHOT);
  4235. if (vanquished != shot || showAllStats) {
  4236. int vanquishedCount = 1;
  4237. sb.append("\n\t\t {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_shot")+": "+shot);
  4238. int crushed = getStat(Stats.ALIENS_CRUSHED);
  4239. if (crushed > 0 || showAllStats) {
  4240. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_crushed")+": "+crushed);
  4241. vanquishedCount++;
  4242. }
  4243. int blownUp = getStat(Stats.ALIENS_BLOWN_UP);
  4244. if (blownUp > 0 || showAllStats) {
  4245. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_blown_up")+": "+blownUp);
  4246. vanquishedCount++;
  4247. }
  4248. int fried = getStat(Stats.ALIENS_FRIED);
  4249. if (fried > 0 || showAllStats) {
  4250. vanquishedCount++;
  4251. if (vanquishedCount>3) {
  4252. vanquishedCount=0;
  4253. sb.append("\n\t\t");
  4254. }
  4255. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_fried")+": "+fried);
  4256. }
  4257. int nuked = getStat(Stats.ALIENS_SMARTBOMBED);
  4258. if (nuked > 0 || showAllStats) {
  4259. vanquishedCount++;
  4260. if (vanquishedCount>3) {
  4261. vanquishedCount=0;
  4262. sb.append("\n\t\t");
  4263. }
  4264. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.aliens_nuked")+": "+nuked);
  4265. }
  4266. }
  4267. int alienAttacksOnBuildings = getStat(Stats.ALIEN_ATTACKS_ON_BUILDINGS);
  4268. if (alienAttacksOnBuildings > 0 || showAllStats) {
  4269. String msg = Game.getMessage("ultraworm.wormgamestate.xmas.alien_attacks");
  4270. msg = msg.replace("[num]", String.valueOf(alienAttacksOnBuildings));
  4271. msg = msg.replace("[value]", String.valueOf(getStat(Stats.VALUE_OF_BUILDINGS_DESTROYED)));
  4272. sb.append(msg);
  4273. }
  4274. sb.append("\n{color:text-bold}"+getStat(Stats.BUILDINGS_BUILT)+" "+Game.getMessage("ultraworm.wormgamestate.xmas.buildings_built"));
  4275. sb.append("\n\t\t {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.buildings_destroyed")+": "+getStat(Stats.BUILDINGS_DESTROYED));
  4276. int recycled = getStat(Stats.RECYCLED);
  4277. int sold = getStat(Stats.SOLD);
  4278. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.buildings_sold")+": "+sold);
  4279. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.xmas.buildings_recycled")+": "+recycled);
  4280. int buildCosts = getStat(Stats.VALUE_OF_BUILDINGS_BUILT);
  4281. sb.append("\n {color:text-bold}\\$"+buildCosts+" "+Game.getMessage("ultraworm.wormgamestate.xmas.money_spent"));
  4282. }
  4283. } else if (getLevel() == 0) {
  4284. sb.append(Game.getMessage("ultraworm.wormgamestate.stats_level0"));
  4285. } else {
  4286. sb.append("{font:smallfont.glfont color:text-bold}");
  4287. sb.append(Game.getMessage("ultraworm.wormgamestate.battle_statistics"));
  4288. sb.append("{font:tinyfont.glfont}\n\n");
  4289. int angry = getStat(Stats.ANGRY_SPAWNED);
  4290. int bosses = getStat(Stats.BOSSES_SPAWNED);
  4291. int gidlets = getStat(Stats.GIDLETS_SPAWNED);
  4292. int spawned = getStat(Stats.ALIENS_SPAWNED);
  4293. sb.append("{color:text-bold}"+spawned+" "+Game.getMessage("ultraworm.wormgamestate.aliens_spawned"));
  4294. if (angry > 0 || bosses > 0 || gidlets > 0 || showAllStats) {
  4295. sb.append("\n\t\t {color:text}");
  4296. int normal = spawned - (angry + gidlets + bosses);
  4297. boolean addSpace = false;
  4298. if (normal > 0 || showAllStats) {
  4299. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.normal_sized_aliens")+": "+normal);
  4300. addSpace = true;
  4301. }
  4302. if (angry > 0 || showAllStats) {
  4303. if (addSpace) {
  4304. sb.append(' ');
  4305. }
  4306. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.large_sized_aliens")+": "+angry);
  4307. addSpace = true;
  4308. }
  4309. if (gidlets > 0 || showAllStats) {
  4310. if (addSpace) {
  4311. sb.append(' ');
  4312. }
  4313. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.tiny_sized_aliens")+": "+gidlets);
  4314. addSpace = true;
  4315. }
  4316. if (bosses > 0 || showAllStats) {
  4317. if (addSpace) {
  4318. sb.append(' ');
  4319. }
  4320. sb.append("{color:text}"+Game.getMessage("ultraworm.wormgamestate.bosses")+": "+bosses);
  4321. }
  4322. }
  4323. int vanquished = getStat(Stats.ALIENS_VANQUISHED);
  4324. sb.append("\n{color:text-bold}"+vanquished+" "+Game.getMessage("ultraworm.wormgamestate.aliens_vanquished"));
  4325. int shot = getStat(Stats.ALIENS_SHOT);
  4326. if (vanquished != shot || showAllStats) {
  4327. int vanquishedCount = 1;
  4328. sb.append("\n\t\t {color:text}"+Game.getMessage("ultraworm.wormgamestate.aliens_shot")+": "+shot);
  4329. int crushed = getStat(Stats.ALIENS_CRUSHED);
  4330. if (crushed > 0 || showAllStats) {
  4331. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.aliens_crushed")+": "+crushed);
  4332. vanquishedCount++;
  4333. }
  4334. int blownUp = getStat(Stats.ALIENS_BLOWN_UP);
  4335. if (blownUp > 0 || showAllStats) {
  4336. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.aliens_blown_up")+": "+blownUp);
  4337. vanquishedCount++;
  4338. }
  4339. int fried = getStat(Stats.ALIENS_FRIED);
  4340. if (fried > 0 || showAllStats) {
  4341. vanquishedCount++;
  4342. if (vanquishedCount>3) {
  4343. vanquishedCount=0;
  4344. sb.append("\n\t\t");
  4345. }
  4346. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.aliens_fried")+": "+fried);
  4347. }
  4348. int nuked = getStat(Stats.ALIENS_SMARTBOMBED);
  4349. if (nuked > 0 || showAllStats) {
  4350. vanquishedCount++;
  4351. if (vanquishedCount>3) {
  4352. vanquishedCount=0;
  4353. sb.append("\n\t\t");
  4354. }
  4355. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.aliens_nuked")+": "+nuked);
  4356. }
  4357. }
  4358. int alienAttacksOnBuildings = getStat(Stats.ALIEN_ATTACKS_ON_BUILDINGS);
  4359. if (alienAttacksOnBuildings > 0 || showAllStats) {
  4360. String msg = Game.getMessage("ultraworm.wormgamestate.alien_attacks");
  4361. msg = msg.replace("[num]", String.valueOf(alienAttacksOnBuildings));
  4362. msg = msg.replace("[value]", String.valueOf(getStat(Stats.VALUE_OF_BUILDINGS_DESTROYED)));
  4363. sb.append(msg);
  4364. }
  4365. sb.append("\n{color:text-bold}"+getStat(Stats.BUILDINGS_BUILT)+" "+Game.getMessage("ultraworm.wormgamestate.buildings_built"));
  4366. sb.append("\n\t\t {color:text}"+Game.getMessage("ultraworm.wormgamestate.buildings_destroyed")+": "+getStat(Stats.BUILDINGS_DESTROYED));
  4367. int recycled = getStat(Stats.RECYCLED);
  4368. int sold = getStat(Stats.SOLD);
  4369. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.buildings_sold")+": "+sold);
  4370. sb.append(" {color:text}"+Game.getMessage("ultraworm.wormgamestate.buildings_recycled")+": "+recycled);
  4371. int buildCosts = getStat(Stats.VALUE_OF_BUILDINGS_BUILT);
  4372. sb.append("\n {color:text-bold}\\$"+buildCosts+" "+Game.getMessage("ultraworm.wormgamestate.money_spent"));
  4373. }
  4374. return sb.toString();
  4375. }
  4376. public void setAwesome() {
  4377. awesome = true;
  4378. }
  4379. public boolean isAwesome() {
  4380. return awesome;
  4381. }
  4382. public int getCrystals() {
  4383. return crystals;
  4384. }
  4385. public int getInitialCrystals() {
  4386. return initialCrystals;
  4387. }
  4388. public PowerupFeature getCrappyPowerup() {
  4389. return metaState.getCrappyPowerup();
  4390. }
  4391. public PowerupFeature getRandomPowerup() {
  4392. return metaState.getRandomPowerup();
  4393. }
  4394. /**
  4395. * @return an exotic powerup
  4396. */
  4397. public PowerupFeature getExoticPowerup() {
  4398. return metaState.getExoticPowerup();
  4399. }
  4400. /**
  4401. * @return a non-exotic powerup
  4402. */
  4403. public PowerupFeature getPowerup() {
  4404. return metaState.getPowerup();
  4405. }
  4406. /**
  4407. * @return an analysis of what went wrong
  4408. */
  4409. public String getAnalysis() {
  4410. boolean lotsOfMoneyLeft = getMoney() > 750 * (getLevel() / LEVELS_IN_WORLD);
  4411. int numBuildings = 0;
  4412. int numTurrets = 0;
  4413. for (Building b : getBuildings()) {
  4414. if (b.canSell()) {
  4415. numBuildings ++;
  4416. }
  4417. if (b instanceof Turret) {
  4418. numTurrets ++;
  4419. }
  4420. }
  4421. boolean lotsOfBuildings = numBuildings > getLevel();
  4422. boolean lotsOfRefineries = totalFactories >= initialCrystals * 3;
  4423. boolean lotsOfTurrets = numTurrets > spawnPoints.size() * 1.5f + getWorld().getIndex();
  4424. boolean hasUnrefinedCrystals = unminedCrystalList.size() > 0;
  4425. boolean lotsOfAliensSlain = aliensVanquishedValue > aliensSpawnedValue * 0.75 && aliensSpawnedValue > 1000;
  4426. boolean lotsDestroyed = (float) valueOfDestroyedBuildings / (float) valueOfBuiltBuildings > 0.5f;
  4427. boolean hasPowerups = false;
  4428. for (Integer i : metaState.powerups.values()) {
  4429. if (i > 0) {
  4430. hasPowerups = true;
  4431. break;
  4432. }
  4433. }
  4434. class Message implements Comparable<Message> {
  4435. int priority;
  4436. String msg;
  4437. public Message(int priority, String msg) {
  4438. this.priority = priority;
  4439. this.msg = msg;
  4440. }
  4441. @Override
  4442. public int compareTo(Message o) {
  4443. if (o.priority > priority) {
  4444. return 1;
  4445. } else if (o.priority < priority) {
  4446. return -1;
  4447. } else {
  4448. return 0;
  4449. }
  4450. }
  4451. }
  4452. List<Message> messages = new ArrayList<Message>();
  4453. if (!lotsOfTurrets) {
  4454. messages.add(new Message(700, Game.getMessage("ultraworm.wormgamestate.hint1")));
  4455. }
  4456. if (lotsDestroyed) {
  4457. messages.add(new Message(800, Game.getMessage("ultraworm.wormgamestate.hint2")));
  4458. } else if (valueOfBuiltBuildings > 2500) {
  4459. messages.add(new Message(600, Game.getMessage("ultraworm.wormgamestate.hint3")));
  4460. }
  4461. if (!lotsOfAliensSlain && lotsOfRefineries) {
  4462. messages.add(new Message(200, Game.getMessage("ultraworm.wormgamestate.hint4")));
  4463. }
  4464. if (hasUnrefinedCrystals) {
  4465. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint5")));
  4466. }
  4467. if (!lotsOfRefineries && (getLevel() > 4 || getGameMode() == GAME_MODE_SURVIVAL)) {
  4468. messages.add(new Message(900, Game.getMessage("ultraworm.wormgamestate.hint6")));
  4469. }
  4470. if (hasPowerups) {
  4471. messages.add(new Message(500, Game.getMessage("ultraworm.wormgamestate.hint7")));
  4472. }
  4473. if (!lotsOfMoneyLeft) {
  4474. if (lotsOfBuildings) {
  4475. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint8")));
  4476. } else {
  4477. if (isResearched(ResearchFeature.CONCRETE)) {
  4478. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint9")));
  4479. } else if (isResearched(ResearchFeature.TANGLEWEB)) {
  4480. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint10")));
  4481. } else if (isResearched(ResearchFeature.MINES)) {
  4482. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint11")));
  4483. } else {
  4484. messages.add(new Message(100, Game.getMessage("ultraworm.wormgamestate.hint12")));
  4485. }
  4486. }
  4487. } else {
  4488. messages.add(new Message(1000, Game.getMessage("ultraworm.wormgamestate.hint13")));
  4489. }
  4490. if (getLevelFeature().getBosses() != null) {
  4491. if (getLevelFeature().getBosses().getNumResources() == 1) {
  4492. messages.add(new Message(900, Game.getMessage("ultraworm.wormgamestate.hint14")));
  4493. } else {
  4494. messages.add(new Message(900, Game.getMessage("ultraworm.wormgamestate.hint15")));
  4495. }
  4496. }
  4497. StringBuilder sb = new StringBuilder(256);
  4498. sb.append("{color:text-bold}");
  4499. sb.append(Game.getMessage("ultraworm.wormgamestate.battle_analysis"));
  4500. sb.append(":{color:text}");
  4501. Collections.sort(messages);
  4502. int count = 0;
  4503. for (Message m : messages) {
  4504. sb.append("\n\t\t");
  4505. sb.append(m.msg);
  4506. if (++ count == 3) {
  4507. break;
  4508. }
  4509. }
  4510. return sb.toString();
  4511. }
  4512. public float getScavengeRate() {
  4513. int rate = Worm.getGameState().isResearched(ResearchFeature.FINETUNING) ? 1 : 0;
  4514. rate += Worm.getGameState().isResearched(ResearchFeature.EXTRACTION) ? 1 : 0;
  4515. return config.getScavengeRate()[rate];
  4516. }
  4517. // public MapGeneratorParams getMapGeneratorParams() {
  4518. // return new MapGeneratorParams( this.getBasicDifficulty(), this.getGameMode(), this.getLevel(), this.getLevelFeature(), this.getLevelInWorld(), this.getMoney(), this.getResearchHash(), this.getWorld() );
  4519. // }
  4520. /**
  4521. * Hax! This returns true if we want the level generator thread to generate a new level, or false if not.
  4522. * @return
  4523. * @see #initXmas(boolean)
  4524. */
  4525. public boolean isXmasReset() {
  4526. return xmasReset;
  4527. }
  4528. }