hexoshi.py 252 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001
  1. #!/usr/bin/env python
  2. # Hexoshi
  3. # Copyright (C) 2014-2018 Julie Marchant <onpon4@riseup.net>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. from __future__ import division
  18. from __future__ import absolute_import
  19. from __future__ import print_function
  20. from __future__ import unicode_literals
  21. __version__ = "0.2.1a0"
  22. import argparse
  23. import datetime
  24. import gettext
  25. import itertools
  26. import json
  27. import math
  28. import os
  29. import random
  30. import sys
  31. import time
  32. import traceback
  33. import warnings
  34. import weakref
  35. import sge
  36. print(sge.IMPLEMENTATION, sge.__version__)
  37. import six
  38. import xsge_gui
  39. print("xsge_gui", xsge_gui.__version__)
  40. import xsge_lighting
  41. print("xsge_lighting", xsge_lighting.__version__)
  42. import xsge_particle
  43. print("xsge_particle", xsge_particle.__version__)
  44. import xsge_path
  45. print("xsge_path", xsge_path.__version__)
  46. import xsge_physics
  47. print("xsge_physics", xsge_physics.__version__)
  48. import xsge_tmx
  49. print("xsge_tmx", xsge_tmx.__version__)
  50. if getattr(sys, "frozen", False):
  51. __file__ = sys.executable
  52. DATA = os.path.join(os.path.dirname(__file__), "data")
  53. CONFIG = os.path.join(os.path.expanduser("~"), ".config", "hexoshi")
  54. SCREEN_SIZE = [400, 224]
  55. TILE_SIZE = 16
  56. FPS = 60
  57. DELTA_MIN = FPS / 2
  58. DELTA_MAX = FPS * 4
  59. SCALE = 2
  60. FSSCALE = None
  61. if six.PY2:
  62. gettext.install("hexoshi", os.path.abspath(os.path.join(DATA, "locale")),
  63. unicode=True)
  64. else:
  65. gettext.install("hexoshi", os.path.abspath(os.path.join(DATA, "locale")))
  66. parser = argparse.ArgumentParser(prog="Hexoshi")
  67. parser.add_argument(
  68. "--version", action="version", version="%(prog)s " + __version__,
  69. help=_("Output version information and exit."))
  70. parser.add_argument(
  71. "-p", "--print-errors",
  72. help=_("Print errors directly to stdout rather than saving them in a file."),
  73. action="store_true")
  74. parser.add_argument(
  75. "-l", "--lang",
  76. help=_("Manually choose a different language to use."))
  77. parser.add_argument(
  78. "--nodelta",
  79. help=_("Disable delta timing. Causes the game to slow down when it can't run at full speed instead of becoming choppier."),
  80. action="store_true")
  81. parser.add_argument(
  82. "-d", "--datadir",
  83. help=_('Where to load the game data from (Default: "{}")').format(DATA))
  84. parser.add_argument(
  85. "--scale",
  86. help=_('The scale factor to use by default in windowed mode (Default: "{}")').format(SCALE))
  87. parser.add_argument(
  88. "--fsscale",
  89. help=_("Specify a scale factor to use in fullscreen mode instead of using dynamic scaling. This will cause the screen resolution to change, which may improve performance. For best results, specify this as the target resolution width divided by {w}, or as the target resolution height divided by {h} (whichever is smaller). For example, to target a resolution of 640x480, use {ex}. A scale factor of 1 will always be fastest, but may result in windowboxing.").format(
  90. w=SCREEN_SIZE[0], h=SCREEN_SIZE[1],
  91. ex=min(640 / SCREEN_SIZE[0], 480 / SCREEN_SIZE[1])))
  92. parser.add_argument(
  93. "--no-backgrounds",
  94. help=_("Only show solid colors for backgrounds (uses less RAM)."),
  95. action="store_true")
  96. parser.add_argument(
  97. "--no-hud", help=_("Don't show the player's heads-up display."),
  98. action="store_true")
  99. parser.add_argument(
  100. "-m", "--gen-map", help=_("Generate the map even if it already exists."),
  101. action="store_true")
  102. parser.add_argument(
  103. "-s", "--save-map", help=_('Save an image of the full map as "map.png".'),
  104. action="store_true")
  105. parser.add_argument("--god")
  106. args = parser.parse_args()
  107. PRINT_ERRORS = args.print_errors
  108. DELTA = not args.nodelta
  109. if args.datadir:
  110. DATA = args.datadir
  111. if args.scale:
  112. SCALE = eval(args.scale)
  113. if args.fsscale:
  114. FSSCALE = eval(args.fsscale)
  115. NO_BACKGROUNDS = args.no_backgrounds
  116. NO_HUD = args.no_hud
  117. GEN_MAP = args.gen_map
  118. SAVE_MAP = args.save_map
  119. GOD = (args.god and args.god.lower() == "inbailey")
  120. if args.lang:
  121. lang = gettext.translation("hexoshi",
  122. os.path.abspath(os.path.join(DATA, "locale")),
  123. [args.lang])
  124. if six.PY2:
  125. lang.install(unicode=True)
  126. else:
  127. lang.install()
  128. GRAVITY = 0.4
  129. PLAYER_MAX_HP = 50
  130. PLAYER_MAX_SPEED = 3
  131. PLAYER_ROLL_MAX_SPEED = 8
  132. PLAYER_ACCELERATION = 0.5
  133. PLAYER_ROLL_ACCELERATION = 0.25
  134. PLAYER_AIR_ACCELERATION = 0.15
  135. PLAYER_FRICTION = 0.75
  136. PLAYER_ROLL_FRICTION = 0.02
  137. PLAYER_AIR_FRICTION = 0.02
  138. PLAYER_JUMP_HEIGHT = 5 * TILE_SIZE + 2
  139. PLAYER_FALL_SPEED = 7
  140. PLAYER_SLIDE_SPEED = 0.25
  141. PLAYER_ROLL_SLIDE_SPEED = 0
  142. PLAYER_ROLL_SLOPE_ACCELERATION = 0.25
  143. PLAYER_HITSTUN = FPS
  144. PLAYER_AIM_LOCK_TIME = FPS / 2
  145. WARP_TIME = FPS / 10
  146. DEATH_TIME = 3 * FPS
  147. DOUBLETAP_TIME = FPS / 3
  148. ANNEROY_BALL_BOUNCE_HEIGHT = 2
  149. ANNEROY_BALL_FORCE_BOUNCE_SPEED = 4
  150. ANNEROY_WALLJUMP_HEIGHT = 3 * TILE_SIZE
  151. ANNEROY_WALLJUMP_SPEED = PLAYER_MAX_SPEED
  152. ANNEROY_WALLJUMP_FRAME_TIME = FPS / 4
  153. ANNEROY_RUN_FRAMES_PER_PIXEL = 1 / 10
  154. ANNEROY_BALL_FRAMES_PER_PIXEL = 1 / 4
  155. ANNEROY_BBOX_X = -7
  156. ANNEROY_BBOX_WIDTH = 14
  157. ANNEROY_STAND_BBOX_Y = -16
  158. ANNEROY_STAND_BBOX_HEIGHT = 40
  159. ANNEROY_CROUCH_BBOX_Y = -5
  160. ANNEROY_CROUCH_BBOX_HEIGHT = 29
  161. ANNEROY_BALL_BBOX_Y = 10
  162. ANNEROY_BALL_BBOX_HEIGHT = 14
  163. ANNEROY_HEDGEHOG_TIME = 15
  164. ANNEROY_HEDGEHOG_FRAME_TIME = 4
  165. ANNEROY_HEDGEHOG_BBOX_X = -14
  166. ANNEROY_HEDGEHOG_BBOX_Y = 3
  167. ANNEROY_HEDGEHOG_BBOX_WIDTH = 28
  168. ANNEROY_HEDGEHOG_BBOX_HEIGHT = 28
  169. ANNEROY_SLOTH_MAX_SPEED = 0.5
  170. ANNEROY_BULLET_SPEED = 8
  171. ANNEROY_BULLET_DSPEED = ANNEROY_BULLET_SPEED * math.sin(math.radians(45))
  172. ANNEROY_BULLET_LIFE = 45
  173. ANNEROY_EXPLODE_TIME = 0.6 * FPS
  174. ANNEROY_DECOMPRESS_LAX = 4
  175. MANTANOID_WANDER_SPEED = 1
  176. MANTANOID_WANDER_INTERVAL = FPS * 2
  177. MANTANOID_APPROACH_SPEED = 1.5
  178. MANTANOID_APPROACH_INTERVAL = FPS / 4
  179. MANTANOID_HOP_HEIGHT = 2 * TILE_SIZE
  180. MANTANOID_JUMP_HEIGHT = 4 * TILE_SIZE
  181. MANTANOID_WALK_FRAMES_PER_PIXEL = 1 / 6
  182. MANTANOID_LEVEL_DISTANCE = 48
  183. MANTANOID_SLASH_DISTANCE = 30
  184. MANTANOID_SLASH2_DISTANCE = 44
  185. MANTANOID_BBOX_X = -12
  186. MANTANOID_BBOX_Y = -16
  187. MANTANOID_BBOX_WIDTH = 24
  188. MANTANOID_BBOX_HEIGHT = 48
  189. MANTANOID_SLASH_BBOX_X = -12
  190. MANTANOID_SLASH_BBOX_Y = -27
  191. MANTANOID_SLASH_BBOX_WIDTH = 38
  192. MANTANOID_SLASH_BBOX_HEIGHT = 59
  193. MANTANOID_DOUBLESLASH_OFFSET = 18
  194. MANTANOID_SLASH2_BBOX_X = 0
  195. MANTANOID_SLASH2_BBOX_Y = -20
  196. MANTANOID_SLASH2_BBOX_WIDTH = 24
  197. MANTANOID_SLASH2_BBOX_HEIGHT = 32
  198. SCORPION_WALK_FRAMES_PER_PIXEL = 1 / 6
  199. CEILING_LAX = 2
  200. CAMERA_HSPEED_FACTOR = 1 / 8
  201. CAMERA_VSPEED_FACTOR = 1 / 20
  202. CAMERA_OFFSET_FACTOR = 10
  203. CAMERA_MARGIN_TOP = 3 * TILE_SIZE
  204. CAMERA_MARGIN_BOTTOM = 3 * TILE_SIZE
  205. CAMERA_TARGET_MARGIN_BOTTOM = SCREEN_SIZE[1] / 2
  206. LIFE_FORCE_CHANCE = 0.25
  207. LIFE_FORCE_SPEED = 1
  208. LIFE_FORCE_HEAL = 5
  209. LIGHT_RANGE = 300
  210. SHAKE_FRAME_TIME = FPS / DELTA_MIN
  211. SHAKE_AMOUNT = 3
  212. MAP_CELL_WIDTH = 8
  213. MAP_CELL_HEIGHT = 8
  214. TEXT_SPEED = 1000
  215. SAVE_NSLOTS = 10
  216. MENU_MAX_ITEMS = 14
  217. SOUND_MAX_RADIUS = 200
  218. SOUND_ZERO_RADIUS = 600
  219. SOUND_CENTERED_RADIUS = 75
  220. SOUND_TILTED_RADIUS = 500
  221. SOUND_TILT_LIMIT = 0.75
  222. ETANK_CHAR = '\x80'
  223. backgrounds = {}
  224. loaded_music = {}
  225. fullscreen = False
  226. scale_method = None
  227. sound_enabled = True
  228. music_enabled = True
  229. stereo_enabled = True
  230. fps_enabled = False
  231. metroid_controls = False
  232. joystick_threshold = 0.1
  233. left_key = [["left", "a"]]
  234. right_key = [["right", "d"]]
  235. up_key = [["up", "w"]]
  236. down_key = [["down", "s"]]
  237. aim_diag_key = [["alt_left", "alt_right"]]
  238. jump_key = [["space"]]
  239. shoot_key = [["ctrl_left", "ctrl_right"]]
  240. aim_up_key = [["x"]]
  241. aim_down_key = [["z"]]
  242. mode_reset_key = [["shift_left", "shift_right"]]
  243. mode_key = [["tab"]]
  244. pause_key = [["enter", "p"]]
  245. map_key = [["m"]]
  246. left_js = [[(0, "axis-", 0), (0, "hat_left", 0)]]
  247. right_js = [[(0, "axis+", 0), (0, "hat_right", 0)]]
  248. up_js = [[(0, "axis-", 1), (0, "hat_up", 0)]]
  249. down_js = [[(0, "axis+", 1), (0, "hat_down", 0)]]
  250. aim_diag_js = [[(0, "button", 10), (0, "button", 11)]]
  251. jump_js = [[(0, "button", 1), (0, "button", 3)]]
  252. shoot_js = [[(0, "button", 0)]]
  253. aim_up_js = [[(0, "button", 5), (0, "button", 7)]]
  254. aim_down_js = [[(0, "button", 4), (0, "button", 6)]]
  255. mode_reset_js = [[(0, "button", 2)]]
  256. mode_js = [[(0, "button", 8)]]
  257. pause_js = [[(0, "button", 9)]]
  258. map_js = [[]]
  259. save_slots = [None for i in six.moves.range(SAVE_NSLOTS)]
  260. with open(os.path.join(DATA, "ai_data.json"), 'r') as f:
  261. ai_data = set(json.load(f))
  262. abort = False
  263. current_save_slot = None
  264. player_name = "Anneroy"
  265. watched_timelines = []
  266. current_level = None
  267. spawn_point = None
  268. map_revealed = []
  269. map_explored = []
  270. map_removed = []
  271. warp_pads = []
  272. powerups = []
  273. progress_flags = []
  274. artifacts = 0
  275. etanks = 0
  276. time_taken = 0
  277. spawn_xoffset = 0
  278. spawn_yoffset = 0
  279. player = None
  280. class Game(sge.dsp.Game):
  281. fps_real = FPS
  282. fps_time = 0
  283. fps_frames = 0
  284. fps_text = ""
  285. cheatcode = ""
  286. def event_step(self, time_passed, delta_mult):
  287. self.fps_time += time_passed
  288. self.fps_frames += 1
  289. if self.fps_time >= 250:
  290. self.fps_real = (1000 * self.fps_frames) / self.fps_time
  291. self.fps_text = '{:.2f}'.format(self.fps_real)
  292. self.fps_time = 0
  293. self.fps_frames = 0
  294. if fps_enabled:
  295. self.project_text(font_small, self.fps_text, self.width - 8,
  296. self.height - 8, z=1000000,
  297. color=sge.gfx.Color("yellow"), halign="right",
  298. valign="bottom")
  299. def event_key_press(self, key, char):
  300. if key == "f7":
  301. self.cheatcode = ""
  302. elif sge.keyboard.get_pressed("f7"):
  303. if not self.cheatcode:
  304. print(_("Code entry:"), end=' ')
  305. self.cheatcode += char
  306. print(char, end='')
  307. sys.stdout.flush()
  308. def event_key_release(self, key):
  309. global map_revealed
  310. global map_explored
  311. if key == "f7":
  312. if self.cheatcode:
  313. print()
  314. if self.cheatcode.lower() == "knowitall":
  315. map_revealed = list(map_objects.keys())
  316. elif self.cheatcode.lower() == "seenitall":
  317. map_explored = map_revealed
  318. elif self.cheatcode.startswith("tele"):
  319. warp(self.cheatcode[4:] + ".tmx")
  320. else:
  321. print(_("Invalid cheat code: {}").format(self.cheatcode))
  322. def event_mouse_button_press(self, button):
  323. if button == "middle":
  324. self.event_close()
  325. def event_close(self):
  326. self.end()
  327. def event_paused_close(self):
  328. self.event_close()
  329. class Level(sge.dsp.Room):
  330. """Handles levels."""
  331. def __init__(self, objects=(), width=None, height=None, views=None,
  332. background=None, background_x=0, background_y=0,
  333. object_area_width=TILE_SIZE * 2,
  334. object_area_height=TILE_SIZE * 2,
  335. name=None, bgname=None, music=None, timeline=None,
  336. ambient_light=None, disable_lights=False,
  337. music_noloop=False):
  338. self.fname = None
  339. self.name = name
  340. self.music = music
  341. self.music_noloop = music_noloop
  342. self.timeline_objects = {}
  343. self.shake_queue = 0
  344. self.death_time = None
  345. self.status_text = None
  346. self.player_z = 0
  347. if bgname is not None:
  348. background = backgrounds.get(bgname, background)
  349. self.load_timeline(timeline)
  350. if ambient_light:
  351. self.ambient_light = sge.gfx.Color(ambient_light)
  352. if (self.ambient_light.red >= 255 and
  353. self.ambient_light.green >= 255 and
  354. self.ambient_light.blue >= 255):
  355. self.ambient_light = None
  356. else:
  357. self.ambient_light = None
  358. self.disable_lights = disable_lights or self.ambient_light is None
  359. super(Level, self).__init__(objects, width, height, views, background,
  360. background_x, background_y,
  361. object_area_width, object_area_height)
  362. self.add(gui_handler)
  363. def load_timeline(self, timeline):
  364. self.timeline = {}
  365. self.timeline_name = ""
  366. self.timeline_step = 0
  367. self.timeline_skip_target = None
  368. if timeline:
  369. self.timeline_name = timeline
  370. fname = os.path.join(DATA, "timelines", timeline)
  371. with open(fname, 'r') as f:
  372. jt = json.load(f)
  373. for i in jt:
  374. self.timeline[eval(i)] = jt[i]
  375. def add_timeline_object(self, obj):
  376. if obj.ID is not None:
  377. self.timeline_objects[obj.ID] = weakref.ref(obj)
  378. def timeline_skipto(self, step):
  379. t_keys = sorted(self.timeline.keys())
  380. self.timeline_step = step
  381. while t_keys and t_keys[0] < step:
  382. i = t_keys.pop(0)
  383. self.timeline[i] = []
  384. def show_hud(self):
  385. # Show darkness
  386. if self.ambient_light:
  387. xsge_lighting.project_darkness(ambient_light=self.ambient_light,
  388. buffer=TILE_SIZE * 2)
  389. else:
  390. xsge_lighting.clear_lights()
  391. if not NO_HUD:
  392. # TODO: Add HUD showing health, ammo, etc.
  393. if self.status_text:
  394. sge.game.project_text(font, self.status_text,
  395. sge.game.width / 2, sge.game.height - 16,
  396. color=sge.gfx.Color("white"),
  397. halign="center", valign="middle")
  398. self.status_text = None
  399. def shake(self, num=1):
  400. shaking = (self.shake_queue or "shake_up" in self.alarms or
  401. "shake_down" in self.alarms)
  402. self.shake_queue = max(self.shake_queue, num)
  403. if not shaking:
  404. self.event_alarm("shake_down")
  405. for obj in self.objects:
  406. if isinstance(obj, SteadyIcicle):
  407. obj.check_shake(True)
  408. def pause(self, player_x=None, player_y=None):
  409. if (self.timeline_skip_target is not None and
  410. self.timeline_step < self.timeline_skip_target):
  411. self.timeline_skipto(self.timeline_skip_target)
  412. else:
  413. play_sound(pause_sound)
  414. PauseMenu.create(player_x=player_x, player_y=player_y)
  415. def die(self):
  416. sge.game.start_room.start(transition="fade")
  417. def win_game(self):
  418. credits_room = CreditsScreen.load(os.path.join("special",
  419. "credits.tmx"))
  420. credits_room.start()
  421. def event_room_start(self):
  422. if player is not None:
  423. self.add(player)
  424. ##self.add(lava_animation)
  425. xsge_lighting.clear_lights()
  426. play_music(self.music, noloop=self.music_noloop)
  427. def event_room_resume(self):
  428. play_music(self.music, noloop=self.music_noloop)
  429. def event_step(self, time_passed, delta_mult):
  430. global watched_timelines
  431. global time_taken
  432. time_taken += time_passed / 1000
  433. for view in self.views:
  434. for obj in self.get_objects_at(
  435. view.x - LIGHT_RANGE, view.y - LIGHT_RANGE,
  436. view.width + LIGHT_RANGE * 2,
  437. view.height + LIGHT_RANGE * 2):
  438. if isinstance(obj, InteractiveObject):
  439. if not self.disable_lights:
  440. obj.project_light()
  441. ##if not obj.active:
  442. ##if isinstance(obj, (Lava, LavaSurface)):
  443. ## obj.image_index = lava_animation.image_index
  444. # Show HUD
  445. self.show_hud()
  446. # Timeline events
  447. t_keys = sorted(self.timeline.keys())
  448. while t_keys:
  449. i = t_keys.pop(0)
  450. if i <= self.timeline_step:
  451. while i in self.timeline and self.timeline[i]:
  452. command = self.timeline[i].pop(0)
  453. command = command.split(None, 1)
  454. if command:
  455. if len(command) >= 2:
  456. command, arg = command[:2]
  457. else:
  458. command = command[0]
  459. arg = ""
  460. if command.startswith("#"):
  461. # Comment; do nothing
  462. pass
  463. elif command == "setattr":
  464. args = arg.split(None, 2)
  465. if len(args) >= 3:
  466. obj, name, value = args[:3]
  467. try:
  468. value = eval(value)
  469. except Exception as e:
  470. m = _("An error occurred in a timeline 'setattr' command:\n\n{}").format(
  471. traceback.format_exc())
  472. show_error(m)
  473. else:
  474. if obj in self.timeline_objects:
  475. obj = self.timeline_objects[obj]()
  476. if obj is not None:
  477. setattr(obj, name, value)
  478. elif obj == "__level__":
  479. setattr(self, name, value)
  480. elif command == "call":
  481. args = arg.split()
  482. if len(args) >= 2:
  483. obj, method = args[:2]
  484. fa = [eval(s) for s in args[2:]]
  485. if obj in self.timeline_objects:
  486. obj = self.timeline_objects[obj]()
  487. if obj is not None:
  488. getattr(obj, method, lambda: None)(*fa)
  489. elif obj == "__level__":
  490. getattr(self, method, lambda: None)(*fa)
  491. elif command == "dialog":
  492. DialogBox(gui_handler, _(arg)).show()
  493. elif command == "play_music":
  494. self.music = arg
  495. play_music(arg)
  496. elif command == "timeline":
  497. if self.timeline_name not in watched_timelines:
  498. watched_timelines = watched_timelines[:]
  499. watched_timelines.append(self.timeline_name)
  500. self.load_timeline(arg)
  501. break
  502. elif command == "skip_to":
  503. try:
  504. arg = float(arg)
  505. except ValueError:
  506. pass
  507. else:
  508. self.timeline_skipto(arg)
  509. break
  510. elif command == "exec":
  511. try:
  512. six.exec_(arg)
  513. except Exception as e:
  514. m = _("An error occurred in a timeline 'exec' command:\n\n{}").format(
  515. traceback.format_exc())
  516. show_error(m)
  517. elif command == "if":
  518. try:
  519. r = eval(arg)
  520. except Exception as e:
  521. m = _("An error occurred in a timeline 'if' statement:\n\n{}").format(
  522. traceback.format_exc())
  523. show_error(m)
  524. r = False
  525. finally:
  526. if not r:
  527. self.timeline[i] = []
  528. break
  529. elif command == "if_watched":
  530. if self.timeline_name not in watched_timelines:
  531. self.timeline[i] = []
  532. break
  533. elif command == "if_not_watched":
  534. if self.timeline_name in watched_timelines:
  535. self.timeline[i] = []
  536. break
  537. elif command == "while":
  538. try:
  539. r = eval(arg)
  540. except Exception as e:
  541. m = _("An error occurred in a timeline 'while' statement:\n\n{}").format(
  542. traceback.format_exc())
  543. show_error(m)
  544. r = False
  545. finally:
  546. if r:
  547. cur_timeline = self.timeline[i][:]
  548. while_command = "while {}".format(arg)
  549. self.timeline[i].insert(0, while_command)
  550. t_keys.insert(0, i)
  551. self.timeline[i - 1] = cur_timeline
  552. self.timeline[i] = loop_timeline
  553. i -= 1
  554. self.timeline_step -= 1
  555. else:
  556. self.timeline[i] = []
  557. break
  558. else:
  559. del self.timeline[i]
  560. else:
  561. break
  562. else:
  563. if (self.timeline_name and
  564. self.timeline_name not in watched_timelines):
  565. watched_timelines = watched_timelines[:]
  566. watched_timelines.append(self.timeline_name)
  567. self.timeline_name = ""
  568. self.timeline_step += delta_mult
  569. def event_paused_step(self, time_passed, delta_mult):
  570. # Handle lighting
  571. for view in self.views:
  572. for obj in self.get_objects_at(
  573. view.x - LIGHT_RANGE, view.y - LIGHT_RANGE,
  574. view.width + LIGHT_RANGE * 2,
  575. view.height + LIGHT_RANGE * 2):
  576. if isinstance(obj, InteractiveObject):
  577. if not self.disable_lights:
  578. obj.project_light()
  579. self.show_hud()
  580. def event_alarm(self, alarm_id):
  581. if alarm_id == "shake_down":
  582. self.shake_queue -= 1
  583. for view in self.views:
  584. view.yport += SHAKE_AMOUNT
  585. self.alarms["shake_up"] = SHAKE_FRAME_TIME
  586. elif alarm_id == "shake_up":
  587. for view in self.views:
  588. view.yport -= SHAKE_AMOUNT
  589. if self.shake_queue:
  590. self.alarms["shake_down"] = SHAKE_FRAME_TIME
  591. elif alarm_id == "death":
  592. self.die()
  593. @classmethod
  594. def load(cls, fname, show_prompt=False):
  595. if show_prompt:
  596. text = _("Loading data...")
  597. if sge.game.current_room is not None:
  598. x = sge.game.width / 2
  599. y = sge.game.height / 2
  600. w = font.get_width(text) + 32
  601. h = font.get_height(text) + 32
  602. sge.game.project_rectangle(x - w / 2, y - h / 2, w, h,
  603. fill=sge.gfx.Color("black"))
  604. sge.game.project_text(font, text, x, y,
  605. color=sge.gfx.Color("white"),
  606. halign="center", valign="middle")
  607. sge.game.refresh()
  608. else:
  609. print(_("Loading \"{}\"...").format(fname))
  610. try:
  611. r = xsge_tmx.load(os.path.join(DATA, "rooms", fname), cls=cls,
  612. types=TYPES)
  613. except Exception as e:
  614. m = _("An error occurred when trying to load the level:\n\n{}").format(
  615. traceback.format_exc())
  616. show_error(m)
  617. r = None
  618. else:
  619. r.fname = fname
  620. return r
  621. class SpecialScreen(Level):
  622. def event_room_start(self):
  623. super(SpecialScreen, self).event_room_start()
  624. if player is not None:
  625. player.destroy()
  626. class TitleScreen(SpecialScreen):
  627. def show_hud(self):
  628. pass
  629. def event_room_start(self):
  630. super(TitleScreen, self).event_room_start()
  631. MainMenu.create()
  632. def event_room_resume(self):
  633. super(TitleScreen, self).event_room_resume()
  634. MainMenu.create()
  635. def event_key_press(self, key, char):
  636. pass
  637. class CreditsScreen(SpecialScreen):
  638. def event_room_start(self):
  639. super(CreditsScreen, self).event_room_start()
  640. with open(os.path.join(DATA, "credits.json"), 'r') as f:
  641. sections = json.load(f)
  642. logo_section = sge.dsp.Object.create(self.width / 2, self.height,
  643. sprite=logo_sprite,
  644. tangible=False)
  645. self.sections = [logo_section]
  646. for section in sections:
  647. if "title" in section:
  648. head_sprite = sge.gfx.Sprite.from_text(
  649. font_big, section["title"], width=self.width,
  650. color=sge.gfx.Color("white"), halign="center")
  651. x = self.width / 2
  652. y = self.sections[-1].bbox_bottom + font_big.size * 3
  653. head_section = sge.dsp.Object.create(x, y, sprite=head_sprite,
  654. tangible=False)
  655. self.sections.append(head_section)
  656. if "lines" in section:
  657. for line in section["lines"]:
  658. list_sprite = sge.gfx.Sprite.from_text(
  659. font, line, width=self.width - 2 * TILE_SIZE,
  660. color=sge.gfx.Color("white"), halign="center")
  661. x = self.width / 2
  662. y = self.sections[-1].bbox_bottom + font.size
  663. list_section = sge.dsp.Object.create(
  664. x, y, sprite=list_sprite, tangible=False)
  665. self.sections.append(list_section)
  666. for obj in self.sections:
  667. obj.yvelocity = -0.2
  668. def event_step(self, time_passed, delta_mult):
  669. if self.sections[0].yvelocity > 0 and self.sections[0].y > self.height:
  670. for obj in self.sections:
  671. obj.yvelocity = 0
  672. if self.sections[-1].bbox_bottom < 0 and "end" not in self.alarms:
  673. sge.snd.Music.stop(fade_time=3000)
  674. self.alarms["end"] = 3.5 * FPS
  675. def event_alarm(self, alarm_id):
  676. if alarm_id == "end":
  677. sge.game.start_room.start()
  678. def event_key_press(self, key, char):
  679. if key in itertools.chain.from_iterable(down_key):
  680. if "end" not in self.alarms:
  681. for obj in self.sections:
  682. obj.yvelocity -= 0.1
  683. elif key in itertools.chain.from_iterable(up_key):
  684. if "end" not in self.alarms:
  685. for obj in self.sections:
  686. obj.yvelocity += 0.1
  687. elif (key in itertools.chain.from_iterable(jump_key) or
  688. key in itertools.chain.from_iterable(shoot_key) or
  689. key in itertools.chain.from_iterable(pause_key)):
  690. sge.game.start_room.start()
  691. def event_joystick(self, js_name, js_id, input_type, input_id, value):
  692. js = (js_id, input_type, input_id)
  693. if value >= joystick_threshold:
  694. if js in itertools.chain.from_iterable(down_js):
  695. if "end" not in self.alarms:
  696. for obj in self.sections:
  697. obj.yvelocity -= 0.1
  698. elif js in itertools.chain.from_iterable(up_js):
  699. if "end" not in self.alarms:
  700. for obj in self.sections:
  701. obj.yvelocity += 0.1
  702. elif (js in itertools.chain.from_iterable(jump_js) or
  703. js in itertools.chain.from_iterable(shoot_js) or
  704. js in itertools.chain.from_iterable(pause_js)):
  705. sge.game.start_room.start()
  706. class SolidLeft(xsge_physics.SolidLeft):
  707. def __init__(self, *args, **kwargs):
  708. kwargs.setdefault("visible", False)
  709. kwargs.setdefault("checks_collisions", False)
  710. super(SolidLeft, self).__init__(*args, **kwargs)
  711. class SolidRight(xsge_physics.SolidRight):
  712. def __init__(self, *args, **kwargs):
  713. kwargs.setdefault("visible", False)
  714. kwargs.setdefault("checks_collisions", False)
  715. super(SolidRight, self).__init__(*args, **kwargs)
  716. class SolidTop(xsge_physics.SolidTop):
  717. def __init__(self, *args, **kwargs):
  718. kwargs.setdefault("visible", False)
  719. kwargs.setdefault("checks_collisions", False)
  720. super(SolidTop, self).__init__(*args, **kwargs)
  721. class SolidBottom(xsge_physics.SolidBottom):
  722. def __init__(self, *args, **kwargs):
  723. kwargs.setdefault("visible", False)
  724. kwargs.setdefault("checks_collisions", False)
  725. super(SolidBottom, self).__init__(*args, **kwargs)
  726. class Solid(xsge_physics.Solid):
  727. def __init__(self, *args, **kwargs):
  728. kwargs.setdefault("visible", False)
  729. kwargs.setdefault("checks_collisions", False)
  730. super(Solid, self).__init__(*args, **kwargs)
  731. class SlopeTopLeft(xsge_physics.SlopeTopLeft):
  732. xsticky_top = True
  733. def __init__(self, *args, **kwargs):
  734. kwargs.setdefault("visible", False)
  735. kwargs.setdefault("checks_collisions", False)
  736. super(SlopeTopLeft, self).__init__(*args, **kwargs)
  737. def event_create(self):
  738. self.slope_xacceleration = -self.bbox_height / self.bbox_width
  739. class SlopeTopRight(xsge_physics.SlopeTopRight):
  740. xsticky_top = True
  741. def __init__(self, *args, **kwargs):
  742. kwargs.setdefault("visible", False)
  743. kwargs.setdefault("checks_collisions", False)
  744. super(SlopeTopRight, self).__init__(*args, **kwargs)
  745. def event_create(self):
  746. self.slope_xacceleration = self.bbox_height / self.bbox_width
  747. class SlopeBottomLeft(xsge_physics.SlopeBottomLeft):
  748. def __init__(self, *args, **kwargs):
  749. kwargs.setdefault("visible", False)
  750. kwargs.setdefault("checks_collisions", False)
  751. super(SlopeBottomLeft, self).__init__(*args, **kwargs)
  752. class SlopeBottomRight(xsge_physics.SlopeBottomRight):
  753. def __init__(self, *args, **kwargs):
  754. kwargs.setdefault("visible", False)
  755. kwargs.setdefault("checks_collisions", False)
  756. super(SlopeBottomRight, self).__init__(*args, **kwargs)
  757. class MovingPlatform(xsge_physics.SolidTop, xsge_physics.MobileWall):
  758. sticky_top = True
  759. def __init__(self, x, y, z=0, **kwargs):
  760. kwargs.setdefault("sprite", platform_sprite)
  761. super(MovingPlatform, self).__init__(x, y, z, **kwargs)
  762. self.path = None
  763. self.following = False
  764. def event_step(self, time_passed, delta_mult):
  765. super(MovingPlatform, self).event_step(time_passed, delta_mult)
  766. if self.path and not self.following:
  767. for other in self.collision(Player, y=(self.y - 1)):
  768. if self in other.get_bottom_touching_wall():
  769. self.path.follow_start(self, self.path.path_speed,
  770. accel=self.path.path_accel,
  771. decel=self.path.path_decel,
  772. loop=self.path.path_loop)
  773. break
  774. class HurtLeft(SolidLeft):
  775. pass
  776. class HurtRight(SolidRight):
  777. pass
  778. class HurtTop(SolidTop):
  779. pass
  780. class HurtBottom(SolidBottom):
  781. pass
  782. class SpikeLeft(HurtLeft, xsge_physics.Solid):
  783. pass
  784. class SpikeRight(HurtRight, xsge_physics.Solid):
  785. pass
  786. class SpikeTop(HurtTop, xsge_physics.Solid):
  787. pass
  788. class SpikeBottom(HurtBottom, xsge_physics.Solid):
  789. pass
  790. class Death(sge.dsp.Object):
  791. def __init__(self, *args, **kwargs):
  792. kwargs.setdefault("visible", False)
  793. kwargs.setdefault("checks_collisions", False)
  794. super(Death, self).__init__(*args, **kwargs)
  795. class Player(xsge_physics.Collider):
  796. name = "Ian C."
  797. max_hp = PLAYER_MAX_HP
  798. max_speed = PLAYER_MAX_SPEED
  799. roll_max_speed = PLAYER_ROLL_MAX_SPEED
  800. acceleration = PLAYER_ACCELERATION
  801. roll_acceleration = PLAYER_ROLL_ACCELERATION
  802. air_acceleration = PLAYER_AIR_ACCELERATION
  803. friction = PLAYER_FRICTION
  804. roll_friction = PLAYER_ROLL_FRICTION
  805. air_friction = PLAYER_AIR_FRICTION
  806. jump_height = PLAYER_JUMP_HEIGHT
  807. gravity = GRAVITY
  808. fall_speed = PLAYER_FALL_SPEED
  809. slide_speed = PLAYER_SLIDE_SPEED
  810. roll_slide_speed = PLAYER_ROLL_SLIDE_SPEED
  811. roll_slope_acceleration = PLAYER_ROLL_SLOPE_ACCELERATION
  812. hitstun_time = PLAYER_HITSTUN
  813. can_move = True
  814. @property
  815. def slope_acceleration(self):
  816. if self.rolling:
  817. return self.roll_slope_acceleration
  818. else:
  819. return 0
  820. @slope_acceleration.setter
  821. def slope_acceleration(self, value):
  822. pass
  823. @property
  824. def hp(self):
  825. return self.__hp
  826. @hp.setter
  827. def hp(self, value):
  828. self.__hp = value
  829. while self.__hp > self.max_hp and self.etanks_used > 0:
  830. self.etanks_used -= 1
  831. self.__hp -= self.max_hp
  832. while self.__hp <= 0 and self.etanks_used < etanks:
  833. self.etanks_used += 1
  834. self.__hp += self.max_hp
  835. self.__hp = min(self.__hp, self.max_hp)
  836. if self.__hp > 0:
  837. new_w = healthbar_width * self.__hp / self.max_hp
  838. healthbar_front_sprite.width = new_w
  839. self.update_hud()
  840. @property
  841. def camera_target_x(self):
  842. guides = self.collision(CameraXGuide)
  843. if guides:
  844. return guides[0].x
  845. else:
  846. return (self.x - self.view.width / 2 +
  847. self.xvelocity * CAMERA_OFFSET_FACTOR)
  848. @property
  849. def camera_target_y(self):
  850. guides = self.collision(CameraYGuide)
  851. if guides:
  852. self.camera_guided_y = True
  853. return guides[0].y
  854. else:
  855. self.camera_guided_y = False
  856. return self.y - self.view.height + CAMERA_TARGET_MARGIN_BOTTOM
  857. @property
  858. def aim_lock(self):
  859. return "aim_lock" in self.alarms
  860. @aim_lock.setter
  861. def aim_lock(self, value):
  862. if value:
  863. self.alarms["aim_lock"] = PLAYER_AIM_LOCK_TIME
  864. elif "aim_lock" in self.alarms:
  865. del self.alarms["aim_lock"]
  866. def __init__(self, x, y, z=0, sprite=None, visible=True, active=True,
  867. checks_collisions=True, tangible=True, bbox_x=8, bbox_y=0,
  868. bbox_width=16, bbox_height=16, regulate_origin=True,
  869. collision_ellipse=False, collision_precise=False, xvelocity=0,
  870. yvelocity=0, xacceleration=0, yacceleration=0,
  871. xdeceleration=0, ydeceleration=0, image_index=0,
  872. image_origin_x=None, image_origin_y=None, image_fps=None,
  873. image_xscale=1, image_yscale=1, image_rotation=0,
  874. image_alpha=255, image_blend=None, ID="player", player=0,
  875. human=True, lose_on_death=True, view_frozen=False):
  876. self.ID = ID
  877. self.player = player
  878. self.human = human
  879. self.lose_on_death = lose_on_death
  880. self.view_frozen = view_frozen
  881. self.input_lock = False
  882. self.warp_dest = None
  883. self.hud_sprite = sge.gfx.Sprite(width=SCREEN_SIZE[0],
  884. height=SCREEN_SIZE[1])
  885. self.reset_input()
  886. self.etanks_used = 0
  887. self.hitstun = False
  888. self.invincible = False
  889. self.facing = 1
  890. self.has_jumped = False
  891. self.current_mode = None
  892. self.rolling = False
  893. self.aim_direction = None
  894. self.aim_direction_time = 0
  895. self.view = None
  896. self.__hp = self.max_hp
  897. healthbar_front_sprite.width = healthbar_width
  898. self.last_xr = None
  899. self.last_yr = None
  900. self.camera_guided_y = False
  901. if GOD:
  902. image_blend = sge.gfx.Color("olive")
  903. super(Player, self).__init__(
  904. x, y, z=z, sprite=sprite, visible=visible, active=active,
  905. checks_collisions=checks_collisions, tangible=tangible,
  906. bbox_x=bbox_x, bbox_y=bbox_y, bbox_width=bbox_width,
  907. bbox_height=bbox_height, regulate_origin=regulate_origin,
  908. collision_ellipse=collision_ellipse,
  909. collision_precise=collision_precise, xvelocity=xvelocity,
  910. yvelocity=yvelocity, xacceleration=xacceleration,
  911. yacceleration=yacceleration, xdeceleration=xdeceleration,
  912. ydeceleration=ydeceleration, image_index=image_index,
  913. image_origin_x=image_origin_x, image_origin_y=image_origin_y,
  914. image_fps=image_fps, image_xscale=image_xscale,
  915. image_yscale=image_yscale, image_rotation=image_rotation,
  916. image_alpha=image_alpha, image_blend=image_blend)
  917. def reset_input(self):
  918. self.left_pressed = False
  919. self.right_pressed = False
  920. self.up_pressed = False
  921. self.down_pressed = False
  922. self.jump_pressed = False
  923. self.shoot_pressed = False
  924. self.aim_diag_pressed = False
  925. self.aim_up_pressed = False
  926. self.aim_down_pressed = False
  927. self.mode_pressed = False
  928. self.mode_reset_pressed = False
  929. def refresh_input(self):
  930. if self.human and not self.input_lock:
  931. key_controls = [left_key, right_key, up_key, down_key, aim_diag_key,
  932. jump_key, shoot_key, aim_up_key, aim_down_key,
  933. mode_key, mode_reset_key]
  934. js_controls = [left_js, right_js, up_js, down_js, aim_diag_js,
  935. jump_js, shoot_js, aim_up_js, aim_down_js, mode_js,
  936. mode_reset_js]
  937. states = [0 for i in key_controls]
  938. for i in six.moves.range(len(key_controls)):
  939. for choice in key_controls[i][self.player]:
  940. value = sge.keyboard.get_pressed(choice)
  941. states[i] = max(states[i], value)
  942. for i in six.moves.range(len(js_controls)):
  943. for choice in js_controls[i][self.player]:
  944. j, t, c = choice
  945. value = min(sge.joystick.get_value(j, t, c), 1)
  946. if value >= joystick_threshold:
  947. states[i] = max(states[i], value)
  948. self.left_pressed = states[0]
  949. self.right_pressed = states[1]
  950. self.up_pressed = states[2]
  951. self.down_pressed = states[3]
  952. self.aim_diag_pressed = states[4]
  953. self.jump_pressed = states[5]
  954. self.shoot_pressed = states[6]
  955. self.aim_up_pressed = states[7]
  956. self.aim_down_pressed = states[8]
  957. self.mode_pressed = states[9]
  958. self.mode_reset_pressed = states[10]
  959. def press_up(self):
  960. if not self.aim_diag_pressed:
  961. warp_pad_objs = self.collision(WarpPad)
  962. if warp_pad_objs:
  963. warp_pad = warp_pad_objs[0]
  964. warp_pad.teleport(self)
  965. def press_down(self):
  966. pass
  967. def jump(self):
  968. if self.on_floor or self.was_on_floor:
  969. self.has_jumped = True
  970. self.yvelocity = get_jump_speed(self.jump_height, self.gravity)
  971. self.on_floor = []
  972. self.was_on_floor = []
  973. self.event_jump()
  974. def jump_release(self):
  975. if self.has_jumped and self.yvelocity < 0:
  976. self.has_jumped = False
  977. self.yvelocity /= 2
  978. def shoot(self):
  979. pass
  980. def shoot_release(self):
  981. pass
  982. def mode(self):
  983. all_modes = [None, "compress"]
  984. if self.current_mode in all_modes:
  985. i = all_modes.index(self.current_mode)
  986. while True:
  987. i += 1
  988. if i >= len(all_modes):
  989. self.current_mode = None
  990. break
  991. elif (all_modes[i] == "compress" and
  992. "atomic_compressor" in progress_flags):
  993. self.current_mode = all_modes[i]
  994. break
  995. else:
  996. self.current_mode = None
  997. self.update_hud()
  998. play_sound(type_sound)
  999. def mode_reset(self):
  1000. self.current_mode = None
  1001. self.update_hud()
  1002. play_sound(cancel_sound)
  1003. def hurt(self, damage=1, touching=False):
  1004. if not self.hitstun and not self.invincible:
  1005. play_sound(hurt_sound, self.x, self.y)
  1006. if not GOD:
  1007. self.hp -= damage
  1008. if self.hp <= 0:
  1009. self.kill()
  1010. else:
  1011. self.hitstun = True
  1012. self.image_alpha = 128
  1013. self.alarms["hitstun"] = self.hitstun_time
  1014. def kill(self):
  1015. if self.lose_on_death:
  1016. sge.snd.Music.clear_queue()
  1017. sge.snd.Music.stop()
  1018. sge.game.current_room.alarms["death"] = DEATH_TIME
  1019. play_sound(death_sound, self.x, self.y)
  1020. self.destroy()
  1021. def refresh(self):
  1022. self.hp = self.max_hp
  1023. self.etanks_used = 0
  1024. self.update_hud()
  1025. def warp_in(self):
  1026. self.input_lock = True
  1027. self.alarms["input_lock"] = WARP_TIME
  1028. self.reset_input()
  1029. self.xvelocity = 0
  1030. self.yvelocity = 0
  1031. def warp_out(self):
  1032. self.input_lock = True
  1033. self.alarms["warp_out"] = WARP_TIME
  1034. self.reset_input()
  1035. self.xvelocity = 0
  1036. self.yvelocity = 0
  1037. def update_hud(self):
  1038. self.hud_sprite.draw_clear()
  1039. if not NO_HUD:
  1040. start_x = 8
  1041. start_y = 8
  1042. x = start_x
  1043. y = start_y
  1044. self.hud_sprite.draw_sprite(healthbar_back_sprite, 0, x, y)
  1045. if self.hp > 0:
  1046. self.hud_sprite.draw_sprite(healthbar_front_sprite, 0, x, y)
  1047. y += 8
  1048. w = etank_empty_sprite.width
  1049. h = etank_empty_sprite.height
  1050. for i in six.moves.range(etanks):
  1051. if x + w >= start_x + healthbar_width:
  1052. x = start_x
  1053. y += h
  1054. if i < etanks - self.etanks_used:
  1055. self.hud_sprite.draw_sprite(etank_full_sprite, 0, x, y)
  1056. else:
  1057. self.hud_sprite.draw_sprite(etank_empty_sprite, 0, x, y)
  1058. x += w
  1059. # Mode image
  1060. x = start_x
  1061. y += 12
  1062. if self.current_mode == "compress":
  1063. self.hud_sprite.draw_sprite(atomic_compressor_sprite, 0, x, y)
  1064. if "map" in progress_flags:
  1065. w = 7
  1066. h = 5
  1067. if sge.game.current_room.fname in map_rooms:
  1068. rm_x, rm_y = map_rooms[sge.game.current_room.fname]
  1069. pl_x = rm_x + get_xregion(self.x)
  1070. pl_y = rm_y + get_yregion(self.y)
  1071. x = pl_x - w // 2
  1072. y = pl_y - h // 2
  1073. else:
  1074. x = 0
  1075. y = 0
  1076. pl_x = None
  1077. pl_y = None
  1078. map_s = draw_map(x, y, w, h, pl_x, pl_y)
  1079. c = sge.gfx.Color((255, 255, 255, 192))
  1080. map_s.draw_rectangle(0, 0, map_s.width, map_s.height, fill=c,
  1081. blend_mode=sge.BLEND_RGBA_MULTIPLY)
  1082. x = SCREEN_SIZE[0] - start_x - w * MAP_CELL_WIDTH
  1083. y = start_y
  1084. self.hud_sprite.draw_sprite(map_s, 0, x, y)
  1085. self.hud_sprite.draw_rectangle(x, y, map_s.width, map_s.height,
  1086. outline=sge.gfx.Color("white"))
  1087. def show_hud(self):
  1088. if not NO_HUD:
  1089. sge.game.project_sprite(self.hud_sprite, 0, 0, 0)
  1090. if not self.human:
  1091. room = sge.game.current_room
  1092. if (room.timeline_skip_target is not None and
  1093. room.timeline_step < room.timeline_skip_target):
  1094. room.status_text = _("Press the Menu button to skip...")
  1095. else:
  1096. room.status_text = _("Cinematic mode enabled")
  1097. def set_image(self):
  1098. pass
  1099. def init_position(self):
  1100. self.last_x = self.x
  1101. self.last_y = self.y
  1102. self.on_slope = self.get_bottom_touching_slope()
  1103. self.on_floor = self.get_bottom_touching_wall() + self.on_slope
  1104. self.was_on_floor = self.on_floor
  1105. self.view.x = self.camera_target_x
  1106. self.view.y = self.camera_target_y
  1107. def event_create(self):
  1108. self.z = sge.game.current_room.player_z
  1109. sge.game.current_room.add_timeline_object(self)
  1110. self.view = sge.game.current_room.views[self.player]
  1111. for obj in sge.game.current_room.objects:
  1112. if isinstance(obj, SpawnPoint) and obj.spawn_id == spawn_point:
  1113. obj.spawn(self)
  1114. break
  1115. self.init_position()
  1116. self.update_hud()
  1117. def event_begin_step(self, time_passed, delta_mult):
  1118. self.refresh_input()
  1119. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1120. v_control = bool(self.down_pressed) - bool(self.up_pressed)
  1121. current_h_movement = (self.xvelocity > 0) - (self.xvelocity < 0)
  1122. if not self.aim_lock:
  1123. prev_aim_direction = self.aim_direction
  1124. if "shooting" in self.alarms:
  1125. self.aim_direction = 0
  1126. else:
  1127. self.aim_direction = None
  1128. if v_control:
  1129. if self.aim_diag_pressed or (h_control and metroid_controls):
  1130. self.aim_direction = 1 * -v_control
  1131. else:
  1132. self.aim_direction = 2 * -v_control
  1133. elif metroid_controls and self.aim_diag_pressed:
  1134. if prev_aim_direction is not None and prev_aim_direction < 0:
  1135. self.aim_direction = -1
  1136. else:
  1137. self.aim_direction = 1
  1138. if self.aim_up_pressed and self.aim_down_pressed:
  1139. self.aim_direction = 2
  1140. elif self.aim_up_pressed:
  1141. self.aim_direction = 1
  1142. elif self.aim_down_pressed:
  1143. self.aim_direction = -1
  1144. if self.aim_direction == prev_aim_direction:
  1145. self.aim_direction_time += 1
  1146. else:
  1147. self.aim_direction_time = 0
  1148. self.xacceleration = 0
  1149. self.yacceleration = 0
  1150. self.xdeceleration = 0
  1151. if h_control:
  1152. if not self.can_move:
  1153. target_speed = 0
  1154. else:
  1155. h_factor = abs(self.right_pressed - self.left_pressed)
  1156. target_speed = min(h_factor * self.max_speed, self.max_speed)
  1157. if (abs(self.xvelocity) < target_speed or
  1158. (self.xvelocity > 0 and h_control < 0) or
  1159. (self.xvelocity < 0 and h_control > 0)):
  1160. if self.on_floor or self.was_on_floor:
  1161. if self.rolling:
  1162. self.xacceleration = self.roll_acceleration * h_control
  1163. else:
  1164. self.xacceleration = self.acceleration * h_control
  1165. else:
  1166. self.xacceleration = self.air_acceleration * h_control
  1167. else:
  1168. if self.on_floor or self.was_on_floor:
  1169. if self.rolling:
  1170. dc = self.roll_friction
  1171. else:
  1172. dc = self.friction
  1173. else:
  1174. dc = self.air_friction
  1175. if abs(self.xvelocity) - dc * delta_mult > target_speed:
  1176. self.xdeceleration = dc
  1177. else:
  1178. self.xvelocity = target_speed * current_h_movement
  1179. if current_h_movement and h_control != current_h_movement:
  1180. if self.on_floor or self.was_on_floor:
  1181. if self.rolling:
  1182. self.xdeceleration = self.roll_friction
  1183. else:
  1184. self.xdeceleration = self.friction
  1185. else:
  1186. self.xdeceleration = self.air_friction
  1187. if not self.on_floor and not self.was_on_floor:
  1188. if self.yvelocity < self.fall_speed:
  1189. self.yacceleration = self.gravity
  1190. else:
  1191. self.yvelocity = self.fall_speed
  1192. elif self.on_slope:
  1193. if self.rolling:
  1194. self.yvelocity = (self.roll_slide_speed *
  1195. (self.on_slope[0].bbox_height /
  1196. self.on_slope[0].bbox_width))
  1197. elif self.xvelocity:
  1198. self.yvelocity = (self.slide_speed *
  1199. (self.on_slope[0].bbox_height /
  1200. self.on_slope[0].bbox_width))
  1201. else:
  1202. self.yvelocity = 0
  1203. def event_step(self, time_passed, delta_mult):
  1204. global map_revealed
  1205. global map_explored
  1206. on_floor = self.get_bottom_touching_wall()
  1207. self.on_slope = self.get_bottom_touching_slope() if not on_floor else []
  1208. self.was_on_floor = self.on_floor
  1209. self.on_floor = on_floor + self.on_slope
  1210. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1211. v_control = bool(self.down_pressed) - bool(self.up_pressed)
  1212. for block in self.on_floor:
  1213. if block in self.was_on_floor and isinstance(block, HurtTop):
  1214. self.hurt()
  1215. # Set image
  1216. self.set_image()
  1217. # Move view
  1218. if self.view is not None and not self.view_frozen:
  1219. view_target_x = self.camera_target_x
  1220. if abs(view_target_x - self.view.x) > 0.5:
  1221. self.view.x += ((view_target_x - self.view.x) *
  1222. CAMERA_HSPEED_FACTOR)
  1223. else:
  1224. self.view.x = view_target_x
  1225. view_min_y = self.y - self.view.height + CAMERA_MARGIN_BOTTOM
  1226. view_max_y = self.y - CAMERA_MARGIN_TOP
  1227. view_target_y = self.camera_target_y
  1228. if (self.on_floor and self.was_on_floor) or self.camera_guided_y:
  1229. if abs(view_target_y - self.view.y) > 0.5:
  1230. self.view.y += ((view_target_y - self.view.y) *
  1231. CAMERA_VSPEED_FACTOR)
  1232. else:
  1233. self.view.y = view_target_y
  1234. if not self.camera_guided_y:
  1235. if self.view.y < view_min_y:
  1236. self.view.y = view_min_y
  1237. elif self.view.y > view_max_y:
  1238. self.view.y = view_max_y
  1239. self.last_x = self.x
  1240. self.last_y = self.y
  1241. if sge.game.current_room.fname in map_rooms:
  1242. xr, yr = map_rooms[sge.game.current_room.fname]
  1243. xr += get_xregion(self.x)
  1244. yr += get_yregion(self.y)
  1245. if xr != self.last_xr or yr != self.last_yr:
  1246. pos = (xr, yr)
  1247. if pos not in map_explored:
  1248. map_explored = map_explored[:]
  1249. map_explored.append(pos)
  1250. if pos not in map_revealed:
  1251. map_revealed = map_revealed[:]
  1252. map_revealed.append(pos)
  1253. self.update_hud()
  1254. self.last_xr = xr
  1255. self.last_yr = yr
  1256. self.show_hud()
  1257. def event_paused_step(self, time_passed, delta_mult):
  1258. self.show_hud()
  1259. def event_alarm(self, alarm_id):
  1260. if alarm_id == "hitstun":
  1261. self.hitstun = False
  1262. self.image_alpha = 255
  1263. elif alarm_id == "input_lock":
  1264. self.input_lock = False
  1265. self.refresh_input()
  1266. elif alarm_id == "warp_out":
  1267. if self.warp_dest:
  1268. warp(self.warp_dest)
  1269. def event_key_press(self, key, char):
  1270. if self.human and not self.input_lock:
  1271. if key in up_key[self.player] and not self.up_pressed:
  1272. self.press_up()
  1273. if key in down_key[self.player] and not self.down_pressed:
  1274. self.press_down()
  1275. if key in jump_key[self.player] and not self.jump_pressed:
  1276. self.jump()
  1277. if key in shoot_key[self.player] and not self.shoot_pressed:
  1278. self.shoot()
  1279. if key in mode_key[self.player] and not self.mode_pressed:
  1280. self.mode()
  1281. if (key in mode_reset_key[self.player] and
  1282. not self.mode_reset_pressed):
  1283. self.mode_reset()
  1284. if key in map_key[self.player]:
  1285. if "map" in progress_flags:
  1286. play_sound(select_sound)
  1287. MapDialog(self.last_xr, self.last_yr).show()
  1288. if not isinstance(sge.game.current_room, SpecialScreen):
  1289. if key == "escape" or key in pause_key[self.player]:
  1290. sge.game.current_room.pause(player_x=self.last_xr,
  1291. player_y=self.last_yr)
  1292. def event_key_release(self, key):
  1293. if self.human and not self.input_lock:
  1294. if key in jump_key[self.player]:
  1295. self.jump_release()
  1296. if key in shoot_key[self.player]:
  1297. self.shoot_release()
  1298. elif key in up_key[self.player] or key in down_key[self.player]:
  1299. self.aim_lock = False
  1300. def event_joystick(self, js_name, js_id, input_type, input_id, value):
  1301. js = (js_id, input_type, input_id)
  1302. if self.human and not self.input_lock:
  1303. if value >= joystick_threshold:
  1304. if js in up_js[self.player] and not self.up_pressed:
  1305. self.press_up()
  1306. if js in down_js[self.player] and not self.down_pressed:
  1307. self.press_down()
  1308. if js in jump_js[self.player] and not self.jump_pressed:
  1309. self.jump()
  1310. if js in shoot_js[self.player] and not self.shoot_pressed:
  1311. self.shoot()
  1312. if js in mode_js[self.player] and not self.mode_pressed:
  1313. self.mode()
  1314. if (js in mode_reset_js[self.player] and
  1315. not self.mode_reset_pressed):
  1316. self.mode_reset()
  1317. if js in map_js[self.player]:
  1318. if "map" in progress_flags:
  1319. play_sound(select_sound)
  1320. MapDialog(self.last_xr, self.last_yr).show()
  1321. else:
  1322. if js in jump_js[self.player]:
  1323. self.jump_release()
  1324. if js in shoot_js[self.player]:
  1325. self.shoot_release()
  1326. if not isinstance(sge.game.current_room, SpecialScreen):
  1327. if value >= joystick_threshold and js in pause_js[self.player]:
  1328. sge.game.current_room.pause(player_x=self.last_xr,
  1329. player_y=self.last_yr)
  1330. def event_collision(self, other, xdirection, ydirection):
  1331. if isinstance(other, InteractiveObject):
  1332. other.touch(self)
  1333. def event_physics_collision_left(self, other, move_loss):
  1334. for block in self.get_left_touching_wall():
  1335. if isinstance(block, HurtRight):
  1336. self.hurt()
  1337. if isinstance(other, xsge_physics.SolidRight):
  1338. self.xvelocity = max(self.xvelocity, 0)
  1339. def event_physics_collision_right(self, other, move_loss):
  1340. for block in self.get_right_touching_wall():
  1341. if isinstance(block, HurtLeft):
  1342. self.hurt()
  1343. if isinstance(other, xsge_physics.SolidLeft):
  1344. self.xvelocity = min(self.xvelocity, 0)
  1345. def event_physics_collision_top(self, other, move_loss):
  1346. top_touching = self.get_top_touching_wall()
  1347. tmv = 0
  1348. for i in six.moves.range(CEILING_LAX):
  1349. if (not self.get_left_touching_wall() and
  1350. not self.get_left_touching_slope()):
  1351. self.x -= 1
  1352. tmv -= 1
  1353. if (not self.get_top_touching_wall() and
  1354. not self.get_top_touching_slope()):
  1355. self.move_y(-move_loss)
  1356. break
  1357. else:
  1358. self.x -= tmv
  1359. tmv = 0
  1360. for i in six.moves.range(CEILING_LAX):
  1361. if (not self.get_left_touching_wall() and
  1362. not self.get_left_touching_slope()):
  1363. self.x += 1
  1364. tmv += 1
  1365. if (not self.get_top_touching_wall() and
  1366. not self.get_top_touching_slope()):
  1367. self.move_y(-move_loss)
  1368. break
  1369. else:
  1370. self.x -= tmv
  1371. tmv = 0
  1372. self.yvelocity = max(self.yvelocity, 0)
  1373. for block in top_touching:
  1374. if isinstance(block, HurtBottom):
  1375. self.hurt()
  1376. def event_physics_collision_bottom(self, other, move_loss):
  1377. self.has_jumped = False
  1378. for block in self.get_bottom_touching_wall():
  1379. if isinstance(block, HurtTop):
  1380. self.hurt()
  1381. if isinstance(other, xsge_physics.SolidTop):
  1382. self.yvelocity = min(self.yvelocity, 0)
  1383. elif isinstance(other, (xsge_physics.SlopeTopLeft,
  1384. xsge_physics.SlopeTopRight)):
  1385. ss = self.roll_slide_speed if self.rolling else self.slide_speed
  1386. self.yvelocity = min(ss * (other.bbox_height / other.bbox_width),
  1387. self.yvelocity)
  1388. def event_jump(self):
  1389. pass
  1390. class Anneroy(Player):
  1391. name = "Anneroy"
  1392. @property
  1393. def can_move(self):
  1394. if self.crouching:
  1395. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1396. if h_control != self.facing:
  1397. self.alarms["autostand_lock"] = 10
  1398. if "autostand_lock" not in self.alarms:
  1399. self.press_up()
  1400. return not self.crouching
  1401. def __init__(self, *args, **kwargs):
  1402. kwargs["bbox_x"] = ANNEROY_BBOX_X
  1403. kwargs["bbox_width"] = ANNEROY_BBOX_WIDTH
  1404. kwargs["bbox_y"] = ANNEROY_STAND_BBOX_Y
  1405. kwargs["bbox_height"] = ANNEROY_STAND_BBOX_HEIGHT
  1406. super(Anneroy, self).__init__(*args, **kwargs)
  1407. self.torso = None
  1408. self.hedgehog_spikes = None
  1409. self.fixed_sprite = False
  1410. self.crouching = False
  1411. self.ball = False
  1412. self.walljumping = False
  1413. self.wall_direction = 0
  1414. self.bouncing = False
  1415. self.hedgehog = False
  1416. self.hedgehog_autocancel = False
  1417. self.last_aim_direction = 0
  1418. def get_up_obstructed(self, x, y, w, h, lax=0):
  1419. def _get_up_obstructed(self=self, x=x, y=y, w=w, h=h):
  1420. for other in sge.collision.rectangle(self.x + x, self.y + y, w, h):
  1421. if isinstance(other, xsge_physics.SolidBottom):
  1422. if not self.collision(other):
  1423. return True
  1424. elif isinstance(other, xsge_physics.SlopeBottomLeft):
  1425. if self.bbox_top >= other.get_slope_y(self.bbox_right):
  1426. return True
  1427. elif isinstance(other, xsge_physics.SlopeBottomRight):
  1428. if self.bbox_top >= other.get_slope_y(self.bbox_left):
  1429. return True
  1430. return False
  1431. rv = _get_up_obstructed()
  1432. if rv:
  1433. xstart = self.x
  1434. for i in six.moves.range(lax):
  1435. self.move_x(-1)
  1436. rv = _get_up_obstructed()
  1437. if not rv:
  1438. break
  1439. else:
  1440. self.move_x(xstart - self.x)
  1441. for i in six.moves.range(lax):
  1442. self.move_x(1)
  1443. rv = _get_up_obstructed()
  1444. if not rv:
  1445. break
  1446. else:
  1447. self.move_x(xstart - self.x)
  1448. return rv
  1449. def press_up(self):
  1450. if self.ball:
  1451. if self.get_up_obstructed(
  1452. ANNEROY_BBOX_X, ANNEROY_CROUCH_BBOX_Y,
  1453. ANNEROY_BBOX_WIDTH, ANNEROY_CROUCH_BBOX_HEIGHT,
  1454. ANNEROY_DECOMPRESS_LAX):
  1455. self.reset_image()
  1456. self.sprite = anneroy_decompress_fail_sprite
  1457. self.image_index = 0
  1458. self.image_speed = None
  1459. self.torso.visible = False
  1460. self.fixed_sprite = "decompress_fail"
  1461. else:
  1462. if self.fixed_sprite != "compress":
  1463. self.reset_image()
  1464. self.sprite = anneroy_compress_sprite
  1465. self.image_index = anneroy_compress_sprite.frames - 1
  1466. self.image_speed = -anneroy_compress_sprite.speed
  1467. self.torso.visible = False
  1468. self.fixed_sprite = "compress"
  1469. self.ball = False
  1470. self.hedgehog = False
  1471. self.rolling = False
  1472. self.aim_lock = True
  1473. if "fixed_sprite" in self.alarms:
  1474. del self.alarms["fixed_sprite"]
  1475. if "hedgehog_retract" in self.alarms:
  1476. del self.alarms["hedgehog_retract"]
  1477. if "hedgehog_extend" in self.alarms:
  1478. del self.alarms["hedgehog_extend"]
  1479. if "hedgehog_extend2" in self.alarms:
  1480. del self.alarms["hedgehog_extend2"]
  1481. self.max_speed = self.__class__.max_speed
  1482. if self.on_floor:
  1483. self.crouching = True
  1484. self.bbox_y = ANNEROY_CROUCH_BBOX_Y
  1485. self.bbox_height = ANNEROY_CROUCH_BBOX_HEIGHT
  1486. else:
  1487. self.bbox_y = ANNEROY_STAND_BBOX_Y
  1488. self.bbox_height = ANNEROY_STAND_BBOX_HEIGHT
  1489. elif self.crouching:
  1490. if not self.aim_diag_pressed:
  1491. for other in sge.collision.rectangle(
  1492. self.x + ANNEROY_BBOX_X, self.y + ANNEROY_STAND_BBOX_Y,
  1493. ANNEROY_BBOX_WIDTH, ANNEROY_STAND_BBOX_HEIGHT):
  1494. if isinstance(other, (xsge_physics.SolidBottom,
  1495. xsge_physics.SlopeBottomLeft,
  1496. xsge_physics.SlopeBottomRight)):
  1497. if not self.collision(other):
  1498. break
  1499. else:
  1500. if self.on_floor:
  1501. if self.fixed_sprite != "crouch":
  1502. self.reset_image()
  1503. self.sprite = anneroy_legs_crouch_sprite
  1504. self.image_index = anneroy_legs_crouch_sprite.frames - 1
  1505. self.image_speed = -anneroy_legs_crouch_sprite.speed
  1506. self.fixed_sprite = "crouch"
  1507. self.crouching = False
  1508. self.bbox_y = ANNEROY_STAND_BBOX_Y
  1509. self.bbox_height = ANNEROY_STAND_BBOX_HEIGHT
  1510. self.aim_lock = True
  1511. else:
  1512. super(Anneroy, self).press_up()
  1513. def press_down(self):
  1514. if not self.aim_diag_pressed:
  1515. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1516. if self.on_floor and self.was_on_floor:
  1517. if self.ball:
  1518. # Do nothing
  1519. pass
  1520. elif self.crouching:
  1521. self.compress()
  1522. else:
  1523. if not h_control:
  1524. if self.fixed_sprite != "crouch":
  1525. self.reset_image()
  1526. self.sprite = anneroy_legs_crouch_sprite
  1527. self.image_index = 0
  1528. self.image_speed = anneroy_legs_crouch_sprite.speed
  1529. self.fixed_sprite = "crouch"
  1530. self.crouching = True
  1531. self.bbox_y = ANNEROY_CROUCH_BBOX_Y
  1532. self.bbox_height = ANNEROY_CROUCH_BBOX_HEIGHT
  1533. self.aim_lock = True
  1534. else:
  1535. if "compress_pressed" in self.alarms:
  1536. self.compress()
  1537. del self.alarms["compress_pressed"]
  1538. else:
  1539. self.alarms["compress_pressed"] = DOUBLETAP_TIME
  1540. else:
  1541. if not self.ball:
  1542. if "compress_pressed" in self.alarms:
  1543. self.compress()
  1544. del self.alarms["compress_pressed"]
  1545. else:
  1546. self.alarms["compress_pressed"] = DOUBLETAP_TIME
  1547. def jump(self):
  1548. if self.crouching:
  1549. self.press_up()
  1550. if not self.crouching and not self.ball and not self.walljumping:
  1551. if (not self.on_floor and not self.was_on_floor and
  1552. "monkey_boots" in progress_flags):
  1553. if self.facing > 0 and self.get_right_touching_wall():
  1554. self.reset_image()
  1555. self.sprite = anneroy_wall_right_sprite
  1556. self.image_index = 0
  1557. self.image_speed = anneroy_wall_right_sprite.speed
  1558. self.image_xscale = abs(self.image_xscale)
  1559. self.fixed_sprite = "wall"
  1560. self.walljumping = True
  1561. self.wall_direction = 1
  1562. self.has_jumped = False
  1563. if "fixed_sprite" in self.alarms:
  1564. del self.alarms["fixed_sprite"]
  1565. self.torso.visible = False
  1566. self.input_lock = True
  1567. self.xvelocity = 0
  1568. self.yvelocity = 0
  1569. self.gravity = 0
  1570. elif self.facing < 0 and self.get_left_touching_wall():
  1571. self.reset_image()
  1572. self.sprite = anneroy_wall_left_sprite
  1573. self.image_index = 0
  1574. self.image_speed = anneroy_wall_left_sprite.speed
  1575. self.image_xscale = abs(self.image_xscale)
  1576. self.fixed_sprite = "wall"
  1577. self.walljumping = True
  1578. self.wall_direction = -1
  1579. self.has_jumped = False
  1580. if "fixed_sprite" in self.alarms:
  1581. del self.alarms["fixed_sprite"]
  1582. self.torso.visible = False
  1583. self.input_lock = True
  1584. self.xvelocity = 0
  1585. self.yvelocity = 0
  1586. self.gravity = 0
  1587. else:
  1588. super(Anneroy, self).jump()
  1589. def retract_spikes(self):
  1590. self.hedgehog = False
  1591. self.sprite = anneroy_hedgehog_start_sprite
  1592. self.fixed_sprite = "hedgehog"
  1593. self.alarms["fixed_sprite"] = ANNEROY_HEDGEHOG_FRAME_TIME
  1594. self.alarms["hedgehog_lock"] = ANNEROY_HEDGEHOG_FRAME_TIME
  1595. self.rolling = True
  1596. self.max_speed = self.__class__.max_speed
  1597. def shoot_default(self):
  1598. if "shoot_lock" not in self.alarms:
  1599. if self.ball:
  1600. if ("hedgehog_hormone" in progress_flags and
  1601. not self.hedgehog and
  1602. "hedgehog_lock" not in self.alarms):
  1603. self.hedgehog = True
  1604. self.sprite = anneroy_hedgehog_start_sprite
  1605. if self.fixed_sprite:
  1606. self.image_speed = (abs(self.xvelocity) *
  1607. ANNEROY_BALL_FRAMES_PER_PIXEL)
  1608. self.fixed_sprite = "hedgehog"
  1609. self.alarms["hedgehog_extend"] = ANNEROY_HEDGEHOG_FRAME_TIME
  1610. play_sound(hedgehog_spikes_sound, self.image_xcenter,
  1611. self.image_ycenter)
  1612. self.rolling = False
  1613. if "sloth_ball" in progress_flags:
  1614. self.hedgehog_autocancel = False
  1615. self.max_speed = ANNEROY_SLOTH_MAX_SPEED
  1616. else:
  1617. self.hedgehog_autocancel = True
  1618. self.max_speed = 0
  1619. else:
  1620. if self.aim_direction is None:
  1621. self.aim_direction = 0
  1622. self.alarms["shooting"] = 30
  1623. self.alarms["shoot_lock"] = 15
  1624. if self.aim_direction is not None:
  1625. self.last_aim_direction = self.aim_direction
  1626. else:
  1627. self.last_aim_direction = 0
  1628. x = 0
  1629. y = 0
  1630. xv = 0
  1631. yv = 0
  1632. image_rotation = 0
  1633. if self.facing > 0:
  1634. if self.aim_direction == 0:
  1635. x = 25
  1636. y = -3
  1637. xv = ANNEROY_BULLET_SPEED
  1638. image_rotation = 0
  1639. elif self.aim_direction == 1:
  1640. x = 22
  1641. y = -27
  1642. xv = ANNEROY_BULLET_DSPEED
  1643. yv = -ANNEROY_BULLET_DSPEED
  1644. image_rotation = 315
  1645. elif self.aim_direction == 2:
  1646. x = 6
  1647. y = -31
  1648. yv = -ANNEROY_BULLET_SPEED
  1649. image_rotation = 270
  1650. elif self.aim_direction == -1:
  1651. x = 19
  1652. y = 9
  1653. xv = ANNEROY_BULLET_DSPEED
  1654. yv = ANNEROY_BULLET_DSPEED
  1655. image_rotation = 45
  1656. elif self.aim_direction == -2:
  1657. x = 9
  1658. y = 21
  1659. yv = ANNEROY_BULLET_SPEED
  1660. image_rotation = 90
  1661. else:
  1662. if self.aim_direction == 0:
  1663. x = -25
  1664. y = -3
  1665. xv = -ANNEROY_BULLET_SPEED
  1666. image_rotation = 180
  1667. elif self.aim_direction == 1:
  1668. x = -22
  1669. y = -27
  1670. xv = -ANNEROY_BULLET_DSPEED
  1671. yv = -ANNEROY_BULLET_DSPEED
  1672. image_rotation = 225
  1673. elif self.aim_direction == 2:
  1674. x = -6
  1675. y = -31
  1676. yv = -ANNEROY_BULLET_SPEED
  1677. image_rotation = 270
  1678. elif self.aim_direction == -1:
  1679. x = -19
  1680. y = 9
  1681. xv = -ANNEROY_BULLET_DSPEED
  1682. yv = ANNEROY_BULLET_DSPEED
  1683. image_rotation = 135
  1684. elif self.aim_direction == -2:
  1685. x = -9
  1686. y = 21
  1687. yv = ANNEROY_BULLET_SPEED
  1688. image_rotation = 90
  1689. if x:
  1690. m = y / x
  1691. else:
  1692. m = None
  1693. xdest = self.torso.x + x
  1694. ydest = self.torso.y + y
  1695. guide = xsge_physics.Collider.create(
  1696. self.torso.x, self.torso.y, sprite=anneroy_bullet_sprite)
  1697. if self.facing > 0:
  1698. guide.bbox_right = self.bbox_right
  1699. else:
  1700. guide.bbox_left = self.bbox_left
  1701. if self.aim_direction < 0:
  1702. guide.bbox_bottom = self.bbox_bottom
  1703. else:
  1704. guide.bbox_top = self.bbox_top
  1705. x += self.torso.x - guide.x
  1706. y += self.torso.y - guide.y
  1707. xsteps = int(abs(x) / guide.bbox_width)
  1708. ysteps = int(abs(y) / guide.bbox_height)
  1709. xfinal = math.copysign(abs(x) - xsteps * guide.bbox_width, x)
  1710. yfinal = math.copysign(abs(y) - ysteps * guide.bbox_height, y)
  1711. for i in six.moves.range(xsteps):
  1712. guide.move_x(math.copysign(guide.bbox_width, x))
  1713. for i in six.moves.range(ysteps):
  1714. guide.move_y(math.copysign(guide.bbox_height, y))
  1715. guide.move_x(xfinal)
  1716. guide.move_y(yfinal)
  1717. if abs(self.aim_direction) == 1 and m:
  1718. target_x = self.torso.x + x
  1719. target_y = self.torso.y + y
  1720. xdiff = guide.x - self.torso.x
  1721. ydiff = guide.y - self.torso.y
  1722. if abs(guide.x - target_x) >= 1:
  1723. guide.y = self.torso.y + m * xdiff
  1724. elif abs(guide.y - target_y) >= 1:
  1725. guide.x = self.torso.x + ydiff / m
  1726. bs = AnneroyBullet.create(
  1727. guide.x, guide.y, self.z + 0.2,
  1728. sprite=anneroy_bullet_sprite, xvelocity=xv,
  1729. yvelocity=yv, regulate_origin=True,
  1730. image_xscale=abs(self.image_xscale),
  1731. image_yscale=self.image_yscale,
  1732. image_rotation=image_rotation, image_blend=self.image_blend)
  1733. guide.destroy()
  1734. Smoke.create(
  1735. xdest, ydest, self.torso.z,
  1736. sprite=anneroy_bullet_dust_sprite,
  1737. xvelocity=self.xvelocity, yvelocity=self.yvelocity,
  1738. regulate_origin=True, image_xscale=abs(self.image_xscale),
  1739. image_yscale=self.image_yscale,
  1740. image_rotation=image_rotation,
  1741. image_blend=self.image_blend)
  1742. play_sound(shoot_sound, xdest, ydest)
  1743. def shoot(self):
  1744. if self.current_mode == "compress":
  1745. if not self.shoot_pressed:
  1746. if self.ball:
  1747. self.shoot_default()
  1748. else:
  1749. self.compress()
  1750. else:
  1751. self.shoot_default()
  1752. def shoot_release(self):
  1753. if self.hedgehog and not self.hedgehog_autocancel:
  1754. if self.fixed_sprite == "hedgehog":
  1755. self.hedgehog_autocancel = True
  1756. elif "hedgehog_lock" not in self.alarms:
  1757. self.retract_spikes()
  1758. def compress(self):
  1759. if "atomic_compressor" in progress_flags and not self.shoot_pressed:
  1760. if self.fixed_sprite != "compress":
  1761. self.reset_image()
  1762. self.sprite = anneroy_compress_sprite
  1763. self.image_index = 0
  1764. self.image_speed = anneroy_compress_sprite.speed
  1765. self.fixed_sprite = "compress"
  1766. self.torso.visible = False
  1767. self.crouching = False
  1768. self.ball = True
  1769. self.rolling = True
  1770. self.bouncing = False
  1771. self.hedgehog = False
  1772. self.max_speed = self.__class__.max_speed
  1773. self.bbox_y = ANNEROY_BALL_BBOX_Y
  1774. self.bbox_height = ANNEROY_BALL_BBOX_HEIGHT
  1775. def hurt(self, damage=1, touching=False):
  1776. if (not touching) or (not self.hedgehog):
  1777. super(Anneroy, self).hurt(damage, touching)
  1778. def kill(self):
  1779. if self.lose_on_death:
  1780. sge.snd.Music.clear_queue()
  1781. sge.snd.Music.stop()
  1782. sge.game.current_room.alarms["death"] = DEATH_TIME
  1783. play_sound(death_sound, self.x, self.y)
  1784. self.ball = False
  1785. self.hedgehog = False
  1786. self.rolling = False
  1787. self.input_lock = True
  1788. self.tangible = False
  1789. self.view_frozen = True
  1790. self.reset_input()
  1791. self.xvelocity = 0
  1792. self.yvelocity = 0
  1793. self.gravity = 0
  1794. self.reset_image()
  1795. if self.facing > 0:
  1796. self.sprite = anneroy_death_right_sprite
  1797. else:
  1798. self.sprite = anneroy_death_left_sprite
  1799. self.image_index = 0
  1800. self.image_fps = None
  1801. self.image_xscale = abs(self.image_xscale)
  1802. self.torso.visible = False
  1803. self.fixed_sprite = "death"
  1804. # Delete all alarms to prevent any problems
  1805. self.alarms = []
  1806. def warp_in(self):
  1807. self.input_lock = True
  1808. self.reset_input()
  1809. self.xvelocity = 0
  1810. self.yvelocity = 0
  1811. self.reset_image()
  1812. self.sprite = anneroy_teleport_sprite
  1813. self.image_index = 0
  1814. self.image_fps = anneroy_teleport_sprite.fps
  1815. self.torso.visible = False
  1816. self.fixed_sprite = "warp_in"
  1817. def warp_out(self):
  1818. self.input_lock = True
  1819. self.reset_input()
  1820. self.xvelocity = 0
  1821. self.yvelocity = 0
  1822. self.reset_image()
  1823. self.sprite = anneroy_teleport_sprite
  1824. self.image_index = anneroy_teleport_sprite.frames - 1
  1825. self.image_fps = -anneroy_teleport_sprite.fps
  1826. self.torso.visible = False
  1827. self.fixed_sprite = "warp_out"
  1828. def reset_image(self):
  1829. self.visible = True
  1830. self.torso.visible = True
  1831. self.image_xscale = self.facing * abs(self.image_xscale)
  1832. self.image_speed = 0
  1833. def set_image(self):
  1834. assert self.torso is not None
  1835. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1836. aim_direction = self.aim_direction
  1837. idle_torso_right = anneroy_torso_right_idle_sprite
  1838. idle_torso_left = anneroy_torso_left_idle_sprite
  1839. # Turn Anneroy around.
  1840. if not self.fixed_sprite or self.fixed_sprite == "turn":
  1841. if not self.crouching and not self.ball:
  1842. if self.facing < 0 and h_control > 0:
  1843. self.facing = 1
  1844. if self.fixed_sprite != "turn":
  1845. self.reset_image()
  1846. self.sprite = anneroy_turn_sprite
  1847. self.image_index = 0
  1848. self.image_speed = anneroy_turn_sprite.speed
  1849. self.image_xscale = abs(self.image_xscale)
  1850. self.torso.visible = False
  1851. self.fixed_sprite = "turn"
  1852. elif self.facing > 0 and h_control < 0:
  1853. self.facing = -1
  1854. if self.fixed_sprite != "turn":
  1855. self.reset_image()
  1856. self.sprite = anneroy_turn_sprite
  1857. self.image_index = anneroy_turn_sprite.frames - 1
  1858. self.image_speed = -anneroy_turn_sprite.speed
  1859. self.image_xscale = abs(self.image_xscale)
  1860. self.torso.visible = False
  1861. self.fixed_sprite = "turn"
  1862. elif h_control:
  1863. self.facing = h_control
  1864. if not self.fixed_sprite:
  1865. old_is = self.image_speed
  1866. self.reset_image()
  1867. if self.xvelocity:
  1868. xm = (self.xvelocity > 0) - (self.xvelocity < 0)
  1869. else:
  1870. real_xv = self.x - self.xprevious
  1871. xm = (real_xv > 0) - (real_xv < 0)
  1872. if self.ball:
  1873. if self.hedgehog:
  1874. self.sprite = anneroy_hedgehog_sprite
  1875. else:
  1876. self.sprite = anneroy_ball_sprite
  1877. self.torso.visible = False
  1878. if self.on_floor:
  1879. if xm:
  1880. self.image_speed = self.speed * ANNEROY_BALL_FRAMES_PER_PIXEL
  1881. if xm != self.facing:
  1882. self.image_speed *= -1
  1883. else:
  1884. self.image_speed = 0
  1885. else:
  1886. self.image_speed = old_is
  1887. else:
  1888. # Set legs
  1889. if self.on_floor and self.was_on_floor:
  1890. if self.crouching:
  1891. self.sprite = anneroy_legs_crouched_sprite
  1892. else:
  1893. if xm:
  1894. self.sprite = anneroy_legs_run_sprite
  1895. self.image_speed = self.speed * ANNEROY_RUN_FRAMES_PER_PIXEL
  1896. if xm != self.facing:
  1897. self.image_speed *= -1
  1898. idle_torso_right = anneroy_torso_right_aim_right_sprite
  1899. idle_torso_left = anneroy_torso_left_aim_left_sprite
  1900. else:
  1901. self.sprite = anneroy_legs_stand_sprite
  1902. else:
  1903. self.sprite = anneroy_legs_jump_sprite
  1904. self.image_index = -1
  1905. elif self.fixed_sprite == "hedgehog":
  1906. if self.on_floor:
  1907. if self.xvelocity:
  1908. xm = (self.xvelocity > 0) - (self.xvelocity < 0)
  1909. else:
  1910. real_xv = self.x - self.xprevious
  1911. xm = (real_xv > 0) - (real_xv < 0)
  1912. if xm:
  1913. self.image_speed = self.speed * ANNEROY_BALL_FRAMES_PER_PIXEL
  1914. if xm != self.facing:
  1915. self.image_speed *= -1
  1916. else:
  1917. self.image_speed = 0
  1918. if "shooting" in self.alarms:
  1919. aim_direction = self.last_aim_direction
  1920. elif not self.fixed_sprite:
  1921. if self.aim_direction_time < 4 and self.aim_direction is not None:
  1922. aim_direction = max(-1, min(self.aim_direction, 1))
  1923. else:
  1924. if self.aim_direction_time < 16:
  1925. aim_direction = None
  1926. elif (self.aim_direction_time < 20 and
  1927. self.aim_direction is not None):
  1928. aim_direction = max(-1, min(self.aim_direction, 1))
  1929. # Set torso
  1930. if self.facing > 0:
  1931. self.torso.sprite = {
  1932. 0: anneroy_torso_right_aim_right_sprite,
  1933. 1: anneroy_torso_right_aim_upright_sprite,
  1934. 2: anneroy_torso_right_aim_up_sprite,
  1935. -1: anneroy_torso_right_aim_downright_sprite,
  1936. -2: anneroy_torso_right_aim_down_sprite}.get(
  1937. aim_direction, idle_torso_right)
  1938. else:
  1939. self.torso.sprite = {
  1940. 0: anneroy_torso_left_aim_left_sprite,
  1941. 1: anneroy_torso_left_aim_upleft_sprite,
  1942. 2: anneroy_torso_left_aim_up_sprite,
  1943. -1: anneroy_torso_left_aim_downleft_sprite,
  1944. -2: anneroy_torso_left_aim_down_sprite}.get(
  1945. aim_direction, idle_torso_left)
  1946. # Position torso
  1947. x, y = anneroy_torso_offset.setdefault(
  1948. (id(self.sprite), self.image_index % self.sprite.frames), (0, 0))
  1949. self.torso.x = self.x + x * self.image_xscale
  1950. self.torso.y = self.y + y * self.image_yscale
  1951. self.torso.z = self.z + 0.1
  1952. self.torso.image_xscale = abs(self.image_xscale)
  1953. self.torso.image_yscale = self.image_yscale
  1954. self.torso.image_alpha = self.image_alpha
  1955. self.torso.image_blend = self.image_blend
  1956. # Position hedgehog spikes
  1957. if self.hedgehog:
  1958. if self.hedgehog_spikes is None:
  1959. self.hedgehog_spikes = HedgehogSpikes.create(
  1960. self.x, self.y, visible=False,
  1961. bbox_x=ANNEROY_HEDGEHOG_BBOX_X,
  1962. bbox_y=ANNEROY_HEDGEHOG_BBOX_Y,
  1963. bbox_width=ANNEROY_HEDGEHOG_BBOX_WIDTH,
  1964. bbox_height=ANNEROY_HEDGEHOG_BBOX_HEIGHT,
  1965. regulate_origin=True)
  1966. else:
  1967. self.hedgehog_spikes.x = self.x
  1968. self.hedgehog_spikes.y = self.y
  1969. self.hedgehog_spikes.tangible = True
  1970. else:
  1971. if self.hedgehog_spikes is not None:
  1972. self.hedgehog_spikes.tangible = False
  1973. def event_create(self):
  1974. self.torso = sge.dsp.Object.create(self.x, self.y, self.z + 0.1,
  1975. regulate_origin=True)
  1976. self.hedgehog_spikes = HedgehogSpikes.create(
  1977. self.x, self.y, visible=False, bbox_x=ANNEROY_HEDGEHOG_BBOX_X,
  1978. bbox_y=ANNEROY_HEDGEHOG_BBOX_Y,
  1979. bbox_width=ANNEROY_HEDGEHOG_BBOX_WIDTH,
  1980. bbox_height=ANNEROY_HEDGEHOG_BBOX_HEIGHT, regulate_origin=True)
  1981. super(Anneroy, self).event_create()
  1982. def event_begin_step(self, time_passed, delta_mult):
  1983. super(Anneroy, self).event_begin_step(time_passed, delta_mult)
  1984. if not self.on_floor and self.crouching:
  1985. self.press_up()
  1986. def event_alarm(self, alarm_id):
  1987. super(Anneroy, self).event_alarm(alarm_id)
  1988. if alarm_id == "fixed_sprite":
  1989. self.fixed_sprite = False
  1990. elif alarm_id == "hedgehog_extend":
  1991. self.sprite = anneroy_hedgehog_extend_sprite
  1992. self.alarms["hedgehog_extend2"] = ANNEROY_HEDGEHOG_FRAME_TIME
  1993. elif alarm_id == "hedgehog_extend2":
  1994. self.fixed_sprite = False
  1995. if self.hedgehog_autocancel:
  1996. self.alarms["hedgehog_retract"] = ANNEROY_HEDGEHOG_TIME
  1997. self.alarms["hedgehog_lock"] = ANNEROY_HEDGEHOG_TIME
  1998. elif alarm_id == "hedgehog_retract":
  1999. self.retract_spikes()
  2000. elif alarm_id == "shoot_lock":
  2001. if self.shoot_pressed:
  2002. self.shoot()
  2003. def event_animation_end(self):
  2004. if self.fixed_sprite in {"turn", "crouch", "anim"}:
  2005. self.fixed_sprite = False
  2006. elif self.fixed_sprite == "warp_in":
  2007. self.image_index = self.sprite.frames - 1
  2008. self.image_speed = 0
  2009. self.alarms["fixed_sprite"] = WARP_TIME
  2010. self.alarms["input_lock"] = WARP_TIME
  2011. elif self.fixed_sprite == "warp_out":
  2012. self.visible = False
  2013. self.image_speed = 0
  2014. self.alarms["warp_out"] = WARP_TIME
  2015. elif self.fixed_sprite in {"compress", "decompress_fail"}:
  2016. self.fixed_sprite = False
  2017. self.image_speed = abs(self.xvelocity) * ANNEROY_BALL_FRAMES_PER_PIXEL
  2018. elif self.fixed_sprite == "wall":
  2019. self.reset_image()
  2020. if self.wall_direction < 0:
  2021. self.sprite = anneroy_walljump_right_sprite
  2022. else:
  2023. self.sprite = anneroy_walljump_left_sprite
  2024. self.image_xscale = abs(self.image_xscale)
  2025. self.torso.visible = False
  2026. self.fixed_sprite = "walljump"
  2027. self.alarms["fixed_sprite"] = ANNEROY_WALLJUMP_FRAME_TIME
  2028. self.walljumping = False
  2029. self.input_lock = False
  2030. self.facing = -self.wall_direction
  2031. self.gravity = self.__class__.gravity
  2032. self.xvelocity = ANNEROY_WALLJUMP_SPEED * self.facing
  2033. self.yvelocity = get_jump_speed(ANNEROY_WALLJUMP_HEIGHT,
  2034. self.gravity)
  2035. elif self.fixed_sprite == "death":
  2036. Smoke.create(self.x, self.y, z=(self.z + 0.1),
  2037. sprite=anneroy_explode_sprite, tangible=False)
  2038. for i in six.moves.range(12):
  2039. shard = Shard.create(
  2040. self.x, self.y, self.z, sprite=anneroy_explode_fragments,
  2041. image_index=random.randrange(anneroy_explode_fragments.frames),
  2042. image_fps=0)
  2043. shard.speed = 5
  2044. shard.move_direction = random.randrange(360)
  2045. self.destroy()
  2046. def event_physics_collision_top(self, other, move_loss):
  2047. super(Anneroy, self).event_physics_collision_top(other, move_loss)
  2048. self.event_animation_end()
  2049. def event_physics_collision_bottom(self, other, move_loss):
  2050. yv = self.yvelocity
  2051. super(Anneroy, self).event_physics_collision_bottom(other, move_loss)
  2052. if not self.was_on_floor:
  2053. if self.hedgehog:
  2054. play_sound(ball_land_sound, self.x, self.y)
  2055. elif self.ball:
  2056. if not self.bouncing or yv >= ANNEROY_BALL_FORCE_BOUNCE_SPEED:
  2057. self.bouncing = True
  2058. self.yvelocity = get_jump_speed(ANNEROY_BALL_BOUNCE_HEIGHT,
  2059. self.gravity)
  2060. else:
  2061. self.bouncing = False
  2062. play_sound(ball_land_sound, self.x, self.y)
  2063. else:
  2064. self.reset_image()
  2065. self.sprite = anneroy_legs_land_sprite
  2066. self.image_speed = None
  2067. self.image_index = 0
  2068. self.fixed_sprite = "anim"
  2069. play_sound(land_sound, self.x, self.y)
  2070. def event_jump(self):
  2071. if not self.ball:
  2072. self.reset_image()
  2073. self.sprite = anneroy_legs_jump_sprite
  2074. self.image_speed = None
  2075. self.image_index = 0
  2076. self.fixed_sprite = "anim"
  2077. def event_destroy(self):
  2078. if self.torso is not None:
  2079. self.torso.destroy()
  2080. class DeadMan(sge.dsp.Object):
  2081. """Object which falls off the screen, then gets destroyed."""
  2082. gravity = GRAVITY
  2083. fall_speed = PLAYER_FALL_SPEED
  2084. def event_begin_step(self, time_passed, delta_mult):
  2085. if self.yvelocity < self.fall_speed:
  2086. self.yacceleration = self.gravity
  2087. else:
  2088. self.yvelocity = self.fall_speed
  2089. self.yacceleration = 0
  2090. def event_step(self, time_passed, delta_mult):
  2091. if self.y - self.image_origin_y > sge.game.current_room.height:
  2092. self.destroy()
  2093. class Corpse(xsge_physics.Collider):
  2094. """Like DeadMan, but just falls to the floor, not off-screen."""
  2095. gravity = GRAVITY
  2096. fall_speed = PLAYER_FALL_SPEED
  2097. def event_create(self):
  2098. self.alarms["die"] = 90
  2099. def event_begin_step(self, time_passed, delta_mult):
  2100. if self.get_bottom_touching_wall() or self.get_bottom_touching_slope():
  2101. self.yvelocity = 0
  2102. else:
  2103. if self.yvelocity < self.fall_speed:
  2104. self.yacceleration = self.gravity
  2105. else:
  2106. self.yvelocity = min(self.yvelocity, self.fall_speed)
  2107. self.yacceleration = 0
  2108. def event_alarm(self, alarm_id):
  2109. if alarm_id == "die":
  2110. self.destroy()
  2111. class Smoke(sge.dsp.Object):
  2112. def event_animation_end(self):
  2113. self.destroy()
  2114. class InteractiveObject(sge.dsp.Object):
  2115. shootable = False
  2116. spikeable = False
  2117. freezable = False
  2118. def get_nearest_player(self):
  2119. player = None
  2120. dist = 0
  2121. for obj in sge.game.current_room.objects:
  2122. if isinstance(obj, Player):
  2123. ndist = math.hypot(self.x - obj.x, self.y - obj.y)
  2124. if player is None or ndist < dist:
  2125. player = obj
  2126. dist = ndist
  2127. return player
  2128. def set_direction(self, direction):
  2129. self.image_xscale = abs(self.image_xscale) * direction
  2130. self.xvelocity = math.copysign(self.xvelocity, self.image_xscale)
  2131. def move(self):
  2132. pass
  2133. def touch(self, other):
  2134. pass
  2135. def shoot(self, other):
  2136. pass
  2137. def spike(self, other):
  2138. self.shoot(other)
  2139. def freeze(self):
  2140. pass
  2141. def project_light(self):
  2142. pass
  2143. def event_begin_step(self, time_passed, delta_mult):
  2144. self.move()
  2145. class InteractiveCollider(InteractiveObject, xsge_physics.Collider):
  2146. def stop_left(self):
  2147. self.xvelocity = 0
  2148. def stop_right(self):
  2149. self.xvelocity = 0
  2150. def stop_up(self):
  2151. self.yvelocity = 0
  2152. def stop_down(self):
  2153. self.yvelocity = 0
  2154. def touch_hurt(self):
  2155. pass
  2156. def event_physics_collision_left(self, other, move_loss):
  2157. if isinstance(other, HurtRight):
  2158. self.touch_hurt()
  2159. if isinstance(other, xsge_physics.SolidRight):
  2160. self.stop_left()
  2161. elif isinstance(other, xsge_physics.SlopeTopRight):
  2162. if self.yvelocity > 0:
  2163. self.stop_down()
  2164. elif isinstance(other, xsge_physics.SlopeBottomRight):
  2165. if self.yvelocity < 0:
  2166. self.stop_up()
  2167. def event_physics_collision_right(self, other, move_loss):
  2168. if isinstance(other, HurtLeft):
  2169. self.touch_hurt()
  2170. if isinstance(other, xsge_physics.SolidLeft):
  2171. self.stop_right()
  2172. elif isinstance(other, xsge_physics.SlopeTopLeft):
  2173. if self.yvelocity > 0:
  2174. self.stop_down()
  2175. elif isinstance(other, xsge_physics.SlopeBottomLeft):
  2176. if self.yvelocity < 0:
  2177. self.stop_up()
  2178. def event_physics_collision_top(self, other, move_loss):
  2179. if isinstance(other, HurtBottom):
  2180. self.touch_hurt()
  2181. if isinstance(other, (xsge_physics.SolidBottom,
  2182. xsge_physics.SlopeBottomLeft,
  2183. xsge_physics.SlopeBottomRight)):
  2184. self.stop_up()
  2185. def event_physics_collision_bottom(self, other, move_loss):
  2186. if isinstance(other, HurtTop):
  2187. self.touch_hurt()
  2188. if isinstance(other, (xsge_physics.SolidTop, xsge_physics.SlopeTopLeft,
  2189. xsge_physics.SlopeTopRight)):
  2190. self.stop_down()
  2191. class FallingObject(InteractiveCollider):
  2192. """
  2193. Falls based on gravity. If on a slope, falls at a constant speed
  2194. based on the steepness of the slope.
  2195. """
  2196. gravity = GRAVITY
  2197. fall_speed = PLAYER_FALL_SPEED
  2198. slide_speed = PLAYER_SLIDE_SPEED
  2199. was_on_floor = False
  2200. def move(self):
  2201. on_floor = self.get_bottom_touching_wall()
  2202. on_slope = self.get_bottom_touching_slope()
  2203. if self.was_on_floor and (on_floor or on_slope) and self.yvelocity >= 0:
  2204. self.yacceleration = 0
  2205. if on_floor:
  2206. if self.yvelocity > 0:
  2207. self.yvelocity = 0
  2208. elif on_slope:
  2209. self.yvelocity = self.slide_speed * (on_slope[0].bbox_height /
  2210. on_slope[0].bbox_width)
  2211. else:
  2212. if self.yvelocity < self.fall_speed:
  2213. self.yacceleration = self.gravity
  2214. else:
  2215. self.yvelocity = self.fall_speed
  2216. self.yacceleration = 0
  2217. self.was_on_floor = on_floor or on_slope
  2218. class WalkingObject(FallingObject):
  2219. """
  2220. Walks in the direction it faces. Turns around at walls, and can
  2221. also be set to turn around at ledges with the stayonplatform
  2222. attribute. If slopeisplatform is False, slopes are regarded as
  2223. ledges.
  2224. """
  2225. walk_speed = PLAYER_MAX_SPEED
  2226. stayonplatform = False
  2227. slopeisplatform = True
  2228. def set_direction(self, direction):
  2229. self.xvelocity = self.walk_speed * direction
  2230. self.image_xscale = abs(self.image_xscale) * direction
  2231. def move(self):
  2232. super(WalkingObject, self).move()
  2233. if not self.xvelocity:
  2234. self.set_direction(math.copysign(1, self.image_xscale))
  2235. on_floor = self.get_bottom_touching_wall()
  2236. on_slope = self.slopeisplatform and self.get_bottom_touching_slope()
  2237. if (on_floor or on_slope) and self.stayonplatform:
  2238. if self.xvelocity < 0:
  2239. my_left = self.bbox_left + (self.x - self.bbox_left) / 2
  2240. for tile in on_floor:
  2241. if tile.bbox_left < my_left:
  2242. break
  2243. else:
  2244. if not on_slope:
  2245. self.set_direction(1)
  2246. else:
  2247. my_right = self.bbox_right - (self.bbox_right - self.x) / 2
  2248. for tile in on_floor:
  2249. if tile.bbox_right > my_right:
  2250. break
  2251. else:
  2252. if not on_slope:
  2253. self.set_direction(-1)
  2254. def stop_left(self):
  2255. self.set_direction(1)
  2256. def stop_right(self):
  2257. self.set_direction(-1)
  2258. class CrowdBlockingObject(InteractiveObject):
  2259. """Blocks CrowdObject instances, causing them to turn around."""
  2260. pass
  2261. class CrowdObject(CrowdBlockingObject):
  2262. """
  2263. Turns around when colliding with a CrowdBlockingObject. (Note: this
  2264. class is itself derived from CrowdBlockingObject.)
  2265. """
  2266. def event_collision(self, other, xdirection, ydirection):
  2267. if isinstance(other, CrowdBlockingObject):
  2268. if xdirection:
  2269. self.set_direction(-xdirection)
  2270. else:
  2271. if self.x > other.x:
  2272. self.set_direction(1)
  2273. elif self.x < other.x:
  2274. self.set_direction(-1)
  2275. elif id(self) > id(other):
  2276. self.set_direction(1)
  2277. else:
  2278. self.set_direction(-1)
  2279. else:
  2280. super(CrowdObject, self).event_collision(other, xdirection,
  2281. ydirection)
  2282. class Shard(FallingObject):
  2283. """Like Corpse, but bounces around a bit before disappearing."""
  2284. fall_speed = 99
  2285. bounce = 0.5
  2286. friction = 0.95
  2287. life = 45
  2288. def stop_left(self):
  2289. self.xvelocity *= -self.bounce
  2290. def stop_right(self):
  2291. self.xvelocity *= -self.bounce
  2292. def stop_up(self):
  2293. self.yvelocity *= -self.bounce
  2294. def stop_down(self):
  2295. self.yvelocity *= -self.bounce
  2296. def event_create(self):
  2297. self.alarms["die"] = self.life
  2298. def move(self):
  2299. super(Shard, self).move()
  2300. self.speed *= self.friction
  2301. def event_alarm(self, alarm_id):
  2302. if alarm_id == "die":
  2303. self.destroy()
  2304. class Enemy(InteractiveObject):
  2305. classname = None
  2306. shootable = True
  2307. spikeable = True
  2308. touch_damage = 5
  2309. hp = 1
  2310. def touch(self, other):
  2311. other.hurt(self.touch_damage, True)
  2312. def shoot(self, other):
  2313. # TODO: Handle different kinds of bullets
  2314. self.hp -= 1
  2315. if self.hp <= 0:
  2316. self.kill()
  2317. else:
  2318. self.hurt()
  2319. def hurt(self):
  2320. self.image_blend = sge.gfx.Color("white")
  2321. self.image_blend_mode = sge.BLEND_RGB_SCREEN
  2322. self.alarms["hurt_flash"] = FPS / 10
  2323. play_sound(enemy_hurt_sound, self.image_xcenter, self.image_ycenter)
  2324. def kill(self):
  2325. blend = sge.gfx.Color((255, 255, 255, 0))
  2326. base_sprite = sge.gfx.Sprite(width=self.sprite.width,
  2327. height=self.sprite.height,
  2328. origin_x=self.sprite.origin_x,
  2329. origin_y=self.sprite.origin_y)
  2330. base_sprite.draw_sprite(self.sprite, self.image_index,
  2331. self.sprite.origin_x, self.sprite.origin_y)
  2332. spr = sge.gfx.Sprite.from_tween(
  2333. base_sprite, int(FPS / 4), fps=FPS, blend=blend,
  2334. blend_mode=sge.BLEND_RGBA_MULTIPLY)
  2335. Smoke.create(self.x, self.y, z=self.z, sprite=spr, tangible=False,
  2336. image_origin_x=self.image_origin_x,
  2337. image_origin_y=self.image_origin_y,
  2338. image_xscale=self.image_xscale,
  2339. image_yscale=self.image_yscale,
  2340. image_rotation=self.image_rotation,
  2341. image_alpha=self.image_alpha,
  2342. image_blend=self.image_blend,
  2343. image_blend_mode=self.image_blend_mode)
  2344. if ("life_orb" in progress_flags and
  2345. random.random() < LIFE_FORCE_CHANCE):
  2346. LifeForce.create(self.image_xcenter, self.image_ycenter,
  2347. z=self.z - 0.1)
  2348. play_sound(enemy_death_sound, self.image_xcenter, self.image_ycenter)
  2349. self.destroy()
  2350. def event_alarm(self, alarm_id):
  2351. if alarm_id == "hurt_flash":
  2352. self.image_blend = None
  2353. self.image_blend_mode = None
  2354. class FreezableObject(InteractiveObject):
  2355. """Provides basic freeze behavior."""
  2356. freezable = True
  2357. frozen_sprite = None
  2358. frozen_time = 120
  2359. frozen = False
  2360. def permafreeze(self):
  2361. prev_frozen_time = self.frozen_time
  2362. self.frozen_time = None
  2363. self.freeze()
  2364. self.frozen_time = prev_frozen_time
  2365. def freeze(self):
  2366. if self.frozen_sprite is None:
  2367. self.frozen_sprite = sge.gfx.Sprite(
  2368. width=self.sprite.width, height=self.sprite.height,
  2369. origin_x=self.sprite.origin_x, origin_y=self.sprite.origin_y,
  2370. fps=THAW_FPS, bbox_x=self.sprite.bbox_x,
  2371. bbox_y=self.sprite.bbox_y, bbox_width=self.sprite.bbox_width,
  2372. bbox_height=self.sprite.bbox_height)
  2373. self.frozen_sprite.append_frame()
  2374. self.frozen_sprite.draw_sprite(self.sprite, self.image_index,
  2375. self.sprite.origin_x,
  2376. self.sprite.origin_y)
  2377. colorizer = sge.gfx.Sprite(width=self.frozen_sprite.width,
  2378. height=self.frozen_sprite.height)
  2379. colorizer.draw_rectangle(0, 0, colorizer.width, colorizer.height,
  2380. fill=sge.gfx.Color((128, 128, 255)))
  2381. self.frozen_sprite.draw_sprite(colorizer, 0, 0, 0, frame=0,
  2382. blend_mode=sge.BLEND_RGB_MULTIPLY)
  2383. frozen_self = FrozenObject.create(self.x, self.y, self.z,
  2384. sprite=self.frozen_sprite,
  2385. image_fps=0,
  2386. image_xscale=self.image_xscale,
  2387. image_yscale=self.image_yscale)
  2388. frozen_self.unfrozen = self
  2389. self.frozen = True
  2390. self.tangible = False
  2391. self.active = False
  2392. self.visible = False
  2393. if self.frozen_time is not None:
  2394. frozen_self.alarms["thaw_warn"] = self.frozen_time
  2395. class FrozenObject(InteractiveObject, xsge_physics.Solid):
  2396. freezable = True
  2397. unfrozen = None
  2398. def thaw(self):
  2399. if self.unfrozen is not None:
  2400. self.unfrozen.frozen = False
  2401. self.unfrozen.tangible = True
  2402. self.unfrozen.visible = True
  2403. self.unfrozen.activate()
  2404. self.destroy()
  2405. def burn(self):
  2406. self.thaw()
  2407. play_sound(sizzle_sound, self.x, self.y)
  2408. def freeze(self):
  2409. if self.unfrozen is not None:
  2410. self.thaw()
  2411. self.unfrozen.freeze()
  2412. def event_alarm(self, alarm_id):
  2413. if self.unfrozen is not None:
  2414. if alarm_id == "thaw_warn":
  2415. self.image_fps = None
  2416. self.alarms["thaw"] = THAW_WARN_TIME
  2417. elif alarm_id == "thaw":
  2418. self.thaw()
  2419. class Frog(Enemy, FallingObject, CrowdObject):
  2420. slide_speed = 0
  2421. jump_distance = 200
  2422. jump_height = 2 * TILE_SIZE + 1
  2423. jump_speed = 3
  2424. jump_interval = FPS / 2
  2425. def stop_left(self):
  2426. if self.yvelocity >= 0:
  2427. self.xvelocity = 0
  2428. def stop_right(self):
  2429. if self.yvelocity >= 0:
  2430. self.xvelocity = 0
  2431. def stop_down(self):
  2432. self.xvelocity = 0
  2433. self.yvelocity = 0
  2434. def event_create(self):
  2435. self.bbox_x = 2
  2436. self.bbox_y = 5
  2437. self.bbox_width = 12
  2438. self.bbox_height = 11
  2439. def event_step(self, time_passed, delta_mult):
  2440. super(Frog, self).event_step(time_passed, delta_mult)
  2441. if ("jump" not in self.alarms and self.was_on_floor and
  2442. not self.yvelocity):
  2443. self.alarms["jump"] = self.jump_interval
  2444. target = self.get_nearest_player()
  2445. if target is not None:
  2446. xvec = target.x - self.image_xcenter
  2447. self.image_xscale = math.copysign(self.image_xscale, xvec)
  2448. if self.was_on_floor:
  2449. self.sprite = frog_stand_sprite
  2450. elif self.yvelocity < 0:
  2451. self.sprite = frog_jump_sprite
  2452. else:
  2453. self.sprite = frog_fall_sprite
  2454. def event_alarm(self, alarm_id):
  2455. if alarm_id == "jump":
  2456. target = self.get_nearest_player()
  2457. if target is not None:
  2458. xvec = target.x - self.image_xcenter
  2459. yvec = target.y - self.image_ycenter
  2460. self.image_xscale = math.copysign(self.image_xscale, xvec)
  2461. dist = math.hypot(xvec, yvec)
  2462. if dist <= self.jump_distance:
  2463. self.xvelocity = math.copysign(self.jump_speed, xvec)
  2464. self.yvelocity = get_jump_speed(self.jump_height,
  2465. self.gravity)
  2466. play_sound(frog_jump_sound, self.image_xcenter,
  2467. self.image_ycenter)
  2468. class Hedgehog(Enemy, FallingObject, CrowdBlockingObject):
  2469. hp = 3
  2470. touch_damage = 7
  2471. charge_distance = 300
  2472. acceleration = 0.25
  2473. walk_speed = 1
  2474. max_speed = 4
  2475. roll_slope_acceleration = 0.2
  2476. roll_max_speed = 8
  2477. friction = PLAYER_FRICTION
  2478. roll_friction = 0.05
  2479. walk_frames_per_pixel = 1 / 4
  2480. roll_frames_per_pixel = 1 / 5
  2481. @property
  2482. def slope_acceleration(self):
  2483. if self.rolling:
  2484. return self.roll_slope_acceleration
  2485. else:
  2486. return 0
  2487. def stop_left(self):
  2488. self.xvelocity = 0
  2489. def stop_right(self):
  2490. self.xvelocity = 0
  2491. def event_create(self):
  2492. self.bbox_x = 4
  2493. self.bbox_y = 6
  2494. self.bbox_width = 12
  2495. self.bbox_height = 14
  2496. self.rolling = False
  2497. self.anim_lock = False
  2498. self.sprite = hedgehog_stand_sprite
  2499. def event_step(self, time_passed, delta_mult):
  2500. self.xacceleration = 0
  2501. if self.rolling:
  2502. if self.was_on_floor:
  2503. self.xdeceleration = self.roll_friction
  2504. target = self.get_nearest_player()
  2505. if target is not None:
  2506. xvec = target.x - self.image_xcenter
  2507. tdir = (xvec > 0) - (xvec < 0)
  2508. vdir = (self.xvelocity > 0) - (self.xvelocity < 0)
  2509. yvec = target.y - self.image_ycenter
  2510. dist = math.hypot(xvec, yvec)
  2511. if dist <= self.charge_distance and tdir == vdir:
  2512. if abs(self.xvelocity) < self.max_speed:
  2513. self.xacceleration = math.copysign(
  2514. self.acceleration, xvec)
  2515. self.xdeceleration = 0
  2516. else:
  2517. self.xdeceleration = 0
  2518. if abs(self.xvelocity) < self.walk_speed:
  2519. self.rolling = False
  2520. self.anim_lock = True
  2521. self.sprite = hedgehog_uncompress_sprite
  2522. self.image_index = 0
  2523. self.image_speed = None
  2524. if not self.anim_lock:
  2525. self.sprite = hedgehog_ball_sprite
  2526. self.image_speed = (abs(self.xvelocity) *
  2527. self.roll_frames_per_pixel)
  2528. if abs(self.xvelocity) >= self.roll_max_speed:
  2529. self.xvelocity = math.copysign(self.roll_max_speed,
  2530. self.xvelocity)
  2531. else:
  2532. if self.was_on_floor:
  2533. self.xdeceleration = self.friction
  2534. target = self.get_nearest_player()
  2535. if target is not None:
  2536. xvec = target.x - self.image_xcenter
  2537. yvec = target.y - self.image_ycenter
  2538. dist = math.hypot(xvec, yvec)
  2539. if dist <= self.charge_distance:
  2540. self.xacceleration = math.copysign(self.acceleration,
  2541. xvec)
  2542. self.xdeceleration = 0
  2543. if abs(self.xvelocity) >= self.max_speed:
  2544. self.xvelocity = math.copysign(self.max_speed,
  2545. self.xvelocity)
  2546. self.rolling = True
  2547. self.anim_lock = True
  2548. self.sprite = hedgehog_compress_sprite
  2549. self.image_index = 0
  2550. self.image_speed = None
  2551. else:
  2552. self.xdeceleration = 0
  2553. if not self.anim_lock:
  2554. if self.xvelocity:
  2555. self.sprite = hedgehog_walk_sprite
  2556. self.image_speed = (abs(self.xvelocity) *
  2557. self.walk_frames_per_pixel)
  2558. else:
  2559. self.sprite = hedgehog_stand_sprite
  2560. self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
  2561. def event_animation_end(self):
  2562. self.anim_lock = False
  2563. class Worm(Enemy, InteractiveCollider, CrowdBlockingObject):
  2564. hp = 5
  2565. touch_damage = 10
  2566. extend_distance = 96
  2567. repeat_delay = 90
  2568. def __init__(self, x, y, **kwargs):
  2569. kwargs["sprite"] = worm_sprite
  2570. kwargs["collision_precise"] = True
  2571. kwargs["tangible"] = False
  2572. kwargs["image_fps"] = 0
  2573. sge.dsp.Object.__init__(self, x, y, **kwargs)
  2574. def event_create(self):
  2575. sge.dsp.Object.create(
  2576. self.x, self.y, self.z + 0.1, sprite=worm_base_sprite,
  2577. tangible=False)
  2578. def event_step(self, time_passed, delta_mult):
  2579. super(Worm, self).event_step(time_passed, delta_mult)
  2580. if not self.tangible and "extend_wait" not in self.alarms:
  2581. target = self.get_nearest_player()
  2582. if target is not None:
  2583. xvec = target.x - self.image_xcenter
  2584. yvec = target.y - self.image_ycenter
  2585. dist = math.hypot(xvec, yvec)
  2586. if dist <= self.extend_distance:
  2587. self.tangible = True
  2588. self.image_fps = None
  2589. self.image_index = 0
  2590. def event_animation_end(self):
  2591. self.tangible = False
  2592. self.image_fps = 0
  2593. self.image_index = 0
  2594. self.alarms["extend_wait"] = self.repeat_delay
  2595. class Bat(Enemy, InteractiveCollider, CrowdBlockingObject):
  2596. charge_distance = 200
  2597. charge_speed = 3
  2598. return_speed = 2
  2599. return_delay = 15
  2600. repeat_delay = 60
  2601. def __init__(self, x, y, **kwargs):
  2602. self.returning = False
  2603. self.path = None
  2604. kwargs["sprite"] = bat_sprite
  2605. sge.dsp.Object.__init__(self, x, y, **kwargs)
  2606. def attack(self, target):
  2607. xvec = target.x - self.image_xcenter
  2608. yvec = target.y - self.image_ycenter
  2609. dist = math.hypot(xvec, yvec)
  2610. if dist <= self.charge_distance:
  2611. self.speed = self.charge_speed
  2612. self.move_direction = math.degrees(math.atan2(yvec, xvec))
  2613. self.image_speed = self.sprite.speed * 2
  2614. def stop(self):
  2615. if self.path is not None:
  2616. self.path.follow_stop(self)
  2617. self.path = None
  2618. self.speed = 0
  2619. self.image_speed = None
  2620. if self.returning:
  2621. self.returning = False
  2622. self.alarms["charge_wait"] = self.repeat_delay
  2623. else:
  2624. self.returning = True
  2625. self.alarms["return"] = self.return_delay
  2626. def stop_left(self):
  2627. self.stop()
  2628. def stop_right(self):
  2629. self.stop()
  2630. def stop_up(self):
  2631. self.stop()
  2632. def stop_down(self):
  2633. self.stop()
  2634. def event_create(self):
  2635. self.image_xscale *= random.choice([1, -1])
  2636. def event_step(self, time_passed, delta_mult):
  2637. super(Bat, self).event_step(time_passed, delta_mult)
  2638. if (self.speed == 0 and "charge_wait" not in self.alarms and
  2639. not self.returning):
  2640. target = self.get_nearest_player()
  2641. if target is not None:
  2642. self.attack(target)
  2643. if self.xvelocity:
  2644. self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
  2645. def event_alarm(self, alarm_id):
  2646. if alarm_id == "return":
  2647. xvec = self.xstart - self.x
  2648. yvec = self.ystart - self.y
  2649. self.path = xsge_path.Path.create(self.x, self.y,
  2650. points=[(xvec, yvec)])
  2651. def evt_follow_end(obj, self=self.path): obj.stop()
  2652. self.path.event_follow_end = evt_follow_end
  2653. self.path.follow_start(self, self.return_speed)
  2654. class Jellyfish(Enemy, CrowdBlockingObject):
  2655. hp = 3
  2656. touch_damage = 7
  2657. swim_speed = 2
  2658. friction = 0.025
  2659. swim_interval = FPS
  2660. def __init__(self, x, y, **kwargs):
  2661. kwargs["sprite"] = jellyfish_idle_sprite
  2662. kwargs["regulate_origin"] = True
  2663. sge.dsp.Object.__init__(self, x, y, **kwargs)
  2664. def stop_left(self):
  2665. self.xvelocity = 0
  2666. self.yvelocity = 0
  2667. def stop_right(self):
  2668. self.xvelocity = 0
  2669. self.yvelocity = 0
  2670. def stop_up(self):
  2671. self.xvelocity = 0
  2672. self.yvelocity = 0
  2673. def stop_down(self):
  2674. self.xvelocity = 0
  2675. self.yvelocity = 0
  2676. def event_create(self):
  2677. self.x += self.image_origin_x
  2678. self.y += self.image_origin_y
  2679. self.bbox_x = -9
  2680. self.bbox_y = -9
  2681. self.bbox_width = 17
  2682. self.bbox_height = 17
  2683. self.xdeceleration = self.friction
  2684. self.ydeceleration = self.friction
  2685. self.alarms["swim"] = self.swim_interval
  2686. def event_alarm(self, alarm_id):
  2687. super(Jellyfish, self).event_alarm(alarm_id)
  2688. if alarm_id == "swim":
  2689. choices = 3 * [1] + [-1]
  2690. xv = random.choice(choices)
  2691. if self.x > self.xstart:
  2692. xv *= -1
  2693. yv = random.choice(choices)
  2694. if self.y > self.ystart:
  2695. yv *= -1
  2696. self.image_xscale = math.copysign(self.image_xscale, xv)
  2697. self.image_yscale = math.copysign(self.image_yscale, yv)
  2698. self.sprite = jellyfish_swim_start_sprite
  2699. self.image_index = 0
  2700. def event_animation_end(self):
  2701. if self.sprite == jellyfish_swim_start_sprite:
  2702. self.sprite = jellyfish_swim_sprite
  2703. self.image_index = 0
  2704. self.xvelocity = math.copysign(self.swim_speed, self.image_xscale)
  2705. self.yvelocity = math.copysign(self.swim_speed, self.image_yscale)
  2706. self.alarms["swim"] = self.swim_interval
  2707. elif self.sprite != jellyfish_idle_sprite:
  2708. self.sprite = jellyfish_idle_sprite
  2709. self.image_index = 0
  2710. class Scorpion(Enemy, WalkingObject, CrowdObject):
  2711. walk_speed = 1
  2712. hp = 10
  2713. touch_damage = 10
  2714. stayonplatform = True
  2715. slopeisplatform = False
  2716. sight_distance = 1000
  2717. sight_threshold = 64
  2718. shoot_interval = FPS
  2719. shoot_recheck_interval = FPS / 10
  2720. bullet_speed = 3
  2721. def __init__(self, x, y, **kwargs):
  2722. x += scorpion_stand_sprite.origin_x
  2723. y += scorpion_stand_sprite.origin_y
  2724. kwargs["sprite"] = scorpion_stand_sprite
  2725. kwargs["bbox_x"] = -28
  2726. kwargs["bbox_y"] = -1
  2727. kwargs["bbox_width"] = 56
  2728. kwargs["bbox_height"] = 28
  2729. super(Scorpion, self).__init__(x, y, **kwargs)
  2730. def move(self):
  2731. if not self.action:
  2732. super(Scorpion, self).move()
  2733. if self.xvelocity:
  2734. self.sprite = scorpion_walk_sprite
  2735. self.image_speed = (abs(self.xvelocity) *
  2736. SCORPION_WALK_FRAMES_PER_PIXEL)
  2737. else:
  2738. self.sprite = scorpion_stand_sprite
  2739. def attack(self):
  2740. target = self.get_nearest_player()
  2741. if target is not None:
  2742. xvec = target.x - self.image_xcenter
  2743. yvec = target.y - self.image_ycenter
  2744. if (abs(xvec) <= self.sight_distance and
  2745. abs(yvec) < self.sight_threshold):
  2746. self.xvelocity = 0
  2747. self.action = "shoot_start"
  2748. self.sprite = scorpion_shoot_start_sprite
  2749. self.image_index = 0
  2750. self.image_fps = None
  2751. self.image_xscale = math.copysign(self.image_xscale, xvec)
  2752. return
  2753. self.alarms["shoot"] = self.shoot_recheck_interval
  2754. def event_create(self):
  2755. super(Scorpion, self).event_create()
  2756. self.alarms["shoot"] = self.shoot_interval
  2757. self.action = None
  2758. def event_alarm(self, alarm_id):
  2759. super(Scorpion, self).event_alarm(alarm_id)
  2760. if alarm_id == "shoot":
  2761. self.attack()
  2762. def event_animation_end(self):
  2763. if self.action == "shoot_start":
  2764. self.action = "shoot_end"
  2765. self.sprite = scorpion_shoot_end_sprite
  2766. self.image_index = 0
  2767. self.image_fps = None
  2768. xv = math.copysign(self.bullet_speed, self.image_xscale)
  2769. x = self.x
  2770. if self.image_xscale < 0:
  2771. x -= scorpion_projectile_sprite.width
  2772. ScorpionBullet.create(
  2773. x, self.y, self.z + 0.1,
  2774. sprite=scorpion_projectile_sprite, xvelocity=xv,
  2775. image_xscale=self.image_xscale, image_yscale=self.image_yscale)
  2776. play_sound(scorpion_shoot_sound, self.x, self.y)
  2777. elif self.action == "shoot_end":
  2778. self.action = None
  2779. self.sprite = scorpion_stand_sprite
  2780. self.alarms["shoot"] = self.shoot_interval
  2781. class Mantanoid(Enemy, FallingObject, CrowdBlockingObject):
  2782. classname = "Mantanoid"
  2783. hp = 10
  2784. touch_damage = 10
  2785. slash_damage = 20
  2786. sight_distance = 300
  2787. def __init__(self, x, y, hiding=False, wander_x=None, **kwargs):
  2788. x += mantanoid_stand_sprite.origin_x
  2789. y += mantanoid_stand_sprite.origin_y
  2790. kwargs["sprite"] = mantanoid_stand_sprite
  2791. kwargs["bbox_x"] = MANTANOID_BBOX_X
  2792. kwargs["bbox_y"] = MANTANOID_BBOX_Y
  2793. kwargs["bbox_width"] = MANTANOID_BBOX_WIDTH
  2794. kwargs["bbox_height"] = MANTANOID_BBOX_HEIGHT
  2795. kwargs["regulate_origin"] = True
  2796. super(Mantanoid, self).__init__(x, y, **kwargs)
  2797. self.hiding = hiding
  2798. self.wander_x = wander_x if wander_x else x
  2799. self.action = None
  2800. self.target = None
  2801. self.movement_speed = 0
  2802. self.can_act = False
  2803. self.action_check = None
  2804. self.action_check_id = None
  2805. self.action_check_x = None
  2806. self.action_check_y = None
  2807. self.action_check_dest_x = None
  2808. self.action_check_dest_y = None
  2809. self.action_check_verify = None
  2810. def set_direction(self, direction):
  2811. if not self.action and self.can_act:
  2812. if direction > 0:
  2813. self.perform_action(self.action_turn_right)
  2814. elif direction < 0:
  2815. self.perform_action(self.action_turn_left)
  2816. def stop_left(self):
  2817. if self.yvelocity > 0:
  2818. self.xvelocity = 0
  2819. if not self.action and self.can_act:
  2820. if self.target is not None:
  2821. if not self.check_action(self.action_hop, self.target.x,
  2822. self.target.y, "stop_down"):
  2823. if not self.check_action(self.action_jump, self.target.x,
  2824. self.target.y, "stop_down"):
  2825. self.target = None
  2826. else:
  2827. self.perform_action(self.action_turn_right)
  2828. def stop_right(self):
  2829. if self.yvelocity > 0:
  2830. self.xvelocity = 0
  2831. if not self.action and self.can_act:
  2832. if self.target is not None:
  2833. if not self.check_action(self.action_hop, self.target.x,
  2834. self.target.y, "stop_down"):
  2835. if not self.check_action(self.action_jump, self.target.x,
  2836. self.target.y, "stop_down"):
  2837. self.target = None
  2838. else:
  2839. self.perform_action(self.action_turn_left)
  2840. def stop_up(self):
  2841. self.yvelocity = 0
  2842. if (self.action_check_verify == "stop_down" and
  2843. self.get_bottom_touching_wall()):
  2844. self.verify_action()
  2845. def stop_down(self):
  2846. self.xvelocity = 0
  2847. self.yvelocity = 0
  2848. if not self.was_on_floor:
  2849. if not self.action or self.action == "animation":
  2850. self.sprite = mantanoid_land_sprite
  2851. self.fps = None
  2852. self.image_index = 0
  2853. self.action = "animation"
  2854. if self.action_check_verify == "stop_down":
  2855. self.verify_action()
  2856. def update_wander(self):
  2857. if (not self.hiding and self.was_on_floor and
  2858. "move_lock" not in self.alarms):
  2859. choices = 3 * [1] + 7 * [0] + [-1]
  2860. xv = random.choice(choices)
  2861. if self.x > self.wander_x:
  2862. xv *= -1
  2863. self.set_direction(xv)
  2864. self.movement_speed = MANTANOID_WANDER_SPEED * abs(xv)
  2865. self.alarms["move_lock"] = MANTANOID_WANDER_INTERVAL
  2866. def perform_action(self, action):
  2867. if not self.action and self.can_act:
  2868. self.can_act = False
  2869. self.reset_action_check()
  2870. self.xvelocity = 0
  2871. if (self.target is not None and self.target.x < self.x and
  2872. self.image_xscale > 0):
  2873. self.action_turn_left()
  2874. elif (self.target is not None and self.target.x > self.x and
  2875. self.image_xscale < 0):
  2876. self.action_turn_right()
  2877. else:
  2878. action()
  2879. def check_action(self, action, target_x, target_y, verify_event):
  2880. if not self.can_act:
  2881. return False
  2882. if target_x is None and target_y is None:
  2883. self.perform_action(action)
  2884. return True
  2885. if target_x is not None and target_y is not None:
  2886. if sge.collision.rectangle(target_x, target_y, 1, 1, MantanoidNoGo):
  2887. # No-go zone, which means we can't go there no matter what.
  2888. return False
  2889. def rough(x):
  2890. return int(math.floor(x / 8)) * 8 if x is not None else None
  2891. action_id = "{}; {}: ({},{})->({},{})!{}~{} -- ".format(
  2892. self.__class__.__name__, sge.game.current_room.fname,
  2893. rough(self.x), rough(self.y), rough(target_x), rough(target_y),
  2894. action.__name__, verify_event)
  2895. action_id_pass = action_id + "pass"
  2896. action_id_fail = action_id + "fail"
  2897. if action_id_pass in ai_data:
  2898. if action_id_fail not in ai_data or random.random() < 0.25:
  2899. if self.target.x < self.x and self.image_xscale > 0:
  2900. self.perform_action(self.action_turn_left)
  2901. return True
  2902. elif self.target.x > self.x and self.image_xscale < 0:
  2903. self.perform_action(self.action_turn_right)
  2904. return True
  2905. else:
  2906. self.perform_action(action)
  2907. return True
  2908. else:
  2909. return False
  2910. elif action_id_fail not in ai_data:
  2911. if self.target.x < self.x and self.image_xscale > 0:
  2912. self.perform_action(self.action_turn_left)
  2913. return True
  2914. elif self.target.x > self.x and self.image_xscale < 0:
  2915. self.perform_action(self.action_turn_right)
  2916. return True
  2917. else:
  2918. self.perform_action(action)
  2919. self.action_check = action
  2920. self.action_check_id = action_id
  2921. self.action_check_x = self.x
  2922. self.action_check_y = self.y
  2923. self.action_check_dest_x = target_x
  2924. self.action_check_dest_y = target_y
  2925. self.action_check_verify = verify_event
  2926. return True
  2927. return False
  2928. def verify_action(self):
  2929. if self.action_check is not None:
  2930. if (self.action_check_dest_x is None and
  2931. self.action_check_dest_y is None):
  2932. ai_data.add(self.action_check_id + "pass")
  2933. elif self.action_check_dest_x is None:
  2934. orig_dist = abs(self.action_check_dest_y - self.action_check_y)
  2935. new_dist = abs(self.action_check_dest_y - self.y)
  2936. elif self.action_check_dest_y is None:
  2937. orig_dist = abs(self.action_check_dest_x - self.action_check_x)
  2938. new_dist = abs(self.action_check_dest_x - self.x)
  2939. else:
  2940. orig_dist = abs(math.hypot(
  2941. self.action_check_dest_x - self.action_check_x,
  2942. self.action_check_dest_y - self.action_check_y))
  2943. new_dist = abs(math.hypot(
  2944. self.action_check_dest_x - self.x,
  2945. self.action_check_dest_y - self.y))
  2946. if new_dist < orig_dist:
  2947. ai_data.add(self.action_check_id + "pass")
  2948. else:
  2949. ai_data.add(self.action_check_id + "fail")
  2950. self.reset_action_check()
  2951. def deverify_action(self):
  2952. if self.action_check is not None:
  2953. ai_data.add(self.action_check_id + "fail")
  2954. self.reset_action_check()
  2955. def reset_action_check(self):
  2956. self.action_check = None
  2957. self.action_check_id = None
  2958. self.action_check_x = None
  2959. self.action_check_y = None
  2960. self.action_check_dest_x = None
  2961. self.action_check_dest_y = None
  2962. self.action_check_verify = None
  2963. def action_turn_left(self):
  2964. if (self.image_xscale > 0 and not self.action and
  2965. self.was_on_floor and self.get_bottom_touching_wall()):
  2966. self.image_xscale = -abs(self.image_xscale)
  2967. self.sprite = mantanoid_turn_sprite
  2968. self.image_fps = None
  2969. self.image_index = 0
  2970. self.action = "animation"
  2971. def action_turn_right(self):
  2972. if (self.image_xscale < 0 and not self.action and
  2973. self.was_on_floor and self.get_bottom_touching_wall()):
  2974. self.image_xscale = abs(self.image_xscale)
  2975. self.sprite = mantanoid_turn_sprite
  2976. self.image_fps = None
  2977. self.image_index = 0
  2978. self.action = "animation"
  2979. def action_hop(self):
  2980. if (self.was_on_floor and self.yvelocity >= 0 and
  2981. (self.get_bottom_touching_wall() or
  2982. self.get_bottom_touching_slope())):
  2983. self.sprite = mantanoid_hop_start_sprite
  2984. self.image_fps = None
  2985. self.image_index = 0
  2986. self.action = "hop"
  2987. def action_jump(self):
  2988. if (self.was_on_floor and self.yvelocity >= 0 and
  2989. (self.get_bottom_touching_wall() or
  2990. self.get_bottom_touching_slope())):
  2991. self.sprite = mantanoid_jump_start_sprite
  2992. self.image_fps = None
  2993. self.image_index = 0
  2994. self.action = "jump"
  2995. def action_approach(self):
  2996. self.hiding = False
  2997. if self.target is not None:
  2998. if self.movement_speed != MANTANOID_APPROACH_SPEED:
  2999. self.movement_speed = MANTANOID_APPROACH_SPEED
  3000. play_sound(mantanoid_approach_sound, self.x, self.y)
  3001. self.alarms["action_lock"] = MANTANOID_APPROACH_INTERVAL
  3002. def action_slash(self):
  3003. self.hiding = False
  3004. if self.target is not None:
  3005. self.action = "slash"
  3006. self.sprite = mantanoid_slash_start_sprite
  3007. self.image_fps = None
  3008. self.image_index = 0
  3009. def check_hazards(self):
  3010. return None
  3011. def update_action(self):
  3012. action = self.check_hazards()
  3013. if not action:
  3014. if (self.target is not None and
  3015. not self.target.collision(MantanoidNoGo)):
  3016. xdist = abs(self.target.x - self.x)
  3017. ydist = abs(self.target.y - self.y)
  3018. if (xdist <= MANTANOID_SLASH_DISTANCE and
  3019. ydist <= MANTANOID_LEVEL_DISTANCE):
  3020. action = self.action_slash
  3021. elif "action_lock" not in self.alarms:
  3022. if ydist <= MANTANOID_LEVEL_DISTANCE:
  3023. action = self.action_approach
  3024. elif not self.hiding:
  3025. if self.target.on_floor and self.check_action(
  3026. self.action_approach, None, self.target.y,
  3027. "action_lock"):
  3028. return
  3029. elif self.target.on_floor and self.check_action(
  3030. self.action_jump, None, self.target.y,
  3031. "stop_down"):
  3032. return
  3033. elif self.target.on_floor and self.check_action(
  3034. self.action_hop, None, self.target.y,
  3035. "stop_down"):
  3036. return
  3037. elif self.check_action(
  3038. self.action_approach, self.target.x,
  3039. self.target.y, "action_lock"):
  3040. return
  3041. else:
  3042. self.target = None
  3043. if action:
  3044. self.perform_action(action)
  3045. else:
  3046. self.update_wander()
  3047. def set_image(self):
  3048. if not self.action:
  3049. if self.was_on_floor:
  3050. if self.xvelocity:
  3051. self.sprite = mantanoid_walk_sprite
  3052. self.image_speed = abs(
  3053. self.xvelocity * MANTANOID_WALK_FRAMES_PER_PIXEL)
  3054. else:
  3055. self.sprite = mantanoid_stand_sprite
  3056. if random.random() < 1 / (2 * FPS):
  3057. self.sprite = mantanoid_idle_sprite
  3058. self.image_fps = None
  3059. self.image_index = 0
  3060. self.action = "animation"
  3061. else:
  3062. if self.yvelocity < 0:
  3063. self.sprite = mantanoid_jump_sprite
  3064. else:
  3065. if self.sprite == mantanoid_jump_sprite:
  3066. self.sprite = mantanoid_fall_start_sprite
  3067. self.image_fps = None
  3068. self.image_index = 0
  3069. self.action = "animation"
  3070. if self.action_check_verify == "peak":
  3071. self.verify_action()
  3072. else:
  3073. self.sprite = mantanoid_fall_sprite
  3074. def event_step(self, time_passed, delta_mult):
  3075. on_floor = self.get_bottom_touching_wall()
  3076. self.can_act = (self.was_on_floor and on_floor and self.yvelocity >= 0)
  3077. if not self.action and self.can_act:
  3078. self.target = self.get_nearest_player()
  3079. dist = 0
  3080. if self.target is not None:
  3081. xvec = self.target.x - self.x
  3082. yvec = self.target.y - self.y
  3083. dist = math.hypot(xvec, yvec)
  3084. if dist > self.sight_distance:
  3085. self.target = None
  3086. self.update_action()
  3087. if not self.action:
  3088. if ((self.image_xscale > 0 or not self.get_left_touching_wall()) and
  3089. (self.image_xscale < 0 or not self.get_right_touching_wall())):
  3090. self.xvelocity = self.movement_speed * self.image_xscale
  3091. on_slope = self.get_bottom_touching_slope()
  3092. if (on_floor or on_slope):
  3093. if self.xvelocity < 0:
  3094. for tile in on_floor:
  3095. if tile.bbox_left < self.bbox_left:
  3096. break
  3097. else:
  3098. if not on_slope:
  3099. if self.can_act:
  3100. if self.target is not None:
  3101. if (not self.check_action(
  3102. self.action_hop, self.target.x,
  3103. self.target.y, "stop_down") and
  3104. not self.check_action(
  3105. self.action_jump,
  3106. self.target.x, self.target.y,
  3107. "stop_down") and
  3108. not self.check_action(
  3109. self.action_approach,
  3110. self.target.x, self.target.y,
  3111. "stop_down")):
  3112. self.target = None
  3113. else:
  3114. self.perform_action(self.action_turn_right)
  3115. else:
  3116. self.xvelocity = 0
  3117. else:
  3118. for tile in on_floor:
  3119. if tile.bbox_right > self.bbox_right:
  3120. break
  3121. else:
  3122. if not on_slope:
  3123. if self.can_act:
  3124. if self.target is not None:
  3125. if (not self.check_action(
  3126. self.action_hop, self.target.x,
  3127. self.target.y, "stop_down") and
  3128. not self.check_action(
  3129. self.action_jump,
  3130. self.target.x, self.target.y,
  3131. "stop_down") and
  3132. not self.check_action(
  3133. self.action_approach,
  3134. self.target.x, self.target.y,
  3135. "stop_down")):
  3136. self.target = None
  3137. else:
  3138. self.perform_action(self.action_turn_left)
  3139. else:
  3140. self.xvelocity = 0
  3141. self.set_image()
  3142. def event_alarm(self, alarm_id):
  3143. super(Mantanoid, self).event_alarm(alarm_id)
  3144. if alarm_id == "action_lock":
  3145. if self.action_check_verify == "action_lock":
  3146. self.verify_action()
  3147. def event_animation_end(self):
  3148. if self.action == "hop":
  3149. self.action = None
  3150. self.can_act = False
  3151. self.set_image()
  3152. if self.was_on_floor and (self.get_bottom_touching_wall() or
  3153. self.get_bottom_touching_slope()):
  3154. self.xvelocity = math.copysign(MANTANOID_APPROACH_SPEED,
  3155. self.image_xscale)
  3156. self.yvelocity = get_jump_speed(MANTANOID_HOP_HEIGHT,
  3157. self.gravity)
  3158. if self.action == "jump":
  3159. self.action = None
  3160. self.can_act = False
  3161. self.set_image()
  3162. if self.was_on_floor and (self.get_bottom_touching_wall() or
  3163. self.get_bottom_touching_slope()):
  3164. self.xvelocity = math.copysign(MANTANOID_APPROACH_SPEED,
  3165. self.image_xscale)
  3166. self.yvelocity = get_jump_speed(MANTANOID_JUMP_HEIGHT,
  3167. self.gravity)
  3168. elif self.action == "slash":
  3169. hit_target = False
  3170. w = MANTANOID_SLASH_BBOX_WIDTH
  3171. h = MANTANOID_SLASH_BBOX_HEIGHT
  3172. if self.image_xscale > 0:
  3173. x = self.x + MANTANOID_SLASH_BBOX_X
  3174. y = self.y + MANTANOID_SLASH_BBOX_Y
  3175. else:
  3176. x = self.x - MANTANOID_SLASH_BBOX_X - w
  3177. y = self.y - MANTANOID_SLASH_BBOX_Y - h
  3178. for other in sge.collision.rectangle(x, y, w, h):
  3179. if isinstance(other, Player):
  3180. other.hurt(self.slash_damage)
  3181. if other is self.target:
  3182. hit_target = True
  3183. elif isinstance(other, AnneroyBullet):
  3184. other.dissipate(other.xvelocity, other.yvelocity)
  3185. double = False
  3186. if self.target is not None and not hit_target:
  3187. xdist = abs(self.target.x - self.x)
  3188. ydist = abs(self.target.y - self.y)
  3189. if (ydist <= MANTANOID_LEVEL_DISTANCE and
  3190. xdist <= MANTANOID_SLASH2_DISTANCE):
  3191. double = True
  3192. play_sound(mantanoid_slash_sound, self.x, self.y)
  3193. self.image_fps = None
  3194. self.image_index = 0
  3195. if double:
  3196. self.sprite = mantanoid_slash_double_first_sprite
  3197. self.action = "doubleslash"
  3198. else:
  3199. self.sprite = mantanoid_slash_single_sprite
  3200. self.action = "animation"
  3201. elif self.action == "doubleslash":
  3202. self.move_x(MANTANOID_DOUBLESLASH_OFFSET * self.image_xscale)
  3203. w = MANTANOID_SLASH2_BBOX_WIDTH
  3204. h = MANTANOID_SLASH2_BBOX_HEIGHT
  3205. if self.image_xscale > 0:
  3206. x = self.x + MANTANOID_SLASH2_BBOX_X
  3207. y = self.y + MANTANOID_SLASH2_BBOX_Y
  3208. else:
  3209. x = self.x - MANTANOID_SLASH2_BBOX_X - w
  3210. y = self.y - MANTANOID_SLASH2_BBOX_Y - h
  3211. for other in sge.collision.rectangle(x, y, w, h):
  3212. if isinstance(other, Player):
  3213. other.hurt(self.slash_damage)
  3214. elif isinstance(other, AnneroyBullet):
  3215. other.dissipate(other.xvelocity, other.yvelocity)
  3216. play_sound(mantanoid_slash_sound, self.x, self.y)
  3217. self.sprite = mantanoid_slash_double_second_sprite
  3218. self.image_fps = None
  3219. self.image_index = 0
  3220. self.action = "animation"
  3221. elif self.action == "animation":
  3222. self.action = None
  3223. self.set_image()
  3224. class MantanoidNoGo(sge.dsp.Object):
  3225. def __init__(self, x, y, **kwargs):
  3226. kwargs["visible"] = False
  3227. kwargs["checks_collisions"] = False
  3228. super(MantanoidNoGo, self).__init__(x, y, **kwargs)
  3229. class Boss(InteractiveObject):
  3230. def __init__(self, x, y, ID="boss", death_timeline=None, stage=0,
  3231. **kwargs):
  3232. self.ID = ID
  3233. self.death_timeline = death_timeline
  3234. self.stage = stage
  3235. super(Boss, self).__init__(x, y, **kwargs)
  3236. def event_create(self):
  3237. super(Boss, self).event_create()
  3238. sge.game.current_room.add_timeline_object(self)
  3239. def event_destroy(self):
  3240. for obj in sge.game.current_room.objects:
  3241. if obj is not self and isinstance(obj, Boss) and obj.stage > 0:
  3242. break
  3243. else:
  3244. if self.death_timeline:
  3245. sge.game.current_room.load_timeline(self.death_timeline)
  3246. class LifeForce(InteractiveObject):
  3247. def __init__(self, *args, **kwargs):
  3248. kwargs["sprite"] = life_force_sprite
  3249. super(LifeForce, self).__init__(*args, **kwargs)
  3250. def move(self):
  3251. if "set_direction" not in self.alarms:
  3252. self.alarms["set_direction"] = FPS / 4
  3253. target = self.get_nearest_player()
  3254. if target is not None:
  3255. xvec = target.x - self.image_xcenter
  3256. yvec = max(target.y, target.bbox_top) - self.image_ycenter
  3257. self.speed = LIFE_FORCE_SPEED
  3258. self.move_direction = math.degrees(math.atan2(yvec, xvec))
  3259. else:
  3260. self.speed = 0
  3261. def touch(self, other):
  3262. other.hp += LIFE_FORCE_HEAL
  3263. play_sound(heal_sound, other.x, other.y)
  3264. self.destroy()
  3265. class Bullet(InteractiveObject):
  3266. attacks_player = False
  3267. attacks_enemy = False
  3268. attacks_bullet = True
  3269. attacks_wall = True
  3270. breaks_stone = False
  3271. player_damage = 5
  3272. life = None
  3273. def dissipate(self, xdirection=0, ydirection=0):
  3274. """
  3275. Show the appropriate dissipation animation for the bullet, based
  3276. on what direction the bullet hit something in, and destroy the
  3277. bullet. Default behavior just calls "self.destroy()".
  3278. """
  3279. self.destroy()
  3280. def shoot_player(self, other):
  3281. other.hurt(self.player_damage)
  3282. def shoot_enemy(self, other):
  3283. other.shoot(self)
  3284. def event_create(self):
  3285. if self.life is not None:
  3286. self.alarms["die"] = self.life
  3287. def event_step(self, time_passed, delta_mult):
  3288. room = sge.game.current_room
  3289. if (self.bbox_right < 0 or self.bbox_left > room.width or
  3290. self.bbox_bottom < 0 or self.bbox_top > room.height):
  3291. self.destroy()
  3292. def event_collision(self, other, xdirection, ydirection):
  3293. super(Bullet, self).event_collision(other, xdirection, ydirection)
  3294. if isinstance(other, Player):
  3295. if self.attacks_player:
  3296. self.shoot_player(other)
  3297. self.dissipate(xdirection, ydirection)
  3298. elif isinstance(other, Bullet):
  3299. if (self.attacks_bullet and
  3300. ((self.attacks_player and other.attacks_enemy) or
  3301. (self.attacks_enemy and other.attacks_player))):
  3302. xd = math.copysign(1, other.xvelocity)
  3303. yd = math.copysign(1, other.yvelocity)
  3304. other.dissipate(xd, yd)
  3305. elif isinstance(other, InteractiveObject) and other.shootable:
  3306. if self.attacks_enemy:
  3307. self.shoot_enemy(other)
  3308. self.dissipate(xdirection, ydirection)
  3309. elif isinstance(other, xsge_physics.Wall) and self.attacks_wall:
  3310. point_x = self.x
  3311. point_y = self.y
  3312. if ((self.xvelocity > 0 and self.yvelocity > 0) or
  3313. (self.xvelocity < 0 and self.yvelocity < 0)):
  3314. collisions = sge.collision.line(
  3315. self.bbox_left, self.bbox_top, self.bbox_right,
  3316. self.bbox_bottom)
  3317. elif ((self.xvelocity > 0 and self.yvelocity < 0) or
  3318. (self.xvelocity < 0 and self.yvelocity > 0)):
  3319. collisions = sge.collision.line(
  3320. self.bbox_left, self.bbox_bottom, self.bbox_right,
  3321. self.bbox_top)
  3322. elif self.xvelocity:
  3323. collisions = sge.collision.rectangle(
  3324. self.bbox_left, self.y, self.bbox_width, 1)
  3325. elif self.yvelocity:
  3326. collisions = sge.collision.rectangle(
  3327. self.x, self.bbox_top, 1, self.bbox_height)
  3328. else:
  3329. warnings.warn("Bullet is not moving!")
  3330. collisions = []
  3331. if self.xvelocity:
  3332. if self.xvelocity > 0:
  3333. slope_cls = (xsge_physics.SlopeTopLeft,
  3334. xsge_physics.SlopeBottomLeft)
  3335. cls = (xsge_physics.SolidLeft,) + slope_cls
  3336. else:
  3337. slope_cls = (xsge_physics.SlopeTopRight,
  3338. xsge_physics.SlopeBottomRight)
  3339. cls = (xsge_physics.SolidRight,) + slope_cls
  3340. touching = False
  3341. if collisions:
  3342. for obj in collisions:
  3343. if isinstance(obj, cls):
  3344. if isinstance(obj, slope_cls):
  3345. if self.xvelocity > 0:
  3346. collision_real = (
  3347. point_x > obj.get_slope_x(point_y))
  3348. else:
  3349. collision_real = (
  3350. point_x < obj.get_slope_x(point_y))
  3351. else:
  3352. collision_real = True
  3353. if collision_real:
  3354. touching = True
  3355. if (self.breaks_stone and
  3356. isinstance(obj, Stone) and
  3357. obj.shootable):
  3358. obj.destroy()
  3359. if touching:
  3360. self.dissipate(xdirection, ydirection)
  3361. if self.yvelocity:
  3362. if self.yvelocity > 0:
  3363. slope_cls = (xsge_physics.SlopeTopLeft,
  3364. xsge_physics.SlopeTopRight)
  3365. cls = (xsge_physics.SolidTop,) + slope_cls
  3366. else:
  3367. slope_cls = (xsge_physics.SlopeBottomLeft,
  3368. xsge_physics.SlopeBottomRight)
  3369. cls = (xsge_physics.SolidBottom,) + slope_cls
  3370. touching = False
  3371. if collisions:
  3372. for obj in collisions:
  3373. if isinstance(obj, cls):
  3374. if isinstance(obj, slope_cls):
  3375. if self.yvelocity > 0:
  3376. collision_real = (
  3377. point_y > obj.get_slope_y(point_x))
  3378. else:
  3379. collision_real = (
  3380. point_y < obj.get_slope_y(point_x))
  3381. else:
  3382. collision_real = True
  3383. if collision_real:
  3384. touching = True
  3385. if (self.breaks_stone and
  3386. isinstance(obj, Stone) and
  3387. obj.shootable):
  3388. obj.destroy()
  3389. if touching:
  3390. self.dissipate(xdirection, ydirection)
  3391. def event_alarm(self, alarm_id):
  3392. if alarm_id == "die":
  3393. self.destroy()
  3394. class AnneroyBullet(Bullet):
  3395. attacks_enemy = True
  3396. attacks_bullet = False
  3397. breaks_stone = True
  3398. life = ANNEROY_BULLET_LIFE
  3399. def dissipate(self, xdirection=0, ydirection=0):
  3400. if self in sge.game.current_room.objects:
  3401. image_rotation = 0
  3402. if abs(xdirection) > abs(ydirection):
  3403. if xdirection < 0:
  3404. image_rotation = 180
  3405. else:
  3406. image_rotation = 0
  3407. elif abs(ydirection) > abs(xdirection):
  3408. if ydirection < 0:
  3409. image_rotation = 270
  3410. else:
  3411. image_rotation = 90
  3412. elif abs(self.xvelocity) > abs(self.yvelocity):
  3413. if self.xvelocity < 0:
  3414. image_rotation = 180
  3415. else:
  3416. image_rotation = 0
  3417. else:
  3418. if self.yvelocity < 0:
  3419. image_rotation = 270
  3420. else:
  3421. image_rotation = 90
  3422. play_sound(bullet_death_sound, self.x, self.y)
  3423. Smoke.create(
  3424. self.x, self.y, self.z, sprite=anneroy_bullet_dissipate_sprite,
  3425. regulate_origin=True, image_xscale=self.image_xscale,
  3426. image_yscale=self.image_yscale, image_rotation=image_rotation,
  3427. image_blend=self.image_blend)
  3428. self.destroy()
  3429. class ScorpionBullet(Bullet):
  3430. attacks_player = True
  3431. player_damage = 15
  3432. shard_num = 50
  3433. shard_speed_min = 1
  3434. shard_speed_max = 3
  3435. def dissipate(self, xdirection=0, ydirection=0):
  3436. self.destroy()
  3437. play_sound(scorpion_projectile_break_sound, self.image_xcenter,
  3438. self.image_ycenter)
  3439. for i in six.moves.range(self.shard_num):
  3440. life = random.uniform(FPS / 8, FPS / 2)
  3441. image_index = random.randrange(
  3442. 0, scorpion_projectile_shard_sprite.frames)
  3443. shard = xsge_particle.TimedParticle.create(
  3444. self.x, self.y, self.z, life=life,
  3445. sprite=scorpion_projectile_shard_sprite,
  3446. image_index=image_index)
  3447. shard.speed = random.randint(self.shard_speed_min,
  3448. self.shard_speed_max)
  3449. shard.move_direction = random.randrange(360)
  3450. class HedgehogSpikes(InteractiveObject):
  3451. spike_hitstun = FPS / 4
  3452. def event_collision(self, other, xdirection, ydirection):
  3453. super(HedgehogSpikes, self).event_collision(other, xdirection, ydirection)
  3454. if isinstance(other, InteractiveObject) and other.spikeable:
  3455. if "spike_hitstun" not in other.alarms:
  3456. other.spike(self)
  3457. other.alarms["spike_hitstun"] = self.spike_hitstun
  3458. elif isinstance(other, Stone) and other.spikeable:
  3459. other.destroy()
  3460. class FakeTile(sge.dsp.Object):
  3461. def event_create(self):
  3462. self.tangible = False
  3463. class Stone(xsge_physics.Solid):
  3464. shard_num_min = 1
  3465. shard_num_max = 4
  3466. shard_speed_min = 2
  3467. shard_speed_max = 5
  3468. shootable = False
  3469. spikeable = False
  3470. fakes = ()
  3471. def event_create(self):
  3472. self.checks_collisions = False
  3473. self.fakes = []
  3474. for other in sge.game.current_room.get_objects_at(
  3475. self.image_left, self.image_top, self.image_width,
  3476. self.image_height):
  3477. if (isinstance(other, FakeTile) and
  3478. self.image_left < other.image_right and
  3479. self.image_right > other.image_left and
  3480. self.image_top < other.image_bottom and
  3481. self.image_bottom > other.image_top):
  3482. self.fakes.append(other)
  3483. def event_destroy(self):
  3484. play_sound(stone_break_sound, self.image_xcenter, self.image_ycenter)
  3485. for other in self.fakes:
  3486. other.destroy()
  3487. if sge.game.fps_real >= FPS:
  3488. shard_num = random.randint(self.shard_num_min, self.shard_num_max)
  3489. else:
  3490. shard_num = self.shard_num_min
  3491. for i in six.moves.range(shard_num):
  3492. shard = Shard.create(self.x, self.y, self.z,
  3493. sprite=stone_fragment_sprite)
  3494. shard.speed = random.randint(self.shard_speed_min,
  3495. self.shard_speed_max)
  3496. shard.move_direction = random.randrange(360)
  3497. class WeakStone(Stone):
  3498. shootable = True
  3499. spikeable = True
  3500. class SpikeStone(Stone):
  3501. spikeable = True
  3502. class Macguffin(InteractiveObject):
  3503. def touch(self, other):
  3504. sge.snd.Music.clear_queue()
  3505. sge.snd.Music.stop()
  3506. play_sound(powerup_sound, self.image_xcenter, self.image_ycenter)
  3507. msg1 = _("MACGUFFIN\n\nThis is the end of the demo! Thank you for playing Hexoshi version {}.".format(__version__))
  3508. msg2 = _("Don't worry; the full game will not end this way. This is just a placeholder until the game is completed. I hope you enjoyed what you have seen so far, and I hope you enjoy the final game when it is finished! :)")
  3509. DialogBox(gui_handler, msg1, self.sprite).show()
  3510. DialogBox(gui_handler, msg2, self.sprite).show()
  3511. sge.game.current_room.win_game()
  3512. class Powerup(InteractiveObject):
  3513. message = _("USELESS ITEM\n\nIt doesn't seem to do anything")
  3514. def collect(self, other):
  3515. pass
  3516. def touch(self, other):
  3517. global powerups
  3518. global map_removed
  3519. play_sound(powerup_sound, self.image_xcenter, self.image_ycenter)
  3520. i = (self.__class__.__name__, sge.game.current_room.fname,
  3521. int(self.x), int(self.y))
  3522. powerups = powerups[:]
  3523. powerups.append(i)
  3524. # Remove the powerup from the map
  3525. px = get_xregion(self.image_xcenter)
  3526. py = get_yregion(self.image_ycenter)
  3527. map_removed.append(("powerup", sge.game.current_room.fname, px, py))
  3528. self.collect(other)
  3529. for obj in sge.game.current_room.objects:
  3530. if isinstance(obj, Player):
  3531. obj.update_hud()
  3532. DialogBox(gui_handler, self.message, self.sprite).show()
  3533. self.destroy()
  3534. def event_create(self):
  3535. i = (self.__class__.__name__, sge.game.current_room.fname,
  3536. int(self.x), int(self.y))
  3537. if i in powerups:
  3538. self.destroy()
  3539. class Artifact(Powerup):
  3540. message = ""
  3541. def __init__(self, x, y, message="It doesn't seem to to anything", **kwargs):
  3542. super(Artifact, self).__init__(x, y, **kwargs)
  3543. self.message = _("HEXOSHI ARTIFACT\n\n{}").format(_(message))
  3544. def collect(self, other):
  3545. global artifacts
  3546. artifacts += 1
  3547. # TODO: Check to see if this is the last artifact, and set up a
  3548. # victory condition if so.
  3549. class Etank(Powerup):
  3550. message = _("E-TANK\n\nExtra energy capacity acquired")
  3551. def collect(self, other):
  3552. global etanks
  3553. etanks += 1
  3554. other.refresh()
  3555. class LifeOrb(Powerup):
  3556. message = _("LIFE ORB\n\nAbsorb life force from defeated enemies")
  3557. def __init__(self, x, y, **kwargs):
  3558. kwargs["sprite"] = life_orb_sprite
  3559. super(LifeOrb, self).__init__(x, y, **kwargs)
  3560. def collect(self, other):
  3561. global progress_flags
  3562. progress_flags = progress_flags[:]
  3563. progress_flags.append("life_orb")
  3564. class Map(Powerup):
  3565. message = _(
  3566. 'HANDHELD MAP\n\nSee mini-map in HUD; see full map by pressing "map" button or from pause menu')
  3567. def __init__(self, x, y, **kwargs):
  3568. kwargs["sprite"] = powerup_map_sprite
  3569. super(Map, self).__init__(x, y, **kwargs)
  3570. def collect(self, other):
  3571. global progress_flags
  3572. progress_flags = progress_flags[:]
  3573. progress_flags.append("map")
  3574. class MapDisk(Powerup):
  3575. message = _("MAP DISK\n\nArea map data loaded")
  3576. def __init__(self, x, y, rooms=None, **kwargs):
  3577. if rooms:
  3578. self.rooms = rooms.split(',')
  3579. else:
  3580. self.rooms = []
  3581. super(MapDisk, self).__init__(x, y, **kwargs)
  3582. def collect(self, other):
  3583. global map_revealed
  3584. for fname in self.rooms:
  3585. sge.game.pump_input()
  3586. if fname in map_rooms:
  3587. room = Level.load(fname, True)
  3588. rm_x, rm_y = map_rooms[fname]
  3589. rm_w = int(math.ceil(room.width / SCREEN_SIZE[0]))
  3590. rm_h = int(math.ceil(room.height / SCREEN_SIZE[1]))
  3591. ignore_regions = set()
  3592. for obj in room.objects:
  3593. sge.game.pump_input()
  3594. if isinstance(obj, IgnoreRegion):
  3595. rx1 = rm_x + get_xregion(obj.bbox_left)
  3596. rx2 = rm_x + get_xregion(obj.bbox_right - 1)
  3597. ry1 = rm_y + get_yregion(obj.bbox_top)
  3598. ry2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  3599. for ry in six.moves.range(ry1, ry2 + 1):
  3600. for rx in six.moves.range(rx1, rx2 + 1):
  3601. ignore_regions.add((rx, ry))
  3602. for y in six.moves.range(rm_y, rm_y + rm_h):
  3603. for x in six.moves.range(rm_x, rm_x + rm_w):
  3604. sge.game.pump_input()
  3605. if ((x, y) not in ignore_regions and
  3606. (x, y) not in map_revealed):
  3607. map_revealed = map_revealed[:]
  3608. map_revealed.append((x, y))
  3609. sge.game.regulate_speed()
  3610. sge.game.pump_input()
  3611. sge.game.input_events = []
  3612. class AtomicCompressor(Powerup):
  3613. message = _('ATOMIC COMPRESSOR\n\nTo compress: press "down" while crouching, or select with "mode" and then press "shoot"\n\nTo uncompress: press "up"')
  3614. def __init__(self, x, y, **kwargs):
  3615. kwargs["sprite"] = atomic_compressor_sprite
  3616. super(AtomicCompressor, self).__init__(x, y, **kwargs)
  3617. def collect(self, other):
  3618. global progress_flags
  3619. progress_flags = progress_flags[:]
  3620. progress_flags.append("atomic_compressor")
  3621. class MonkeyBoots(Powerup):
  3622. message = _('MONKEY BOOTS\n\nPress "jump" while touching a wall to wall-jump')
  3623. def __init__(self, x, y, **kwargs):
  3624. kwargs["sprite"] = monkey_boots_sprite
  3625. super(MonkeyBoots, self).__init__(x, y, **kwargs)
  3626. self.emitter = None
  3627. def event_create(self):
  3628. self.emitter = xsge_particle.Emitter.create(
  3629. self.bbox_left, self.bbox_top, self.z + 0.5, interval=(FPS * 2),
  3630. particle_cls=xsge_particle.AnimationParticle,
  3631. particle_args=[self.x, self.y, self.z + 0.5],
  3632. particle_kwargs={"sprite": monkey_boots_gleam_sprite},
  3633. particle_lambda_args=[
  3634. lambda e: random.uniform(e.x, e.x + 13),
  3635. lambda e: random.uniform(e.y, e.y + 4)])
  3636. super(MonkeyBoots, self).event_create()
  3637. def collect(self, other):
  3638. global progress_flags
  3639. progress_flags = progress_flags[:]
  3640. progress_flags.append("monkey_boots")
  3641. def event_destroy(self):
  3642. if self.emitter is not None:
  3643. self.emitter.destroy()
  3644. self.emitter = None
  3645. class HedgehogHormone(Powerup):
  3646. message = _('HEDGEHOG HORMONE\n\nPress "shoot" while in the form of a ball to grow spikes')
  3647. def __init__(self, x, y, **kwargs):
  3648. kwargs["sprite"] = hedgehog_hormone_sprite
  3649. super(HedgehogHormone, self).__init__(x, y, **kwargs)
  3650. self.emitter = None
  3651. def event_create(self):
  3652. self.emitter = xsge_particle.Emitter.create(
  3653. self.image_xcenter, self.bbox_top + 1, self.z - 0.5, interval=8,
  3654. chance=0.5, particle_cls=xsge_particle.AnimationBubbleParticle,
  3655. particle_args=[self.image_xcenter, self.bbox_top + 1, self.z - 0.5],
  3656. particle_kwargs={"sprite": hedgehog_hormone_bubble_sprite,
  3657. "yvelocity": -0.25, "turn_factor": 20,
  3658. "min_angle": 225, "max_angle": 315})
  3659. super(HedgehogHormone, self).event_create()
  3660. def collect(self, other):
  3661. global progress_flags
  3662. progress_flags = progress_flags[:]
  3663. progress_flags.append("hedgehog_hormone")
  3664. def event_destroy(self):
  3665. if self.emitter is not None:
  3666. self.emitter.destroy()
  3667. self.emitter = None
  3668. class Tunnel(InteractiveObject):
  3669. def __init__(self, x, y, dest=None, **kwargs):
  3670. self.dest = dest
  3671. kwargs["visible"] = False
  3672. kwargs["checks_collisions"] = False
  3673. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3674. def touch(self, other):
  3675. global spawn_xoffset
  3676. global spawn_yoffset
  3677. spawn_xoffset = other.x - self.x
  3678. spawn_yoffset = other.y - self.y
  3679. if self.dest:
  3680. warp(self.dest)
  3681. class SpawnPoint(sge.dsp.Object):
  3682. def __init__(self, x, y, spawn_id=None, spawn_direction=None, barrier=None,
  3683. **kwargs):
  3684. self.spawn_id = spawn_id
  3685. self.spawn_direction = spawn_direction
  3686. self.barrier = barrier
  3687. kwargs["visible"] = False
  3688. kwargs["tangible"] = False
  3689. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3690. def spawn(self, other):
  3691. other.x = self.x + spawn_xoffset
  3692. other.y = self.y + spawn_yoffset
  3693. if self.spawn_direction == 0:
  3694. other.bbox_left = self.bbox_right
  3695. elif self.spawn_direction == 90:
  3696. other.bbox_top = self.bbox_bottom
  3697. elif self.spawn_direction == 180:
  3698. other.bbox_right = self.bbox_left
  3699. elif self.spawn_direction == 270:
  3700. other.bbox_bottom = self.bbox_top
  3701. other.yvelocity = get_jump_speed(TILE_SIZE / 2, other.gravity)
  3702. other.init_position()
  3703. def event_create(self):
  3704. if spawn_point == self.spawn_id:
  3705. for obj in sge.game.current_room.objects:
  3706. if isinstance(obj, Player):
  3707. self.spawn(obj)
  3708. if self.barrier is not None:
  3709. self.barrier.image_index = self.barrier.sprite.frames - 1
  3710. self.barrier.image_speed = -self.barrier.sprite.speed
  3711. play_sound(door_close_sound, self.barrier.image_xcenter,
  3712. self.barrier.image_ycenter)
  3713. class WarpPad(SpawnPoint):
  3714. message = _('WARP PAD\n\nProgress saved. Press "up" to teleport.')
  3715. def __init__(self, x, y, spawn_id="save", **kwargs):
  3716. self.spawn_id = spawn_id
  3717. self.spawn_direction = None
  3718. self.barrier = None
  3719. self.created = False
  3720. self.activated = False
  3721. kwargs["sprite"] = warp_pad_inactive_sprite
  3722. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3723. def activate(self):
  3724. global current_level
  3725. global spawn_point
  3726. global warp_pads
  3727. self.activated = True
  3728. self.sprite = warp_pad_active_sprite
  3729. current_level = sge.game.current_room.fname
  3730. spawn_point = self.spawn_id
  3731. x = get_xregion(self.image_xcenter)
  3732. y = get_yregion(self.image_ycenter)
  3733. i = (sge.game.current_room.fname, self.spawn_id, x, y)
  3734. if i not in warp_pads:
  3735. warp_pads = warp_pads[:]
  3736. warp_pads.append(i)
  3737. def spawn(self, other):
  3738. if not self.created:
  3739. self.create_children()
  3740. other.x = self.image_xcenter
  3741. other.bbox_bottom = self.image_top
  3742. other.z = self.z - 0.5
  3743. other.init_position()
  3744. other.warp_in()
  3745. other.refresh()
  3746. self.activate()
  3747. save_game()
  3748. play_sound(teleport_sound, self.image_xcenter, self.image_ycenter)
  3749. def teleport(self, other):
  3750. x = get_xregion(self.image_xcenter)
  3751. y = get_yregion(self.image_ycenter)
  3752. i = (sge.game.current_room.fname, self.spawn_id, x, y)
  3753. dlg = TeleportDialog(i)
  3754. dlg.show()
  3755. if dlg.selection and dlg.selection != i:
  3756. other.x = self.image_xcenter
  3757. other.bbox_bottom = self.image_top
  3758. other.warp_dest = "{}:{}".format(*dlg.selection[:2])
  3759. other.warp_out()
  3760. play_sound(teleport_sound, self.image_xcenter, self.image_ycenter)
  3761. def create_children(self):
  3762. self.created = True
  3763. self.bbox_x = 8
  3764. self.bbox_y = -4
  3765. self.bbox_width = 32
  3766. self.bbox_height = 4
  3767. Solid.create(self.x + 8, self.y, bbox_width=32, bbox_height=8)
  3768. SlopeTopLeft.create(self.x, self.y, bbox_width=8, bbox_height=8)
  3769. SlopeTopRight.create(self.x + 40, self.y, bbox_width=8, bbox_height=8)
  3770. def event_create(self):
  3771. super(WarpPad, self).event_create()
  3772. if not self.created:
  3773. self.create_children()
  3774. def event_collision(self, other, xdirection, ydirection):
  3775. global progress_flags
  3776. if isinstance(other, Player):
  3777. if xdirection or ydirection:
  3778. if not self.activated:
  3779. self.activate()
  3780. other.refresh()
  3781. play_sound(warp_pad_sound, self.image_xcenter,
  3782. self.image_ycenter)
  3783. if "warp" not in progress_flags:
  3784. progress_flags.append("warp")
  3785. DialogBox(gui_handler, self.message).show()
  3786. save_game()
  3787. class DoorBarrier(InteractiveObject, xsge_physics.Solid):
  3788. shootable = True
  3789. spikeable = True
  3790. parent = None
  3791. def shoot(self, other):
  3792. if self.parent is not None:
  3793. self.parent.shoot(other)
  3794. def event_animation_end(self):
  3795. self.image_speed = 0
  3796. if self.tangible:
  3797. self.image_index = 0
  3798. else:
  3799. self.image_index = self.sprite.frames - 1
  3800. class DoorFrame(InteractiveObject):
  3801. shootable = True
  3802. spikeable = True
  3803. closed_sprite = None
  3804. open_sprite = None
  3805. barrier_sprite = None
  3806. edge1_area = (0, 0, 8, 8)
  3807. edge2_area = (0, 56, 8, 8)
  3808. def __init__(self, x, y, **kwargs):
  3809. self.edge1 = None
  3810. self.edge2 = None
  3811. self.barrier = None
  3812. kwargs["tangible"] = False
  3813. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3814. def shoot(self, other):
  3815. if self.barrier is not None:
  3816. self.sprite = self.open_sprite
  3817. self.barrier.tangible = False
  3818. self.barrier.image_speed = self.barrier.sprite.speed
  3819. play_sound(door_open_sound, self.barrier.image_xcenter,
  3820. self.barrier.image_ycenter)
  3821. def event_create(self):
  3822. self.sprite = self.closed_sprite
  3823. x, y, w, h = self.edge1_area
  3824. self.edge1 = Solid.create(self.x + x, self.y + y, bbox_width=w,
  3825. bbox_height=h)
  3826. x, y, w, h = self.edge2_area
  3827. self.edge2 = Solid.create(self.x + x, self.y + y, bbox_width=w,
  3828. bbox_height=h)
  3829. self.barrier = DoorBarrier.create(self.x, self.y, self.z,
  3830. sprite=self.barrier_sprite,
  3831. image_index=0, image_fps=0)
  3832. self.barrier.parent = self
  3833. class DoorFrameX(DoorFrame):
  3834. def event_create(self):
  3835. self.closed_sprite = doorframe_regular_x_closed_sprite
  3836. self.open_sprite = doorframe_regular_x_open_sprite
  3837. self.barrier_sprite = door_barrier_x_sprite
  3838. super(DoorFrameX, self).event_create()
  3839. class DoorFrameY(DoorFrame):
  3840. edge2_area = (56, 0, 8, 8)
  3841. def event_create(self):
  3842. self.closed_sprite = doorframe_regular_y_closed_sprite
  3843. self.open_sprite = doorframe_regular_y_open_sprite
  3844. self.barrier_sprite = door_barrier_y_sprite
  3845. super(DoorFrameY, self).event_create()
  3846. class Door(sge.dsp.Object):
  3847. def __init__(self, x, y, dest=None, spawn_id=None, **kwargs):
  3848. self.dest = dest
  3849. self.spawn_id = spawn_id
  3850. if not spawn_id and dest:
  3851. if ':' in dest:
  3852. level_f, spawn = dest.split(':', 1)
  3853. if level_f:
  3854. self.spawn_id = level_f
  3855. else:
  3856. self.spawn_id = dest
  3857. kwargs["visible"] = False
  3858. kwargs["tangible"] = False
  3859. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3860. def event_create(self):
  3861. self.destroy()
  3862. class LeftDoor(Door):
  3863. def event_create(self):
  3864. frame = DoorFrameX.create(self.x, self.y, z=self.z)
  3865. Tunnel.create(frame.barrier.bbox_left - TILE_SIZE,
  3866. frame.barrier.bbox_top, dest=self.dest,
  3867. bbox_width=TILE_SIZE,
  3868. bbox_height=frame.barrier.bbox_height)
  3869. SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
  3870. spawn_id=self.spawn_id, spawn_direction=0,
  3871. barrier=frame.barrier,
  3872. bbox_width=frame.barrier.bbox_width,
  3873. bbox_height=frame.barrier.bbox_height)
  3874. self.destroy()
  3875. class RightDoor(Door):
  3876. def event_create(self):
  3877. frame = DoorFrameX.create(self.x, self.y, z=self.z)
  3878. Tunnel.create(frame.barrier.bbox_right, frame.barrier.bbox_top,
  3879. dest=self.dest, bbox_width=TILE_SIZE,
  3880. bbox_height=frame.barrier.bbox_height)
  3881. SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
  3882. spawn_id=self.spawn_id, spawn_direction=180,
  3883. barrier=frame.barrier,
  3884. bbox_width=frame.barrier.bbox_width,
  3885. bbox_height=frame.barrier.bbox_height)
  3886. self.destroy()
  3887. class UpDoor(Door):
  3888. def event_create(self):
  3889. frame = DoorFrameY.create(self.x, self.y, z=self.z)
  3890. Tunnel.create(frame.barrier.bbox_left,
  3891. frame.barrier.bbox_top - TILE_SIZE, dest=self.dest,
  3892. bbox_width=frame.barrier.bbox_width,
  3893. bbox_height=TILE_SIZE)
  3894. SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
  3895. spawn_id=self.spawn_id, spawn_direction=90,
  3896. barrier=frame.barrier,
  3897. bbox_width=frame.barrier.bbox_width,
  3898. bbox_height=frame.barrier.bbox_height)
  3899. self.destroy()
  3900. class DownDoor(Door):
  3901. def event_create(self):
  3902. frame = DoorFrameY.create(self.x, self.y, z=self.z)
  3903. Tunnel.create(frame.barrier.bbox_left, frame.barrier.bbox_bottom,
  3904. dest=self.dest, bbox_width=frame.barrier.bbox_width,
  3905. bbox_height=TILE_SIZE)
  3906. SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
  3907. spawn_id=self.spawn_id, spawn_direction=270,
  3908. barrier=frame.barrier,
  3909. bbox_width=frame.barrier.bbox_width,
  3910. bbox_height=frame.barrier.bbox_height)
  3911. self.destroy()
  3912. class TimelineSwitcher(InteractiveObject):
  3913. def __init__(self, x, y, timeline=None, **kwargs):
  3914. self.timeline = timeline
  3915. kwargs["visible"] = False
  3916. kwargs["checks_collisions"] = False
  3917. sge.dsp.Object.__init__(self, x, y, **kwargs)
  3918. def touch(self, other):
  3919. sge.game.current_room.load_timeline(self.timeline)
  3920. self.destroy()
  3921. class MovingObjectPath(xsge_path.PathLink):
  3922. cls = None
  3923. default_speed = PLAYER_MAX_SPEED
  3924. default_accel = None
  3925. default_decel = None
  3926. default_loop = None
  3927. auto_follow = True
  3928. def __init__(self, x, y, path_speed=None, path_accel=None, path_decel=None,
  3929. path_loop=None, path_id=None, prime=False, parent=None,
  3930. **kwargs):
  3931. if path_speed is None:
  3932. path_speed = self.default_speed
  3933. if path_accel is None:
  3934. path_accel = self.default_accel
  3935. if path_decel is None:
  3936. path_decel = self.default_decel
  3937. if path_loop is None:
  3938. path_loop = self.default_loop
  3939. self.path_speed = path_speed
  3940. self.path_accel = path_accel if path_accel != -1 else None
  3941. self.path_decel = path_decel if path_decel != -1 else None
  3942. self.path_loop = path_loop if path_loop != -1 else None
  3943. self.path_id = path_id
  3944. self.prime = prime
  3945. self.parent = parent
  3946. self.obj = lambda: None
  3947. super(MovingObjectPath, self).__init__(x, y, **kwargs)
  3948. def event_create(self):
  3949. if self.parent is not None:
  3950. for obj in sge.game.current_room.objects:
  3951. if (isinstance(obj, self.__class__) and
  3952. obj.path_id == self.parent):
  3953. obj.next_path = self
  3954. obj.next_speed = self.path_speed
  3955. obj.next_accel = self.path_accel
  3956. obj.next_decel = self.path_decel
  3957. obj.next_loop = self.path_loop
  3958. break
  3959. else:
  3960. self.prime = True
  3961. if self.prime and self.cls in TYPES:
  3962. obj = TYPES[self.cls].create(self.x, self.y, z=self.z)
  3963. self.obj = weakref.ref(obj)
  3964. if self.auto_follow:
  3965. self.follow_start(obj, self.path_speed, accel=self.path_accel,
  3966. decel=self.path_decel, loop=self.path_loop)
  3967. class MovingPlatformPath(MovingObjectPath):
  3968. cls = "moving_platform"
  3969. default_speed = 3
  3970. default_accel = 0.02
  3971. default_decel = 0.02
  3972. def event_create(self):
  3973. super(MovingPlatformPath, self).event_create()
  3974. obj = self.obj()
  3975. if obj:
  3976. obj.path = self
  3977. def follow_start(self, obj, *args, **kwargs):
  3978. super(MovingPlatformPath, self).follow_start(obj, *args, **kwargs)
  3979. obj.following = True
  3980. def event_follow_end(self, obj):
  3981. obj.following = False
  3982. obj.speed = 0
  3983. obj.x = self.x + self.points[-1][0]
  3984. obj.y = self.y + self.points[-1][1]
  3985. class TriggeredMovingPlatformPath(MovingPlatformPath):
  3986. default_speed = 2
  3987. default_accel = None
  3988. default_decel = None
  3989. auto_follow = False
  3990. followed = False
  3991. class CircoflamePath(xsge_path.Path):
  3992. def __init__(self, x, y, z=0, points=(), rvelocity=2):
  3993. self.rvelocity = rvelocity
  3994. x += TILE_SIZE / 2
  3995. y += TILE_SIZE / 2
  3996. super(CircoflamePath, self).__init__(x, y, z=z, points=points)
  3997. def event_create(self):
  3998. if self.points:
  3999. fx, fy = self.points[0]
  4000. radius = math.hypot(fx, fy)
  4001. pos = math.degrees(math.atan2(fy, fx))
  4002. CircoflameCenter.create(self.x, self.y, z=self.z, radius=radius,
  4003. pos=pos, rvelocity=self.rvelocity)
  4004. self.destroy()
  4005. class PlayerLayer(sge.dsp.Object):
  4006. def event_create(self):
  4007. sge.game.current_room.player_z = self.z
  4008. for obj in sge.game.current_room.objects:
  4009. if isinstance(obj, Player):
  4010. obj.z = self.z
  4011. self.destroy()
  4012. class CameraGuide(sge.dsp.Object):
  4013. def __init__(self, x, y, **kwargs):
  4014. kwargs.setdefault("visible", False)
  4015. kwargs.setdefault("checks_collisions", False)
  4016. super(CameraGuide, self).__init__(x, y, **kwargs)
  4017. class CameraXGuide(CameraGuide):
  4018. pass
  4019. class CameraYGuide(CameraGuide):
  4020. pass
  4021. class MapHint(sge.dsp.Object):
  4022. def event_create(self):
  4023. self.alarms["destroy"] = 1
  4024. def event_alarm(self, alarm_id):
  4025. if alarm_id == "destroy":
  4026. self.destroy()
  4027. class MapLeftWall(MapHint):
  4028. pass
  4029. class MapRightWall(MapHint):
  4030. pass
  4031. class MapTopWall(MapHint):
  4032. pass
  4033. class MapBottomWall(MapHint):
  4034. pass
  4035. class MapLeftDoor(MapHint):
  4036. pass
  4037. class MapRightDoor(MapHint):
  4038. pass
  4039. class MapTopDoor(MapHint):
  4040. pass
  4041. class MapBottomDoor(MapHint):
  4042. pass
  4043. class IgnoreRegion(MapHint):
  4044. pass
  4045. class Menu(xsge_gui.MenuWindow):
  4046. items = []
  4047. @classmethod
  4048. def create(cls, default=0):
  4049. if cls.items:
  4050. self = cls.from_text(
  4051. gui_handler, sge.game.width / 2, sge.game.height * 2 / 3,
  4052. cls.items, font_normal=font,
  4053. color_normal=menu_text_color,
  4054. color_selected=menu_text_selected_color,
  4055. background_color=menu_color, margin=4, halign="center",
  4056. valign="middle")
  4057. default %= len(self.widgets)
  4058. self.keyboard_focused_widget = self.widgets[default]
  4059. self.show()
  4060. return self
  4061. def event_change_keyboard_focus(self):
  4062. play_sound(select_sound)
  4063. class MainMenu(Menu):
  4064. items = [_("New Game"), _("Load Game"), _("Options"), _("Credits"), _("Quit")]
  4065. def event_choose(self):
  4066. if self.choice == 0:
  4067. play_sound(confirm_sound)
  4068. NewGameMenu.create_page()
  4069. elif self.choice == 1:
  4070. play_sound(confirm_sound)
  4071. LoadGameMenu.create_page()
  4072. elif self.choice == 2:
  4073. play_sound(confirm_sound)
  4074. OptionsMenu.create_page()
  4075. elif self.choice == 3:
  4076. credits_room = CreditsScreen.load(os.path.join("special",
  4077. "credits.tmx"))
  4078. credits_room.start()
  4079. else:
  4080. sge.game.end()
  4081. class NewGameMenu(Menu):
  4082. @classmethod
  4083. def create_page(cls, default=0):
  4084. cls.items = []
  4085. for slot in save_slots:
  4086. if slot is None:
  4087. cls.items.append(_("-Empty-"))
  4088. else:
  4089. name = slot.get("player_name", "Anneroy")
  4090. etanks = slot.get("etanks", 0)
  4091. if etanks:
  4092. text = "{} {}".format(name, ETANK_CHAR * etanks)
  4093. else:
  4094. text = name
  4095. cls.items.append(text)
  4096. cls.items.append(_("Back"))
  4097. return cls.create(default)
  4098. def event_choose(self):
  4099. global abort
  4100. global current_save_slot
  4101. abort = False
  4102. if self.choice in six.moves.range(len(save_slots)):
  4103. play_sound(confirm_sound)
  4104. current_save_slot = self.choice
  4105. if save_slots[current_save_slot] is None:
  4106. set_new_game()
  4107. if not abort:
  4108. start_game()
  4109. else:
  4110. NewGameMenu.create(default=self.choice)
  4111. else:
  4112. OverwriteConfirmMenu.create(default=1)
  4113. else:
  4114. play_sound(cancel_sound)
  4115. MainMenu.create(default=0)
  4116. class OverwriteConfirmMenu(Menu):
  4117. items = [_("Overwrite this save file"), _("Cancel")]
  4118. def event_choose(self):
  4119. global abort
  4120. abort = False
  4121. if self.choice == 0:
  4122. play_sound(confirm_sound)
  4123. set_new_game()
  4124. if not abort:
  4125. start_game()
  4126. else:
  4127. play_sound(cancel_sound)
  4128. NewGameMenu.create(default=current_save_slot)
  4129. else:
  4130. play_sound(cancel_sound)
  4131. NewGameMenu.create(default=current_save_slot)
  4132. class LoadGameMenu(NewGameMenu):
  4133. def event_choose(self):
  4134. global abort
  4135. global current_save_slot
  4136. abort = False
  4137. if self.choice in six.moves.range(len(save_slots)):
  4138. play_sound(confirm_sound)
  4139. current_save_slot = self.choice
  4140. load_game()
  4141. if abort:
  4142. MainMenu.create(default=1)
  4143. elif not start_game():
  4144. play_sound(error_sound)
  4145. show_error(_("An error occurred when trying to load the game."))
  4146. MainMenu.create(default=1)
  4147. else:
  4148. play_sound(cancel_sound)
  4149. MainMenu.create(default=1)
  4150. class OptionsMenu(Menu):
  4151. @classmethod
  4152. def create_page(cls, default=0):
  4153. smt = scale_method if scale_method else "fastest"
  4154. cls.items = [
  4155. _("Fullscreen: {}").format(_("On") if fullscreen else _("Off")),
  4156. _("Scale Method: {}").format(smt),
  4157. _("Sound: {}").format(_("On") if sound_enabled else _("Off")),
  4158. _("Music: {}").format(_("On") if music_enabled else _("Off")),
  4159. _("Stereo: {}").format(_("On") if stereo_enabled else _("Off")),
  4160. _("Show FPS: {}").format(_("On") if fps_enabled else _("Off")),
  4161. _("Metroid-Style Aiming: {}").format(_("On") if metroid_controls else _("Off")),
  4162. _("Joystick Threshold: {}%").format(int(joystick_threshold * 100)),
  4163. _("Configure keyboard"), _("Configure joysticks"),
  4164. _("Detect joysticks"), _("Back")]
  4165. return cls.create(default)
  4166. def event_choose(self):
  4167. global fullscreen
  4168. global scale_method
  4169. global sound_enabled
  4170. global music_enabled
  4171. global stereo_enabled
  4172. global fps_enabled
  4173. global metroid_controls
  4174. global joystick_threshold
  4175. if self.choice == 0:
  4176. play_sound(select_sound)
  4177. fullscreen = not fullscreen
  4178. update_fullscreen()
  4179. OptionsMenu.create_page(default=self.choice)
  4180. elif self.choice == 1:
  4181. choices = [None, "noblur", "smooth"] + sge.SCALE_METHODS
  4182. if scale_method in choices:
  4183. i = choices.index(scale_method)
  4184. else:
  4185. i = 0
  4186. play_sound(select_sound)
  4187. i += 1
  4188. i %= len(choices)
  4189. scale_method = choices[i]
  4190. sge.game.scale_method = scale_method
  4191. OptionsMenu.create_page(default=self.choice)
  4192. elif self.choice == 2:
  4193. sound_enabled = not sound_enabled
  4194. play_sound(teleport_sound)
  4195. OptionsMenu.create_page(default=self.choice)
  4196. elif self.choice == 3:
  4197. music_enabled = not music_enabled
  4198. play_music(sge.game.current_room.music,
  4199. noloop=sge.game.current_room.music_noloop)
  4200. OptionsMenu.create_page(default=self.choice)
  4201. elif self.choice == 4:
  4202. stereo_enabled = not stereo_enabled
  4203. play_sound(confirm_sound)
  4204. OptionsMenu.create_page(default=self.choice)
  4205. elif self.choice == 5:
  4206. play_sound(select_sound)
  4207. fps_enabled = not fps_enabled
  4208. OptionsMenu.create_page(default=self.choice)
  4209. elif self.choice == 6:
  4210. play_sound(select_sound)
  4211. metroid_controls = not metroid_controls
  4212. OptionsMenu.create_page(default=self.choice)
  4213. elif self.choice == 7:
  4214. play_sound(select_sound)
  4215. # This somewhat complicated method is to prevent rounding
  4216. # irregularities.
  4217. threshold = ((int(joystick_threshold * 100) + 5) % 100) / 100
  4218. if not threshold:
  4219. threshold = 0.0001
  4220. joystick_threshold = threshold
  4221. xsge_gui.joystick_threshold = threshold
  4222. OptionsMenu.create_page(default=self.choice)
  4223. elif self.choice == 8:
  4224. play_sound(confirm_sound)
  4225. KeyboardMenu.create_page()
  4226. elif self.choice == 9:
  4227. play_sound(confirm_sound)
  4228. JoystickMenu.create_page()
  4229. elif self.choice == 10:
  4230. sge.joystick.refresh()
  4231. play_sound(heal_sound)
  4232. OptionsMenu.create_page(default=self.choice)
  4233. else:
  4234. play_sound(cancel_sound)
  4235. write_to_disk()
  4236. MainMenu.create(default=2)
  4237. class KeyboardMenu(Menu):
  4238. page = 0
  4239. @classmethod
  4240. def create_page(cls, default=0, page=0):
  4241. page %= min(len(left_key), len(right_key), len(up_key), len(down_key),
  4242. len(jump_key), len(shoot_key), len(aim_diag_key),
  4243. len(aim_up_key), len(aim_down_key), len(mode_reset_key),
  4244. len(mode_key), len(pause_key), len(map_key))
  4245. def format_key(key):
  4246. if key:
  4247. return " ".join(key)
  4248. else:
  4249. return None
  4250. cls.items = [_("Player {}").format(page + 1),
  4251. _("Left: {}").format(format_key(left_key[page])),
  4252. _("Right: {}").format(format_key(right_key[page])),
  4253. _("Up: {}").format(format_key(up_key[page])),
  4254. _("Down: {}").format(format_key(down_key[page])),
  4255. _("Jump: {}").format(format_key(jump_key[page])),
  4256. _("Shoot: {}").format(format_key(shoot_key[page])),
  4257. _("Aim Diagonal: {}").format(format_key(aim_diag_key[page])),
  4258. _("Aim Up: {}").format(format_key(aim_up_key[page])),
  4259. _("Aim Down: {}").format(format_key(aim_down_key[page])),
  4260. _("Reset Mode: {}").format(format_key(mode_reset_key[page])),
  4261. _("Mode: {}").format(format_key(mode_key[page])),
  4262. _("Pause: {}").format(format_key(pause_key[page])),
  4263. _("Map: {}").format(format_key(map_key[page])),
  4264. _("Back")]
  4265. self = cls.create(default)
  4266. self.page = page
  4267. return self
  4268. def event_choose(self):
  4269. def bind_key(key, new_key, self=self):
  4270. for other_key in [
  4271. left_key[self.page], right_key[self.page],
  4272. up_key[self.page], down_key[self.page],
  4273. jump_key[self.page], shoot_key[self.page],
  4274. aim_diag_key[self.page], aim_up_key[self.page],
  4275. aim_down_key[self.page], mode_reset_key[self.page],
  4276. mode_key[self.page], pause_key[self.page],
  4277. map_key[self.page]]:
  4278. if new_key in other_key:
  4279. other_key.remove(new_key)
  4280. key.append(new_key)
  4281. while len(key) > 2:
  4282. key.pop(0)
  4283. text = _("Press the key you wish to bind to this function, or the Escape key to cancel.")
  4284. if self.choice == 0:
  4285. play_sound(select_sound)
  4286. self.__class__.create_page(default=self.choice, page=(self.page + 1))
  4287. elif self.choice == 1:
  4288. k = wait_key(text)
  4289. if k is not None:
  4290. bind_key(left_key[self.page], k)
  4291. play_sound(confirm_sound)
  4292. else:
  4293. play_sound(cancel_sound)
  4294. self.__class__.create_page(default=self.choice, page=self.page)
  4295. elif self.choice == 2:
  4296. k = wait_key(text)
  4297. if k is not None:
  4298. bind_key(right_key[self.page], k)
  4299. play_sound(confirm_sound)
  4300. else:
  4301. play_sound(cancel_sound)
  4302. self.__class__.create_page(default=self.choice, page=self.page)
  4303. elif self.choice == 3:
  4304. k = wait_key(text)
  4305. if k is not None:
  4306. bind_key(up_key[self.page], k)
  4307. play_sound(confirm_sound)
  4308. else:
  4309. play_sound(cancel_sound)
  4310. self.__class__.create_page(default=self.choice, page=self.page)
  4311. elif self.choice == 4:
  4312. k = wait_key(text)
  4313. if k is not None:
  4314. bind_key(down_key[self.page], k)
  4315. play_sound(confirm_sound)
  4316. else:
  4317. play_sound(cancel_sound)
  4318. self.__class__.create_page(default=self.choice, page=self.page)
  4319. elif self.choice == 5:
  4320. k = wait_key(text)
  4321. if k is not None:
  4322. bind_key(jump_key[self.page], k)
  4323. play_sound(confirm_sound)
  4324. else:
  4325. play_sound(cancel_sound)
  4326. self.__class__.create_page(default=self.choice, page=self.page)
  4327. elif self.choice == 6:
  4328. k = wait_key(text)
  4329. if k is not None:
  4330. bind_key(shoot_key[self.page], k)
  4331. play_sound(confirm_sound)
  4332. else:
  4333. play_sound(cancel_sound)
  4334. self.__class__.create_page(default=self.choice, page=self.page)
  4335. elif self.choice == 7:
  4336. k = wait_key(text)
  4337. if k is not None:
  4338. bind_key(aim_diag_key[self.page], k)
  4339. play_sound(confirm_sound)
  4340. else:
  4341. play_sound(cancel_sound)
  4342. self.__class__.create_page(default=self.choice, page=self.page)
  4343. elif self.choice == 8:
  4344. k = wait_key(text)
  4345. if k is not None:
  4346. bind_key(aim_up_key[self.page], k)
  4347. play_sound(confirm_sound)
  4348. else:
  4349. play_sound(cancel_sound)
  4350. self.__class__.create_page(default=self.choice, page=self.page)
  4351. elif self.choice == 9:
  4352. k = wait_key(text)
  4353. if k is not None:
  4354. bind_key(aim_down_key[self.page], k)
  4355. play_sound(confirm_sound)
  4356. else:
  4357. play_sound(cancel_sound)
  4358. self.__class__.create_page(default=self.choice, page=self.page)
  4359. elif self.choice == 10:
  4360. k = wait_key(text)
  4361. if k is not None:
  4362. bind_key(mode_reset_key[self.page], k)
  4363. play_sound(confirm_sound)
  4364. else:
  4365. play_sound(cancel_sound)
  4366. self.__class__.create_page(default=self.choice, page=self.page)
  4367. elif self.choice == 11:
  4368. k = wait_key(text)
  4369. if k is not None:
  4370. bind_key(mode_key[self.page], k)
  4371. play_sound(confirm_sound)
  4372. else:
  4373. play_sound(cancel_sound)
  4374. self.__class__.create_page(default=self.choice, page=self.page)
  4375. elif self.choice == 12:
  4376. k = wait_key(text)
  4377. if k is not None:
  4378. bind_key(pause_key[self.page], k)
  4379. play_sound(confirm_sound)
  4380. else:
  4381. play_sound(cancel_sound)
  4382. self.__class__.create_page(default=self.choice, page=self.page)
  4383. elif self.choice == 13:
  4384. k = wait_key(text)
  4385. if k is not None:
  4386. bind_key(map_key[self.page], k)
  4387. play_sound(confirm_sound)
  4388. else:
  4389. play_sound(cancel_sound)
  4390. self.__class__.create_page(default=self.choice, page=self.page)
  4391. else:
  4392. play_sound(cancel_sound)
  4393. OptionsMenu.create_page(default=5)
  4394. class JoystickMenu(Menu):
  4395. page = 0
  4396. @classmethod
  4397. def create_page(cls, default=0, page=0):
  4398. page %= min(len(left_js), len(right_js), len(up_js), len(down_js),
  4399. len(jump_js), len(shoot_js), len(aim_diag_js),
  4400. len(aim_up_js), len(aim_down_js), len(mode_reset_js),
  4401. len(mode_js), len(pause_js), len(map_js))
  4402. def format_js(js):
  4403. js_template = "{},{},{}"
  4404. sL = []
  4405. for j in js:
  4406. sL.append(js_template.format(*j))
  4407. if sL:
  4408. return " ".join(sL)
  4409. else:
  4410. return _("None")
  4411. cls.items = [_("Player {}").format(page + 1),
  4412. _("Left: {}").format(format_js(left_js[page])),
  4413. _("Right: {}").format(format_js(right_js[page])),
  4414. _("Up: {}").format(format_js(up_js[page])),
  4415. _("Down: {}").format(format_js(down_js[page])),
  4416. _("Jump: {}").format(format_js(jump_js[page])),
  4417. _("Shoot: {}").format(format_js(shoot_js[page])),
  4418. _("Aim Diagonal: {}").format(format_js(aim_diag_js[page])),
  4419. _("Aim Up: {}").format(format_js(aim_up_js[page])),
  4420. _("Aim Down: {}").format(format_js(aim_down_js[page])),
  4421. _("Reset Mode: {}").format(format_js(mode_reset_js[page])),
  4422. _("Mode: {}").format(format_js(mode_js[page])),
  4423. _("Pause: {}").format(format_js(pause_js[page])),
  4424. _("Map: {}").format(format_js(map_js[page])),
  4425. _("Back")]
  4426. self = cls.create(default)
  4427. self.page = page
  4428. return self
  4429. def event_choose(self):
  4430. def bind_js(js, new_js, self=self):
  4431. for other_js in [
  4432. left_js[self.page], right_js[self.page],
  4433. up_js[self.page], down_js[self.page],
  4434. jump_js[self.page], shoot_js[self.page],
  4435. aim_diag_js[self.page], aim_up_js[self.page],
  4436. aim_down_js[self.page], mode_reset_js[self.page],
  4437. mode_js[self.page], pause_js[self.page],
  4438. map_js[self.page]]:
  4439. if new_js in other_js:
  4440. other_js.remove(new_js)
  4441. js.append(new_js)
  4442. while len(js) > 2:
  4443. js.pop(0)
  4444. text = _("Press the joystick button, axis, or hat direction you wish to bind to this function, or the Escape key to cancel.")
  4445. if self.choice == 0:
  4446. play_sound(select_sound)
  4447. self.__class__.create_page(default=self.choice, page=(self.page + 1))
  4448. elif self.choice == 1:
  4449. js = wait_js(text)
  4450. if js is not None:
  4451. bind_js(left_js[self.page], js)
  4452. play_sound(confirm_sound)
  4453. else:
  4454. play_sound(cancel_sound)
  4455. self.__class__.create_page(default=self.choice, page=self.page)
  4456. elif self.choice == 2:
  4457. js = wait_js(text)
  4458. if js is not None:
  4459. bind_js(right_js[self.page], js)
  4460. play_sound(confirm_sound)
  4461. else:
  4462. play_sound(cancel_sound)
  4463. self.__class__.create_page(default=self.choice, page=self.page)
  4464. elif self.choice == 3:
  4465. js = wait_js(text)
  4466. if js is not None:
  4467. bind_js(up_js[self.page], js)
  4468. play_sound(confirm_sound)
  4469. else:
  4470. play_sound(cancel_sound)
  4471. self.__class__.create_page(default=self.choice, page=self.page)
  4472. elif self.choice == 4:
  4473. js = wait_js(text)
  4474. if js is not None:
  4475. bind_js(down_js[self.page], js)
  4476. play_sound(confirm_sound)
  4477. else:
  4478. play_sound(cancel_sound)
  4479. self.__class__.create_page(default=self.choice, page=self.page)
  4480. elif self.choice == 5:
  4481. js = wait_js(text)
  4482. if js is not None:
  4483. bind_js(jump_js[self.page], js)
  4484. play_sound(confirm_sound)
  4485. else:
  4486. play_sound(cancel_sound)
  4487. self.__class__.create_page(default=self.choice, page=self.page)
  4488. elif self.choice == 6:
  4489. js = wait_js(text)
  4490. if js is not None:
  4491. bind_js(shoot_js[self.page], js)
  4492. play_sound(confirm_sound)
  4493. else:
  4494. play_sound(cancel_sound)
  4495. self.__class__.create_page(default=self.choice, page=self.page)
  4496. elif self.choice == 7:
  4497. js = wait_js(text)
  4498. if js is not None:
  4499. bind_js(aim_diag_js[self.page], js)
  4500. play_sound(confirm_sound)
  4501. else:
  4502. play_sound(cancel_sound)
  4503. self.__class__.create_page(default=self.choice, page=self.page)
  4504. elif self.choice == 8:
  4505. js = wait_js(text)
  4506. if js is not None:
  4507. bind_js(aim_up_js[self.page], js)
  4508. play_sound(confirm_sound)
  4509. else:
  4510. play_sound(cancel_sound)
  4511. self.__class__.create_page(default=self.choice, page=self.page)
  4512. elif self.choice == 9:
  4513. js = wait_js(text)
  4514. if js is not None:
  4515. bind_js(aim_down_js[self.page], js)
  4516. play_sound(confirm_sound)
  4517. else:
  4518. play_sound(cancel_sound)
  4519. self.__class__.create_page(default=self.choice, page=self.page)
  4520. elif self.choice == 10:
  4521. js = wait_js(text)
  4522. if js is not None:
  4523. bind_js(mode_reset_js[self.page], js)
  4524. play_sound(confirm_sound)
  4525. else:
  4526. play_sound(cancel_sound)
  4527. self.__class__.create_page(default=self.choice, page=self.page)
  4528. elif self.choice == 11:
  4529. js = wait_js(text)
  4530. if js is not None:
  4531. bind_js(mode_js[self.page], js)
  4532. play_sound(confirm_sound)
  4533. else:
  4534. play_sound(cancel_sound)
  4535. self.__class__.create_page(default=self.choice, page=self.page)
  4536. elif self.choice == 12:
  4537. js = wait_js(text)
  4538. if js is not None:
  4539. bind_js(pause_js[self.page], js)
  4540. play_sound(confirm_sound)
  4541. else:
  4542. play_sound(cancel_sound)
  4543. self.__class__.create_page(default=self.choice, page=self.page)
  4544. elif self.choice == 13:
  4545. js = wait_js(text)
  4546. if js is not None:
  4547. bind_js(map_js[self.page], js)
  4548. play_sound(confirm_sound)
  4549. else:
  4550. play_sound(cancel_sound)
  4551. self.__class__.create_page(default=self.choice, page=self.page)
  4552. else:
  4553. play_sound(cancel_sound)
  4554. OptionsMenu.create_page(default=6)
  4555. class ModalMenu(xsge_gui.MenuDialog):
  4556. items = []
  4557. @classmethod
  4558. def create(cls, default=0):
  4559. if cls.items:
  4560. self = cls.from_text(
  4561. gui_handler, sge.game.width / 2, sge.game.height / 2,
  4562. cls.items, font_normal=font,
  4563. color_normal=menu_text_color,
  4564. color_selected=menu_text_selected_color,
  4565. background_color=menu_color, margin=9, halign="center",
  4566. valign="middle")
  4567. default %= len(self.widgets)
  4568. self.keyboard_focused_widget = self.widgets[default]
  4569. sge.game.refresh()
  4570. self.show()
  4571. return self
  4572. def event_change_keyboard_focus(self):
  4573. play_sound(select_sound)
  4574. class PauseMenu(ModalMenu):
  4575. @classmethod
  4576. def create(cls, default=0, player_x=None, player_y=None):
  4577. if "map" in progress_flags:
  4578. items = [_("Continue"), _("View Stats"), _("Configure keyboard"),
  4579. _("Configure joysticks"), _("View Map"),
  4580. _("Return to Title Screen")]
  4581. else:
  4582. items = [_("Continue"), _("View Stats"), _("Configure keyboard"),
  4583. _("Configure joysticks"), _("Return to Title Screen")]
  4584. self = cls.from_text(
  4585. gui_handler, sge.game.width / 2, sge.game.height / 2,
  4586. items, font_normal=font, color_normal=menu_text_color,
  4587. color_selected=menu_text_selected_color,
  4588. background_color=menu_color, margin=9, halign="center",
  4589. valign="middle")
  4590. default %= len(self.widgets)
  4591. self.keyboard_focused_widget = self.widgets[default]
  4592. self.player_x = player_x
  4593. self.player_y = player_y
  4594. sge.game.refresh()
  4595. self.show()
  4596. return self
  4597. def event_choose(self):
  4598. self.hide()
  4599. sge.game.refresh()
  4600. def check_quit():
  4601. if current_save_slot is not None:
  4602. slot = save_slots[current_save_slot]
  4603. else:
  4604. slot = {}
  4605. if (slot.get("map_revealed") == map_revealed and
  4606. slot.get("map_explored") == map_explored and
  4607. slot.get("map_removed") == map_removed and
  4608. slot.get("warp_pads") == warp_pads and
  4609. slot.get("powerups") == powerups and
  4610. slot.get("progress_flags") == progress_flags and
  4611. slot.get("artifacts") == artifacts and
  4612. slot.get("etanks") == etanks):
  4613. sge.game.start_room.start()
  4614. else:
  4615. text = _("Some progress has not been saved. If you leave the game now, this unsaved progress will be lost. You can save the game by touching any warp pad.")
  4616. DialogBox(gui_handler, text).show()
  4617. play_sound(type_sound)
  4618. LoseProgressMenu.create(1)
  4619. if self.choice == 1:
  4620. seconds = int(time_taken % 60)
  4621. minutes = int((time_taken / 60) % 60)
  4622. hours = int(time_taken / 3600)
  4623. powerups_col = len(powerups) - artifacts
  4624. text = _("PLAYER STATISTICS\n\nTime spent: {hours}:{minutes:02}:{seconds:02}\nPowerups collected: {powerups} ({powerups_percent}%)\nArtifacts collected: {artifacts} ({artifacts_percent}%)").format(
  4625. hours=hours, minutes=minutes, seconds=seconds,
  4626. powerups=powerups_col,
  4627. powerups_percent=int(100 * powerups_col / max(num_powerups, 1)),
  4628. artifacts=artifacts,
  4629. artifacts_percent=int(100 * artifacts / max(num_artifacts, 1)))
  4630. DialogBox(gui_handler, text).show()
  4631. PauseMenu.create(default=self.choice, player_x=self.player_x,
  4632. player_y=self.player_y)
  4633. elif self.choice == 2:
  4634. play_sound(confirm_sound)
  4635. ModalKeyboardMenu.create_page()
  4636. PauseMenu.create(default=self.choice, player_x=self.player_x,
  4637. player_y=self.player_y)
  4638. elif self.choice == 3:
  4639. play_sound(confirm_sound)
  4640. ModalJoystickMenu.create_page()
  4641. PauseMenu.create(default=self.choice, player_x=self.player_x,
  4642. player_y=self.player_y)
  4643. elif self.choice == 4:
  4644. if "map" in progress_flags:
  4645. play_sound(select_sound)
  4646. MapDialog(self.player_x, self.player_y).show()
  4647. else:
  4648. check_quit()
  4649. elif self.choice == 5:
  4650. check_quit()
  4651. else:
  4652. play_sound(select_sound)
  4653. class ModalKeyboardMenu(ModalMenu, KeyboardMenu):
  4654. def event_choose(self):
  4655. self.hide()
  4656. sge.game.refresh()
  4657. if self.choice is not None and self.choice < len(self.items) - 1:
  4658. super(ModalKeyboardMenu, self).event_choose()
  4659. else:
  4660. play_sound(cancel_sound)
  4661. class ModalJoystickMenu(ModalMenu, JoystickMenu):
  4662. def event_choose(self):
  4663. self.hide()
  4664. sge.game.refresh()
  4665. if self.choice is not None and self.choice < len(self.items) - 1:
  4666. super(ModalJoystickMenu, self).event_choose()
  4667. else:
  4668. play_sound(cancel_sound)
  4669. class LoseProgressMenu(ModalMenu):
  4670. items = [_("Abandon unsaved progress"), _("Return to game")]
  4671. def event_choose(self):
  4672. if self.choice == 0:
  4673. sge.game.start_room.start()
  4674. else:
  4675. play_sound(select_sound)
  4676. class MapDialog(xsge_gui.Dialog):
  4677. def __init__(self, player_x, player_y):
  4678. if player_x is None:
  4679. player_x = 0
  4680. if player_y is None:
  4681. player_y = 0
  4682. xcells = int(sge.game.width / MAP_CELL_WIDTH)
  4683. ycells = int(sge.game.height / MAP_CELL_HEIGHT)
  4684. w = sge.game.width
  4685. h = sge.game.height
  4686. super(MapDialog, self).__init__(
  4687. gui_handler, 0, 0, w, h, background_color=sge.gfx.Color("black"),
  4688. border=False)
  4689. self.map = xsge_gui.Widget(self, 0, 0, 0)
  4690. self.map.sprite = draw_map(player_x=player_x, player_y=player_y)
  4691. self.map.tab_focus = False
  4692. self.left = 0
  4693. self.top = 0
  4694. for rx, ry in set(map_revealed + map_explored):
  4695. self.left = min(self.left, rx)
  4696. self.top = min(self.top, ry)
  4697. player_x -= self.left
  4698. player_y -= self.top
  4699. self.map.x = (xcells // 2 - player_x) * MAP_CELL_WIDTH
  4700. self.map.y = (ycells // 2 - player_y) * MAP_CELL_HEIGHT
  4701. def event_press_left(self):
  4702. play_sound(select_sound)
  4703. self.map.x += MAP_CELL_WIDTH
  4704. def event_press_right(self):
  4705. play_sound(select_sound)
  4706. self.map.x -= MAP_CELL_WIDTH
  4707. def event_press_up(self):
  4708. play_sound(select_sound)
  4709. self.map.y += MAP_CELL_HEIGHT
  4710. def event_press_down(self):
  4711. play_sound(select_sound)
  4712. self.map.y -= MAP_CELL_HEIGHT
  4713. def event_press_enter(self):
  4714. play_sound(select_sound)
  4715. self.destroy()
  4716. def event_press_escape(self):
  4717. play_sound(select_sound)
  4718. self.destroy()
  4719. class TeleportDialog(MapDialog):
  4720. def __init__(self, selection):
  4721. self.selection = selection
  4722. w = sge.game.width
  4723. h = sge.game.height
  4724. super(MapDialog, self).__init__(
  4725. gui_handler, 0, 0, w, h, background_color=sge.gfx.Color("black"),
  4726. border=False)
  4727. self.map = xsge_gui.Widget(self, 0, 0, 0)
  4728. self.map.sprite = draw_map()
  4729. self.map.tab_focus = False
  4730. self.location_indicator = xsge_gui.Widget(self, 0, 0, 1)
  4731. self.location_indicator.sprite = map_player_sprite
  4732. self.location_indicator.tab_focus = False
  4733. self.left = 0
  4734. self.top = 0
  4735. for rx, ry in set(map_revealed + map_explored):
  4736. self.left = min(self.left, rx)
  4737. self.top = min(self.top, ry)
  4738. xcells = int(sge.game.width / MAP_CELL_WIDTH)
  4739. ycells = int(sge.game.height / MAP_CELL_HEIGHT)
  4740. self.location_indicator.x = (xcells // 2) * MAP_CELL_WIDTH
  4741. self.location_indicator.y = (ycells // 2) * MAP_CELL_HEIGHT
  4742. self.update_selection()
  4743. def update_selection(self):
  4744. if self.selection[0] in map_rooms:
  4745. xcells = int(sge.game.width / MAP_CELL_WIDTH)
  4746. ycells = int(sge.game.height / MAP_CELL_HEIGHT)
  4747. x, y = map_rooms[self.selection[0]]
  4748. x += self.selection[2] - self.left
  4749. y += self.selection[3] - self.top
  4750. self.map.x = (xcells // 2 - x) * MAP_CELL_WIDTH
  4751. self.map.y = (ycells // 2 - y) * MAP_CELL_HEIGHT
  4752. def event_press_left(self):
  4753. play_sound(select_sound)
  4754. if self.selection in warp_pads:
  4755. i = warp_pads.index(self.selection)
  4756. else:
  4757. i = 0
  4758. self.selection = warp_pads[(i - 1) % len(warp_pads)]
  4759. self.update_selection()
  4760. def event_press_right(self):
  4761. play_sound(select_sound)
  4762. if self.selection in warp_pads:
  4763. i = warp_pads.index(self.selection)
  4764. else:
  4765. i = -1
  4766. self.selection = warp_pads[(i + 1) % len(warp_pads)]
  4767. self.update_selection()
  4768. def event_press_up(self):
  4769. pass
  4770. def event_press_down(self):
  4771. pass
  4772. def event_press_enter(self):
  4773. self.destroy()
  4774. def event_press_escape(self):
  4775. self.selection = None
  4776. self.destroy()
  4777. class DialogLabel(xsge_gui.ProgressiveLabel):
  4778. def event_add_character(self):
  4779. if self.text[-1] not in (' ', '\n', '\t'):
  4780. play_sound(type_sound)
  4781. class DialogBox(xsge_gui.Dialog):
  4782. def __init__(self, parent, text, portrait=None, rate=TEXT_SPEED):
  4783. width = sge.game.width / 2
  4784. x_padding = 16
  4785. y_padding = 16
  4786. label_x = 8
  4787. label_y = 8
  4788. if portrait is not None:
  4789. x_padding += 8
  4790. label_x += 8
  4791. portrait_w = portrait.width
  4792. portrait_h = portrait.height
  4793. label_x += portrait_w
  4794. else:
  4795. portrait_w = 0
  4796. portrait_h = 0
  4797. label_w = max(1, width - portrait_w - x_padding)
  4798. height = max(1, portrait_h + y_padding,
  4799. font.get_height(text, width=label_w) + y_padding)
  4800. x = sge.game.width / 2 - width / 2
  4801. y = sge.game.height / 2 - height / 2
  4802. super(DialogBox, self).__init__(
  4803. parent, x, y, width, height,
  4804. background_color=menu_color, border=False)
  4805. label_h = max(1, height - y_padding)
  4806. self.label = DialogLabel(self, label_x, label_y, 0, text, font=font,
  4807. width=label_w, height=label_h,
  4808. color=sge.gfx.Color("white"), rate=rate)
  4809. if portrait is not None:
  4810. xsge_gui.Widget(self, 8, 8, 0, sprite=portrait)
  4811. def event_press_enter(self):
  4812. if len(self.label.text) < len(self.label.full_text):
  4813. self.label.text = self.label.full_text
  4814. else:
  4815. self.destroy()
  4816. def event_press_escape(self):
  4817. self.destroy()
  4818. room = sge.game.current_room
  4819. if (isinstance(room, Level) and
  4820. room.timeline_skip_target is not None and
  4821. room.timeline_step < room.timeline_skip_target):
  4822. room.timeline_skipto(room.timeline_skip_target)
  4823. def get_object(x, y, cls=None, **kwargs):
  4824. cls = TYPES.get(cls, xsge_tmx.Decoration)
  4825. return cls(x, y, **kwargs)
  4826. def get_scaled_copy(obj):
  4827. s = obj.sprite.copy()
  4828. if obj.image_xscale < 0:
  4829. s.mirror()
  4830. if obj.image_yscale < 0:
  4831. s.flip()
  4832. s.width *= abs(obj.image_xscale)
  4833. s.height *= abs(obj.image_yscale)
  4834. s.rotate(obj.image_rotation)
  4835. s.origin_x = obj.image_origin_x
  4836. s.origin_y = obj.image_origin_y
  4837. return s
  4838. def get_jump_speed(height, gravity=GRAVITY):
  4839. # Get the speed to achieve a given height using a kinematic
  4840. # equation: v[f]^2 = v[i]^2 + 2ad
  4841. return -math.sqrt(2 * gravity * height)
  4842. def get_xregion(x):
  4843. return int(x / SCREEN_SIZE[0])
  4844. def get_yregion(y):
  4845. return int(y / SCREEN_SIZE[1])
  4846. def warp(dest):
  4847. global spawn_point
  4848. if ":" in dest:
  4849. level_f, spawn_point = dest.split(':', 1)
  4850. else:
  4851. level_f = dest
  4852. spawn_point = sge.game.current_room.fname
  4853. if level_f:
  4854. level = sge.game.current_room.__class__.load(level_f)
  4855. else:
  4856. level = sge.game.current_room
  4857. if level is not None:
  4858. level.start()
  4859. else:
  4860. sge.game.start_room.start()
  4861. def set_gui_controls():
  4862. # Set the controls for xsge_gui.
  4863. xsge_gui.next_widget_keys = ["down", "tab", "s", "kp_2"]
  4864. xsge_gui.previous_widget_keys = ["up", "w", "kp_8"]
  4865. xsge_gui.left_keys = ["left", "a", "kp_4"]
  4866. xsge_gui.right_keys = ["right", "d", "kp_6"]
  4867. xsge_gui.up_keys = []
  4868. xsge_gui.down_keys = []
  4869. xsge_gui.enter_keys = ["enter", "kp_enter", "space", "end"]
  4870. xsge_gui.escape_keys = ["escape"]
  4871. xsge_gui.next_widget_joystick_events = [
  4872. (0, "axis+", 1), (0, "hat_down", 0)]
  4873. xsge_gui.previous_widget_joystick_events = [
  4874. (0, "axis-", 1), (0, "hat_up", 0)]
  4875. xsge_gui.left_joystick_events = [(0, "axis-", 0), (0, "hat_left", 0)]
  4876. xsge_gui.right_joystick_events = [(0, "axis+", 0), (0, "hat_right", 0)]
  4877. xsge_gui.up_joystick_events = []
  4878. xsge_gui.down_joystick_events = []
  4879. xsge_gui.enter_joystick_events = [
  4880. (0, "button", 0), (0, "button", 1), (0, "button", 2), (0, "button", 3),
  4881. (0, "button", 9)]
  4882. xsge_gui.escape_joystick_events = [(0, "button", 8)]
  4883. def wait_key(text):
  4884. # Wait for a key press and return it.
  4885. while True:
  4886. # Input events
  4887. sge.game.pump_input()
  4888. while sge.game.input_events:
  4889. event = sge.game.input_events.pop(0)
  4890. if isinstance(event, sge.input.KeyPress):
  4891. sge.game.pump_input()
  4892. sge.game.input_events = []
  4893. if event.key == "escape":
  4894. return None
  4895. else:
  4896. return event.key
  4897. # Regulate speed
  4898. sge.game.regulate_speed(fps=10)
  4899. # Project text
  4900. sge.game.project_text(font, text, sge.game.width / 2,
  4901. sge.game.height / 2, width=sge.game.width,
  4902. height=sge.game.height,
  4903. color=sge.gfx.Color("white"),
  4904. halign="center", valign="middle")
  4905. # Refresh
  4906. sge.game.refresh()
  4907. def wait_js(text):
  4908. # Wait for a joystick press and return it.
  4909. sge.game.pump_input()
  4910. sge.game.input_events = []
  4911. while True:
  4912. # Input events
  4913. sge.game.pump_input()
  4914. while sge.game.input_events:
  4915. event = sge.game.input_events.pop(0)
  4916. if isinstance(event, sge.input.KeyPress):
  4917. if event.key == "escape":
  4918. sge.game.pump_input()
  4919. sge.game.input_events = []
  4920. return None
  4921. elif isinstance(event, sge.input.JoystickEvent):
  4922. if (event.input_type not in {"axis0", "hat_center_x",
  4923. "hat_center_y"} and
  4924. event.value >= joystick_threshold):
  4925. sge.game.pump_input()
  4926. sge.game.input_events = []
  4927. return (event.js_id, event.input_type, event.input_id)
  4928. # Regulate speed
  4929. sge.game.regulate_speed(fps=10)
  4930. # Project text
  4931. sge.game.project_text(font, text, sge.game.width / 2,
  4932. sge.game.height / 2, width=sge.game.width,
  4933. height=sge.game.height,
  4934. color=sge.gfx.Color("white"),
  4935. halign="center", valign="middle")
  4936. # Refresh
  4937. sge.game.refresh()
  4938. def show_error(message):
  4939. print(message)
  4940. raise
  4941. def play_sound(sound, x=None, y=None, force=True):
  4942. if sound_enabled and sound:
  4943. if x is None or y is None:
  4944. sound.play(force=force)
  4945. else:
  4946. current_view = None
  4947. view_x = 0
  4948. view_y = 0
  4949. dist = 0
  4950. for view in sge.game.current_room.views:
  4951. vx = view.x + view.width / 2
  4952. vy = view.y + view.height / 2
  4953. new_dist = math.hypot(vx - x, vy - y)
  4954. if current_view is None or new_dist < dist:
  4955. current_view = view
  4956. view_x = vx
  4957. view_y = vy
  4958. dist = new_dist
  4959. bl = min(x, view_x)
  4960. bw = abs(x - view_x)
  4961. bt = min(y, view_y)
  4962. bh = abs(y - view_y)
  4963. for obj in sge.game.current_room.get_objects_at(bl, bt, bw, bh):
  4964. if isinstance(obj, Player):
  4965. new_dist = math.hypot(obj.x - x, obj.y - y)
  4966. if new_dist < dist:
  4967. view_x = obj.x
  4968. view_y = obj.y
  4969. dist = new_dist
  4970. if dist <= SOUND_MAX_RADIUS:
  4971. volume = 1
  4972. elif dist < SOUND_ZERO_RADIUS:
  4973. rng = SOUND_ZERO_RADIUS - SOUND_MAX_RADIUS
  4974. reldist = rng - (dist - SOUND_MAX_RADIUS)
  4975. volume = min(1, abs(reldist / rng))
  4976. else:
  4977. # No point in continuing; it's too far away
  4978. return
  4979. if stereo_enabled:
  4980. hdist = x - view_x
  4981. if abs(hdist) < SOUND_CENTERED_RADIUS:
  4982. balance = 0
  4983. else:
  4984. rng = SOUND_TILTED_RADIUS - SOUND_CENTERED_RADIUS
  4985. balance = max(-SOUND_TILT_LIMIT,
  4986. min(hdist / rng, SOUND_TILT_LIMIT))
  4987. else:
  4988. balance = 0
  4989. sound.play(volume=volume, balance=balance, force=force)
  4990. def play_music(music, force_restart=False, noloop=False):
  4991. """Play the given music file, starting with its start piece."""
  4992. if music_enabled:
  4993. loops = 1 if noloop else None
  4994. if music:
  4995. music_object = loaded_music.get(music)
  4996. if music_object is None:
  4997. try:
  4998. music_object = sge.snd.Music(os.path.join(DATA, "music",
  4999. music))
  5000. except (IOError, OSError):
  5001. sge.snd.Music.clear_queue()
  5002. sge.snd.Music.stop()
  5003. return
  5004. else:
  5005. loaded_music[music] = music_object
  5006. name, ext = os.path.splitext(music)
  5007. music_start = ''.join([name, "-start", ext])
  5008. music_start_object = loaded_music.get(music_start)
  5009. if music_start_object is None:
  5010. try:
  5011. music_start_object = sge.snd.Music(os.path.join(DATA, "music",
  5012. music_start))
  5013. except (IOError, OSError):
  5014. pass
  5015. else:
  5016. loaded_music[music_start] = music_start_object
  5017. if (force_restart or (not music_object.playing and
  5018. (music_start_object is None or
  5019. not music_start_object.playing))):
  5020. sge.snd.Music.clear_queue()
  5021. sge.snd.Music.stop()
  5022. if music_start_object is not None:
  5023. music_start_object.play()
  5024. music_object.queue(loops=loops)
  5025. else:
  5026. music_object.play(loops=loops)
  5027. else:
  5028. sge.snd.Music.clear_queue()
  5029. sge.snd.Music.stop(fade_time=1000)
  5030. else:
  5031. sge.snd.Music.clear_queue()
  5032. sge.snd.Music.stop()
  5033. def set_new_game():
  5034. global player_name
  5035. global watched_timelines
  5036. global current_level
  5037. global spawn_point
  5038. global warp_pads
  5039. global map_revealed
  5040. global map_explored
  5041. global map_removed
  5042. global powerups
  5043. global progress_flags
  5044. global artifacts
  5045. global etanks
  5046. global time_taken
  5047. player_name = "Anneroy"
  5048. watched_timelines = []
  5049. current_level = None
  5050. spawn_point = "save"
  5051. map_revealed = []
  5052. map_explored = []
  5053. map_removed = []
  5054. warp_pads = []
  5055. powerups = []
  5056. progress_flags = []
  5057. artifacts = 0
  5058. etanks = 0
  5059. time_taken = 0
  5060. def write_to_disk():
  5061. # Write our saves and settings to disk.
  5062. keys_cfg = {"left": left_key, "right": right_key, "up": up_key,
  5063. "down": down_key, "aim_diag": aim_diag_key, "jump": jump_key,
  5064. "shoot": shoot_key, "aim_up": aim_up_key,
  5065. "aim_down": aim_down_key, "mode_reset": mode_reset_key,
  5066. "mode": mode_key, "pause": pause_key, "map": map_key}
  5067. js_cfg = {"left": left_js, "right": right_js, "up": up_js,
  5068. "down": down_js, "aim_diag": aim_diag_js, "jump": jump_js,
  5069. "shoot": shoot_js, "aim_up": aim_up_js, "aim_down": aim_down_js,
  5070. "mode_reset": mode_reset_js, "mode": mode_js, "pause": pause_js,
  5071. "map": map_js}
  5072. cfg = {"version": 1, "fullscreen": fullscreen,
  5073. "scale_method": scale_method, "sound_enabled": sound_enabled,
  5074. "music_enabled": music_enabled, "stereo_enabled": stereo_enabled,
  5075. "fps_enabled": fps_enabled, "metroid_controls": metroid_controls,
  5076. "joystick_threshold": joystick_threshold, "keys": keys_cfg,
  5077. "joystick": js_cfg, "ai_data": sorted(list(ai_data))}
  5078. with open(os.path.join(CONFIG, "config.json"), 'w') as f:
  5079. json.dump(cfg, f, indent=4)
  5080. with open(os.path.join(CONFIG, "save_slots.json"), 'w') as f:
  5081. json.dump(save_slots, f, indent=4)
  5082. def save_game():
  5083. global save_slots
  5084. if current_save_slot is not None:
  5085. save_slots[current_save_slot] = {
  5086. "save_format": 1,
  5087. "player_name": player_name,
  5088. "watched_timelines": watched_timelines[:],
  5089. "current_level": current_level,
  5090. "spawn_point": spawn_point,
  5091. "map_revealed": map_revealed[:],
  5092. "map_explored": map_explored[:],
  5093. "map_removed": map_removed[:],
  5094. "warp_pads": warp_pads[:],
  5095. "powerups": powerups[:],
  5096. "progress_flags": progress_flags[:],
  5097. "artifacts": artifacts,
  5098. "etanks": etanks,
  5099. "time_taken": time_taken}
  5100. write_to_disk()
  5101. def load_game():
  5102. global player_name
  5103. global watched_timelines
  5104. global current_level
  5105. global spawn_point
  5106. global map_revealed
  5107. global map_explored
  5108. global map_removed
  5109. global warp_pads
  5110. global powerups
  5111. global progress_flags
  5112. global etanks
  5113. global time_taken
  5114. if (current_save_slot is not None and
  5115. save_slots[current_save_slot] is not None):
  5116. slot = save_slots[current_save_slot]
  5117. save_format = slot.get("save_format", 0)
  5118. if save_format == 1:
  5119. player_name = slot.get("player_name", "Anneroy")
  5120. watched_timelines = slot.get("watched_timelines", [])
  5121. current_level = slot.get("current_level")
  5122. spawn_point = slot.get("spawn_point")
  5123. map_revealed = [tuple(i) for i in slot.get("map_revealed", [])]
  5124. map_explored = [tuple(i) for i in slot.get("map_explored", [])]
  5125. map_removed = [tuple(i) for i in slot.get("map_removed", [])]
  5126. warp_pads = [tuple(i) for i in slot.get("warp_pads", [])]
  5127. powerups = [tuple(i) for i in slot.get("powerups", [])]
  5128. progress_flags = slot.get("progress_flags", [])
  5129. artifacts = slot.get("artifacts", 0)
  5130. etanks = slot.get("etanks", 0)
  5131. time_taken = slot.get("time_taken", 0)
  5132. else:
  5133. set_new_game()
  5134. def start_game():
  5135. global player
  5136. player = Anneroy(0, 0)
  5137. if current_level is None:
  5138. level = SpecialScreen.load("special/intro.tmx")
  5139. else:
  5140. level = Level.load(current_level)
  5141. if level is not None:
  5142. level.start()
  5143. else:
  5144. return False
  5145. return True
  5146. def generate_map():
  5147. global map_rooms
  5148. global map_objects
  5149. global num_powerups
  5150. global num_artifacts
  5151. print(_("Generating new map files; this may take some time."))
  5152. files_checked = set()
  5153. files_remaining = {("0.tmx", 0, 0, None, None)}
  5154. map_rooms = {}
  5155. map_objects = {}
  5156. num_powerups = 0
  5157. num_artifacts = 0
  5158. while files_remaining:
  5159. fname, rm_x, rm_y, origin_level, origin_spawn = files_remaining.pop()
  5160. files_checked.add(fname)
  5161. room = Level.load(fname, True)
  5162. rm_w = int(math.ceil(room.width / SCREEN_SIZE[0]))
  5163. rm_h = int(math.ceil(room.height / SCREEN_SIZE[1]))
  5164. for obj in room.objects:
  5165. if isinstance(obj, Door):
  5166. if ":" in obj.dest:
  5167. level_f, spawn = obj.dest.split(':', 1)
  5168. else:
  5169. level_f = obj.dest
  5170. spawn = fname
  5171. if level_f == origin_level and spawn == origin_spawn:
  5172. if isinstance(obj, LeftDoor):
  5173. rm_x += 1
  5174. elif isinstance(obj, RightDoor):
  5175. rm_x -= 1
  5176. elif isinstance(obj, UpDoor):
  5177. rm_y += 1
  5178. elif isinstance(obj, DownDoor):
  5179. rm_y -= 1
  5180. rm_x -= get_xregion(obj.image_xcenter)
  5181. rm_y -= get_yregion(obj.image_ycenter)
  5182. origin = None
  5183. break
  5184. map_rooms[fname] = (rm_x, rm_y)
  5185. ignore_regions = set()
  5186. for obj in room.objects:
  5187. if isinstance(obj, IgnoreRegion):
  5188. rx1 = rm_x + get_xregion(obj.bbox_left)
  5189. rx2 = rm_x + get_xregion(obj.bbox_right - 1)
  5190. ry1 = rm_y + get_yregion(obj.bbox_top)
  5191. ry2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  5192. for ry in six.moves.range(ry1, ry2 + 1):
  5193. for rx in six.moves.range(rx1, rx2 + 1):
  5194. ignore_regions.add((rx, ry))
  5195. for obj in room.objects:
  5196. if isinstance(obj, Door):
  5197. dx = rm_x + get_xregion(obj.image_xcenter)
  5198. dy = rm_y + get_yregion(obj.image_ycenter)
  5199. if ":" in obj.dest:
  5200. level_f, spawn = obj.dest.split(':', 1)
  5201. else:
  5202. level_f = obj.dest
  5203. spawn = fname
  5204. if level_f not in files_checked:
  5205. files_remaining.add((level_f, dx, dy, fname, obj.spawn_id))
  5206. files_checked.add(level_f)
  5207. if (dx, dy) not in ignore_regions:
  5208. if isinstance(obj, LeftDoor):
  5209. map_objects.setdefault((dx, dy), []).append("door_left")
  5210. elif isinstance(obj, RightDoor):
  5211. map_objects.setdefault((dx, dy), []).append("door_right")
  5212. elif isinstance(obj, UpDoor):
  5213. map_objects.setdefault((dx, dy), []).append("door_top")
  5214. elif isinstance(obj, DownDoor):
  5215. map_objects.setdefault((dx, dy), []).append("door_bottom")
  5216. elif isinstance(obj, WarpPad):
  5217. wx = rm_x + get_xregion(obj.image_xcenter)
  5218. wy = rm_y + get_yregion(obj.image_ycenter)
  5219. if (wx, wy) not in ignore_regions:
  5220. map_objects.setdefault((wx, wy), []).append("warp_pad")
  5221. elif isinstance(obj, Powerup):
  5222. if isinstance(obj, Artifact):
  5223. num_artifacts += 1
  5224. else:
  5225. num_powerups += 1
  5226. px = rm_x + get_xregion(obj.image_xcenter)
  5227. py = rm_y + get_yregion(obj.image_ycenter)
  5228. if (px, py) not in ignore_regions:
  5229. map_objects.setdefault((px, py), []).append("powerup")
  5230. elif isinstance(obj, MapLeftWall):
  5231. wx = rm_x + get_xregion(obj.bbox_left)
  5232. wy1 = rm_y + get_yregion(obj.bbox_top)
  5233. wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  5234. for wy in six.moves.range(wy1, wy2 + 1):
  5235. if (wx, wy) not in ignore_regions:
  5236. map_objects.setdefault((wx, wy), []).append("wall_left")
  5237. elif isinstance(obj, MapRightWall):
  5238. wx = rm_x + get_xregion(obj.bbox_right - 1)
  5239. wy1 = rm_y + get_yregion(obj.bbox_top)
  5240. wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  5241. for wy in six.moves.range(wy1, wy2 + 1):
  5242. if (wx, wy) not in ignore_regions:
  5243. map_objects.setdefault((wx, wy), []).append("wall_right")
  5244. elif isinstance(obj, MapTopWall):
  5245. wx1 = rm_x + get_xregion(obj.bbox_left)
  5246. wx2 = rm_x + get_xregion(obj.bbox_right - 1)
  5247. wy = rm_y + get_yregion(obj.bbox_top)
  5248. for wx in six.moves.range(wx1, wx2 + 1):
  5249. if (wx, wy) not in ignore_regions:
  5250. map_objects.setdefault((wx, wy), []).append("wall_top")
  5251. elif isinstance(obj, MapBottomWall):
  5252. wx1 = rm_x + get_xregion(obj.bbox_left)
  5253. wx2 = rm_x + get_xregion(obj.bbox_right - 1)
  5254. wy = rm_y + get_yregion(obj.bbox_bottom - 1)
  5255. for wx in six.moves.range(wx1, wx2 + 1):
  5256. if (wx, wy) not in ignore_regions:
  5257. map_objects.setdefault((wx, wy), []).append("wall_bottom")
  5258. elif isinstance(obj, MapLeftDoor):
  5259. wx = rm_x + get_xregion(obj.bbox_left)
  5260. wy1 = rm_y + get_yregion(obj.bbox_top)
  5261. wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  5262. for wy in six.moves.range(wy1, wy2 + 1):
  5263. if (wx, wy) not in ignore_regions:
  5264. map_objects.setdefault((wx, wy), []).append("door_left")
  5265. elif isinstance(obj, MapRightDoor):
  5266. wx = rm_x + get_xregion(obj.bbox_right - 1)
  5267. wy1 = rm_y + get_yregion(obj.bbox_top)
  5268. wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
  5269. for wy in six.moves.range(wy1, wy2 + 1):
  5270. if (wx, wy) not in ignore_regions:
  5271. map_objects.setdefault((wx, wy), []).append("door_right")
  5272. elif isinstance(obj, MapTopDoor):
  5273. wx1 = rm_x + get_xregion(obj.bbox_left)
  5274. wx2 = rm_x + get_xregion(obj.bbox_right - 1)
  5275. wy = rm_y + get_yregion(obj.bbox_top)
  5276. for wx in six.moves.range(wx1, wx2 + 1):
  5277. if (wx, wy) not in ignore_regions:
  5278. map_objects.setdefault((wx, wy), []).append("door_top")
  5279. elif isinstance(obj, MapBottomDoor):
  5280. wx1 = rm_x + get_xregion(obj.bbox_left)
  5281. wx2 = rm_x + get_xregion(obj.bbox_right - 1)
  5282. wy = rm_y + get_yregion(obj.bbox_bottom - 1)
  5283. for wx in six.moves.range(wx1, wx2 + 1):
  5284. if (wx, wy) not in ignore_regions:
  5285. map_objects.setdefault((wx, wy), []).append("door_bottom")
  5286. for x in six.moves.range(rm_x, rm_x + rm_w):
  5287. y = rm_y
  5288. if ((x, y) not in ignore_regions and
  5289. "door_top" not in map_objects.setdefault((x, y), [])):
  5290. map_objects[(x, y)].append("wall_top")
  5291. y = rm_y + rm_h - 1
  5292. if ((x, y) not in ignore_regions and
  5293. "door_bottom" not in map_objects.setdefault((x, y), [])):
  5294. map_objects[(x, y)].append("wall_bottom")
  5295. for y in six.moves.range(rm_y, rm_y + rm_h):
  5296. x = rm_x
  5297. if ((x, y) not in ignore_regions and
  5298. "door_left" not in map_objects.setdefault((x, y), [])):
  5299. map_objects[(x, y)].append("wall_left")
  5300. x = rm_x + rm_w - 1
  5301. if ((x, y) not in ignore_regions and
  5302. "door_right" not in map_objects.setdefault((x, y), [])):
  5303. map_objects[(x, y)].append("wall_right")
  5304. f_objects = {}
  5305. for x, y in map_objects:
  5306. i = "{},{}".format(x, y)
  5307. f_objects[i] = map_objects[(x, y)]
  5308. info = {"powerups": num_powerups, "artifacts": num_artifacts}
  5309. with open(os.path.join(DATA, "map", "rooms.json"), 'w') as f:
  5310. json.dump(map_rooms, f, indent=4, sort_keys=True)
  5311. with open(os.path.join(DATA, "map", "objects.json"), 'w') as f:
  5312. json.dump(f_objects, f, indent=4, sort_keys=True)
  5313. with open(os.path.join(DATA, "map", "info.json"), 'w') as f:
  5314. json.dump(info, f, indent=4, sort_keys=True)
  5315. def draw_map(x=None, y=None, w=None, h=None, player_x=None, player_y=None):
  5316. if x is None or y is None or w is None or h is None:
  5317. left = 0
  5318. right = 0
  5319. top = 0
  5320. bottom = 0
  5321. for rx, ry in set(map_revealed + map_explored):
  5322. left = min(left, rx)
  5323. right = max(right, rx)
  5324. top = min(top, ry)
  5325. bottom = max(bottom, ry)
  5326. if x is None:
  5327. x = left
  5328. if y is None:
  5329. y = top
  5330. if w is None:
  5331. w = right - x + 1
  5332. if h is None:
  5333. h = bottom - y + 1
  5334. removed = []
  5335. for obj, fname, ox, oy in map_removed:
  5336. if fname in map_rooms:
  5337. rm_x, rm_y = map_rooms[fname]
  5338. removed.append((obj, rm_x + ox, rm_y + oy))
  5339. s_w = w * MAP_CELL_WIDTH
  5340. s_h = h * MAP_CELL_HEIGHT
  5341. map_sprite = sge.gfx.Sprite(width=s_w, height=s_h)
  5342. map_sprite.draw_rectangle(0, 0, s_w, s_h, fill=sge.gfx.Color("black"))
  5343. for ex, ey in map_explored:
  5344. dx = (ex - x) * MAP_CELL_WIDTH
  5345. dy = (ey - y) * MAP_CELL_HEIGHT
  5346. map_sprite.draw_rectangle(dx, dy, MAP_CELL_WIDTH, MAP_CELL_HEIGHT,
  5347. fill=sge.gfx.Color("teal"))
  5348. for ox, oy in set(map_objects) & set(map_revealed + map_explored):
  5349. if x <= ox < x + w and y <= oy < y + h:
  5350. for obj in map_objects[(ox, oy)]:
  5351. if (obj, ox, oy) in removed:
  5352. removed.remove((obj, ox, oy))
  5353. else:
  5354. dx = (ox - x) * MAP_CELL_WIDTH
  5355. dy = (oy - y) * MAP_CELL_HEIGHT
  5356. if obj == "wall_left":
  5357. map_sprite.draw_sprite(map_wall_left_sprite, 0, dx, dy)
  5358. elif obj == "wall_right":
  5359. map_sprite.draw_sprite(map_wall_right_sprite, 0, dx, dy)
  5360. elif obj == "wall_top":
  5361. map_sprite.draw_sprite(map_wall_top_sprite, 0, dx, dy)
  5362. elif obj == "wall_bottom":
  5363. map_sprite.draw_sprite(map_wall_bottom_sprite, 0, dx, dy)
  5364. elif obj == "door_left":
  5365. map_sprite.draw_sprite(map_door_left_sprite, 0, dx, dy)
  5366. elif obj == "door_right":
  5367. map_sprite.draw_sprite(map_door_right_sprite, 0, dx, dy)
  5368. elif obj == "door_top":
  5369. map_sprite.draw_sprite(map_door_top_sprite, 0, dx, dy)
  5370. elif obj == "door_bottom":
  5371. map_sprite.draw_sprite(map_door_bottom_sprite, 0, dx, dy)
  5372. elif obj == "powerup":
  5373. if "warp_pad" not in map_objects[(ox, oy)]:
  5374. map_sprite.draw_sprite(map_powerup_sprite, 0, dx, dy)
  5375. elif obj == "warp_pad":
  5376. map_sprite.draw_sprite(map_warp_pad_sprite, 0, dx, dy)
  5377. if player_x is not None and player_y is not None:
  5378. dx = (player_x - x) * MAP_CELL_WIDTH
  5379. dy = (player_y - y) * MAP_CELL_HEIGHT
  5380. map_sprite.draw_sprite(map_player_sprite, 0, dx, dy)
  5381. return map_sprite
  5382. def update_fullscreen():
  5383. if fullscreen:
  5384. sge.game.scale = FSSCALE if FSSCALE else None
  5385. sge.game.fullscreen = True
  5386. else:
  5387. sge.game.scale = SCALE
  5388. sge.game.fullscreen = False
  5389. sge.game.scale = None
  5390. TYPES = {
  5391. "solid_left": SolidLeft, "solid_right": SolidRight, "solid_top": SolidTop,
  5392. "solid_bottom": SolidBottom, "solid": Solid, "slope_topleft": SlopeTopLeft,
  5393. "slope_topright": SlopeTopRight, "slope_bottomleft": SlopeBottomLeft,
  5394. "slope_bottomright": SlopeBottomRight, "moving_platform": MovingPlatform,
  5395. "spike_left": SpikeLeft, "spike_right": SpikeRight, "spike_top": SpikeTop,
  5396. "spike_bottom": SpikeBottom, "death": Death,
  5397. "frog": Frog, "hedgehog": Hedgehog, "bat": Bat, "jellyfish": Jellyfish,
  5398. "worm": Worm, "scorpion": Scorpion, "mantanoid": Mantanoid,
  5399. "fake_tile": FakeTile, "weak_stone": WeakStone, "spike_stone": SpikeStone,
  5400. "macguffin": Macguffin, "artifact": Artifact, "etank": Etank,
  5401. "life_orb": LifeOrb, "map": Map, "map_disk": MapDisk,
  5402. "atomic_compressor": AtomicCompressor, "monkey_boots": MonkeyBoots,
  5403. "hedgehog_hormone": HedgehogHormone,
  5404. "warp_pad": WarpPad, "doorframe_x": DoorFrameX, "doorframe_y": DoorFrameY,
  5405. "door_left": LeftDoor, "door_right": RightDoor, "door_up": UpDoor,
  5406. "door_down": DownDoor,
  5407. "timeline_switcher": TimelineSwitcher,
  5408. "enemies": get_object, "doors": get_object, "stones": get_object,
  5409. "powerups": get_object, "objects": get_object,
  5410. "moving_platform_path": MovingPlatformPath,
  5411. "triggered_moving_platform_path": TriggeredMovingPlatformPath,
  5412. "player": PlayerLayer,
  5413. "camera_x_guide": CameraXGuide, "camera_y_guide": CameraYGuide,
  5414. "map_wall_left": MapLeftWall, "map_wall_right": MapRightWall,
  5415. "map_wall_top": MapTopWall, "map_wall_bottom": MapBottomWall,
  5416. "map_door_left": MapLeftDoor, "map_door_right": MapRightDoor,
  5417. "map_door_top": MapTopDoor, "map_door_bottom": MapBottomDoor,
  5418. "map_ignore_region": IgnoreRegion, "mantanoid_nogo": MantanoidNoGo
  5419. }
  5420. print(_("Initializing game system..."))
  5421. Game(SCREEN_SIZE[0], SCREEN_SIZE[1], scale=SCALE, fps=FPS, delta=DELTA,
  5422. delta_min=DELTA_MIN, delta_max=DELTA_MAX,
  5423. window_text="Hexoshi DEMO {}".format(__version__))
  5424. #window_icon=os.path.join(DATA, "images", "misc", "icon.png"))
  5425. sge.game.scale = None
  5426. print(_("Initializing GUI system..."))
  5427. xsge_gui.init()
  5428. gui_handler = xsge_gui.Handler()
  5429. xsge_gui.default_font.size = 8
  5430. xsge_gui.textbox_font.size = 8
  5431. menu_color = sge.gfx.Color("black")
  5432. menu_text_color = sge.gfx.Color((64, 0, 255))
  5433. menu_text_selected_color = sge.gfx.Color("white")
  5434. print(_("Loading resources..."))
  5435. if not os.path.exists(CONFIG):
  5436. os.makedirs(CONFIG)
  5437. # Save error messages to a text file (so they aren't lost).
  5438. if not PRINT_ERRORS:
  5439. stderr = os.path.join(CONFIG, "stderr.txt")
  5440. if not os.path.isfile(stderr) or os.path.getsize(stderr) > 1000000:
  5441. sys.stderr = open(stderr, 'w')
  5442. else:
  5443. sys.stderr = open(stderr, 'a')
  5444. dt = datetime.datetime.now()
  5445. sys.stderr.write("\n{}-{}-{} {}:{}:{}\n".format(
  5446. dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second))
  5447. del dt
  5448. # Load sprites
  5449. d = os.path.join(DATA, "images", "objects", "anneroy")
  5450. anneroy_torso_offset = {}
  5451. fname = os.path.join(d, "anneroy_sheet.png")
  5452. anneroy_turn_sprite = sge.gfx.Sprite.from_tileset(
  5453. fname, 2, 109, 3, xsep=3, width=39, height=43, origin_x=19, origin_y=19,
  5454. fps=10)
  5455. anneroy_teleport_sprite = sge.gfx.Sprite.from_tileset(
  5456. fname, 360, 455, 7, xsep=4, width=46, height=49, origin_x=23, origin_y=25,
  5457. fps=20)
  5458. anneroy_wall_right_sprite = sge.gfx.Sprite.from_tileset(
  5459. fname, 439, 228, 2, xsep=5, width=32, height=45, origin_x=23, origin_y=19,
  5460. fps=10)
  5461. anneroy_wall_left_sprite = sge.gfx.Sprite.from_tileset(
  5462. fname, 439, 284, 2, xsep=5, width=31, height=45, origin_x=9, origin_y=19,
  5463. fps=10)
  5464. anneroy_walljump_left_sprite = sge.gfx.Sprite.from_tileset(
  5465. fname, 522, 229, width=34, height=46, origin_x=17, origin_y=20)
  5466. anneroy_walljump_right_sprite = sge.gfx.Sprite.from_tileset(
  5467. fname, 522, 283, width=34, height=46, origin_x=15, origin_y=20)
  5468. anneroy_compress_sprite = sge.gfx.Sprite.from_tileset(
  5469. fname, 9, 393, 3, xsep=5, width=27, height=32, origin_x=12, origin_y=8,
  5470. fps=15)
  5471. anneroy_ball_sprite = sge.gfx.Sprite.from_tileset(
  5472. fname, 9, 440, 8, xsep=8, width=16, height=16, origin_x=8, origin_y=-8)
  5473. anneroy_decompress_fail_sprite = sge.gfx.Sprite.from_tileset(
  5474. fname, 150, 393, 3, xsep=5, width=27, height=32, origin_x=12, origin_y=8,
  5475. fps=15)
  5476. anneroy_hedgehog_start_sprite = sge.gfx.Sprite.from_tileset(
  5477. fname, 9, 469, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
  5478. anneroy_hedgehog_extend_sprite = sge.gfx.Sprite.from_tileset(
  5479. fname, 9, 510, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
  5480. anneroy_hedgehog_sprite = sge.gfx.Sprite.from_tileset(
  5481. fname, 9, 551, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
  5482. anneroy_death_right_sprite = sge.gfx.Sprite.from_tileset(
  5483. fname, 5, 597, 7, xsep=5, width=86, height=82, origin_x=40, origin_y=38,
  5484. fps=10)
  5485. anneroy_death_left_sprite = sge.gfx.Sprite.from_tileset(
  5486. fname, 5, 684, 7, xsep=5, width=86, height=82, origin_x=46, origin_y=38,
  5487. fps=10)
  5488. anneroy_explode_sprite = sge.gfx.Sprite.from_tileset(
  5489. fname, 369, 771, 3, xsep=5, width=86, height=82, origin_x=43, origin_y=38,
  5490. fps=10)
  5491. anneroy_explode_fragments = sge.gfx.Sprite.from_tileset(
  5492. fname, 406, 582, 21, xsep=3, width=6, height=6, origin_x=3, origin_y=3)
  5493. anneroy_torso_right_idle_sprite = sge.gfx.Sprite.from_tileset(
  5494. fname, 317, 45, width=26, height=27, origin_x=9, origin_y=19)
  5495. anneroy_torso_right_aim_right_sprite = sge.gfx.Sprite.from_tileset(
  5496. fname, 234, 45, width=26, height=20, origin_x=5, origin_y=19)
  5497. anneroy_torso_right_aim_up_sprite = sge.gfx.Sprite.from_tileset(
  5498. fname, 293, 38, width=20, height=27, origin_x=6, origin_y=26)
  5499. anneroy_torso_right_aim_down_sprite = sge.gfx.Sprite.from_tileset(
  5500. fname, 182, 52, width=20, height=30, origin_x=1, origin_y=12)
  5501. anneroy_torso_right_aim_upright_sprite = sge.gfx.Sprite.from_tileset(
  5502. fname, 264, 39, width=25, height=26, origin_x=5, origin_y=25)
  5503. anneroy_torso_right_aim_downright_sprite = sge.gfx.Sprite.from_tileset(
  5504. fname, 207, 45, width=23, height=26, origin_x=5, origin_y=19)
  5505. anneroy_torso_left_idle_sprite = sge.gfx.Sprite.from_tileset(
  5506. fname, 14, 45, width=27, height=25, origin_x=18, origin_y=19)
  5507. anneroy_torso_left_aim_left_sprite = sge.gfx.Sprite.from_tileset(
  5508. fname, 95, 45, width=26, height=20, origin_x=20, origin_y=19)
  5509. anneroy_torso_left_aim_up_sprite = sge.gfx.Sprite.from_tileset(
  5510. fname, 45, 38, width=17, height=27, origin_x=11, origin_y=26)
  5511. anneroy_torso_left_aim_down_sprite = sge.gfx.Sprite.from_tileset(
  5512. fname, 154, 52, width=20, height=30, origin_x=18, origin_y=12)
  5513. anneroy_torso_left_aim_upleft_sprite = sge.gfx.Sprite.from_tileset(
  5514. fname, 66, 39, width=25, height=26, origin_x=19, origin_y=25)
  5515. anneroy_torso_left_aim_downleft_sprite = sge.gfx.Sprite.from_tileset(
  5516. fname, 125, 45, width=23, height=26, origin_x=17, origin_y=19)
  5517. anneroy_legs_stand_sprite = sge.gfx.Sprite.from_tileset(
  5518. fname, 47, 76, width=19, height=24, origin_x=8, origin_y=0)
  5519. anneroy_legs_run_sprite = sge.gfx.Sprite.from_tileset(
  5520. fname, 9, 299, 5, 2, xsep=8, ysep=31, width=40, height=24, origin_x=17,
  5521. origin_y=0)
  5522. anneroy_legs_jump_sprite = sge.gfx.Sprite.from_tileset(
  5523. fname, 14, 234, 5, xsep=15, width=23, height=29, origin_x=8, origin_y=5,
  5524. fps=30)
  5525. anneroy_legs_fall_sprite = sge.gfx.Sprite.from_tileset(
  5526. fname, 204, 234, width=23, height=29, origin_x=8, origin_y=5)
  5527. anneroy_legs_land_sprite = sge.gfx.Sprite.from_tileset(
  5528. fname, 242, 234, 2, xsep=15, width=23, height=29, origin_x=8, origin_y=5,
  5529. fps=30)
  5530. anneroy_legs_crouched_sprite = sge.gfx.Sprite.from_tileset(
  5531. fname, 23, 85, width=21, height=15, origin_x=7, origin_y=-9)
  5532. anneroy_legs_crouch_sprite = sge.gfx.Sprite.from_tileset(
  5533. fname, 9, 189, 2, xsep=7, width=21, height=21, origin_x=8, origin_y=-3,
  5534. fps=10)
  5535. anneroy_bullet_dust_sprite = sge.gfx.Sprite.from_tileset(
  5536. fname, 249, 119, width=26, height=16, origin_x=2, origin_y=7, fps=10)
  5537. anneroy_bullet_sprite = sge.gfx.Sprite.from_tileset(
  5538. fname, 287, 123, width=17, height=6, origin_x=14, origin_y=3, bbox_x=-8,
  5539. bbox_y=-8, bbox_width=16, bbox_height=16)
  5540. anneroy_bullet_dissipate_sprite = sge.gfx.Sprite.from_tileset(
  5541. fname, 317, 102, 2, xsep=12, width=21, height=52, origin_x=12, origin_y=23,
  5542. fps=10)
  5543. n = id(anneroy_compress_sprite)
  5544. anneroy_torso_offset[(n, 0)] = (0, 11)
  5545. anneroy_torso_offset[(n, 1)] = (0, 11)
  5546. anneroy_torso_offset[(n, 2)] = (0, 11)
  5547. n = id(anneroy_decompress_fail_sprite)
  5548. anneroy_torso_offset[(n, 0)] = (0, 11)
  5549. anneroy_torso_offset[(n, 1)] = (0, 11)
  5550. anneroy_torso_offset[(n, 2)] = (0, 11)
  5551. n = id(anneroy_legs_run_sprite)
  5552. anneroy_torso_offset[(n, 1)] = (0, 1)
  5553. anneroy_torso_offset[(n, 2)] = (0, 3)
  5554. anneroy_torso_offset[(n, 3)] = (0, 4)
  5555. anneroy_torso_offset[(n, 4)] = (0, 2)
  5556. anneroy_torso_offset[(n, 6)] = (0, 1)
  5557. anneroy_torso_offset[(n, 7)] = (0, 3)
  5558. anneroy_torso_offset[(n, 8)] = (0, 5)
  5559. anneroy_torso_offset[(n, 9)] = (0, 3)
  5560. n = id(anneroy_legs_jump_sprite)
  5561. anneroy_torso_offset[(n, 0)] = (0, 3)
  5562. anneroy_torso_offset[(n, 1)] = (0, -5)
  5563. anneroy_torso_offset[(n, 2)] = (0, -2)
  5564. anneroy_torso_offset[(n, 3)] = (0, -2)
  5565. anneroy_torso_offset[(n, 4)] = (0, -3)
  5566. n = id(anneroy_legs_fall_sprite)
  5567. anneroy_torso_offset[(n, 0)] = (0, -2)
  5568. n = id(anneroy_legs_land_sprite)
  5569. anneroy_torso_offset[(n, 0)] = (0, -5)
  5570. anneroy_torso_offset[(n, 1)] = (0, 3)
  5571. n = id(anneroy_legs_crouched_sprite)
  5572. anneroy_torso_offset[(n, 0)] = (0, 11)
  5573. n = id(anneroy_legs_crouch_sprite)
  5574. anneroy_torso_offset[(n, 0)] = (0, 3)
  5575. anneroy_torso_offset[(n, 1)] = (0, 9)
  5576. d = os.path.join(DATA, "images", "objects", "enemies")
  5577. frog_stand_sprite = sge.gfx.Sprite("frog_stand", d)
  5578. frog_jump_sprite = sge.gfx.Sprite("frog_jump", d)
  5579. frog_fall_sprite = sge.gfx.Sprite("frog_fall", d)
  5580. bat_sprite = sge.gfx.Sprite("bat", d, fps=10, bbox_x=3, bbox_y=4,
  5581. bbox_width=10, bbox_height=10)
  5582. worm_sprite = sge.gfx.Sprite("worm", d, fps=10)
  5583. worm_base_sprite = sge.gfx.Sprite("worm_base", d, fps=10)
  5584. fname = os.path.join(d, "hedgehog_sheet.png")
  5585. hedgehog_stand_sprite = sge.gfx.Sprite.from_tileset(
  5586. fname, 0, 0, width=20, height=20)
  5587. hedgehog_walk_sprite = sge.gfx.Sprite.from_tileset(
  5588. fname, 0, 20, 6, width=20, height=20)
  5589. hedgehog_compress_sprite = sge.gfx.Sprite.from_tileset(
  5590. fname, 0, 40, 2, width=20, height=20, fps=15)
  5591. hedgehog_ball_sprite = sge.gfx.Sprite.from_tileset(
  5592. fname, 0, 60, 8, width=20, height=20)
  5593. hedgehog_uncompress_sprite = sge.gfx.Sprite.from_tileset(
  5594. fname, 0, 80, 2, width=20, height=20, fps=15)
  5595. fname = os.path.join(d, "jellyfish_sheet.png")
  5596. jellyfish_idle_sprite = sge.gfx.Sprite.from_tileset(
  5597. fname, 0, 0, 7, width=32, height=32, origin_x=24, origin_y=24, fps=20)
  5598. jellyfish_swim_start_sprite = sge.gfx.Sprite.from_tileset(
  5599. fname, 0, 64, 6, width=32, height=32, origin_x=24, origin_y=24, fps=50)
  5600. jellyfish_swim_sprite = sge.gfx.Sprite.from_tileset(
  5601. fname, 192, 64, 6, width=32, height=32, origin_x=24, origin_y=24, fps=50)
  5602. fname = os.path.join(d, "scorpion_sheet.png")
  5603. scorpion_stand_sprite = sge.gfx.Sprite.from_tileset(
  5604. fname, 0, 0, width=60, height=36, origin_x=30, origin_y=9)
  5605. scorpion_walk_sprite = sge.gfx.Sprite.from_tileset(
  5606. fname, 0, 36, 6, width=60, height=36, origin_x=30, origin_y=9)
  5607. scorpion_shoot_start_sprite = sge.gfx.Sprite.from_tileset(
  5608. fname, 0, 108, 11, width=60, height=36, origin_x=30, origin_y=9, fps=20)
  5609. scorpion_shoot_end_sprite = sge.gfx.Sprite.from_tileset(
  5610. fname, 0, 144, 5, width=60, height=36, origin_x=30, origin_y=9, fps=20)
  5611. scorpion_projectile_sprite = sge.gfx.Sprite(
  5612. "scorpion_projectile", d, origin_y=2, bbox_x=2, bbox_y=1, bbox_width=17,
  5613. bbox_height=4)
  5614. scorpion_projectile_shard_sprite = sge.gfx.Sprite(
  5615. "scorpion_projectile_shard", d, fps=0)
  5616. fname = os.path.join(d, "mantanoid_sheet.png")
  5617. mantanoid_stand_sprite = sge.gfx.Sprite.from_tileset(
  5618. fname, 41, 51, width=32, height=48, origin_x=15, origin_y=15)
  5619. mantanoid_idle_sprite = sge.gfx.Sprite.from_tileset(
  5620. fname, 41, 208, 12, xsep=5, width=33, height=50, origin_x=15, origin_y=17,
  5621. fps=10)
  5622. mantanoid_turn_sprite = sge.gfx.Sprite.from_tileset(
  5623. fname, 41, 120, 3, xsep=5, width=32, height=47, origin_x=15, origin_y=14,
  5624. fps=10)
  5625. mantanoid_walk_sprite = sge.gfx.Sprite.from_tileset(
  5626. fname, 41, 657, 10, xsep=3, width=41, height=49, origin_x=23, origin_y=16)
  5627. mantanoid_hop_start_sprite = sge.gfx.Sprite.from_tileset(
  5628. fname, 41, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
  5629. fps=10)
  5630. mantanoid_jump_start_sprite = sge.gfx.Sprite.from_tileset(
  5631. fname, 41, 372, 5, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
  5632. fps=10)
  5633. mantanoid_jump_sprite = sge.gfx.Sprite.from_tileset(
  5634. fname, 156, 299, width=32, height=57, origin_x=15, origin_y=24)
  5635. mantanoid_fall_start_sprite = sge.gfx.Sprite.from_tileset(
  5636. fname, 193, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
  5637. fps=10)
  5638. mantanoid_fall_sprite = sge.gfx.Sprite.from_tileset(
  5639. fname, 304, 299, width=32, height=57, origin_x=15, origin_y=24)
  5640. mantanoid_land_sprite = sge.gfx.Sprite.from_tileset(
  5641. fname, 341, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
  5642. fps=10)
  5643. mantanoid_slash_start_sprite = sge.gfx.Sprite.from_tileset(
  5644. fname, 41, 470, 3, xsep=5, width=45, height=65, origin_x=15, origin_y=32,
  5645. fps=10)
  5646. mantanoid_slash_single_sprite = sge.gfx.Sprite.from_tileset(
  5647. fname, 191, 470, 4, xsep=5, width=45, height=65, origin_x=15, origin_y=32,
  5648. fps=10)
  5649. mantanoid_slash_double_first_sprite = sge.gfx.Sprite.from_tileset(
  5650. fname, 233, 551, 4, xsep=3, width=61, height=65, origin_x=15, origin_y=32,
  5651. fps=10)
  5652. mantanoid_slash_double_second_sprite = sge.gfx.Sprite.from_tileset(
  5653. fname, 489, 551, 3, xsep=3, width=61, height=65,
  5654. origin_x=(15 + MANTANOID_DOUBLESLASH_OFFSET), origin_y=32, fps=10)
  5655. d = os.path.join(DATA, "images", "objects", "doors")
  5656. door_barrier_x_sprite = sge.gfx.Sprite("barrier_x", d, origin_y=-8, fps=30,
  5657. bbox_y=8, bbox_width=8, bbox_height=48)
  5658. door_barrier_y_sprite = sge.gfx.Sprite("barrier_y", d, origin_x=-8, fps=30,
  5659. bbox_x=8, bbox_width=48, bbox_height=8)
  5660. doorframe_regular_x_closed_sprite = sge.gfx.Sprite("regular_x_closed", d)
  5661. doorframe_regular_x_open_sprite = sge.gfx.Sprite("regular_x_open", d)
  5662. doorframe_regular_y_closed_sprite = sge.gfx.Sprite("regular_y_closed", d)
  5663. doorframe_regular_y_open_sprite = sge.gfx.Sprite("regular_y_open", d)
  5664. d = os.path.join(DATA, "images", "objects", "stones")
  5665. stone_fragment_sprite = sge.gfx.Sprite("stone_fragment", d)
  5666. d = os.path.join(DATA, "images", "objects", "powerups")
  5667. life_orb_sprite = sge.gfx.Sprite("life_orb", d, fps=10)
  5668. powerup_map_sprite = sge.gfx.Sprite("map", d, fps=3)
  5669. atomic_compressor_sprite = sge.gfx.Sprite(
  5670. "atomic_compressor", d, origin_y=1, fps=10, bbox_width=16, bbox_height=16)
  5671. monkey_boots_sprite = sge.gfx.Sprite(
  5672. "monkey_boots", d, bbox_y=9, bbox_width=16, bbox_height=7)
  5673. monkey_boots_gleam_sprite = sge.gfx.Sprite(
  5674. "monkey_boots_gleam", d, origin_x=10, origin_y=5, fps=15)
  5675. hedgehog_hormone_sprite = sge.gfx.Sprite("hedgehog_hormone", d)
  5676. hedgehog_hormone_bubble_sprite = sge.gfx.Sprite("hedgehog_hormone_bubble", d,
  5677. fps=5)
  5678. d = os.path.join(DATA, "images", "objects", "misc")
  5679. warp_pad_active_sprite = sge.gfx.Sprite("warp_pad_active", d)
  5680. warp_pad_inactive_sprite = sge.gfx.Sprite("warp_pad_inactive", d)
  5681. d = os.path.join(DATA, "images", "map")
  5682. map_wall_left_sprite = sge.gfx.Sprite("wall_left", d)
  5683. map_wall_right_sprite = sge.gfx.Sprite("wall_right", d)
  5684. map_wall_top_sprite = sge.gfx.Sprite("wall_top", d)
  5685. map_wall_bottom_sprite = sge.gfx.Sprite("wall_bottom", d)
  5686. map_door_left_sprite = sge.gfx.Sprite("door_left", d)
  5687. map_door_right_sprite = sge.gfx.Sprite("door_right", d)
  5688. map_door_top_sprite = sge.gfx.Sprite("door_top", d)
  5689. map_door_bottom_sprite = sge.gfx.Sprite("door_bottom", d)
  5690. map_powerup_sprite = sge.gfx.Sprite("powerup", d)
  5691. map_warp_pad_sprite = sge.gfx.Sprite("warp_pad", d)
  5692. map_player_sprite = sge.gfx.Sprite("player", d)
  5693. d = os.path.join(DATA, "images", "misc")
  5694. logo_sprite = sge.gfx.Sprite("logo", d, origin_x=125)
  5695. font_sprite = sge.gfx.Sprite.from_tileset(
  5696. os.path.join(d, "font.png"), columns=18, rows=19, width=7, height=9)
  5697. font_small_sprite = sge.gfx.Sprite.from_tileset(
  5698. os.path.join(d, "font_small.png"), columns=8, rows=12, width=7, height=7)
  5699. font_big_sprite = sge.gfx.Sprite.from_tileset(
  5700. os.path.join(d, "font_big.png"), columns=8, rows=12, width=14, height=14,
  5701. xsep=2, ysep=2)
  5702. healthbar_back_sprite = sge.gfx.Sprite("healthbar_back", d, origin_x=2,
  5703. origin_y=1)
  5704. healthbar_front_sprite = sge.gfx.Sprite("healthbar_front", d,
  5705. transparent=False)
  5706. healthbar_width = healthbar_front_sprite.width
  5707. healthbar_height = healthbar_front_sprite.height
  5708. etank_empty_sprite = sge.gfx.Sprite("etank_empty", d)
  5709. etank_full_sprite = sge.gfx.Sprite("etank_full", d)
  5710. life_force_sprite = sge.gfx.Sprite(
  5711. "life_force", d, origin_x=7, origin_y=7, fps=10)
  5712. # Load backgrounds
  5713. d = os.path.join(DATA, "images", "backgrounds")
  5714. layers = []
  5715. if not NO_BACKGROUNDS:
  5716. layers = [
  5717. sge.gfx.BackgroundLayer(
  5718. sge.gfx.Sprite("iridia", d), 0, 0, -100000, xscroll_rate=0.7,
  5719. yscroll_rate=0.7, repeat_left=True, repeat_right=True,
  5720. repeat_up=True, repeat_down=True)]
  5721. backgrounds["iridia"] = sge.gfx.Background(layers,
  5722. sge.gfx.Color((21, 17, 22)))
  5723. # Load fonts
  5724. chars = ([six.unichr(i) for i in six.moves.range(32, 127)] +
  5725. [None, ETANK_CHAR] + [' '] * 11 +
  5726. [six.unichr(i) for i in six.moves.range(161, 384)])
  5727. font = sge.gfx.Font.from_sprite(font_sprite, chars, size=9, hsep=-1)
  5728. chars = [six.unichr(i) for i in six.moves.range(32, 127)] + [None]
  5729. font_big = sge.gfx.Font.from_sprite(font_big_sprite, chars, size=14,
  5730. hsep=2, vsep=2)
  5731. chars = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
  5732. "0123456789.,;:?!-_~#\"'&()[]|`\\/@^+=*$\xa3\u20ac<> ") + [None]
  5733. font_small = sge.gfx.Font.from_sprite(font_small_sprite, chars, size=7,
  5734. hsep=-1)
  5735. # Load sounds
  5736. shoot_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "shoot.wav"))
  5737. bullet_death_sound = sge.snd.Sound(
  5738. os.path.join(DATA, "sounds", "bullet_death.ogg"), volume=0.2)
  5739. land_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "land.ogg"), volume=0.5)
  5740. ball_land_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "ball_land.ogg"))
  5741. hedgehog_spikes_sound = sge.snd.Sound(
  5742. os.path.join(DATA, "sounds", "hedgehog_spikes.wav"), volume=0.5)
  5743. hurt_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "hurt.wav"))
  5744. death_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "death.wav"))
  5745. stone_break_sound = sge.snd.Sound(
  5746. os.path.join(DATA, "sounds", "stone_break.ogg"), volume=0.5)
  5747. powerup_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "powerup.wav"))
  5748. heal_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "heal.wav"))
  5749. warp_pad_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "warp_pad.ogg"))
  5750. teleport_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "teleport.wav"))
  5751. door_open_sound = sge.snd.Sound(
  5752. os.path.join(DATA, "sounds", "door_open.ogg"), volume=0.5)
  5753. door_close_sound = sge.snd.Sound(
  5754. os.path.join(DATA, "sounds", "door_close.ogg"), volume=0.5)
  5755. enemy_hurt_sound = stone_break_sound
  5756. enemy_death_sound = sge.snd.Sound(
  5757. os.path.join(DATA, "sounds", "enemy_death.wav"))
  5758. frog_jump_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "frog_jump.wav"))
  5759. scorpion_shoot_sound = sge.snd.Sound(
  5760. os.path.join(DATA, "sounds", "scorpion_shoot.wav"))
  5761. scorpion_projectile_break_sound = sge.snd.Sound(
  5762. os.path.join(DATA, "sounds", "scorpion_projectile_break.ogg"), volume=0.5)
  5763. mantanoid_approach_sound = sge.snd.Sound(
  5764. os.path.join(DATA, "sounds", "mantanoid_approach.wav"))
  5765. mantanoid_slash_sound = sge.snd.Sound(
  5766. os.path.join(DATA, "sounds", "mantanoid_slash.wav"))
  5767. select_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "select.ogg"))
  5768. pause_sound = select_sound
  5769. confirm_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "confirm.wav"))
  5770. cancel_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "cancel.wav"))
  5771. error_sound = cancel_sound
  5772. type_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "type.wav"))
  5773. # Create objects
  5774. ##lava_animation = sge.dsp.Object(0, 0, sprite=lava_body_sprite, visible=False,
  5775. ## tangible=False)
  5776. # Create rooms
  5777. sge.game.start_room = TitleScreen.load(
  5778. os.path.join("special", "title_screen.tmx"), True)
  5779. sge.game.mouse.visible = False
  5780. # Load map data
  5781. map_rooms = {}
  5782. map_objects = {}
  5783. if not GEN_MAP:
  5784. try:
  5785. with open(os.path.join(DATA, "map", "rooms.json")) as f:
  5786. d = json.load(f)
  5787. except (IOError, ValueError):
  5788. generate_map()
  5789. else:
  5790. for i in d:
  5791. map_rooms[i] = tuple(d[i])
  5792. try:
  5793. with open(os.path.join(DATA, "map", "objects.json")) as f:
  5794. d = json.load(f)
  5795. except (IOError, ValueError):
  5796. generate_map()
  5797. else:
  5798. for i in d:
  5799. x, y = tuple(i.split(','))
  5800. j = (int(x), int(y))
  5801. map_objects[j] = d[i]
  5802. try:
  5803. with open(os.path.join(DATA, "map", "info.json")) as f:
  5804. d = json.load(f)
  5805. except (IOError, ValueError):
  5806. generate_map()
  5807. else:
  5808. num_powerups = d.get("powerups", 0)
  5809. num_artifacts = d.get("artifacts", 0)
  5810. else:
  5811. generate_map()
  5812. if SAVE_MAP:
  5813. map_revealed = list(map_objects.keys())
  5814. map_explored = map_revealed
  5815. draw_map().save("map.png")
  5816. map_revealed = []
  5817. map_explored = []
  5818. try:
  5819. with open(os.path.join(CONFIG, "config.json")) as f:
  5820. cfg = json.load(f)
  5821. except (IOError, ValueError):
  5822. cfg = {}
  5823. finally:
  5824. cfg_version = cfg.get("version", 0)
  5825. fullscreen = cfg.get("fullscreen", fullscreen)
  5826. update_fullscreen()
  5827. scale_method = cfg.get("scale_method", scale_method)
  5828. sge.game.scale_method = scale_method
  5829. sound_enabled = cfg.get("sound_enabled", sound_enabled)
  5830. music_enabled = cfg.get("music_enabled", music_enabled)
  5831. stereo_enabled = cfg.get("stereo_enabled", stereo_enabled)
  5832. fps_enabled = cfg.get("fps_enabled", fps_enabled)
  5833. metroid_controls = cfg.get("metroid_controls", metroid_controls)
  5834. joystick_threshold = cfg.get("joystick_threshold", joystick_threshold)
  5835. xsge_gui.joystick_threshold = joystick_threshold
  5836. ai_data |= set(cfg.get("ai_data", ai_data))
  5837. keys_cfg = cfg.get("keys", {})
  5838. left_key = keys_cfg.get("left", left_key)
  5839. right_key = keys_cfg.get("right", right_key)
  5840. up_key = keys_cfg.get("up", up_key)
  5841. aim_diag_key = keys_cfg.get("aim_diag", aim_diag_key)
  5842. down_key = keys_cfg.get("down", down_key)
  5843. jump_key = keys_cfg.get("jump", jump_key)
  5844. shoot_key = keys_cfg.get("shoot", shoot_key)
  5845. aim_up_key = keys_cfg.get("aim_up", aim_up_key)
  5846. aim_down_key = keys_cfg.get("aim_down", aim_down_key)
  5847. mode_reset_key = keys_cfg.get("mode_reset", mode_reset_key)
  5848. mode_key = keys_cfg.get("mode", mode_key)
  5849. pause_key = keys_cfg.get("pause", pause_key)
  5850. map_key = keys_cfg.get("map", map_key)
  5851. js_cfg = cfg.get("joystick", {})
  5852. left_js = [[tuple(j) for j in js] for js in js_cfg.get("left", left_js)]
  5853. right_js = [[tuple(j) for j in js] for js in js_cfg.get("right", right_js)]
  5854. up_js = [[tuple(j) for j in js] for js in js_cfg.get("up", up_js)]
  5855. down_js = [[tuple(j) for j in js] for js in js_cfg.get("down", down_js)]
  5856. aim_diag_js = [[tuple(j) for j in js]
  5857. for js in js_cfg.get("aim_diag", aim_diag_js)]
  5858. jump_js = [[tuple(j) for j in js] for js in js_cfg.get("jump", jump_js)]
  5859. shoot_js = [[tuple(j) for j in js] for js in js_cfg.get("shoot", shoot_js)]
  5860. aim_up_js = [[tuple(j) for j in js]
  5861. for js in js_cfg.get("aim_up", aim_up_js)]
  5862. aim_down_js = [[tuple(j) for j in js]
  5863. for js in js_cfg.get("aim_down", aim_down_js)]
  5864. mode_reset_js = [[tuple(j) for j in js]
  5865. for js in js_cfg.get("mode_reset", mode_reset_js)]
  5866. mode_js = [[tuple(j) for j in js] for js in js_cfg.get("mode", mode_js)]
  5867. pause_js = [[tuple(j) for j in js] for js in js_cfg.get("pause", pause_js)]
  5868. map_js = [[tuple(j) for j in js] for js in js_cfg.get("map", map_js)]
  5869. set_gui_controls()
  5870. try:
  5871. with open(os.path.join(CONFIG, "save_slots.json")) as f:
  5872. loaded_slots = json.load(f)
  5873. except (IOError, ValueError):
  5874. pass
  5875. else:
  5876. for i in six.moves.range(min(len(loaded_slots), len(save_slots))):
  5877. slot = loaded_slots[i]
  5878. if slot is not None and slot.get("save_format", 0) > 0:
  5879. save_slots[i] = slot
  5880. else:
  5881. save_slots[i] = None
  5882. print(_("Starting game..."))
  5883. try:
  5884. sge.game.start()
  5885. finally:
  5886. write_to_disk()