12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001 |
- #!/usr/bin/env python
- # Hexoshi
- # Copyright (C) 2014-2018 Julie Marchant <onpon4@riseup.net>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- from __future__ import division
- from __future__ import absolute_import
- from __future__ import print_function
- from __future__ import unicode_literals
- __version__ = "0.2.1a0"
- import argparse
- import datetime
- import gettext
- import itertools
- import json
- import math
- import os
- import random
- import sys
- import time
- import traceback
- import warnings
- import weakref
- import sge
- print(sge.IMPLEMENTATION, sge.__version__)
- import six
- import xsge_gui
- print("xsge_gui", xsge_gui.__version__)
- import xsge_lighting
- print("xsge_lighting", xsge_lighting.__version__)
- import xsge_particle
- print("xsge_particle", xsge_particle.__version__)
- import xsge_path
- print("xsge_path", xsge_path.__version__)
- import xsge_physics
- print("xsge_physics", xsge_physics.__version__)
- import xsge_tmx
- print("xsge_tmx", xsge_tmx.__version__)
- if getattr(sys, "frozen", False):
- __file__ = sys.executable
- DATA = os.path.join(os.path.dirname(__file__), "data")
- CONFIG = os.path.join(os.path.expanduser("~"), ".config", "hexoshi")
- SCREEN_SIZE = [400, 224]
- TILE_SIZE = 16
- FPS = 60
- DELTA_MIN = FPS / 2
- DELTA_MAX = FPS * 4
- SCALE = 2
- FSSCALE = None
- if six.PY2:
- gettext.install("hexoshi", os.path.abspath(os.path.join(DATA, "locale")),
- unicode=True)
- else:
- gettext.install("hexoshi", os.path.abspath(os.path.join(DATA, "locale")))
- parser = argparse.ArgumentParser(prog="Hexoshi")
- parser.add_argument(
- "--version", action="version", version="%(prog)s " + __version__,
- help=_("Output version information and exit."))
- parser.add_argument(
- "-p", "--print-errors",
- help=_("Print errors directly to stdout rather than saving them in a file."),
- action="store_true")
- parser.add_argument(
- "-l", "--lang",
- help=_("Manually choose a different language to use."))
- parser.add_argument(
- "--nodelta",
- help=_("Disable delta timing. Causes the game to slow down when it can't run at full speed instead of becoming choppier."),
- action="store_true")
- parser.add_argument(
- "-d", "--datadir",
- help=_('Where to load the game data from (Default: "{}")').format(DATA))
- parser.add_argument(
- "--scale",
- help=_('The scale factor to use by default in windowed mode (Default: "{}")').format(SCALE))
- parser.add_argument(
- "--fsscale",
- 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(
- w=SCREEN_SIZE[0], h=SCREEN_SIZE[1],
- ex=min(640 / SCREEN_SIZE[0], 480 / SCREEN_SIZE[1])))
- parser.add_argument(
- "--no-backgrounds",
- help=_("Only show solid colors for backgrounds (uses less RAM)."),
- action="store_true")
- parser.add_argument(
- "--no-hud", help=_("Don't show the player's heads-up display."),
- action="store_true")
- parser.add_argument(
- "-m", "--gen-map", help=_("Generate the map even if it already exists."),
- action="store_true")
- parser.add_argument(
- "-s", "--save-map", help=_('Save an image of the full map as "map.png".'),
- action="store_true")
- parser.add_argument("--god")
- args = parser.parse_args()
- PRINT_ERRORS = args.print_errors
- DELTA = not args.nodelta
- if args.datadir:
- DATA = args.datadir
- if args.scale:
- SCALE = eval(args.scale)
- if args.fsscale:
- FSSCALE = eval(args.fsscale)
- NO_BACKGROUNDS = args.no_backgrounds
- NO_HUD = args.no_hud
- GEN_MAP = args.gen_map
- SAVE_MAP = args.save_map
- GOD = (args.god and args.god.lower() == "inbailey")
- if args.lang:
- lang = gettext.translation("hexoshi",
- os.path.abspath(os.path.join(DATA, "locale")),
- [args.lang])
- if six.PY2:
- lang.install(unicode=True)
- else:
- lang.install()
- GRAVITY = 0.4
- PLAYER_MAX_HP = 50
- PLAYER_MAX_SPEED = 3
- PLAYER_ROLL_MAX_SPEED = 8
- PLAYER_ACCELERATION = 0.5
- PLAYER_ROLL_ACCELERATION = 0.25
- PLAYER_AIR_ACCELERATION = 0.15
- PLAYER_FRICTION = 0.75
- PLAYER_ROLL_FRICTION = 0.02
- PLAYER_AIR_FRICTION = 0.02
- PLAYER_JUMP_HEIGHT = 5 * TILE_SIZE + 2
- PLAYER_FALL_SPEED = 7
- PLAYER_SLIDE_SPEED = 0.25
- PLAYER_ROLL_SLIDE_SPEED = 0
- PLAYER_ROLL_SLOPE_ACCELERATION = 0.25
- PLAYER_HITSTUN = FPS
- PLAYER_AIM_LOCK_TIME = FPS / 2
- WARP_TIME = FPS / 10
- DEATH_TIME = 3 * FPS
- DOUBLETAP_TIME = FPS / 3
- ANNEROY_BALL_BOUNCE_HEIGHT = 2
- ANNEROY_BALL_FORCE_BOUNCE_SPEED = 4
- ANNEROY_WALLJUMP_HEIGHT = 3 * TILE_SIZE
- ANNEROY_WALLJUMP_SPEED = PLAYER_MAX_SPEED
- ANNEROY_WALLJUMP_FRAME_TIME = FPS / 4
- ANNEROY_RUN_FRAMES_PER_PIXEL = 1 / 10
- ANNEROY_BALL_FRAMES_PER_PIXEL = 1 / 4
- ANNEROY_BBOX_X = -7
- ANNEROY_BBOX_WIDTH = 14
- ANNEROY_STAND_BBOX_Y = -16
- ANNEROY_STAND_BBOX_HEIGHT = 40
- ANNEROY_CROUCH_BBOX_Y = -5
- ANNEROY_CROUCH_BBOX_HEIGHT = 29
- ANNEROY_BALL_BBOX_Y = 10
- ANNEROY_BALL_BBOX_HEIGHT = 14
- ANNEROY_HEDGEHOG_TIME = 15
- ANNEROY_HEDGEHOG_FRAME_TIME = 4
- ANNEROY_HEDGEHOG_BBOX_X = -14
- ANNEROY_HEDGEHOG_BBOX_Y = 3
- ANNEROY_HEDGEHOG_BBOX_WIDTH = 28
- ANNEROY_HEDGEHOG_BBOX_HEIGHT = 28
- ANNEROY_SLOTH_MAX_SPEED = 0.5
- ANNEROY_BULLET_SPEED = 8
- ANNEROY_BULLET_DSPEED = ANNEROY_BULLET_SPEED * math.sin(math.radians(45))
- ANNEROY_BULLET_LIFE = 45
- ANNEROY_EXPLODE_TIME = 0.6 * FPS
- ANNEROY_DECOMPRESS_LAX = 4
- MANTANOID_WANDER_SPEED = 1
- MANTANOID_WANDER_INTERVAL = FPS * 2
- MANTANOID_APPROACH_SPEED = 1.5
- MANTANOID_APPROACH_INTERVAL = FPS / 4
- MANTANOID_HOP_HEIGHT = 2 * TILE_SIZE
- MANTANOID_JUMP_HEIGHT = 4 * TILE_SIZE
- MANTANOID_WALK_FRAMES_PER_PIXEL = 1 / 6
- MANTANOID_LEVEL_DISTANCE = 48
- MANTANOID_SLASH_DISTANCE = 30
- MANTANOID_SLASH2_DISTANCE = 44
- MANTANOID_BBOX_X = -12
- MANTANOID_BBOX_Y = -16
- MANTANOID_BBOX_WIDTH = 24
- MANTANOID_BBOX_HEIGHT = 48
- MANTANOID_SLASH_BBOX_X = -12
- MANTANOID_SLASH_BBOX_Y = -27
- MANTANOID_SLASH_BBOX_WIDTH = 38
- MANTANOID_SLASH_BBOX_HEIGHT = 59
- MANTANOID_DOUBLESLASH_OFFSET = 18
- MANTANOID_SLASH2_BBOX_X = 0
- MANTANOID_SLASH2_BBOX_Y = -20
- MANTANOID_SLASH2_BBOX_WIDTH = 24
- MANTANOID_SLASH2_BBOX_HEIGHT = 32
- SCORPION_WALK_FRAMES_PER_PIXEL = 1 / 6
- CEILING_LAX = 2
- CAMERA_HSPEED_FACTOR = 1 / 8
- CAMERA_VSPEED_FACTOR = 1 / 20
- CAMERA_OFFSET_FACTOR = 10
- CAMERA_MARGIN_TOP = 3 * TILE_SIZE
- CAMERA_MARGIN_BOTTOM = 3 * TILE_SIZE
- CAMERA_TARGET_MARGIN_BOTTOM = SCREEN_SIZE[1] / 2
- LIFE_FORCE_CHANCE = 0.25
- LIFE_FORCE_SPEED = 1
- LIFE_FORCE_HEAL = 5
- LIGHT_RANGE = 300
- SHAKE_FRAME_TIME = FPS / DELTA_MIN
- SHAKE_AMOUNT = 3
- MAP_CELL_WIDTH = 8
- MAP_CELL_HEIGHT = 8
- TEXT_SPEED = 1000
- SAVE_NSLOTS = 10
- MENU_MAX_ITEMS = 14
- SOUND_MAX_RADIUS = 200
- SOUND_ZERO_RADIUS = 600
- SOUND_CENTERED_RADIUS = 75
- SOUND_TILTED_RADIUS = 500
- SOUND_TILT_LIMIT = 0.75
- ETANK_CHAR = '\x80'
- backgrounds = {}
- loaded_music = {}
- fullscreen = False
- scale_method = None
- sound_enabled = True
- music_enabled = True
- stereo_enabled = True
- fps_enabled = False
- metroid_controls = False
- joystick_threshold = 0.1
- left_key = [["left", "a"]]
- right_key = [["right", "d"]]
- up_key = [["up", "w"]]
- down_key = [["down", "s"]]
- aim_diag_key = [["alt_left", "alt_right"]]
- jump_key = [["space"]]
- shoot_key = [["ctrl_left", "ctrl_right"]]
- aim_up_key = [["x"]]
- aim_down_key = [["z"]]
- mode_reset_key = [["shift_left", "shift_right"]]
- mode_key = [["tab"]]
- pause_key = [["enter", "p"]]
- map_key = [["m"]]
- left_js = [[(0, "axis-", 0), (0, "hat_left", 0)]]
- right_js = [[(0, "axis+", 0), (0, "hat_right", 0)]]
- up_js = [[(0, "axis-", 1), (0, "hat_up", 0)]]
- down_js = [[(0, "axis+", 1), (0, "hat_down", 0)]]
- aim_diag_js = [[(0, "button", 10), (0, "button", 11)]]
- jump_js = [[(0, "button", 1), (0, "button", 3)]]
- shoot_js = [[(0, "button", 0)]]
- aim_up_js = [[(0, "button", 5), (0, "button", 7)]]
- aim_down_js = [[(0, "button", 4), (0, "button", 6)]]
- mode_reset_js = [[(0, "button", 2)]]
- mode_js = [[(0, "button", 8)]]
- pause_js = [[(0, "button", 9)]]
- map_js = [[]]
- save_slots = [None for i in six.moves.range(SAVE_NSLOTS)]
- with open(os.path.join(DATA, "ai_data.json"), 'r') as f:
- ai_data = set(json.load(f))
- abort = False
- current_save_slot = None
- player_name = "Anneroy"
- watched_timelines = []
- current_level = None
- spawn_point = None
- map_revealed = []
- map_explored = []
- map_removed = []
- warp_pads = []
- powerups = []
- progress_flags = []
- artifacts = 0
- etanks = 0
- time_taken = 0
- spawn_xoffset = 0
- spawn_yoffset = 0
- player = None
- class Game(sge.dsp.Game):
- fps_real = FPS
- fps_time = 0
- fps_frames = 0
- fps_text = ""
- cheatcode = ""
- def event_step(self, time_passed, delta_mult):
- self.fps_time += time_passed
- self.fps_frames += 1
- if self.fps_time >= 250:
- self.fps_real = (1000 * self.fps_frames) / self.fps_time
- self.fps_text = '{:.2f}'.format(self.fps_real)
- self.fps_time = 0
- self.fps_frames = 0
- if fps_enabled:
- self.project_text(font_small, self.fps_text, self.width - 8,
- self.height - 8, z=1000000,
- color=sge.gfx.Color("yellow"), halign="right",
- valign="bottom")
- def event_key_press(self, key, char):
- if key == "f7":
- self.cheatcode = ""
- elif sge.keyboard.get_pressed("f7"):
- if not self.cheatcode:
- print(_("Code entry:"), end=' ')
- self.cheatcode += char
- print(char, end='')
- sys.stdout.flush()
- def event_key_release(self, key):
- global map_revealed
- global map_explored
- if key == "f7":
- if self.cheatcode:
- print()
- if self.cheatcode.lower() == "knowitall":
- map_revealed = list(map_objects.keys())
- elif self.cheatcode.lower() == "seenitall":
- map_explored = map_revealed
- elif self.cheatcode.startswith("tele"):
- warp(self.cheatcode[4:] + ".tmx")
- else:
- print(_("Invalid cheat code: {}").format(self.cheatcode))
- def event_mouse_button_press(self, button):
- if button == "middle":
- self.event_close()
- def event_close(self):
- self.end()
- def event_paused_close(self):
- self.event_close()
- class Level(sge.dsp.Room):
- """Handles levels."""
- def __init__(self, objects=(), width=None, height=None, views=None,
- background=None, background_x=0, background_y=0,
- object_area_width=TILE_SIZE * 2,
- object_area_height=TILE_SIZE * 2,
- name=None, bgname=None, music=None, timeline=None,
- ambient_light=None, disable_lights=False,
- music_noloop=False):
- self.fname = None
- self.name = name
- self.music = music
- self.music_noloop = music_noloop
- self.timeline_objects = {}
- self.shake_queue = 0
- self.death_time = None
- self.status_text = None
- self.player_z = 0
- if bgname is not None:
- background = backgrounds.get(bgname, background)
- self.load_timeline(timeline)
- if ambient_light:
- self.ambient_light = sge.gfx.Color(ambient_light)
- if (self.ambient_light.red >= 255 and
- self.ambient_light.green >= 255 and
- self.ambient_light.blue >= 255):
- self.ambient_light = None
- else:
- self.ambient_light = None
- self.disable_lights = disable_lights or self.ambient_light is None
- super(Level, self).__init__(objects, width, height, views, background,
- background_x, background_y,
- object_area_width, object_area_height)
- self.add(gui_handler)
- def load_timeline(self, timeline):
- self.timeline = {}
- self.timeline_name = ""
- self.timeline_step = 0
- self.timeline_skip_target = None
- if timeline:
- self.timeline_name = timeline
- fname = os.path.join(DATA, "timelines", timeline)
- with open(fname, 'r') as f:
- jt = json.load(f)
- for i in jt:
- self.timeline[eval(i)] = jt[i]
- def add_timeline_object(self, obj):
- if obj.ID is not None:
- self.timeline_objects[obj.ID] = weakref.ref(obj)
- def timeline_skipto(self, step):
- t_keys = sorted(self.timeline.keys())
- self.timeline_step = step
- while t_keys and t_keys[0] < step:
- i = t_keys.pop(0)
- self.timeline[i] = []
- def show_hud(self):
- # Show darkness
- if self.ambient_light:
- xsge_lighting.project_darkness(ambient_light=self.ambient_light,
- buffer=TILE_SIZE * 2)
- else:
- xsge_lighting.clear_lights()
- if not NO_HUD:
- # TODO: Add HUD showing health, ammo, etc.
- if self.status_text:
- sge.game.project_text(font, self.status_text,
- sge.game.width / 2, sge.game.height - 16,
- color=sge.gfx.Color("white"),
- halign="center", valign="middle")
- self.status_text = None
- def shake(self, num=1):
- shaking = (self.shake_queue or "shake_up" in self.alarms or
- "shake_down" in self.alarms)
- self.shake_queue = max(self.shake_queue, num)
- if not shaking:
- self.event_alarm("shake_down")
- for obj in self.objects:
- if isinstance(obj, SteadyIcicle):
- obj.check_shake(True)
- def pause(self, player_x=None, player_y=None):
- if (self.timeline_skip_target is not None and
- self.timeline_step < self.timeline_skip_target):
- self.timeline_skipto(self.timeline_skip_target)
- else:
- play_sound(pause_sound)
- PauseMenu.create(player_x=player_x, player_y=player_y)
- def die(self):
- sge.game.start_room.start(transition="fade")
- def win_game(self):
- credits_room = CreditsScreen.load(os.path.join("special",
- "credits.tmx"))
- credits_room.start()
- def event_room_start(self):
- if player is not None:
- self.add(player)
- ##self.add(lava_animation)
- xsge_lighting.clear_lights()
- play_music(self.music, noloop=self.music_noloop)
- def event_room_resume(self):
- play_music(self.music, noloop=self.music_noloop)
- def event_step(self, time_passed, delta_mult):
- global watched_timelines
- global time_taken
- time_taken += time_passed / 1000
- for view in self.views:
- for obj in self.get_objects_at(
- view.x - LIGHT_RANGE, view.y - LIGHT_RANGE,
- view.width + LIGHT_RANGE * 2,
- view.height + LIGHT_RANGE * 2):
- if isinstance(obj, InteractiveObject):
- if not self.disable_lights:
- obj.project_light()
- ##if not obj.active:
- ##if isinstance(obj, (Lava, LavaSurface)):
- ## obj.image_index = lava_animation.image_index
- # Show HUD
- self.show_hud()
- # Timeline events
- t_keys = sorted(self.timeline.keys())
- while t_keys:
- i = t_keys.pop(0)
- if i <= self.timeline_step:
- while i in self.timeline and self.timeline[i]:
- command = self.timeline[i].pop(0)
- command = command.split(None, 1)
- if command:
- if len(command) >= 2:
- command, arg = command[:2]
- else:
- command = command[0]
- arg = ""
- if command.startswith("#"):
- # Comment; do nothing
- pass
- elif command == "setattr":
- args = arg.split(None, 2)
- if len(args) >= 3:
- obj, name, value = args[:3]
- try:
- value = eval(value)
- except Exception as e:
- m = _("An error occurred in a timeline 'setattr' command:\n\n{}").format(
- traceback.format_exc())
- show_error(m)
- else:
- if obj in self.timeline_objects:
- obj = self.timeline_objects[obj]()
- if obj is not None:
- setattr(obj, name, value)
- elif obj == "__level__":
- setattr(self, name, value)
- elif command == "call":
- args = arg.split()
- if len(args) >= 2:
- obj, method = args[:2]
- fa = [eval(s) for s in args[2:]]
- if obj in self.timeline_objects:
- obj = self.timeline_objects[obj]()
- if obj is not None:
- getattr(obj, method, lambda: None)(*fa)
- elif obj == "__level__":
- getattr(self, method, lambda: None)(*fa)
- elif command == "dialog":
- DialogBox(gui_handler, _(arg)).show()
- elif command == "play_music":
- self.music = arg
- play_music(arg)
- elif command == "timeline":
- if self.timeline_name not in watched_timelines:
- watched_timelines = watched_timelines[:]
- watched_timelines.append(self.timeline_name)
- self.load_timeline(arg)
- break
- elif command == "skip_to":
- try:
- arg = float(arg)
- except ValueError:
- pass
- else:
- self.timeline_skipto(arg)
- break
- elif command == "exec":
- try:
- six.exec_(arg)
- except Exception as e:
- m = _("An error occurred in a timeline 'exec' command:\n\n{}").format(
- traceback.format_exc())
- show_error(m)
- elif command == "if":
- try:
- r = eval(arg)
- except Exception as e:
- m = _("An error occurred in a timeline 'if' statement:\n\n{}").format(
- traceback.format_exc())
- show_error(m)
- r = False
- finally:
- if not r:
- self.timeline[i] = []
- break
- elif command == "if_watched":
- if self.timeline_name not in watched_timelines:
- self.timeline[i] = []
- break
- elif command == "if_not_watched":
- if self.timeline_name in watched_timelines:
- self.timeline[i] = []
- break
- elif command == "while":
- try:
- r = eval(arg)
- except Exception as e:
- m = _("An error occurred in a timeline 'while' statement:\n\n{}").format(
- traceback.format_exc())
- show_error(m)
- r = False
- finally:
- if r:
- cur_timeline = self.timeline[i][:]
- while_command = "while {}".format(arg)
- self.timeline[i].insert(0, while_command)
- t_keys.insert(0, i)
- self.timeline[i - 1] = cur_timeline
- self.timeline[i] = loop_timeline
- i -= 1
- self.timeline_step -= 1
- else:
- self.timeline[i] = []
- break
- else:
- del self.timeline[i]
- else:
- break
- else:
- if (self.timeline_name and
- self.timeline_name not in watched_timelines):
- watched_timelines = watched_timelines[:]
- watched_timelines.append(self.timeline_name)
- self.timeline_name = ""
- self.timeline_step += delta_mult
- def event_paused_step(self, time_passed, delta_mult):
- # Handle lighting
- for view in self.views:
- for obj in self.get_objects_at(
- view.x - LIGHT_RANGE, view.y - LIGHT_RANGE,
- view.width + LIGHT_RANGE * 2,
- view.height + LIGHT_RANGE * 2):
- if isinstance(obj, InteractiveObject):
- if not self.disable_lights:
- obj.project_light()
- self.show_hud()
- def event_alarm(self, alarm_id):
- if alarm_id == "shake_down":
- self.shake_queue -= 1
- for view in self.views:
- view.yport += SHAKE_AMOUNT
- self.alarms["shake_up"] = SHAKE_FRAME_TIME
- elif alarm_id == "shake_up":
- for view in self.views:
- view.yport -= SHAKE_AMOUNT
- if self.shake_queue:
- self.alarms["shake_down"] = SHAKE_FRAME_TIME
- elif alarm_id == "death":
- self.die()
- @classmethod
- def load(cls, fname, show_prompt=False):
- if show_prompt:
- text = _("Loading data...")
- if sge.game.current_room is not None:
- x = sge.game.width / 2
- y = sge.game.height / 2
- w = font.get_width(text) + 32
- h = font.get_height(text) + 32
- sge.game.project_rectangle(x - w / 2, y - h / 2, w, h,
- fill=sge.gfx.Color("black"))
- sge.game.project_text(font, text, x, y,
- color=sge.gfx.Color("white"),
- halign="center", valign="middle")
- sge.game.refresh()
- else:
- print(_("Loading \"{}\"...").format(fname))
- try:
- r = xsge_tmx.load(os.path.join(DATA, "rooms", fname), cls=cls,
- types=TYPES)
- except Exception as e:
- m = _("An error occurred when trying to load the level:\n\n{}").format(
- traceback.format_exc())
- show_error(m)
- r = None
- else:
- r.fname = fname
- return r
- class SpecialScreen(Level):
- def event_room_start(self):
- super(SpecialScreen, self).event_room_start()
- if player is not None:
- player.destroy()
- class TitleScreen(SpecialScreen):
- def show_hud(self):
- pass
- def event_room_start(self):
- super(TitleScreen, self).event_room_start()
- MainMenu.create()
- def event_room_resume(self):
- super(TitleScreen, self).event_room_resume()
- MainMenu.create()
- def event_key_press(self, key, char):
- pass
- class CreditsScreen(SpecialScreen):
- def event_room_start(self):
- super(CreditsScreen, self).event_room_start()
- with open(os.path.join(DATA, "credits.json"), 'r') as f:
- sections = json.load(f)
- logo_section = sge.dsp.Object.create(self.width / 2, self.height,
- sprite=logo_sprite,
- tangible=False)
- self.sections = [logo_section]
- for section in sections:
- if "title" in section:
- head_sprite = sge.gfx.Sprite.from_text(
- font_big, section["title"], width=self.width,
- color=sge.gfx.Color("white"), halign="center")
- x = self.width / 2
- y = self.sections[-1].bbox_bottom + font_big.size * 3
- head_section = sge.dsp.Object.create(x, y, sprite=head_sprite,
- tangible=False)
- self.sections.append(head_section)
- if "lines" in section:
- for line in section["lines"]:
- list_sprite = sge.gfx.Sprite.from_text(
- font, line, width=self.width - 2 * TILE_SIZE,
- color=sge.gfx.Color("white"), halign="center")
- x = self.width / 2
- y = self.sections[-1].bbox_bottom + font.size
- list_section = sge.dsp.Object.create(
- x, y, sprite=list_sprite, tangible=False)
- self.sections.append(list_section)
- for obj in self.sections:
- obj.yvelocity = -0.2
- def event_step(self, time_passed, delta_mult):
- if self.sections[0].yvelocity > 0 and self.sections[0].y > self.height:
- for obj in self.sections:
- obj.yvelocity = 0
- if self.sections[-1].bbox_bottom < 0 and "end" not in self.alarms:
- sge.snd.Music.stop(fade_time=3000)
- self.alarms["end"] = 3.5 * FPS
- def event_alarm(self, alarm_id):
- if alarm_id == "end":
- sge.game.start_room.start()
- def event_key_press(self, key, char):
- if key in itertools.chain.from_iterable(down_key):
- if "end" not in self.alarms:
- for obj in self.sections:
- obj.yvelocity -= 0.1
- elif key in itertools.chain.from_iterable(up_key):
- if "end" not in self.alarms:
- for obj in self.sections:
- obj.yvelocity += 0.1
- elif (key in itertools.chain.from_iterable(jump_key) or
- key in itertools.chain.from_iterable(shoot_key) or
- key in itertools.chain.from_iterable(pause_key)):
- sge.game.start_room.start()
- def event_joystick(self, js_name, js_id, input_type, input_id, value):
- js = (js_id, input_type, input_id)
- if value >= joystick_threshold:
- if js in itertools.chain.from_iterable(down_js):
- if "end" not in self.alarms:
- for obj in self.sections:
- obj.yvelocity -= 0.1
- elif js in itertools.chain.from_iterable(up_js):
- if "end" not in self.alarms:
- for obj in self.sections:
- obj.yvelocity += 0.1
- elif (js in itertools.chain.from_iterable(jump_js) or
- js in itertools.chain.from_iterable(shoot_js) or
- js in itertools.chain.from_iterable(pause_js)):
- sge.game.start_room.start()
- class SolidLeft(xsge_physics.SolidLeft):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SolidLeft, self).__init__(*args, **kwargs)
- class SolidRight(xsge_physics.SolidRight):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SolidRight, self).__init__(*args, **kwargs)
- class SolidTop(xsge_physics.SolidTop):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SolidTop, self).__init__(*args, **kwargs)
- class SolidBottom(xsge_physics.SolidBottom):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SolidBottom, self).__init__(*args, **kwargs)
- class Solid(xsge_physics.Solid):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(Solid, self).__init__(*args, **kwargs)
- class SlopeTopLeft(xsge_physics.SlopeTopLeft):
- xsticky_top = True
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SlopeTopLeft, self).__init__(*args, **kwargs)
- def event_create(self):
- self.slope_xacceleration = -self.bbox_height / self.bbox_width
- class SlopeTopRight(xsge_physics.SlopeTopRight):
- xsticky_top = True
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SlopeTopRight, self).__init__(*args, **kwargs)
- def event_create(self):
- self.slope_xacceleration = self.bbox_height / self.bbox_width
- class SlopeBottomLeft(xsge_physics.SlopeBottomLeft):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SlopeBottomLeft, self).__init__(*args, **kwargs)
- class SlopeBottomRight(xsge_physics.SlopeBottomRight):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(SlopeBottomRight, self).__init__(*args, **kwargs)
- class MovingPlatform(xsge_physics.SolidTop, xsge_physics.MobileWall):
- sticky_top = True
- def __init__(self, x, y, z=0, **kwargs):
- kwargs.setdefault("sprite", platform_sprite)
- super(MovingPlatform, self).__init__(x, y, z, **kwargs)
- self.path = None
- self.following = False
- def event_step(self, time_passed, delta_mult):
- super(MovingPlatform, self).event_step(time_passed, delta_mult)
- if self.path and not self.following:
- for other in self.collision(Player, y=(self.y - 1)):
- if self in other.get_bottom_touching_wall():
- self.path.follow_start(self, self.path.path_speed,
- accel=self.path.path_accel,
- decel=self.path.path_decel,
- loop=self.path.path_loop)
- break
- class HurtLeft(SolidLeft):
- pass
- class HurtRight(SolidRight):
- pass
- class HurtTop(SolidTop):
- pass
- class HurtBottom(SolidBottom):
- pass
- class SpikeLeft(HurtLeft, xsge_physics.Solid):
- pass
- class SpikeRight(HurtRight, xsge_physics.Solid):
- pass
- class SpikeTop(HurtTop, xsge_physics.Solid):
- pass
- class SpikeBottom(HurtBottom, xsge_physics.Solid):
- pass
- class Death(sge.dsp.Object):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(Death, self).__init__(*args, **kwargs)
- class Player(xsge_physics.Collider):
- name = "Ian C."
- max_hp = PLAYER_MAX_HP
- max_speed = PLAYER_MAX_SPEED
- roll_max_speed = PLAYER_ROLL_MAX_SPEED
- acceleration = PLAYER_ACCELERATION
- roll_acceleration = PLAYER_ROLL_ACCELERATION
- air_acceleration = PLAYER_AIR_ACCELERATION
- friction = PLAYER_FRICTION
- roll_friction = PLAYER_ROLL_FRICTION
- air_friction = PLAYER_AIR_FRICTION
- jump_height = PLAYER_JUMP_HEIGHT
- gravity = GRAVITY
- fall_speed = PLAYER_FALL_SPEED
- slide_speed = PLAYER_SLIDE_SPEED
- roll_slide_speed = PLAYER_ROLL_SLIDE_SPEED
- roll_slope_acceleration = PLAYER_ROLL_SLOPE_ACCELERATION
- hitstun_time = PLAYER_HITSTUN
- can_move = True
- @property
- def slope_acceleration(self):
- if self.rolling:
- return self.roll_slope_acceleration
- else:
- return 0
- @slope_acceleration.setter
- def slope_acceleration(self, value):
- pass
- @property
- def hp(self):
- return self.__hp
- @hp.setter
- def hp(self, value):
- self.__hp = value
- while self.__hp > self.max_hp and self.etanks_used > 0:
- self.etanks_used -= 1
- self.__hp -= self.max_hp
- while self.__hp <= 0 and self.etanks_used < etanks:
- self.etanks_used += 1
- self.__hp += self.max_hp
- self.__hp = min(self.__hp, self.max_hp)
- if self.__hp > 0:
- new_w = healthbar_width * self.__hp / self.max_hp
- healthbar_front_sprite.width = new_w
- self.update_hud()
- @property
- def camera_target_x(self):
- guides = self.collision(CameraXGuide)
- if guides:
- return guides[0].x
- else:
- return (self.x - self.view.width / 2 +
- self.xvelocity * CAMERA_OFFSET_FACTOR)
- @property
- def camera_target_y(self):
- guides = self.collision(CameraYGuide)
- if guides:
- self.camera_guided_y = True
- return guides[0].y
- else:
- self.camera_guided_y = False
- return self.y - self.view.height + CAMERA_TARGET_MARGIN_BOTTOM
- @property
- def aim_lock(self):
- return "aim_lock" in self.alarms
- @aim_lock.setter
- def aim_lock(self, value):
- if value:
- self.alarms["aim_lock"] = PLAYER_AIM_LOCK_TIME
- elif "aim_lock" in self.alarms:
- del self.alarms["aim_lock"]
- def __init__(self, x, y, z=0, sprite=None, visible=True, active=True,
- checks_collisions=True, tangible=True, bbox_x=8, bbox_y=0,
- bbox_width=16, bbox_height=16, regulate_origin=True,
- collision_ellipse=False, collision_precise=False, xvelocity=0,
- yvelocity=0, xacceleration=0, yacceleration=0,
- xdeceleration=0, ydeceleration=0, image_index=0,
- image_origin_x=None, image_origin_y=None, image_fps=None,
- image_xscale=1, image_yscale=1, image_rotation=0,
- image_alpha=255, image_blend=None, ID="player", player=0,
- human=True, lose_on_death=True, view_frozen=False):
- self.ID = ID
- self.player = player
- self.human = human
- self.lose_on_death = lose_on_death
- self.view_frozen = view_frozen
- self.input_lock = False
- self.warp_dest = None
- self.hud_sprite = sge.gfx.Sprite(width=SCREEN_SIZE[0],
- height=SCREEN_SIZE[1])
- self.reset_input()
- self.etanks_used = 0
- self.hitstun = False
- self.invincible = False
- self.facing = 1
- self.has_jumped = False
- self.current_mode = None
- self.rolling = False
- self.aim_direction = None
- self.aim_direction_time = 0
- self.view = None
- self.__hp = self.max_hp
- healthbar_front_sprite.width = healthbar_width
- self.last_xr = None
- self.last_yr = None
- self.camera_guided_y = False
- if GOD:
- image_blend = sge.gfx.Color("olive")
- super(Player, self).__init__(
- x, y, z=z, sprite=sprite, visible=visible, active=active,
- checks_collisions=checks_collisions, tangible=tangible,
- bbox_x=bbox_x, bbox_y=bbox_y, bbox_width=bbox_width,
- bbox_height=bbox_height, regulate_origin=regulate_origin,
- collision_ellipse=collision_ellipse,
- collision_precise=collision_precise, xvelocity=xvelocity,
- yvelocity=yvelocity, xacceleration=xacceleration,
- yacceleration=yacceleration, xdeceleration=xdeceleration,
- ydeceleration=ydeceleration, image_index=image_index,
- image_origin_x=image_origin_x, image_origin_y=image_origin_y,
- image_fps=image_fps, image_xscale=image_xscale,
- image_yscale=image_yscale, image_rotation=image_rotation,
- image_alpha=image_alpha, image_blend=image_blend)
- def reset_input(self):
- self.left_pressed = False
- self.right_pressed = False
- self.up_pressed = False
- self.down_pressed = False
- self.jump_pressed = False
- self.shoot_pressed = False
- self.aim_diag_pressed = False
- self.aim_up_pressed = False
- self.aim_down_pressed = False
- self.mode_pressed = False
- self.mode_reset_pressed = False
- def refresh_input(self):
- if self.human and not self.input_lock:
- key_controls = [left_key, right_key, up_key, down_key, aim_diag_key,
- jump_key, shoot_key, aim_up_key, aim_down_key,
- mode_key, mode_reset_key]
- js_controls = [left_js, right_js, up_js, down_js, aim_diag_js,
- jump_js, shoot_js, aim_up_js, aim_down_js, mode_js,
- mode_reset_js]
- states = [0 for i in key_controls]
- for i in six.moves.range(len(key_controls)):
- for choice in key_controls[i][self.player]:
- value = sge.keyboard.get_pressed(choice)
- states[i] = max(states[i], value)
- for i in six.moves.range(len(js_controls)):
- for choice in js_controls[i][self.player]:
- j, t, c = choice
- value = min(sge.joystick.get_value(j, t, c), 1)
- if value >= joystick_threshold:
- states[i] = max(states[i], value)
- self.left_pressed = states[0]
- self.right_pressed = states[1]
- self.up_pressed = states[2]
- self.down_pressed = states[3]
- self.aim_diag_pressed = states[4]
- self.jump_pressed = states[5]
- self.shoot_pressed = states[6]
- self.aim_up_pressed = states[7]
- self.aim_down_pressed = states[8]
- self.mode_pressed = states[9]
- self.mode_reset_pressed = states[10]
- def press_up(self):
- if not self.aim_diag_pressed:
- warp_pad_objs = self.collision(WarpPad)
- if warp_pad_objs:
- warp_pad = warp_pad_objs[0]
- warp_pad.teleport(self)
- def press_down(self):
- pass
- def jump(self):
- if self.on_floor or self.was_on_floor:
- self.has_jumped = True
- self.yvelocity = get_jump_speed(self.jump_height, self.gravity)
- self.on_floor = []
- self.was_on_floor = []
- self.event_jump()
- def jump_release(self):
- if self.has_jumped and self.yvelocity < 0:
- self.has_jumped = False
- self.yvelocity /= 2
- def shoot(self):
- pass
- def shoot_release(self):
- pass
- def mode(self):
- all_modes = [None, "compress"]
- if self.current_mode in all_modes:
- i = all_modes.index(self.current_mode)
- while True:
- i += 1
- if i >= len(all_modes):
- self.current_mode = None
- break
- elif (all_modes[i] == "compress" and
- "atomic_compressor" in progress_flags):
- self.current_mode = all_modes[i]
- break
- else:
- self.current_mode = None
- self.update_hud()
- play_sound(type_sound)
- def mode_reset(self):
- self.current_mode = None
- self.update_hud()
- play_sound(cancel_sound)
- def hurt(self, damage=1, touching=False):
- if not self.hitstun and not self.invincible:
- play_sound(hurt_sound, self.x, self.y)
- if not GOD:
- self.hp -= damage
- if self.hp <= 0:
- self.kill()
- else:
- self.hitstun = True
- self.image_alpha = 128
- self.alarms["hitstun"] = self.hitstun_time
- def kill(self):
- if self.lose_on_death:
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- sge.game.current_room.alarms["death"] = DEATH_TIME
- play_sound(death_sound, self.x, self.y)
- self.destroy()
- def refresh(self):
- self.hp = self.max_hp
- self.etanks_used = 0
- self.update_hud()
- def warp_in(self):
- self.input_lock = True
- self.alarms["input_lock"] = WARP_TIME
- self.reset_input()
- self.xvelocity = 0
- self.yvelocity = 0
- def warp_out(self):
- self.input_lock = True
- self.alarms["warp_out"] = WARP_TIME
- self.reset_input()
- self.xvelocity = 0
- self.yvelocity = 0
- def update_hud(self):
- self.hud_sprite.draw_clear()
- if not NO_HUD:
- start_x = 8
- start_y = 8
- x = start_x
- y = start_y
- self.hud_sprite.draw_sprite(healthbar_back_sprite, 0, x, y)
- if self.hp > 0:
- self.hud_sprite.draw_sprite(healthbar_front_sprite, 0, x, y)
- y += 8
- w = etank_empty_sprite.width
- h = etank_empty_sprite.height
- for i in six.moves.range(etanks):
- if x + w >= start_x + healthbar_width:
- x = start_x
- y += h
- if i < etanks - self.etanks_used:
- self.hud_sprite.draw_sprite(etank_full_sprite, 0, x, y)
- else:
- self.hud_sprite.draw_sprite(etank_empty_sprite, 0, x, y)
- x += w
- # Mode image
- x = start_x
- y += 12
- if self.current_mode == "compress":
- self.hud_sprite.draw_sprite(atomic_compressor_sprite, 0, x, y)
- if "map" in progress_flags:
- w = 7
- h = 5
- if sge.game.current_room.fname in map_rooms:
- rm_x, rm_y = map_rooms[sge.game.current_room.fname]
- pl_x = rm_x + get_xregion(self.x)
- pl_y = rm_y + get_yregion(self.y)
- x = pl_x - w // 2
- y = pl_y - h // 2
- else:
- x = 0
- y = 0
- pl_x = None
- pl_y = None
- map_s = draw_map(x, y, w, h, pl_x, pl_y)
- c = sge.gfx.Color((255, 255, 255, 192))
- map_s.draw_rectangle(0, 0, map_s.width, map_s.height, fill=c,
- blend_mode=sge.BLEND_RGBA_MULTIPLY)
- x = SCREEN_SIZE[0] - start_x - w * MAP_CELL_WIDTH
- y = start_y
- self.hud_sprite.draw_sprite(map_s, 0, x, y)
- self.hud_sprite.draw_rectangle(x, y, map_s.width, map_s.height,
- outline=sge.gfx.Color("white"))
-
- def show_hud(self):
- if not NO_HUD:
- sge.game.project_sprite(self.hud_sprite, 0, 0, 0)
- if not self.human:
- room = sge.game.current_room
- if (room.timeline_skip_target is not None and
- room.timeline_step < room.timeline_skip_target):
- room.status_text = _("Press the Menu button to skip...")
- else:
- room.status_text = _("Cinematic mode enabled")
- def set_image(self):
- pass
- def init_position(self):
- self.last_x = self.x
- self.last_y = self.y
- self.on_slope = self.get_bottom_touching_slope()
- self.on_floor = self.get_bottom_touching_wall() + self.on_slope
- self.was_on_floor = self.on_floor
- self.view.x = self.camera_target_x
- self.view.y = self.camera_target_y
- def event_create(self):
- self.z = sge.game.current_room.player_z
- sge.game.current_room.add_timeline_object(self)
- self.view = sge.game.current_room.views[self.player]
- for obj in sge.game.current_room.objects:
- if isinstance(obj, SpawnPoint) and obj.spawn_id == spawn_point:
- obj.spawn(self)
- break
- self.init_position()
- self.update_hud()
- def event_begin_step(self, time_passed, delta_mult):
- self.refresh_input()
- h_control = bool(self.right_pressed) - bool(self.left_pressed)
- v_control = bool(self.down_pressed) - bool(self.up_pressed)
- current_h_movement = (self.xvelocity > 0) - (self.xvelocity < 0)
- if not self.aim_lock:
- prev_aim_direction = self.aim_direction
- if "shooting" in self.alarms:
- self.aim_direction = 0
- else:
- self.aim_direction = None
- if v_control:
- if self.aim_diag_pressed or (h_control and metroid_controls):
- self.aim_direction = 1 * -v_control
- else:
- self.aim_direction = 2 * -v_control
- elif metroid_controls and self.aim_diag_pressed:
- if prev_aim_direction is not None and prev_aim_direction < 0:
- self.aim_direction = -1
- else:
- self.aim_direction = 1
- if self.aim_up_pressed and self.aim_down_pressed:
- self.aim_direction = 2
- elif self.aim_up_pressed:
- self.aim_direction = 1
- elif self.aim_down_pressed:
- self.aim_direction = -1
- if self.aim_direction == prev_aim_direction:
- self.aim_direction_time += 1
- else:
- self.aim_direction_time = 0
- self.xacceleration = 0
- self.yacceleration = 0
- self.xdeceleration = 0
- if h_control:
- if not self.can_move:
- target_speed = 0
- else:
- h_factor = abs(self.right_pressed - self.left_pressed)
- target_speed = min(h_factor * self.max_speed, self.max_speed)
- if (abs(self.xvelocity) < target_speed or
- (self.xvelocity > 0 and h_control < 0) or
- (self.xvelocity < 0 and h_control > 0)):
- if self.on_floor or self.was_on_floor:
- if self.rolling:
- self.xacceleration = self.roll_acceleration * h_control
- else:
- self.xacceleration = self.acceleration * h_control
- else:
- self.xacceleration = self.air_acceleration * h_control
- else:
- if self.on_floor or self.was_on_floor:
- if self.rolling:
- dc = self.roll_friction
- else:
- dc = self.friction
- else:
- dc = self.air_friction
- if abs(self.xvelocity) - dc * delta_mult > target_speed:
- self.xdeceleration = dc
- else:
- self.xvelocity = target_speed * current_h_movement
- if current_h_movement and h_control != current_h_movement:
- if self.on_floor or self.was_on_floor:
- if self.rolling:
- self.xdeceleration = self.roll_friction
- else:
- self.xdeceleration = self.friction
- else:
- self.xdeceleration = self.air_friction
- if not self.on_floor and not self.was_on_floor:
- if self.yvelocity < self.fall_speed:
- self.yacceleration = self.gravity
- else:
- self.yvelocity = self.fall_speed
- elif self.on_slope:
- if self.rolling:
- self.yvelocity = (self.roll_slide_speed *
- (self.on_slope[0].bbox_height /
- self.on_slope[0].bbox_width))
- elif self.xvelocity:
- self.yvelocity = (self.slide_speed *
- (self.on_slope[0].bbox_height /
- self.on_slope[0].bbox_width))
- else:
- self.yvelocity = 0
- def event_step(self, time_passed, delta_mult):
- global map_revealed
- global map_explored
- on_floor = self.get_bottom_touching_wall()
- self.on_slope = self.get_bottom_touching_slope() if not on_floor else []
- self.was_on_floor = self.on_floor
- self.on_floor = on_floor + self.on_slope
- h_control = bool(self.right_pressed) - bool(self.left_pressed)
- v_control = bool(self.down_pressed) - bool(self.up_pressed)
- for block in self.on_floor:
- if block in self.was_on_floor and isinstance(block, HurtTop):
- self.hurt()
- # Set image
- self.set_image()
- # Move view
- if self.view is not None and not self.view_frozen:
- view_target_x = self.camera_target_x
- if abs(view_target_x - self.view.x) > 0.5:
- self.view.x += ((view_target_x - self.view.x) *
- CAMERA_HSPEED_FACTOR)
- else:
- self.view.x = view_target_x
- view_min_y = self.y - self.view.height + CAMERA_MARGIN_BOTTOM
- view_max_y = self.y - CAMERA_MARGIN_TOP
- view_target_y = self.camera_target_y
- if (self.on_floor and self.was_on_floor) or self.camera_guided_y:
- if abs(view_target_y - self.view.y) > 0.5:
- self.view.y += ((view_target_y - self.view.y) *
- CAMERA_VSPEED_FACTOR)
- else:
- self.view.y = view_target_y
- if not self.camera_guided_y:
- if self.view.y < view_min_y:
- self.view.y = view_min_y
- elif self.view.y > view_max_y:
- self.view.y = view_max_y
- self.last_x = self.x
- self.last_y = self.y
- if sge.game.current_room.fname in map_rooms:
- xr, yr = map_rooms[sge.game.current_room.fname]
- xr += get_xregion(self.x)
- yr += get_yregion(self.y)
- if xr != self.last_xr or yr != self.last_yr:
- pos = (xr, yr)
- if pos not in map_explored:
- map_explored = map_explored[:]
- map_explored.append(pos)
- if pos not in map_revealed:
- map_revealed = map_revealed[:]
- map_revealed.append(pos)
- self.update_hud()
- self.last_xr = xr
- self.last_yr = yr
- self.show_hud()
- def event_paused_step(self, time_passed, delta_mult):
- self.show_hud()
- def event_alarm(self, alarm_id):
- if alarm_id == "hitstun":
- self.hitstun = False
- self.image_alpha = 255
- elif alarm_id == "input_lock":
- self.input_lock = False
- self.refresh_input()
- elif alarm_id == "warp_out":
- if self.warp_dest:
- warp(self.warp_dest)
- def event_key_press(self, key, char):
- if self.human and not self.input_lock:
- if key in up_key[self.player] and not self.up_pressed:
- self.press_up()
- if key in down_key[self.player] and not self.down_pressed:
- self.press_down()
- if key in jump_key[self.player] and not self.jump_pressed:
- self.jump()
- if key in shoot_key[self.player] and not self.shoot_pressed:
- self.shoot()
- if key in mode_key[self.player] and not self.mode_pressed:
- self.mode()
- if (key in mode_reset_key[self.player] and
- not self.mode_reset_pressed):
- self.mode_reset()
- if key in map_key[self.player]:
- if "map" in progress_flags:
- play_sound(select_sound)
- MapDialog(self.last_xr, self.last_yr).show()
- if not isinstance(sge.game.current_room, SpecialScreen):
- if key == "escape" or key in pause_key[self.player]:
- sge.game.current_room.pause(player_x=self.last_xr,
- player_y=self.last_yr)
- def event_key_release(self, key):
- if self.human and not self.input_lock:
- if key in jump_key[self.player]:
- self.jump_release()
- if key in shoot_key[self.player]:
- self.shoot_release()
- elif key in up_key[self.player] or key in down_key[self.player]:
- self.aim_lock = False
- def event_joystick(self, js_name, js_id, input_type, input_id, value):
- js = (js_id, input_type, input_id)
- if self.human and not self.input_lock:
- if value >= joystick_threshold:
- if js in up_js[self.player] and not self.up_pressed:
- self.press_up()
- if js in down_js[self.player] and not self.down_pressed:
- self.press_down()
- if js in jump_js[self.player] and not self.jump_pressed:
- self.jump()
- if js in shoot_js[self.player] and not self.shoot_pressed:
- self.shoot()
- if js in mode_js[self.player] and not self.mode_pressed:
- self.mode()
- if (js in mode_reset_js[self.player] and
- not self.mode_reset_pressed):
- self.mode_reset()
- if js in map_js[self.player]:
- if "map" in progress_flags:
- play_sound(select_sound)
- MapDialog(self.last_xr, self.last_yr).show()
- else:
- if js in jump_js[self.player]:
- self.jump_release()
- if js in shoot_js[self.player]:
- self.shoot_release()
- if not isinstance(sge.game.current_room, SpecialScreen):
- if value >= joystick_threshold and js in pause_js[self.player]:
- sge.game.current_room.pause(player_x=self.last_xr,
- player_y=self.last_yr)
- def event_collision(self, other, xdirection, ydirection):
- if isinstance(other, InteractiveObject):
- other.touch(self)
- def event_physics_collision_left(self, other, move_loss):
- for block in self.get_left_touching_wall():
- if isinstance(block, HurtRight):
- self.hurt()
- if isinstance(other, xsge_physics.SolidRight):
- self.xvelocity = max(self.xvelocity, 0)
- def event_physics_collision_right(self, other, move_loss):
- for block in self.get_right_touching_wall():
- if isinstance(block, HurtLeft):
- self.hurt()
- if isinstance(other, xsge_physics.SolidLeft):
- self.xvelocity = min(self.xvelocity, 0)
- def event_physics_collision_top(self, other, move_loss):
- top_touching = self.get_top_touching_wall()
- tmv = 0
- for i in six.moves.range(CEILING_LAX):
- if (not self.get_left_touching_wall() and
- not self.get_left_touching_slope()):
- self.x -= 1
- tmv -= 1
- if (not self.get_top_touching_wall() and
- not self.get_top_touching_slope()):
- self.move_y(-move_loss)
- break
- else:
- self.x -= tmv
- tmv = 0
- for i in six.moves.range(CEILING_LAX):
- if (not self.get_left_touching_wall() and
- not self.get_left_touching_slope()):
- self.x += 1
- tmv += 1
- if (not self.get_top_touching_wall() and
- not self.get_top_touching_slope()):
- self.move_y(-move_loss)
- break
- else:
- self.x -= tmv
- tmv = 0
- self.yvelocity = max(self.yvelocity, 0)
- for block in top_touching:
- if isinstance(block, HurtBottom):
- self.hurt()
- def event_physics_collision_bottom(self, other, move_loss):
- self.has_jumped = False
- for block in self.get_bottom_touching_wall():
- if isinstance(block, HurtTop):
- self.hurt()
- if isinstance(other, xsge_physics.SolidTop):
- self.yvelocity = min(self.yvelocity, 0)
- elif isinstance(other, (xsge_physics.SlopeTopLeft,
- xsge_physics.SlopeTopRight)):
- ss = self.roll_slide_speed if self.rolling else self.slide_speed
- self.yvelocity = min(ss * (other.bbox_height / other.bbox_width),
- self.yvelocity)
- def event_jump(self):
- pass
- class Anneroy(Player):
- name = "Anneroy"
- @property
- def can_move(self):
- if self.crouching:
- h_control = bool(self.right_pressed) - bool(self.left_pressed)
- if h_control != self.facing:
- self.alarms["autostand_lock"] = 10
- if "autostand_lock" not in self.alarms:
- self.press_up()
- return not self.crouching
- def __init__(self, *args, **kwargs):
- kwargs["bbox_x"] = ANNEROY_BBOX_X
- kwargs["bbox_width"] = ANNEROY_BBOX_WIDTH
- kwargs["bbox_y"] = ANNEROY_STAND_BBOX_Y
- kwargs["bbox_height"] = ANNEROY_STAND_BBOX_HEIGHT
- super(Anneroy, self).__init__(*args, **kwargs)
- self.torso = None
- self.hedgehog_spikes = None
- self.fixed_sprite = False
- self.crouching = False
- self.ball = False
- self.walljumping = False
- self.wall_direction = 0
- self.bouncing = False
- self.hedgehog = False
- self.hedgehog_autocancel = False
- self.last_aim_direction = 0
- def get_up_obstructed(self, x, y, w, h, lax=0):
- def _get_up_obstructed(self=self, x=x, y=y, w=w, h=h):
- for other in sge.collision.rectangle(self.x + x, self.y + y, w, h):
- if isinstance(other, xsge_physics.SolidBottom):
- if not self.collision(other):
- return True
- elif isinstance(other, xsge_physics.SlopeBottomLeft):
- if self.bbox_top >= other.get_slope_y(self.bbox_right):
- return True
- elif isinstance(other, xsge_physics.SlopeBottomRight):
- if self.bbox_top >= other.get_slope_y(self.bbox_left):
- return True
- return False
- rv = _get_up_obstructed()
- if rv:
- xstart = self.x
- for i in six.moves.range(lax):
- self.move_x(-1)
- rv = _get_up_obstructed()
- if not rv:
- break
- else:
- self.move_x(xstart - self.x)
- for i in six.moves.range(lax):
- self.move_x(1)
- rv = _get_up_obstructed()
- if not rv:
- break
- else:
- self.move_x(xstart - self.x)
- return rv
- def press_up(self):
- if self.ball:
- if self.get_up_obstructed(
- ANNEROY_BBOX_X, ANNEROY_CROUCH_BBOX_Y,
- ANNEROY_BBOX_WIDTH, ANNEROY_CROUCH_BBOX_HEIGHT,
- ANNEROY_DECOMPRESS_LAX):
- self.reset_image()
- self.sprite = anneroy_decompress_fail_sprite
- self.image_index = 0
- self.image_speed = None
- self.torso.visible = False
- self.fixed_sprite = "decompress_fail"
- else:
- if self.fixed_sprite != "compress":
- self.reset_image()
- self.sprite = anneroy_compress_sprite
- self.image_index = anneroy_compress_sprite.frames - 1
- self.image_speed = -anneroy_compress_sprite.speed
- self.torso.visible = False
- self.fixed_sprite = "compress"
- self.ball = False
- self.hedgehog = False
- self.rolling = False
- self.aim_lock = True
- if "fixed_sprite" in self.alarms:
- del self.alarms["fixed_sprite"]
- if "hedgehog_retract" in self.alarms:
- del self.alarms["hedgehog_retract"]
- if "hedgehog_extend" in self.alarms:
- del self.alarms["hedgehog_extend"]
- if "hedgehog_extend2" in self.alarms:
- del self.alarms["hedgehog_extend2"]
- self.max_speed = self.__class__.max_speed
- if self.on_floor:
- self.crouching = True
- self.bbox_y = ANNEROY_CROUCH_BBOX_Y
- self.bbox_height = ANNEROY_CROUCH_BBOX_HEIGHT
- else:
- self.bbox_y = ANNEROY_STAND_BBOX_Y
- self.bbox_height = ANNEROY_STAND_BBOX_HEIGHT
- elif self.crouching:
- if not self.aim_diag_pressed:
- for other in sge.collision.rectangle(
- self.x + ANNEROY_BBOX_X, self.y + ANNEROY_STAND_BBOX_Y,
- ANNEROY_BBOX_WIDTH, ANNEROY_STAND_BBOX_HEIGHT):
- if isinstance(other, (xsge_physics.SolidBottom,
- xsge_physics.SlopeBottomLeft,
- xsge_physics.SlopeBottomRight)):
- if not self.collision(other):
- break
- else:
- if self.on_floor:
- if self.fixed_sprite != "crouch":
- self.reset_image()
- self.sprite = anneroy_legs_crouch_sprite
- self.image_index = anneroy_legs_crouch_sprite.frames - 1
- self.image_speed = -anneroy_legs_crouch_sprite.speed
- self.fixed_sprite = "crouch"
- self.crouching = False
- self.bbox_y = ANNEROY_STAND_BBOX_Y
- self.bbox_height = ANNEROY_STAND_BBOX_HEIGHT
- self.aim_lock = True
- else:
- super(Anneroy, self).press_up()
- def press_down(self):
- if not self.aim_diag_pressed:
- h_control = bool(self.right_pressed) - bool(self.left_pressed)
- if self.on_floor and self.was_on_floor:
- if self.ball:
- # Do nothing
- pass
- elif self.crouching:
- self.compress()
- else:
- if not h_control:
- if self.fixed_sprite != "crouch":
- self.reset_image()
- self.sprite = anneroy_legs_crouch_sprite
- self.image_index = 0
- self.image_speed = anneroy_legs_crouch_sprite.speed
- self.fixed_sprite = "crouch"
- self.crouching = True
- self.bbox_y = ANNEROY_CROUCH_BBOX_Y
- self.bbox_height = ANNEROY_CROUCH_BBOX_HEIGHT
- self.aim_lock = True
- else:
- if "compress_pressed" in self.alarms:
- self.compress()
- del self.alarms["compress_pressed"]
- else:
- self.alarms["compress_pressed"] = DOUBLETAP_TIME
- else:
- if not self.ball:
- if "compress_pressed" in self.alarms:
- self.compress()
- del self.alarms["compress_pressed"]
- else:
- self.alarms["compress_pressed"] = DOUBLETAP_TIME
- def jump(self):
- if self.crouching:
- self.press_up()
- if not self.crouching and not self.ball and not self.walljumping:
- if (not self.on_floor and not self.was_on_floor and
- "monkey_boots" in progress_flags):
- if self.facing > 0 and self.get_right_touching_wall():
- self.reset_image()
- self.sprite = anneroy_wall_right_sprite
- self.image_index = 0
- self.image_speed = anneroy_wall_right_sprite.speed
- self.image_xscale = abs(self.image_xscale)
- self.fixed_sprite = "wall"
- self.walljumping = True
- self.wall_direction = 1
- self.has_jumped = False
- if "fixed_sprite" in self.alarms:
- del self.alarms["fixed_sprite"]
- self.torso.visible = False
- self.input_lock = True
- self.xvelocity = 0
- self.yvelocity = 0
- self.gravity = 0
- elif self.facing < 0 and self.get_left_touching_wall():
- self.reset_image()
- self.sprite = anneroy_wall_left_sprite
- self.image_index = 0
- self.image_speed = anneroy_wall_left_sprite.speed
- self.image_xscale = abs(self.image_xscale)
- self.fixed_sprite = "wall"
- self.walljumping = True
- self.wall_direction = -1
- self.has_jumped = False
- if "fixed_sprite" in self.alarms:
- del self.alarms["fixed_sprite"]
- self.torso.visible = False
- self.input_lock = True
- self.xvelocity = 0
- self.yvelocity = 0
- self.gravity = 0
- else:
- super(Anneroy, self).jump()
- def retract_spikes(self):
- self.hedgehog = False
- self.sprite = anneroy_hedgehog_start_sprite
- self.fixed_sprite = "hedgehog"
- self.alarms["fixed_sprite"] = ANNEROY_HEDGEHOG_FRAME_TIME
- self.alarms["hedgehog_lock"] = ANNEROY_HEDGEHOG_FRAME_TIME
- self.rolling = True
- self.max_speed = self.__class__.max_speed
- def shoot_default(self):
- if "shoot_lock" not in self.alarms:
- if self.ball:
- if ("hedgehog_hormone" in progress_flags and
- not self.hedgehog and
- "hedgehog_lock" not in self.alarms):
- self.hedgehog = True
- self.sprite = anneroy_hedgehog_start_sprite
- if self.fixed_sprite:
- self.image_speed = (abs(self.xvelocity) *
- ANNEROY_BALL_FRAMES_PER_PIXEL)
- self.fixed_sprite = "hedgehog"
- self.alarms["hedgehog_extend"] = ANNEROY_HEDGEHOG_FRAME_TIME
- play_sound(hedgehog_spikes_sound, self.image_xcenter,
- self.image_ycenter)
- self.rolling = False
- if "sloth_ball" in progress_flags:
- self.hedgehog_autocancel = False
- self.max_speed = ANNEROY_SLOTH_MAX_SPEED
- else:
- self.hedgehog_autocancel = True
- self.max_speed = 0
- else:
- if self.aim_direction is None:
- self.aim_direction = 0
- self.alarms["shooting"] = 30
- self.alarms["shoot_lock"] = 15
- if self.aim_direction is not None:
- self.last_aim_direction = self.aim_direction
- else:
- self.last_aim_direction = 0
- x = 0
- y = 0
- xv = 0
- yv = 0
- image_rotation = 0
- if self.facing > 0:
- if self.aim_direction == 0:
- x = 25
- y = -3
- xv = ANNEROY_BULLET_SPEED
- image_rotation = 0
- elif self.aim_direction == 1:
- x = 22
- y = -27
- xv = ANNEROY_BULLET_DSPEED
- yv = -ANNEROY_BULLET_DSPEED
- image_rotation = 315
- elif self.aim_direction == 2:
- x = 6
- y = -31
- yv = -ANNEROY_BULLET_SPEED
- image_rotation = 270
- elif self.aim_direction == -1:
- x = 19
- y = 9
- xv = ANNEROY_BULLET_DSPEED
- yv = ANNEROY_BULLET_DSPEED
- image_rotation = 45
- elif self.aim_direction == -2:
- x = 9
- y = 21
- yv = ANNEROY_BULLET_SPEED
- image_rotation = 90
- else:
- if self.aim_direction == 0:
- x = -25
- y = -3
- xv = -ANNEROY_BULLET_SPEED
- image_rotation = 180
- elif self.aim_direction == 1:
- x = -22
- y = -27
- xv = -ANNEROY_BULLET_DSPEED
- yv = -ANNEROY_BULLET_DSPEED
- image_rotation = 225
- elif self.aim_direction == 2:
- x = -6
- y = -31
- yv = -ANNEROY_BULLET_SPEED
- image_rotation = 270
- elif self.aim_direction == -1:
- x = -19
- y = 9
- xv = -ANNEROY_BULLET_DSPEED
- yv = ANNEROY_BULLET_DSPEED
- image_rotation = 135
- elif self.aim_direction == -2:
- x = -9
- y = 21
- yv = ANNEROY_BULLET_SPEED
- image_rotation = 90
- if x:
- m = y / x
- else:
- m = None
- xdest = self.torso.x + x
- ydest = self.torso.y + y
- guide = xsge_physics.Collider.create(
- self.torso.x, self.torso.y, sprite=anneroy_bullet_sprite)
- if self.facing > 0:
- guide.bbox_right = self.bbox_right
- else:
- guide.bbox_left = self.bbox_left
- if self.aim_direction < 0:
- guide.bbox_bottom = self.bbox_bottom
- else:
- guide.bbox_top = self.bbox_top
- x += self.torso.x - guide.x
- y += self.torso.y - guide.y
- xsteps = int(abs(x) / guide.bbox_width)
- ysteps = int(abs(y) / guide.bbox_height)
- xfinal = math.copysign(abs(x) - xsteps * guide.bbox_width, x)
- yfinal = math.copysign(abs(y) - ysteps * guide.bbox_height, y)
- for i in six.moves.range(xsteps):
- guide.move_x(math.copysign(guide.bbox_width, x))
- for i in six.moves.range(ysteps):
- guide.move_y(math.copysign(guide.bbox_height, y))
- guide.move_x(xfinal)
- guide.move_y(yfinal)
- if abs(self.aim_direction) == 1 and m:
- target_x = self.torso.x + x
- target_y = self.torso.y + y
- xdiff = guide.x - self.torso.x
- ydiff = guide.y - self.torso.y
- if abs(guide.x - target_x) >= 1:
- guide.y = self.torso.y + m * xdiff
- elif abs(guide.y - target_y) >= 1:
- guide.x = self.torso.x + ydiff / m
- bs = AnneroyBullet.create(
- guide.x, guide.y, self.z + 0.2,
- sprite=anneroy_bullet_sprite, xvelocity=xv,
- yvelocity=yv, regulate_origin=True,
- image_xscale=abs(self.image_xscale),
- image_yscale=self.image_yscale,
- image_rotation=image_rotation, image_blend=self.image_blend)
- guide.destroy()
- Smoke.create(
- xdest, ydest, self.torso.z,
- sprite=anneroy_bullet_dust_sprite,
- xvelocity=self.xvelocity, yvelocity=self.yvelocity,
- regulate_origin=True, image_xscale=abs(self.image_xscale),
- image_yscale=self.image_yscale,
- image_rotation=image_rotation,
- image_blend=self.image_blend)
- play_sound(shoot_sound, xdest, ydest)
- def shoot(self):
- if self.current_mode == "compress":
- if not self.shoot_pressed:
- if self.ball:
- self.shoot_default()
- else:
- self.compress()
- else:
- self.shoot_default()
- def shoot_release(self):
- if self.hedgehog and not self.hedgehog_autocancel:
- if self.fixed_sprite == "hedgehog":
- self.hedgehog_autocancel = True
- elif "hedgehog_lock" not in self.alarms:
- self.retract_spikes()
- def compress(self):
- if "atomic_compressor" in progress_flags and not self.shoot_pressed:
- if self.fixed_sprite != "compress":
- self.reset_image()
- self.sprite = anneroy_compress_sprite
- self.image_index = 0
- self.image_speed = anneroy_compress_sprite.speed
- self.fixed_sprite = "compress"
- self.torso.visible = False
- self.crouching = False
- self.ball = True
- self.rolling = True
- self.bouncing = False
- self.hedgehog = False
- self.max_speed = self.__class__.max_speed
- self.bbox_y = ANNEROY_BALL_BBOX_Y
- self.bbox_height = ANNEROY_BALL_BBOX_HEIGHT
- def hurt(self, damage=1, touching=False):
- if (not touching) or (not self.hedgehog):
- super(Anneroy, self).hurt(damage, touching)
- def kill(self):
- if self.lose_on_death:
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- sge.game.current_room.alarms["death"] = DEATH_TIME
- play_sound(death_sound, self.x, self.y)
- self.ball = False
- self.hedgehog = False
- self.rolling = False
- self.input_lock = True
- self.tangible = False
- self.view_frozen = True
- self.reset_input()
- self.xvelocity = 0
- self.yvelocity = 0
- self.gravity = 0
- self.reset_image()
- if self.facing > 0:
- self.sprite = anneroy_death_right_sprite
- else:
- self.sprite = anneroy_death_left_sprite
- self.image_index = 0
- self.image_fps = None
- self.image_xscale = abs(self.image_xscale)
- self.torso.visible = False
- self.fixed_sprite = "death"
-
- # Delete all alarms to prevent any problems
- self.alarms = []
- def warp_in(self):
- self.input_lock = True
- self.reset_input()
- self.xvelocity = 0
- self.yvelocity = 0
- self.reset_image()
- self.sprite = anneroy_teleport_sprite
- self.image_index = 0
- self.image_fps = anneroy_teleport_sprite.fps
- self.torso.visible = False
- self.fixed_sprite = "warp_in"
- def warp_out(self):
- self.input_lock = True
- self.reset_input()
- self.xvelocity = 0
- self.yvelocity = 0
- self.reset_image()
- self.sprite = anneroy_teleport_sprite
- self.image_index = anneroy_teleport_sprite.frames - 1
- self.image_fps = -anneroy_teleport_sprite.fps
- self.torso.visible = False
- self.fixed_sprite = "warp_out"
- def reset_image(self):
- self.visible = True
- self.torso.visible = True
- self.image_xscale = self.facing * abs(self.image_xscale)
- self.image_speed = 0
- def set_image(self):
- assert self.torso is not None
- h_control = bool(self.right_pressed) - bool(self.left_pressed)
- aim_direction = self.aim_direction
- idle_torso_right = anneroy_torso_right_idle_sprite
- idle_torso_left = anneroy_torso_left_idle_sprite
- # Turn Anneroy around.
- if not self.fixed_sprite or self.fixed_sprite == "turn":
- if not self.crouching and not self.ball:
- if self.facing < 0 and h_control > 0:
- self.facing = 1
- if self.fixed_sprite != "turn":
- self.reset_image()
- self.sprite = anneroy_turn_sprite
- self.image_index = 0
- self.image_speed = anneroy_turn_sprite.speed
- self.image_xscale = abs(self.image_xscale)
- self.torso.visible = False
- self.fixed_sprite = "turn"
- elif self.facing > 0 and h_control < 0:
- self.facing = -1
- if self.fixed_sprite != "turn":
- self.reset_image()
- self.sprite = anneroy_turn_sprite
- self.image_index = anneroy_turn_sprite.frames - 1
- self.image_speed = -anneroy_turn_sprite.speed
- self.image_xscale = abs(self.image_xscale)
- self.torso.visible = False
- self.fixed_sprite = "turn"
- elif h_control:
- self.facing = h_control
- if not self.fixed_sprite:
- old_is = self.image_speed
- self.reset_image()
- if self.xvelocity:
- xm = (self.xvelocity > 0) - (self.xvelocity < 0)
- else:
- real_xv = self.x - self.xprevious
- xm = (real_xv > 0) - (real_xv < 0)
- if self.ball:
- if self.hedgehog:
- self.sprite = anneroy_hedgehog_sprite
- else:
- self.sprite = anneroy_ball_sprite
- self.torso.visible = False
- if self.on_floor:
- if xm:
- self.image_speed = self.speed * ANNEROY_BALL_FRAMES_PER_PIXEL
- if xm != self.facing:
- self.image_speed *= -1
- else:
- self.image_speed = 0
- else:
- self.image_speed = old_is
- else:
- # Set legs
- if self.on_floor and self.was_on_floor:
- if self.crouching:
- self.sprite = anneroy_legs_crouched_sprite
- else:
- if xm:
- self.sprite = anneroy_legs_run_sprite
- self.image_speed = self.speed * ANNEROY_RUN_FRAMES_PER_PIXEL
- if xm != self.facing:
- self.image_speed *= -1
- idle_torso_right = anneroy_torso_right_aim_right_sprite
- idle_torso_left = anneroy_torso_left_aim_left_sprite
- else:
- self.sprite = anneroy_legs_stand_sprite
- else:
- self.sprite = anneroy_legs_jump_sprite
- self.image_index = -1
- elif self.fixed_sprite == "hedgehog":
- if self.on_floor:
- if self.xvelocity:
- xm = (self.xvelocity > 0) - (self.xvelocity < 0)
- else:
- real_xv = self.x - self.xprevious
- xm = (real_xv > 0) - (real_xv < 0)
- if xm:
- self.image_speed = self.speed * ANNEROY_BALL_FRAMES_PER_PIXEL
- if xm != self.facing:
- self.image_speed *= -1
- else:
- self.image_speed = 0
- if "shooting" in self.alarms:
- aim_direction = self.last_aim_direction
- elif not self.fixed_sprite:
- if self.aim_direction_time < 4 and self.aim_direction is not None:
- aim_direction = max(-1, min(self.aim_direction, 1))
- else:
- if self.aim_direction_time < 16:
- aim_direction = None
- elif (self.aim_direction_time < 20 and
- self.aim_direction is not None):
- aim_direction = max(-1, min(self.aim_direction, 1))
- # Set torso
- if self.facing > 0:
- self.torso.sprite = {
- 0: anneroy_torso_right_aim_right_sprite,
- 1: anneroy_torso_right_aim_upright_sprite,
- 2: anneroy_torso_right_aim_up_sprite,
- -1: anneroy_torso_right_aim_downright_sprite,
- -2: anneroy_torso_right_aim_down_sprite}.get(
- aim_direction, idle_torso_right)
- else:
- self.torso.sprite = {
- 0: anneroy_torso_left_aim_left_sprite,
- 1: anneroy_torso_left_aim_upleft_sprite,
- 2: anneroy_torso_left_aim_up_sprite,
- -1: anneroy_torso_left_aim_downleft_sprite,
- -2: anneroy_torso_left_aim_down_sprite}.get(
- aim_direction, idle_torso_left)
- # Position torso
- x, y = anneroy_torso_offset.setdefault(
- (id(self.sprite), self.image_index % self.sprite.frames), (0, 0))
- self.torso.x = self.x + x * self.image_xscale
- self.torso.y = self.y + y * self.image_yscale
- self.torso.z = self.z + 0.1
- self.torso.image_xscale = abs(self.image_xscale)
- self.torso.image_yscale = self.image_yscale
- self.torso.image_alpha = self.image_alpha
- self.torso.image_blend = self.image_blend
- # Position hedgehog spikes
- if self.hedgehog:
- if self.hedgehog_spikes is None:
- self.hedgehog_spikes = HedgehogSpikes.create(
- self.x, self.y, visible=False,
- bbox_x=ANNEROY_HEDGEHOG_BBOX_X,
- bbox_y=ANNEROY_HEDGEHOG_BBOX_Y,
- bbox_width=ANNEROY_HEDGEHOG_BBOX_WIDTH,
- bbox_height=ANNEROY_HEDGEHOG_BBOX_HEIGHT,
- regulate_origin=True)
- else:
- self.hedgehog_spikes.x = self.x
- self.hedgehog_spikes.y = self.y
- self.hedgehog_spikes.tangible = True
- else:
- if self.hedgehog_spikes is not None:
- self.hedgehog_spikes.tangible = False
- def event_create(self):
- self.torso = sge.dsp.Object.create(self.x, self.y, self.z + 0.1,
- regulate_origin=True)
- self.hedgehog_spikes = HedgehogSpikes.create(
- self.x, self.y, visible=False, bbox_x=ANNEROY_HEDGEHOG_BBOX_X,
- bbox_y=ANNEROY_HEDGEHOG_BBOX_Y,
- bbox_width=ANNEROY_HEDGEHOG_BBOX_WIDTH,
- bbox_height=ANNEROY_HEDGEHOG_BBOX_HEIGHT, regulate_origin=True)
- super(Anneroy, self).event_create()
- def event_begin_step(self, time_passed, delta_mult):
- super(Anneroy, self).event_begin_step(time_passed, delta_mult)
- if not self.on_floor and self.crouching:
- self.press_up()
- def event_alarm(self, alarm_id):
- super(Anneroy, self).event_alarm(alarm_id)
- if alarm_id == "fixed_sprite":
- self.fixed_sprite = False
- elif alarm_id == "hedgehog_extend":
- self.sprite = anneroy_hedgehog_extend_sprite
- self.alarms["hedgehog_extend2"] = ANNEROY_HEDGEHOG_FRAME_TIME
- elif alarm_id == "hedgehog_extend2":
- self.fixed_sprite = False
- if self.hedgehog_autocancel:
- self.alarms["hedgehog_retract"] = ANNEROY_HEDGEHOG_TIME
- self.alarms["hedgehog_lock"] = ANNEROY_HEDGEHOG_TIME
- elif alarm_id == "hedgehog_retract":
- self.retract_spikes()
- elif alarm_id == "shoot_lock":
- if self.shoot_pressed:
- self.shoot()
- def event_animation_end(self):
- if self.fixed_sprite in {"turn", "crouch", "anim"}:
- self.fixed_sprite = False
- elif self.fixed_sprite == "warp_in":
- self.image_index = self.sprite.frames - 1
- self.image_speed = 0
- self.alarms["fixed_sprite"] = WARP_TIME
- self.alarms["input_lock"] = WARP_TIME
- elif self.fixed_sprite == "warp_out":
- self.visible = False
- self.image_speed = 0
- self.alarms["warp_out"] = WARP_TIME
- elif self.fixed_sprite in {"compress", "decompress_fail"}:
- self.fixed_sprite = False
- self.image_speed = abs(self.xvelocity) * ANNEROY_BALL_FRAMES_PER_PIXEL
- elif self.fixed_sprite == "wall":
- self.reset_image()
- if self.wall_direction < 0:
- self.sprite = anneroy_walljump_right_sprite
- else:
- self.sprite = anneroy_walljump_left_sprite
- self.image_xscale = abs(self.image_xscale)
- self.torso.visible = False
- self.fixed_sprite = "walljump"
- self.alarms["fixed_sprite"] = ANNEROY_WALLJUMP_FRAME_TIME
- self.walljumping = False
- self.input_lock = False
- self.facing = -self.wall_direction
- self.gravity = self.__class__.gravity
- self.xvelocity = ANNEROY_WALLJUMP_SPEED * self.facing
- self.yvelocity = get_jump_speed(ANNEROY_WALLJUMP_HEIGHT,
- self.gravity)
- elif self.fixed_sprite == "death":
- Smoke.create(self.x, self.y, z=(self.z + 0.1),
- sprite=anneroy_explode_sprite, tangible=False)
- for i in six.moves.range(12):
- shard = Shard.create(
- self.x, self.y, self.z, sprite=anneroy_explode_fragments,
- image_index=random.randrange(anneroy_explode_fragments.frames),
- image_fps=0)
- shard.speed = 5
- shard.move_direction = random.randrange(360)
- self.destroy()
- def event_physics_collision_top(self, other, move_loss):
- super(Anneroy, self).event_physics_collision_top(other, move_loss)
- self.event_animation_end()
- def event_physics_collision_bottom(self, other, move_loss):
- yv = self.yvelocity
- super(Anneroy, self).event_physics_collision_bottom(other, move_loss)
- if not self.was_on_floor:
- if self.hedgehog:
- play_sound(ball_land_sound, self.x, self.y)
- elif self.ball:
- if not self.bouncing or yv >= ANNEROY_BALL_FORCE_BOUNCE_SPEED:
- self.bouncing = True
- self.yvelocity = get_jump_speed(ANNEROY_BALL_BOUNCE_HEIGHT,
- self.gravity)
- else:
- self.bouncing = False
- play_sound(ball_land_sound, self.x, self.y)
- else:
- self.reset_image()
- self.sprite = anneroy_legs_land_sprite
- self.image_speed = None
- self.image_index = 0
- self.fixed_sprite = "anim"
- play_sound(land_sound, self.x, self.y)
- def event_jump(self):
- if not self.ball:
- self.reset_image()
- self.sprite = anneroy_legs_jump_sprite
- self.image_speed = None
- self.image_index = 0
- self.fixed_sprite = "anim"
- def event_destroy(self):
- if self.torso is not None:
- self.torso.destroy()
- class DeadMan(sge.dsp.Object):
- """Object which falls off the screen, then gets destroyed."""
- gravity = GRAVITY
- fall_speed = PLAYER_FALL_SPEED
- def event_begin_step(self, time_passed, delta_mult):
- if self.yvelocity < self.fall_speed:
- self.yacceleration = self.gravity
- else:
- self.yvelocity = self.fall_speed
- self.yacceleration = 0
- def event_step(self, time_passed, delta_mult):
- if self.y - self.image_origin_y > sge.game.current_room.height:
- self.destroy()
- class Corpse(xsge_physics.Collider):
- """Like DeadMan, but just falls to the floor, not off-screen."""
- gravity = GRAVITY
- fall_speed = PLAYER_FALL_SPEED
- def event_create(self):
- self.alarms["die"] = 90
- def event_begin_step(self, time_passed, delta_mult):
- if self.get_bottom_touching_wall() or self.get_bottom_touching_slope():
- self.yvelocity = 0
- else:
- if self.yvelocity < self.fall_speed:
- self.yacceleration = self.gravity
- else:
- self.yvelocity = min(self.yvelocity, self.fall_speed)
- self.yacceleration = 0
- def event_alarm(self, alarm_id):
- if alarm_id == "die":
- self.destroy()
- class Smoke(sge.dsp.Object):
- def event_animation_end(self):
- self.destroy()
- class InteractiveObject(sge.dsp.Object):
- shootable = False
- spikeable = False
- freezable = False
- def get_nearest_player(self):
- player = None
- dist = 0
- for obj in sge.game.current_room.objects:
- if isinstance(obj, Player):
- ndist = math.hypot(self.x - obj.x, self.y - obj.y)
- if player is None or ndist < dist:
- player = obj
- dist = ndist
- return player
- def set_direction(self, direction):
- self.image_xscale = abs(self.image_xscale) * direction
- self.xvelocity = math.copysign(self.xvelocity, self.image_xscale)
- def move(self):
- pass
- def touch(self, other):
- pass
- def shoot(self, other):
- pass
- def spike(self, other):
- self.shoot(other)
- def freeze(self):
- pass
- def project_light(self):
- pass
- def event_begin_step(self, time_passed, delta_mult):
- self.move()
- class InteractiveCollider(InteractiveObject, xsge_physics.Collider):
- def stop_left(self):
- self.xvelocity = 0
- def stop_right(self):
- self.xvelocity = 0
- def stop_up(self):
- self.yvelocity = 0
- def stop_down(self):
- self.yvelocity = 0
- def touch_hurt(self):
- pass
- def event_physics_collision_left(self, other, move_loss):
- if isinstance(other, HurtRight):
- self.touch_hurt()
- if isinstance(other, xsge_physics.SolidRight):
- self.stop_left()
- elif isinstance(other, xsge_physics.SlopeTopRight):
- if self.yvelocity > 0:
- self.stop_down()
- elif isinstance(other, xsge_physics.SlopeBottomRight):
- if self.yvelocity < 0:
- self.stop_up()
- def event_physics_collision_right(self, other, move_loss):
- if isinstance(other, HurtLeft):
- self.touch_hurt()
- if isinstance(other, xsge_physics.SolidLeft):
- self.stop_right()
- elif isinstance(other, xsge_physics.SlopeTopLeft):
- if self.yvelocity > 0:
- self.stop_down()
- elif isinstance(other, xsge_physics.SlopeBottomLeft):
- if self.yvelocity < 0:
- self.stop_up()
- def event_physics_collision_top(self, other, move_loss):
- if isinstance(other, HurtBottom):
- self.touch_hurt()
- if isinstance(other, (xsge_physics.SolidBottom,
- xsge_physics.SlopeBottomLeft,
- xsge_physics.SlopeBottomRight)):
- self.stop_up()
- def event_physics_collision_bottom(self, other, move_loss):
- if isinstance(other, HurtTop):
- self.touch_hurt()
- if isinstance(other, (xsge_physics.SolidTop, xsge_physics.SlopeTopLeft,
- xsge_physics.SlopeTopRight)):
- self.stop_down()
- class FallingObject(InteractiveCollider):
- """
- Falls based on gravity. If on a slope, falls at a constant speed
- based on the steepness of the slope.
- """
- gravity = GRAVITY
- fall_speed = PLAYER_FALL_SPEED
- slide_speed = PLAYER_SLIDE_SPEED
- was_on_floor = False
- def move(self):
- on_floor = self.get_bottom_touching_wall()
- on_slope = self.get_bottom_touching_slope()
- if self.was_on_floor and (on_floor or on_slope) and self.yvelocity >= 0:
- self.yacceleration = 0
- if on_floor:
- if self.yvelocity > 0:
- self.yvelocity = 0
- elif on_slope:
- self.yvelocity = self.slide_speed * (on_slope[0].bbox_height /
- on_slope[0].bbox_width)
- else:
- if self.yvelocity < self.fall_speed:
- self.yacceleration = self.gravity
- else:
- self.yvelocity = self.fall_speed
- self.yacceleration = 0
- self.was_on_floor = on_floor or on_slope
- class WalkingObject(FallingObject):
- """
- Walks in the direction it faces. Turns around at walls, and can
- also be set to turn around at ledges with the stayonplatform
- attribute. If slopeisplatform is False, slopes are regarded as
- ledges.
- """
- walk_speed = PLAYER_MAX_SPEED
- stayonplatform = False
- slopeisplatform = True
- def set_direction(self, direction):
- self.xvelocity = self.walk_speed * direction
- self.image_xscale = abs(self.image_xscale) * direction
- def move(self):
- super(WalkingObject, self).move()
- if not self.xvelocity:
- self.set_direction(math.copysign(1, self.image_xscale))
- on_floor = self.get_bottom_touching_wall()
- on_slope = self.slopeisplatform and self.get_bottom_touching_slope()
- if (on_floor or on_slope) and self.stayonplatform:
- if self.xvelocity < 0:
- my_left = self.bbox_left + (self.x - self.bbox_left) / 2
- for tile in on_floor:
- if tile.bbox_left < my_left:
- break
- else:
- if not on_slope:
- self.set_direction(1)
- else:
- my_right = self.bbox_right - (self.bbox_right - self.x) / 2
- for tile in on_floor:
- if tile.bbox_right > my_right:
- break
- else:
- if not on_slope:
- self.set_direction(-1)
- def stop_left(self):
- self.set_direction(1)
- def stop_right(self):
- self.set_direction(-1)
- class CrowdBlockingObject(InteractiveObject):
- """Blocks CrowdObject instances, causing them to turn around."""
- pass
- class CrowdObject(CrowdBlockingObject):
- """
- Turns around when colliding with a CrowdBlockingObject. (Note: this
- class is itself derived from CrowdBlockingObject.)
- """
- def event_collision(self, other, xdirection, ydirection):
- if isinstance(other, CrowdBlockingObject):
- if xdirection:
- self.set_direction(-xdirection)
- else:
- if self.x > other.x:
- self.set_direction(1)
- elif self.x < other.x:
- self.set_direction(-1)
- elif id(self) > id(other):
- self.set_direction(1)
- else:
- self.set_direction(-1)
- else:
- super(CrowdObject, self).event_collision(other, xdirection,
- ydirection)
- class Shard(FallingObject):
- """Like Corpse, but bounces around a bit before disappearing."""
- fall_speed = 99
- bounce = 0.5
- friction = 0.95
- life = 45
- def stop_left(self):
- self.xvelocity *= -self.bounce
- def stop_right(self):
- self.xvelocity *= -self.bounce
- def stop_up(self):
- self.yvelocity *= -self.bounce
- def stop_down(self):
- self.yvelocity *= -self.bounce
- def event_create(self):
- self.alarms["die"] = self.life
- def move(self):
- super(Shard, self).move()
- self.speed *= self.friction
- def event_alarm(self, alarm_id):
- if alarm_id == "die":
- self.destroy()
- class Enemy(InteractiveObject):
- classname = None
- shootable = True
- spikeable = True
- touch_damage = 5
- hp = 1
- def touch(self, other):
- other.hurt(self.touch_damage, True)
- def shoot(self, other):
- # TODO: Handle different kinds of bullets
- self.hp -= 1
- if self.hp <= 0:
- self.kill()
- else:
- self.hurt()
- def hurt(self):
- self.image_blend = sge.gfx.Color("white")
- self.image_blend_mode = sge.BLEND_RGB_SCREEN
- self.alarms["hurt_flash"] = FPS / 10
- play_sound(enemy_hurt_sound, self.image_xcenter, self.image_ycenter)
- def kill(self):
- blend = sge.gfx.Color((255, 255, 255, 0))
- base_sprite = sge.gfx.Sprite(width=self.sprite.width,
- height=self.sprite.height,
- origin_x=self.sprite.origin_x,
- origin_y=self.sprite.origin_y)
- base_sprite.draw_sprite(self.sprite, self.image_index,
- self.sprite.origin_x, self.sprite.origin_y)
- spr = sge.gfx.Sprite.from_tween(
- base_sprite, int(FPS / 4), fps=FPS, blend=blend,
- blend_mode=sge.BLEND_RGBA_MULTIPLY)
- Smoke.create(self.x, self.y, z=self.z, sprite=spr, tangible=False,
- image_origin_x=self.image_origin_x,
- image_origin_y=self.image_origin_y,
- image_xscale=self.image_xscale,
- image_yscale=self.image_yscale,
- image_rotation=self.image_rotation,
- image_alpha=self.image_alpha,
- image_blend=self.image_blend,
- image_blend_mode=self.image_blend_mode)
- if ("life_orb" in progress_flags and
- random.random() < LIFE_FORCE_CHANCE):
- LifeForce.create(self.image_xcenter, self.image_ycenter,
- z=self.z - 0.1)
- play_sound(enemy_death_sound, self.image_xcenter, self.image_ycenter)
- self.destroy()
- def event_alarm(self, alarm_id):
- if alarm_id == "hurt_flash":
- self.image_blend = None
- self.image_blend_mode = None
- class FreezableObject(InteractiveObject):
- """Provides basic freeze behavior."""
- freezable = True
- frozen_sprite = None
- frozen_time = 120
- frozen = False
- def permafreeze(self):
- prev_frozen_time = self.frozen_time
- self.frozen_time = None
- self.freeze()
- self.frozen_time = prev_frozen_time
- def freeze(self):
- if self.frozen_sprite is None:
- self.frozen_sprite = sge.gfx.Sprite(
- width=self.sprite.width, height=self.sprite.height,
- origin_x=self.sprite.origin_x, origin_y=self.sprite.origin_y,
- fps=THAW_FPS, bbox_x=self.sprite.bbox_x,
- bbox_y=self.sprite.bbox_y, bbox_width=self.sprite.bbox_width,
- bbox_height=self.sprite.bbox_height)
- self.frozen_sprite.append_frame()
- self.frozen_sprite.draw_sprite(self.sprite, self.image_index,
- self.sprite.origin_x,
- self.sprite.origin_y)
- colorizer = sge.gfx.Sprite(width=self.frozen_sprite.width,
- height=self.frozen_sprite.height)
- colorizer.draw_rectangle(0, 0, colorizer.width, colorizer.height,
- fill=sge.gfx.Color((128, 128, 255)))
- self.frozen_sprite.draw_sprite(colorizer, 0, 0, 0, frame=0,
- blend_mode=sge.BLEND_RGB_MULTIPLY)
- frozen_self = FrozenObject.create(self.x, self.y, self.z,
- sprite=self.frozen_sprite,
- image_fps=0,
- image_xscale=self.image_xscale,
- image_yscale=self.image_yscale)
- frozen_self.unfrozen = self
- self.frozen = True
- self.tangible = False
- self.active = False
- self.visible = False
- if self.frozen_time is not None:
- frozen_self.alarms["thaw_warn"] = self.frozen_time
- class FrozenObject(InteractiveObject, xsge_physics.Solid):
- freezable = True
- unfrozen = None
- def thaw(self):
- if self.unfrozen is not None:
- self.unfrozen.frozen = False
- self.unfrozen.tangible = True
- self.unfrozen.visible = True
- self.unfrozen.activate()
- self.destroy()
- def burn(self):
- self.thaw()
- play_sound(sizzle_sound, self.x, self.y)
- def freeze(self):
- if self.unfrozen is not None:
- self.thaw()
- self.unfrozen.freeze()
- def event_alarm(self, alarm_id):
- if self.unfrozen is not None:
- if alarm_id == "thaw_warn":
- self.image_fps = None
- self.alarms["thaw"] = THAW_WARN_TIME
- elif alarm_id == "thaw":
- self.thaw()
- class Frog(Enemy, FallingObject, CrowdObject):
- slide_speed = 0
- jump_distance = 200
- jump_height = 2 * TILE_SIZE + 1
- jump_speed = 3
- jump_interval = FPS / 2
- def stop_left(self):
- if self.yvelocity >= 0:
- self.xvelocity = 0
- def stop_right(self):
- if self.yvelocity >= 0:
- self.xvelocity = 0
- def stop_down(self):
- self.xvelocity = 0
- self.yvelocity = 0
- def event_create(self):
- self.bbox_x = 2
- self.bbox_y = 5
- self.bbox_width = 12
- self.bbox_height = 11
- def event_step(self, time_passed, delta_mult):
- super(Frog, self).event_step(time_passed, delta_mult)
- if ("jump" not in self.alarms and self.was_on_floor and
- not self.yvelocity):
- self.alarms["jump"] = self.jump_interval
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- self.image_xscale = math.copysign(self.image_xscale, xvec)
- if self.was_on_floor:
- self.sprite = frog_stand_sprite
- elif self.yvelocity < 0:
- self.sprite = frog_jump_sprite
- else:
- self.sprite = frog_fall_sprite
- def event_alarm(self, alarm_id):
- if alarm_id == "jump":
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- yvec = target.y - self.image_ycenter
- self.image_xscale = math.copysign(self.image_xscale, xvec)
- dist = math.hypot(xvec, yvec)
- if dist <= self.jump_distance:
- self.xvelocity = math.copysign(self.jump_speed, xvec)
- self.yvelocity = get_jump_speed(self.jump_height,
- self.gravity)
- play_sound(frog_jump_sound, self.image_xcenter,
- self.image_ycenter)
- class Hedgehog(Enemy, FallingObject, CrowdBlockingObject):
- hp = 3
- touch_damage = 7
- charge_distance = 300
- acceleration = 0.25
- walk_speed = 1
- max_speed = 4
- roll_slope_acceleration = 0.2
- roll_max_speed = 8
- friction = PLAYER_FRICTION
- roll_friction = 0.05
- walk_frames_per_pixel = 1 / 4
- roll_frames_per_pixel = 1 / 5
- @property
- def slope_acceleration(self):
- if self.rolling:
- return self.roll_slope_acceleration
- else:
- return 0
- def stop_left(self):
- self.xvelocity = 0
- def stop_right(self):
- self.xvelocity = 0
- def event_create(self):
- self.bbox_x = 4
- self.bbox_y = 6
- self.bbox_width = 12
- self.bbox_height = 14
- self.rolling = False
- self.anim_lock = False
- self.sprite = hedgehog_stand_sprite
- def event_step(self, time_passed, delta_mult):
- self.xacceleration = 0
- if self.rolling:
- if self.was_on_floor:
- self.xdeceleration = self.roll_friction
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- tdir = (xvec > 0) - (xvec < 0)
- vdir = (self.xvelocity > 0) - (self.xvelocity < 0)
- yvec = target.y - self.image_ycenter
- dist = math.hypot(xvec, yvec)
- if dist <= self.charge_distance and tdir == vdir:
- if abs(self.xvelocity) < self.max_speed:
- self.xacceleration = math.copysign(
- self.acceleration, xvec)
- self.xdeceleration = 0
-
- else:
- self.xdeceleration = 0
- if abs(self.xvelocity) < self.walk_speed:
- self.rolling = False
- self.anim_lock = True
- self.sprite = hedgehog_uncompress_sprite
- self.image_index = 0
- self.image_speed = None
- if not self.anim_lock:
- self.sprite = hedgehog_ball_sprite
- self.image_speed = (abs(self.xvelocity) *
- self.roll_frames_per_pixel)
- if abs(self.xvelocity) >= self.roll_max_speed:
- self.xvelocity = math.copysign(self.roll_max_speed,
- self.xvelocity)
- else:
- if self.was_on_floor:
- self.xdeceleration = self.friction
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- yvec = target.y - self.image_ycenter
- dist = math.hypot(xvec, yvec)
- if dist <= self.charge_distance:
- self.xacceleration = math.copysign(self.acceleration,
- xvec)
- self.xdeceleration = 0
- if abs(self.xvelocity) >= self.max_speed:
- self.xvelocity = math.copysign(self.max_speed,
- self.xvelocity)
- self.rolling = True
- self.anim_lock = True
- self.sprite = hedgehog_compress_sprite
- self.image_index = 0
- self.image_speed = None
- else:
- self.xdeceleration = 0
- if not self.anim_lock:
- if self.xvelocity:
- self.sprite = hedgehog_walk_sprite
- self.image_speed = (abs(self.xvelocity) *
- self.walk_frames_per_pixel)
- else:
- self.sprite = hedgehog_stand_sprite
- self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
- def event_animation_end(self):
- self.anim_lock = False
- class Worm(Enemy, InteractiveCollider, CrowdBlockingObject):
- hp = 5
- touch_damage = 10
- extend_distance = 96
- repeat_delay = 90
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = worm_sprite
- kwargs["collision_precise"] = True
- kwargs["tangible"] = False
- kwargs["image_fps"] = 0
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def event_create(self):
- sge.dsp.Object.create(
- self.x, self.y, self.z + 0.1, sprite=worm_base_sprite,
- tangible=False)
- def event_step(self, time_passed, delta_mult):
- super(Worm, self).event_step(time_passed, delta_mult)
- if not self.tangible and "extend_wait" not in self.alarms:
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- yvec = target.y - self.image_ycenter
- dist = math.hypot(xvec, yvec)
- if dist <= self.extend_distance:
- self.tangible = True
- self.image_fps = None
- self.image_index = 0
- def event_animation_end(self):
- self.tangible = False
- self.image_fps = 0
- self.image_index = 0
- self.alarms["extend_wait"] = self.repeat_delay
- class Bat(Enemy, InteractiveCollider, CrowdBlockingObject):
- charge_distance = 200
- charge_speed = 3
- return_speed = 2
- return_delay = 15
- repeat_delay = 60
- def __init__(self, x, y, **kwargs):
- self.returning = False
- self.path = None
- kwargs["sprite"] = bat_sprite
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def attack(self, target):
- xvec = target.x - self.image_xcenter
- yvec = target.y - self.image_ycenter
- dist = math.hypot(xvec, yvec)
- if dist <= self.charge_distance:
- self.speed = self.charge_speed
- self.move_direction = math.degrees(math.atan2(yvec, xvec))
- self.image_speed = self.sprite.speed * 2
- def stop(self):
- if self.path is not None:
- self.path.follow_stop(self)
- self.path = None
- self.speed = 0
- self.image_speed = None
- if self.returning:
- self.returning = False
- self.alarms["charge_wait"] = self.repeat_delay
- else:
- self.returning = True
- self.alarms["return"] = self.return_delay
- def stop_left(self):
- self.stop()
- def stop_right(self):
- self.stop()
- def stop_up(self):
- self.stop()
- def stop_down(self):
- self.stop()
- def event_create(self):
- self.image_xscale *= random.choice([1, -1])
- def event_step(self, time_passed, delta_mult):
- super(Bat, self).event_step(time_passed, delta_mult)
- if (self.speed == 0 and "charge_wait" not in self.alarms and
- not self.returning):
- target = self.get_nearest_player()
- if target is not None:
- self.attack(target)
- if self.xvelocity:
- self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
- def event_alarm(self, alarm_id):
- if alarm_id == "return":
- xvec = self.xstart - self.x
- yvec = self.ystart - self.y
- self.path = xsge_path.Path.create(self.x, self.y,
- points=[(xvec, yvec)])
- def evt_follow_end(obj, self=self.path): obj.stop()
- self.path.event_follow_end = evt_follow_end
- self.path.follow_start(self, self.return_speed)
- class Jellyfish(Enemy, CrowdBlockingObject):
- hp = 3
- touch_damage = 7
- swim_speed = 2
- friction = 0.025
- swim_interval = FPS
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = jellyfish_idle_sprite
- kwargs["regulate_origin"] = True
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def stop_left(self):
- self.xvelocity = 0
- self.yvelocity = 0
- def stop_right(self):
- self.xvelocity = 0
- self.yvelocity = 0
- def stop_up(self):
- self.xvelocity = 0
- self.yvelocity = 0
- def stop_down(self):
- self.xvelocity = 0
- self.yvelocity = 0
- def event_create(self):
- self.x += self.image_origin_x
- self.y += self.image_origin_y
- self.bbox_x = -9
- self.bbox_y = -9
- self.bbox_width = 17
- self.bbox_height = 17
- self.xdeceleration = self.friction
- self.ydeceleration = self.friction
- self.alarms["swim"] = self.swim_interval
- def event_alarm(self, alarm_id):
- super(Jellyfish, self).event_alarm(alarm_id)
- if alarm_id == "swim":
- choices = 3 * [1] + [-1]
- xv = random.choice(choices)
- if self.x > self.xstart:
- xv *= -1
- yv = random.choice(choices)
- if self.y > self.ystart:
- yv *= -1
- self.image_xscale = math.copysign(self.image_xscale, xv)
- self.image_yscale = math.copysign(self.image_yscale, yv)
- self.sprite = jellyfish_swim_start_sprite
- self.image_index = 0
- def event_animation_end(self):
- if self.sprite == jellyfish_swim_start_sprite:
- self.sprite = jellyfish_swim_sprite
- self.image_index = 0
- self.xvelocity = math.copysign(self.swim_speed, self.image_xscale)
- self.yvelocity = math.copysign(self.swim_speed, self.image_yscale)
- self.alarms["swim"] = self.swim_interval
- elif self.sprite != jellyfish_idle_sprite:
- self.sprite = jellyfish_idle_sprite
- self.image_index = 0
- class Scorpion(Enemy, WalkingObject, CrowdObject):
- walk_speed = 1
- hp = 10
- touch_damage = 10
- stayonplatform = True
- slopeisplatform = False
- sight_distance = 1000
- sight_threshold = 64
- shoot_interval = FPS
- shoot_recheck_interval = FPS / 10
- bullet_speed = 3
- def __init__(self, x, y, **kwargs):
- x += scorpion_stand_sprite.origin_x
- y += scorpion_stand_sprite.origin_y
- kwargs["sprite"] = scorpion_stand_sprite
- kwargs["bbox_x"] = -28
- kwargs["bbox_y"] = -1
- kwargs["bbox_width"] = 56
- kwargs["bbox_height"] = 28
- super(Scorpion, self).__init__(x, y, **kwargs)
- def move(self):
- if not self.action:
- super(Scorpion, self).move()
- if self.xvelocity:
- self.sprite = scorpion_walk_sprite
- self.image_speed = (abs(self.xvelocity) *
- SCORPION_WALK_FRAMES_PER_PIXEL)
- else:
- self.sprite = scorpion_stand_sprite
- def attack(self):
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- yvec = target.y - self.image_ycenter
- if (abs(xvec) <= self.sight_distance and
- abs(yvec) < self.sight_threshold):
- self.xvelocity = 0
- self.action = "shoot_start"
- self.sprite = scorpion_shoot_start_sprite
- self.image_index = 0
- self.image_fps = None
- self.image_xscale = math.copysign(self.image_xscale, xvec)
- return
- self.alarms["shoot"] = self.shoot_recheck_interval
- def event_create(self):
- super(Scorpion, self).event_create()
- self.alarms["shoot"] = self.shoot_interval
- self.action = None
- def event_alarm(self, alarm_id):
- super(Scorpion, self).event_alarm(alarm_id)
- if alarm_id == "shoot":
- self.attack()
- def event_animation_end(self):
- if self.action == "shoot_start":
- self.action = "shoot_end"
- self.sprite = scorpion_shoot_end_sprite
- self.image_index = 0
- self.image_fps = None
- xv = math.copysign(self.bullet_speed, self.image_xscale)
- x = self.x
- if self.image_xscale < 0:
- x -= scorpion_projectile_sprite.width
- ScorpionBullet.create(
- x, self.y, self.z + 0.1,
- sprite=scorpion_projectile_sprite, xvelocity=xv,
- image_xscale=self.image_xscale, image_yscale=self.image_yscale)
- play_sound(scorpion_shoot_sound, self.x, self.y)
- elif self.action == "shoot_end":
- self.action = None
- self.sprite = scorpion_stand_sprite
- self.alarms["shoot"] = self.shoot_interval
- class Mantanoid(Enemy, FallingObject, CrowdBlockingObject):
- classname = "Mantanoid"
- hp = 10
- touch_damage = 10
- slash_damage = 20
- sight_distance = 300
- def __init__(self, x, y, hiding=False, wander_x=None, **kwargs):
- x += mantanoid_stand_sprite.origin_x
- y += mantanoid_stand_sprite.origin_y
- kwargs["sprite"] = mantanoid_stand_sprite
- kwargs["bbox_x"] = MANTANOID_BBOX_X
- kwargs["bbox_y"] = MANTANOID_BBOX_Y
- kwargs["bbox_width"] = MANTANOID_BBOX_WIDTH
- kwargs["bbox_height"] = MANTANOID_BBOX_HEIGHT
- kwargs["regulate_origin"] = True
- super(Mantanoid, self).__init__(x, y, **kwargs)
- self.hiding = hiding
- self.wander_x = wander_x if wander_x else x
- self.action = None
- self.target = None
- self.movement_speed = 0
- self.can_act = False
- self.action_check = None
- self.action_check_id = None
- self.action_check_x = None
- self.action_check_y = None
- self.action_check_dest_x = None
- self.action_check_dest_y = None
- self.action_check_verify = None
- def set_direction(self, direction):
- if not self.action and self.can_act:
- if direction > 0:
- self.perform_action(self.action_turn_right)
- elif direction < 0:
- self.perform_action(self.action_turn_left)
- def stop_left(self):
- if self.yvelocity > 0:
- self.xvelocity = 0
- if not self.action and self.can_act:
- if self.target is not None:
- if not self.check_action(self.action_hop, self.target.x,
- self.target.y, "stop_down"):
- if not self.check_action(self.action_jump, self.target.x,
- self.target.y, "stop_down"):
- self.target = None
- else:
- self.perform_action(self.action_turn_right)
- def stop_right(self):
- if self.yvelocity > 0:
- self.xvelocity = 0
- if not self.action and self.can_act:
- if self.target is not None:
- if not self.check_action(self.action_hop, self.target.x,
- self.target.y, "stop_down"):
- if not self.check_action(self.action_jump, self.target.x,
- self.target.y, "stop_down"):
- self.target = None
- else:
- self.perform_action(self.action_turn_left)
- def stop_up(self):
- self.yvelocity = 0
- if (self.action_check_verify == "stop_down" and
- self.get_bottom_touching_wall()):
- self.verify_action()
- def stop_down(self):
- self.xvelocity = 0
- self.yvelocity = 0
- if not self.was_on_floor:
- if not self.action or self.action == "animation":
- self.sprite = mantanoid_land_sprite
- self.fps = None
- self.image_index = 0
- self.action = "animation"
- if self.action_check_verify == "stop_down":
- self.verify_action()
- def update_wander(self):
- if (not self.hiding and self.was_on_floor and
- "move_lock" not in self.alarms):
- choices = 3 * [1] + 7 * [0] + [-1]
- xv = random.choice(choices)
- if self.x > self.wander_x:
- xv *= -1
- self.set_direction(xv)
- self.movement_speed = MANTANOID_WANDER_SPEED * abs(xv)
- self.alarms["move_lock"] = MANTANOID_WANDER_INTERVAL
- def perform_action(self, action):
- if not self.action and self.can_act:
- self.can_act = False
- self.reset_action_check()
- self.xvelocity = 0
- if (self.target is not None and self.target.x < self.x and
- self.image_xscale > 0):
- self.action_turn_left()
- elif (self.target is not None and self.target.x > self.x and
- self.image_xscale < 0):
- self.action_turn_right()
- else:
- action()
- def check_action(self, action, target_x, target_y, verify_event):
- if not self.can_act:
- return False
- if target_x is None and target_y is None:
- self.perform_action(action)
- return True
- if target_x is not None and target_y is not None:
- if sge.collision.rectangle(target_x, target_y, 1, 1, MantanoidNoGo):
- # No-go zone, which means we can't go there no matter what.
- return False
- def rough(x):
- return int(math.floor(x / 8)) * 8 if x is not None else None
- action_id = "{}; {}: ({},{})->({},{})!{}~{} -- ".format(
- self.__class__.__name__, sge.game.current_room.fname,
- rough(self.x), rough(self.y), rough(target_x), rough(target_y),
- action.__name__, verify_event)
- action_id_pass = action_id + "pass"
- action_id_fail = action_id + "fail"
- if action_id_pass in ai_data:
- if action_id_fail not in ai_data or random.random() < 0.25:
- if self.target.x < self.x and self.image_xscale > 0:
- self.perform_action(self.action_turn_left)
- return True
- elif self.target.x > self.x and self.image_xscale < 0:
- self.perform_action(self.action_turn_right)
- return True
- else:
- self.perform_action(action)
- return True
- else:
- return False
- elif action_id_fail not in ai_data:
- if self.target.x < self.x and self.image_xscale > 0:
- self.perform_action(self.action_turn_left)
- return True
- elif self.target.x > self.x and self.image_xscale < 0:
- self.perform_action(self.action_turn_right)
- return True
- else:
- self.perform_action(action)
- self.action_check = action
- self.action_check_id = action_id
- self.action_check_x = self.x
- self.action_check_y = self.y
- self.action_check_dest_x = target_x
- self.action_check_dest_y = target_y
- self.action_check_verify = verify_event
- return True
- return False
- def verify_action(self):
- if self.action_check is not None:
- if (self.action_check_dest_x is None and
- self.action_check_dest_y is None):
- ai_data.add(self.action_check_id + "pass")
- elif self.action_check_dest_x is None:
- orig_dist = abs(self.action_check_dest_y - self.action_check_y)
- new_dist = abs(self.action_check_dest_y - self.y)
- elif self.action_check_dest_y is None:
- orig_dist = abs(self.action_check_dest_x - self.action_check_x)
- new_dist = abs(self.action_check_dest_x - self.x)
- else:
- orig_dist = abs(math.hypot(
- self.action_check_dest_x - self.action_check_x,
- self.action_check_dest_y - self.action_check_y))
- new_dist = abs(math.hypot(
- self.action_check_dest_x - self.x,
- self.action_check_dest_y - self.y))
- if new_dist < orig_dist:
- ai_data.add(self.action_check_id + "pass")
- else:
- ai_data.add(self.action_check_id + "fail")
- self.reset_action_check()
- def deverify_action(self):
- if self.action_check is not None:
- ai_data.add(self.action_check_id + "fail")
- self.reset_action_check()
- def reset_action_check(self):
- self.action_check = None
- self.action_check_id = None
- self.action_check_x = None
- self.action_check_y = None
- self.action_check_dest_x = None
- self.action_check_dest_y = None
- self.action_check_verify = None
- def action_turn_left(self):
- if (self.image_xscale > 0 and not self.action and
- self.was_on_floor and self.get_bottom_touching_wall()):
- self.image_xscale = -abs(self.image_xscale)
- self.sprite = mantanoid_turn_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "animation"
- def action_turn_right(self):
- if (self.image_xscale < 0 and not self.action and
- self.was_on_floor and self.get_bottom_touching_wall()):
- self.image_xscale = abs(self.image_xscale)
- self.sprite = mantanoid_turn_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "animation"
- def action_hop(self):
- if (self.was_on_floor and self.yvelocity >= 0 and
- (self.get_bottom_touching_wall() or
- self.get_bottom_touching_slope())):
- self.sprite = mantanoid_hop_start_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "hop"
- def action_jump(self):
- if (self.was_on_floor and self.yvelocity >= 0 and
- (self.get_bottom_touching_wall() or
- self.get_bottom_touching_slope())):
- self.sprite = mantanoid_jump_start_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "jump"
- def action_approach(self):
- self.hiding = False
- if self.target is not None:
- if self.movement_speed != MANTANOID_APPROACH_SPEED:
- self.movement_speed = MANTANOID_APPROACH_SPEED
- play_sound(mantanoid_approach_sound, self.x, self.y)
- self.alarms["action_lock"] = MANTANOID_APPROACH_INTERVAL
- def action_slash(self):
- self.hiding = False
- if self.target is not None:
- self.action = "slash"
- self.sprite = mantanoid_slash_start_sprite
- self.image_fps = None
- self.image_index = 0
- def check_hazards(self):
- return None
- def update_action(self):
- action = self.check_hazards()
- if not action:
- if (self.target is not None and
- not self.target.collision(MantanoidNoGo)):
- xdist = abs(self.target.x - self.x)
- ydist = abs(self.target.y - self.y)
- if (xdist <= MANTANOID_SLASH_DISTANCE and
- ydist <= MANTANOID_LEVEL_DISTANCE):
- action = self.action_slash
- elif "action_lock" not in self.alarms:
- if ydist <= MANTANOID_LEVEL_DISTANCE:
- action = self.action_approach
- elif not self.hiding:
- if self.target.on_floor and self.check_action(
- self.action_approach, None, self.target.y,
- "action_lock"):
- return
- elif self.target.on_floor and self.check_action(
- self.action_jump, None, self.target.y,
- "stop_down"):
- return
- elif self.target.on_floor and self.check_action(
- self.action_hop, None, self.target.y,
- "stop_down"):
- return
- elif self.check_action(
- self.action_approach, self.target.x,
- self.target.y, "action_lock"):
- return
- else:
- self.target = None
- if action:
- self.perform_action(action)
- else:
- self.update_wander()
- def set_image(self):
- if not self.action:
- if self.was_on_floor:
- if self.xvelocity:
- self.sprite = mantanoid_walk_sprite
- self.image_speed = abs(
- self.xvelocity * MANTANOID_WALK_FRAMES_PER_PIXEL)
- else:
- self.sprite = mantanoid_stand_sprite
- if random.random() < 1 / (2 * FPS):
- self.sprite = mantanoid_idle_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "animation"
- else:
- if self.yvelocity < 0:
- self.sprite = mantanoid_jump_sprite
- else:
- if self.sprite == mantanoid_jump_sprite:
- self.sprite = mantanoid_fall_start_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "animation"
- if self.action_check_verify == "peak":
- self.verify_action()
- else:
- self.sprite = mantanoid_fall_sprite
- def event_step(self, time_passed, delta_mult):
- on_floor = self.get_bottom_touching_wall()
- self.can_act = (self.was_on_floor and on_floor and self.yvelocity >= 0)
- if not self.action and self.can_act:
- self.target = self.get_nearest_player()
- dist = 0
- if self.target is not None:
- xvec = self.target.x - self.x
- yvec = self.target.y - self.y
- dist = math.hypot(xvec, yvec)
- if dist > self.sight_distance:
- self.target = None
- self.update_action()
- if not self.action:
- if ((self.image_xscale > 0 or not self.get_left_touching_wall()) and
- (self.image_xscale < 0 or not self.get_right_touching_wall())):
- self.xvelocity = self.movement_speed * self.image_xscale
- on_slope = self.get_bottom_touching_slope()
- if (on_floor or on_slope):
- if self.xvelocity < 0:
- for tile in on_floor:
- if tile.bbox_left < self.bbox_left:
- break
- else:
- if not on_slope:
- if self.can_act:
- if self.target is not None:
- if (not self.check_action(
- self.action_hop, self.target.x,
- self.target.y, "stop_down") and
- not self.check_action(
- self.action_jump,
- self.target.x, self.target.y,
- "stop_down") and
- not self.check_action(
- self.action_approach,
- self.target.x, self.target.y,
- "stop_down")):
- self.target = None
- else:
- self.perform_action(self.action_turn_right)
- else:
- self.xvelocity = 0
- else:
- for tile in on_floor:
- if tile.bbox_right > self.bbox_right:
- break
- else:
- if not on_slope:
- if self.can_act:
- if self.target is not None:
- if (not self.check_action(
- self.action_hop, self.target.x,
- self.target.y, "stop_down") and
- not self.check_action(
- self.action_jump,
- self.target.x, self.target.y,
- "stop_down") and
- not self.check_action(
- self.action_approach,
- self.target.x, self.target.y,
- "stop_down")):
- self.target = None
- else:
- self.perform_action(self.action_turn_left)
- else:
- self.xvelocity = 0
- self.set_image()
- def event_alarm(self, alarm_id):
- super(Mantanoid, self).event_alarm(alarm_id)
- if alarm_id == "action_lock":
- if self.action_check_verify == "action_lock":
- self.verify_action()
- def event_animation_end(self):
- if self.action == "hop":
- self.action = None
- self.can_act = False
- self.set_image()
- if self.was_on_floor and (self.get_bottom_touching_wall() or
- self.get_bottom_touching_slope()):
- self.xvelocity = math.copysign(MANTANOID_APPROACH_SPEED,
- self.image_xscale)
- self.yvelocity = get_jump_speed(MANTANOID_HOP_HEIGHT,
- self.gravity)
- if self.action == "jump":
- self.action = None
- self.can_act = False
- self.set_image()
- if self.was_on_floor and (self.get_bottom_touching_wall() or
- self.get_bottom_touching_slope()):
- self.xvelocity = math.copysign(MANTANOID_APPROACH_SPEED,
- self.image_xscale)
- self.yvelocity = get_jump_speed(MANTANOID_JUMP_HEIGHT,
- self.gravity)
- elif self.action == "slash":
- hit_target = False
- w = MANTANOID_SLASH_BBOX_WIDTH
- h = MANTANOID_SLASH_BBOX_HEIGHT
- if self.image_xscale > 0:
- x = self.x + MANTANOID_SLASH_BBOX_X
- y = self.y + MANTANOID_SLASH_BBOX_Y
- else:
- x = self.x - MANTANOID_SLASH_BBOX_X - w
- y = self.y - MANTANOID_SLASH_BBOX_Y - h
- for other in sge.collision.rectangle(x, y, w, h):
- if isinstance(other, Player):
- other.hurt(self.slash_damage)
- if other is self.target:
- hit_target = True
- elif isinstance(other, AnneroyBullet):
- other.dissipate(other.xvelocity, other.yvelocity)
- double = False
- if self.target is not None and not hit_target:
- xdist = abs(self.target.x - self.x)
- ydist = abs(self.target.y - self.y)
- if (ydist <= MANTANOID_LEVEL_DISTANCE and
- xdist <= MANTANOID_SLASH2_DISTANCE):
- double = True
- play_sound(mantanoid_slash_sound, self.x, self.y)
- self.image_fps = None
- self.image_index = 0
- if double:
- self.sprite = mantanoid_slash_double_first_sprite
- self.action = "doubleslash"
- else:
- self.sprite = mantanoid_slash_single_sprite
- self.action = "animation"
- elif self.action == "doubleslash":
- self.move_x(MANTANOID_DOUBLESLASH_OFFSET * self.image_xscale)
- w = MANTANOID_SLASH2_BBOX_WIDTH
- h = MANTANOID_SLASH2_BBOX_HEIGHT
- if self.image_xscale > 0:
- x = self.x + MANTANOID_SLASH2_BBOX_X
- y = self.y + MANTANOID_SLASH2_BBOX_Y
- else:
- x = self.x - MANTANOID_SLASH2_BBOX_X - w
- y = self.y - MANTANOID_SLASH2_BBOX_Y - h
- for other in sge.collision.rectangle(x, y, w, h):
- if isinstance(other, Player):
- other.hurt(self.slash_damage)
- elif isinstance(other, AnneroyBullet):
- other.dissipate(other.xvelocity, other.yvelocity)
- play_sound(mantanoid_slash_sound, self.x, self.y)
- self.sprite = mantanoid_slash_double_second_sprite
- self.image_fps = None
- self.image_index = 0
- self.action = "animation"
- elif self.action == "animation":
- self.action = None
- self.set_image()
- class MantanoidNoGo(sge.dsp.Object):
- def __init__(self, x, y, **kwargs):
- kwargs["visible"] = False
- kwargs["checks_collisions"] = False
- super(MantanoidNoGo, self).__init__(x, y, **kwargs)
- class Boss(InteractiveObject):
- def __init__(self, x, y, ID="boss", death_timeline=None, stage=0,
- **kwargs):
- self.ID = ID
- self.death_timeline = death_timeline
- self.stage = stage
- super(Boss, self).__init__(x, y, **kwargs)
- def event_create(self):
- super(Boss, self).event_create()
- sge.game.current_room.add_timeline_object(self)
- def event_destroy(self):
- for obj in sge.game.current_room.objects:
- if obj is not self and isinstance(obj, Boss) and obj.stage > 0:
- break
- else:
- if self.death_timeline:
- sge.game.current_room.load_timeline(self.death_timeline)
- class LifeForce(InteractiveObject):
- def __init__(self, *args, **kwargs):
- kwargs["sprite"] = life_force_sprite
- super(LifeForce, self).__init__(*args, **kwargs)
- def move(self):
- if "set_direction" not in self.alarms:
- self.alarms["set_direction"] = FPS / 4
- target = self.get_nearest_player()
- if target is not None:
- xvec = target.x - self.image_xcenter
- yvec = max(target.y, target.bbox_top) - self.image_ycenter
- self.speed = LIFE_FORCE_SPEED
- self.move_direction = math.degrees(math.atan2(yvec, xvec))
- else:
- self.speed = 0
- def touch(self, other):
- other.hp += LIFE_FORCE_HEAL
- play_sound(heal_sound, other.x, other.y)
- self.destroy()
- class Bullet(InteractiveObject):
- attacks_player = False
- attacks_enemy = False
- attacks_bullet = True
- attacks_wall = True
- breaks_stone = False
- player_damage = 5
- life = None
- def dissipate(self, xdirection=0, ydirection=0):
- """
- Show the appropriate dissipation animation for the bullet, based
- on what direction the bullet hit something in, and destroy the
- bullet. Default behavior just calls "self.destroy()".
- """
- self.destroy()
- def shoot_player(self, other):
- other.hurt(self.player_damage)
- def shoot_enemy(self, other):
- other.shoot(self)
- def event_create(self):
- if self.life is not None:
- self.alarms["die"] = self.life
- def event_step(self, time_passed, delta_mult):
- room = sge.game.current_room
- if (self.bbox_right < 0 or self.bbox_left > room.width or
- self.bbox_bottom < 0 or self.bbox_top > room.height):
- self.destroy()
- def event_collision(self, other, xdirection, ydirection):
- super(Bullet, self).event_collision(other, xdirection, ydirection)
- if isinstance(other, Player):
- if self.attacks_player:
- self.shoot_player(other)
- self.dissipate(xdirection, ydirection)
- elif isinstance(other, Bullet):
- if (self.attacks_bullet and
- ((self.attacks_player and other.attacks_enemy) or
- (self.attacks_enemy and other.attacks_player))):
- xd = math.copysign(1, other.xvelocity)
- yd = math.copysign(1, other.yvelocity)
- other.dissipate(xd, yd)
- elif isinstance(other, InteractiveObject) and other.shootable:
- if self.attacks_enemy:
- self.shoot_enemy(other)
- self.dissipate(xdirection, ydirection)
- elif isinstance(other, xsge_physics.Wall) and self.attacks_wall:
- point_x = self.x
- point_y = self.y
- if ((self.xvelocity > 0 and self.yvelocity > 0) or
- (self.xvelocity < 0 and self.yvelocity < 0)):
- collisions = sge.collision.line(
- self.bbox_left, self.bbox_top, self.bbox_right,
- self.bbox_bottom)
- elif ((self.xvelocity > 0 and self.yvelocity < 0) or
- (self.xvelocity < 0 and self.yvelocity > 0)):
- collisions = sge.collision.line(
- self.bbox_left, self.bbox_bottom, self.bbox_right,
- self.bbox_top)
- elif self.xvelocity:
- collisions = sge.collision.rectangle(
- self.bbox_left, self.y, self.bbox_width, 1)
- elif self.yvelocity:
- collisions = sge.collision.rectangle(
- self.x, self.bbox_top, 1, self.bbox_height)
- else:
- warnings.warn("Bullet is not moving!")
- collisions = []
- if self.xvelocity:
- if self.xvelocity > 0:
- slope_cls = (xsge_physics.SlopeTopLeft,
- xsge_physics.SlopeBottomLeft)
- cls = (xsge_physics.SolidLeft,) + slope_cls
- else:
- slope_cls = (xsge_physics.SlopeTopRight,
- xsge_physics.SlopeBottomRight)
- cls = (xsge_physics.SolidRight,) + slope_cls
- touching = False
- if collisions:
- for obj in collisions:
- if isinstance(obj, cls):
- if isinstance(obj, slope_cls):
- if self.xvelocity > 0:
- collision_real = (
- point_x > obj.get_slope_x(point_y))
- else:
- collision_real = (
- point_x < obj.get_slope_x(point_y))
- else:
- collision_real = True
- if collision_real:
- touching = True
- if (self.breaks_stone and
- isinstance(obj, Stone) and
- obj.shootable):
- obj.destroy()
- if touching:
- self.dissipate(xdirection, ydirection)
- if self.yvelocity:
- if self.yvelocity > 0:
- slope_cls = (xsge_physics.SlopeTopLeft,
- xsge_physics.SlopeTopRight)
- cls = (xsge_physics.SolidTop,) + slope_cls
- else:
- slope_cls = (xsge_physics.SlopeBottomLeft,
- xsge_physics.SlopeBottomRight)
- cls = (xsge_physics.SolidBottom,) + slope_cls
- touching = False
- if collisions:
- for obj in collisions:
- if isinstance(obj, cls):
- if isinstance(obj, slope_cls):
- if self.yvelocity > 0:
- collision_real = (
- point_y > obj.get_slope_y(point_x))
- else:
- collision_real = (
- point_y < obj.get_slope_y(point_x))
- else:
- collision_real = True
- if collision_real:
- touching = True
- if (self.breaks_stone and
- isinstance(obj, Stone) and
- obj.shootable):
- obj.destroy()
- if touching:
- self.dissipate(xdirection, ydirection)
- def event_alarm(self, alarm_id):
- if alarm_id == "die":
- self.destroy()
- class AnneroyBullet(Bullet):
- attacks_enemy = True
- attacks_bullet = False
- breaks_stone = True
- life = ANNEROY_BULLET_LIFE
- def dissipate(self, xdirection=0, ydirection=0):
- if self in sge.game.current_room.objects:
- image_rotation = 0
- if abs(xdirection) > abs(ydirection):
- if xdirection < 0:
- image_rotation = 180
- else:
- image_rotation = 0
- elif abs(ydirection) > abs(xdirection):
- if ydirection < 0:
- image_rotation = 270
- else:
- image_rotation = 90
- elif abs(self.xvelocity) > abs(self.yvelocity):
- if self.xvelocity < 0:
- image_rotation = 180
- else:
- image_rotation = 0
- else:
- if self.yvelocity < 0:
- image_rotation = 270
- else:
- image_rotation = 90
- play_sound(bullet_death_sound, self.x, self.y)
- Smoke.create(
- self.x, self.y, self.z, sprite=anneroy_bullet_dissipate_sprite,
- regulate_origin=True, image_xscale=self.image_xscale,
- image_yscale=self.image_yscale, image_rotation=image_rotation,
- image_blend=self.image_blend)
- self.destroy()
- class ScorpionBullet(Bullet):
- attacks_player = True
- player_damage = 15
- shard_num = 50
- shard_speed_min = 1
- shard_speed_max = 3
- def dissipate(self, xdirection=0, ydirection=0):
- self.destroy()
- play_sound(scorpion_projectile_break_sound, self.image_xcenter,
- self.image_ycenter)
- for i in six.moves.range(self.shard_num):
- life = random.uniform(FPS / 8, FPS / 2)
- image_index = random.randrange(
- 0, scorpion_projectile_shard_sprite.frames)
- shard = xsge_particle.TimedParticle.create(
- self.x, self.y, self.z, life=life,
- sprite=scorpion_projectile_shard_sprite,
- image_index=image_index)
- shard.speed = random.randint(self.shard_speed_min,
- self.shard_speed_max)
- shard.move_direction = random.randrange(360)
- class HedgehogSpikes(InteractiveObject):
- spike_hitstun = FPS / 4
- def event_collision(self, other, xdirection, ydirection):
- super(HedgehogSpikes, self).event_collision(other, xdirection, ydirection)
- if isinstance(other, InteractiveObject) and other.spikeable:
- if "spike_hitstun" not in other.alarms:
- other.spike(self)
- other.alarms["spike_hitstun"] = self.spike_hitstun
-
- elif isinstance(other, Stone) and other.spikeable:
- other.destroy()
- class FakeTile(sge.dsp.Object):
- def event_create(self):
- self.tangible = False
- class Stone(xsge_physics.Solid):
- shard_num_min = 1
- shard_num_max = 4
- shard_speed_min = 2
- shard_speed_max = 5
- shootable = False
- spikeable = False
- fakes = ()
- def event_create(self):
- self.checks_collisions = False
- self.fakes = []
- for other in sge.game.current_room.get_objects_at(
- self.image_left, self.image_top, self.image_width,
- self.image_height):
- if (isinstance(other, FakeTile) and
- self.image_left < other.image_right and
- self.image_right > other.image_left and
- self.image_top < other.image_bottom and
- self.image_bottom > other.image_top):
- self.fakes.append(other)
- def event_destroy(self):
- play_sound(stone_break_sound, self.image_xcenter, self.image_ycenter)
- for other in self.fakes:
- other.destroy()
- if sge.game.fps_real >= FPS:
- shard_num = random.randint(self.shard_num_min, self.shard_num_max)
- else:
- shard_num = self.shard_num_min
- for i in six.moves.range(shard_num):
- shard = Shard.create(self.x, self.y, self.z,
- sprite=stone_fragment_sprite)
- shard.speed = random.randint(self.shard_speed_min,
- self.shard_speed_max)
- shard.move_direction = random.randrange(360)
- class WeakStone(Stone):
- shootable = True
- spikeable = True
- class SpikeStone(Stone):
- spikeable = True
- class Macguffin(InteractiveObject):
- def touch(self, other):
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- play_sound(powerup_sound, self.image_xcenter, self.image_ycenter)
- msg1 = _("MACGUFFIN\n\nThis is the end of the demo! Thank you for playing Hexoshi version {}.".format(__version__))
- 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! :)")
- DialogBox(gui_handler, msg1, self.sprite).show()
- DialogBox(gui_handler, msg2, self.sprite).show()
- sge.game.current_room.win_game()
- class Powerup(InteractiveObject):
- message = _("USELESS ITEM\n\nIt doesn't seem to do anything")
- def collect(self, other):
- pass
- def touch(self, other):
- global powerups
- global map_removed
- play_sound(powerup_sound, self.image_xcenter, self.image_ycenter)
- i = (self.__class__.__name__, sge.game.current_room.fname,
- int(self.x), int(self.y))
- powerups = powerups[:]
- powerups.append(i)
- # Remove the powerup from the map
- px = get_xregion(self.image_xcenter)
- py = get_yregion(self.image_ycenter)
- map_removed.append(("powerup", sge.game.current_room.fname, px, py))
- self.collect(other)
- for obj in sge.game.current_room.objects:
- if isinstance(obj, Player):
- obj.update_hud()
- DialogBox(gui_handler, self.message, self.sprite).show()
- self.destroy()
- def event_create(self):
- i = (self.__class__.__name__, sge.game.current_room.fname,
- int(self.x), int(self.y))
- if i in powerups:
- self.destroy()
- class Artifact(Powerup):
- message = ""
- def __init__(self, x, y, message="It doesn't seem to to anything", **kwargs):
- super(Artifact, self).__init__(x, y, **kwargs)
- self.message = _("HEXOSHI ARTIFACT\n\n{}").format(_(message))
- def collect(self, other):
- global artifacts
- artifacts += 1
- # TODO: Check to see if this is the last artifact, and set up a
- # victory condition if so.
- class Etank(Powerup):
- message = _("E-TANK\n\nExtra energy capacity acquired")
- def collect(self, other):
- global etanks
- etanks += 1
- other.refresh()
- class LifeOrb(Powerup):
- message = _("LIFE ORB\n\nAbsorb life force from defeated enemies")
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = life_orb_sprite
- super(LifeOrb, self).__init__(x, y, **kwargs)
- def collect(self, other):
- global progress_flags
- progress_flags = progress_flags[:]
- progress_flags.append("life_orb")
- class Map(Powerup):
- message = _(
- 'HANDHELD MAP\n\nSee mini-map in HUD; see full map by pressing "map" button or from pause menu')
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = powerup_map_sprite
- super(Map, self).__init__(x, y, **kwargs)
- def collect(self, other):
- global progress_flags
- progress_flags = progress_flags[:]
- progress_flags.append("map")
- class MapDisk(Powerup):
- message = _("MAP DISK\n\nArea map data loaded")
- def __init__(self, x, y, rooms=None, **kwargs):
- if rooms:
- self.rooms = rooms.split(',')
- else:
- self.rooms = []
- super(MapDisk, self).__init__(x, y, **kwargs)
- def collect(self, other):
- global map_revealed
- for fname in self.rooms:
- sge.game.pump_input()
- if fname in map_rooms:
- room = Level.load(fname, True)
- rm_x, rm_y = map_rooms[fname]
- rm_w = int(math.ceil(room.width / SCREEN_SIZE[0]))
- rm_h = int(math.ceil(room.height / SCREEN_SIZE[1]))
- ignore_regions = set()
- for obj in room.objects:
- sge.game.pump_input()
- if isinstance(obj, IgnoreRegion):
- rx1 = rm_x + get_xregion(obj.bbox_left)
- rx2 = rm_x + get_xregion(obj.bbox_right - 1)
- ry1 = rm_y + get_yregion(obj.bbox_top)
- ry2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for ry in six.moves.range(ry1, ry2 + 1):
- for rx in six.moves.range(rx1, rx2 + 1):
- ignore_regions.add((rx, ry))
- for y in six.moves.range(rm_y, rm_y + rm_h):
- for x in six.moves.range(rm_x, rm_x + rm_w):
- sge.game.pump_input()
- if ((x, y) not in ignore_regions and
- (x, y) not in map_revealed):
- map_revealed = map_revealed[:]
- map_revealed.append((x, y))
- sge.game.regulate_speed()
- sge.game.pump_input()
- sge.game.input_events = []
- class AtomicCompressor(Powerup):
- message = _('ATOMIC COMPRESSOR\n\nTo compress: press "down" while crouching, or select with "mode" and then press "shoot"\n\nTo uncompress: press "up"')
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = atomic_compressor_sprite
- super(AtomicCompressor, self).__init__(x, y, **kwargs)
- def collect(self, other):
- global progress_flags
- progress_flags = progress_flags[:]
- progress_flags.append("atomic_compressor")
- class MonkeyBoots(Powerup):
- message = _('MONKEY BOOTS\n\nPress "jump" while touching a wall to wall-jump')
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = monkey_boots_sprite
- super(MonkeyBoots, self).__init__(x, y, **kwargs)
- self.emitter = None
- def event_create(self):
- self.emitter = xsge_particle.Emitter.create(
- self.bbox_left, self.bbox_top, self.z + 0.5, interval=(FPS * 2),
- particle_cls=xsge_particle.AnimationParticle,
- particle_args=[self.x, self.y, self.z + 0.5],
- particle_kwargs={"sprite": monkey_boots_gleam_sprite},
- particle_lambda_args=[
- lambda e: random.uniform(e.x, e.x + 13),
- lambda e: random.uniform(e.y, e.y + 4)])
- super(MonkeyBoots, self).event_create()
- def collect(self, other):
- global progress_flags
- progress_flags = progress_flags[:]
- progress_flags.append("monkey_boots")
- def event_destroy(self):
- if self.emitter is not None:
- self.emitter.destroy()
- self.emitter = None
- class HedgehogHormone(Powerup):
- message = _('HEDGEHOG HORMONE\n\nPress "shoot" while in the form of a ball to grow spikes')
- def __init__(self, x, y, **kwargs):
- kwargs["sprite"] = hedgehog_hormone_sprite
- super(HedgehogHormone, self).__init__(x, y, **kwargs)
- self.emitter = None
- def event_create(self):
- self.emitter = xsge_particle.Emitter.create(
- self.image_xcenter, self.bbox_top + 1, self.z - 0.5, interval=8,
- chance=0.5, particle_cls=xsge_particle.AnimationBubbleParticle,
- particle_args=[self.image_xcenter, self.bbox_top + 1, self.z - 0.5],
- particle_kwargs={"sprite": hedgehog_hormone_bubble_sprite,
- "yvelocity": -0.25, "turn_factor": 20,
- "min_angle": 225, "max_angle": 315})
- super(HedgehogHormone, self).event_create()
- def collect(self, other):
- global progress_flags
- progress_flags = progress_flags[:]
- progress_flags.append("hedgehog_hormone")
- def event_destroy(self):
- if self.emitter is not None:
- self.emitter.destroy()
- self.emitter = None
- class Tunnel(InteractiveObject):
- def __init__(self, x, y, dest=None, **kwargs):
- self.dest = dest
- kwargs["visible"] = False
- kwargs["checks_collisions"] = False
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def touch(self, other):
- global spawn_xoffset
- global spawn_yoffset
- spawn_xoffset = other.x - self.x
- spawn_yoffset = other.y - self.y
- if self.dest:
- warp(self.dest)
- class SpawnPoint(sge.dsp.Object):
- def __init__(self, x, y, spawn_id=None, spawn_direction=None, barrier=None,
- **kwargs):
- self.spawn_id = spawn_id
- self.spawn_direction = spawn_direction
- self.barrier = barrier
- kwargs["visible"] = False
- kwargs["tangible"] = False
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def spawn(self, other):
- other.x = self.x + spawn_xoffset
- other.y = self.y + spawn_yoffset
- if self.spawn_direction == 0:
- other.bbox_left = self.bbox_right
- elif self.spawn_direction == 90:
- other.bbox_top = self.bbox_bottom
- elif self.spawn_direction == 180:
- other.bbox_right = self.bbox_left
- elif self.spawn_direction == 270:
- other.bbox_bottom = self.bbox_top
- other.yvelocity = get_jump_speed(TILE_SIZE / 2, other.gravity)
- other.init_position()
- def event_create(self):
- if spawn_point == self.spawn_id:
- for obj in sge.game.current_room.objects:
- if isinstance(obj, Player):
- self.spawn(obj)
- if self.barrier is not None:
- self.barrier.image_index = self.barrier.sprite.frames - 1
- self.barrier.image_speed = -self.barrier.sprite.speed
- play_sound(door_close_sound, self.barrier.image_xcenter,
- self.barrier.image_ycenter)
- class WarpPad(SpawnPoint):
- message = _('WARP PAD\n\nProgress saved. Press "up" to teleport.')
- def __init__(self, x, y, spawn_id="save", **kwargs):
- self.spawn_id = spawn_id
- self.spawn_direction = None
- self.barrier = None
- self.created = False
- self.activated = False
- kwargs["sprite"] = warp_pad_inactive_sprite
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def activate(self):
- global current_level
- global spawn_point
- global warp_pads
- self.activated = True
- self.sprite = warp_pad_active_sprite
- current_level = sge.game.current_room.fname
- spawn_point = self.spawn_id
- x = get_xregion(self.image_xcenter)
- y = get_yregion(self.image_ycenter)
- i = (sge.game.current_room.fname, self.spawn_id, x, y)
- if i not in warp_pads:
- warp_pads = warp_pads[:]
- warp_pads.append(i)
- def spawn(self, other):
- if not self.created:
- self.create_children()
- other.x = self.image_xcenter
- other.bbox_bottom = self.image_top
- other.z = self.z - 0.5
- other.init_position()
- other.warp_in()
- other.refresh()
- self.activate()
- save_game()
- play_sound(teleport_sound, self.image_xcenter, self.image_ycenter)
- def teleport(self, other):
- x = get_xregion(self.image_xcenter)
- y = get_yregion(self.image_ycenter)
- i = (sge.game.current_room.fname, self.spawn_id, x, y)
- dlg = TeleportDialog(i)
- dlg.show()
- if dlg.selection and dlg.selection != i:
- other.x = self.image_xcenter
- other.bbox_bottom = self.image_top
- other.warp_dest = "{}:{}".format(*dlg.selection[:2])
- other.warp_out()
- play_sound(teleport_sound, self.image_xcenter, self.image_ycenter)
- def create_children(self):
- self.created = True
- self.bbox_x = 8
- self.bbox_y = -4
- self.bbox_width = 32
- self.bbox_height = 4
- Solid.create(self.x + 8, self.y, bbox_width=32, bbox_height=8)
- SlopeTopLeft.create(self.x, self.y, bbox_width=8, bbox_height=8)
- SlopeTopRight.create(self.x + 40, self.y, bbox_width=8, bbox_height=8)
- def event_create(self):
- super(WarpPad, self).event_create()
- if not self.created:
- self.create_children()
- def event_collision(self, other, xdirection, ydirection):
- global progress_flags
- if isinstance(other, Player):
- if xdirection or ydirection:
- if not self.activated:
- self.activate()
- other.refresh()
- play_sound(warp_pad_sound, self.image_xcenter,
- self.image_ycenter)
- if "warp" not in progress_flags:
- progress_flags.append("warp")
- DialogBox(gui_handler, self.message).show()
- save_game()
- class DoorBarrier(InteractiveObject, xsge_physics.Solid):
- shootable = True
- spikeable = True
- parent = None
- def shoot(self, other):
- if self.parent is not None:
- self.parent.shoot(other)
- def event_animation_end(self):
- self.image_speed = 0
- if self.tangible:
- self.image_index = 0
- else:
- self.image_index = self.sprite.frames - 1
- class DoorFrame(InteractiveObject):
- shootable = True
- spikeable = True
- closed_sprite = None
- open_sprite = None
- barrier_sprite = None
- edge1_area = (0, 0, 8, 8)
- edge2_area = (0, 56, 8, 8)
- def __init__(self, x, y, **kwargs):
- self.edge1 = None
- self.edge2 = None
- self.barrier = None
- kwargs["tangible"] = False
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def shoot(self, other):
- if self.barrier is not None:
- self.sprite = self.open_sprite
- self.barrier.tangible = False
- self.barrier.image_speed = self.barrier.sprite.speed
- play_sound(door_open_sound, self.barrier.image_xcenter,
- self.barrier.image_ycenter)
- def event_create(self):
- self.sprite = self.closed_sprite
- x, y, w, h = self.edge1_area
- self.edge1 = Solid.create(self.x + x, self.y + y, bbox_width=w,
- bbox_height=h)
- x, y, w, h = self.edge2_area
- self.edge2 = Solid.create(self.x + x, self.y + y, bbox_width=w,
- bbox_height=h)
- self.barrier = DoorBarrier.create(self.x, self.y, self.z,
- sprite=self.barrier_sprite,
- image_index=0, image_fps=0)
- self.barrier.parent = self
- class DoorFrameX(DoorFrame):
- def event_create(self):
- self.closed_sprite = doorframe_regular_x_closed_sprite
- self.open_sprite = doorframe_regular_x_open_sprite
- self.barrier_sprite = door_barrier_x_sprite
- super(DoorFrameX, self).event_create()
- class DoorFrameY(DoorFrame):
- edge2_area = (56, 0, 8, 8)
- def event_create(self):
- self.closed_sprite = doorframe_regular_y_closed_sprite
- self.open_sprite = doorframe_regular_y_open_sprite
- self.barrier_sprite = door_barrier_y_sprite
- super(DoorFrameY, self).event_create()
- class Door(sge.dsp.Object):
- def __init__(self, x, y, dest=None, spawn_id=None, **kwargs):
- self.dest = dest
- self.spawn_id = spawn_id
- if not spawn_id and dest:
- if ':' in dest:
- level_f, spawn = dest.split(':', 1)
- if level_f:
- self.spawn_id = level_f
- else:
- self.spawn_id = dest
- kwargs["visible"] = False
- kwargs["tangible"] = False
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def event_create(self):
- self.destroy()
- class LeftDoor(Door):
- def event_create(self):
- frame = DoorFrameX.create(self.x, self.y, z=self.z)
- Tunnel.create(frame.barrier.bbox_left - TILE_SIZE,
- frame.barrier.bbox_top, dest=self.dest,
- bbox_width=TILE_SIZE,
- bbox_height=frame.barrier.bbox_height)
- SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
- spawn_id=self.spawn_id, spawn_direction=0,
- barrier=frame.barrier,
- bbox_width=frame.barrier.bbox_width,
- bbox_height=frame.barrier.bbox_height)
- self.destroy()
- class RightDoor(Door):
- def event_create(self):
- frame = DoorFrameX.create(self.x, self.y, z=self.z)
- Tunnel.create(frame.barrier.bbox_right, frame.barrier.bbox_top,
- dest=self.dest, bbox_width=TILE_SIZE,
- bbox_height=frame.barrier.bbox_height)
- SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
- spawn_id=self.spawn_id, spawn_direction=180,
- barrier=frame.barrier,
- bbox_width=frame.barrier.bbox_width,
- bbox_height=frame.barrier.bbox_height)
- self.destroy()
- class UpDoor(Door):
- def event_create(self):
- frame = DoorFrameY.create(self.x, self.y, z=self.z)
- Tunnel.create(frame.barrier.bbox_left,
- frame.barrier.bbox_top - TILE_SIZE, dest=self.dest,
- bbox_width=frame.barrier.bbox_width,
- bbox_height=TILE_SIZE)
- SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
- spawn_id=self.spawn_id, spawn_direction=90,
- barrier=frame.barrier,
- bbox_width=frame.barrier.bbox_width,
- bbox_height=frame.barrier.bbox_height)
- self.destroy()
- class DownDoor(Door):
- def event_create(self):
- frame = DoorFrameY.create(self.x, self.y, z=self.z)
- Tunnel.create(frame.barrier.bbox_left, frame.barrier.bbox_bottom,
- dest=self.dest, bbox_width=frame.barrier.bbox_width,
- bbox_height=TILE_SIZE)
- SpawnPoint.create(frame.barrier.bbox_left, frame.barrier.bbox_top,
- spawn_id=self.spawn_id, spawn_direction=270,
- barrier=frame.barrier,
- bbox_width=frame.barrier.bbox_width,
- bbox_height=frame.barrier.bbox_height)
- self.destroy()
- class TimelineSwitcher(InteractiveObject):
- def __init__(self, x, y, timeline=None, **kwargs):
- self.timeline = timeline
- kwargs["visible"] = False
- kwargs["checks_collisions"] = False
- sge.dsp.Object.__init__(self, x, y, **kwargs)
- def touch(self, other):
- sge.game.current_room.load_timeline(self.timeline)
- self.destroy()
- class MovingObjectPath(xsge_path.PathLink):
- cls = None
- default_speed = PLAYER_MAX_SPEED
- default_accel = None
- default_decel = None
- default_loop = None
- auto_follow = True
- def __init__(self, x, y, path_speed=None, path_accel=None, path_decel=None,
- path_loop=None, path_id=None, prime=False, parent=None,
- **kwargs):
- if path_speed is None:
- path_speed = self.default_speed
- if path_accel is None:
- path_accel = self.default_accel
- if path_decel is None:
- path_decel = self.default_decel
- if path_loop is None:
- path_loop = self.default_loop
- self.path_speed = path_speed
- self.path_accel = path_accel if path_accel != -1 else None
- self.path_decel = path_decel if path_decel != -1 else None
- self.path_loop = path_loop if path_loop != -1 else None
- self.path_id = path_id
- self.prime = prime
- self.parent = parent
- self.obj = lambda: None
- super(MovingObjectPath, self).__init__(x, y, **kwargs)
- def event_create(self):
- if self.parent is not None:
- for obj in sge.game.current_room.objects:
- if (isinstance(obj, self.__class__) and
- obj.path_id == self.parent):
- obj.next_path = self
- obj.next_speed = self.path_speed
- obj.next_accel = self.path_accel
- obj.next_decel = self.path_decel
- obj.next_loop = self.path_loop
- break
- else:
- self.prime = True
- if self.prime and self.cls in TYPES:
- obj = TYPES[self.cls].create(self.x, self.y, z=self.z)
- self.obj = weakref.ref(obj)
- if self.auto_follow:
- self.follow_start(obj, self.path_speed, accel=self.path_accel,
- decel=self.path_decel, loop=self.path_loop)
- class MovingPlatformPath(MovingObjectPath):
- cls = "moving_platform"
- default_speed = 3
- default_accel = 0.02
- default_decel = 0.02
- def event_create(self):
- super(MovingPlatformPath, self).event_create()
- obj = self.obj()
- if obj:
- obj.path = self
- def follow_start(self, obj, *args, **kwargs):
- super(MovingPlatformPath, self).follow_start(obj, *args, **kwargs)
- obj.following = True
- def event_follow_end(self, obj):
- obj.following = False
- obj.speed = 0
- obj.x = self.x + self.points[-1][0]
- obj.y = self.y + self.points[-1][1]
- class TriggeredMovingPlatformPath(MovingPlatformPath):
- default_speed = 2
- default_accel = None
- default_decel = None
- auto_follow = False
- followed = False
- class CircoflamePath(xsge_path.Path):
- def __init__(self, x, y, z=0, points=(), rvelocity=2):
- self.rvelocity = rvelocity
- x += TILE_SIZE / 2
- y += TILE_SIZE / 2
- super(CircoflamePath, self).__init__(x, y, z=z, points=points)
- def event_create(self):
- if self.points:
- fx, fy = self.points[0]
- radius = math.hypot(fx, fy)
- pos = math.degrees(math.atan2(fy, fx))
- CircoflameCenter.create(self.x, self.y, z=self.z, radius=radius,
- pos=pos, rvelocity=self.rvelocity)
- self.destroy()
- class PlayerLayer(sge.dsp.Object):
- def event_create(self):
- sge.game.current_room.player_z = self.z
- for obj in sge.game.current_room.objects:
- if isinstance(obj, Player):
- obj.z = self.z
- self.destroy()
- class CameraGuide(sge.dsp.Object):
- def __init__(self, x, y, **kwargs):
- kwargs.setdefault("visible", False)
- kwargs.setdefault("checks_collisions", False)
- super(CameraGuide, self).__init__(x, y, **kwargs)
- class CameraXGuide(CameraGuide):
- pass
- class CameraYGuide(CameraGuide):
- pass
- class MapHint(sge.dsp.Object):
- def event_create(self):
- self.alarms["destroy"] = 1
- def event_alarm(self, alarm_id):
- if alarm_id == "destroy":
- self.destroy()
- class MapLeftWall(MapHint):
- pass
- class MapRightWall(MapHint):
- pass
- class MapTopWall(MapHint):
- pass
- class MapBottomWall(MapHint):
- pass
- class MapLeftDoor(MapHint):
- pass
- class MapRightDoor(MapHint):
- pass
- class MapTopDoor(MapHint):
- pass
- class MapBottomDoor(MapHint):
- pass
- class IgnoreRegion(MapHint):
- pass
- class Menu(xsge_gui.MenuWindow):
- items = []
- @classmethod
- def create(cls, default=0):
- if cls.items:
- self = cls.from_text(
- gui_handler, sge.game.width / 2, sge.game.height * 2 / 3,
- cls.items, font_normal=font,
- color_normal=menu_text_color,
- color_selected=menu_text_selected_color,
- background_color=menu_color, margin=4, halign="center",
- valign="middle")
- default %= len(self.widgets)
- self.keyboard_focused_widget = self.widgets[default]
- self.show()
- return self
- def event_change_keyboard_focus(self):
- play_sound(select_sound)
- class MainMenu(Menu):
- items = [_("New Game"), _("Load Game"), _("Options"), _("Credits"), _("Quit")]
- def event_choose(self):
- if self.choice == 0:
- play_sound(confirm_sound)
- NewGameMenu.create_page()
- elif self.choice == 1:
- play_sound(confirm_sound)
- LoadGameMenu.create_page()
- elif self.choice == 2:
- play_sound(confirm_sound)
- OptionsMenu.create_page()
- elif self.choice == 3:
- credits_room = CreditsScreen.load(os.path.join("special",
- "credits.tmx"))
- credits_room.start()
- else:
- sge.game.end()
- class NewGameMenu(Menu):
- @classmethod
- def create_page(cls, default=0):
- cls.items = []
- for slot in save_slots:
- if slot is None:
- cls.items.append(_("-Empty-"))
- else:
- name = slot.get("player_name", "Anneroy")
- etanks = slot.get("etanks", 0)
- if etanks:
- text = "{} {}".format(name, ETANK_CHAR * etanks)
- else:
- text = name
- cls.items.append(text)
- cls.items.append(_("Back"))
- return cls.create(default)
- def event_choose(self):
- global abort
- global current_save_slot
- abort = False
- if self.choice in six.moves.range(len(save_slots)):
- play_sound(confirm_sound)
- current_save_slot = self.choice
- if save_slots[current_save_slot] is None:
- set_new_game()
- if not abort:
- start_game()
- else:
- NewGameMenu.create(default=self.choice)
- else:
- OverwriteConfirmMenu.create(default=1)
- else:
- play_sound(cancel_sound)
- MainMenu.create(default=0)
- class OverwriteConfirmMenu(Menu):
- items = [_("Overwrite this save file"), _("Cancel")]
- def event_choose(self):
- global abort
- abort = False
- if self.choice == 0:
- play_sound(confirm_sound)
- set_new_game()
- if not abort:
- start_game()
- else:
- play_sound(cancel_sound)
- NewGameMenu.create(default=current_save_slot)
- else:
- play_sound(cancel_sound)
- NewGameMenu.create(default=current_save_slot)
- class LoadGameMenu(NewGameMenu):
- def event_choose(self):
- global abort
- global current_save_slot
- abort = False
- if self.choice in six.moves.range(len(save_slots)):
- play_sound(confirm_sound)
- current_save_slot = self.choice
- load_game()
- if abort:
- MainMenu.create(default=1)
- elif not start_game():
- play_sound(error_sound)
- show_error(_("An error occurred when trying to load the game."))
- MainMenu.create(default=1)
- else:
- play_sound(cancel_sound)
- MainMenu.create(default=1)
- class OptionsMenu(Menu):
- @classmethod
- def create_page(cls, default=0):
- smt = scale_method if scale_method else "fastest"
- cls.items = [
- _("Fullscreen: {}").format(_("On") if fullscreen else _("Off")),
- _("Scale Method: {}").format(smt),
- _("Sound: {}").format(_("On") if sound_enabled else _("Off")),
- _("Music: {}").format(_("On") if music_enabled else _("Off")),
- _("Stereo: {}").format(_("On") if stereo_enabled else _("Off")),
- _("Show FPS: {}").format(_("On") if fps_enabled else _("Off")),
- _("Metroid-Style Aiming: {}").format(_("On") if metroid_controls else _("Off")),
- _("Joystick Threshold: {}%").format(int(joystick_threshold * 100)),
- _("Configure keyboard"), _("Configure joysticks"),
- _("Detect joysticks"), _("Back")]
- return cls.create(default)
- def event_choose(self):
- global fullscreen
- global scale_method
- global sound_enabled
- global music_enabled
- global stereo_enabled
- global fps_enabled
- global metroid_controls
- global joystick_threshold
- if self.choice == 0:
- play_sound(select_sound)
- fullscreen = not fullscreen
- update_fullscreen()
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 1:
- choices = [None, "noblur", "smooth"] + sge.SCALE_METHODS
- if scale_method in choices:
- i = choices.index(scale_method)
- else:
- i = 0
- play_sound(select_sound)
- i += 1
- i %= len(choices)
- scale_method = choices[i]
- sge.game.scale_method = scale_method
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 2:
- sound_enabled = not sound_enabled
- play_sound(teleport_sound)
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 3:
- music_enabled = not music_enabled
- play_music(sge.game.current_room.music,
- noloop=sge.game.current_room.music_noloop)
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 4:
- stereo_enabled = not stereo_enabled
- play_sound(confirm_sound)
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 5:
- play_sound(select_sound)
- fps_enabled = not fps_enabled
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 6:
- play_sound(select_sound)
- metroid_controls = not metroid_controls
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 7:
- play_sound(select_sound)
- # This somewhat complicated method is to prevent rounding
- # irregularities.
- threshold = ((int(joystick_threshold * 100) + 5) % 100) / 100
- if not threshold:
- threshold = 0.0001
- joystick_threshold = threshold
- xsge_gui.joystick_threshold = threshold
- OptionsMenu.create_page(default=self.choice)
- elif self.choice == 8:
- play_sound(confirm_sound)
- KeyboardMenu.create_page()
- elif self.choice == 9:
- play_sound(confirm_sound)
- JoystickMenu.create_page()
- elif self.choice == 10:
- sge.joystick.refresh()
- play_sound(heal_sound)
- OptionsMenu.create_page(default=self.choice)
- else:
- play_sound(cancel_sound)
- write_to_disk()
- MainMenu.create(default=2)
- class KeyboardMenu(Menu):
- page = 0
- @classmethod
- def create_page(cls, default=0, page=0):
- page %= min(len(left_key), len(right_key), len(up_key), len(down_key),
- len(jump_key), len(shoot_key), len(aim_diag_key),
- len(aim_up_key), len(aim_down_key), len(mode_reset_key),
- len(mode_key), len(pause_key), len(map_key))
- def format_key(key):
- if key:
- return " ".join(key)
- else:
- return None
- cls.items = [_("Player {}").format(page + 1),
- _("Left: {}").format(format_key(left_key[page])),
- _("Right: {}").format(format_key(right_key[page])),
- _("Up: {}").format(format_key(up_key[page])),
- _("Down: {}").format(format_key(down_key[page])),
- _("Jump: {}").format(format_key(jump_key[page])),
- _("Shoot: {}").format(format_key(shoot_key[page])),
- _("Aim Diagonal: {}").format(format_key(aim_diag_key[page])),
- _("Aim Up: {}").format(format_key(aim_up_key[page])),
- _("Aim Down: {}").format(format_key(aim_down_key[page])),
- _("Reset Mode: {}").format(format_key(mode_reset_key[page])),
- _("Mode: {}").format(format_key(mode_key[page])),
- _("Pause: {}").format(format_key(pause_key[page])),
- _("Map: {}").format(format_key(map_key[page])),
- _("Back")]
- self = cls.create(default)
- self.page = page
- return self
- def event_choose(self):
- def bind_key(key, new_key, self=self):
- for other_key in [
- left_key[self.page], right_key[self.page],
- up_key[self.page], down_key[self.page],
- jump_key[self.page], shoot_key[self.page],
- aim_diag_key[self.page], aim_up_key[self.page],
- aim_down_key[self.page], mode_reset_key[self.page],
- mode_key[self.page], pause_key[self.page],
- map_key[self.page]]:
- if new_key in other_key:
- other_key.remove(new_key)
- key.append(new_key)
- while len(key) > 2:
- key.pop(0)
- text = _("Press the key you wish to bind to this function, or the Escape key to cancel.")
- if self.choice == 0:
- play_sound(select_sound)
- self.__class__.create_page(default=self.choice, page=(self.page + 1))
- elif self.choice == 1:
- k = wait_key(text)
- if k is not None:
- bind_key(left_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 2:
- k = wait_key(text)
- if k is not None:
- bind_key(right_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 3:
- k = wait_key(text)
- if k is not None:
- bind_key(up_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 4:
- k = wait_key(text)
- if k is not None:
- bind_key(down_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 5:
- k = wait_key(text)
- if k is not None:
- bind_key(jump_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 6:
- k = wait_key(text)
- if k is not None:
- bind_key(shoot_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 7:
- k = wait_key(text)
- if k is not None:
- bind_key(aim_diag_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 8:
- k = wait_key(text)
- if k is not None:
- bind_key(aim_up_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 9:
- k = wait_key(text)
- if k is not None:
- bind_key(aim_down_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 10:
- k = wait_key(text)
- if k is not None:
- bind_key(mode_reset_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 11:
- k = wait_key(text)
- if k is not None:
- bind_key(mode_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 12:
- k = wait_key(text)
- if k is not None:
- bind_key(pause_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 13:
- k = wait_key(text)
- if k is not None:
- bind_key(map_key[self.page], k)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- else:
- play_sound(cancel_sound)
- OptionsMenu.create_page(default=5)
- class JoystickMenu(Menu):
- page = 0
- @classmethod
- def create_page(cls, default=0, page=0):
- page %= min(len(left_js), len(right_js), len(up_js), len(down_js),
- len(jump_js), len(shoot_js), len(aim_diag_js),
- len(aim_up_js), len(aim_down_js), len(mode_reset_js),
- len(mode_js), len(pause_js), len(map_js))
- def format_js(js):
- js_template = "{},{},{}"
- sL = []
- for j in js:
- sL.append(js_template.format(*j))
- if sL:
- return " ".join(sL)
- else:
- return _("None")
- cls.items = [_("Player {}").format(page + 1),
- _("Left: {}").format(format_js(left_js[page])),
- _("Right: {}").format(format_js(right_js[page])),
- _("Up: {}").format(format_js(up_js[page])),
- _("Down: {}").format(format_js(down_js[page])),
- _("Jump: {}").format(format_js(jump_js[page])),
- _("Shoot: {}").format(format_js(shoot_js[page])),
- _("Aim Diagonal: {}").format(format_js(aim_diag_js[page])),
- _("Aim Up: {}").format(format_js(aim_up_js[page])),
- _("Aim Down: {}").format(format_js(aim_down_js[page])),
- _("Reset Mode: {}").format(format_js(mode_reset_js[page])),
- _("Mode: {}").format(format_js(mode_js[page])),
- _("Pause: {}").format(format_js(pause_js[page])),
- _("Map: {}").format(format_js(map_js[page])),
- _("Back")]
- self = cls.create(default)
- self.page = page
- return self
- def event_choose(self):
- def bind_js(js, new_js, self=self):
- for other_js in [
- left_js[self.page], right_js[self.page],
- up_js[self.page], down_js[self.page],
- jump_js[self.page], shoot_js[self.page],
- aim_diag_js[self.page], aim_up_js[self.page],
- aim_down_js[self.page], mode_reset_js[self.page],
- mode_js[self.page], pause_js[self.page],
- map_js[self.page]]:
- if new_js in other_js:
- other_js.remove(new_js)
- js.append(new_js)
- while len(js) > 2:
- js.pop(0)
- text = _("Press the joystick button, axis, or hat direction you wish to bind to this function, or the Escape key to cancel.")
- if self.choice == 0:
- play_sound(select_sound)
- self.__class__.create_page(default=self.choice, page=(self.page + 1))
- elif self.choice == 1:
- js = wait_js(text)
- if js is not None:
- bind_js(left_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 2:
- js = wait_js(text)
- if js is not None:
- bind_js(right_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 3:
- js = wait_js(text)
- if js is not None:
- bind_js(up_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 4:
- js = wait_js(text)
- if js is not None:
- bind_js(down_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 5:
- js = wait_js(text)
- if js is not None:
- bind_js(jump_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 6:
- js = wait_js(text)
- if js is not None:
- bind_js(shoot_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 7:
- js = wait_js(text)
- if js is not None:
- bind_js(aim_diag_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 8:
- js = wait_js(text)
- if js is not None:
- bind_js(aim_up_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 9:
- js = wait_js(text)
- if js is not None:
- bind_js(aim_down_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 10:
- js = wait_js(text)
- if js is not None:
- bind_js(mode_reset_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 11:
- js = wait_js(text)
- if js is not None:
- bind_js(mode_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 12:
- js = wait_js(text)
- if js is not None:
- bind_js(pause_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- elif self.choice == 13:
- js = wait_js(text)
- if js is not None:
- bind_js(map_js[self.page], js)
- play_sound(confirm_sound)
- else:
- play_sound(cancel_sound)
- self.__class__.create_page(default=self.choice, page=self.page)
- else:
- play_sound(cancel_sound)
- OptionsMenu.create_page(default=6)
- class ModalMenu(xsge_gui.MenuDialog):
- items = []
- @classmethod
- def create(cls, default=0):
- if cls.items:
- self = cls.from_text(
- gui_handler, sge.game.width / 2, sge.game.height / 2,
- cls.items, font_normal=font,
- color_normal=menu_text_color,
- color_selected=menu_text_selected_color,
- background_color=menu_color, margin=9, halign="center",
- valign="middle")
- default %= len(self.widgets)
- self.keyboard_focused_widget = self.widgets[default]
- sge.game.refresh()
- self.show()
- return self
- def event_change_keyboard_focus(self):
- play_sound(select_sound)
- class PauseMenu(ModalMenu):
- @classmethod
- def create(cls, default=0, player_x=None, player_y=None):
- if "map" in progress_flags:
- items = [_("Continue"), _("View Stats"), _("Configure keyboard"),
- _("Configure joysticks"), _("View Map"),
- _("Return to Title Screen")]
- else:
- items = [_("Continue"), _("View Stats"), _("Configure keyboard"),
- _("Configure joysticks"), _("Return to Title Screen")]
- self = cls.from_text(
- gui_handler, sge.game.width / 2, sge.game.height / 2,
- items, font_normal=font, color_normal=menu_text_color,
- color_selected=menu_text_selected_color,
- background_color=menu_color, margin=9, halign="center",
- valign="middle")
- default %= len(self.widgets)
- self.keyboard_focused_widget = self.widgets[default]
- self.player_x = player_x
- self.player_y = player_y
- sge.game.refresh()
- self.show()
- return self
- def event_choose(self):
- self.hide()
- sge.game.refresh()
- def check_quit():
- if current_save_slot is not None:
- slot = save_slots[current_save_slot]
- else:
- slot = {}
- if (slot.get("map_revealed") == map_revealed and
- slot.get("map_explored") == map_explored and
- slot.get("map_removed") == map_removed and
- slot.get("warp_pads") == warp_pads and
- slot.get("powerups") == powerups and
- slot.get("progress_flags") == progress_flags and
- slot.get("artifacts") == artifacts and
- slot.get("etanks") == etanks):
- sge.game.start_room.start()
- else:
- 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.")
- DialogBox(gui_handler, text).show()
- play_sound(type_sound)
- LoseProgressMenu.create(1)
- if self.choice == 1:
- seconds = int(time_taken % 60)
- minutes = int((time_taken / 60) % 60)
- hours = int(time_taken / 3600)
- powerups_col = len(powerups) - artifacts
- text = _("PLAYER STATISTICS\n\nTime spent: {hours}:{minutes:02}:{seconds:02}\nPowerups collected: {powerups} ({powerups_percent}%)\nArtifacts collected: {artifacts} ({artifacts_percent}%)").format(
- hours=hours, minutes=minutes, seconds=seconds,
- powerups=powerups_col,
- powerups_percent=int(100 * powerups_col / max(num_powerups, 1)),
- artifacts=artifacts,
- artifacts_percent=int(100 * artifacts / max(num_artifacts, 1)))
- DialogBox(gui_handler, text).show()
- PauseMenu.create(default=self.choice, player_x=self.player_x,
- player_y=self.player_y)
- elif self.choice == 2:
- play_sound(confirm_sound)
- ModalKeyboardMenu.create_page()
- PauseMenu.create(default=self.choice, player_x=self.player_x,
- player_y=self.player_y)
- elif self.choice == 3:
- play_sound(confirm_sound)
- ModalJoystickMenu.create_page()
- PauseMenu.create(default=self.choice, player_x=self.player_x,
- player_y=self.player_y)
- elif self.choice == 4:
- if "map" in progress_flags:
- play_sound(select_sound)
- MapDialog(self.player_x, self.player_y).show()
- else:
- check_quit()
- elif self.choice == 5:
- check_quit()
- else:
- play_sound(select_sound)
- class ModalKeyboardMenu(ModalMenu, KeyboardMenu):
- def event_choose(self):
- self.hide()
- sge.game.refresh()
- if self.choice is not None and self.choice < len(self.items) - 1:
- super(ModalKeyboardMenu, self).event_choose()
- else:
- play_sound(cancel_sound)
- class ModalJoystickMenu(ModalMenu, JoystickMenu):
- def event_choose(self):
- self.hide()
- sge.game.refresh()
- if self.choice is not None and self.choice < len(self.items) - 1:
- super(ModalJoystickMenu, self).event_choose()
- else:
- play_sound(cancel_sound)
- class LoseProgressMenu(ModalMenu):
- items = [_("Abandon unsaved progress"), _("Return to game")]
- def event_choose(self):
- if self.choice == 0:
- sge.game.start_room.start()
- else:
- play_sound(select_sound)
- class MapDialog(xsge_gui.Dialog):
- def __init__(self, player_x, player_y):
- if player_x is None:
- player_x = 0
- if player_y is None:
- player_y = 0
- xcells = int(sge.game.width / MAP_CELL_WIDTH)
- ycells = int(sge.game.height / MAP_CELL_HEIGHT)
- w = sge.game.width
- h = sge.game.height
- super(MapDialog, self).__init__(
- gui_handler, 0, 0, w, h, background_color=sge.gfx.Color("black"),
- border=False)
- self.map = xsge_gui.Widget(self, 0, 0, 0)
- self.map.sprite = draw_map(player_x=player_x, player_y=player_y)
- self.map.tab_focus = False
- self.left = 0
- self.top = 0
- for rx, ry in set(map_revealed + map_explored):
- self.left = min(self.left, rx)
- self.top = min(self.top, ry)
- player_x -= self.left
- player_y -= self.top
- self.map.x = (xcells // 2 - player_x) * MAP_CELL_WIDTH
- self.map.y = (ycells // 2 - player_y) * MAP_CELL_HEIGHT
- def event_press_left(self):
- play_sound(select_sound)
- self.map.x += MAP_CELL_WIDTH
- def event_press_right(self):
- play_sound(select_sound)
- self.map.x -= MAP_CELL_WIDTH
- def event_press_up(self):
- play_sound(select_sound)
- self.map.y += MAP_CELL_HEIGHT
- def event_press_down(self):
- play_sound(select_sound)
- self.map.y -= MAP_CELL_HEIGHT
- def event_press_enter(self):
- play_sound(select_sound)
- self.destroy()
- def event_press_escape(self):
- play_sound(select_sound)
- self.destroy()
- class TeleportDialog(MapDialog):
- def __init__(self, selection):
- self.selection = selection
- w = sge.game.width
- h = sge.game.height
- super(MapDialog, self).__init__(
- gui_handler, 0, 0, w, h, background_color=sge.gfx.Color("black"),
- border=False)
- self.map = xsge_gui.Widget(self, 0, 0, 0)
- self.map.sprite = draw_map()
- self.map.tab_focus = False
- self.location_indicator = xsge_gui.Widget(self, 0, 0, 1)
- self.location_indicator.sprite = map_player_sprite
- self.location_indicator.tab_focus = False
- self.left = 0
- self.top = 0
- for rx, ry in set(map_revealed + map_explored):
- self.left = min(self.left, rx)
- self.top = min(self.top, ry)
- xcells = int(sge.game.width / MAP_CELL_WIDTH)
- ycells = int(sge.game.height / MAP_CELL_HEIGHT)
- self.location_indicator.x = (xcells // 2) * MAP_CELL_WIDTH
- self.location_indicator.y = (ycells // 2) * MAP_CELL_HEIGHT
- self.update_selection()
- def update_selection(self):
- if self.selection[0] in map_rooms:
- xcells = int(sge.game.width / MAP_CELL_WIDTH)
- ycells = int(sge.game.height / MAP_CELL_HEIGHT)
- x, y = map_rooms[self.selection[0]]
- x += self.selection[2] - self.left
- y += self.selection[3] - self.top
- self.map.x = (xcells // 2 - x) * MAP_CELL_WIDTH
- self.map.y = (ycells // 2 - y) * MAP_CELL_HEIGHT
- def event_press_left(self):
- play_sound(select_sound)
- if self.selection in warp_pads:
- i = warp_pads.index(self.selection)
- else:
- i = 0
- self.selection = warp_pads[(i - 1) % len(warp_pads)]
- self.update_selection()
- def event_press_right(self):
- play_sound(select_sound)
- if self.selection in warp_pads:
- i = warp_pads.index(self.selection)
- else:
- i = -1
- self.selection = warp_pads[(i + 1) % len(warp_pads)]
- self.update_selection()
- def event_press_up(self):
- pass
- def event_press_down(self):
- pass
- def event_press_enter(self):
- self.destroy()
- def event_press_escape(self):
- self.selection = None
- self.destroy()
- class DialogLabel(xsge_gui.ProgressiveLabel):
- def event_add_character(self):
- if self.text[-1] not in (' ', '\n', '\t'):
- play_sound(type_sound)
- class DialogBox(xsge_gui.Dialog):
- def __init__(self, parent, text, portrait=None, rate=TEXT_SPEED):
- width = sge.game.width / 2
- x_padding = 16
- y_padding = 16
- label_x = 8
- label_y = 8
- if portrait is not None:
- x_padding += 8
- label_x += 8
- portrait_w = portrait.width
- portrait_h = portrait.height
- label_x += portrait_w
- else:
- portrait_w = 0
- portrait_h = 0
- label_w = max(1, width - portrait_w - x_padding)
- height = max(1, portrait_h + y_padding,
- font.get_height(text, width=label_w) + y_padding)
- x = sge.game.width / 2 - width / 2
- y = sge.game.height / 2 - height / 2
- super(DialogBox, self).__init__(
- parent, x, y, width, height,
- background_color=menu_color, border=False)
- label_h = max(1, height - y_padding)
- self.label = DialogLabel(self, label_x, label_y, 0, text, font=font,
- width=label_w, height=label_h,
- color=sge.gfx.Color("white"), rate=rate)
- if portrait is not None:
- xsge_gui.Widget(self, 8, 8, 0, sprite=portrait)
- def event_press_enter(self):
- if len(self.label.text) < len(self.label.full_text):
- self.label.text = self.label.full_text
- else:
- self.destroy()
- def event_press_escape(self):
- self.destroy()
- room = sge.game.current_room
- if (isinstance(room, Level) and
- room.timeline_skip_target is not None and
- room.timeline_step < room.timeline_skip_target):
- room.timeline_skipto(room.timeline_skip_target)
- def get_object(x, y, cls=None, **kwargs):
- cls = TYPES.get(cls, xsge_tmx.Decoration)
- return cls(x, y, **kwargs)
- def get_scaled_copy(obj):
- s = obj.sprite.copy()
- if obj.image_xscale < 0:
- s.mirror()
- if obj.image_yscale < 0:
- s.flip()
- s.width *= abs(obj.image_xscale)
- s.height *= abs(obj.image_yscale)
- s.rotate(obj.image_rotation)
- s.origin_x = obj.image_origin_x
- s.origin_y = obj.image_origin_y
- return s
- def get_jump_speed(height, gravity=GRAVITY):
- # Get the speed to achieve a given height using a kinematic
- # equation: v[f]^2 = v[i]^2 + 2ad
- return -math.sqrt(2 * gravity * height)
- def get_xregion(x):
- return int(x / SCREEN_SIZE[0])
- def get_yregion(y):
- return int(y / SCREEN_SIZE[1])
- def warp(dest):
- global spawn_point
- if ":" in dest:
- level_f, spawn_point = dest.split(':', 1)
- else:
- level_f = dest
- spawn_point = sge.game.current_room.fname
- if level_f:
- level = sge.game.current_room.__class__.load(level_f)
- else:
- level = sge.game.current_room
- if level is not None:
- level.start()
- else:
- sge.game.start_room.start()
- def set_gui_controls():
- # Set the controls for xsge_gui.
- xsge_gui.next_widget_keys = ["down", "tab", "s", "kp_2"]
- xsge_gui.previous_widget_keys = ["up", "w", "kp_8"]
- xsge_gui.left_keys = ["left", "a", "kp_4"]
- xsge_gui.right_keys = ["right", "d", "kp_6"]
- xsge_gui.up_keys = []
- xsge_gui.down_keys = []
- xsge_gui.enter_keys = ["enter", "kp_enter", "space", "end"]
- xsge_gui.escape_keys = ["escape"]
- xsge_gui.next_widget_joystick_events = [
- (0, "axis+", 1), (0, "hat_down", 0)]
- xsge_gui.previous_widget_joystick_events = [
- (0, "axis-", 1), (0, "hat_up", 0)]
- xsge_gui.left_joystick_events = [(0, "axis-", 0), (0, "hat_left", 0)]
- xsge_gui.right_joystick_events = [(0, "axis+", 0), (0, "hat_right", 0)]
- xsge_gui.up_joystick_events = []
- xsge_gui.down_joystick_events = []
- xsge_gui.enter_joystick_events = [
- (0, "button", 0), (0, "button", 1), (0, "button", 2), (0, "button", 3),
- (0, "button", 9)]
- xsge_gui.escape_joystick_events = [(0, "button", 8)]
- def wait_key(text):
- # Wait for a key press and return it.
- while True:
- # Input events
- sge.game.pump_input()
- while sge.game.input_events:
- event = sge.game.input_events.pop(0)
- if isinstance(event, sge.input.KeyPress):
- sge.game.pump_input()
- sge.game.input_events = []
- if event.key == "escape":
- return None
- else:
- return event.key
- # Regulate speed
- sge.game.regulate_speed(fps=10)
- # Project text
- sge.game.project_text(font, text, sge.game.width / 2,
- sge.game.height / 2, width=sge.game.width,
- height=sge.game.height,
- color=sge.gfx.Color("white"),
- halign="center", valign="middle")
- # Refresh
- sge.game.refresh()
- def wait_js(text):
- # Wait for a joystick press and return it.
- sge.game.pump_input()
- sge.game.input_events = []
- while True:
- # Input events
- sge.game.pump_input()
- while sge.game.input_events:
- event = sge.game.input_events.pop(0)
- if isinstance(event, sge.input.KeyPress):
- if event.key == "escape":
- sge.game.pump_input()
- sge.game.input_events = []
- return None
- elif isinstance(event, sge.input.JoystickEvent):
- if (event.input_type not in {"axis0", "hat_center_x",
- "hat_center_y"} and
- event.value >= joystick_threshold):
- sge.game.pump_input()
- sge.game.input_events = []
- return (event.js_id, event.input_type, event.input_id)
- # Regulate speed
- sge.game.regulate_speed(fps=10)
- # Project text
- sge.game.project_text(font, text, sge.game.width / 2,
- sge.game.height / 2, width=sge.game.width,
- height=sge.game.height,
- color=sge.gfx.Color("white"),
- halign="center", valign="middle")
- # Refresh
- sge.game.refresh()
- def show_error(message):
- print(message)
- raise
- def play_sound(sound, x=None, y=None, force=True):
- if sound_enabled and sound:
- if x is None or y is None:
- sound.play(force=force)
- else:
- current_view = None
- view_x = 0
- view_y = 0
- dist = 0
- for view in sge.game.current_room.views:
- vx = view.x + view.width / 2
- vy = view.y + view.height / 2
- new_dist = math.hypot(vx - x, vy - y)
- if current_view is None or new_dist < dist:
- current_view = view
- view_x = vx
- view_y = vy
- dist = new_dist
- bl = min(x, view_x)
- bw = abs(x - view_x)
- bt = min(y, view_y)
- bh = abs(y - view_y)
- for obj in sge.game.current_room.get_objects_at(bl, bt, bw, bh):
- if isinstance(obj, Player):
- new_dist = math.hypot(obj.x - x, obj.y - y)
- if new_dist < dist:
- view_x = obj.x
- view_y = obj.y
- dist = new_dist
- if dist <= SOUND_MAX_RADIUS:
- volume = 1
- elif dist < SOUND_ZERO_RADIUS:
- rng = SOUND_ZERO_RADIUS - SOUND_MAX_RADIUS
- reldist = rng - (dist - SOUND_MAX_RADIUS)
- volume = min(1, abs(reldist / rng))
- else:
- # No point in continuing; it's too far away
- return
- if stereo_enabled:
- hdist = x - view_x
- if abs(hdist) < SOUND_CENTERED_RADIUS:
- balance = 0
- else:
- rng = SOUND_TILTED_RADIUS - SOUND_CENTERED_RADIUS
- balance = max(-SOUND_TILT_LIMIT,
- min(hdist / rng, SOUND_TILT_LIMIT))
- else:
- balance = 0
- sound.play(volume=volume, balance=balance, force=force)
- def play_music(music, force_restart=False, noloop=False):
- """Play the given music file, starting with its start piece."""
- if music_enabled:
- loops = 1 if noloop else None
- if music:
- music_object = loaded_music.get(music)
- if music_object is None:
- try:
- music_object = sge.snd.Music(os.path.join(DATA, "music",
- music))
- except (IOError, OSError):
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- return
- else:
- loaded_music[music] = music_object
- name, ext = os.path.splitext(music)
- music_start = ''.join([name, "-start", ext])
- music_start_object = loaded_music.get(music_start)
- if music_start_object is None:
- try:
- music_start_object = sge.snd.Music(os.path.join(DATA, "music",
- music_start))
- except (IOError, OSError):
- pass
- else:
- loaded_music[music_start] = music_start_object
- if (force_restart or (not music_object.playing and
- (music_start_object is None or
- not music_start_object.playing))):
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- if music_start_object is not None:
- music_start_object.play()
- music_object.queue(loops=loops)
- else:
- music_object.play(loops=loops)
- else:
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop(fade_time=1000)
- else:
- sge.snd.Music.clear_queue()
- sge.snd.Music.stop()
- def set_new_game():
- global player_name
- global watched_timelines
- global current_level
- global spawn_point
- global warp_pads
- global map_revealed
- global map_explored
- global map_removed
- global powerups
- global progress_flags
- global artifacts
- global etanks
- global time_taken
- player_name = "Anneroy"
- watched_timelines = []
- current_level = None
- spawn_point = "save"
- map_revealed = []
- map_explored = []
- map_removed = []
- warp_pads = []
- powerups = []
- progress_flags = []
- artifacts = 0
- etanks = 0
- time_taken = 0
- def write_to_disk():
- # Write our saves and settings to disk.
- keys_cfg = {"left": left_key, "right": right_key, "up": up_key,
- "down": down_key, "aim_diag": aim_diag_key, "jump": jump_key,
- "shoot": shoot_key, "aim_up": aim_up_key,
- "aim_down": aim_down_key, "mode_reset": mode_reset_key,
- "mode": mode_key, "pause": pause_key, "map": map_key}
- js_cfg = {"left": left_js, "right": right_js, "up": up_js,
- "down": down_js, "aim_diag": aim_diag_js, "jump": jump_js,
- "shoot": shoot_js, "aim_up": aim_up_js, "aim_down": aim_down_js,
- "mode_reset": mode_reset_js, "mode": mode_js, "pause": pause_js,
- "map": map_js}
- cfg = {"version": 1, "fullscreen": fullscreen,
- "scale_method": scale_method, "sound_enabled": sound_enabled,
- "music_enabled": music_enabled, "stereo_enabled": stereo_enabled,
- "fps_enabled": fps_enabled, "metroid_controls": metroid_controls,
- "joystick_threshold": joystick_threshold, "keys": keys_cfg,
- "joystick": js_cfg, "ai_data": sorted(list(ai_data))}
- with open(os.path.join(CONFIG, "config.json"), 'w') as f:
- json.dump(cfg, f, indent=4)
- with open(os.path.join(CONFIG, "save_slots.json"), 'w') as f:
- json.dump(save_slots, f, indent=4)
- def save_game():
- global save_slots
- if current_save_slot is not None:
- save_slots[current_save_slot] = {
- "save_format": 1,
- "player_name": player_name,
- "watched_timelines": watched_timelines[:],
- "current_level": current_level,
- "spawn_point": spawn_point,
- "map_revealed": map_revealed[:],
- "map_explored": map_explored[:],
- "map_removed": map_removed[:],
- "warp_pads": warp_pads[:],
- "powerups": powerups[:],
- "progress_flags": progress_flags[:],
- "artifacts": artifacts,
- "etanks": etanks,
- "time_taken": time_taken}
- write_to_disk()
- def load_game():
- global player_name
- global watched_timelines
- global current_level
- global spawn_point
- global map_revealed
- global map_explored
- global map_removed
- global warp_pads
- global powerups
- global progress_flags
- global etanks
- global time_taken
- if (current_save_slot is not None and
- save_slots[current_save_slot] is not None):
- slot = save_slots[current_save_slot]
- save_format = slot.get("save_format", 0)
- if save_format == 1:
- player_name = slot.get("player_name", "Anneroy")
- watched_timelines = slot.get("watched_timelines", [])
- current_level = slot.get("current_level")
- spawn_point = slot.get("spawn_point")
- map_revealed = [tuple(i) for i in slot.get("map_revealed", [])]
- map_explored = [tuple(i) for i in slot.get("map_explored", [])]
- map_removed = [tuple(i) for i in slot.get("map_removed", [])]
- warp_pads = [tuple(i) for i in slot.get("warp_pads", [])]
- powerups = [tuple(i) for i in slot.get("powerups", [])]
- progress_flags = slot.get("progress_flags", [])
- artifacts = slot.get("artifacts", 0)
- etanks = slot.get("etanks", 0)
- time_taken = slot.get("time_taken", 0)
- else:
- set_new_game()
- def start_game():
- global player
- player = Anneroy(0, 0)
- if current_level is None:
- level = SpecialScreen.load("special/intro.tmx")
- else:
- level = Level.load(current_level)
- if level is not None:
- level.start()
- else:
- return False
- return True
- def generate_map():
- global map_rooms
- global map_objects
- global num_powerups
- global num_artifacts
- print(_("Generating new map files; this may take some time."))
- files_checked = set()
- files_remaining = {("0.tmx", 0, 0, None, None)}
- map_rooms = {}
- map_objects = {}
- num_powerups = 0
- num_artifacts = 0
- while files_remaining:
- fname, rm_x, rm_y, origin_level, origin_spawn = files_remaining.pop()
- files_checked.add(fname)
- room = Level.load(fname, True)
- rm_w = int(math.ceil(room.width / SCREEN_SIZE[0]))
- rm_h = int(math.ceil(room.height / SCREEN_SIZE[1]))
- for obj in room.objects:
- if isinstance(obj, Door):
- if ":" in obj.dest:
- level_f, spawn = obj.dest.split(':', 1)
- else:
- level_f = obj.dest
- spawn = fname
- if level_f == origin_level and spawn == origin_spawn:
- if isinstance(obj, LeftDoor):
- rm_x += 1
- elif isinstance(obj, RightDoor):
- rm_x -= 1
- elif isinstance(obj, UpDoor):
- rm_y += 1
- elif isinstance(obj, DownDoor):
- rm_y -= 1
- rm_x -= get_xregion(obj.image_xcenter)
- rm_y -= get_yregion(obj.image_ycenter)
- origin = None
- break
- map_rooms[fname] = (rm_x, rm_y)
- ignore_regions = set()
- for obj in room.objects:
- if isinstance(obj, IgnoreRegion):
- rx1 = rm_x + get_xregion(obj.bbox_left)
- rx2 = rm_x + get_xregion(obj.bbox_right - 1)
- ry1 = rm_y + get_yregion(obj.bbox_top)
- ry2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for ry in six.moves.range(ry1, ry2 + 1):
- for rx in six.moves.range(rx1, rx2 + 1):
- ignore_regions.add((rx, ry))
- for obj in room.objects:
- if isinstance(obj, Door):
- dx = rm_x + get_xregion(obj.image_xcenter)
- dy = rm_y + get_yregion(obj.image_ycenter)
- if ":" in obj.dest:
- level_f, spawn = obj.dest.split(':', 1)
- else:
- level_f = obj.dest
- spawn = fname
- if level_f not in files_checked:
- files_remaining.add((level_f, dx, dy, fname, obj.spawn_id))
- files_checked.add(level_f)
- if (dx, dy) not in ignore_regions:
- if isinstance(obj, LeftDoor):
- map_objects.setdefault((dx, dy), []).append("door_left")
- elif isinstance(obj, RightDoor):
- map_objects.setdefault((dx, dy), []).append("door_right")
- elif isinstance(obj, UpDoor):
- map_objects.setdefault((dx, dy), []).append("door_top")
- elif isinstance(obj, DownDoor):
- map_objects.setdefault((dx, dy), []).append("door_bottom")
- elif isinstance(obj, WarpPad):
- wx = rm_x + get_xregion(obj.image_xcenter)
- wy = rm_y + get_yregion(obj.image_ycenter)
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("warp_pad")
- elif isinstance(obj, Powerup):
- if isinstance(obj, Artifact):
- num_artifacts += 1
- else:
- num_powerups += 1
- px = rm_x + get_xregion(obj.image_xcenter)
- py = rm_y + get_yregion(obj.image_ycenter)
- if (px, py) not in ignore_regions:
- map_objects.setdefault((px, py), []).append("powerup")
- elif isinstance(obj, MapLeftWall):
- wx = rm_x + get_xregion(obj.bbox_left)
- wy1 = rm_y + get_yregion(obj.bbox_top)
- wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wy in six.moves.range(wy1, wy2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("wall_left")
- elif isinstance(obj, MapRightWall):
- wx = rm_x + get_xregion(obj.bbox_right - 1)
- wy1 = rm_y + get_yregion(obj.bbox_top)
- wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wy in six.moves.range(wy1, wy2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("wall_right")
- elif isinstance(obj, MapTopWall):
- wx1 = rm_x + get_xregion(obj.bbox_left)
- wx2 = rm_x + get_xregion(obj.bbox_right - 1)
- wy = rm_y + get_yregion(obj.bbox_top)
- for wx in six.moves.range(wx1, wx2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("wall_top")
- elif isinstance(obj, MapBottomWall):
- wx1 = rm_x + get_xregion(obj.bbox_left)
- wx2 = rm_x + get_xregion(obj.bbox_right - 1)
- wy = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wx in six.moves.range(wx1, wx2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("wall_bottom")
- elif isinstance(obj, MapLeftDoor):
- wx = rm_x + get_xregion(obj.bbox_left)
- wy1 = rm_y + get_yregion(obj.bbox_top)
- wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wy in six.moves.range(wy1, wy2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("door_left")
- elif isinstance(obj, MapRightDoor):
- wx = rm_x + get_xregion(obj.bbox_right - 1)
- wy1 = rm_y + get_yregion(obj.bbox_top)
- wy2 = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wy in six.moves.range(wy1, wy2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("door_right")
- elif isinstance(obj, MapTopDoor):
- wx1 = rm_x + get_xregion(obj.bbox_left)
- wx2 = rm_x + get_xregion(obj.bbox_right - 1)
- wy = rm_y + get_yregion(obj.bbox_top)
- for wx in six.moves.range(wx1, wx2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("door_top")
- elif isinstance(obj, MapBottomDoor):
- wx1 = rm_x + get_xregion(obj.bbox_left)
- wx2 = rm_x + get_xregion(obj.bbox_right - 1)
- wy = rm_y + get_yregion(obj.bbox_bottom - 1)
- for wx in six.moves.range(wx1, wx2 + 1):
- if (wx, wy) not in ignore_regions:
- map_objects.setdefault((wx, wy), []).append("door_bottom")
- for x in six.moves.range(rm_x, rm_x + rm_w):
- y = rm_y
- if ((x, y) not in ignore_regions and
- "door_top" not in map_objects.setdefault((x, y), [])):
- map_objects[(x, y)].append("wall_top")
- y = rm_y + rm_h - 1
- if ((x, y) not in ignore_regions and
- "door_bottom" not in map_objects.setdefault((x, y), [])):
- map_objects[(x, y)].append("wall_bottom")
- for y in six.moves.range(rm_y, rm_y + rm_h):
- x = rm_x
- if ((x, y) not in ignore_regions and
- "door_left" not in map_objects.setdefault((x, y), [])):
- map_objects[(x, y)].append("wall_left")
- x = rm_x + rm_w - 1
- if ((x, y) not in ignore_regions and
- "door_right" not in map_objects.setdefault((x, y), [])):
- map_objects[(x, y)].append("wall_right")
- f_objects = {}
- for x, y in map_objects:
- i = "{},{}".format(x, y)
- f_objects[i] = map_objects[(x, y)]
- info = {"powerups": num_powerups, "artifacts": num_artifacts}
- with open(os.path.join(DATA, "map", "rooms.json"), 'w') as f:
- json.dump(map_rooms, f, indent=4, sort_keys=True)
- with open(os.path.join(DATA, "map", "objects.json"), 'w') as f:
- json.dump(f_objects, f, indent=4, sort_keys=True)
- with open(os.path.join(DATA, "map", "info.json"), 'w') as f:
- json.dump(info, f, indent=4, sort_keys=True)
- def draw_map(x=None, y=None, w=None, h=None, player_x=None, player_y=None):
- if x is None or y is None or w is None or h is None:
- left = 0
- right = 0
- top = 0
- bottom = 0
- for rx, ry in set(map_revealed + map_explored):
- left = min(left, rx)
- right = max(right, rx)
- top = min(top, ry)
- bottom = max(bottom, ry)
- if x is None:
- x = left
- if y is None:
- y = top
- if w is None:
- w = right - x + 1
- if h is None:
- h = bottom - y + 1
- removed = []
- for obj, fname, ox, oy in map_removed:
- if fname in map_rooms:
- rm_x, rm_y = map_rooms[fname]
- removed.append((obj, rm_x + ox, rm_y + oy))
- s_w = w * MAP_CELL_WIDTH
- s_h = h * MAP_CELL_HEIGHT
- map_sprite = sge.gfx.Sprite(width=s_w, height=s_h)
- map_sprite.draw_rectangle(0, 0, s_w, s_h, fill=sge.gfx.Color("black"))
- for ex, ey in map_explored:
- dx = (ex - x) * MAP_CELL_WIDTH
- dy = (ey - y) * MAP_CELL_HEIGHT
- map_sprite.draw_rectangle(dx, dy, MAP_CELL_WIDTH, MAP_CELL_HEIGHT,
- fill=sge.gfx.Color("teal"))
- for ox, oy in set(map_objects) & set(map_revealed + map_explored):
- if x <= ox < x + w and y <= oy < y + h:
- for obj in map_objects[(ox, oy)]:
- if (obj, ox, oy) in removed:
- removed.remove((obj, ox, oy))
- else:
- dx = (ox - x) * MAP_CELL_WIDTH
- dy = (oy - y) * MAP_CELL_HEIGHT
- if obj == "wall_left":
- map_sprite.draw_sprite(map_wall_left_sprite, 0, dx, dy)
- elif obj == "wall_right":
- map_sprite.draw_sprite(map_wall_right_sprite, 0, dx, dy)
- elif obj == "wall_top":
- map_sprite.draw_sprite(map_wall_top_sprite, 0, dx, dy)
- elif obj == "wall_bottom":
- map_sprite.draw_sprite(map_wall_bottom_sprite, 0, dx, dy)
- elif obj == "door_left":
- map_sprite.draw_sprite(map_door_left_sprite, 0, dx, dy)
- elif obj == "door_right":
- map_sprite.draw_sprite(map_door_right_sprite, 0, dx, dy)
- elif obj == "door_top":
- map_sprite.draw_sprite(map_door_top_sprite, 0, dx, dy)
- elif obj == "door_bottom":
- map_sprite.draw_sprite(map_door_bottom_sprite, 0, dx, dy)
- elif obj == "powerup":
- if "warp_pad" not in map_objects[(ox, oy)]:
- map_sprite.draw_sprite(map_powerup_sprite, 0, dx, dy)
- elif obj == "warp_pad":
- map_sprite.draw_sprite(map_warp_pad_sprite, 0, dx, dy)
- if player_x is not None and player_y is not None:
- dx = (player_x - x) * MAP_CELL_WIDTH
- dy = (player_y - y) * MAP_CELL_HEIGHT
- map_sprite.draw_sprite(map_player_sprite, 0, dx, dy)
- return map_sprite
- def update_fullscreen():
- if fullscreen:
- sge.game.scale = FSSCALE if FSSCALE else None
- sge.game.fullscreen = True
- else:
- sge.game.scale = SCALE
- sge.game.fullscreen = False
- sge.game.scale = None
- TYPES = {
- "solid_left": SolidLeft, "solid_right": SolidRight, "solid_top": SolidTop,
- "solid_bottom": SolidBottom, "solid": Solid, "slope_topleft": SlopeTopLeft,
- "slope_topright": SlopeTopRight, "slope_bottomleft": SlopeBottomLeft,
- "slope_bottomright": SlopeBottomRight, "moving_platform": MovingPlatform,
- "spike_left": SpikeLeft, "spike_right": SpikeRight, "spike_top": SpikeTop,
- "spike_bottom": SpikeBottom, "death": Death,
- "frog": Frog, "hedgehog": Hedgehog, "bat": Bat, "jellyfish": Jellyfish,
- "worm": Worm, "scorpion": Scorpion, "mantanoid": Mantanoid,
- "fake_tile": FakeTile, "weak_stone": WeakStone, "spike_stone": SpikeStone,
- "macguffin": Macguffin, "artifact": Artifact, "etank": Etank,
- "life_orb": LifeOrb, "map": Map, "map_disk": MapDisk,
- "atomic_compressor": AtomicCompressor, "monkey_boots": MonkeyBoots,
- "hedgehog_hormone": HedgehogHormone,
- "warp_pad": WarpPad, "doorframe_x": DoorFrameX, "doorframe_y": DoorFrameY,
- "door_left": LeftDoor, "door_right": RightDoor, "door_up": UpDoor,
- "door_down": DownDoor,
- "timeline_switcher": TimelineSwitcher,
- "enemies": get_object, "doors": get_object, "stones": get_object,
- "powerups": get_object, "objects": get_object,
- "moving_platform_path": MovingPlatformPath,
- "triggered_moving_platform_path": TriggeredMovingPlatformPath,
- "player": PlayerLayer,
- "camera_x_guide": CameraXGuide, "camera_y_guide": CameraYGuide,
- "map_wall_left": MapLeftWall, "map_wall_right": MapRightWall,
- "map_wall_top": MapTopWall, "map_wall_bottom": MapBottomWall,
- "map_door_left": MapLeftDoor, "map_door_right": MapRightDoor,
- "map_door_top": MapTopDoor, "map_door_bottom": MapBottomDoor,
- "map_ignore_region": IgnoreRegion, "mantanoid_nogo": MantanoidNoGo
- }
- print(_("Initializing game system..."))
- Game(SCREEN_SIZE[0], SCREEN_SIZE[1], scale=SCALE, fps=FPS, delta=DELTA,
- delta_min=DELTA_MIN, delta_max=DELTA_MAX,
- window_text="Hexoshi DEMO {}".format(__version__))
- #window_icon=os.path.join(DATA, "images", "misc", "icon.png"))
- sge.game.scale = None
- print(_("Initializing GUI system..."))
- xsge_gui.init()
- gui_handler = xsge_gui.Handler()
- xsge_gui.default_font.size = 8
- xsge_gui.textbox_font.size = 8
- menu_color = sge.gfx.Color("black")
- menu_text_color = sge.gfx.Color((64, 0, 255))
- menu_text_selected_color = sge.gfx.Color("white")
- print(_("Loading resources..."))
- if not os.path.exists(CONFIG):
- os.makedirs(CONFIG)
- # Save error messages to a text file (so they aren't lost).
- if not PRINT_ERRORS:
- stderr = os.path.join(CONFIG, "stderr.txt")
- if not os.path.isfile(stderr) or os.path.getsize(stderr) > 1000000:
- sys.stderr = open(stderr, 'w')
- else:
- sys.stderr = open(stderr, 'a')
- dt = datetime.datetime.now()
- sys.stderr.write("\n{}-{}-{} {}:{}:{}\n".format(
- dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second))
- del dt
- # Load sprites
- d = os.path.join(DATA, "images", "objects", "anneroy")
- anneroy_torso_offset = {}
- fname = os.path.join(d, "anneroy_sheet.png")
- anneroy_turn_sprite = sge.gfx.Sprite.from_tileset(
- fname, 2, 109, 3, xsep=3, width=39, height=43, origin_x=19, origin_y=19,
- fps=10)
- anneroy_teleport_sprite = sge.gfx.Sprite.from_tileset(
- fname, 360, 455, 7, xsep=4, width=46, height=49, origin_x=23, origin_y=25,
- fps=20)
- anneroy_wall_right_sprite = sge.gfx.Sprite.from_tileset(
- fname, 439, 228, 2, xsep=5, width=32, height=45, origin_x=23, origin_y=19,
- fps=10)
- anneroy_wall_left_sprite = sge.gfx.Sprite.from_tileset(
- fname, 439, 284, 2, xsep=5, width=31, height=45, origin_x=9, origin_y=19,
- fps=10)
- anneroy_walljump_left_sprite = sge.gfx.Sprite.from_tileset(
- fname, 522, 229, width=34, height=46, origin_x=17, origin_y=20)
- anneroy_walljump_right_sprite = sge.gfx.Sprite.from_tileset(
- fname, 522, 283, width=34, height=46, origin_x=15, origin_y=20)
- anneroy_compress_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 393, 3, xsep=5, width=27, height=32, origin_x=12, origin_y=8,
- fps=15)
- anneroy_ball_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 440, 8, xsep=8, width=16, height=16, origin_x=8, origin_y=-8)
- anneroy_decompress_fail_sprite = sge.gfx.Sprite.from_tileset(
- fname, 150, 393, 3, xsep=5, width=27, height=32, origin_x=12, origin_y=8,
- fps=15)
- anneroy_hedgehog_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 469, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
- anneroy_hedgehog_extend_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 510, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
- anneroy_hedgehog_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 551, 8, xsep=3, width=38, height=38, origin_x=19, origin_y=3)
- anneroy_death_right_sprite = sge.gfx.Sprite.from_tileset(
- fname, 5, 597, 7, xsep=5, width=86, height=82, origin_x=40, origin_y=38,
- fps=10)
- anneroy_death_left_sprite = sge.gfx.Sprite.from_tileset(
- fname, 5, 684, 7, xsep=5, width=86, height=82, origin_x=46, origin_y=38,
- fps=10)
- anneroy_explode_sprite = sge.gfx.Sprite.from_tileset(
- fname, 369, 771, 3, xsep=5, width=86, height=82, origin_x=43, origin_y=38,
- fps=10)
- anneroy_explode_fragments = sge.gfx.Sprite.from_tileset(
- fname, 406, 582, 21, xsep=3, width=6, height=6, origin_x=3, origin_y=3)
- anneroy_torso_right_idle_sprite = sge.gfx.Sprite.from_tileset(
- fname, 317, 45, width=26, height=27, origin_x=9, origin_y=19)
- anneroy_torso_right_aim_right_sprite = sge.gfx.Sprite.from_tileset(
- fname, 234, 45, width=26, height=20, origin_x=5, origin_y=19)
- anneroy_torso_right_aim_up_sprite = sge.gfx.Sprite.from_tileset(
- fname, 293, 38, width=20, height=27, origin_x=6, origin_y=26)
- anneroy_torso_right_aim_down_sprite = sge.gfx.Sprite.from_tileset(
- fname, 182, 52, width=20, height=30, origin_x=1, origin_y=12)
- anneroy_torso_right_aim_upright_sprite = sge.gfx.Sprite.from_tileset(
- fname, 264, 39, width=25, height=26, origin_x=5, origin_y=25)
- anneroy_torso_right_aim_downright_sprite = sge.gfx.Sprite.from_tileset(
- fname, 207, 45, width=23, height=26, origin_x=5, origin_y=19)
- anneroy_torso_left_idle_sprite = sge.gfx.Sprite.from_tileset(
- fname, 14, 45, width=27, height=25, origin_x=18, origin_y=19)
- anneroy_torso_left_aim_left_sprite = sge.gfx.Sprite.from_tileset(
- fname, 95, 45, width=26, height=20, origin_x=20, origin_y=19)
- anneroy_torso_left_aim_up_sprite = sge.gfx.Sprite.from_tileset(
- fname, 45, 38, width=17, height=27, origin_x=11, origin_y=26)
- anneroy_torso_left_aim_down_sprite = sge.gfx.Sprite.from_tileset(
- fname, 154, 52, width=20, height=30, origin_x=18, origin_y=12)
- anneroy_torso_left_aim_upleft_sprite = sge.gfx.Sprite.from_tileset(
- fname, 66, 39, width=25, height=26, origin_x=19, origin_y=25)
- anneroy_torso_left_aim_downleft_sprite = sge.gfx.Sprite.from_tileset(
- fname, 125, 45, width=23, height=26, origin_x=17, origin_y=19)
- anneroy_legs_stand_sprite = sge.gfx.Sprite.from_tileset(
- fname, 47, 76, width=19, height=24, origin_x=8, origin_y=0)
- anneroy_legs_run_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 299, 5, 2, xsep=8, ysep=31, width=40, height=24, origin_x=17,
- origin_y=0)
- anneroy_legs_jump_sprite = sge.gfx.Sprite.from_tileset(
- fname, 14, 234, 5, xsep=15, width=23, height=29, origin_x=8, origin_y=5,
- fps=30)
- anneroy_legs_fall_sprite = sge.gfx.Sprite.from_tileset(
- fname, 204, 234, width=23, height=29, origin_x=8, origin_y=5)
- anneroy_legs_land_sprite = sge.gfx.Sprite.from_tileset(
- fname, 242, 234, 2, xsep=15, width=23, height=29, origin_x=8, origin_y=5,
- fps=30)
- anneroy_legs_crouched_sprite = sge.gfx.Sprite.from_tileset(
- fname, 23, 85, width=21, height=15, origin_x=7, origin_y=-9)
- anneroy_legs_crouch_sprite = sge.gfx.Sprite.from_tileset(
- fname, 9, 189, 2, xsep=7, width=21, height=21, origin_x=8, origin_y=-3,
- fps=10)
- anneroy_bullet_dust_sprite = sge.gfx.Sprite.from_tileset(
- fname, 249, 119, width=26, height=16, origin_x=2, origin_y=7, fps=10)
- anneroy_bullet_sprite = sge.gfx.Sprite.from_tileset(
- fname, 287, 123, width=17, height=6, origin_x=14, origin_y=3, bbox_x=-8,
- bbox_y=-8, bbox_width=16, bbox_height=16)
- anneroy_bullet_dissipate_sprite = sge.gfx.Sprite.from_tileset(
- fname, 317, 102, 2, xsep=12, width=21, height=52, origin_x=12, origin_y=23,
- fps=10)
- n = id(anneroy_compress_sprite)
- anneroy_torso_offset[(n, 0)] = (0, 11)
- anneroy_torso_offset[(n, 1)] = (0, 11)
- anneroy_torso_offset[(n, 2)] = (0, 11)
- n = id(anneroy_decompress_fail_sprite)
- anneroy_torso_offset[(n, 0)] = (0, 11)
- anneroy_torso_offset[(n, 1)] = (0, 11)
- anneroy_torso_offset[(n, 2)] = (0, 11)
- n = id(anneroy_legs_run_sprite)
- anneroy_torso_offset[(n, 1)] = (0, 1)
- anneroy_torso_offset[(n, 2)] = (0, 3)
- anneroy_torso_offset[(n, 3)] = (0, 4)
- anneroy_torso_offset[(n, 4)] = (0, 2)
- anneroy_torso_offset[(n, 6)] = (0, 1)
- anneroy_torso_offset[(n, 7)] = (0, 3)
- anneroy_torso_offset[(n, 8)] = (0, 5)
- anneroy_torso_offset[(n, 9)] = (0, 3)
- n = id(anneroy_legs_jump_sprite)
- anneroy_torso_offset[(n, 0)] = (0, 3)
- anneroy_torso_offset[(n, 1)] = (0, -5)
- anneroy_torso_offset[(n, 2)] = (0, -2)
- anneroy_torso_offset[(n, 3)] = (0, -2)
- anneroy_torso_offset[(n, 4)] = (0, -3)
- n = id(anneroy_legs_fall_sprite)
- anneroy_torso_offset[(n, 0)] = (0, -2)
- n = id(anneroy_legs_land_sprite)
- anneroy_torso_offset[(n, 0)] = (0, -5)
- anneroy_torso_offset[(n, 1)] = (0, 3)
- n = id(anneroy_legs_crouched_sprite)
- anneroy_torso_offset[(n, 0)] = (0, 11)
- n = id(anneroy_legs_crouch_sprite)
- anneroy_torso_offset[(n, 0)] = (0, 3)
- anneroy_torso_offset[(n, 1)] = (0, 9)
- d = os.path.join(DATA, "images", "objects", "enemies")
- frog_stand_sprite = sge.gfx.Sprite("frog_stand", d)
- frog_jump_sprite = sge.gfx.Sprite("frog_jump", d)
- frog_fall_sprite = sge.gfx.Sprite("frog_fall", d)
- bat_sprite = sge.gfx.Sprite("bat", d, fps=10, bbox_x=3, bbox_y=4,
- bbox_width=10, bbox_height=10)
- worm_sprite = sge.gfx.Sprite("worm", d, fps=10)
- worm_base_sprite = sge.gfx.Sprite("worm_base", d, fps=10)
- fname = os.path.join(d, "hedgehog_sheet.png")
- hedgehog_stand_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 0, width=20, height=20)
- hedgehog_walk_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 20, 6, width=20, height=20)
- hedgehog_compress_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 40, 2, width=20, height=20, fps=15)
- hedgehog_ball_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 60, 8, width=20, height=20)
- hedgehog_uncompress_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 80, 2, width=20, height=20, fps=15)
- fname = os.path.join(d, "jellyfish_sheet.png")
- jellyfish_idle_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 0, 7, width=32, height=32, origin_x=24, origin_y=24, fps=20)
- jellyfish_swim_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 64, 6, width=32, height=32, origin_x=24, origin_y=24, fps=50)
- jellyfish_swim_sprite = sge.gfx.Sprite.from_tileset(
- fname, 192, 64, 6, width=32, height=32, origin_x=24, origin_y=24, fps=50)
- fname = os.path.join(d, "scorpion_sheet.png")
- scorpion_stand_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 0, width=60, height=36, origin_x=30, origin_y=9)
- scorpion_walk_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 36, 6, width=60, height=36, origin_x=30, origin_y=9)
- scorpion_shoot_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 108, 11, width=60, height=36, origin_x=30, origin_y=9, fps=20)
- scorpion_shoot_end_sprite = sge.gfx.Sprite.from_tileset(
- fname, 0, 144, 5, width=60, height=36, origin_x=30, origin_y=9, fps=20)
- scorpion_projectile_sprite = sge.gfx.Sprite(
- "scorpion_projectile", d, origin_y=2, bbox_x=2, bbox_y=1, bbox_width=17,
- bbox_height=4)
- scorpion_projectile_shard_sprite = sge.gfx.Sprite(
- "scorpion_projectile_shard", d, fps=0)
- fname = os.path.join(d, "mantanoid_sheet.png")
- mantanoid_stand_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 51, width=32, height=48, origin_x=15, origin_y=15)
- mantanoid_idle_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 208, 12, xsep=5, width=33, height=50, origin_x=15, origin_y=17,
- fps=10)
- mantanoid_turn_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 120, 3, xsep=5, width=32, height=47, origin_x=15, origin_y=14,
- fps=10)
- mantanoid_walk_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 657, 10, xsep=3, width=41, height=49, origin_x=23, origin_y=16)
- mantanoid_hop_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
- fps=10)
- mantanoid_jump_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 372, 5, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
- fps=10)
- mantanoid_jump_sprite = sge.gfx.Sprite.from_tileset(
- fname, 156, 299, width=32, height=57, origin_x=15, origin_y=24)
- mantanoid_fall_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 193, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
- fps=10)
- mantanoid_fall_sprite = sge.gfx.Sprite.from_tileset(
- fname, 304, 299, width=32, height=57, origin_x=15, origin_y=24)
- mantanoid_land_sprite = sge.gfx.Sprite.from_tileset(
- fname, 341, 299, 3, xsep=5, width=32, height=57, origin_x=15, origin_y=24,
- fps=10)
- mantanoid_slash_start_sprite = sge.gfx.Sprite.from_tileset(
- fname, 41, 470, 3, xsep=5, width=45, height=65, origin_x=15, origin_y=32,
- fps=10)
- mantanoid_slash_single_sprite = sge.gfx.Sprite.from_tileset(
- fname, 191, 470, 4, xsep=5, width=45, height=65, origin_x=15, origin_y=32,
- fps=10)
- mantanoid_slash_double_first_sprite = sge.gfx.Sprite.from_tileset(
- fname, 233, 551, 4, xsep=3, width=61, height=65, origin_x=15, origin_y=32,
- fps=10)
- mantanoid_slash_double_second_sprite = sge.gfx.Sprite.from_tileset(
- fname, 489, 551, 3, xsep=3, width=61, height=65,
- origin_x=(15 + MANTANOID_DOUBLESLASH_OFFSET), origin_y=32, fps=10)
- d = os.path.join(DATA, "images", "objects", "doors")
- door_barrier_x_sprite = sge.gfx.Sprite("barrier_x", d, origin_y=-8, fps=30,
- bbox_y=8, bbox_width=8, bbox_height=48)
- door_barrier_y_sprite = sge.gfx.Sprite("barrier_y", d, origin_x=-8, fps=30,
- bbox_x=8, bbox_width=48, bbox_height=8)
- doorframe_regular_x_closed_sprite = sge.gfx.Sprite("regular_x_closed", d)
- doorframe_regular_x_open_sprite = sge.gfx.Sprite("regular_x_open", d)
- doorframe_regular_y_closed_sprite = sge.gfx.Sprite("regular_y_closed", d)
- doorframe_regular_y_open_sprite = sge.gfx.Sprite("regular_y_open", d)
- d = os.path.join(DATA, "images", "objects", "stones")
- stone_fragment_sprite = sge.gfx.Sprite("stone_fragment", d)
- d = os.path.join(DATA, "images", "objects", "powerups")
- life_orb_sprite = sge.gfx.Sprite("life_orb", d, fps=10)
- powerup_map_sprite = sge.gfx.Sprite("map", d, fps=3)
- atomic_compressor_sprite = sge.gfx.Sprite(
- "atomic_compressor", d, origin_y=1, fps=10, bbox_width=16, bbox_height=16)
- monkey_boots_sprite = sge.gfx.Sprite(
- "monkey_boots", d, bbox_y=9, bbox_width=16, bbox_height=7)
- monkey_boots_gleam_sprite = sge.gfx.Sprite(
- "monkey_boots_gleam", d, origin_x=10, origin_y=5, fps=15)
- hedgehog_hormone_sprite = sge.gfx.Sprite("hedgehog_hormone", d)
- hedgehog_hormone_bubble_sprite = sge.gfx.Sprite("hedgehog_hormone_bubble", d,
- fps=5)
- d = os.path.join(DATA, "images", "objects", "misc")
- warp_pad_active_sprite = sge.gfx.Sprite("warp_pad_active", d)
- warp_pad_inactive_sprite = sge.gfx.Sprite("warp_pad_inactive", d)
- d = os.path.join(DATA, "images", "map")
- map_wall_left_sprite = sge.gfx.Sprite("wall_left", d)
- map_wall_right_sprite = sge.gfx.Sprite("wall_right", d)
- map_wall_top_sprite = sge.gfx.Sprite("wall_top", d)
- map_wall_bottom_sprite = sge.gfx.Sprite("wall_bottom", d)
- map_door_left_sprite = sge.gfx.Sprite("door_left", d)
- map_door_right_sprite = sge.gfx.Sprite("door_right", d)
- map_door_top_sprite = sge.gfx.Sprite("door_top", d)
- map_door_bottom_sprite = sge.gfx.Sprite("door_bottom", d)
- map_powerup_sprite = sge.gfx.Sprite("powerup", d)
- map_warp_pad_sprite = sge.gfx.Sprite("warp_pad", d)
- map_player_sprite = sge.gfx.Sprite("player", d)
- d = os.path.join(DATA, "images", "misc")
- logo_sprite = sge.gfx.Sprite("logo", d, origin_x=125)
- font_sprite = sge.gfx.Sprite.from_tileset(
- os.path.join(d, "font.png"), columns=18, rows=19, width=7, height=9)
- font_small_sprite = sge.gfx.Sprite.from_tileset(
- os.path.join(d, "font_small.png"), columns=8, rows=12, width=7, height=7)
- font_big_sprite = sge.gfx.Sprite.from_tileset(
- os.path.join(d, "font_big.png"), columns=8, rows=12, width=14, height=14,
- xsep=2, ysep=2)
- healthbar_back_sprite = sge.gfx.Sprite("healthbar_back", d, origin_x=2,
- origin_y=1)
- healthbar_front_sprite = sge.gfx.Sprite("healthbar_front", d,
- transparent=False)
- healthbar_width = healthbar_front_sprite.width
- healthbar_height = healthbar_front_sprite.height
- etank_empty_sprite = sge.gfx.Sprite("etank_empty", d)
- etank_full_sprite = sge.gfx.Sprite("etank_full", d)
- life_force_sprite = sge.gfx.Sprite(
- "life_force", d, origin_x=7, origin_y=7, fps=10)
- # Load backgrounds
- d = os.path.join(DATA, "images", "backgrounds")
- layers = []
- if not NO_BACKGROUNDS:
- layers = [
- sge.gfx.BackgroundLayer(
- sge.gfx.Sprite("iridia", d), 0, 0, -100000, xscroll_rate=0.7,
- yscroll_rate=0.7, repeat_left=True, repeat_right=True,
- repeat_up=True, repeat_down=True)]
- backgrounds["iridia"] = sge.gfx.Background(layers,
- sge.gfx.Color((21, 17, 22)))
- # Load fonts
- chars = ([six.unichr(i) for i in six.moves.range(32, 127)] +
- [None, ETANK_CHAR] + [' '] * 11 +
- [six.unichr(i) for i in six.moves.range(161, 384)])
- font = sge.gfx.Font.from_sprite(font_sprite, chars, size=9, hsep=-1)
- chars = [six.unichr(i) for i in six.moves.range(32, 127)] + [None]
- font_big = sge.gfx.Font.from_sprite(font_big_sprite, chars, size=14,
- hsep=2, vsep=2)
- chars = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
- "0123456789.,;:?!-_~#\"'&()[]|`\\/@^+=*$\xa3\u20ac<> ") + [None]
- font_small = sge.gfx.Font.from_sprite(font_small_sprite, chars, size=7,
- hsep=-1)
- # Load sounds
- shoot_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "shoot.wav"))
- bullet_death_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "bullet_death.ogg"), volume=0.2)
- land_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "land.ogg"), volume=0.5)
- ball_land_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "ball_land.ogg"))
- hedgehog_spikes_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "hedgehog_spikes.wav"), volume=0.5)
- hurt_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "hurt.wav"))
- death_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "death.wav"))
- stone_break_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "stone_break.ogg"), volume=0.5)
- powerup_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "powerup.wav"))
- heal_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "heal.wav"))
- warp_pad_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "warp_pad.ogg"))
- teleport_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "teleport.wav"))
- door_open_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "door_open.ogg"), volume=0.5)
- door_close_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "door_close.ogg"), volume=0.5)
- enemy_hurt_sound = stone_break_sound
- enemy_death_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "enemy_death.wav"))
- frog_jump_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "frog_jump.wav"))
- scorpion_shoot_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "scorpion_shoot.wav"))
- scorpion_projectile_break_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "scorpion_projectile_break.ogg"), volume=0.5)
- mantanoid_approach_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "mantanoid_approach.wav"))
- mantanoid_slash_sound = sge.snd.Sound(
- os.path.join(DATA, "sounds", "mantanoid_slash.wav"))
- select_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "select.ogg"))
- pause_sound = select_sound
- confirm_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "confirm.wav"))
- cancel_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "cancel.wav"))
- error_sound = cancel_sound
- type_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "type.wav"))
- # Create objects
- ##lava_animation = sge.dsp.Object(0, 0, sprite=lava_body_sprite, visible=False,
- ## tangible=False)
- # Create rooms
- sge.game.start_room = TitleScreen.load(
- os.path.join("special", "title_screen.tmx"), True)
- sge.game.mouse.visible = False
- # Load map data
- map_rooms = {}
- map_objects = {}
- if not GEN_MAP:
- try:
- with open(os.path.join(DATA, "map", "rooms.json")) as f:
- d = json.load(f)
- except (IOError, ValueError):
- generate_map()
- else:
- for i in d:
- map_rooms[i] = tuple(d[i])
- try:
- with open(os.path.join(DATA, "map", "objects.json")) as f:
- d = json.load(f)
- except (IOError, ValueError):
- generate_map()
- else:
- for i in d:
- x, y = tuple(i.split(','))
- j = (int(x), int(y))
- map_objects[j] = d[i]
- try:
- with open(os.path.join(DATA, "map", "info.json")) as f:
- d = json.load(f)
- except (IOError, ValueError):
- generate_map()
- else:
- num_powerups = d.get("powerups", 0)
- num_artifacts = d.get("artifacts", 0)
- else:
- generate_map()
- if SAVE_MAP:
- map_revealed = list(map_objects.keys())
- map_explored = map_revealed
- draw_map().save("map.png")
- map_revealed = []
- map_explored = []
- try:
- with open(os.path.join(CONFIG, "config.json")) as f:
- cfg = json.load(f)
- except (IOError, ValueError):
- cfg = {}
- finally:
- cfg_version = cfg.get("version", 0)
- fullscreen = cfg.get("fullscreen", fullscreen)
- update_fullscreen()
- scale_method = cfg.get("scale_method", scale_method)
- sge.game.scale_method = scale_method
- sound_enabled = cfg.get("sound_enabled", sound_enabled)
- music_enabled = cfg.get("music_enabled", music_enabled)
- stereo_enabled = cfg.get("stereo_enabled", stereo_enabled)
- fps_enabled = cfg.get("fps_enabled", fps_enabled)
- metroid_controls = cfg.get("metroid_controls", metroid_controls)
- joystick_threshold = cfg.get("joystick_threshold", joystick_threshold)
- xsge_gui.joystick_threshold = joystick_threshold
- ai_data |= set(cfg.get("ai_data", ai_data))
- keys_cfg = cfg.get("keys", {})
- left_key = keys_cfg.get("left", left_key)
- right_key = keys_cfg.get("right", right_key)
- up_key = keys_cfg.get("up", up_key)
- aim_diag_key = keys_cfg.get("aim_diag", aim_diag_key)
- down_key = keys_cfg.get("down", down_key)
- jump_key = keys_cfg.get("jump", jump_key)
- shoot_key = keys_cfg.get("shoot", shoot_key)
- aim_up_key = keys_cfg.get("aim_up", aim_up_key)
- aim_down_key = keys_cfg.get("aim_down", aim_down_key)
- mode_reset_key = keys_cfg.get("mode_reset", mode_reset_key)
- mode_key = keys_cfg.get("mode", mode_key)
- pause_key = keys_cfg.get("pause", pause_key)
- map_key = keys_cfg.get("map", map_key)
- js_cfg = cfg.get("joystick", {})
- left_js = [[tuple(j) for j in js] for js in js_cfg.get("left", left_js)]
- right_js = [[tuple(j) for j in js] for js in js_cfg.get("right", right_js)]
- up_js = [[tuple(j) for j in js] for js in js_cfg.get("up", up_js)]
- down_js = [[tuple(j) for j in js] for js in js_cfg.get("down", down_js)]
- aim_diag_js = [[tuple(j) for j in js]
- for js in js_cfg.get("aim_diag", aim_diag_js)]
- jump_js = [[tuple(j) for j in js] for js in js_cfg.get("jump", jump_js)]
- shoot_js = [[tuple(j) for j in js] for js in js_cfg.get("shoot", shoot_js)]
- aim_up_js = [[tuple(j) for j in js]
- for js in js_cfg.get("aim_up", aim_up_js)]
- aim_down_js = [[tuple(j) for j in js]
- for js in js_cfg.get("aim_down", aim_down_js)]
- mode_reset_js = [[tuple(j) for j in js]
- for js in js_cfg.get("mode_reset", mode_reset_js)]
- mode_js = [[tuple(j) for j in js] for js in js_cfg.get("mode", mode_js)]
- pause_js = [[tuple(j) for j in js] for js in js_cfg.get("pause", pause_js)]
- map_js = [[tuple(j) for j in js] for js in js_cfg.get("map", map_js)]
- set_gui_controls()
- try:
- with open(os.path.join(CONFIG, "save_slots.json")) as f:
- loaded_slots = json.load(f)
- except (IOError, ValueError):
- pass
- else:
- for i in six.moves.range(min(len(loaded_slots), len(save_slots))):
- slot = loaded_slots[i]
- if slot is not None and slot.get("save_format", 0) > 0:
- save_slots[i] = slot
- else:
- save_slots[i] = None
- print(_("Starting game..."))
- try:
- sge.game.start()
- finally:
- write_to_disk()
|