retux.py 310 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741
  1. #!/usr/bin/env python3
  2. # reTux
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. __version__ = "1.6"
  17. import argparse
  18. import datetime
  19. import gettext
  20. import itertools
  21. import json
  22. import math
  23. import os
  24. import random
  25. import shutil
  26. import sys
  27. import tempfile
  28. import time
  29. import traceback
  30. import warnings
  31. import weakref
  32. import zipfile
  33. import sge
  34. import xsge_gui
  35. import xsge_lighting
  36. import xsge_path
  37. import xsge_physics
  38. import xsge_tiled
  39. try:
  40. from tkinter import Tk
  41. import tkinter.filedialog as tkinter_filedialog
  42. except ImportError:
  43. HAVE_TK = False
  44. else:
  45. HAVE_TK = True
  46. if getattr(sys, "frozen", False):
  47. __file__ = sys.executable
  48. DATA = tempfile.mkdtemp("retux-data")
  49. CONFIG = os.path.join(
  50. os.getenv("XDG_CONFIG_HOME", os.path.join(os.path.expanduser("~"),
  51. ".config")), "retux")
  52. LOCAL = os.path.join(
  53. os.getenv("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local",
  54. "share")), "retux")
  55. dirs = [os.path.join(os.path.dirname(__file__), "data"),
  56. os.path.join(LOCAL, "data")]
  57. gettext.install("retux", os.path.abspath(os.path.join(dirs[0], "locale")))
  58. parser = argparse.ArgumentParser()
  59. parser.add_argument(
  60. "--version", action="version", version=f"ReTux {__version__}",
  61. help=_("Output version information and exit."))
  62. parser.add_argument(
  63. "-p", "--print-errors",
  64. help=_("Print errors directly to stdout rather than saving them in a file."),
  65. action="store_true")
  66. parser.add_argument(
  67. "-l", "--lang",
  68. help=_("Manually choose a different language to use."))
  69. parser.add_argument(
  70. "--nodelta",
  71. help=_("Disable delta timing. Causes the game to slow down when it can't "
  72. "run at full speed instead of becoming choppier."),
  73. action="store_true")
  74. parser.add_argument(
  75. "-d", "--datadir",
  76. help=_('Where to load the game data from (Default: "{}")').format(dirs[0]))
  77. parser.add_argument(
  78. "--level",
  79. help=_("Play the indicated level and then exit."))
  80. parser.add_argument(
  81. "--record",
  82. help=_("Start the indicated level and record player actions in a "
  83. "timeline. Useful for making cutscenes."))
  84. parser.add_argument(
  85. "--no-backgrounds",
  86. help=_("Only show solid colors for backgrounds (uses less RAM)."),
  87. action="store_true")
  88. parser.add_argument(
  89. "--no-hud", help=_("Don't show the player's heads-up display."),
  90. action="store_true")
  91. parser.add_argument("--god", help=_("Pray to your god. Choose wisely…"))
  92. args = parser.parse_args()
  93. PRINT_ERRORS = args.print_errors
  94. DELTA = not args.nodelta
  95. if args.datadir:
  96. dirs[0] = args.datadir
  97. LEVEL = args.level
  98. RECORD = args.record
  99. NO_BACKGROUNDS = args.no_backgrounds
  100. NO_HUD = args.no_hud
  101. GOD = False
  102. HELL = False
  103. if args.god:
  104. if args.god.lower() == "nano":
  105. GOD = True
  106. elif args.god.lower() in {"emacs", "vi", "vim", "ed"}:
  107. HELL = True
  108. for d in dirs:
  109. if os.path.isdir(d):
  110. for dirpath, dirnames, filenames in os.walk(d, True, None, True):
  111. dirtail = os.path.relpath(dirpath, d)
  112. nd = os.path.join(DATA, dirtail)
  113. for dirname in dirnames:
  114. dp = os.path.join(nd, dirname)
  115. if not os.path.exists(dp):
  116. os.makedirs(dp)
  117. for fname in filenames:
  118. shutil.copy2(os.path.join(dirpath, fname), nd)
  119. del dirs
  120. gettext.install("retux", os.path.abspath(os.path.join(DATA, "locale")))
  121. if args.lang:
  122. lang = gettext.translation("retux",
  123. os.path.abspath(os.path.join(DATA, "locale")),
  124. [args.lang])
  125. lang.install()
  126. if GOD:
  127. print(_("The text editor god of nano smiles upon you. God mode enabled!"))
  128. elif HELL:
  129. print(_("The true text editor god has sent you to hell for your heresy!"))
  130. SCREEN_SIZE = [800, 480]
  131. TILE_SIZE = 32
  132. FPS = 56
  133. DELTA_MIN = FPS / 2
  134. DELTA_MAX = FPS * 4
  135. TRANSITION_TIME = 750
  136. DEFAULT_LEVELSET = "retux.json"
  137. DEFAULT_LEVEL_TIME_BONUS = 90000
  138. TUX_ORIGIN_X = 28
  139. TUX_ORIGIN_Y = 16
  140. TUX_KICK_TIME = 10
  141. GRAVITY = 0.4
  142. PLAYER_MAX_HP = 2 if HELL else 5
  143. PLAYER_WALK_SPEED = 2
  144. PLAYER_SKID_THRESHOLD = 3
  145. PLAYER_RUN_SPEED = 4
  146. PLAYER_MAX_SPEED = 5
  147. PLAYER_ACCELERATION = 0.2
  148. PLAYER_AIR_ACCELERATION = 0.1
  149. PLAYER_FRICTION = 0.17
  150. PLAYER_AIR_FRICTION = 0.03
  151. PLAYER_JUMP_HEIGHT = 4 * TILE_SIZE + 2
  152. PLAYER_RUN_JUMP_HEIGHT = 5 * TILE_SIZE + 2
  153. PLAYER_STOMP_HEIGHT = TILE_SIZE / 2
  154. PLAYER_FALL_SPEED = 8
  155. PLAYER_SLIDE_ACCEL = 0.3
  156. PLAYER_SLIDE_SPEED = 1
  157. PLAYER_WALK_FRAMES_PER_PIXEL = 2 / 17
  158. PLAYER_RUN_FRAMES_PER_PIXEL = 1 / 10
  159. PLAYER_HITSTUN = 30 if HELL else 120
  160. PLAYER_DIE_HEIGHT = 6 * TILE_SIZE
  161. PLAYER_DIE_FALL_SPEED = 8
  162. PLAYER_OFF_FLOOR_THRESHOLD = 2
  163. SNOWMAN_WALK_SPEED = 3 if HELL else 2
  164. SNOWMAN_STRONG_WALK_SPEED = 4 if HELL else 3
  165. SNOWMAN_FINAL_WALK_SPEED = 6 if HELL else 4
  166. SNOWMAN_STUNNED_WALK_SPEED = 8 if HELL else 6
  167. SNOWMAN_ACCELERATION = 0.5 if HELL else 0.1
  168. SNOWMAN_STRONG_ACCELERATION = 0.5 if HELL else 0.2
  169. SNOWMAN_FINAL_ACCELERATION = 1 if HELL else 0.5
  170. SNOWMAN_HOP_HEIGHT = 2 * TILE_SIZE
  171. SNOWMAN_JUMP_HEIGHT = 7 * TILE_SIZE
  172. SNOWMAN_JUMP_TRIGGER = 2 * TILE_SIZE
  173. SNOWMAN_STOMP_DELAY = 30
  174. SNOWMAN_WALK_FRAMES_PER_PIXEL = 1 / 4
  175. SNOWMAN_HP = 20 if HELL else 5
  176. SNOWMAN_STRONG_STAGE = 2
  177. SNOWMAN_FINAL_STAGE = 3
  178. SNOWMAN_HITSTUN = 120
  179. SNOWMAN_SHAKE_NUM = 3
  180. RACCOT_WALK_SPEED = 4 if HELL else 3
  181. RACCOT_ACCELERATION = 0.2
  182. RACCOT_HOP_HEIGHT = TILE_SIZE
  183. RACCOT_JUMP_HEIGHT = 5 * TILE_SIZE
  184. RACCOT_JUMP_TRIGGER = 2 * TILE_SIZE
  185. RACCOT_STOMP_SPEED = 4
  186. RACCOT_STOMP_DELAY = 15
  187. RACCOT_WALK_FRAMES_PER_PIXEL = 1 / 22
  188. RACCOT_HP = 20 if HELL else 5
  189. RACCOT_HOP_TIME = 5
  190. RACCOT_HOP_INTERVAL_MIN = 45
  191. RACCOT_HOP_INTERVAL_MAX = 60 if HELL else 120
  192. RACCOT_CHARGE_INTERVAL_MIN = 300
  193. RACCOT_CHARGE_INTERVAL_MAX = 600
  194. RACCOT_CRUSH_LAX = -8 # A negative lax makes it have the opposite effect.
  195. RACCOT_CRUSH_GRAVITY = 0.6
  196. RACCOT_CRUSH_FALL_SPEED = 15
  197. RACCOT_CRUSH_SPEED = 12
  198. RACCOT_CRUSH_CHARGE = TILE_SIZE
  199. RACCOT_SHAKE_NUM = 4
  200. HP_POINTS = 1000
  201. TIMER_FRAMES = 40
  202. HEAL_COINS = 20
  203. CEILING_LAX = 10
  204. STOMP_LAX = 8
  205. BLOCK_HIT_HEIGHT = 8
  206. ITEM_HIT_HEIGHT = 16
  207. COIN_COLLECT_TIME = 30
  208. COIN_COLLECT_SPEED = 2
  209. ITEM_SPAWN_SPEED = 1
  210. SECOND_POINTS = 100
  211. COIN_POINTS = 100
  212. ENEMY_KILL_POINTS = 50
  213. AMMO_POINTS = 10
  214. TUXDOLL_POINTS = 50000 if HELL else 5000
  215. CAMERA_STOPPED_THRESHOLD = PLAYER_WALK_SPEED
  216. CAMERA_STOPPED_HSPEED_MAX = 4
  217. CAMERA_HSPEED_FACTOR = 1 / 2
  218. CAMERA_VSPEED_FACTOR = 1 / 20
  219. CAMERA_OFFSET_FACTOR = 10
  220. CAMERA_MARGIN_TOP = 4 * TILE_SIZE
  221. CAMERA_MARGIN_BOTTOM = 5 * TILE_SIZE
  222. CAMERA_TARGET_MARGIN_BOTTOM = CAMERA_MARGIN_BOTTOM + TILE_SIZE
  223. WARP_LAX = 12
  224. WARP_SPEED = 3
  225. WARP_EDGE_SPEED = 1.5
  226. SHAKE_FRAME_TIME = FPS / DELTA_MIN
  227. SHAKE_AMOUNT = 3
  228. ENEMY_WALK_SPEED = 1
  229. ENEMY_FALL_SPEED = 7
  230. ENEMY_SLIDE_SPEED = 0.3
  231. ENEMY_HIT_BELOW_HEIGHT = TILE_SIZE * 3 / 4
  232. SNOWBALL_BOUNCE_HEIGHT = TILE_SIZE * 3 + 2
  233. KICK_FORWARD_SPEED = 6
  234. KICK_FORWARD_HEIGHT = TILE_SIZE * 3 / 4
  235. KICK_UP_HEIGHT = 5.5 * TILE_SIZE
  236. ICEBLOCK_GRAVITY = 0.6
  237. ICEBLOCK_FALL_SPEED = 9
  238. ICEBLOCK_FRICTION = 0.1
  239. ICEBLOCK_DASH_SPEED = 7
  240. JUMPY_BOUNCE_HEIGHT = TILE_SIZE * 4
  241. BOMB_GRAVITY = 0.6
  242. BOMB_TICK_TIME = 4
  243. EXPLOSION_TIME = FPS * 3 / 4
  244. ICICLE_LAX = TILE_SIZE * 3 / 4
  245. ICICLE_SHAKE_TIME = FPS
  246. ICICLE_GRAVITY = 0.75
  247. ICICLE_FALL_SPEED = 12
  248. CRUSHER_LAX = TILE_SIZE * 3 / 4
  249. CRUSHER_GRAVITY = 1
  250. CRUSHER_FALL_SPEED = 15
  251. CRUSHER_RISE_SPEED = 2
  252. CRUSHER_CRUSH_TIME = FPS * 2 / 3
  253. CRUSHER_SHAKE_NUM = 2
  254. THAW_FPS = 15
  255. THAW_TIME_DEFAULT = FPS * 5
  256. THAW_WARN_TIME = FPS
  257. BRICK_SHARD_NUM = 6
  258. BRICK_SHARD_SPEED = 3
  259. BRICK_SHARD_HEIGHT = TILE_SIZE * 2
  260. BRICK_SHARD_GRAVITY = 0.75
  261. BRICK_SHARD_FALL_SPEED = 12
  262. ROCK_GRAVITY = 0.6
  263. ROCK_FALL_SPEED = 10
  264. ROCK_FRICTION = 0.4
  265. SPRING_JUMP_HEIGHT = 8 * TILE_SIZE + 11
  266. FLOWER_FALL_SPEED = 5
  267. FLOWER_THROW_HEIGHT = TILE_SIZE / 2
  268. FLOWER_THROW_UP_HEIGHT = TILE_SIZE * 3 / 2
  269. FIREBALL_AMMO = 20
  270. FIREBALL_SPEED = 8
  271. FIREBALL_GRAVITY = 0.5
  272. FIREBALL_FALL_SPEED = 5
  273. FIREBALL_BOUNCE_HEIGHT = TILE_SIZE / 2
  274. FIREBALL_UP_HEIGHT = TILE_SIZE * 3 / 2
  275. ICEBULLET_AMMO = 20
  276. ICEBULLET_SPEED = 16
  277. COINBRICK_COINS = 5 if HELL else 20
  278. COINBRICK_DECAY_TIME = 25
  279. ICE_CRACK_TIME = 20
  280. ICE_REFREEZE_RATE = 1 / 4
  281. LIGHT_RANGE = 600
  282. ACTIVATE_RANGE = 528
  283. ENEMY_ACTIVE_RANGE = 32
  284. ICEBLOCK_ACTIVE_RANGE = 400
  285. BULLET_ACTIVE_RANGE = 96
  286. ROCK_ACTIVE_RANGE = 464
  287. TILE_ACTIVE_RANGE = 528
  288. DEATHZONE = 2 * TILE_SIZE
  289. DEATH_FADE_TIME = 3000
  290. DEATH_RESTART_WAIT = FPS
  291. WIN_COUNT_START_TIME = 120
  292. WIN_COUNT_CONTINUE_TIME = 45
  293. WIN_COUNT_POINTS_MULT = 111
  294. WIN_COUNT_TIME_MULT = 311
  295. WIN_COUNT_POINTS_MAX = FPS
  296. WIN_COUNT_TIME_MAX = 5 * FPS
  297. WIN_FINISH_DELAY = 120
  298. MAP_SPEED = 4
  299. TEXT_SPEED = 1000
  300. SAVE_NSLOTS = 10
  301. MENU_MAX_ITEMS = 14
  302. SOUND_MAX_RADIUS = 400
  303. SOUND_ZERO_RADIUS = 1200
  304. SOUND_CENTERED_RADIUS = 150
  305. SOUND_TILTED_RADIUS = 1000
  306. SOUND_TILT_LIMIT = 0.75
  307. text_outline_thickness = 0
  308. backgrounds = {}
  309. loaded_music = {}
  310. tux_grab_sprites = {}
  311. fullscreen = False
  312. scale_method = None
  313. scale_proportional = True
  314. sound_volume = 1
  315. music_volume = 1
  316. stereo_enabled = True
  317. fps_enabled = False
  318. joystick_threshold = 0.1
  319. left_key = [["left", "a"]]
  320. right_key = [["right", "d"]]
  321. up_key = [["up", "w"]]
  322. down_key = [["down", "s"]]
  323. jump_key = [["space"]]
  324. action_key = [["ctrl_left", "ctrl_right"]]
  325. sneak_key = [["shift_left", "shift_right"]]
  326. menu_key = [["tab", "backspace"]]
  327. pause_key = [["enter", "p"]]
  328. left_js = [[(0, "axis-", 0), (0, "hat_left", 0)]]
  329. right_js = [[(0, "axis+", 0), (0, "hat_right", 0)]]
  330. up_js = [[(0, "axis-", 1), (0, "hat_up", 0)]]
  331. down_js = [[(0, "axis+", 1), (0, "hat_down", 0)]]
  332. jump_js = [[(0, "button", 1), (0, "button", 3)]]
  333. action_js = [[(0, "button", 0)]]
  334. sneak_js = [[(0, "button", 2)]]
  335. menu_js = [[(0, "button", 8)]]
  336. pause_js = [[(0, "button", 9)]]
  337. save_slots = [None for i in range(SAVE_NSLOTS)]
  338. abort = False
  339. current_save_slot = None
  340. current_levelset = None
  341. start_cutscene = None
  342. worldmap = None
  343. loaded_worldmaps = {}
  344. levels = []
  345. loaded_levels = {}
  346. level_names = {}
  347. level_timers = {}
  348. cleared_levels = []
  349. tuxdolls_available = []
  350. tuxdolls_found = []
  351. watched_timelines = []
  352. level_time_bonus = 0
  353. current_worldmap = None
  354. worldmap_entry_space = None
  355. current_worldmap_space = None
  356. current_level = 0
  357. current_checkpoints = {}
  358. score = 0
  359. current_areas = {}
  360. main_area = None
  361. level_cleared = False
  362. mapdest = None
  363. mapdest_space = None
  364. class Game(sge.dsp.Game):
  365. fps_time = 0
  366. fps_frames = 0
  367. fps_text = ""
  368. def show_hell(self):
  369. # Disabling this at least for now. It's just too inconsistent.
  370. return
  371. if HELL:
  372. self.project_rectangle(0, 0, self.width, self.height, 20000,
  373. fill=sge.gfx.Color((255, 128, 128)),
  374. blend_mode=sge.BLEND_RGB_MULTIPLY)
  375. def event_step(self, time_passed, delta_mult):
  376. self.show_hell()
  377. if fps_enabled:
  378. self.fps_time += time_passed
  379. self.fps_frames += 1
  380. if self.fps_time >= 250:
  381. self.fps_text = str(round(
  382. (1000 * self.fps_frames) / self.fps_time, 2))
  383. self.fps_time = 0
  384. self.fps_frames = 0
  385. self.project_text(font_small, self.fps_text, self.width - 8,
  386. self.height - 8, z=1000,
  387. color=sge.gfx.Color("yellow"), halign="right",
  388. valign="bottom", outline=sge.gfx.Color("black"),
  389. outline_thickness=text_outline_thickness)
  390. def event_paused_step(self, time_passed, delta_mult):
  391. self.show_hell()
  392. def event_close(self):
  393. rush_save()
  394. self.end()
  395. def event_paused_close(self):
  396. self.event_close()
  397. class Level(sge.dsp.Room):
  398. """Handles levels."""
  399. def __init__(self, objects=(), *, background=None,
  400. object_area_width=TILE_SIZE * 2,
  401. object_area_height=TILE_SIZE * 2,
  402. name=None, bgname=None, music=None,
  403. time_bonus=DEFAULT_LEVEL_TIME_BONUS, spawn=None,
  404. timeline=None, ambient_light=None, disable_lights=False,
  405. persistent=True, **kwargs):
  406. self.fname = None
  407. self.name = name
  408. self.music = music
  409. self.time_bonus = time_bonus
  410. self.spawn = spawn
  411. self.persistent = persistent
  412. self.points = 0
  413. self.timeline_objects = {}
  414. self.warps = []
  415. self.shake_queue = 0
  416. self.pause_delay = TRANSITION_TIME
  417. self.game_won = False
  418. self.status_text = None
  419. if bgname is not None:
  420. background = backgrounds.get(bgname, background)
  421. self.load_timeline(timeline)
  422. self.ambient_light = ambient_light
  423. if (self.ambient_light and self.ambient_light.red >= 255
  424. and self.ambient_light.green >= 255
  425. and self.ambient_light.blue >= 255):
  426. self.ambient_light = None
  427. self.disable_lights = disable_lights or self.ambient_light is None
  428. super().__init__(objects, background=background,
  429. object_area_width=object_area_width,
  430. object_area_height=object_area_height, **kwargs)
  431. self.add(gui_handler)
  432. def load_timeline(self, timeline):
  433. self.timeline = {}
  434. self.timeline_name = ""
  435. self.timeline_step = 0
  436. self.timeline_skip_target = None
  437. if timeline:
  438. self.timeline_name = timeline
  439. fname = os.path.join(DATA, "timelines", timeline)
  440. with open(fname, 'r') as f:
  441. jt = json.load(f)
  442. for i in jt:
  443. self.timeline[eval(i)] = jt[i]
  444. def add_timeline_object(self, obj):
  445. if obj.ID is not None:
  446. self.timeline_objects[obj.ID] = weakref.ref(obj)
  447. def timeline_skipto(self, step):
  448. t_keys = sorted(self.timeline.keys())
  449. self.timeline_step = step
  450. while t_keys and t_keys[0] < step:
  451. i = t_keys.pop(0)
  452. self.timeline[i] = []
  453. def add_points(self, x):
  454. if main_area not in cleared_levels:
  455. self.points += x
  456. def show_hud(self):
  457. # Show darkness
  458. if self.ambient_light:
  459. xsge_lighting.project_darkness(ambient_light=self.ambient_light,
  460. buffer=TILE_SIZE * 2)
  461. else:
  462. xsge_lighting.clear_lights()
  463. if not NO_HUD:
  464. if self.points:
  465. score_text = f"{self.points}+{score}"
  466. else:
  467. score_text = str(score)
  468. time_bonus = level_timers.get(main_area, 0)
  469. text = "{}\n{}\n\n{}\n{}".format(
  470. _("Score"), score_text,
  471. _("Time Bonus") if time_bonus >= 0 else _("Time Penalty"),
  472. abs(time_bonus))
  473. sge.game.project_text(font, text, sge.game.width, 0,
  474. color=sge.gfx.Color("white"), halign="right",
  475. outline=sge.gfx.Color("black"),
  476. outline_thickness=text_outline_thickness)
  477. y = 0
  478. if self.name:
  479. sge.game.project_text(
  480. font, self.name, sge.game.width / 2, y,
  481. width=sge.game.width * 2 / 5, color=sge.gfx.Color("white"),
  482. halign="center", outline=sge.gfx.Color("black"),
  483. outline_thickness=text_outline_thickness)
  484. y += font.get_height(self.name, sge.game.width * 2 / 5,
  485. outline_thickness=text_outline_thickness)
  486. if main_area in tuxdolls_available or main_area in tuxdolls_found:
  487. if main_area in tuxdolls_found:
  488. s = tuxdoll_sprite
  489. else:
  490. s = tuxdoll_transparent_sprite
  491. sge.game.project_sprite(s, 0, sge.game.width / 2,
  492. s.height/2 + 8 + y)
  493. if self.status_text:
  494. sge.game.project_text(font, self.status_text,
  495. sge.game.width / 2, sge.game.height - 16,
  496. color=sge.gfx.Color("white"),
  497. halign="center", valign="middle",
  498. outline=sge.gfx.Color("black"),
  499. outline_thickness=text_outline_thickness)
  500. self.status_text = None
  501. def shake(self, num=1):
  502. shaking = (self.shake_queue or "shake_up" in self.alarms or
  503. "shake_down" in self.alarms)
  504. self.shake_queue = max(self.shake_queue, num)
  505. if not shaking:
  506. self.event_alarm("shake_down")
  507. for obj in self.objects:
  508. if isinstance(obj, SteadyIcicle):
  509. obj.check_shake(True)
  510. def pause(self):
  511. global level_timers
  512. global score
  513. if self.death_time is not None or "death" in self.alarms:
  514. if level_timers.setdefault(main_area, 0) >= 0:
  515. sge.snd.Music.stop()
  516. self.alarms["death"] = 0
  517. elif (self.timeline_skip_target is not None and
  518. self.timeline_step < self.timeline_skip_target):
  519. self.timeline_skipto(self.timeline_skip_target)
  520. elif self.pause_delay <= 0 and not self.won:
  521. sge.snd.Music.pause()
  522. play_sound(pause_sound)
  523. PauseMenu.create()
  524. def die(self):
  525. global current_areas
  526. current_areas = {}
  527. self.death_time = DEATH_FADE_TIME
  528. self.death_time_bonus = level_timers.setdefault(main_area, 0)
  529. if "timer" in self.alarms:
  530. del self.alarms["timer"]
  531. fade_time = DEATH_FADE_TIME
  532. for m in loaded_music.values():
  533. if m.playing:
  534. name, ext = os.path.splitext(m.fname)
  535. if name.endswith("-start"):
  536. fade_time = min(fade_time, round(m.length - m.position))
  537. break
  538. sge.snd.Music.clear_queue()
  539. sge.snd.Music.stop(fade_time)
  540. def return_to_map(self, completed=False):
  541. global current_worldmap
  542. global current_worldmap_space
  543. global mapdest
  544. global mapdest_space
  545. if completed:
  546. if mapdest:
  547. current_worldmap = mapdest
  548. if mapdest_space:
  549. current_worldmap_space = mapdest_space
  550. worldmap_entry_space = mapdest_space
  551. mapdest = None
  552. mapdest_space = None
  553. save_game()
  554. if current_worldmap:
  555. m = Worldmap.load(current_worldmap)
  556. m.start(transition="iris_out", transition_time=TRANSITION_TIME)
  557. else:
  558. sge.game.start_room.start()
  559. def win_level(self, victory_walk=True):
  560. global current_checkpoints
  561. if self.death_time is None and "death" not in self.alarms:
  562. for obj in self.objects[:]:
  563. if isinstance(obj, WinPuffObject) and obj.active:
  564. obj.win_puff()
  565. for obj in self.objects:
  566. if isinstance(obj, Player):
  567. obj.human = False
  568. obj.left_pressed = False
  569. obj.right_pressed = False
  570. obj.up_pressed = False
  571. obj.down_pressed = False
  572. obj.jump_pressed = False
  573. obj.action_pressed = False
  574. obj.sneak_pressed = True
  575. obj.jump_release()
  576. if victory_walk:
  577. if obj.xvelocity >= 0:
  578. obj.right_pressed = True
  579. else:
  580. obj.left_pressed = True
  581. if "timer" in self.alarms:
  582. del self.alarms["timer"]
  583. self.won = True
  584. self.alarms["win_count_points"] = WIN_COUNT_START_TIME
  585. current_checkpoints[main_area] = None
  586. sge.snd.Music.clear_queue()
  587. sge.snd.Music.stop()
  588. if music_volume:
  589. level_win_music.volume = music_volume
  590. level_win_music.play()
  591. def win_game(self):
  592. global current_level
  593. current_level = 0
  594. save_game()
  595. credits_room = CreditsScreen.load(os.path.join("special",
  596. "credits.json"))
  597. credits_room.start()
  598. def event_room_start(self):
  599. self.add(coin_animation)
  600. self.add(bonus_animation)
  601. self.add(lava_animation)
  602. self.add(goal_animation)
  603. self.event_room_resume()
  604. def event_room_resume(self):
  605. global main_area
  606. global level_time_bonus
  607. xsge_lighting.clear_lights()
  608. self.won = False
  609. self.win_count_points = False
  610. self.win_count_time = False
  611. self.count_mult = 1
  612. self.count_time = 0
  613. self.death_time = None
  614. self.alarms["timer"] = TIMER_FRAMES
  615. self.pause_delay = TRANSITION_TIME
  616. play_music(self.music)
  617. if main_area is None:
  618. main_area = self.fname
  619. if main_area == self.fname:
  620. level_time_bonus = self.time_bonus
  621. if GOD:
  622. level_timers[main_area] = min(0, level_timers.get(main_area, 0))
  623. elif main_area not in level_timers:
  624. if main_area in levels:
  625. level_timers[main_area] = level_time_bonus
  626. else:
  627. level_timers[main_area] = 0
  628. players = []
  629. spawn_point = None
  630. for obj in self.objects:
  631. if isinstance(obj, (Spawn, Door, WarpSpawn)):
  632. if self.spawn is not None and obj.spawn_id == self.spawn:
  633. spawn_point = obj
  634. if isinstance(obj, Warp) and obj not in self.warps:
  635. self.warps.append(obj)
  636. elif isinstance(obj, Player):
  637. players.append(obj)
  638. del_warps = []
  639. for warp in self.warps:
  640. if warp not in self.objects:
  641. del_warps.append(warp)
  642. for warp in del_warps:
  643. self.warps.remove(warp)
  644. if spawn_point is not None:
  645. for player in players:
  646. player.x = spawn_point.x
  647. player.y = spawn_point.y
  648. if player.view is not None:
  649. player.view.x = player.x - player.view.width / 2
  650. player.view.y = (player.y - player.view.height +
  651. CAMERA_TARGET_MARGIN_BOTTOM)
  652. if isinstance(spawn_point, WarpSpawn):
  653. player.visible = False
  654. player.tangible = False
  655. player.warping = True
  656. spawn_point.follow_start(player, WARP_SPEED)
  657. else:
  658. player.visible = True
  659. player.tangible = True
  660. player.warping = False
  661. def event_step(self, time_passed, delta_mult):
  662. global watched_timelines
  663. global level_timers
  664. global current_level
  665. global score
  666. global current_areas
  667. global main_area
  668. global level_cleared
  669. if self.pause_delay > 0:
  670. self.pause_delay -= time_passed
  671. # Handle inactive objects and lighting
  672. if self.ambient_light:
  673. range_ = max(ACTIVATE_RANGE, LIGHT_RANGE)
  674. else:
  675. range_ = ACTIVATE_RANGE
  676. for view in self.views:
  677. for obj in self.get_objects_at(
  678. view.x - range_, view.y - range_, view.width + range_ * 2,
  679. view.height + range_ * 2):
  680. if isinstance(obj, (Player, InteractiveObject)):
  681. if not self.disable_lights:
  682. obj.project_light()
  683. if not obj.active:
  684. if isinstance(obj, InteractiveObject):
  685. obj.update_active()
  686. elif isinstance(obj, (Lava, LavaSurface)):
  687. obj.image_index = lava_animation.image_index
  688. elif isinstance(obj, (Goal, GoalTop)):
  689. obj.image_index = goal_animation.image_index
  690. # Show HUD
  691. self.show_hud()
  692. # Timeline events
  693. t_keys = sorted(self.timeline.keys())
  694. while t_keys:
  695. i = t_keys.pop(0)
  696. if i <= self.timeline_step:
  697. while i in self.timeline and self.timeline[i]:
  698. command = self.timeline[i].pop(0)
  699. command = command.split(None, 1)
  700. if command:
  701. if len(command) >= 2:
  702. command, arg = command[:2]
  703. else:
  704. command = command[0]
  705. arg = ""
  706. if command.startswith("#"):
  707. # Comment; do nothing
  708. pass
  709. elif command == "setattr":
  710. args = arg.split(None, 2)
  711. if len(args) >= 3:
  712. obj, name, value = args[:3]
  713. try:
  714. value = eval(value)
  715. except Exception as e:
  716. m = _("An error occurred in a timeline "
  717. "'setattr' command:\n\n{}").format(
  718. traceback.format_exc())
  719. show_error(m)
  720. else:
  721. if obj in self.timeline_objects:
  722. obj = self.timeline_objects[obj]()
  723. if obj is not None:
  724. setattr(obj, name, value)
  725. elif obj == "__level__":
  726. setattr(self, name, value)
  727. elif command == "call":
  728. args = arg.split()
  729. if len(args) >= 2:
  730. obj, method = args[:2]
  731. fa = [eval(s) for s in args[2:]]
  732. if obj in self.timeline_objects:
  733. obj = self.timeline_objects[obj]()
  734. if obj is not None:
  735. getattr(obj, method, lambda: None)(*fa)
  736. elif obj == "__level__":
  737. getattr(self, method, lambda: None)(*fa)
  738. elif command == "dialog":
  739. args = arg.split(None, 1)
  740. if len(args) >= 2:
  741. portrait, text = args[:2]
  742. sprite = portrait_sprites.get(portrait)
  743. DialogBox(gui_handler, _(text), sprite).show()
  744. elif command == "play_music":
  745. self.music = arg
  746. play_music(arg)
  747. elif command == "timeline":
  748. if self.timeline_name not in watched_timelines:
  749. watched_timelines.append(self.timeline_name)
  750. self.load_timeline(arg)
  751. break
  752. elif command == "skip_to":
  753. try:
  754. arg = float(arg)
  755. except ValueError:
  756. pass
  757. else:
  758. self.timeline_skipto(arg)
  759. break
  760. elif command == "exec":
  761. try:
  762. exec(arg)
  763. except Exception as e:
  764. m = _("An error occurred in a timeline 'exec' "
  765. "command:\n\n{}").format(
  766. traceback.format_exc())
  767. show_error(m)
  768. elif command == "if":
  769. try:
  770. r = eval(arg)
  771. except Exception as e:
  772. m = _("An error occurred in a timeline 'if' "
  773. "statement:\n\n{}").format(
  774. traceback.format_exc())
  775. show_error(m)
  776. r = False
  777. finally:
  778. if not r:
  779. self.timeline[i] = []
  780. break
  781. elif command == "if_watched":
  782. if self.timeline_name not in watched_timelines:
  783. self.timeline[i] = []
  784. break
  785. elif command == "if_not_watched":
  786. if self.timeline_name in watched_timelines:
  787. self.timeline[i] = []
  788. break
  789. elif command == "while":
  790. try:
  791. r = eval(arg)
  792. except Exception as e:
  793. m = _("An error occurred in a timeline "
  794. "'while' statement:\n\n{}").format(
  795. traceback.format_exc())
  796. show_error(m)
  797. r = False
  798. finally:
  799. if r:
  800. cur_timeline = self.timeline[i][:]
  801. while_command = "while {}".format(arg)
  802. self.timeline[i].insert(0, while_command)
  803. t_keys.insert(0, i)
  804. self.timeline[i - 1] = cur_timeline
  805. self.timeline[i] = loop_timeline
  806. i -= 1
  807. self.timeline_step -= 1
  808. else:
  809. self.timeline[i] = []
  810. break
  811. else:
  812. del self.timeline[i]
  813. else:
  814. break
  815. else:
  816. if (self.timeline_name and
  817. self.timeline_name not in watched_timelines):
  818. watched_timelines.append(self.timeline_name)
  819. self.timeline_name = ""
  820. self.timeline_step += delta_mult
  821. if self.death_time is not None:
  822. a = int(255 * (DEATH_FADE_TIME - self.death_time) / DEATH_FADE_TIME)
  823. sge.game.project_rectangle(
  824. 0, 0, sge.game.width, sge.game.height, z=100,
  825. fill=sge.gfx.Color((0, 0, 0, min(a, 255))))
  826. time_bonus = level_timers.setdefault(main_area, 0)
  827. if time_bonus < 0 and cleared_levels:
  828. if HELL:
  829. self.count_time += time_passed
  830. if self.count_time >= 1000 / FPS:
  831. amt = int(math.copysign(
  832. min(math.ceil(abs(self.death_time_bonus) * 3
  833. * self.count_time / DEATH_FADE_TIME),
  834. abs(time_bonus)),
  835. time_bonus))
  836. self.count_time = 0
  837. if amt:
  838. score += amt
  839. level_timers[main_area] -= amt
  840. play_sound(coin_sound)
  841. else:
  842. level_timers[main_area] = 0
  843. if self.death_time < 0:
  844. self.death_time = None
  845. self.alarms["death"] = DEATH_RESTART_WAIT
  846. else:
  847. self.death_time -= time_passed
  848. elif "death" in self.alarms:
  849. sge.game.project_rectangle(0, 0, sge.game.width, sge.game.height,
  850. z=100, fill=sge.gfx.Color("black"))
  851. if self.won:
  852. if self.win_count_points:
  853. if self.points:
  854. self.count_time += delta_mult
  855. if self.count_time >= 1:
  856. amt = int(math.copysign(
  857. min(self.count_time * self.count_mult,
  858. abs(self.points)),
  859. self.points))
  860. self.count_time = 0
  861. score += amt
  862. self.points -= amt
  863. play_sound(coin_sound)
  864. else:
  865. self.win_count_points = False
  866. self.alarms["win_count_time"] = WIN_COUNT_CONTINUE_TIME
  867. elif self.win_count_time:
  868. time_bonus = level_timers.setdefault(main_area, 0)
  869. if time_bonus:
  870. self.count_time += delta_mult
  871. if self.count_time >= 1:
  872. amt = int(math.copysign(
  873. min(self.count_time * self.count_mult,
  874. abs(time_bonus)),
  875. time_bonus))
  876. self.count_time = 0
  877. score += amt
  878. level_timers[main_area] -= amt
  879. play_sound(coin_sound)
  880. else:
  881. self.win_count_time = False
  882. if main_area not in cleared_levels:
  883. self.alarms["win_count_hp"] = WIN_COUNT_CONTINUE_TIME
  884. else:
  885. self.alarms["win"] = WIN_FINISH_DELAY
  886. elif (not level_win_music.playing and
  887. "win_count_points" not in self.alarms and
  888. "win_count_time" not in self.alarms and
  889. "win_count_hp" not in self.alarms and
  890. "win" not in self.alarms):
  891. if main_area not in cleared_levels:
  892. cleared_levels.append(main_area)
  893. current_areas = {}
  894. level_cleared = True
  895. if self.game_won:
  896. self.win_game()
  897. elif current_worldmap:
  898. self.return_to_map(True)
  899. else:
  900. main_area = None
  901. current_level += 1
  902. if current_level < len(levels):
  903. save_game()
  904. level = self.__class__.load(levels[current_level],
  905. True)
  906. level.start(transition="fade")
  907. else:
  908. self.win_game()
  909. def event_paused_step(self, time_passed, delta_mult):
  910. # Handle lighting
  911. if self.ambient_light:
  912. range_ = max(ACTIVATE_RANGE, LIGHT_RANGE)
  913. else:
  914. range_ = ACTIVATE_RANGE
  915. for view in self.views:
  916. for obj in self.get_objects_at(
  917. view.x - range_, view.y - range_, view.width + range_ * 2,
  918. view.height + range_ * 2):
  919. if isinstance(obj, InteractiveObject):
  920. if not self.disable_lights:
  921. obj.project_light()
  922. self.show_hud()
  923. def event_alarm(self, alarm_id):
  924. global level_timers
  925. global score
  926. if alarm_id == "timer":
  927. if main_area in levels:
  928. level_timers.setdefault(main_area, 0)
  929. if main_area not in cleared_levels:
  930. level_timers[main_area] -= SECOND_POINTS
  931. if not HELL:
  932. level_timers[main_area] = max(
  933. -self.points, level_timers[main_area])
  934. self.alarms["timer"] = TIMER_FRAMES
  935. elif alarm_id == "shake_down":
  936. self.shake_queue -= 1
  937. for view in self.views:
  938. view.yport += SHAKE_AMOUNT
  939. self.alarms["shake_up"] = SHAKE_FRAME_TIME
  940. elif alarm_id == "shake_up":
  941. for view in self.views:
  942. view.yport -= SHAKE_AMOUNT
  943. if self.shake_queue:
  944. self.alarms["shake_down"] = SHAKE_FRAME_TIME
  945. elif alarm_id == "death":
  946. # Project a black rectangle to prevent showing the level on
  947. # the last frame.
  948. sge.game.project_rectangle(0, 0, sge.game.width, sge.game.height,
  949. z=100, fill=sge.gfx.Color("black"))
  950. if (not cleared_levels and
  951. current_checkpoints.get(main_area) is None):
  952. level_timers[main_area] = level_time_bonus
  953. if current_worldmap:
  954. self.return_to_map()
  955. elif main_area is not None:
  956. save_game()
  957. r = self.__class__.load(main_area, True)
  958. checkpoint = current_checkpoints.get(self.fname)
  959. if checkpoint is not None:
  960. area_name, area_spawn = checkpoint.split(':', 1)
  961. r = self.__class__.load(area_name, True)
  962. r.spawn = area_spawn
  963. r.start()
  964. elif alarm_id == "win_count_points":
  965. self.count_time = 0
  966. if self.points > 0:
  967. self.win_count_points = True
  968. self.count_mult = max(WIN_COUNT_POINTS_MULT,
  969. self.points / WIN_COUNT_POINTS_MAX)
  970. else:
  971. self.win_count_time = True
  972. time_bonus = level_timers.setdefault(main_area, 0)
  973. self.count_mult = max(WIN_COUNT_TIME_MULT,
  974. time_bonus / WIN_COUNT_TIME_MAX)
  975. elif alarm_id == "win_count_time":
  976. self.count_time = 0
  977. self.win_count_time = True
  978. time_bonus = level_timers.setdefault(main_area, 0)
  979. self.count_mult = max(WIN_COUNT_TIME_MULT,
  980. time_bonus / WIN_COUNT_TIME_MAX)
  981. elif alarm_id == "win_count_hp":
  982. if GOD:
  983. self.alarms["win"] = WIN_FINISH_DELAY
  984. else:
  985. for obj in self.objects:
  986. if isinstance(obj, Player) and obj.hp > 0:
  987. obj.hp -= 1
  988. score += HP_POINTS
  989. play_sound(heal_sound)
  990. self.alarms["win_count_hp"] = WIN_COUNT_CONTINUE_TIME
  991. break
  992. else:
  993. self.alarms["win"] = WIN_FINISH_DELAY
  994. @classmethod
  995. def load(cls, fname, show_prompt=False):
  996. global level_names
  997. global tuxdolls_available
  998. if fname in current_areas:
  999. r = current_areas[fname]
  1000. elif fname in loaded_levels:
  1001. r = loaded_levels.pop(fname)
  1002. else:
  1003. if show_prompt:
  1004. text = _("Loading level...")
  1005. if isinstance(sge.game.current_room, Worldmap):
  1006. sge.game.refresh()
  1007. sge.game.current_room.level_text = text
  1008. _refresh_screen(0, 0)
  1009. elif sge.game.current_room is not None:
  1010. sge.game.refresh()
  1011. x = sge.game.width / 2
  1012. y = sge.game.height / 2
  1013. w = font.get_width(text) + 32
  1014. h = font.get_height(text) + 32
  1015. sge.game.project_rectangle(x - w / 2, y - h / 2, w, h,
  1016. fill=sge.gfx.Color("black"))
  1017. sge.game.project_text(
  1018. font, text, x, y, color=sge.gfx.Color("white"),
  1019. halign="center", valign="middle",
  1020. outline=sge.gfx.Color("black"),
  1021. outline_thickness=text_outline_thickness)
  1022. _refresh_screen(0, 0)
  1023. else:
  1024. print(_("Loading \"{}\"...").format(fname))
  1025. try:
  1026. r = xsge_tiled.load(
  1027. os.path.join(DATA, "levels", fname), cls=cls, types=TYPES)
  1028. except Exception as e:
  1029. m = _("An error occurred when trying to load the level:\n\n"
  1030. "{}").format(traceback.format_exc())
  1031. show_error(m)
  1032. r = None
  1033. else:
  1034. r.fname = fname
  1035. if r is not None:
  1036. if r.persistent:
  1037. current_areas[fname] = r
  1038. if fname not in level_names:
  1039. name = r.name
  1040. if name:
  1041. level_names[fname] = name
  1042. elif fname in levels:
  1043. level_names[fname] = _("Level {}").format(
  1044. levels.index(fname) + 1)
  1045. else:
  1046. level_names[fname] = "???"
  1047. if main_area in levels and main_area not in tuxdolls_available:
  1048. for obj in r.objects:
  1049. if (isinstance(obj, TuxDoll) or
  1050. (isinstance(obj, (ItemBlock, HiddenItemBlock)) and
  1051. obj.item == "tuxdoll")):
  1052. tuxdolls_available.append(main_area)
  1053. break
  1054. elif fname in levels and fname not in tuxdolls_available:
  1055. for obj in r.objects:
  1056. if (isinstance(obj, TuxDoll) or
  1057. (isinstance(obj, (ItemBlock, HiddenItemBlock)) and
  1058. obj.item == "tuxdoll")):
  1059. tuxdolls_available.append(fname)
  1060. break
  1061. return r
  1062. class LevelTester(Level):
  1063. def return_to_map(self):
  1064. sge.game.end()
  1065. def win_game(self):
  1066. sge.game.end()
  1067. def event_alarm(self, alarm_id):
  1068. if alarm_id == "death":
  1069. sge.game.end()
  1070. else:
  1071. super().event_alarm(alarm_id)
  1072. class LevelRecorder(LevelTester):
  1073. def __init__(self, *args, **kwargs):
  1074. super().__init__(*args, **kwargs)
  1075. self.recording = {}
  1076. def add_recording_event(self, command):
  1077. self.recording.setdefault(self.timeline_step, []).append(command)
  1078. def event_key_press(self, key, char):
  1079. if key == "f12":
  1080. jt = self.recording
  1081. fname = "recording_{}.json".format(time.time())
  1082. with open(fname, 'w') as f:
  1083. json.dump(jt, f, indent=4, sort_keys=True)
  1084. sge.game.end()
  1085. for i in self.timeline_objects:
  1086. obj = self.timeline_objects[i]()
  1087. if isinstance(obj, Player) and obj.human:
  1088. if key in left_key[obj.player]:
  1089. self.add_recording_event(
  1090. "setattr {} left_pressed 1".format(obj.ID))
  1091. if key in right_key[obj.player]:
  1092. self.add_recording_event(
  1093. "setattr {} right_pressed 1".format(obj.ID))
  1094. if key in up_key[obj.player]:
  1095. self.add_recording_event("call {} press_up".format(obj.ID))
  1096. self.add_recording_event(
  1097. "setattr {} up_pressed 1".format(obj.ID))
  1098. if key in down_key[obj.player]:
  1099. self.add_recording_event(
  1100. "setattr {} down_pressed 1".format(obj.ID))
  1101. if key in jump_key[obj.player]:
  1102. self.add_recording_event("call {} jump".format(obj.ID))
  1103. self.add_recording_event(
  1104. "setattr {} jump_pressed 1".format(obj.ID))
  1105. if key in action_key[obj.player]:
  1106. self.add_recording_event("call {} action".format(obj.ID))
  1107. self.add_recording_event(
  1108. "setattr {} action_pressed 1".format(obj.ID))
  1109. if key in sneak_key[obj.player]:
  1110. self.add_recording_event(
  1111. "setattr {} sneak_pressed 1".format(obj.ID))
  1112. def event_key_release(self, key):
  1113. for i in self.timeline_objects:
  1114. obj = self.timeline_objects[i]()
  1115. if isinstance(obj, Player) and obj.human:
  1116. if key in left_key[obj.player]:
  1117. self.add_recording_event(
  1118. "setattr {} left_pressed 0".format(obj.ID))
  1119. if key in right_key[obj.player]:
  1120. self.add_recording_event(
  1121. "setattr {} right_pressed 0".format(obj.ID))
  1122. if key in up_key[obj.player]:
  1123. self.add_recording_event(
  1124. "setattr {} up_pressed 0".format(obj.ID))
  1125. if key in down_key[obj.player]:
  1126. self.add_recording_event(
  1127. "setattr {} down_pressed 0".format(obj.ID))
  1128. if key in jump_key[obj.player]:
  1129. self.add_recording_event(
  1130. "call {} jump_release".format(obj.ID))
  1131. self.add_recording_event(
  1132. "setattr {} jump_pressed 0".format(obj.ID))
  1133. if key in action_key[obj.player]:
  1134. self.add_recording_event(
  1135. "setattr {} action_pressed 0".format(obj.ID))
  1136. if key in sneak_key[obj.player]:
  1137. self.add_recording_event(
  1138. "setattr {} sneak_pressed 0".format(obj.ID))
  1139. class SpecialScreen(Level):
  1140. pass
  1141. class TitleScreen(SpecialScreen):
  1142. def show_hud(self):
  1143. pass
  1144. def event_room_resume(self):
  1145. super().event_room_resume()
  1146. MainMenu.create()
  1147. def event_key_press(self, key, char):
  1148. pass
  1149. class CreditsScreen(SpecialScreen):
  1150. def event_room_start(self):
  1151. super().event_room_start()
  1152. if self.fname in current_areas:
  1153. del current_areas[self.fname]
  1154. if self.fname in loaded_levels:
  1155. del loaded_levels[self.fname]
  1156. with open(os.path.join(DATA, "credits.json"), 'r') as f:
  1157. sections = json.load(f)
  1158. logo_section = sge.dsp.Object.create(self.width / 2, self.height,
  1159. sprite=logo_sprite,
  1160. tangible=False)
  1161. self.sections = [logo_section]
  1162. for section in sections:
  1163. if "title" in section:
  1164. head_sprite = sge.gfx.Sprite.from_text(
  1165. font_big, _(section["title"]), width=self.width,
  1166. color=sge.gfx.Color("white"), halign="center",
  1167. outline=sge.gfx.Color("black"),
  1168. outline_thickness=text_outline_thickness)
  1169. x = self.width / 2
  1170. y = self.sections[-1].bbox_bottom + font_big.size * 3
  1171. head_section = sge.dsp.Object.create(x, y, sprite=head_sprite,
  1172. tangible=False)
  1173. self.sections.append(head_section)
  1174. if "lines" in section:
  1175. for line in section["lines"]:
  1176. list_sprite = sge.gfx.Sprite.from_text(
  1177. font, _(line), width=self.width - 2 * TILE_SIZE,
  1178. color=sge.gfx.Color("white"), halign="center",
  1179. outline=sge.gfx.Color("black"),
  1180. outline_thickness=text_outline_thickness)
  1181. x = self.width / 2
  1182. y = self.sections[-1].bbox_bottom + font.size
  1183. list_section = sge.dsp.Object.create(
  1184. x, y, sprite=list_sprite, tangible=False)
  1185. self.sections.append(list_section)
  1186. for obj in self.sections:
  1187. obj.yvelocity = -0.5
  1188. def event_step(self, time_passed, delta_mult):
  1189. if self.sections[0].yvelocity > 0 and self.sections[0].y > self.height:
  1190. for obj in self.sections:
  1191. obj.yvelocity = 0
  1192. if self.sections[-1].bbox_bottom < 0 and "end" not in self.alarms:
  1193. sge.snd.Music.stop(fade_time=3000)
  1194. self.alarms["end"] = 3.5 * FPS
  1195. def event_alarm(self, alarm_id):
  1196. if alarm_id == "end":
  1197. sge.game.start_room.start()
  1198. def event_key_press(self, key, char):
  1199. if key in itertools.chain.from_iterable(down_key):
  1200. if "end" not in self.alarms:
  1201. for obj in self.sections:
  1202. obj.yvelocity -= 0.25
  1203. elif key in itertools.chain.from_iterable(up_key):
  1204. if "end" not in self.alarms:
  1205. for obj in self.sections:
  1206. obj.yvelocity += 0.25
  1207. elif (key in itertools.chain.from_iterable(jump_key) or
  1208. key in itertools.chain.from_iterable(action_key) or
  1209. key in itertools.chain.from_iterable(pause_key)):
  1210. sge.game.start_room.start()
  1211. def event_joystick(self, js_name, js_id, input_type, input_id, value):
  1212. js = (js_id, input_type, input_id)
  1213. if value >= joystick_threshold:
  1214. if js in itertools.chain.from_iterable(down_js):
  1215. if "end" not in self.alarms:
  1216. for obj in self.sections:
  1217. obj.yvelocity -= 0.25
  1218. elif js in itertools.chain.from_iterable(up_js):
  1219. if "end" not in self.alarms:
  1220. for obj in self.sections:
  1221. obj.yvelocity += 0.25
  1222. elif (js in itertools.chain.from_iterable(jump_js) or
  1223. js in itertools.chain.from_iterable(action_js) or
  1224. js in itertools.chain.from_iterable(pause_js)):
  1225. sge.game.start_room.start()
  1226. class Worldmap(sge.dsp.Room):
  1227. """Handles worldmaps."""
  1228. def __init__(self, objects=(), *, object_area_width=TILE_SIZE * 2,
  1229. object_area_height=TILE_SIZE * 2, music=None, **kwargs):
  1230. self.music = music
  1231. super().__init__(objects, object_area_width=object_area_width,
  1232. object_area_height=object_area_height, **kwargs)
  1233. def show_menu(self):
  1234. sge.snd.Music.pause()
  1235. play_sound(pause_sound)
  1236. WorldmapMenu.create()
  1237. def event_room_start(self):
  1238. self.level_text = None
  1239. self.level_tuxdoll_available = False
  1240. self.level_tuxdoll_found = False
  1241. self.event_room_resume()
  1242. def event_room_resume(self):
  1243. global loaded_levels
  1244. global main_area
  1245. global current_areas
  1246. global level_cleared
  1247. main_area = None
  1248. for obj in self.objects:
  1249. if isinstance(obj, MapSpace):
  1250. obj.update_sprite()
  1251. play_music(self.music)
  1252. level_cleared = False
  1253. def event_step(self, time_passed, delta_mult):
  1254. text = " {}/{}".format(len(tuxdolls_found), len(tuxdolls_available))
  1255. w = tuxdoll_sprite.width + font.get_width(text)
  1256. x = sge.game.width / 2 + tuxdoll_sprite.origin_x - w / 2
  1257. y = tuxdoll_sprite.origin_y + 16
  1258. sge.game.project_sprite(tuxdoll_shadow_sprite, 0, x + 2, y + 2)
  1259. sge.game.project_sprite(tuxdoll_sprite, 0, x, y)
  1260. x += tuxdoll_sprite.width - tuxdoll_sprite.origin_x
  1261. sge.game.project_text(font, text, x + 2, y + 2,
  1262. color=sge.gfx.Color("black"), halign="left",
  1263. valign="middle", outline=sge.gfx.Color("black"),
  1264. outline_thickness=text_outline_thickness)
  1265. sge.game.project_text(font, text, x, y, color=sge.gfx.Color("white"),
  1266. halign="left", valign="middle",
  1267. outline=sge.gfx.Color("black"),
  1268. outline_thickness=text_outline_thickness)
  1269. if self.level_text:
  1270. x = sge.game.width / 2
  1271. y = sge.game.height - font.size
  1272. sge.game.project_text(font, self.level_text, x + 2, y + 2,
  1273. color=sge.gfx.Color("black"),
  1274. halign="center", valign="bottom")
  1275. sge.game.project_text(font, self.level_text, x, y,
  1276. color=sge.gfx.Color("white"),
  1277. halign="center", valign="bottom",
  1278. outline=sge.gfx.Color("black"),
  1279. outline_thickness=text_outline_thickness)
  1280. if self.level_tuxdoll_available:
  1281. x = sge.game.width / 2
  1282. y = sge.game.height - font.size * 4
  1283. if self.level_tuxdoll_found:
  1284. sge.game.project_sprite(tuxdoll_shadow_sprite, 0, x + 2, y + 2)
  1285. sge.game.project_sprite(tuxdoll_sprite, 0, x, y)
  1286. else:
  1287. sge.game.project_sprite(tuxdoll_transparent_sprite, 0, x, y)
  1288. @classmethod
  1289. def load(cls, fname):
  1290. if fname in loaded_worldmaps:
  1291. return loaded_worldmaps.pop(fname)
  1292. else:
  1293. return xsge_tiled.load(
  1294. os.path.join(DATA, "worldmaps", fname), cls=cls,
  1295. types=TYPES)
  1296. class SolidLeft(xsge_physics.SolidLeft):
  1297. def __init__(self, *args, **kwargs):
  1298. kwargs.setdefault("visible", False)
  1299. kwargs.setdefault("checks_collisions", False)
  1300. super().__init__(*args, **kwargs)
  1301. class SolidRight(xsge_physics.SolidRight):
  1302. def __init__(self, *args, **kwargs):
  1303. kwargs.setdefault("visible", False)
  1304. kwargs.setdefault("checks_collisions", False)
  1305. super().__init__(*args, **kwargs)
  1306. class SolidTop(xsge_physics.SolidTop):
  1307. def __init__(self, *args, **kwargs):
  1308. kwargs.setdefault("visible", False)
  1309. kwargs.setdefault("checks_collisions", False)
  1310. super().__init__(*args, **kwargs)
  1311. class SolidBottom(xsge_physics.SolidBottom):
  1312. def __init__(self, *args, **kwargs):
  1313. kwargs.setdefault("visible", False)
  1314. kwargs.setdefault("checks_collisions", False)
  1315. super().__init__(*args, **kwargs)
  1316. class Solid(xsge_physics.Solid):
  1317. def __init__(self, *args, **kwargs):
  1318. kwargs.setdefault("visible", False)
  1319. kwargs.setdefault("checks_collisions", False)
  1320. super().__init__(*args, **kwargs)
  1321. class SlopeTopLeft(xsge_physics.SlopeTopLeft):
  1322. xsticky_top = True
  1323. def __init__(self, *args, **kwargs):
  1324. kwargs.setdefault("visible", False)
  1325. kwargs.setdefault("checks_collisions", False)
  1326. super().__init__(*args, **kwargs)
  1327. class SlopeTopRight(xsge_physics.SlopeTopRight):
  1328. xsticky_top = True
  1329. def __init__(self, *args, **kwargs):
  1330. kwargs.setdefault("visible", False)
  1331. kwargs.setdefault("checks_collisions", False)
  1332. super().__init__(*args, **kwargs)
  1333. class SlopeBottomLeft(xsge_physics.SlopeBottomLeft):
  1334. def __init__(self, *args, **kwargs):
  1335. kwargs.setdefault("visible", False)
  1336. kwargs.setdefault("checks_collisions", False)
  1337. super().__init__(*args, **kwargs)
  1338. class SlopeBottomRight(xsge_physics.SlopeBottomRight):
  1339. def __init__(self, *args, **kwargs):
  1340. kwargs.setdefault("visible", False)
  1341. kwargs.setdefault("checks_collisions", False)
  1342. super().__init__(*args, **kwargs)
  1343. class MovingPlatform(xsge_physics.SolidTop, xsge_physics.MobileWall):
  1344. sticky_top = True
  1345. def __init__(self, x, y, z=0, **kwargs):
  1346. kwargs.setdefault("sprite", platform_sprite)
  1347. super().__init__(x, y, z, **kwargs)
  1348. self.path = None
  1349. self.following = False
  1350. def event_step(self, time_passed, delta_mult):
  1351. super().event_step(time_passed, delta_mult)
  1352. if self.path and not self.following:
  1353. for other in self.collision(Player, y=(self.y - 1)):
  1354. if self in other.get_bottom_touching_wall():
  1355. self.path.follow_start(self, self.path.path_speed,
  1356. accel=self.path.path_accel,
  1357. decel=self.path.path_decel,
  1358. loop=self.path.path_loop)
  1359. break
  1360. class HurtLeft(SolidLeft):
  1361. pass
  1362. class HurtRight(SolidRight):
  1363. pass
  1364. class HurtTop(SolidTop):
  1365. pass
  1366. class HurtBottom(SolidBottom):
  1367. pass
  1368. class SpikeLeft(HurtLeft, xsge_physics.Solid):
  1369. pass
  1370. class SpikeRight(HurtRight, xsge_physics.Solid):
  1371. pass
  1372. class SpikeTop(HurtTop, xsge_physics.Solid):
  1373. pass
  1374. class SpikeBottom(HurtBottom, xsge_physics.Solid):
  1375. pass
  1376. class Death(sge.dsp.Object):
  1377. def __init__(self, *args, **kwargs):
  1378. kwargs.setdefault("visible", False)
  1379. kwargs.setdefault("checks_collisions", False)
  1380. super().__init__(*args, **kwargs)
  1381. class LevelEnd(sge.dsp.Object):
  1382. def __init__(self, *args, **kwargs):
  1383. kwargs.setdefault("visible", False)
  1384. kwargs.setdefault("checks_collisions", False)
  1385. super().__init__(*args, **kwargs)
  1386. class Player(xsge_physics.Collider):
  1387. name = "Super-Penguin Tux" if GOD else "Heretic Tux" if HELL else "Tux"
  1388. max_hp = PLAYER_MAX_HP
  1389. walk_speed = PLAYER_WALK_SPEED
  1390. run_speed = PLAYER_RUN_SPEED
  1391. max_speed = PLAYER_MAX_SPEED
  1392. acceleration = PLAYER_ACCELERATION
  1393. air_acceleration = PLAYER_AIR_ACCELERATION
  1394. friction = PLAYER_FRICTION
  1395. air_friction = PLAYER_AIR_FRICTION
  1396. jump_height = PLAYER_JUMP_HEIGHT
  1397. run_jump_height = PLAYER_RUN_JUMP_HEIGHT
  1398. stomp_height = PLAYER_STOMP_HEIGHT
  1399. gravity = GRAVITY
  1400. fall_speed = PLAYER_FALL_SPEED
  1401. slide_accel = PLAYER_SLIDE_ACCEL
  1402. slide_speed = PLAYER_SLIDE_SPEED
  1403. hitstun_time = PLAYER_HITSTUN
  1404. carry_x = 0
  1405. carry_y = 20
  1406. @property
  1407. def warping(self):
  1408. return self.__warping
  1409. @warping.setter
  1410. def warping(self, value):
  1411. self.__warping = value
  1412. if self.held_object is not None:
  1413. if value:
  1414. self.held_object.x = -666
  1415. self.held_object.y = -666 * (self.player + 1)
  1416. else:
  1417. self.held_object.x = self.x + self.held_object.image_origin_x
  1418. self.held_object.y = self.y
  1419. if self.image_xscale < 0:
  1420. self.held_object.x -= self.held_object.sprite.width
  1421. def __init__(self, x, y, z=0, *, bbox_x=-13, bbox_y=2, bbox_width=26,
  1422. bbox_height=30, regulate_origin=True, ID="player", player=0,
  1423. human=True, lose_on_death=True, view_frozen=False,
  1424. view_is_barrier=True, **kwargs):
  1425. self.ID = ID
  1426. self.player = player
  1427. self.human = human
  1428. self.lose_on_death = lose_on_death
  1429. self.view_frozen = view_frozen
  1430. self.view_is_barrier = view_is_barrier
  1431. self.held_object = None
  1432. self.left_pressed = False
  1433. self.right_pressed = False
  1434. self.up_pressed = False
  1435. self.down_pressed = False
  1436. self.jump_pressed = False
  1437. self.action_pressed = False
  1438. self.sneak_pressed = False
  1439. self.hp = self.max_hp
  1440. self.coins = 0
  1441. self.hitstun = False
  1442. self.warping = False
  1443. self.facing = 1
  1444. self.view = None
  1445. super().__init__(
  1446. x, y, z=z, bbox_x=bbox_x, bbox_y=bbox_y, bbox_width=bbox_width,
  1447. bbox_height=bbox_height, regulate_origin=regulate_origin, **kwargs)
  1448. def refresh_input(self):
  1449. if self.human:
  1450. key_controls = [left_key, right_key, up_key, down_key, jump_key,
  1451. action_key, sneak_key]
  1452. js_controls = [left_js, right_js, up_js, down_js, jump_js,
  1453. action_js, sneak_js]
  1454. states = [0 for i in key_controls]
  1455. for i in range(len(key_controls)):
  1456. for choice in key_controls[i][self.player]:
  1457. value = sge.keyboard.get_pressed(choice)
  1458. states[i] = max(states[i], value)
  1459. for i in range(len(js_controls)):
  1460. for choice in js_controls[i][self.player]:
  1461. j, t, c = choice
  1462. value = min(sge.joystick.get_value(j, t, c), 1)
  1463. if value >= joystick_threshold:
  1464. states[i] = max(states[i], value)
  1465. self.left_pressed = states[0]
  1466. self.right_pressed = states[1]
  1467. self.up_pressed = states[2]
  1468. self.down_pressed = states[3]
  1469. self.jump_pressed = states[4]
  1470. self.action_pressed = states[5]
  1471. self.sneak_pressed = states[6]
  1472. def jump(self):
  1473. if not self.warping and (self.on_floor or self.was_on_floor):
  1474. for thin_ice in self.collision(ThinIce, y=(self.y + 1)):
  1475. thin_ice.crack()
  1476. thin_ice.crack()
  1477. if abs(self.xvelocity) >= self.run_speed:
  1478. self.yvelocity = get_jump_speed(self.run_jump_height,
  1479. self.gravity)
  1480. else:
  1481. self.yvelocity = get_jump_speed(self.jump_height, self.gravity)
  1482. self.on_floor = []
  1483. self.was_on_floor = []
  1484. play_sound(jump_sound, self.x, self.y)
  1485. def jump_release(self):
  1486. if self.yvelocity < 0:
  1487. self.yvelocity /= 2
  1488. def action(self):
  1489. if not self.warping and self.held_object is not None:
  1490. if self.up_pressed:
  1491. self.held_object.kick_up()
  1492. elif self.down_pressed:
  1493. self.held_object.drop()
  1494. else:
  1495. self.held_object.kick()
  1496. def press_up(self):
  1497. if self.on_floor and self.was_on_floor:
  1498. for door in sorted(self.collision(Door),
  1499. key=lambda o, x=self.x: -abs(x - o.x)):
  1500. if self.y == door.y and abs(self.x - door.x) <= WARP_LAX:
  1501. self.move_x(door.x - self.x)
  1502. if abs(self.x - door.x) < 1:
  1503. self.x = door.x
  1504. door.warp(self)
  1505. break
  1506. def stomp_jump(self, other, jump_height=None):
  1507. if jump_height is None:
  1508. jump_height = self.jump_height
  1509. if self.jump_pressed:
  1510. self.yvelocity = get_jump_speed(jump_height, self.gravity)
  1511. else:
  1512. self.yvelocity = get_jump_speed(self.stomp_height, self.gravity)
  1513. T = math.floor(other.bbox_top / TILE_SIZE) * TILE_SIZE
  1514. self.move_y(T - self.bbox_bottom)
  1515. def hurt(self):
  1516. if not GOD and not self.hitstun and not sge.game.current_room.won:
  1517. self.hp -= 1
  1518. if self.hp <= 0:
  1519. self.kill()
  1520. else:
  1521. play_sound(hurt_sound, self.x, self.y)
  1522. self.hitstun = True
  1523. self.image_alpha = 128
  1524. self.alarms["hitstun"] = self.hitstun_time
  1525. def kill(self, show_fall=True):
  1526. if GOD:
  1527. self.yvelocity = get_jump_speed(SCREEN_SIZE[1], self.gravity)
  1528. play_sound(hurt_sound, self.x, self.y)
  1529. else:
  1530. if self.held_object is not None:
  1531. self.held_object.drop()
  1532. play_sound(kill_sound, self.x, self.y)
  1533. if show_fall:
  1534. DeadMan.create(self.x, self.y, 100000, sprite=tux_die_sprite,
  1535. yvelocity=get_jump_speed(PLAYER_DIE_HEIGHT))
  1536. if self.lose_on_death and not sge.game.current_room.won:
  1537. sge.game.current_room.die()
  1538. self.destroy()
  1539. def pickup(self, other):
  1540. if self.held_object is None and other.parent is None:
  1541. other.visible = False
  1542. self.held_object = other
  1543. other.parent = self
  1544. return True
  1545. else:
  1546. return False
  1547. def drop_object(self):
  1548. if self.held_object is not None:
  1549. self.held_object.visible = True
  1550. self.held_object = None
  1551. def do_kick(self):
  1552. play_sound(kick_sound, self.x, self.y)
  1553. self.alarms["fixed_sprite"] = TUX_KICK_TIME
  1554. if self.held_object is not None:
  1555. self.sprite = self.get_grab_sprite(tux_body_kick_sprite)
  1556. else:
  1557. self.sprite = tux_kick_sprite
  1558. def kick_object(self):
  1559. self.drop_object()
  1560. self.do_kick()
  1561. def show_hud(self):
  1562. if not NO_HUD:
  1563. y = 0
  1564. sge.game.project_text(font, self.name, 0, y,
  1565. color=sge.gfx.Color("white"),
  1566. outline=sge.gfx.Color("black"),
  1567. outline_thickness=text_outline_thickness)
  1568. y += font.size * 2
  1569. if not GOD:
  1570. x = 0
  1571. for i in range(self.max_hp):
  1572. if self.hp >= i + 1:
  1573. sge.game.project_sprite(heart_full_sprite, 0, x, y)
  1574. else:
  1575. sge.game.project_sprite(heart_empty_sprite, 0, x, y)
  1576. x += heart_empty_sprite.width
  1577. y += 18
  1578. sge.game.project_sprite(coin_icon_sprite,
  1579. coin_animation.image_index, 0, y)
  1580. sge.game.project_text(font, f"{self.coins}", 16, y,
  1581. color=sge.gfx.Color("white"),
  1582. outline=sge.gfx.Color("black"),
  1583. outline_thickness=text_outline_thickness)
  1584. if not self.human:
  1585. room = sge.game.current_room
  1586. if (room.timeline_skip_target is not None and
  1587. room.timeline_step < room.timeline_skip_target):
  1588. room.status_text = _("Press the Menu button to skip...")
  1589. else:
  1590. room.status_text = _("Cinematic mode enabled")
  1591. def get_grab_sprite(self, body_sprite, arms_sprite=None):
  1592. if arms_sprite is None: arms_sprite = tux_arms_grab_sprite
  1593. if self.held_object is not None:
  1594. obj_sprite = self.held_object.sprite
  1595. obj_image_index = self.held_object.image_index
  1596. obj_image_xscale = self.held_object.image_xscale
  1597. obj_image_yscale = self.held_object.image_yscale
  1598. i = (body_sprite, obj_sprite, obj_image_index,
  1599. obj_image_xscale, obj_image_yscale)
  1600. if i in tux_grab_sprites:
  1601. return tux_grab_sprites[i]
  1602. else:
  1603. if abs(obj_image_xscale) != 1 or abs(obj_image_yscale) != 1:
  1604. obj_sprite = obj_sprite.copy()
  1605. obj_sprite.width *= abs(obj_image_xscale)
  1606. obj_sprite.height *= abs(obj_image_yscale)
  1607. origin_x = body_sprite.origin_x
  1608. origin_y = body_sprite.origin_y
  1609. width = body_sprite.width
  1610. height = body_sprite.height
  1611. left = body_sprite.origin_x + self.carry_x
  1612. if left < 0:
  1613. origin_x -= left
  1614. width -= left
  1615. width = max(width, left + obj_sprite.width)
  1616. top = (body_sprite.origin_y + self.carry_y -
  1617. obj_sprite.origin_y - obj_sprite.height)
  1618. if top < 0:
  1619. origin_y -= top
  1620. height -= top
  1621. height = max(height, top + obj_sprite.height)
  1622. grab_sprite = sge.gfx.Sprite(
  1623. width=width, height=height, origin_x=origin_x,
  1624. origin_y=origin_y)
  1625. for j in range(1, body_sprite.frames):
  1626. grab_sprite.append_frame()
  1627. grab_sprite.draw_lock()
  1628. for j in range(grab_sprite.frames):
  1629. x = origin_x + obj_sprite.origin_x
  1630. y = (origin_y + self.carry_y + obj_sprite.origin_y -
  1631. obj_sprite.height)
  1632. grab_sprite.draw_sprite(obj_sprite, obj_image_index, x, y,
  1633. j)
  1634. grab_sprite.draw_sprite(body_sprite, j, origin_x, origin_y,
  1635. j)
  1636. grab_sprite.draw_sprite(arms_sprite, j, origin_x, origin_y,
  1637. j)
  1638. grab_sprite.draw_unlock()
  1639. tux_grab_sprites[i] = grab_sprite
  1640. return grab_sprite
  1641. else:
  1642. i = id(body_sprite)
  1643. if i in tux_grab_sprites:
  1644. return tux_grab_sprites[i]
  1645. else:
  1646. grab_sprite = body_sprite.copy()
  1647. grab_sprite.draw_lock()
  1648. for j in range(grab_sprite.frames):
  1649. grab_sprite.draw_sprite(arms_sprite, j,
  1650. grab_sprite.origin_x,
  1651. grab_sprite.origin_y, j)
  1652. grab_sprite.draw_unlock()
  1653. tux_grab_sprites[i] = grab_sprite
  1654. return grab_sprite
  1655. def set_image(self):
  1656. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1657. hands_free = (self.held_object is None)
  1658. if self.on_floor and self.was_on_floor:
  1659. xm = (self.xvelocity > 0) - (self.xvelocity < 0)
  1660. speed = abs(self.xvelocity)
  1661. if speed > 0:
  1662. if xm != self.facing:
  1663. skidding = skid_sound.playing
  1664. if (not skidding and h_control and
  1665. speed >= PLAYER_SKID_THRESHOLD):
  1666. skidding = True
  1667. play_sound(skid_sound, self.x, self.y)
  1668. else:
  1669. skidding = False
  1670. if skidding:
  1671. if hands_free:
  1672. self.sprite = tux_skid_sprite
  1673. else:
  1674. self.sprite = self.get_grab_sprite(
  1675. tux_body_skid_sprite, tux_arms_skid_grab_sprite)
  1676. else:
  1677. if (xm != self.facing or
  1678. abs(self.xvelocity) < self.run_speed):
  1679. if hands_free:
  1680. self.sprite = tux_walk_sprite
  1681. else:
  1682. self.sprite = self.get_grab_sprite(
  1683. tux_body_walk_sprite)
  1684. self.image_speed = speed * PLAYER_WALK_FRAMES_PER_PIXEL
  1685. if xm != self.facing:
  1686. self.image_speed *= -1
  1687. else:
  1688. if hands_free:
  1689. self.sprite = tux_run_sprite
  1690. else:
  1691. self.sprite = self.get_grab_sprite(
  1692. tux_body_run_sprite)
  1693. self.image_speed = speed * PLAYER_RUN_FRAMES_PER_PIXEL
  1694. else:
  1695. if hands_free:
  1696. self.sprite = tux_stand_sprite
  1697. else:
  1698. self.sprite = self.get_grab_sprite(
  1699. tux_body_stand_sprite)
  1700. else:
  1701. if self.yvelocity < 0:
  1702. if hands_free:
  1703. self.sprite = tux_jump_sprite
  1704. else:
  1705. self.sprite = self.get_grab_sprite(tux_body_jump_sprite)
  1706. else:
  1707. if hands_free:
  1708. self.sprite = tux_fall_sprite
  1709. else:
  1710. self.sprite = self.get_grab_sprite(tux_body_fall_sprite)
  1711. def set_warp_image(self):
  1712. hands_free = (self.held_object is None)
  1713. if abs(self.xvelocity) >= WARP_EDGE_SPEED / 2:
  1714. if hands_free:
  1715. self.sprite = tux_walk_sprite
  1716. else:
  1717. self.sprite = self.get_grab_sprite(tux_body_walk_sprite)
  1718. self.image_speed = WARP_EDGE_SPEED * PLAYER_WALK_FRAMES_PER_PIXEL
  1719. if self.xvelocity > 0:
  1720. self.image_xscale = abs(self.image_xscale)
  1721. else:
  1722. self.image_xscale = -abs(self.image_xscale)
  1723. else:
  1724. if self.on_floor and self.was_on_floor and abs(self.yvelocity) < 1:
  1725. if hands_free:
  1726. self.sprite = tux_stand_sprite
  1727. else:
  1728. self.sprite = self.get_grab_sprite(tux_body_stand_sprite)
  1729. else:
  1730. if hands_free:
  1731. self.sprite = tux_jump_sprite
  1732. else:
  1733. self.sprite = self.get_grab_sprite(tux_body_jump_sprite)
  1734. def project_light(self):
  1735. if GOD:
  1736. xsge_lighting.project_light(self.x, self.y, light_sprite)
  1737. def event_create(self):
  1738. sge.game.current_room.add_timeline_object(self)
  1739. self.last_x = self.x
  1740. self.last_y = self.y
  1741. self.on_slope = self.get_bottom_touching_slope()
  1742. self.on_floor = self.get_bottom_touching_wall() + self.on_slope
  1743. self.was_on_floor = self.on_floor
  1744. self.off_floor_counter = 0
  1745. self.view = sge.game.current_room.views[self.player]
  1746. self.view.x = self.x - self.view.width / 2
  1747. self.view.y = self.y - self.view.height + CAMERA_TARGET_MARGIN_BOTTOM
  1748. def event_update_position(self, delta_mult):
  1749. super().event_update_position(delta_mult)
  1750. held_object = self.held_object
  1751. if not self.warping and held_object is not None:
  1752. target_x = self.x + held_object.sprite.origin_x + self.carry_x
  1753. h = held_object.sprite.height * abs(held_object.image_yscale)
  1754. target_y = self.y + held_object.sprite.origin_y - h + self.carry_y
  1755. if self.image_xscale < 0:
  1756. target_x -= (held_object.sprite.width *
  1757. abs(held_object.image_xscale))
  1758. target_x -= 2 * self.carry_x
  1759. if isinstance(held_object, xsge_physics.Collider):
  1760. held_object.move_x(target_x - held_object.x)
  1761. held_object.move_y(target_y - held_object.y)
  1762. else:
  1763. held_object.x = target_x
  1764. held_object.y = target_y
  1765. held_object.image_xscale = math.copysign(held_object.image_xscale,
  1766. self.image_xscale)
  1767. held_object.image_yscale = math.copysign(held_object.image_yscale,
  1768. self.image_yscale)
  1769. def event_begin_step(self, time_passed, delta_mult):
  1770. if not self.warping:
  1771. self.refresh_input()
  1772. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1773. current_h_movement = (self.xvelocity > 0) - (self.xvelocity < 0)
  1774. self.xacceleration = 0
  1775. self.yacceleration = 0
  1776. self.xdeceleration = 0
  1777. if abs(self.xvelocity) >= self.max_speed:
  1778. self.xvelocity = self.max_speed * current_h_movement
  1779. if h_control:
  1780. self.facing = h_control
  1781. self.image_xscale = h_control * abs(self.image_xscale)
  1782. h_factor = abs(self.right_pressed - self.left_pressed)
  1783. target_speed = min(h_factor * self.max_speed, self.max_speed)
  1784. if self.sneak_pressed:
  1785. target_speed = min(target_speed, self.walk_speed)
  1786. if (abs(self.xvelocity) < target_speed
  1787. or h_control != current_h_movement):
  1788. if self.on_floor or self.was_on_floor:
  1789. self.xacceleration = self.acceleration * h_control
  1790. else:
  1791. self.xacceleration = self.air_acceleration * h_control
  1792. else:
  1793. if self.on_floor or self.was_on_floor:
  1794. dc = self.friction
  1795. else:
  1796. dc = self.air_friction
  1797. if abs(self.xvelocity) - dc*delta_mult > target_speed:
  1798. self.xdeceleration = dc
  1799. else:
  1800. self.xvelocity = target_speed * current_h_movement
  1801. if current_h_movement and h_control != current_h_movement:
  1802. if self.on_floor or self.was_on_floor:
  1803. self.xdeceleration = self.friction
  1804. else:
  1805. self.xdeceleration = self.air_friction
  1806. if not self.on_floor and not self.was_on_floor:
  1807. if self.yvelocity < self.fall_speed:
  1808. self.yacceleration = self.gravity
  1809. else:
  1810. self.yvelocity = self.fall_speed
  1811. elif self.on_slope:
  1812. self.yvelocity = (self.slide_speed
  1813. * (self.on_slope[0].bbox_height
  1814. / self.on_slope[0].bbox_width))
  1815. def event_step(self, time_passed, delta_mult):
  1816. if self.warping:
  1817. self.event_step_warp(time_passed, delta_mult)
  1818. else:
  1819. self.event_step_normal(time_passed, delta_mult)
  1820. # Move view
  1821. if not self.view_frozen:
  1822. view_target_x = (self.x - self.view.width/2
  1823. + self.xvelocity*CAMERA_OFFSET_FACTOR)
  1824. if abs(view_target_x - self.view.x) > 0.5:
  1825. camera_xvel = ((view_target_x - self.view.x)
  1826. * CAMERA_HSPEED_FACTOR * delta_mult)
  1827. if abs(self.xvelocity) > CAMERA_STOPPED_THRESHOLD:
  1828. self.view.x += camera_xvel
  1829. else:
  1830. xvel_max = CAMERA_STOPPED_HSPEED_MAX * delta_mult
  1831. self.view.x += max(-xvel_max, min(camera_xvel, xvel_max))
  1832. else:
  1833. self.view.x = view_target_x
  1834. view_min_y = self.y - self.view.height + CAMERA_MARGIN_BOTTOM
  1835. view_max_y = self.y - CAMERA_MARGIN_TOP
  1836. if self.warping or (self.on_floor and self.was_on_floor):
  1837. view_target_y = (self.y - self.view.height
  1838. + CAMERA_TARGET_MARGIN_BOTTOM)
  1839. if abs(view_target_y - self.view.y) > 0.5:
  1840. self.view.y += ((view_target_y - self.view.y)
  1841. * CAMERA_VSPEED_FACTOR * delta_mult)
  1842. else:
  1843. self.view.y = view_target_y
  1844. if self.view.y < view_min_y:
  1845. self.view.y = view_min_y
  1846. elif self.view.y > view_max_y:
  1847. self.view.y = view_max_y
  1848. self.last_x = self.x
  1849. self.last_y = self.y
  1850. if self.bbox_bottom <= self.view.y:
  1851. sge.game.current_room.project_sprite(tux_offscreen_sprite, 0,
  1852. self.x, 0, self.z)
  1853. while self.coins >= HEAL_COINS:
  1854. self.coins -= HEAL_COINS
  1855. play_sound(heal_sound)
  1856. if self.hp < self.max_hp:
  1857. self.hp += 1
  1858. else:
  1859. sge.game.current_room.add_points(HP_POINTS)
  1860. self.show_hud()
  1861. def event_step_normal(self, time_passed, delta_mult):
  1862. on_floor = self.get_bottom_touching_wall()
  1863. self.on_slope = self.get_bottom_touching_slope() if not on_floor else []
  1864. if self.on_floor:
  1865. self.was_on_floor = self.on_floor
  1866. self.off_floor_counter = 0
  1867. else:
  1868. self.off_floor_counter += delta_mult
  1869. if self.off_floor_counter >= PLAYER_OFF_FLOOR_THRESHOLD:
  1870. self.was_on_floor = []
  1871. self.off_floor_counter = 0
  1872. self.on_floor = on_floor + self.on_slope
  1873. h_control = bool(self.right_pressed) - bool(self.left_pressed)
  1874. v_control = bool(self.down_pressed) - bool(self.up_pressed)
  1875. for block in self.on_floor:
  1876. if block in self.was_on_floor and isinstance(block, HurtTop):
  1877. self.hurt()
  1878. # Set image
  1879. if "fixed_sprite" not in self.alarms:
  1880. self.set_image()
  1881. # Enter warp pipes
  1882. if h_control > 0 and self.xvelocity >= 0:
  1883. for warp in sge.game.current_room.warps:
  1884. if (warp.direction == "right" and self.bbox_right == warp.x
  1885. and abs(self.y - warp.y) < WARP_LAX):
  1886. self.y = warp.y
  1887. warp.warp(self)
  1888. elif h_control < 0 and self.xvelocity <= 0:
  1889. for warp in sge.game.current_room.warps:
  1890. if (warp.direction == "left" and self.bbox_left == warp.x
  1891. and abs(self.y - warp.y) < WARP_LAX):
  1892. self.y = warp.y
  1893. warp.warp(self)
  1894. if v_control > 0 and self.yvelocity >= 0:
  1895. for warp in sge.game.current_room.warps:
  1896. if (warp.direction == "down" and self.bbox_bottom == warp.y
  1897. and abs(self.x - warp.x) < WARP_LAX):
  1898. self.x = warp.x
  1899. warp.warp(self)
  1900. elif v_control < 0 and self.yvelocity <= 0:
  1901. for warp in sge.game.current_room.warps:
  1902. if (warp.direction == "up" and self.bbox_top == warp.y
  1903. and abs(self.x - warp.x) < WARP_LAX):
  1904. self.x = warp.x
  1905. warp.warp(self)
  1906. # Prevent moving off-screen to the right or left
  1907. if self.view_is_barrier:
  1908. if self.bbox_left < self.view.x:
  1909. self.move_x(self.view.x - self.bbox_left, True)
  1910. self.bbox_left = self.view.x
  1911. elif self.bbox_right > self.view.x + self.view.width:
  1912. self.move_x(self.view.x + self.view.width - self.bbox_right,
  1913. True)
  1914. self.bbox_right = self.view.x + self.view.width
  1915. # Off-screen death
  1916. if (not sge.game.current_room.won
  1917. and self.bbox_top > self.view.y + self.view.height + DEATHZONE):
  1918. self.kill(False)
  1919. def event_step_warp(self, time_passed, delta_mult):
  1920. self.set_warp_image()
  1921. def event_paused_step(self, time_passed, delta_mult):
  1922. self.show_hud()
  1923. def event_alarm(self, alarm_id):
  1924. if alarm_id == "hitstun":
  1925. self.hitstun = False
  1926. self.image_alpha = 255
  1927. def event_key_press(self, key, char):
  1928. if self.human:
  1929. if key in jump_key[self.player]:
  1930. self.jump()
  1931. if key in action_key[self.player]:
  1932. self.action()
  1933. if key in up_key[self.player]:
  1934. self.press_up()
  1935. if not isinstance(sge.game.current_room, SpecialScreen):
  1936. if (key == "escape" or key in pause_key[self.player]
  1937. or key in menu_key[self.player]):
  1938. sge.game.current_room.pause()
  1939. def event_key_release(self, key):
  1940. if self.human:
  1941. if key in jump_key[self.player]:
  1942. self.jump_release()
  1943. def event_joystick(self, js_name, js_id, input_type, input_id, value):
  1944. if self.human:
  1945. js = (js_id, input_type, input_id)
  1946. if value >= joystick_threshold:
  1947. if js in jump_js[self.player]:
  1948. self.jump()
  1949. if js in action_js[self.player]:
  1950. self.action()
  1951. if js in up_js[self.player]:
  1952. self.press_up()
  1953. if js in pause_js[self.player] or js in menu_js[self.player]:
  1954. sge.game.current_room.pause()
  1955. else:
  1956. if js in jump_js[self.player]:
  1957. self.jump_release()
  1958. def event_collision(self, other, xdirection, ydirection):
  1959. if isinstance(other, Death):
  1960. self.kill()
  1961. elif isinstance(other, LevelEnd):
  1962. sge.game.current_room.win_level()
  1963. other.destroy()
  1964. elif isinstance(other, Explosion):
  1965. other.touch(self)
  1966. elif isinstance(other, InteractiveObject):
  1967. if (ydirection == 1
  1968. or (xdirection and not ydirection
  1969. and self.bbox_bottom - other.bbox_top <= STOMP_LAX)):
  1970. other.stomp(self)
  1971. # This check is necessary to allow the player to drop held
  1972. # objects. It also has a nice side-effect of preventing the
  1973. # player from being hurt by the same object more than once
  1974. # until the collision stops.
  1975. elif xdirection or ydirection:
  1976. other.touch(self)
  1977. elif isinstance(other, HiddenItemBlock):
  1978. if ydirection == -1 and not xdirection:
  1979. move_loss = max(0, other.bbox_bottom - self.bbox_top)
  1980. self.move_y(move_loss, absolute=True, do_events=False)
  1981. self.event_physics_collision_top(other, move_loss)
  1982. def event_physics_collision_left(self, other, move_loss):
  1983. for block in self.get_left_touching_wall():
  1984. if isinstance(block, HurtRight):
  1985. self.hurt()
  1986. elif isinstance(block, RockWall):
  1987. rock = block.parent()
  1988. if rock is not None:
  1989. rock.touch(self)
  1990. if isinstance(other, xsge_physics.SolidRight):
  1991. self.xvelocity = max(self.xvelocity, 0)
  1992. if self.left_pressed:
  1993. for warp in sge.game.current_room.warps:
  1994. if (warp.direction == "left" and self.bbox_left == warp.x
  1995. and abs(self.y - warp.y) < WARP_LAX):
  1996. warp.warp(self)
  1997. def event_physics_collision_right(self, other, move_loss):
  1998. for block in self.get_right_touching_wall():
  1999. if isinstance(block, HurtLeft):
  2000. self.hurt()
  2001. elif isinstance(block, RockWall):
  2002. rock = block.parent()
  2003. if rock is not None:
  2004. rock.touch(self)
  2005. if isinstance(other, xsge_physics.SolidLeft):
  2006. self.xvelocity = min(self.xvelocity, 0)
  2007. if self.right_pressed:
  2008. for warp in sge.game.current_room.warps:
  2009. if (warp.direction == "right" and self.bbox_right == warp.x
  2010. and abs(self.y - warp.y) < WARP_LAX):
  2011. warp.warp(self)
  2012. def event_physics_collision_top(self, other, move_loss):
  2013. top_touching = self.get_top_touching_wall()
  2014. for hblock in self.collision(HiddenItemBlock, y=(self.y - 1)):
  2015. if not self.collision(hblock):
  2016. hblock.hit(self)
  2017. tmv = 0
  2018. for i in range(CEILING_LAX):
  2019. if (not self.get_left_touching_wall()
  2020. and not self.get_left_touching_slope()):
  2021. self.x -= 1
  2022. tmv -= 1
  2023. if (not self.get_top_touching_wall()
  2024. and not self.get_top_touching_slope()):
  2025. self.move_y(-move_loss)
  2026. break
  2027. else:
  2028. self.x -= tmv
  2029. tmv = 0
  2030. for i in range(CEILING_LAX):
  2031. if (not self.get_left_touching_wall()
  2032. and not self.get_left_touching_slope()):
  2033. self.x += 1
  2034. tmv += 1
  2035. if (not self.get_top_touching_wall()
  2036. and not self.get_top_touching_slope()):
  2037. self.move_y(-move_loss)
  2038. break
  2039. else:
  2040. self.x -= tmv
  2041. tmv = 0
  2042. self.yvelocity = max(self.yvelocity, 0)
  2043. for block in top_touching:
  2044. if isinstance(block, HittableBlock):
  2045. block.hit(self)
  2046. elif isinstance(block, HurtBottom):
  2047. self.hurt()
  2048. elif isinstance(block, RockWall):
  2049. rock = block.parent()
  2050. if rock is not None:
  2051. rock.touch(self)
  2052. if self.up_pressed:
  2053. for warp in sge.game.current_room.warps:
  2054. if (warp.direction == "up" and self.bbox_top == warp.y
  2055. and abs(self.x - warp.x) < WARP_LAX):
  2056. warp.warp(self)
  2057. break
  2058. def event_physics_collision_bottom(self, other, move_loss):
  2059. for block in self.get_bottom_touching_wall():
  2060. if isinstance(block, HurtTop):
  2061. self.hurt()
  2062. if isinstance(other, xsge_physics.SolidTop):
  2063. self.yvelocity = min(self.yvelocity, 0)
  2064. elif isinstance(other, (xsge_physics.SlopeTopLeft,
  2065. xsge_physics.SlopeTopRight)):
  2066. self.yvelocity = min(self.slide_speed * (other.bbox_height /
  2067. other.bbox_width),
  2068. self.yvelocity)
  2069. if self.down_pressed:
  2070. for warp in sge.game.current_room.warps:
  2071. if (warp.direction == "down" and self.bbox_bottom == warp.y
  2072. and abs(self.x - warp.x) < WARP_LAX):
  2073. warp.warp(self)
  2074. class DeadMan(sge.dsp.Object):
  2075. """Object which falls off the screen, then gets destroyed."""
  2076. gravity = GRAVITY
  2077. fall_speed = PLAYER_DIE_FALL_SPEED
  2078. def event_begin_step(self, time_passed, delta_mult):
  2079. if self.yvelocity < self.fall_speed:
  2080. self.yacceleration = self.gravity
  2081. else:
  2082. self.yvelocity = self.fall_speed
  2083. self.yacceleration = 0
  2084. def event_step(self, time_passed, delta_mult):
  2085. if self.y - self.image_origin_y > sge.game.current_room.height:
  2086. self.destroy()
  2087. class Corpse(xsge_physics.Collider):
  2088. """Like DeadMan, but just falls to the floor, not off-screen."""
  2089. gravity = GRAVITY
  2090. fall_speed = ENEMY_FALL_SPEED
  2091. def event_create(self):
  2092. self.alarms["die"] = 90
  2093. def event_begin_step(self, time_passed, delta_mult):
  2094. if self.get_bottom_touching_wall() or self.get_bottom_touching_slope():
  2095. self.yvelocity = 0
  2096. else:
  2097. if self.yvelocity < self.fall_speed:
  2098. self.yacceleration = self.gravity
  2099. else:
  2100. self.yvelocity = min(self.yvelocity, self.fall_speed)
  2101. self.yacceleration = 0
  2102. def event_alarm(self, alarm_id):
  2103. if alarm_id == "die":
  2104. self.destroy()
  2105. class Smoke(sge.dsp.Object):
  2106. def event_animation_end(self):
  2107. self.destroy()
  2108. class InteractiveObject(sge.dsp.Object):
  2109. active_range = ENEMY_ACTIVE_RANGE
  2110. killed_by_void = True
  2111. always_active = False
  2112. never_active = False
  2113. always_tangible = False
  2114. never_tangible = False
  2115. knockable = False
  2116. burnable = False
  2117. freezable = False
  2118. blastable = False
  2119. activated = False
  2120. parent = None
  2121. warping = False
  2122. def activate(self):
  2123. self.activated = True
  2124. if not self.never_tangible:
  2125. self.tangible = True
  2126. if not self.never_active:
  2127. self.active = True
  2128. def deactivate(self):
  2129. self.activated = False
  2130. if not self.always_active:
  2131. self.active = False
  2132. if not self.always_tangible:
  2133. self.tangible = False
  2134. def update_active(self):
  2135. if not self.warping:
  2136. for view in sge.game.current_room.views:
  2137. if (self.bbox_left <= (view.x + view.width + self.active_range)
  2138. and self.bbox_right >= view.x - self.active_range
  2139. and self.bbox_top <= (view.y + view.height
  2140. + self.active_range)
  2141. and self.bbox_bottom >= view.y - self.active_range):
  2142. if not self.activated:
  2143. self.activate()
  2144. break
  2145. else:
  2146. if self.activated:
  2147. self.deactivate()
  2148. void_y = sge.game.current_room.height + self.active_range
  2149. if self.killed_by_void and self.bbox_top > void_y:
  2150. self.destroy()
  2151. def get_nearest_player(self):
  2152. player = None
  2153. dist = 0
  2154. for obj in sge.game.current_room.objects:
  2155. if isinstance(obj, Player):
  2156. ndist = math.hypot(self.x - obj.x, self.y - obj.y)
  2157. if player is None or ndist < dist:
  2158. player = obj
  2159. dist = ndist
  2160. return player
  2161. def set_direction(self, direction):
  2162. self.image_xscale = abs(self.image_xscale) * direction
  2163. def move(self):
  2164. pass
  2165. def touch(self, other):
  2166. pass
  2167. def stomp(self, other):
  2168. self.touch(other)
  2169. def knock(self, other=None):
  2170. pass
  2171. def burn(self):
  2172. pass
  2173. def freeze(self):
  2174. pass
  2175. def blast(self):
  2176. self.burn()
  2177. def kick(self):
  2178. self.drop()
  2179. def drop(self):
  2180. if self.parent is not None:
  2181. self.parent.drop_object()
  2182. self.parent = None
  2183. def kick_up(self):
  2184. self.kick()
  2185. def touch_death(self):
  2186. if self.parent is None:
  2187. play_sound(fall_sound, self.x, self.y)
  2188. DeadMan.create(self.x, self.y, self.z, sprite=self.sprite,
  2189. xvelocity=self.xvelocity, yvelocity=0,
  2190. image_xscale=self.image_xscale,
  2191. image_yscale=-abs(self.image_yscale))
  2192. self.destroy()
  2193. def project_light(self):
  2194. pass
  2195. def event_create(self):
  2196. InteractiveObject.deactivate(self)
  2197. def event_begin_step(self, time_passed, delta_mult):
  2198. if not self.warping:
  2199. self.move()
  2200. elif self.xvelocity:
  2201. self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
  2202. def event_step(self, time_passed, delta_mult):
  2203. self.update_active()
  2204. def event_collision(self, other, xdirection, ydirection):
  2205. if isinstance(other, Death):
  2206. self.touch_death()
  2207. def event_destroy(self):
  2208. if self.parent is not None:
  2209. self.parent.drop_object()
  2210. self.parent = None
  2211. class InteractiveCollider(InteractiveObject, xsge_physics.Collider):
  2212. def deactivate(self):
  2213. tangible_anyway = False
  2214. if not self.never_tangible:
  2215. for other in self.get_bottom_touching_wall():
  2216. if isinstance(other, xsge_physics.MobileWall):
  2217. tangible_anyway = True
  2218. break
  2219. super().deactivate()
  2220. if tangible_anyway:
  2221. self.tangible = True
  2222. def stop_left(self):
  2223. self.xvelocity = 0
  2224. def stop_right(self):
  2225. self.xvelocity = 0
  2226. def stop_up(self):
  2227. self.yvelocity = 0
  2228. def stop_down(self):
  2229. self.yvelocity = 0
  2230. def touch_hurt(self):
  2231. self.touch_death()
  2232. def event_physics_collision_left(self, other, move_loss):
  2233. if isinstance(other, HurtRight):
  2234. self.touch_hurt()
  2235. if isinstance(other, xsge_physics.SolidRight):
  2236. self.stop_left()
  2237. elif isinstance(other, xsge_physics.SlopeTopRight):
  2238. if self.yvelocity > 0:
  2239. self.stop_down()
  2240. elif isinstance(other, xsge_physics.SlopeBottomRight):
  2241. if self.yvelocity < 0:
  2242. self.stop_up()
  2243. def event_physics_collision_right(self, other, move_loss):
  2244. if isinstance(other, HurtLeft):
  2245. self.touch_hurt()
  2246. if isinstance(other, xsge_physics.SolidLeft):
  2247. self.stop_right()
  2248. elif isinstance(other, xsge_physics.SlopeTopLeft):
  2249. if self.yvelocity > 0:
  2250. self.stop_down()
  2251. elif isinstance(other, xsge_physics.SlopeBottomLeft):
  2252. if self.yvelocity < 0:
  2253. self.stop_up()
  2254. def event_physics_collision_top(self, other, move_loss):
  2255. if isinstance(other, HurtBottom):
  2256. self.touch_hurt()
  2257. if isinstance(other, (xsge_physics.SolidBottom,
  2258. xsge_physics.SlopeBottomLeft,
  2259. xsge_physics.SlopeBottomRight)):
  2260. self.stop_up()
  2261. def event_physics_collision_bottom(self, other, move_loss):
  2262. if isinstance(other, HurtTop):
  2263. self.touch_hurt()
  2264. if isinstance(other, (xsge_physics.SolidTop, xsge_physics.SlopeTopLeft,
  2265. xsge_physics.SlopeTopRight)):
  2266. self.stop_down()
  2267. class WinPuffObject(InteractiveObject):
  2268. win_puff_score = ENEMY_KILL_POINTS
  2269. def win_puff(self):
  2270. play_sound(pop_sound, self.x, self.y)
  2271. if self.sprite is None:
  2272. x = self.x
  2273. y = self.y
  2274. else:
  2275. x = self.x - self.image_origin_x + self.sprite.width/2
  2276. y = self.y - self.image_origin_y + self.sprite.height/2
  2277. Smoke.create(x, y, self.z, sprite=smoke_plume_sprite)
  2278. self.destroy()
  2279. sge.game.current_room.add_points(self.win_puff_score)
  2280. class FallingObject(InteractiveCollider):
  2281. """
  2282. Falls based on gravity. If on a slope, falls at a constant speed
  2283. based on the steepness of the slope.
  2284. """
  2285. gravity = GRAVITY
  2286. fall_speed = ENEMY_FALL_SPEED
  2287. slide_speed = ENEMY_SLIDE_SPEED
  2288. was_on_floor = False
  2289. def move(self):
  2290. on_floor = self.get_bottom_touching_wall()
  2291. on_slope = self.get_bottom_touching_slope()
  2292. if self.was_on_floor and (on_floor or on_slope) and self.yvelocity >= 0:
  2293. self.yacceleration = 0
  2294. if on_floor:
  2295. if self.yvelocity > 0:
  2296. self.yvelocity = 0
  2297. self.stop_down()
  2298. elif on_slope:
  2299. self.yvelocity = self.slide_speed * (on_slope[0].bbox_height
  2300. / on_slope[0].bbox_width)
  2301. else:
  2302. if self.yvelocity < self.fall_speed:
  2303. self.yacceleration = self.gravity
  2304. else:
  2305. self.yvelocity = self.fall_speed
  2306. self.yacceleration = 0
  2307. self.was_on_floor = on_floor or on_slope
  2308. class WalkingObject(FallingObject):
  2309. """
  2310. Walks toward the player. Turns around at walls, and can also be set
  2311. to turn around at ledges with the stayonplatform attribute.
  2312. """
  2313. walk_speed = ENEMY_WALK_SPEED
  2314. stayonplatform = False
  2315. def deactivate(self):
  2316. super().deactivate()
  2317. self.xvelocity = 0
  2318. def set_direction(self, direction):
  2319. self.xvelocity = self.walk_speed * direction
  2320. self.image_xscale = abs(self.image_xscale) * direction
  2321. def move(self):
  2322. super().move()
  2323. if not self.xvelocity:
  2324. player = self.get_nearest_player()
  2325. if player is not None:
  2326. self.set_direction(1 if self.x < player.x else -1)
  2327. else:
  2328. self.set_direction(-1)
  2329. on_floor = self.get_bottom_touching_wall()
  2330. on_slope = self.get_bottom_touching_slope()
  2331. if (on_floor or on_slope) and self.stayonplatform:
  2332. if self.xvelocity < 0:
  2333. for tile in on_floor:
  2334. if tile.bbox_left < self.x:
  2335. break
  2336. else:
  2337. if not on_slope:
  2338. self.set_direction(1)
  2339. else:
  2340. for tile in on_floor:
  2341. if tile.bbox_right > self.x:
  2342. break
  2343. else:
  2344. if not on_slope:
  2345. self.set_direction(-1)
  2346. def stop_left(self):
  2347. self.set_direction(1)
  2348. def stop_right(self):
  2349. self.set_direction(-1)
  2350. class CrowdBlockingObject(InteractiveObject):
  2351. """Blocks CrowdObject instances, causing them to turn around."""
  2352. pass
  2353. class CrowdObject(WalkingObject, CrowdBlockingObject):
  2354. """
  2355. Turns around when colliding with a CrowdBlockingObject. (Note: this
  2356. class is itself derived from CrowdBlockingObject.)
  2357. """
  2358. def event_collision(self, other, xdirection, ydirection):
  2359. if isinstance(other, CrowdBlockingObject):
  2360. if xdirection:
  2361. self.set_direction(-xdirection)
  2362. else:
  2363. if self.x > other.x:
  2364. self.set_direction(1)
  2365. elif self.x < other.x:
  2366. self.set_direction(-1)
  2367. elif id(self) > id(other):
  2368. self.set_direction(1)
  2369. else:
  2370. self.set_direction(-1)
  2371. else:
  2372. super().event_collision(other, xdirection, ydirection)
  2373. class KnockableObject(InteractiveObject):
  2374. """Provides basic knocking behavior."""
  2375. knockable = True
  2376. blastable = True
  2377. def knock(self, other=None):
  2378. play_sound(fall_sound, self.x, self.y)
  2379. DeadMan.create(self.x, self.y, self.z, sprite=self.sprite,
  2380. xvelocity=self.xvelocity,
  2381. yvelocity=get_jump_speed(ENEMY_HIT_BELOW_HEIGHT),
  2382. image_xscale=self.image_xscale,
  2383. image_yscale=-abs(self.image_yscale))
  2384. self.destroy()
  2385. class BurnableObject(InteractiveObject):
  2386. """Provides basic burn behavior."""
  2387. burnable = True
  2388. blastable = True
  2389. def burn(self):
  2390. play_sound(fall_sound, self.x, self.y)
  2391. DeadMan.create(self.x, self.y, self.z, sprite=self.sprite,
  2392. xvelocity=self.xvelocity, yvelocity=0,
  2393. image_xscale=self.image_xscale,
  2394. image_yscale=-abs(self.image_yscale))
  2395. self.destroy()
  2396. class FreezableObject(InteractiveObject):
  2397. """Provides basic freeze behavior."""
  2398. freezable = True
  2399. frozen_sprite = None
  2400. frozen_time = THAW_TIME_DEFAULT
  2401. frozen = False
  2402. def update_active(self):
  2403. if self.frozen:
  2404. self.active = False
  2405. else:
  2406. super().update_active()
  2407. def permafreeze(self):
  2408. prev_frozen_time = self.frozen_time
  2409. self.frozen_time = None
  2410. self.freeze()
  2411. self.frozen_time = prev_frozen_time
  2412. def freeze(self):
  2413. if self.frozen_sprite is None:
  2414. self.frozen_sprite = sge.gfx.Sprite(
  2415. width=self.sprite.width, height=self.sprite.height,
  2416. origin_x=self.sprite.origin_x, origin_y=self.sprite.origin_y,
  2417. fps=THAW_FPS, bbox_x=self.sprite.bbox_x,
  2418. bbox_y=self.sprite.bbox_y, bbox_width=self.sprite.bbox_width,
  2419. bbox_height=self.sprite.bbox_height)
  2420. self.frozen_sprite.append_frame()
  2421. self.frozen_sprite.draw_sprite(self.sprite, self.image_index,
  2422. self.sprite.origin_x,
  2423. self.sprite.origin_y)
  2424. colorizer = sge.gfx.Sprite(width=self.frozen_sprite.width,
  2425. height=self.frozen_sprite.height)
  2426. colorizer.draw_rectangle(0, 0, colorizer.width, colorizer.height,
  2427. fill=sge.gfx.Color((128, 128, 255)))
  2428. self.frozen_sprite.draw_sprite(colorizer, 0, 0, 0, frame=0,
  2429. blend_mode=sge.BLEND_RGB_MULTIPLY)
  2430. frozen_self = FrozenObject.create(self.x, self.y, self.z,
  2431. sprite=self.frozen_sprite,
  2432. image_fps=0,
  2433. image_xscale=self.image_xscale,
  2434. image_yscale=self.image_yscale)
  2435. frozen_self.unfrozen = self
  2436. self.frozen = True
  2437. self.tangible = False
  2438. self.active = False
  2439. self.visible = False
  2440. if self.frozen_time is not None:
  2441. frozen_self.alarms["thaw_warn"] = self.frozen_time
  2442. class FrozenObject(InteractiveObject, xsge_physics.Solid):
  2443. always_active = True
  2444. always_tangible = True
  2445. burnable = True
  2446. freezable = True
  2447. blastable = True
  2448. unfrozen = None
  2449. def thaw(self):
  2450. if self.unfrozen is not None:
  2451. self.unfrozen.frozen = False
  2452. self.unfrozen.tangible = True
  2453. self.unfrozen.visible = True
  2454. self.unfrozen.activate()
  2455. self.destroy()
  2456. def burn(self):
  2457. self.thaw()
  2458. play_sound(sizzle_sound, self.x, self.y)
  2459. def freeze(self):
  2460. if self.unfrozen is not None:
  2461. self.thaw()
  2462. self.unfrozen.freeze()
  2463. def event_alarm(self, alarm_id):
  2464. if self.unfrozen is not None:
  2465. if alarm_id == "thaw_warn":
  2466. self.image_fps = None
  2467. self.alarms["thaw"] = THAW_WARN_TIME
  2468. elif alarm_id == "thaw":
  2469. self.thaw()
  2470. class WalkingSnowball(CrowdObject, KnockableObject, BurnableObject,
  2471. WinPuffObject):
  2472. freezable = True
  2473. def __init__(self, x, y, z=0, **kwargs):
  2474. kwargs["sprite"] = snowball_walk_sprite
  2475. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2476. def touch(self, other):
  2477. other.hurt()
  2478. def stomp(self, other):
  2479. other.stomp_jump(self)
  2480. play_sound(squish_sound, self.x, self.y)
  2481. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2482. Corpse.create(self.x, self.y, self.z, sprite=snowball_squished_sprite,
  2483. image_xscale=self.image_xscale,
  2484. image_yscale=self.image_yscale)
  2485. self.destroy()
  2486. def knock(self, other=None):
  2487. super().knock(other)
  2488. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2489. def burn(self):
  2490. super().burn()
  2491. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2492. def freeze(self):
  2493. self.burn()
  2494. class BouncingSnowball(WalkingSnowball):
  2495. def __init__(self, x, y, z=0, **kwargs):
  2496. kwargs["sprite"] = bouncing_snowball_sprite
  2497. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2498. def stop_up(self):
  2499. self.yvelocity = 0
  2500. def stop_down(self):
  2501. self.yvelocity = get_jump_speed(SNOWBALL_BOUNCE_HEIGHT, self.gravity)
  2502. class Crystallo(CrowdObject, KnockableObject, WinPuffObject):
  2503. burnable = True
  2504. freezable = True
  2505. stayonplatform = True
  2506. def __init__(self, x, y, z=0, **kwargs):
  2507. kwargs["sprite"] = crystallo_walk_sprite
  2508. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2509. def touch(self, other):
  2510. other.hurt()
  2511. def stomp(self, other):
  2512. other.stomp_jump(self)
  2513. play_sound(stomp_sound, self.x, self.y)
  2514. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2515. Corpse.create(self.x, self.y, self.z, sprite=crystallo_squished_sprite,
  2516. image_xscale=self.image_xscale,
  2517. image_yscale=self.image_yscale)
  2518. self.destroy()
  2519. def knock(self, other=None):
  2520. super().knock(other)
  2521. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2522. def touch_hurt(self):
  2523. pass
  2524. class WalkingIceblock(CrowdObject, KnockableObject, BurnableObject,
  2525. WinPuffObject):
  2526. gravity = ICEBLOCK_GRAVITY
  2527. fall_speed = ICEBLOCK_FALL_SPEED
  2528. freezable = True
  2529. stayonplatform = True
  2530. def __init__(self, x, y, z=0, start_flat=False, **kwargs):
  2531. self.start_flat = start_flat
  2532. kwargs["sprite"] = iceblock_walk_sprite
  2533. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2534. self.flat = False
  2535. self.dashing = False
  2536. self.thrower = None
  2537. def init_flat(self):
  2538. self.flat = True
  2539. self.dashing = False
  2540. self.active_range = ICEBLOCK_ACTIVE_RANGE
  2541. self.walk_speed = self.__class__.walk_speed
  2542. self.stayonplatform = False
  2543. self.xvelocity = 0
  2544. self.xdeceleration = 0
  2545. self.sprite = iceblock_flat_sprite
  2546. self.image_index = 0
  2547. self.image_fps = None
  2548. def cancel_flat(self):
  2549. self.flat = False
  2550. self.dashing = False
  2551. self.active_range = self.__class__.active_range
  2552. self.walk_speed = self.__class__.walk_speed
  2553. self.stayonplatform = True
  2554. self.xvelocity = 0
  2555. self.xdeceleration = 0
  2556. self.sprite = iceblock_walk_sprite
  2557. self.image_index = 0
  2558. self.image_fps = None
  2559. def init_dash(self, direction):
  2560. self.flat = True
  2561. self.dashing = True
  2562. self.active_range = ICEBLOCK_ACTIVE_RANGE
  2563. self.walk_speed = ICEBLOCK_DASH_SPEED
  2564. self.xvelocity = 0
  2565. self.xdeceleration = 0
  2566. self.sprite = iceblock_flat_sprite
  2567. self.image_index = 0
  2568. self.image_fps = None
  2569. self.set_direction(direction)
  2570. def cancel_dash(self):
  2571. self.flat = True
  2572. self.dashing = False
  2573. self.active_range = ICEBLOCK_ACTIVE_RANGE
  2574. self.walk_speed = self.__class__.walk_speed
  2575. self.sprite = iceblock_flat_sprite
  2576. self.image_index = 0
  2577. self.image_fps = 0
  2578. def move(self):
  2579. if not self.flat or self.dashing:
  2580. super().move()
  2581. else:
  2582. FallingObject.move(self)
  2583. def deactivate(self):
  2584. if self.dashing:
  2585. self.destroy()
  2586. else:
  2587. super().deactivate()
  2588. def touch(self, other):
  2589. if self.flat and not self.dashing:
  2590. if self.parent is None:
  2591. self.thrower = other
  2592. if other.pickup(self):
  2593. self.gravity = 0
  2594. if other.action_pressed:
  2595. other.action()
  2596. else:
  2597. other.do_kick()
  2598. self.init_dash(-1 if other.image_xscale < 0 else 1)
  2599. else:
  2600. other.hurt()
  2601. def stomp(self, other):
  2602. if not self.flat or self.dashing:
  2603. other.stomp_jump(self)
  2604. play_sound(stomp_sound, self.x, self.y)
  2605. self.init_flat()
  2606. else:
  2607. self.touch(other)
  2608. def knock(self, other=None):
  2609. if self.parent is None:
  2610. super().knock(other)
  2611. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2612. def burn(self):
  2613. super().burn()
  2614. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2615. def freeze(self):
  2616. if self.dashing:
  2617. self.cancel_dash()
  2618. elif self.flat:
  2619. self.cancel_flat()
  2620. def stop_left(self):
  2621. if self.flat and self.parent is None:
  2622. play_sound(iceblock_bump_sound, self.x, self.y)
  2623. self.xvelocity = abs(self.xvelocity)
  2624. self.set_direction(1)
  2625. left_touching = self.get_left_touching_wall()
  2626. for hblock in self.collision(HiddenItemBlock, x=(self.x - 1)):
  2627. if not self.collision(hblock):
  2628. left_touching.append(hblock)
  2629. for block in left_touching:
  2630. if isinstance(block, HittableBlock):
  2631. block.hit(self.thrower)
  2632. else:
  2633. super().stop_left()
  2634. def stop_right(self):
  2635. if self.flat and self.parent is None:
  2636. play_sound(iceblock_bump_sound, self.x, self.y)
  2637. self.xvelocity = -abs(self.xvelocity)
  2638. self.set_direction(-1)
  2639. right_touching = self.get_right_touching_wall()
  2640. for hblock in self.collision(HiddenItemBlock, x=(self.x + 1)):
  2641. if not self.collision(hblock):
  2642. right_touching.append(hblock)
  2643. for block in right_touching:
  2644. if isinstance(block, HittableBlock):
  2645. block.hit(self.thrower)
  2646. else:
  2647. super().stop_right()
  2648. def stop_up(self):
  2649. self.yvelocity = 0
  2650. if self.flat and self.parent is None:
  2651. top_touching = self.get_top_touching_wall()
  2652. for hblock in self.collision(HiddenItemBlock, y=(self.y - 1)):
  2653. if not self.collision(hblock):
  2654. top_touching.append(hblock)
  2655. for block in top_touching:
  2656. if isinstance(block, HittableBlock):
  2657. block.hit(self.thrower)
  2658. def drop(self):
  2659. if self.parent is not None:
  2660. self.parent.drop_object()
  2661. self.parent = None
  2662. self.gravity = self.__class__.gravity
  2663. def kick(self):
  2664. if self.parent is not None:
  2665. self.parent.kick_object()
  2666. self.gravity = self.__class__.gravity
  2667. self.init_dash(-1 if self.parent.image_xscale < 0 else 1)
  2668. self.yvelocity = 0
  2669. self.parent = None
  2670. def kick_up(self):
  2671. if self.parent is not None:
  2672. self.parent.kick_object()
  2673. play_sound(kick_sound, self.x, self.y)
  2674. self.gravity = self.__class__.gravity
  2675. self.xvelocity = self.parent.xvelocity
  2676. self.yvelocity = get_jump_speed(KICK_UP_HEIGHT, self.gravity)
  2677. self.parent = None
  2678. def event_create(self):
  2679. super().event_create()
  2680. if self.start_flat:
  2681. self.init_flat()
  2682. def event_end_step(self, time_passed, delta_mult):
  2683. if self.parent is None:
  2684. if (self.flat and not self.dashing and self.yvelocity >= 0
  2685. and (self.get_bottom_touching_wall()
  2686. or self.get_bottom_touching_slope())):
  2687. self.xdeceleration = ICEBLOCK_FRICTION
  2688. else:
  2689. self.xdeceleration = 0
  2690. def event_collision(self, other, xdirection, ydirection):
  2691. if self.parent is None:
  2692. if self.flat:
  2693. if isinstance(other, InteractiveObject) and other.knockable:
  2694. if (self.dashing or abs(self.xvelocity) > 0.05
  2695. or self.yvelocity < 0
  2696. or (not self.was_on_floor
  2697. and self.yvelocity > 0.05)):
  2698. other.knock(self)
  2699. elif isinstance(other, Coin):
  2700. other.event_collision(self.thrower, -xdirection,
  2701. -ydirection)
  2702. elif isinstance(other, HiddenItemBlock):
  2703. if ydirection == -1:
  2704. self.move_y(max(0, other.bbox_bottom - self.bbox_top),
  2705. absolute=True, do_events=False)
  2706. self.stop_up()
  2707. elif xdirection == -1:
  2708. self.move_x(max(0, other.bbox_right - self.bbox_left),
  2709. absolute=True, do_events=False)
  2710. self.stop_left()
  2711. elif xdirection == 1:
  2712. self.move_x(min(0, other.bbox_left - self.bbox_right),
  2713. absolute=True, do_events=False)
  2714. self.stop_right()
  2715. elif isinstance(other, Death):
  2716. self.touch_death()
  2717. else:
  2718. super().event_collision(other, xdirection, ydirection)
  2719. class Spiky(CrowdObject, KnockableObject, FreezableObject, WinPuffObject):
  2720. burnable = True
  2721. blastable = True
  2722. stayonplatform = True
  2723. def __init__(self, x, y, z=0, start_frozen=False, **kwargs):
  2724. self.start_frozen = start_frozen
  2725. kwargs["sprite"] = spiky_walk_sprite
  2726. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2727. self.frozen_sprite = spiky_iced_sprite
  2728. def touch(self, other):
  2729. other.hurt()
  2730. def stomp(self, other):
  2731. other.hurt()
  2732. def knock(self, other=None):
  2733. super().knock(other)
  2734. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2735. def blast(self):
  2736. self.knock()
  2737. def touch_hurt(self):
  2738. pass
  2739. def event_create(self):
  2740. super().event_create()
  2741. if self.start_frozen:
  2742. self.permafreeze()
  2743. class WalkingBomb(CrowdObject, KnockableObject, FreezableObject,
  2744. WinPuffObject):
  2745. burnable = True
  2746. blastable = True
  2747. stayonplatform = True
  2748. def __init__(self, x, y, z=0, start_frozen=False, start_ticking=False,
  2749. **kwargs):
  2750. self.start_frozen = start_frozen
  2751. self.start_ticking = start_ticking
  2752. kwargs["sprite"] = bomb_walk_sprite
  2753. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2754. self.frozen_sprite = bomb_iced_sprite
  2755. self.ticking = False
  2756. self.thrower = None
  2757. self.normal_gravity = self.__class__.gravity
  2758. def init_ticking(self):
  2759. self.ticking = True
  2760. self.active_range = ICEBLOCK_ACTIVE_RANGE
  2761. self.normal_gravity = BOMB_GRAVITY
  2762. self.gravity = self.normal_gravity
  2763. self.xvelocity = 0
  2764. self.sprite = bomb_ticking_sprite
  2765. self.image_index = 0
  2766. self.image_fps = None
  2767. def cancel_ticking(self):
  2768. self.ticking = False
  2769. self.active_range = self.__class__.active_range
  2770. self.normal_gravity = self.__class__.gravity
  2771. self.gravity = self.normal_gravity
  2772. self.xvelocity = 0
  2773. self.sprite = bomb_walk_sprite
  2774. self.image_index = 0
  2775. self.image_fps = None
  2776. def set_direction(self, direction):
  2777. if self.ticking:
  2778. self.image_xscale = abs(self.image_xscale) * direction
  2779. self.xvelocity = abs(self.xvelocity) * direction / 2
  2780. else:
  2781. super().set_direction(direction)
  2782. def move(self):
  2783. if not self.ticking:
  2784. super().move()
  2785. else:
  2786. FallingObject.move(self)
  2787. def touch(self, other):
  2788. if self.ticking:
  2789. if other.pickup(self):
  2790. self.thrower = other
  2791. self.gravity = 0
  2792. self.xvelocity = 0
  2793. self.yvelocity = 0
  2794. if other.action_pressed:
  2795. other.action()
  2796. else:
  2797. other.hurt()
  2798. def stomp(self, other):
  2799. if self.ticking:
  2800. self.touch(other)
  2801. else:
  2802. other.stomp_jump(self)
  2803. play_sound(stomp_sound, self.x, self.y)
  2804. self.init_ticking()
  2805. self.thrower = other
  2806. def knock(self, other=None):
  2807. if isinstance(other, Player):
  2808. self.thrower = other
  2809. self.burn()
  2810. elif isinstance(other, WalkingIceblock):
  2811. self.thrower = other.thrower
  2812. self.burn()
  2813. elif isinstance(other, FallingIcicle):
  2814. super().knock(other)
  2815. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2816. else:
  2817. self.burn()
  2818. def burn(self):
  2819. e = Explosion.create(self.x, self.y, self.z, sprite=explosion_sprite)
  2820. e.detonator = self.thrower
  2821. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2822. self.destroy()
  2823. def freeze(self):
  2824. if self.ticking:
  2825. if self.image_index > 0:
  2826. self.image_index -= 1
  2827. elif self.parent is None:
  2828. self.cancel_ticking()
  2829. else:
  2830. super().freeze()
  2831. def touch_hurt(self):
  2832. pass
  2833. def touch_death(self):
  2834. self.burn()
  2835. def drop(self):
  2836. if self.parent is not None:
  2837. self.parent.drop_object()
  2838. self.parent = None
  2839. self.gravity = self.normal_gravity
  2840. def kick(self):
  2841. if self.parent is not None:
  2842. self.parent.kick_object()
  2843. self.xvelocity = math.copysign(KICK_FORWARD_SPEED,
  2844. self.parent.image_xscale)
  2845. self.yvelocity = get_jump_speed(KICK_FORWARD_HEIGHT,
  2846. self.normal_gravity)
  2847. self.parent = None
  2848. self.gravity = self.normal_gravity
  2849. def kick_up(self):
  2850. if self.parent is not None:
  2851. self.parent.kick_object()
  2852. self.xvelocity = self.parent.xvelocity
  2853. self.yvelocity = get_jump_speed(KICK_UP_HEIGHT,
  2854. self.normal_gravity)
  2855. self.parent = None
  2856. self.gravity = self.normal_gravity
  2857. def stop_left(self):
  2858. if self.ticking:
  2859. if self.parent is None:
  2860. self.set_direction(1)
  2861. else:
  2862. super().stop_left()
  2863. def stop_right(self):
  2864. if self.ticking:
  2865. if self.parent is None:
  2866. self.set_direction(-1)
  2867. else:
  2868. super().stop_right()
  2869. def stop_up(self):
  2870. if self.parent is None:
  2871. self.yvelocity = 0
  2872. def event_create(self):
  2873. super().event_create()
  2874. if self.start_ticking:
  2875. self.init_ticking()
  2876. if self.start_frozen:
  2877. self.permafreeze()
  2878. def event_end_step(self, time_passed, delta_mult):
  2879. if (self.ticking and self.yvelocity >= 0
  2880. and (self.get_bottom_touching_wall()
  2881. or self.get_bottom_touching_slope())):
  2882. self.xdeceleration = ROCK_FRICTION
  2883. else:
  2884. self.xdeceleration = 0
  2885. def event_animation_end(self):
  2886. if self.ticking:
  2887. self.burn()
  2888. class Jumpy(CrowdObject, KnockableObject, FreezableObject, WinPuffObject):
  2889. nonstick_left = True
  2890. nonstick_right = True
  2891. nonstick_top = True
  2892. nonstick_bottom = True
  2893. burnable = True
  2894. blastable = True
  2895. walk_speed = 0
  2896. def __init__(self, x, y, z=0, start_frozen=False, **kwargs):
  2897. self.start_frozen = start_frozen
  2898. kwargs["sprite"] = jumpy_sprite
  2899. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2900. self.frozen_sprite = jumpy_iced_sprite
  2901. def move(self):
  2902. super().move()
  2903. y = self.y + (jumpy_sprite.height - jumpy_bounce_sprite.height)
  2904. for obj in self.collision(xsge_physics.SolidTop, y=y):
  2905. if not self.collision(obj):
  2906. self.sprite = jumpy_bounce_sprite
  2907. break
  2908. else:
  2909. self.sprite = jumpy_sprite
  2910. def touch(self, other):
  2911. other.hurt()
  2912. def stomp(self, other):
  2913. other.hurt()
  2914. def knock(self, other=None):
  2915. super().knock(other)
  2916. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2917. def blast(self):
  2918. self.knock()
  2919. def touch_hurt(self):
  2920. pass
  2921. def stop_up(self):
  2922. self.yvelocity = 0
  2923. def stop_down(self):
  2924. self.yvelocity = get_jump_speed(JUMPY_BOUNCE_HEIGHT, self.gravity)
  2925. def event_create(self):
  2926. super().event_create()
  2927. if self.start_frozen:
  2928. self.permafreeze()
  2929. class FlyingEnemy(CrowdBlockingObject):
  2930. def move(self):
  2931. if abs(self.xvelocity) > abs(self.yvelocity):
  2932. self.image_xscale = math.copysign(self.image_xscale, self.xvelocity)
  2933. self.had_xv = 5
  2934. elif self.had_xv > 0:
  2935. self.had_xv -= 1
  2936. else:
  2937. player = self.get_nearest_player()
  2938. if player is not None:
  2939. if self.x < player.x:
  2940. self.image_xscale = abs(self.image_xscale)
  2941. else:
  2942. self.image_xscale = -abs(self.image_xscale)
  2943. class FlyingSnowball(FlyingEnemy, KnockableObject, BurnableObject,
  2944. WinPuffObject):
  2945. killed_by_void = False
  2946. always_active = True
  2947. freezable = True
  2948. had_xv = 0
  2949. def __init__(self, x, y, z=0, **kwargs):
  2950. kwargs["sprite"] = flying_snowball_sprite
  2951. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2952. def touch(self, other):
  2953. other.hurt()
  2954. def stomp(self, other):
  2955. other.stomp_jump(self)
  2956. play_sound(squish_sound, self.x, self.y)
  2957. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2958. Corpse.create(self.x, self.y, self.z,
  2959. sprite=flying_snowball_squished_sprite,
  2960. image_xscale=self.image_xscale,
  2961. image_yscale=self.image_yscale)
  2962. self.destroy()
  2963. def knock(self, other=None):
  2964. super().knock(other)
  2965. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2966. def burn(self):
  2967. super().burn()
  2968. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2969. def freeze(self):
  2970. self.burn()
  2971. class FlyingSpiky(FlyingEnemy, KnockableObject, FreezableObject,
  2972. WinPuffObject):
  2973. killed_by_void = False
  2974. always_active = True
  2975. burnable = True
  2976. had_xv = 0
  2977. def __init__(self, x, y, z=0, start_frozen=False, **kwargs):
  2978. self.start_frozen = start_frozen
  2979. kwargs["sprite"] = flying_spiky_sprite
  2980. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  2981. self.frozen_sprite = flying_spiky_iced_sprite
  2982. def touch(self, other):
  2983. other.hurt()
  2984. def stomp(self, other):
  2985. other.hurt()
  2986. def knock(self, other=None):
  2987. super().knock(other)
  2988. sge.game.current_room.add_points(ENEMY_KILL_POINTS)
  2989. def event_create(self):
  2990. super().event_create()
  2991. if self.start_frozen:
  2992. self.permafreeze()
  2993. class Explosion(InteractiveObject):
  2994. killed_by_void = False
  2995. detonator = None
  2996. def event_create(self):
  2997. super().event_create()
  2998. self.__life = EXPLOSION_TIME
  2999. self.__friends = set()
  3000. play_sound(explosion_sound, self.x, self.y)
  3001. def deactivate(self):
  3002. pass
  3003. def update_active(self):
  3004. self.active = True
  3005. self.tangible = True
  3006. def touch(self, other):
  3007. other.hurt()
  3008. def touch_death(self):
  3009. pass
  3010. def project_light(self):
  3011. xsge_lighting.project_light(self.x, self.y, explosion_light_sprite)
  3012. def event_step(self, time_passed, delta_mult):
  3013. self.__life -= delta_mult
  3014. if self.__life <= 0:
  3015. self.destroy()
  3016. def event_collision(self, other, xdirection, ydirection):
  3017. if other not in (friend() for friend in self.__friends):
  3018. self.__friends.add(weakref.ref(other))
  3019. if isinstance(other, InteractiveObject):
  3020. if other.blastable:
  3021. other.blast()
  3022. if isinstance(other, HittableBlock):
  3023. if self.detonator is not None:
  3024. other.hit(self.detonator)
  3025. else:
  3026. detonator = self.get_nearest_player()
  3027. if detonator is not None:
  3028. other.hit(detonator)
  3029. else:
  3030. other.hit(None)
  3031. if isinstance(other, (Iceblock)):
  3032. other.burn()
  3033. if isinstance(other, (ThinIce)):
  3034. other.shatter()
  3035. super().event_collision(other, xdirection, ydirection)
  3036. class Icicle(InteractiveObject):
  3037. shaking = False
  3038. def __init__(self, x, y, z=0, **kwargs):
  3039. kwargs["sprite"] = icicle_sprite
  3040. kwargs["checks_collisions"] = False
  3041. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3042. self.shake_counter = SHAKE_FRAME_TIME
  3043. def do_shake(self):
  3044. self.shaking = True
  3045. play_sound(icicle_shake_sound, self.x, self.y)
  3046. self.alarms["fall"] = ICICLE_SHAKE_TIME
  3047. def check_shake(self):
  3048. if not self.warping:
  3049. players = []
  3050. crash_y = sge.game.current_room.height
  3051. objects = (
  3052. sge.game.current_room.get_objects_at(
  3053. self.bbox_left - ICICLE_LAX, self.bbox_bottom,
  3054. self.bbox_width + 2*ICICLE_LAX,
  3055. (sge.game.current_room.height-self.bbox_bottom
  3056. + sge.game.current_room.object_area_height))
  3057. | sge.game.current_room.object_area_void)
  3058. for obj in objects:
  3059. if (obj.bbox_top > self.bbox_bottom
  3060. and self.bbox_right > obj.bbox_left
  3061. and self.bbox_left < obj.bbox_right):
  3062. if isinstance(obj, xsge_physics.SolidTop):
  3063. crash_y = min(crash_y, obj.bbox_top)
  3064. elif isinstance(obj, xsge_physics.SlopeTopLeft):
  3065. crash_y = min(crash_y, obj.get_slope_y(self.bbox_right))
  3066. elif isinstance(obj, xsge_physics.SlopeTopRight):
  3067. crash_y = min(crash_y, obj.get_slope_y(self.bbox_left))
  3068. if (obj.bbox_bottom > self.bbox_top
  3069. and self.bbox_right + ICICLE_LAX > obj.bbox_left
  3070. and self.bbox_left - ICICLE_LAX < obj.bbox_right):
  3071. if isinstance(obj, Player):
  3072. players.append(obj)
  3073. for player in players:
  3074. if player.bbox_top < crash_y:
  3075. self.do_shake()
  3076. break
  3077. def deactivate(self):
  3078. self.shaking = False
  3079. super().deactivate()
  3080. def touch(self, other):
  3081. other.hurt()
  3082. def event_step(self, time_passed, delta_mult):
  3083. super().event_step(time_passed, delta_mult)
  3084. if self.active:
  3085. if self.shaking:
  3086. self.shake_counter -= delta_mult
  3087. while self.shake_counter <= 0:
  3088. self.shake_counter += SHAKE_FRAME_TIME
  3089. if self.image_origin_x > self.sprite.origin_x:
  3090. self.image_origin_x = self.sprite.origin_x - 2
  3091. else:
  3092. self.image_origin_x = self.sprite.origin_x + 2
  3093. else:
  3094. self.check_shake()
  3095. def event_alarm(self, alarm_id):
  3096. if alarm_id == "fall":
  3097. FallingIcicle.create(self.x, self.y, self.z, sprite=self.sprite,
  3098. image_xscale=self.image_xscale,
  3099. image_yscale=self.image_yscale)
  3100. self.destroy()
  3101. def event_collision(self, other, xdirection, ydirection):
  3102. if isinstance(other, InteractiveObject) and other.knockable:
  3103. other.knock(self)
  3104. super().event_collision(other, xdirection, ydirection)
  3105. class SteadyIcicle(Icicle):
  3106. def check_shake(self, earthquake=False):
  3107. if earthquake:
  3108. super().check_shake()
  3109. class RaccotIcicle(Icicle):
  3110. never_tangible = True
  3111. def __init__(self, x, y, z=0, **kwargs):
  3112. kwargs["visible"] = False
  3113. super().__init__(x, y, z, **kwargs)
  3114. def check_shake(self, raccot=False):
  3115. if raccot:
  3116. super().check_shake()
  3117. def do_shake(self):
  3118. FallingIcicle.create(self.x, self.y, self.z, sprite=self.sprite,
  3119. image_xscale=self.image_xscale,
  3120. image_yscale=self.image_yscale)
  3121. class FallingIcicle(FallingObject):
  3122. gravity = ICICLE_GRAVITY
  3123. fall_speed = ICICLE_FALL_SPEED
  3124. def deactivate(self):
  3125. self.destroy()
  3126. def touch(self, other):
  3127. other.hurt()
  3128. def touch_death(self):
  3129. play_sound(sizzle_sound, self.x, self.y)
  3130. self.destroy()
  3131. def touch_hurt(self):
  3132. pass
  3133. def stop_down(self):
  3134. play_sound(icicle_crash_sound, self.x, self.y)
  3135. Corpse.create(self.x, self.y, self.z,
  3136. sprite=icicle_broken_sprite,
  3137. image_xscale=self.image_xscale,
  3138. image_yscale=self.image_yscale)
  3139. self.destroy()
  3140. def event_collision(self, other, xdirection, ydirection):
  3141. if isinstance(other, InteractiveObject) and other.knockable:
  3142. other.knock(self)
  3143. super().event_collision(other, xdirection,
  3144. ydirection)
  3145. class Crusher(FallingObject, xsge_physics.MobileColliderWall,
  3146. xsge_physics.Solid):
  3147. nonstick_left = True
  3148. nonstick_right = True
  3149. nonstick_top = True
  3150. nonstick_bottom = True
  3151. sticky_top = True
  3152. always_tangible = True
  3153. burnable = True
  3154. freezable = True
  3155. gravity = 0
  3156. fall_speed = CRUSHER_FALL_SPEED
  3157. crushing = False
  3158. def touch(self, other):
  3159. other.hurt()
  3160. def touch_death(self):
  3161. pass
  3162. def touch_hurt(self):
  3163. pass
  3164. def stop_up(self):
  3165. self.yvelocity = 0
  3166. self.crushing = False
  3167. def stop_down(self):
  3168. play_sound(brick_sound, self.x, self.y)
  3169. self.yvelocity = 0
  3170. self.gravity = 0
  3171. sge.game.current_room.shake(CRUSHER_SHAKE_NUM)
  3172. self.alarms["crush_end"] = CRUSHER_CRUSH_TIME
  3173. def event_step(self, time_passed, delta_mult):
  3174. if not self.crushing:
  3175. super().event_step(time_passed, delta_mult)
  3176. if self.active:
  3177. players = []
  3178. crash_y = sge.game.current_room.height
  3179. objects = (
  3180. sge.game.current_room.get_objects_at(
  3181. self.bbox_left - CRUSHER_LAX, self.bbox_bottom,
  3182. self.bbox_width + 2 * CRUSHER_LAX,
  3183. (sge.game.current_room.height - self.bbox_bottom +
  3184. sge.game.current_room.object_area_height)) |
  3185. sge.game.current_room.object_area_void)
  3186. for obj in objects:
  3187. if (obj.bbox_top > self.bbox_bottom
  3188. and self.bbox_right > obj.bbox_left
  3189. and self.bbox_left < obj.bbox_right):
  3190. if isinstance(obj, xsge_physics.SolidTop):
  3191. crash_y = min(crash_y, obj.bbox_top)
  3192. elif isinstance(obj, xsge_physics.SlopeTopLeft):
  3193. crash_y = min(crash_y,
  3194. obj.get_slope_y(self.bbox_right))
  3195. elif isinstance(obj, xsge_physics.SlopeTopRight):
  3196. crash_y = min(crash_y,
  3197. obj.get_slope_y(self.bbox_left))
  3198. if (obj.bbox_top > self.bbox_bottom
  3199. and self.bbox_right + CRUSHER_LAX > obj.bbox_left
  3200. and self.bbox_left - CRUSHER_LAX < obj.bbox_right):
  3201. if isinstance(obj, Player):
  3202. players.append(obj)
  3203. for player in players:
  3204. if player.bbox_top < crash_y + CRUSHER_LAX:
  3205. self.crushing = True
  3206. self.gravity = CRUSHER_GRAVITY
  3207. break
  3208. else:
  3209. if not self.get_top_touching_wall():
  3210. self.yvelocity = -CRUSHER_RISE_SPEED
  3211. self.crushing = True
  3212. def event_alarm(self, alarm_id):
  3213. if alarm_id == "crush_end":
  3214. self.yvelocity = -CRUSHER_RISE_SPEED
  3215. def event_collision(self, other, xdirection, ydirection):
  3216. if isinstance(other, InteractiveObject) and other.knockable:
  3217. other.knock(self)
  3218. super().event_collision(other, xdirection, ydirection)
  3219. class Krush(Crusher):
  3220. def __init__(self, x, y, z=0, **kwargs):
  3221. kwargs["sprite"] = krush_sprite
  3222. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3223. class Krosh(Crusher):
  3224. def __init__(self, x, y, z=0, **kwargs):
  3225. kwargs["sprite"] = krosh_sprite
  3226. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3227. class Circoflame(InteractiveObject):
  3228. killed_by_void = False
  3229. active_range = 0
  3230. burnable = True
  3231. freezable = True
  3232. def __init__(self, center, x, y, z=0, **kwargs):
  3233. self.center = weakref.ref(center)
  3234. kwargs["sprite"] = circoflame_sprite
  3235. kwargs["checks_collisions"] = False
  3236. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3237. def touch(self, other):
  3238. other.hurt()
  3239. def freeze(self):
  3240. play_sound(sizzle_sound, self.x, self.y)
  3241. center = self.center()
  3242. if center is not None:
  3243. center.destroy()
  3244. self.destroy()
  3245. def project_light(self):
  3246. xsge_lighting.project_light(self.x, self.y, circoflame_light_sprite)
  3247. class CircoflameCenter(InteractiveObject):
  3248. killed_by_void = False
  3249. always_active = True
  3250. never_tangible = True
  3251. def __init__(self, x, y, z=0, radius=(TILE_SIZE * 4), pos=180,
  3252. rvelocity=2):
  3253. self.radius = radius
  3254. self.pos = pos
  3255. self.rvelocity = rvelocity
  3256. self.flame = Circoflame(self, x, y, z)
  3257. super().__init__(x, y, z, visible=False, tangible=False)
  3258. def event_create(self):
  3259. sge.game.current_room.add(self.flame)
  3260. def event_step(self, time_passed, delta_mult):
  3261. self.pos += self.rvelocity * delta_mult
  3262. self.pos %= 360
  3263. x = math.cos(math.radians(self.pos)) * self.radius
  3264. y = math.sin(math.radians(self.pos)) * self.radius
  3265. self.flame.x = self.x + x
  3266. self.flame.y = self.y + y
  3267. class Boss(InteractiveObject):
  3268. always_active = True
  3269. always_tangible = True
  3270. def __init__(self, x, y, ID="boss", death_timeline=None, stage=0,
  3271. **kwargs):
  3272. self.ID = ID
  3273. self.death_timeline = death_timeline
  3274. self.stage = stage
  3275. super().__init__(x, y, **kwargs)
  3276. def event_create(self):
  3277. super().event_create()
  3278. sge.game.current_room.add_timeline_object(self)
  3279. def event_destroy(self):
  3280. for obj in sge.game.current_room.objects:
  3281. if obj is not self and isinstance(obj, Boss) and obj.stage > 0:
  3282. break
  3283. else:
  3284. if self.death_timeline:
  3285. sge.game.current_room.load_timeline(self.death_timeline)
  3286. else:
  3287. sge.game.current_room.win_level(False)
  3288. class Snowman(FallingObject, Boss):
  3289. burnable = True
  3290. freezable = True
  3291. knockable = True
  3292. blastable = True
  3293. def __init__(self, x, y, hp=SNOWMAN_HP, strong_stage=SNOWMAN_STRONG_STAGE,
  3294. final_stage=SNOWMAN_FINAL_STAGE, **kwargs):
  3295. self.full_hp = hp
  3296. self.hp = hp
  3297. self.strong_stage = strong_stage
  3298. self.final_stage = final_stage
  3299. if HELL:
  3300. self.final_stage += 1
  3301. self.stunned = False
  3302. self.stun_end = False
  3303. self.stun_time = 0
  3304. self.fixed_sprite = False
  3305. kwargs["sprite"] = snowman_stand_sprite
  3306. super().__init__(x, y, **kwargs)
  3307. def jump(self):
  3308. if self.was_on_floor:
  3309. play_sound(bigjump_sound, self.x, self.y)
  3310. self.yvelocity = get_jump_speed(SNOWMAN_JUMP_HEIGHT, self.gravity)
  3311. def stun(self):
  3312. self.stunned = True
  3313. self.fixed_sprite = True
  3314. self.sprite = snowman_hurt_walk_sprite
  3315. self.xvelocity = 0
  3316. self.xacceleration = 0
  3317. self.image_speed = 0
  3318. if self.yvelocity < 0:
  3319. self.yvelocity = 0
  3320. self.alarms["stun_start"] = SNOWMAN_STOMP_DELAY
  3321. def next_stage(self):
  3322. self.xvelocity = 0
  3323. self.xacceleration = 0
  3324. if self.stage == self.final_stage:
  3325. self.kill()
  3326. else:
  3327. if self.was_on_floor:
  3328. play_sound(bigjump_sound, self.x, self.y)
  3329. self.yvelocity = get_jump_speed(SNOWMAN_HOP_HEIGHT,
  3330. self.gravity)
  3331. self.stage += 1
  3332. self.hp = self.full_hp
  3333. self.stun_end = True
  3334. else:
  3335. self.alarms["stun"] = 1
  3336. def kill(self):
  3337. play_sound(fall_sound, self.x, self.y)
  3338. DeadMan.create(self.x, self.y, self.z, sprite=self.sprite,
  3339. yvelocity=get_jump_speed(ENEMY_HIT_BELOW_HEIGHT),
  3340. image_xscale=self.image_xscale,
  3341. image_yscale=-abs(self.image_yscale))
  3342. self.destroy()
  3343. def move(self):
  3344. super().move()
  3345. if "stomp_delay" not in self.alarms and not self.stunned:
  3346. self.xacceleration = 0
  3347. if self.stage > 0:
  3348. if self.get_bottom_touching_wall():
  3349. can_jump = False
  3350. if self.stage >= self.final_stage:
  3351. walk_speed = SNOWMAN_FINAL_WALK_SPEED
  3352. accel = SNOWMAN_FINAL_ACCELERATION
  3353. elif self.stage >= self.strong_stage:
  3354. walk_speed = SNOWMAN_STRONG_WALK_SPEED
  3355. accel = SNOWMAN_STRONG_ACCELERATION
  3356. can_jump = True
  3357. else:
  3358. walk_speed = SNOWMAN_WALK_SPEED
  3359. accel = SNOWMAN_ACCELERATION
  3360. player = self.get_nearest_player()
  3361. if player is not None:
  3362. d = player.x - self.x
  3363. if (abs(self.xvelocity) < walk_speed
  3364. or (self.xvelocity > 0) != (d > 0)):
  3365. self.xacceleration = math.copysign(accel, d)
  3366. else:
  3367. self.xvelocity = math.copysign(walk_speed, d)
  3368. if (can_jump and self.yvelocity == 0
  3369. and self.y - player.y >= SNOWMAN_JUMP_TRIGGER
  3370. and abs(self.xvelocity) >= walk_speed / 2):
  3371. self.jump()
  3372. else:
  3373. player = self.get_nearest_player()
  3374. if player is not None:
  3375. self.image_xscale = math.copysign(self.image_xscale,
  3376. player.x - self.x)
  3377. def stop_left(self):
  3378. self.xvelocity = abs(self.xvelocity)
  3379. def stop_right(self):
  3380. self.xvelocity = -abs(self.xvelocity)
  3381. def stop_up(self):
  3382. self.yvelocity = 0
  3383. def stop_down(self):
  3384. if self.stage > 0 and self.yvelocity > 1:
  3385. play_sound(brick_sound, self.x, self.y)
  3386. self.yvelocity = 0
  3387. self.xvelocity = 0
  3388. self.xacceleration = 0
  3389. sge.game.current_room.shake(SNOWMAN_SHAKE_NUM)
  3390. self.alarms["stomp_delay"] = SNOWMAN_STOMP_DELAY
  3391. if self.stun_end:
  3392. self.fixed_sprite = False
  3393. self.stunned = False
  3394. self.stun_end = False
  3395. def touch(self, other):
  3396. other.hurt()
  3397. def stomp(self, other):
  3398. other.stomp_jump(self)
  3399. if self.stage > 0 and not self.stunned:
  3400. play_sound(squish_sound, self.x, self.y)
  3401. self.stun()
  3402. def burn(self):
  3403. if self.stage > 0:
  3404. play_sound(sizzle_sound, self.x, self.y)
  3405. self.hp -= 1
  3406. if self.hp <= 0:
  3407. self.next_stage()
  3408. def knock(self, other=None):
  3409. if self.stage > 0 and not isinstance(other, (Icicle, FallingIcicle)):
  3410. play_sound(stomp_sound, self.x, self.y)
  3411. self.stun()
  3412. if other is not None and other.knockable:
  3413. other.knock(self)
  3414. def blast(self):
  3415. if self.stage > 0:
  3416. play_sound(sizzle_sound, self.x, self.y)
  3417. self.next_stage()
  3418. def touch_hurt(self):
  3419. pass
  3420. def touch_death(self):
  3421. self.kill()
  3422. def event_step(self, time_passed, delta_mult):
  3423. super().event_step(time_passed, delta_mult)
  3424. if not self.fixed_sprite:
  3425. if self.was_on_floor:
  3426. speed = abs(self.xvelocity)
  3427. if speed > 0:
  3428. self.sprite = snowman_walk_sprite
  3429. self.image_speed = (speed * SNOWMAN_WALK_FRAMES_PER_PIXEL)
  3430. else:
  3431. self.sprite = snowman_stand_sprite
  3432. else:
  3433. self.sprite = snowman_jump_sprite
  3434. if self.xvelocity:
  3435. self.image_xscale = math.copysign(self.image_xscale,
  3436. self.xvelocity)
  3437. def event_alarm(self, alarm_id):
  3438. if alarm_id == "stun_start":
  3439. self.image_speed = (SNOWMAN_STUNNED_WALK_SPEED
  3440. * SNOWMAN_WALK_FRAMES_PER_PIXEL)
  3441. self.xvelocity = math.copysign(SNOWMAN_STUNNED_WALK_SPEED,
  3442. self.image_xscale)
  3443. self.alarms["stun"] = SNOWMAN_HITSTUN
  3444. elif alarm_id == "stun":
  3445. self.next_stage()
  3446. class Raccot(FallingObject, Boss):
  3447. burnable = True
  3448. freezable = True
  3449. knockable = True
  3450. blastable = True
  3451. @property
  3452. def stage(self):
  3453. return self.__stage
  3454. @stage.setter
  3455. def stage(self, value):
  3456. self.__stage = value
  3457. if self.__ready:
  3458. if value >= 2:
  3459. self.alarms["hop"] = random.uniform(self.hop_interval_min,
  3460. self.hop_interval_max)
  3461. self.alarms["charge"] = random.uniform(self.charge_interval_min,
  3462. self.charge_interval_max)
  3463. else:
  3464. if "hop" in self.alarms:
  3465. del self.alarms["hop"]
  3466. if "charge" in self.alarms:
  3467. del self.alarms["charge"]
  3468. if "charge_end" in self.alarms:
  3469. del self.alarms["charge_end"]
  3470. def __init__(self, x, y, hp=RACCOT_HP, hop_time=RACCOT_HOP_TIME,
  3471. hop_interval_min=RACCOT_HOP_INTERVAL_MIN,
  3472. hop_interval_max=RACCOT_HOP_INTERVAL_MAX,
  3473. charge_interval_min=RACCOT_CHARGE_INTERVAL_MIN,
  3474. charge_interval_max=RACCOT_CHARGE_INTERVAL_MAX, **kwargs):
  3475. self.hp = hp
  3476. self.hop_time = hop_time
  3477. self.hop_interval_min = hop_interval_min
  3478. self.hop_interval_max = hop_interval_max
  3479. self.charge_interval_min = charge_interval_min
  3480. self.charge_interval_max = charge_interval_max
  3481. self.direction = 0
  3482. self.hopping = False
  3483. self.charging = False
  3484. self.crushing = False
  3485. self.__ready = False
  3486. kwargs["sprite"] = raccot_stand_sprite
  3487. super().__init__(x, y, **kwargs)
  3488. self.__ready = True
  3489. if self.stage >= 2:
  3490. self.alarms["hop"] = random.uniform(self.hop_interval_min,
  3491. self.hop_interval_max)
  3492. self.alarms["charge"] = random.uniform(self.charge_interval_min,
  3493. self.charge_interval_max)
  3494. def jump(self):
  3495. if (self.was_on_floor and self.yvelocity == 0
  3496. and "stomp_delay" not in self.alarms):
  3497. play_sound(bigjump_sound, self.x, self.y)
  3498. self.yvelocity = get_jump_speed(RACCOT_JUMP_HEIGHT, self.gravity)
  3499. def hop(self):
  3500. if self.was_on_floor and self.yvelocity == 0:
  3501. self.hopping = True
  3502. self.xvelocity = 0
  3503. self.sprite = raccot_stomp_sprite
  3504. play_sound(yeti_gna_sound, self.x, self.y)
  3505. self.alarms["do_hop"] = self.hop_time
  3506. def do_hop(self):
  3507. self.xvelocity = 0
  3508. self.yvelocity = get_jump_speed(RACCOT_HOP_HEIGHT, self.gravity)
  3509. self.sprite = raccot_hop_sprite
  3510. if self.stage > 1:
  3511. self.alarms["hop"] = random.uniform(self.hop_interval_min,
  3512. self.hop_interval_max)
  3513. def charge(self):
  3514. self.charging = True
  3515. self.alarms["charge_end"] = random.uniform(self.charge_interval_min,
  3516. self.charge_interval_max)
  3517. def charge_end(self):
  3518. self.charging = False
  3519. if self.stage > 1:
  3520. self.alarms["hop"] = random.uniform(self.hop_interval_min,
  3521. self.hop_interval_max)
  3522. self.alarms["charge"] = random.uniform(self.charge_interval_min,
  3523. self.charge_interval_max)
  3524. def crush(self):
  3525. self.crushing = True
  3526. self.gravity = RACCOT_CRUSH_GRAVITY
  3527. self.fall_speed = RACCOT_CRUSH_FALL_SPEED
  3528. self.xacceleration = 0
  3529. self.xvelocity = 0
  3530. self.yvelocity = get_jump_speed(RACCOT_CRUSH_CHARGE, self.gravity)
  3531. play_sound(yeti_gna_sound, self.x, self.y)
  3532. def hurt(self):
  3533. if self.stage > 0:
  3534. play_sound(yeti_roar_sound, self.x, self.y)
  3535. self.hp -= 1
  3536. if self.hp <= 0:
  3537. self.kill()
  3538. def kill(self):
  3539. play_sound(fall_sound, self.x, self.y)
  3540. DeadMan.create(self.x, self.y, self.z, sprite=raccot_hop_sprite,
  3541. xvelocity=self.xvelocity,
  3542. yvelocity=get_jump_speed(ENEMY_HIT_BELOW_HEIGHT),
  3543. image_xscale=self.image_xscale,
  3544. image_yscale=-abs(self.image_yscale))
  3545. self.destroy()
  3546. def move(self):
  3547. super().move()
  3548. player = self.get_nearest_player()
  3549. if player is not None:
  3550. if self.charging:
  3551. self.direction = player.x - self.x
  3552. if self.y - player.y >= RACCOT_JUMP_TRIGGER:
  3553. self.jump()
  3554. elif self.stage > 1:
  3555. self.direction = 0
  3556. if not self.xvelocity:
  3557. self.image_xscale = math.copysign(self.image_xscale,
  3558. player.x - self.x)
  3559. if not self.crushing:
  3560. if "stomp_delay" not in self.alarms:
  3561. self.xacceleration = 0
  3562. if self.direction and not self.hopping:
  3563. if (abs(self.xvelocity) < RACCOT_WALK_SPEED
  3564. or (self.xvelocity > 0) != (self.direction > 0)):
  3565. self.xacceleration = math.copysign(
  3566. RACCOT_ACCELERATION, self.direction)
  3567. else:
  3568. self.xvelocity = math.copysign(RACCOT_WALK_SPEED,
  3569. self.direction)
  3570. if self.charging and not self.was_on_floor:
  3571. players = []
  3572. crash_y = sge.game.current_room.height
  3573. objects = (
  3574. sge.game.current_room.get_objects_at(
  3575. self.bbox_left, self.bbox_bottom, self.bbox_width,
  3576. (sge.game.current_room.height - self.bbox_bottom +
  3577. sge.game.current_room.object_area_height)) |
  3578. sge.game.current_room.object_area_void)
  3579. for obj in objects:
  3580. if (obj.bbox_top > self.bbox_bottom
  3581. and self.bbox_right > obj.bbox_left
  3582. and self.bbox_left < obj.bbox_right):
  3583. if isinstance(obj, xsge_physics.SolidTop):
  3584. crash_y = min(crash_y, obj.bbox_top)
  3585. elif isinstance(obj, xsge_physics.SlopeTopLeft):
  3586. crash_y = min(crash_y,
  3587. obj.get_slope_y(self.bbox_right))
  3588. elif isinstance(obj, xsge_physics.SlopeTopRight):
  3589. crash_y = min(crash_y,
  3590. obj.get_slope_y(self.bbox_left))
  3591. if (obj.bbox_top > self.bbox_bottom
  3592. and (self.bbox_right + RACCOT_CRUSH_LAX
  3593. > obj.bbox_left)
  3594. and (self.bbox_left - RACCOT_CRUSH_LAX
  3595. < obj.bbox_right)):
  3596. if isinstance(obj, Player):
  3597. players.append(obj)
  3598. for player in players:
  3599. if player.bbox_top < crash_y:
  3600. self.crush()
  3601. break
  3602. def stop_left(self):
  3603. self.xvelocity = 0
  3604. if self.charging:
  3605. self.jump()
  3606. def stop_right(self):
  3607. self.xvelocity = 0
  3608. if self.charging:
  3609. self.jump()
  3610. def stop_up(self):
  3611. self.yvelocity = 0
  3612. if self.get_bottom_touching_wall():
  3613. self.stop_down()
  3614. def stop_down(self):
  3615. if self.stage > 0 and self.yvelocity >= RACCOT_STOMP_SPEED:
  3616. crushed_brick = False
  3617. self.xvelocity = 0
  3618. self.xacceleration = 0
  3619. sge.game.current_room.shake(RACCOT_SHAKE_NUM)
  3620. self.alarms["stomp_delay"] = RACCOT_STOMP_DELAY
  3621. if self.hopping:
  3622. self.hopping = False
  3623. for obj in sge.game.current_room.objects:
  3624. if isinstance(obj, RaccotIcicle):
  3625. obj.check_shake(True)
  3626. if self.crushing and self.yvelocity >= RACCOT_CRUSH_SPEED:
  3627. for obj in self.get_bottom_touching_wall():
  3628. if isinstance(obj, Brick):
  3629. obj.hit(self)
  3630. crushed_brick = True
  3631. self.alarms["charge_end"] = 0
  3632. self.yvelocity = 0
  3633. if not crushed_brick:
  3634. play_sound(brick_sound, self.x, self.y)
  3635. self.hopping = False
  3636. self.crushing = False
  3637. self.gravity = self.__class__.gravity
  3638. self.sprite = raccot_stand_sprite
  3639. def touch(self, other):
  3640. if self.stage > 0:
  3641. other.hurt()
  3642. def stomp(self, other):
  3643. other.stomp_jump(self)
  3644. def knock(self, other=None):
  3645. if isinstance(other, InteractiveObject) and other.knockable:
  3646. other.knock(self)
  3647. def blast(self):
  3648. self.hurt()
  3649. def touch_hurt(self):
  3650. pass
  3651. def touch_death(self):
  3652. self.kill()
  3653. def event_step(self, time_passed, delta_mult):
  3654. super().event_step(time_passed, delta_mult)
  3655. if self.was_on_floor or self.get_bottom_touching_wall():
  3656. if self.hopping:
  3657. self.sprite = raccot_stomp_sprite
  3658. else:
  3659. xm = (self.xvelocity > 0) - (self.xvelocity < 0)
  3660. facing = (self.image_xscale > 0) - (self.image_xscale < 0)
  3661. speed = abs(self.xvelocity)
  3662. if speed > 0:
  3663. self.sprite = raccot_walk_sprite
  3664. self.image_speed = speed * RACCOT_WALK_FRAMES_PER_PIXEL
  3665. if xm != facing:
  3666. self.image_speed *= -1
  3667. else:
  3668. self.sprite = raccot_stand_sprite
  3669. else:
  3670. if self.hopping:
  3671. self.sprite = raccot_hop_sprite
  3672. elif self.crushing:
  3673. self.sprite = raccot_stomp_sprite
  3674. else:
  3675. self.sprite = raccot_jump_sprite
  3676. if self.xvelocity:
  3677. self.image_xscale = math.copysign(self.image_xscale,
  3678. self.xvelocity)
  3679. def event_alarm(self, alarm_id):
  3680. if alarm_id == "hop":
  3681. if not self.charging:
  3682. self.hop()
  3683. elif alarm_id == "do_hop":
  3684. self.do_hop()
  3685. elif alarm_id == "charge":
  3686. if self.was_on_floor and "do_hop" not in self.alarms:
  3687. self.charge()
  3688. else:
  3689. self.alarms["charge"] = 0
  3690. elif alarm_id == "charge_end":
  3691. self.charge_end()
  3692. def event_destroy(self):
  3693. super().event_destroy()
  3694. play_sound(yeti_roar_sound, self.x, self.y)
  3695. class FireFlower(FallingObject, WinPuffObject):
  3696. fall_speed = FLOWER_FALL_SPEED
  3697. slide_speed = 0
  3698. win_puff_points = 0
  3699. def __init__(self, x, y, z=0, **kwargs):
  3700. kwargs["sprite"] = fire_flower_sprite
  3701. x += fire_flower_sprite.origin_x
  3702. y += fire_flower_sprite.origin_y
  3703. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3704. self.ammo = FIREBALL_AMMO
  3705. self.light_sprite = fire_flower_light_sprite
  3706. def touch(self, other):
  3707. if other.pickup(self):
  3708. self.gravity = 0
  3709. def knock(self, other=None):
  3710. self.yvelocity = get_jump_speed(ITEM_HIT_HEIGHT)
  3711. def drop(self):
  3712. if self.parent is not None:
  3713. self.parent.drop_object()
  3714. self.parent = None
  3715. self.gravity = self.__class__.gravity
  3716. def kick(self, up=False):
  3717. if self.parent is not None:
  3718. d = (self.image_xscale >= 0) - (self.image_xscale < 0)
  3719. if self.ammo > 0:
  3720. if up:
  3721. yv = get_jump_speed(FIREBALL_UP_HEIGHT, Fireball.gravity)
  3722. else:
  3723. yv = FIREBALL_FALL_SPEED
  3724. Fireball.create(self.x, self.y, self.parent.z,
  3725. sprite=fire_bullet_sprite,
  3726. xvelocity=(FIREBALL_SPEED * d), yvelocity=yv,
  3727. image_xscale=self.image_xscale)
  3728. play_sound(shoot_sound, self.x, self.y)
  3729. if not GOD:
  3730. self.ammo -= 1
  3731. self.sprite = fire_flower_sprite.copy()
  3732. lightness = int((self.ammo / FIREBALL_AMMO) * 255)
  3733. darkener = sge.gfx.Sprite(width=self.sprite.width,
  3734. height=self.sprite.height)
  3735. darkener.draw_rectangle(0, 0, darkener.width, darkener.height,
  3736. fill=sge.gfx.Color([lightness] * 3))
  3737. self.sprite.draw_sprite(darkener, 0, 0, 0,
  3738. blend_mode=sge.BLEND_RGB_MULTIPLY)
  3739. self.light_sprite = fire_flower_light_sprite.copy()
  3740. darkener.width = self.light_sprite.width
  3741. darkener.height = self.light_sprite.height
  3742. self.light_sprite.draw_sprite(
  3743. darkener, 0, 0, 0, blend_mode=sge.BLEND_RGB_MULTIPLY)
  3744. else:
  3745. h = FLOWER_THROW_UP_HEIGHT if up else FLOWER_THROW_HEIGHT
  3746. yv = get_jump_speed(h, ThrownFireFlower.gravity)
  3747. self.parent.kick_object()
  3748. play_sound(kick_sound, self.x, self.y)
  3749. ThrownFireFlower.create(self.parent, self.x, self.y, self.z,
  3750. sprite=self.sprite,
  3751. xvelocity=(FIREBALL_SPEED * d),
  3752. yvelocity=yv,
  3753. image_xscale=self.image_xscale)
  3754. self.parent = None
  3755. self.destroy()
  3756. pass
  3757. def kick_up(self):
  3758. self.kick(True)
  3759. def project_light(self):
  3760. if self.parent is not None:
  3761. x = self.parent.x + self.image_origin_x
  3762. y = self.parent.y
  3763. if self.parent.image_xscale < 0:
  3764. x -= self.sprite.width
  3765. else:
  3766. x = self.x
  3767. y = self.y
  3768. xsge_lighting.project_light(x, y, self.light_sprite)
  3769. def win_puff(self):
  3770. super().win_puff()
  3771. sge.game.current_room.add_points(AMMO_POINTS * (self.ammo + 1))
  3772. def event_end_step(self, time_passed, delta_mult):
  3773. if self.parent is not None:
  3774. direction = -1 if self.parent.image_xscale < 0 else 1
  3775. self.image_xscale = abs(self.image_xscale) * direction
  3776. class IceFlower(FallingObject, WinPuffObject):
  3777. fall_speed = FLOWER_FALL_SPEED
  3778. slide_speed = 0
  3779. win_puff_points = 0
  3780. def __init__(self, x, y, z=0, **kwargs):
  3781. kwargs["sprite"] = ice_flower_sprite
  3782. x += ice_flower_sprite.origin_x
  3783. y += ice_flower_sprite.origin_y
  3784. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3785. self.ammo = ICEBULLET_AMMO
  3786. self.light_sprite = ice_flower_light_sprite
  3787. def touch(self, other):
  3788. if other.pickup(self):
  3789. self.gravity = 0
  3790. def knock(self, other=None):
  3791. self.yvelocity = get_jump_speed(ITEM_HIT_HEIGHT)
  3792. def drop(self):
  3793. if self.parent is not None:
  3794. self.parent.drop_object()
  3795. self.parent = None
  3796. self.gravity = self.__class__.gravity
  3797. def kick(self):
  3798. if self.parent is not None:
  3799. d = (self.image_xscale >= 0) - (self.image_xscale < 0)
  3800. if self.ammo > 0:
  3801. if d < 0:
  3802. bbox_x = -ice_bullet_sprite.origin_x
  3803. else:
  3804. bbox_x = (-ice_bullet_sprite.origin_x -
  3805. ice_bullet_sprite.bbox_width +
  3806. ice_bullet_sprite.width)
  3807. IceBullet.create(self.x, self.y, self.parent.z,
  3808. sprite=ice_bullet_sprite,
  3809. xvelocity=(ICEBULLET_SPEED * d),
  3810. bbox_x=bbox_x)
  3811. play_sound(shoot_sound, self.x, self.y)
  3812. if not GOD:
  3813. self.ammo -= 1
  3814. self.sprite = ice_flower_sprite.copy()
  3815. lightness = int((self.ammo / ICEBULLET_AMMO) * 255)
  3816. darkener = sge.gfx.Sprite(width=self.sprite.width,
  3817. height=self.sprite.height)
  3818. darkener.draw_rectangle(0, 0, darkener.width, darkener.height,
  3819. fill=sge.gfx.Color([lightness] * 3))
  3820. self.sprite.draw_sprite(darkener, 0, 0, 0,
  3821. blend_mode=sge.BLEND_RGB_MULTIPLY)
  3822. self.light_sprite = ice_flower_light_sprite.copy()
  3823. darkener.width = self.light_sprite.width
  3824. darkener.height = self.light_sprite.height
  3825. self.light_sprite.draw_sprite(
  3826. darkener, 0, 0, 0, blend_mode=sge.BLEND_RGB_MULTIPLY)
  3827. else:
  3828. yv = get_jump_speed(FLOWER_THROW_HEIGHT,
  3829. ThrownIceFlower.gravity)
  3830. self.parent.kick_object()
  3831. play_sound(kick_sound, self.x, self.y)
  3832. ThrownIceFlower.create(self.parent, self.x, self.y, self.z,
  3833. sprite=self.sprite,
  3834. xvelocity=(FIREBALL_SPEED * d),
  3835. yvelocity=yv,
  3836. image_xscale=self.image_xscale)
  3837. self.parent = None
  3838. self.destroy()
  3839. pass
  3840. def project_light(self):
  3841. if self.parent is not None:
  3842. x = self.parent.x + self.image_origin_x
  3843. y = self.parent.y
  3844. if self.parent.image_xscale < 0:
  3845. x -= self.sprite.width
  3846. else:
  3847. x = self.x
  3848. y = self.y
  3849. xsge_lighting.project_light(x, y, self.light_sprite)
  3850. def win_puff(self):
  3851. super().win_puff()
  3852. sge.game.current_room.add_points(AMMO_POINTS * (self.ammo + 1))
  3853. def event_end_step(self, time_passed, delta_mult):
  3854. if self.parent is not None:
  3855. direction = -1 if self.parent.image_xscale < 0 else 1
  3856. self.image_xscale = abs(self.image_xscale) * direction
  3857. class ThrownFlower(FallingObject, WinPuffObject):
  3858. active_range = BULLET_ACTIVE_RANGE
  3859. fall_speed = FLOWER_FALL_SPEED
  3860. def __init__(self, thrower, *args, **kwargs):
  3861. self.thrower = thrower
  3862. super().__init__(*args, **kwargs)
  3863. def dissipate(self):
  3864. play_sound(stomp_sound, self.x, self.y)
  3865. Smoke.create(self.x, self.y, self.z, sprite=smoke_puff_sprite)
  3866. self.destroy()
  3867. def deactivate(self):
  3868. self.destroy()
  3869. def touch_hurt(self):
  3870. pass
  3871. def touch_death(self):
  3872. self.dissipate()
  3873. def event_physics_collision_left(self, other, move_loss):
  3874. self.event_collision(other, -1, 0)
  3875. self.dissipate()
  3876. def event_physics_collision_right(self, other, move_loss):
  3877. self.event_collision(other, 1, 0)
  3878. self.dissipate()
  3879. def event_physics_collision_top(self, other, move_loss):
  3880. self.event_collision(other, 0, -1)
  3881. self.dissipate()
  3882. def event_physics_collision_bottom(self, other, move_loss):
  3883. self.event_collision(other, 0, 1)
  3884. self.dissipate()
  3885. class ThrownFireFlower(ThrownFlower):
  3886. def event_collision(self, other, xdirection, ydirection):
  3887. if ((isinstance(other, InteractiveObject) and other.burnable) or
  3888. isinstance(other, (Iceblock, ThinIce))):
  3889. self.dissipate()
  3890. other.burn()
  3891. super().event_collision(other, xdirection, ydirection)
  3892. class ThrownIceFlower(ThrownFlower):
  3893. def event_collision(self, other, xdirection, ydirection):
  3894. if isinstance(other, InteractiveObject) and other.burnable:
  3895. self.dissipate()
  3896. other.freeze()
  3897. super().event_collision(other, xdirection, ydirection)
  3898. class Fireball(FallingObject):
  3899. active_range = BULLET_ACTIVE_RANGE
  3900. gravity = FIREBALL_GRAVITY
  3901. fall_speed = FIREBALL_FALL_SPEED
  3902. def deactivate(self):
  3903. self.destroy()
  3904. def dissipate(self):
  3905. if self in sge.game.current_room.objects:
  3906. Smoke.create(self.x, self.y, self.z, sprite=fireball_smoke_sprite)
  3907. play_sound(fire_dissipate_sound, self.x, self.y)
  3908. self.destroy()
  3909. def touch_hurt(self):
  3910. pass
  3911. def touch_death(self):
  3912. self.destroy()
  3913. def project_light(self):
  3914. xsge_lighting.project_light(self.x, self.y, fireball_light_sprite)
  3915. def stop_left(self):
  3916. self.dissipate()
  3917. def stop_right(self):
  3918. self.dissipate()
  3919. def stop_down(self):
  3920. self.yvelocity = get_jump_speed(FIREBALL_BOUNCE_HEIGHT, self.gravity)
  3921. def event_collision(self, other, xdirection, ydirection):
  3922. if ((isinstance(other, InteractiveObject) and other.burnable) or
  3923. isinstance(other, (Iceblock, ThinIce))):
  3924. other.burn()
  3925. self.dissipate()
  3926. super().event_collision(other, xdirection, ydirection)
  3927. def event_physics_collision_left(self, other, move_loss):
  3928. super().event_physics_collision_left(other, move_loss)
  3929. self.event_collision(other, -1, 0)
  3930. def event_physics_collision_right(self, other, move_loss):
  3931. super().event_physics_collision_right(other, move_loss)
  3932. self.event_collision(other, 1, 0)
  3933. def event_physics_collision_top(self, other, move_loss):
  3934. super().event_physics_collision_top(other, move_loss)
  3935. self.event_collision(other, 0, -1)
  3936. def event_physics_collision_bottom(self, other, move_loss):
  3937. super().event_physics_collision_bottom(other, move_loss)
  3938. self.event_collision(other, 0, 1)
  3939. class IceBullet(InteractiveObject, xsge_physics.Collider):
  3940. active_range = BULLET_ACTIVE_RANGE
  3941. def deactivate(self):
  3942. self.destroy()
  3943. def dissipate(self):
  3944. if self in sge.game.current_room.objects:
  3945. Smoke.create(self.x, self.y, self.z,
  3946. sprite=ice_bullet_break_sprite)
  3947. play_sound(icebullet_break_sound, self.x, self.y)
  3948. self.destroy()
  3949. def event_collision(self, other, xdirection, ydirection):
  3950. if ((isinstance(other, InteractiveObject) and other.freezable) or
  3951. isinstance(other, ThinIce)):
  3952. other.freeze()
  3953. self.dissipate()
  3954. super().event_collision(other, xdirection, ydirection)
  3955. def event_physics_collision_left(self, other, move_loss):
  3956. self.event_collision(other, -1, 0)
  3957. self.dissipate()
  3958. def event_physics_collision_right(self, other, move_loss):
  3959. self.event_collision(other, 1, 0)
  3960. self.dissipate()
  3961. def event_physics_collision_top(self, other, move_loss):
  3962. self.event_collision(other, 0, -1)
  3963. self.dissipate()
  3964. def event_physics_collision_bottom(self, other, move_loss):
  3965. self.event_collision(other, 0, 1)
  3966. self.dissipate()
  3967. class TuxDoll(FallingObject):
  3968. fall_speed = FLOWER_FALL_SPEED
  3969. slide_speed = 0
  3970. def __init__(self, x, y, z=0, **kwargs):
  3971. kwargs["sprite"] = tuxdoll_sprite
  3972. x += tuxdoll_sprite.origin_x
  3973. y += tuxdoll_sprite.origin_y
  3974. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  3975. def touch(self, other):
  3976. play_sound(tuxdoll_sound, self.x, self.y)
  3977. sge.game.current_room.add_points(TUXDOLL_POINTS)
  3978. if main_area and main_area not in tuxdolls_found:
  3979. tuxdolls_found.append(main_area)
  3980. self.destroy()
  3981. def knock(self, other=None):
  3982. self.yvelocity = get_jump_speed(ITEM_HIT_HEIGHT)
  3983. class RockWall(xsge_physics.MobileWall, xsge_physics.Solid):
  3984. push_left = False
  3985. push_right = False
  3986. push_down = False
  3987. sticky_top = True
  3988. def __init__(self, x, y, z=0, parent=None, **kwargs):
  3989. if parent is not None:
  3990. self.parent = weakref.ref(parent)
  3991. else:
  3992. self.parent = lambda: None
  3993. super().__init__(x, y, z, **kwargs)
  3994. class Rock(FallingObject, CrowdBlockingObject, WinPuffObject):
  3995. active_range = ROCK_ACTIVE_RANGE
  3996. gravity = ROCK_GRAVITY
  3997. fall_speed = ROCK_FALL_SPEED
  3998. win_puff_score = 0
  3999. def __init__(self, x, y, z=0, **kwargs):
  4000. kwargs.setdefault("sprite", rock_sprite)
  4001. kwargs["checks_collisions"] = False
  4002. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  4003. self.wall = RockWall(
  4004. self.x, self.y, self.z, parent=self, sprite=self.sprite,
  4005. visible=False, active=False, checks_collisions=False,
  4006. image_xscale=self.image_xscale, image_yscale=self.image_yscale)
  4007. def move_x(self, move, absolute=False, do_events=True, exclude_events=()):
  4008. super().move_x(move, absolute=absolute, do_events=do_events,
  4009. exclude_events=exclude_events)
  4010. tangible = self.tangible
  4011. self.tangible = False
  4012. self.wall.move_x(self.x - self.wall.x)
  4013. self.tangible = tangible
  4014. self.x = self.wall.x
  4015. def move_y(self, move, absolute=False, do_events=True, exclude_events=()):
  4016. super().move_y(move, absolute=absolute, do_events=do_events,
  4017. exclude_events=exclude_events)
  4018. tangible = self.tangible
  4019. self.tangible = False
  4020. self.wall.move_y(self.y - self.wall.y)
  4021. self.tangible = tangible
  4022. self.y = self.wall.y
  4023. def touch(self, other):
  4024. if other.pickup(self):
  4025. self.active = False
  4026. self.wall.tangible = False
  4027. self.xvelocity = 0
  4028. self.yvelocity = 0
  4029. if other.action_pressed:
  4030. other.action()
  4031. def stop_left(self):
  4032. self.xvelocity = 0
  4033. def stop_right(self):
  4034. self.xvelocity = 0
  4035. def stop_up(self):
  4036. self.yvelocity = 0
  4037. def touch_hurt(self):
  4038. pass
  4039. def touch_death(self):
  4040. pass
  4041. def drop(self):
  4042. if self.parent is not None:
  4043. self.parent.drop_object()
  4044. self.active = True
  4045. self.wall.tangible = True
  4046. self.parent = None
  4047. def kick(self):
  4048. if self.parent is not None:
  4049. self.parent.kick_object()
  4050. self.active = True
  4051. self.wall.tangible = True
  4052. self.xvelocity = math.copysign(KICK_FORWARD_SPEED,
  4053. self.parent.image_xscale)
  4054. self.yvelocity = get_jump_speed(KICK_FORWARD_HEIGHT, Rock.gravity)
  4055. self.parent = None
  4056. def kick_up(self):
  4057. if self.parent is not None:
  4058. self.parent.kick_object()
  4059. self.active = True
  4060. self.wall.tangible = True
  4061. self.xvelocity = self.parent.xvelocity
  4062. self.yvelocity = get_jump_speed(KICK_UP_HEIGHT, Rock.gravity)
  4063. self.parent = None
  4064. def event_create(self):
  4065. super().event_create()
  4066. sge.game.current_room.add(self.wall)
  4067. def event_end_step(self, time_passed, delta_mult):
  4068. if (self.yvelocity >= 0 and
  4069. (self.get_bottom_touching_wall() or
  4070. self.get_bottom_touching_slope())):
  4071. self.xdeceleration = ROCK_FRICTION
  4072. else:
  4073. self.xdeceleration = 0
  4074. def event_destroy(self):
  4075. if self.wall is not None:
  4076. self.wall.destroy()
  4077. class FixedSpring(FallingObject):
  4078. active_range = ROCK_ACTIVE_RANGE
  4079. gravity = ROCK_GRAVITY
  4080. fall_speed = ROCK_FALL_SPEED
  4081. jump_height = SPRING_JUMP_HEIGHT
  4082. def set_sprite(self):
  4083. # This is a function because the names referenced don't exist
  4084. # when this class is defined, and these therefore can't be class
  4085. # attributes.
  4086. self.normal_sprite = fixed_spring_sprite
  4087. self.expand_sprite = fixed_spring_expand_sprite
  4088. def set_sound(self):
  4089. self.sound = spring_sound
  4090. def __init__(self, x, y, z=0, **kwargs):
  4091. self.set_sprite()
  4092. self.set_sound()
  4093. kwargs["sprite"] = self.normal_sprite
  4094. x += self.normal_sprite.origin_x
  4095. y += self.normal_sprite.origin_y
  4096. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  4097. def stomp(self, other):
  4098. if other is not self.parent and other.yvelocity > 0:
  4099. other.stomp_jump(self, self.jump_height)
  4100. play_sound(self.sound, self.x, self.y)
  4101. self.sprite = self.expand_sprite
  4102. self.image_index = 0
  4103. self.image_fps = None
  4104. def touch_hurt(self):
  4105. pass
  4106. def event_animation_end(self):
  4107. if self.sprite == self.expand_sprite:
  4108. self.sprite = self.normal_sprite
  4109. class Spring(FixedSpring, WinPuffObject):
  4110. active_range = ROCK_ACTIVE_RANGE
  4111. gravity = ROCK_GRAVITY
  4112. fall_speed = ROCK_FALL_SPEED
  4113. win_puff_score = 0
  4114. def set_sprite(self):
  4115. self.normal_sprite = spring_sprite
  4116. self.expand_sprite = spring_expand_sprite
  4117. def touch(self, other):
  4118. if other.pickup(self):
  4119. self.gravity = 0
  4120. if other.action_pressed:
  4121. other.action()
  4122. def drop(self):
  4123. if self.parent is not None:
  4124. self.parent.drop_object()
  4125. self.parent = None
  4126. self.gravity = self.__class__.gravity
  4127. def kick(self):
  4128. if self.parent is not None:
  4129. self.parent.kick_object()
  4130. self.xvelocity = math.copysign(KICK_FORWARD_SPEED,
  4131. self.parent.image_xscale)
  4132. self.yvelocity = get_jump_speed(KICK_FORWARD_HEIGHT, Rock.gravity)
  4133. self.gravity = self.__class__.gravity
  4134. self.parent = None
  4135. def kick_up(self):
  4136. if self.parent is not None:
  4137. self.parent.kick_object()
  4138. self.xvelocity = self.parent.xvelocity
  4139. self.yvelocity = get_jump_speed(KICK_UP_HEIGHT, Rock.gravity)
  4140. self.gravity = self.__class__.gravity
  4141. self.parent = None
  4142. def event_end_step(self, time_passed, delta_mult):
  4143. if (self.yvelocity >= 0 and
  4144. (self.get_bottom_touching_wall() or
  4145. self.get_bottom_touching_slope())):
  4146. self.xdeceleration = ROCK_FRICTION
  4147. else:
  4148. self.xdeceleration = 0
  4149. class RustySpring(Spring):
  4150. def set_sprite(self):
  4151. self.normal_sprite = rusty_spring_sprite
  4152. self.expand_sprite = rusty_spring_expand_sprite
  4153. def set_sound(self):
  4154. self.sound = rusty_spring_sound
  4155. def event_animation_end(self):
  4156. if self.sprite == self.expand_sprite:
  4157. Corpse.create(self.x, self.y, self.z,
  4158. sprite=rusty_spring_dead_sprite,
  4159. image_xscale=self.image_xscale,
  4160. image_yscale=self.image_yscale)
  4161. self.destroy()
  4162. class Lantern(FallingObject):
  4163. active_range = ROCK_ACTIVE_RANGE
  4164. gravity = ROCK_GRAVITY
  4165. fall_speed = ROCK_FALL_SPEED
  4166. def __init__(self, x, y, color="white", **kwargs):
  4167. kwargs["sprite"] = lantern_sprite
  4168. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4169. c = sge.gfx.Color(color)
  4170. if c.red < 255 or c.green < 255 or c.blue < 255:
  4171. self.light_sprite = light_sprite.copy()
  4172. blender = sge.gfx.Sprite(width=self.light_sprite.width,
  4173. height=self.light_sprite.height)
  4174. blender.draw_rectangle(0, 0, blender.width, blender.height, fill=c)
  4175. self.light_sprite.draw_sprite(blender, 0, 0, 0,
  4176. blend_mode=sge.BLEND_RGB_MULTIPLY)
  4177. else:
  4178. self.light_sprite = light_sprite
  4179. def touch(self, other):
  4180. if other.pickup(self):
  4181. self.gravity = 0
  4182. if other.action_pressed:
  4183. other.action()
  4184. def drop(self):
  4185. if self.parent is not None:
  4186. self.parent.drop_object()
  4187. self.parent = None
  4188. self.gravity = self.__class__.gravity
  4189. def kick(self):
  4190. if self.parent is not None:
  4191. self.parent.kick_object()
  4192. self.xvelocity = math.copysign(KICK_FORWARD_SPEED,
  4193. self.parent.image_xscale)
  4194. self.yvelocity = get_jump_speed(KICK_FORWARD_HEIGHT, Rock.gravity)
  4195. self.gravity = self.__class__.gravity
  4196. self.parent = None
  4197. def kick_up(self):
  4198. if self.parent is not None:
  4199. self.parent.kick_object()
  4200. self.xvelocity = self.parent.xvelocity
  4201. self.yvelocity = get_jump_speed(KICK_UP_HEIGHT, Rock.gravity)
  4202. self.gravity = self.__class__.gravity
  4203. self.parent = None
  4204. def project_light(self):
  4205. if self.parent is not None:
  4206. x = self.parent.x + self.image_origin_x
  4207. y = self.parent.y
  4208. if self.parent.image_xscale < 0:
  4209. x -= self.sprite.width
  4210. else:
  4211. x = self.x
  4212. y = self.y
  4213. xsge_lighting.project_light(x, y, self.light_sprite)
  4214. def event_end_step(self, time_passed, delta_mult):
  4215. if (self.yvelocity >= 0 and
  4216. (self.get_bottom_touching_wall() or
  4217. self.get_bottom_touching_slope())):
  4218. self.xdeceleration = ROCK_FRICTION
  4219. else:
  4220. self.xdeceleration = 0
  4221. class TimelineSwitcher(InteractiveObject):
  4222. def __init__(self, x, y, timeline=None, **kwargs):
  4223. self.timeline = timeline
  4224. kwargs["visible"] = False
  4225. kwargs["checks_collisions"] = False
  4226. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4227. def touch(self, other):
  4228. sge.game.current_room.load_timeline(self.timeline)
  4229. self.destroy()
  4230. class Iceblock(xsge_physics.Solid):
  4231. def __init__(self, x, y, **kwargs):
  4232. kwargs["checks_collisions"] = False
  4233. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4234. def burn(self):
  4235. play_sound(sizzle_sound, self.x, self.y)
  4236. Smoke.create(self.x, self.y, self.z, sprite=iceblock_melt_sprite)
  4237. self.destroy()
  4238. class BossBlock(InteractiveObject):
  4239. never_active = True
  4240. never_tangible = True
  4241. def __init__(self, x, y, ID=None, **kwargs):
  4242. self.ID = ID
  4243. kwargs["visible"] = False
  4244. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4245. def event_create(self):
  4246. super().event_create()
  4247. sge.game.current_room.add_timeline_object(self)
  4248. def activate(self):
  4249. self.child = xsge_physics.Solid.create(
  4250. self.x, self.y, self.z, sprite=boss_block_sprite)
  4251. self.child.x += self.child.image_origin_x
  4252. self.child.y += self.child.image_origin_y
  4253. Smoke.create(self.child.x, self.child.y, z=(self.child.z + 0.5),
  4254. sprite=item_spawn_cloud_sprite)
  4255. play_sound(pop_sound, self.x, self.y)
  4256. def deactivate(self):
  4257. if self.child is not None:
  4258. Smoke.create(self.child.x, self.child.y, z=self.child.z,
  4259. sprite=smoke_plume_sprite)
  4260. self.child.destroy()
  4261. self.child = None
  4262. play_sound(pop_sound, self.x, self.y)
  4263. def update_active(self):
  4264. pass
  4265. class HittableBlock(sge.dsp.Object):
  4266. hit_sprite = None
  4267. hit_obj = None
  4268. def __init__(self, x, y, **kwargs):
  4269. kwargs["checks_collisions"] = False
  4270. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4271. def event_destroy(self):
  4272. if self.hit_obj is not None:
  4273. self.hit_obj.destroy()
  4274. def event_begin_step(self, time_passed, delta_mult):
  4275. if self.hit_obj is not None:
  4276. if self.hit_obj.y > self.y:
  4277. self.hit_obj.destroy()
  4278. self.hit_obj = None
  4279. self.visible = True
  4280. self.event_hit_end()
  4281. def hit(self, other):
  4282. play_sound(brick_sound, self.x, self.y)
  4283. if self.hit_obj is not None:
  4284. self.hit_obj.destroy()
  4285. self.hit_obj = None
  4286. self.visible = True
  4287. self.event_hit_end()
  4288. if isinstance(self, xsge_physics.SolidTop):
  4289. for obj in self.collision(InteractiveObject, y=(self.y - 1)):
  4290. if obj.knockable:
  4291. obj.knock()
  4292. if self in sge.game.current_room.objects:
  4293. if self.hit_sprite is not None:
  4294. s = self.hit_sprite
  4295. else:
  4296. s = self.sprite
  4297. self.visible = False
  4298. self.hit_obj = sge.dsp.Object.create(
  4299. self.x, self.y, self.z, sprite=s, tangible=False,
  4300. yvelocity=get_jump_speed(BLOCK_HIT_HEIGHT),
  4301. yacceleration=GRAVITY, image_index=self.image_index,
  4302. image_origin_x=self.image_origin_x,
  4303. image_origin_y=self.image_origin_y,
  4304. image_fps=self.image_fps, image_xscale=self.image_xscale,
  4305. image_yscale=self.image_yscale,
  4306. image_rotation=self.image_rotation)
  4307. self.event_hit(other)
  4308. def event_hit(self, other):
  4309. pass
  4310. def event_hit_end(self):
  4311. pass
  4312. class Brick(HittableBlock, xsge_physics.Solid):
  4313. def event_hit(self, other):
  4314. for i in range(BRICK_SHARD_NUM):
  4315. xv = random.uniform(-BRICK_SHARD_SPEED, BRICK_SHARD_SPEED)
  4316. yv = get_jump_speed(BRICK_SHARD_HEIGHT, BRICK_SHARD_GRAVITY)
  4317. shard = DeadMan.create(
  4318. self.x, self.y, self.z, sprite=brick_shard_sprite,
  4319. xvelocity=xv, yvelocity=yv)
  4320. shard.gravity = BRICK_SHARD_GRAVITY
  4321. shard.fall_speed = BRICK_SHARD_FALL_SPEED
  4322. sge.game.current_room.add_points(10)
  4323. self.destroy()
  4324. class CoinBrick(Brick):
  4325. coins = COINBRICK_COINS
  4326. def __init__(self, x, y, disguised=False, **kwargs):
  4327. if not disguised:
  4328. kwargs["sprite"] = brick_sprite
  4329. elif kwargs.get("sprite") is None:
  4330. kwargs["bbox_x"] = brick_sprite.bbox_x
  4331. kwargs["bbox_y"] = brick_sprite.bbox_y
  4332. kwargs["bbox_width"] = brick_sprite.bbox_width
  4333. kwargs["bbox_height"] = brick_sprite.bbox_height
  4334. kwargs["checks_collisions"] = False
  4335. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4336. def event_alarm(self, alarm_id):
  4337. if alarm_id == "decay":
  4338. self.coins -= 1
  4339. self.alarms["decay"] = COINBRICK_DECAY_TIME
  4340. def event_hit(self, other):
  4341. if self.coins > 0:
  4342. self.coins -= 1
  4343. CoinCollect.create(self.x, self.y, z=(self.z + 0.5))
  4344. if other is not None:
  4345. other.coins += 1
  4346. if "decay" not in self.alarms:
  4347. self.alarms["decay"] = COINBRICK_DECAY_TIME
  4348. else:
  4349. super().event_hit(other)
  4350. class EmptyBlock(HittableBlock, xsge_physics.Solid):
  4351. pass
  4352. class ItemBlock(HittableBlock, xsge_physics.Solid):
  4353. def __init__(self, x, y, item=None, disguised=False, **kwargs):
  4354. if not disguised:
  4355. kwargs["sprite"] = bonus_full_sprite
  4356. elif kwargs.get("sprite") is None:
  4357. kwargs["bbox_x"] = bonus_full_sprite.bbox_x
  4358. kwargs["bbox_y"] = bonus_full_sprite.bbox_y
  4359. kwargs["bbox_width"] = bonus_full_sprite.bbox_width
  4360. kwargs["bbox_height"] = bonus_full_sprite.bbox_height
  4361. kwargs["checks_collisions"] = False
  4362. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4363. self.hit_sprite = bonus_empty_sprite
  4364. self.item = item
  4365. def event_hit(self, other):
  4366. if self.item and self.item in TYPES:
  4367. obj = TYPES[self.item].create(self.x, self.y, z=self.z)
  4368. if obj.sprite is not None and self.sprite is not None:
  4369. obj.x = (self.bbox_left + self.sprite.width / 2 +
  4370. obj.image_origin_x - obj.sprite.width / 2)
  4371. else:
  4372. obj.bbox_left = self.bbox_left
  4373. obj.bbox_bottom = self.bbox_top
  4374. Smoke.create(obj.x, obj.y, z=(obj.z + 0.5),
  4375. sprite=item_spawn_cloud_sprite)
  4376. play_sound(find_powerup_sound, self.x, self.y)
  4377. else:
  4378. CoinCollect.create(self.x, self.y, z=(self.z + 0.5))
  4379. if other is not None:
  4380. other.coins += 1
  4381. def event_hit_end(self):
  4382. EmptyBlock.create(self.x, self.y, z=self.z, sprite=bonus_empty_sprite)
  4383. self.destroy()
  4384. class HiddenItemBlock(HittableBlock):
  4385. def __init__(self, x, y, item=None, **kwargs):
  4386. kwargs["sprite"] = None
  4387. kwargs["bbox_x"] = bonus_full_sprite.bbox_x
  4388. kwargs["bbox_y"] = bonus_full_sprite.bbox_y
  4389. kwargs["bbox_width"] = bonus_full_sprite.bbox_width
  4390. kwargs["bbox_height"] = bonus_full_sprite.bbox_height
  4391. kwargs["checks_collisions"] = False
  4392. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4393. self.item = item
  4394. def hit(self, other):
  4395. ib = ItemBlock.create(self.x, self.y, item=self.item, z=self.z)
  4396. ib.hit(other)
  4397. self.destroy()
  4398. class InfoBlock(HittableBlock, xsge_physics.Solid):
  4399. def __init__(self, x, y, text="(null)", **kwargs):
  4400. super().__init__(x, y, **kwargs)
  4401. self.text = text
  4402. def event_hit_end(self):
  4403. DialogBox(gui_handler, _(self.text), self.sprite).show()
  4404. class ThinIce(xsge_physics.Solid):
  4405. def __init__(self, x, y, z=0, permanent=False, **kwargs):
  4406. kwargs["sprite"] = thin_ice_sprite
  4407. kwargs["checks_collisions"] = False
  4408. kwargs["image_fps"] = 0
  4409. sge.dsp.Object.__init__(self, x, y, z, **kwargs)
  4410. self.permanent = permanent
  4411. self.crack_time = 0
  4412. self.freeze_time = 0
  4413. def burn(self):
  4414. self.crack()
  4415. def freeze(self):
  4416. if self.image_index > 0:
  4417. self.image_index -= 1
  4418. def event_step(self, time_passed, delta_mult):
  4419. if self.sprite is thin_ice_sprite:
  4420. players = self.collision(Player, y=(self.y - 1))
  4421. if players:
  4422. if not GOD:
  4423. for player in players:
  4424. self.crack_time += delta_mult
  4425. while self.crack_time >= ICE_CRACK_TIME:
  4426. self.crack_time -= ICE_CRACK_TIME
  4427. self.crack()
  4428. elif not self.permanent:
  4429. if self.image_index > 0:
  4430. rfa = delta_mult * ICE_REFREEZE_RATE
  4431. self.crack_time -= rfa
  4432. self.rfa = max(0, -self.crack_time)
  4433. self.crack_time = max(0, self.crack_time)
  4434. self.freeze_time += rfa
  4435. while self.freeze_time >= ICE_CRACK_TIME:
  4436. self.freeze_time -= ICE_CRACK_TIME
  4437. if self.image_index > 0:
  4438. self.image_index -= 1
  4439. else:
  4440. self.crack_time -= delta_mult * ICE_REFREEZE_RATE
  4441. self.crack_time = max(0, self.crack_time)
  4442. def event_animation_end(self):
  4443. self.destroy()
  4444. def shatter(self):
  4445. if self.sprite != thin_ice_break_sprite:
  4446. self.sprite = thin_ice_break_sprite
  4447. self.image_index = 0
  4448. self.image_fps = None
  4449. play_sound(ice_shatter_sound, self.x, self.y)
  4450. def crack(self):
  4451. if self.image_index + 1 < self.sprite.frames:
  4452. play_sound(random.choice(ice_crack_sounds), self.x, self.y)
  4453. self.image_index += 1
  4454. self.freeze_time = 0
  4455. else:
  4456. self.shatter()
  4457. class Lava(xsge_tiled.Decoration):
  4458. def event_create(self):
  4459. self.sprite = lava_body_sprite
  4460. self.image_fps = None
  4461. class LavaSurface(xsge_tiled.Decoration):
  4462. def event_create(self):
  4463. self.sprite = lava_surface_sprite
  4464. self.image_fps = None
  4465. class Goal(xsge_tiled.Decoration):
  4466. def event_create(self):
  4467. self.sprite = goal_sprite
  4468. self.image_fps = None
  4469. class GoalTop(xsge_tiled.Decoration):
  4470. def event_create(self):
  4471. self.sprite = goal_top_sprite
  4472. self.image_fps = None
  4473. class Coin(sge.dsp.Object):
  4474. def __init__(self, x, y, **kwargs):
  4475. kwargs["sprite"] = coin_sprite
  4476. kwargs["checks_collisions"] = False
  4477. super().__init__(x, y, **kwargs)
  4478. def activate(self):
  4479. self.event_step(0, 0)
  4480. def event_step(self, time_passed, delta_mult):
  4481. self.image_index = coin_animation.image_index
  4482. def event_collision(self, other, xdirection, ydirection):
  4483. if isinstance(other, Player) and self in sge.game.current_room.objects:
  4484. CoinCollect.create(self.x, self.y, z=self.z,
  4485. image_index=self.image_index)
  4486. self.destroy()
  4487. other.coins += 1
  4488. class CoinCollect(sge.dsp.Object):
  4489. def __init__(self, x, y, **kwargs):
  4490. kwargs["sprite"] = coin_sprite
  4491. kwargs["tangible"] = False
  4492. sge.dsp.Object.__init__(self, x, y, **kwargs)
  4493. def event_create(self):
  4494. play_sound(coin_sound, self.x, self.y)
  4495. sge.game.current_room.add_points(COIN_POINTS)
  4496. self.alarms["destroy"] = COIN_COLLECT_TIME
  4497. self.yvelocity = -COIN_COLLECT_SPEED
  4498. def event_step(self, time_passed, delta_mult):
  4499. T = self.alarms.get("destroy", COIN_COLLECT_TIME)
  4500. self.image_alpha = 255 * (T / COIN_COLLECT_TIME)
  4501. def event_alarm(self, alarm_id):
  4502. if alarm_id == "destroy":
  4503. self.destroy()
  4504. class Spawn(sge.dsp.Object):
  4505. def __init__(self, x, y, spawn_id=None, **kwargs):
  4506. kwargs["visible"] = False
  4507. kwargs["tangible"] = False
  4508. super().__init__(x, y, **kwargs)
  4509. self.spawn_id = spawn_id
  4510. class Checkpoint(InteractiveObject):
  4511. def __init__(self, x, y, dest=None, **kwargs):
  4512. kwargs["visible"] = False
  4513. super().__init__(x, y, **kwargs)
  4514. self.dest = dest
  4515. def event_create(self):
  4516. if self.dest is not None:
  4517. if ":" not in self.dest:
  4518. self.dest = "{}:{}".format(sge.game.current_room.fname,
  4519. self.dest)
  4520. self.reset()
  4521. def reset(self):
  4522. pass
  4523. def touch(self, other):
  4524. global current_checkpoints
  4525. current_checkpoints[main_area] = self.dest
  4526. for obj in sge.game.current_room.objects:
  4527. if isinstance(obj, Checkpoint):
  4528. obj.reset()
  4529. class Bell(Checkpoint):
  4530. def __init__(self, x, y, dest=None, **kwargs):
  4531. kwargs["sprite"] = bell_sprite
  4532. InteractiveObject.__init__(self, x, y, **kwargs)
  4533. self.dest = dest
  4534. def reset(self):
  4535. if current_checkpoints.get(main_area) == self.dest:
  4536. self.image_fps = None
  4537. else:
  4538. self.image_fps = 0
  4539. self.image_index = 0
  4540. def touch(self, other):
  4541. super().touch(other)
  4542. play_sound(bell_sound, self.x, self.y)
  4543. class Door(sge.dsp.Object):
  4544. def __init__(self, x, y, dest=None, spawn_id=None, **kwargs):
  4545. y += 64
  4546. kwargs["sprite"] = door_sprite
  4547. kwargs["checks_collisions"] = False
  4548. kwargs["image_fps"] = 0
  4549. super().__init__(x, y, **kwargs)
  4550. self.dest = dest
  4551. self.spawn_id = spawn_id
  4552. self.occupant = None
  4553. def warp(self, other):
  4554. if self.occupant is None and self.image_index == 0:
  4555. self.occupant = other
  4556. play_sound(door_sound, self.x, self.y)
  4557. self.image_fps = self.sprite.fps
  4558. other.visible = False
  4559. other.tangible = False
  4560. other.warping = True
  4561. other.xvelocity = 0
  4562. other.yvelocity = 0
  4563. other.xacceleration = 0
  4564. other.yacceleration = 0
  4565. other.xdeceleration = 0
  4566. other.ydeceleration = 0
  4567. def warp_end(self):
  4568. warp(self.dest)
  4569. def event_step(self, time_passed, delta_mult):
  4570. if self.occupant is not None:
  4571. s = get_scaled_copy(self.occupant)
  4572. if self.image_fps > 0:
  4573. sge.game.current_room.project_sprite(
  4574. door_back_sprite, 0, self.x, self.y, self.z - 0.5)
  4575. sge.game.current_room.project_sprite(s, 0, self.x, self.y,
  4576. self.occupant.z)
  4577. else:
  4578. dbs = door_back_sprite.copy()
  4579. dbs.draw_sprite(s, 0, dbs.origin_x, dbs.origin_y)
  4580. sge.game.current_room.project_sprite(dbs, 0, self.x, self.y,
  4581. self.z - 0.5)
  4582. elif self.image_index != 0:
  4583. sge.game.current_room.project_sprite(door_back_sprite, 0, self.x,
  4584. self.y, self.z - 0.5)
  4585. def event_animation_end(self):
  4586. if self.image_fps > 0:
  4587. if self.dest and (':' in self.dest or self.dest == "__map__"):
  4588. self.image_fps = -self.image_fps
  4589. self.image_index = self.sprite.frames - 1
  4590. else:
  4591. self.image_fps = 0
  4592. self.image_index = self.sprite.frames - 1
  4593. self.occupant.visible = True
  4594. self.occupant.tangible = True
  4595. self.occupant.warping = False
  4596. self.occupant.xvelocity = 0
  4597. self.occupant.yvelocity = 0
  4598. self.occupant = None
  4599. elif self.image_fps < 0:
  4600. play_sound(door_shut_sound, self.x, self.y)
  4601. self.image_fps = 0
  4602. self.image_index = 0
  4603. self.occupant = None
  4604. self.warp_end()
  4605. class WarpSpawn(xsge_path.Path):
  4606. silent = False
  4607. def __init__(self, x, y, points=(), dest=None, spawn_id=None, **kwargs):
  4608. super().__init__(x, y, points=points, **kwargs)
  4609. self.dest = dest
  4610. self.spawn_id = spawn_id
  4611. self.direction = None
  4612. self.end_direction = None
  4613. self.warps_out = []
  4614. if points:
  4615. xm, ym = points[0]
  4616. if abs(xm) > abs(ym):
  4617. self.direction = "right" if xm > 0 else "left"
  4618. elif ym:
  4619. self.direction = "down" if ym > 0 else "up"
  4620. else:
  4621. warnings.warn(f"Warp at position ({x}, {y}) has no direction")
  4622. if len(points) >= 2:
  4623. x1, y1 = points[-2]
  4624. x2, y2 = points[-1]
  4625. xm = x2 - x1
  4626. ym = y2 - y1
  4627. if abs(xm) > abs(ym):
  4628. self.end_direction = "right" if xm > 0 else "left"
  4629. elif ym:
  4630. self.end_direction = "down" if ym > 0 else "up"
  4631. else:
  4632. warnings.warn(f"Warp at position ({x}, {y}) has no end "
  4633. "direction")
  4634. else:
  4635. self.end_direction = self.direction
  4636. def event_step(self, time_passed, delta_mult):
  4637. super().event_step(time_passed, delta_mult)
  4638. x, y = self.points[-1]
  4639. x += self.x
  4640. y += self.y
  4641. finished = []
  4642. for obj in self.warps_out:
  4643. left_edge = obj.x - obj.image_origin_x
  4644. top_edge = obj.y - obj.image_origin_y
  4645. if self.end_direction == "left":
  4646. if obj.bbox_right <= x:
  4647. obj.bbox_right = x
  4648. finished.append(obj)
  4649. else:
  4650. warp_sprite = get_scaled_copy(obj)
  4651. warp_sprite.draw_erase(
  4652. math.ceil(x - left_edge), 0, warp_sprite.width,
  4653. warp_sprite.height)
  4654. sge.game.current_room.project_sprite(
  4655. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4656. elif self.end_direction == "right":
  4657. if obj.bbox_left >= x:
  4658. obj.bbox_left = x
  4659. finished.append(obj)
  4660. else:
  4661. warp_sprite = get_scaled_copy(obj)
  4662. warp_sprite.draw_erase(0, 0, math.floor(x - left_edge),
  4663. warp_sprite.height)
  4664. sge.game.current_room.project_sprite(
  4665. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4666. elif self.end_direction == "up":
  4667. if obj.bbox_bottom <= y:
  4668. obj.bbox_bottom = y
  4669. finished.append(obj)
  4670. else:
  4671. warp_sprite = get_scaled_copy(obj)
  4672. warp_sprite.draw_erase(
  4673. 0, math.ceil(y - top_edge), warp_sprite.width,
  4674. warp_sprite.height)
  4675. sge.game.current_room.project_sprite(
  4676. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4677. elif self.end_direction == "down":
  4678. if obj.bbox_top >= y:
  4679. obj.bbox_top = y
  4680. finished.append(obj)
  4681. else:
  4682. warp_sprite = get_scaled_copy(obj)
  4683. warp_sprite.draw_erase(0, 0, warp_sprite.width,
  4684. math.floor(y - top_edge))
  4685. sge.game.current_room.project_sprite(
  4686. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4687. for obj in finished:
  4688. obj.visible = True
  4689. obj.tangible = True
  4690. obj.warping = False
  4691. obj.speed = 0
  4692. self.warps_out.remove(obj)
  4693. def event_follow_end(self, obj):
  4694. global level_timers
  4695. global score
  4696. if self.dest and (':' in self.dest or self.dest == "__map__"):
  4697. warp(self.dest)
  4698. else:
  4699. if not self.silent:
  4700. play_sound(pipe_sound, obj.x, obj.y)
  4701. self.warps_out.append(obj)
  4702. x, y = self.points[-1]
  4703. x += self.x
  4704. y += self.y
  4705. if self.end_direction == "left":
  4706. obj.x = x + obj.sprite.origin_x
  4707. obj.y = y
  4708. obj.move_direction = 180
  4709. elif self.end_direction == "right":
  4710. obj.x = x + obj.sprite.origin_x - obj.sprite.width
  4711. obj.y = y
  4712. obj.move_direction = 0
  4713. elif self.end_direction == "up":
  4714. obj.x = x
  4715. obj.y = y + obj.sprite.origin_y
  4716. obj.move_direction = 270
  4717. elif self.end_direction == "down":
  4718. obj.x = x
  4719. obj.y = y + obj.sprite.origin_y - obj.sprite.height
  4720. obj.move_direction = 90
  4721. obj.speed = WARP_EDGE_SPEED
  4722. obj.xacceleration = 0
  4723. obj.yacceleration = 0
  4724. obj.xdeceleration = 0
  4725. obj.ydeceleration = 0
  4726. class Warp(WarpSpawn):
  4727. def __init__(self, x, y, **kwargs):
  4728. super().__init__(x, y, **kwargs)
  4729. self.warps_in = []
  4730. def warp(self, other):
  4731. if not self.silent:
  4732. play_sound(pipe_sound, other.x, other.y)
  4733. self.warps_in.append(other)
  4734. if getattr(other, "held_object") is not None:
  4735. other.held_object.drop()
  4736. other.visible = False
  4737. other.tangible = False
  4738. other.warping = True
  4739. other.move_direction = {"right": 0, "up": 270, "left": 180,
  4740. "down": 90}.get(self.direction, 0)
  4741. other.speed = WARP_EDGE_SPEED
  4742. other.xacceleration = 0
  4743. other.yacceleration = 0
  4744. other.xdeceleration = 0
  4745. other.ydeceleration = 0
  4746. def event_create(self):
  4747. if self not in sge.game.current_room.warps:
  4748. sge.game.current_room.warps.append(self)
  4749. def event_end_step(self, time_passed, delta_mult):
  4750. super().event_step(time_passed, delta_mult)
  4751. finished = []
  4752. for obj in self.warps_in:
  4753. left_edge = obj.x - obj.image_origin_x
  4754. top_edge = obj.y - obj.image_origin_y
  4755. if self.direction == "left":
  4756. if obj.x <= self.x + obj.image_origin_x - obj.sprite.width:
  4757. finished.append(obj)
  4758. else:
  4759. warp_sprite = get_scaled_copy(obj)
  4760. warp_sprite.draw_erase(
  4761. 0, 0, math.floor(self.x - left_edge),
  4762. warp_sprite.height)
  4763. sge.game.current_room.project_sprite(
  4764. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4765. elif self.direction == "right":
  4766. if obj.x >= self.x + obj.image_origin_x:
  4767. finished.append(obj)
  4768. else:
  4769. warp_sprite = get_scaled_copy(obj)
  4770. warp_sprite.draw_erase(
  4771. math.ceil(self.x - left_edge), 0, warp_sprite.width,
  4772. warp_sprite.height)
  4773. sge.game.current_room.project_sprite(
  4774. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4775. elif self.direction == "up":
  4776. if obj.y <= self.y + obj.image_origin_y - obj.sprite.height:
  4777. finished.append(obj)
  4778. else:
  4779. warp_sprite = get_scaled_copy(obj)
  4780. warp_sprite.draw_erase(0, 0, warp_sprite.width,
  4781. math.floor(self.y - top_edge))
  4782. sge.game.current_room.project_sprite(
  4783. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4784. elif self.direction == "down":
  4785. if obj.y >= self.y + obj.image_origin_y:
  4786. finished.append(obj)
  4787. else:
  4788. warp_sprite = get_scaled_copy(obj)
  4789. warp_sprite.draw_erase(
  4790. 0, math.ceil(self.y - top_edge), warp_sprite.width,
  4791. warp_sprite.height)
  4792. sge.game.current_room.project_sprite(
  4793. warp_sprite, obj.image_index, obj.x, obj.y, self.z)
  4794. for obj in finished:
  4795. obj.x = self.x
  4796. obj.y = self.y
  4797. self.follow_start(obj, WARP_SPEED)
  4798. self.warps_in.remove(obj)
  4799. def event_destroy(self):
  4800. while self in sge.game.current_room.warps:
  4801. sge.game.current_room.warps.remove(self)
  4802. class ObjectWarpSpawn(WarpSpawn):
  4803. def __init__(self, x, y, points=(), cls=None, interval=180, limit=None,
  4804. silent=False, **kwargs):
  4805. self.cls = TYPES.get(cls)
  4806. self.kwargs = kwargs
  4807. self.interval = interval
  4808. self.limit = limit
  4809. self.silent = silent
  4810. self.__steps_passed = interval
  4811. self.__objects = []
  4812. super().__init__(x, y, points=points)
  4813. def event_begin_step(self, time_passed, delta_mult):
  4814. in_view = False
  4815. for view in sge.game.current_room.views:
  4816. if (self.x <= view.x + view.width and self.x >= view.x and
  4817. self.y <= view.y + view.height and self.y >= view.y):
  4818. in_view = True
  4819. break
  4820. if in_view and self.cls is not None:
  4821. self.__steps_passed += delta_mult
  4822. self.__objects = [ref for ref in self.__objects
  4823. if (ref() is not None and
  4824. ref() in sge.game.current_room.objects)]
  4825. if self.limit and len(self.__objects) >= self.limit:
  4826. self.__steps_passed = 0
  4827. while self.__steps_passed >= self.interval:
  4828. self.__steps_passed -= self.interval
  4829. obj = self.cls.create(self.x, self.y, **self.kwargs)
  4830. obj.activate()
  4831. obj.warping = True
  4832. obj.visible = False
  4833. obj.tangible = False
  4834. self.follow_start(obj, WARP_SPEED)
  4835. self.__objects.append(weakref.ref(obj))
  4836. class MovingObjectPath(xsge_path.PathLink):
  4837. cls = None
  4838. default_speed = ENEMY_WALK_SPEED
  4839. default_accel = None
  4840. default_decel = None
  4841. default_loop = None
  4842. auto_follow = True
  4843. def __init__(self, x, y, path_speed=None, path_accel=None, path_decel=None,
  4844. path_loop=None, path_id=None, prime=False, parent=None,
  4845. **kwargs):
  4846. if path_speed is None:
  4847. path_speed = self.default_speed
  4848. if path_accel is None:
  4849. path_accel = self.default_accel
  4850. if path_decel is None:
  4851. path_decel = self.default_decel
  4852. if path_loop is None:
  4853. path_loop = self.default_loop
  4854. self.path_speed = path_speed
  4855. self.path_accel = path_accel if path_accel != -1 else None
  4856. self.path_decel = path_decel if path_decel != -1 else None
  4857. self.path_loop = path_loop if path_loop != -1 else None
  4858. self.path_id = path_id
  4859. self.prime = prime
  4860. self.parent = parent
  4861. self.obj = lambda: None
  4862. super().__init__(x, y, **kwargs)
  4863. def event_create(self):
  4864. if self.parent is not None:
  4865. for obj in sge.game.current_room.objects:
  4866. if (isinstance(obj, self.__class__) and
  4867. obj.path_id == self.parent):
  4868. obj.next_path = self
  4869. obj.next_speed = self.path_speed
  4870. obj.next_accel = self.path_accel
  4871. obj.next_decel = self.path_decel
  4872. obj.next_loop = self.path_loop
  4873. break
  4874. else:
  4875. self.prime = True
  4876. if self.prime and self.cls in TYPES:
  4877. obj = TYPES[self.cls].create(self.x, self.y, z=self.z)
  4878. self.obj = weakref.ref(obj)
  4879. if self.auto_follow:
  4880. self.follow_start(obj, self.path_speed, accel=self.path_accel,
  4881. decel=self.path_decel, loop=self.path_loop)
  4882. class MovingPlatformPath(MovingObjectPath):
  4883. cls = "moving_platform"
  4884. default_speed = 3
  4885. default_accel = 0.02
  4886. default_decel = 0.02
  4887. def event_create(self):
  4888. super().event_create()
  4889. obj = self.obj()
  4890. if obj:
  4891. obj.path = self
  4892. def follow_start(self, obj, *args, **kwargs):
  4893. super().follow_start(obj, *args, **kwargs)
  4894. obj.following = True
  4895. def event_follow_end(self, obj):
  4896. obj.following = False
  4897. obj.speed = 0
  4898. obj.x = self.x + self.points[-1][0]
  4899. obj.y = self.y + self.points[-1][1]
  4900. class TriggeredMovingPlatformPath(MovingPlatformPath):
  4901. default_speed = 2
  4902. default_accel = None
  4903. default_decel = None
  4904. auto_follow = False
  4905. followed = False
  4906. class FlyingSnowballPath(MovingObjectPath):
  4907. cls = "flying_snowball"
  4908. default_speed = 2
  4909. default_accel = 0.02
  4910. default_decel = 0.02
  4911. class FlyingSpikyPath(MovingObjectPath):
  4912. cls = "flying_spiky"
  4913. default_speed = 2
  4914. default_accel = 0.02
  4915. default_decel = 0.02
  4916. class CircoflamePath(xsge_path.Path):
  4917. def __init__(self, x, y, z=0, points=(), rvelocity=2):
  4918. self.rvelocity = rvelocity
  4919. x += TILE_SIZE / 2
  4920. y += TILE_SIZE / 2
  4921. super().__init__(x, y, z=z, points=points)
  4922. def event_create(self):
  4923. if self.points:
  4924. fx, fy = self.points[0]
  4925. radius = math.hypot(fx, fy)
  4926. pos = math.degrees(math.atan2(fy, fx))
  4927. CircoflameCenter.create(self.x, self.y, z=self.z, radius=radius,
  4928. pos=pos, rvelocity=self.rvelocity)
  4929. self.destroy()
  4930. class MapPlayer(sge.dsp.Object):
  4931. moving = False
  4932. def _follow_path(self, space, path):
  4933. if path is not None and not self.moving:
  4934. if path.points:
  4935. x, y = path.points[-1]
  4936. else:
  4937. x = 0
  4938. y = 0
  4939. target_space = MapSpace.get_at(path.x + x, path.y + y)
  4940. if target_space is not None:
  4941. if space.cleared or target_space.cleared:
  4942. self.moving = True
  4943. path.follow_start(self, MAP_SPEED)
  4944. else:
  4945. print("Space at position ({}, {}) doesn't exist!".format(
  4946. path.x + x, path.y + y))
  4947. def move_left(self):
  4948. space = MapSpace.get_at(self.x, self.y)
  4949. if space is not None:
  4950. path = space.get_left_exit()
  4951. self._follow_path(space, path)
  4952. def move_right(self):
  4953. space = MapSpace.get_at(self.x, self.y)
  4954. if space is not None:
  4955. path = space.get_right_exit()
  4956. self._follow_path(space, path)
  4957. def move_up(self):
  4958. space = MapSpace.get_at(self.x, self.y)
  4959. if space is not None:
  4960. path = space.get_up_exit()
  4961. self._follow_path(space, path)
  4962. def move_down(self):
  4963. space = MapSpace.get_at(self.x, self.y)
  4964. if space is not None:
  4965. path = space.get_down_exit()
  4966. self._follow_path(space, path)
  4967. def move_forward(self):
  4968. space = MapSpace.get_at(self.x, self.y)
  4969. if space is not None:
  4970. paths = []
  4971. for path in space.get_exits():
  4972. if path is not None and path.forward:
  4973. paths.append(path)
  4974. if len(paths) == 1:
  4975. self._follow_path(space, paths[0])
  4976. def start_level(self):
  4977. space = MapSpace.get_at(self.x, self.y)
  4978. if space is not None:
  4979. space.start_level()
  4980. def event_create(self):
  4981. global worldmap_entry_space
  4982. start_space = MapSpace.get_at(self.x, self.y)
  4983. if start_space is None:
  4984. start_space = MapSpace.create(self.x, self.y)
  4985. if worldmap_entry_space is None:
  4986. worldmap_entry_space = start_space.ID
  4987. if current_worldmap_space is not None:
  4988. for obj in sge.game.current_room.objects:
  4989. if (isinstance(obj, MapSpace) and
  4990. obj.ID == current_worldmap_space):
  4991. self.x = obj.x
  4992. self.y = obj.y
  4993. def event_step(self, time_passed, delta_mult):
  4994. global current_areas
  4995. room = sge.game.current_room
  4996. space = MapSpace.get_at(self.x, self.y)
  4997. if self.moving:
  4998. self.sprite = worldmap_tux_walk_sprite
  4999. else:
  5000. self.sprite = worldmap_tux_sprite
  5001. self.image_fps = self.sprite.fps
  5002. if space is not None:
  5003. if space.level and space.level not in level_names:
  5004. fname = os.path.join(DATA, "levels", space.level)
  5005. try:
  5006. with open(fname) as f:
  5007. data = json.load(f)
  5008. except (OSError, ValueError) as e:
  5009. play_sound(error_sound)
  5010. show_error(_("An error occurred when trying to load the "
  5011. "level:\n\n{}").format(e))
  5012. rush_save()
  5013. sge.game.start_room.start()
  5014. else:
  5015. room_properties = xsge_tiled.t_get_properties(
  5016. data.get("properties", {}))
  5017. level_names[space.level] = room_properties.get("name")
  5018. room.level_text = level_names.get(space.level)
  5019. room.level_tuxdoll_available = space.level in tuxdolls_available
  5020. room.level_tuxdoll_found = space.level in tuxdolls_found
  5021. key_controls = [left_key, right_key, up_key, down_key, sneak_key]
  5022. js_controls = [left_js, right_js, up_js, down_js, sneak_js]
  5023. states = [0 for i in key_controls]
  5024. for i in range(len(key_controls)):
  5025. for player in range(len(key_controls[i])):
  5026. for choice in key_controls[i][player]:
  5027. value = sge.keyboard.get_pressed(choice)
  5028. states[i] = max(states[i], value)
  5029. for i in range(len(js_controls)):
  5030. for player in range(len(key_controls[i])):
  5031. for choice in js_controls[i][player]:
  5032. j, t, c = choice
  5033. value = min(sge.joystick.get_value(j, t, c), 1)
  5034. if value >= joystick_threshold:
  5035. states[i] = max(states[i], value)
  5036. left_pressed = states[0]
  5037. right_pressed = states[1]
  5038. up_pressed = states[2]
  5039. down_pressed = states[3]
  5040. sneak_pressed = states[4]
  5041. if sneak_pressed:
  5042. self.move_forward()
  5043. if right_pressed - left_pressed > 0:
  5044. self.move_right()
  5045. elif left_pressed - right_pressed > 0:
  5046. self.move_left()
  5047. if down_pressed - up_pressed > 0:
  5048. self.move_down()
  5049. elif up_pressed - down_pressed > 0:
  5050. self.move_up()
  5051. if room.views:
  5052. view = room.views[0]
  5053. view.x = self.x - view.width / 2 + self.sprite.width / 2
  5054. view.y = self.y - view.height / 2 + self.sprite.height / 2
  5055. def event_key_press(self, key, char):
  5056. if (key in itertools.chain.from_iterable(jump_key) or
  5057. key in itertools.chain.from_iterable(action_key) or
  5058. key in itertools.chain.from_iterable(pause_key)):
  5059. self.start_level()
  5060. elif key == "escape" or key in itertools.chain.from_iterable(menu_key):
  5061. sge.game.current_room.show_menu()
  5062. def event_joystick(self, js_name, js_id, input_type, input_id, value):
  5063. js = (js_id, input_type, input_id)
  5064. if value >= joystick_threshold:
  5065. if (js in itertools.chain.from_iterable(jump_js) or
  5066. js in itertools.chain.from_iterable(action_js) or
  5067. js in itertools.chain.from_iterable(pause_js)):
  5068. self.start_level()
  5069. elif js in itertools.chain.from_iterable(menu_js):
  5070. sge.game.current_room.show_menu()
  5071. def event_stop(self):
  5072. self.moving = False
  5073. class MapSpace(sge.dsp.Object):
  5074. def __init__(self, x, y, level=None, level_spawn=None, ID=None, **kwargs):
  5075. super().__init__(x, y, **kwargs)
  5076. self.level = level
  5077. self.level_spawn = level_spawn
  5078. if ID is not None:
  5079. self.ID = ID
  5080. elif level is not None:
  5081. self.ID = level
  5082. else:
  5083. self.ID = "__{}x{}__".format(x, y)
  5084. @property
  5085. def cleared(self):
  5086. if self.ID == worldmap_entry_space:
  5087. return True
  5088. else:
  5089. if self.level is not None:
  5090. return self.level in cleared_levels
  5091. else:
  5092. connected_spaces = []
  5093. already_checked = []
  5094. for path in self.get_exits():
  5095. if path is not None:
  5096. x, y = path.points[-1]
  5097. space = MapSpace.get_at(self.x + x, self.y + y)
  5098. if space is not None:
  5099. connected_spaces.append(space)
  5100. while connected_spaces:
  5101. space = connected_spaces.pop(0)
  5102. already_checked.append(space)
  5103. if (space.ID == worldmap_entry_space or
  5104. space.level in cleared_levels):
  5105. return True
  5106. elif space.level is None:
  5107. for path in space.get_exits():
  5108. if path is not None:
  5109. x, y = path.points[-1]
  5110. new_space = MapSpace.get_at(space.x + x,
  5111. space.y + y)
  5112. if (new_space is not None and
  5113. new_space not in connected_spaces and
  5114. new_space not in already_checked):
  5115. connected_spaces.append(new_space)
  5116. return False
  5117. def update_sprite(self):
  5118. if self.level is not None:
  5119. if self.cleared:
  5120. self.sprite = worldmap_level_complete_sprite
  5121. else:
  5122. self.sprite = worldmap_level_incomplete_sprite
  5123. self.image_fps = None
  5124. else:
  5125. self.sprite = None
  5126. def get_exits(self):
  5127. """
  5128. Return the exits of this space as a tuple in the form:
  5129. (up, right, down, left)
  5130. """
  5131. exits = []
  5132. diagonal_exits = []
  5133. left_exit = None
  5134. right_exit = None
  5135. up_exit = None
  5136. down_exit = None
  5137. for obj in sge.game.current_room.get_objects_at(self.x - 1, self.y - 1,
  5138. 2, 2):
  5139. if (isinstance(obj, MapPath) and obj.points and
  5140. abs(self.x - obj.x) < 1 and abs(self.y - obj.y) < 1):
  5141. exits.append(obj)
  5142. # First do exits that are unambiguously one direction
  5143. for obj in exits:
  5144. x, y = obj.points[0]
  5145. if x == 0:
  5146. if y > 0:
  5147. if down_exit is None:
  5148. down_exit = obj
  5149. elif y < 0:
  5150. if up_exit is None:
  5151. up_exit = obj
  5152. else:
  5153. warnings.warn(f"Path at ({obj.x}, {obj.y}) has no "
  5154. "direction!")
  5155. elif y == 0:
  5156. if x > 0:
  5157. if right_exit is None:
  5158. right_exit = obj
  5159. elif x < 0:
  5160. if left_exit is None:
  5161. left_exit = obj
  5162. else:
  5163. warnings.warn(f"Path at ({obj.x}, {obj.y}) has no "
  5164. "direction!")
  5165. else:
  5166. diagonal_exits.append(obj)
  5167. # And now do diagonal exits
  5168. for obj in diagonal_exits:
  5169. x, y = obj.points[0]
  5170. assert x and y
  5171. if abs(y) > abs(x):
  5172. # Mostly vertical
  5173. if y > 0:
  5174. if down_exit is None:
  5175. down_exit = obj
  5176. else:
  5177. if x > 0:
  5178. if right_exit is None:
  5179. right_exit = obj
  5180. else:
  5181. if left_exit is None:
  5182. left_exit = obj
  5183. else:
  5184. if up_exit is None:
  5185. up_exit = obj
  5186. else:
  5187. if x > 0:
  5188. if right_exit is None:
  5189. right_exit = obj
  5190. else:
  5191. if left_exit is None:
  5192. left_exit = obj
  5193. else:
  5194. # Mostly horizontal, or equal
  5195. if x > 0:
  5196. if right_exit is None:
  5197. right_exit = obj
  5198. else:
  5199. if y > 0:
  5200. if down_exit is None:
  5201. down_exit = obj
  5202. else:
  5203. if up_exit is None:
  5204. up_exit = obj
  5205. else:
  5206. if left_exit is None:
  5207. left_exit = obj
  5208. else:
  5209. if y > 0:
  5210. if down_exit is None:
  5211. down_exit = obj
  5212. else:
  5213. if up_exit is None:
  5214. up_exit = obj
  5215. return (up_exit, right_exit, down_exit, left_exit)
  5216. def get_left_exit(self):
  5217. return self.get_exits()[3]
  5218. def get_right_exit(self):
  5219. return self.get_exits()[1]
  5220. def get_up_exit(self):
  5221. return self.get_exits()[0]
  5222. def get_down_exit(self):
  5223. return self.get_exits()[2]
  5224. def start_level(self):
  5225. global main_area
  5226. global level_time_bonus
  5227. global current_areas
  5228. global current_checkpoints
  5229. if self.level:
  5230. main_area = None
  5231. current_areas = {}
  5232. level = Level.load(self.level, True)
  5233. if level is not None:
  5234. checkpoint = current_checkpoints.get(self.level)
  5235. if checkpoint is not None:
  5236. main_area = level.fname
  5237. level_time_bonus = level.time_bonus
  5238. area_name, area_spawn = checkpoint.split(':', 1)
  5239. level = Level.load(area_name, True)
  5240. level.spawn = area_spawn
  5241. else:
  5242. level.spawn = self.level_spawn
  5243. x = self.x - sge.game.current_room.views[0].x
  5244. y = self.y - sge.game.current_room.views[0].y
  5245. if self.sprite:
  5246. x += self.sprite.width / 2
  5247. y += self.sprite.height / 2
  5248. level.start(transition="iris_in",
  5249. transition_time=TRANSITION_TIME,
  5250. transition_arg=(x, y))
  5251. else:
  5252. rush_save()
  5253. sge.game.start_room.start()
  5254. @classmethod
  5255. def get_at(cls, x, y):
  5256. for obj in sge.game.current_room.get_objects_at(x - 1, y - 1, 2, 2):
  5257. if (isinstance(obj, MapSpace) and abs(x - obj.x) < 1 and
  5258. abs(y - obj.y) < 1):
  5259. return obj
  5260. return None
  5261. class MapWarp(MapSpace):
  5262. def __init__(self, x, y, dest=None, **kwargs):
  5263. super().__init__(x, y, **kwargs)
  5264. self.dest = dest
  5265. def update_sprite(self):
  5266. self.sprite = worldmap_warp_sprite
  5267. self.image_fps = None
  5268. def start_level(self):
  5269. global current_worldmap
  5270. global current_worldmap_space
  5271. global worldmap_entry_space
  5272. global mapdest
  5273. global mapdest_space
  5274. if self.dest and ':' in self.dest:
  5275. mapdest, mapdest_space = self.dest.split(':', 1)
  5276. else:
  5277. mapdest_space = None
  5278. if self.dest:
  5279. mapdest = self.dest
  5280. if self.level:
  5281. MapSpace.start_level(self)
  5282. else:
  5283. current_worldmap = mapdest
  5284. current_worldmap_space = mapdest_space
  5285. worldmap_entry_space = mapdest_space
  5286. mapdest = None
  5287. mapdest_space = None
  5288. m = Worldmap.load(current_worldmap)
  5289. m.start(transition="pixelate", transition_time=TRANSITION_TIME,
  5290. transition_arg=100)
  5291. play_sound(warp_sound)
  5292. class MapPath(xsge_path.Path):
  5293. forward = True
  5294. def event_create(self):
  5295. if self.points:
  5296. if self.forward:
  5297. rx, ry = self.points[-1]
  5298. rx += self.x
  5299. ry += self.y
  5300. rp = []
  5301. for x, y in self.points[-2::-1] + [(0, 0)]:
  5302. x = x + self.x - rx
  5303. y = y + self.y - ry
  5304. rp.append((x, y))
  5305. # Not using Object.create to prevent infinite recursion.
  5306. m = MapPath(rx, ry, rp)
  5307. m.forward = False
  5308. sge.game.current_room.add(m)
  5309. if MapSpace.get_at(self.x, self.y) is None:
  5310. MapSpace.create(self.x, self.y)
  5311. else:
  5312. warnings.warn(f"MapPath at ({self.x}, {self.y}) has only one "
  5313. "point!")
  5314. def event_follow_end(self, obj):
  5315. global current_worldmap_space
  5316. global current_level
  5317. if self.points:
  5318. x, y = self.points[-1]
  5319. else:
  5320. x = 0
  5321. y = 0
  5322. obj.x = self.x + x
  5323. obj.y = self.y + y
  5324. obj.moving = False
  5325. space = MapSpace.get_at(obj.x, obj.y)
  5326. if space is not None and space.ID is not None:
  5327. current_worldmap_space = space.ID
  5328. save_game()
  5329. class MapWater(sge.dsp.Object):
  5330. def __init__(self, x, y, **kwargs):
  5331. kwargs["sprite"] = worldmap_water_sprite
  5332. kwargs["tangible"] = False
  5333. super().__init__(x, y, **kwargs)
  5334. class Menu(xsge_gui.MenuWindow):
  5335. items = []
  5336. @classmethod
  5337. def create(cls, default=0):
  5338. if cls.items:
  5339. self = cls.from_text(
  5340. gui_handler, sge.game.width / 2, sge.game.height * 2 / 3,
  5341. cls.items, font_normal=font,
  5342. color_normal=menu_text_color,
  5343. color_selected=sge.gfx.Color("white"),
  5344. background_color=menu_color, margin=9, halign="center",
  5345. valign="middle", outline_normal=sge.gfx.Color("black"),
  5346. outline_selected=sge.gfx.Color("black"),
  5347. outline_thickness_normal=text_outline_thickness,
  5348. outline_thickness_selected=text_outline_thickness,
  5349. selection_prefix="«", selection_suffix="»")
  5350. default %= len(self.widgets)
  5351. self.keyboard_focused_widget = self.widgets[default]
  5352. self.show()
  5353. return self
  5354. def event_change_keyboard_focus(self):
  5355. play_sound(select_sound)
  5356. def event_press_left(self):
  5357. self.event_press_enter()
  5358. def event_press_right(self):
  5359. self.event_press_enter()
  5360. class MainMenu(Menu):
  5361. items = [_("New Game"), _("Load Game"), _("Select Levelset"), _("Options"),
  5362. _("Credits"), _("Quit")]
  5363. def event_choose(self):
  5364. if self.choice == 0:
  5365. play_sound(confirm_sound)
  5366. NewGameMenu.create_page()
  5367. elif self.choice == 1:
  5368. play_sound(confirm_sound)
  5369. LoadGameMenu.create_page()
  5370. elif self.choice == 2:
  5371. play_sound(confirm_sound)
  5372. LevelsetMenu.create_page(refreshlist=True)
  5373. elif self.choice == 3:
  5374. play_sound(confirm_sound)
  5375. OptionsMenu.create_page()
  5376. elif self.choice == 4:
  5377. credits_room = CreditsScreen.load(os.path.join("special",
  5378. "credits.json"))
  5379. credits_room.start()
  5380. else:
  5381. sge.game.end()
  5382. class NewGameMenu(Menu):
  5383. @classmethod
  5384. def create_page(cls, default=0):
  5385. cls.items = []
  5386. for slot in save_slots:
  5387. if slot is None:
  5388. cls.items.append(_("-Empty-"))
  5389. elif slot.get("levelset") is None:
  5390. cls.items.append(_("-No Levelset-"))
  5391. else:
  5392. fname = os.path.join(DATA, "levelsets", slot["levelset"])
  5393. try:
  5394. with open(fname, 'r') as f:
  5395. data = json.load(f)
  5396. except (OSError, ValueError):
  5397. cls.items.append(_("-Corrupt Levelset-"))
  5398. continue
  5399. else:
  5400. levelset_name = data.get("name", slot["levelset"])
  5401. completion = slot.get("completion", 0)
  5402. cls.items.append("{} ({}%)".format(levelset_name,
  5403. completion))
  5404. cls.items.append(_("Back"))
  5405. return cls.create(default)
  5406. def event_choose(self):
  5407. global abort
  5408. global current_save_slot
  5409. abort = False
  5410. if self.choice in range(len(save_slots)):
  5411. play_sound(confirm_sound)
  5412. current_save_slot = self.choice
  5413. if save_slots[current_save_slot] is None:
  5414. set_new_game()
  5415. if not abort:
  5416. start_levelset()
  5417. else:
  5418. NewGameMenu.create(default=self.choice)
  5419. else:
  5420. OverwriteConfirmMenu.create(default=1)
  5421. else:
  5422. play_sound(cancel_sound)
  5423. MainMenu.create(default=0)
  5424. class OverwriteConfirmMenu(Menu):
  5425. items = [_("Overwrite this save file"), _("Cancel")]
  5426. def event_choose(self):
  5427. global abort
  5428. abort = False
  5429. if self.choice == 0:
  5430. play_sound(confirm_sound)
  5431. set_new_game()
  5432. if not abort:
  5433. start_levelset()
  5434. else:
  5435. play_sound(cancel_sound)
  5436. NewGameMenu.create(default=current_save_slot)
  5437. else:
  5438. play_sound(cancel_sound)
  5439. NewGameMenu.create(default=current_save_slot)
  5440. class LoadGameMenu(NewGameMenu):
  5441. def event_choose(self):
  5442. global abort
  5443. global current_save_slot
  5444. abort = False
  5445. if self.choice in range(len(save_slots)):
  5446. play_sound(confirm_sound)
  5447. current_save_slot = self.choice
  5448. load_game()
  5449. if abort:
  5450. MainMenu.create(default=1)
  5451. elif not start_levelset():
  5452. play_sound(error_sound)
  5453. show_error(_("An error occurred when trying to load the game."))
  5454. MainMenu.create(default=1)
  5455. else:
  5456. play_sound(cancel_sound)
  5457. MainMenu.create(default=1)
  5458. class LevelsetMenu(Menu):
  5459. levelsets = []
  5460. current_levelsets = []
  5461. page = 0
  5462. @classmethod
  5463. def create_page(cls, default=0, page=0, refreshlist=False):
  5464. if refreshlist or not cls.levelsets:
  5465. cls.levelsets = []
  5466. for fname in os.listdir(os.path.join(DATA, "levelsets")):
  5467. try:
  5468. with open(os.path.join(DATA, "levelsets", fname), 'r') as f:
  5469. data = json.load(f)
  5470. except (OSError, ValueError):
  5471. continue
  5472. else:
  5473. cls.levelsets.append((fname, str(data.get("name", "???"))))
  5474. def sort_key(T):
  5475. # The current levelset has top priority, followed by the
  5476. # ReTux levelset, and every other levelset is sorted
  5477. # alphabetically based first on their displayed names
  5478. # and secondly on their file names.
  5479. return (T[0] != current_levelset, T[0] != "retux.json",
  5480. T[1].lower(), T[0].lower())
  5481. cls.levelsets.sort(key=sort_key)
  5482. cls.current_levelsets = []
  5483. cls.items = []
  5484. if cls.levelsets:
  5485. page_size = MENU_MAX_ITEMS - 2
  5486. n_pages = math.ceil(len(cls.levelsets) / page_size)
  5487. page = int(page % n_pages)
  5488. page_start = page * page_size
  5489. page_end = min(page_start + page_size, len(cls.levelsets))
  5490. current_page = cls.levelsets[page_start:page_end]
  5491. cls.current_levelsets = []
  5492. cls.items = []
  5493. for fname, name in current_page:
  5494. cls.current_levelsets.append(fname)
  5495. cls.items.append(name)
  5496. cls.items.append(_("Next page"))
  5497. cls.items.append(_("Back"))
  5498. cls.page = page
  5499. return cls.create(default)
  5500. def event_choose(self):
  5501. if self.choice == len(self.items) - 2:
  5502. play_sound(select_sound)
  5503. self.create_page(default=-2, page=(self.page + 1))
  5504. else:
  5505. if self.choice is not None and self.choice < len(self.items) - 2:
  5506. play_sound(confirm_sound)
  5507. load_levelset(self.current_levelsets[self.choice])
  5508. DialogBox(gui_handler,
  5509. _("Levelset loaded successfully.")).show()
  5510. else:
  5511. play_sound(cancel_sound)
  5512. MainMenu.create(default=2)
  5513. class OptionsMenu(Menu):
  5514. @classmethod
  5515. def create_page(cls, default=0):
  5516. smt = scale_method if scale_method else "fastest"
  5517. cls.items = [
  5518. _("Fullscreen: {}").format(_("On") if fullscreen else _("Off")),
  5519. _("Scale Method: {}").format(smt),
  5520. _("Scale Proportional: {}").format(
  5521. _("On") if scale_proportional else _("Off")),
  5522. _("Sound Volume: {}%").format(int(sound_volume * 100)),
  5523. _("Music Volume: {}%").format(int(music_volume * 100)),
  5524. _("Stereo: {}").format(_("On") if stereo_enabled else _("Off")),
  5525. _("Show FPS: {}").format(_("On") if fps_enabled else _("Off")),
  5526. _("Joystick Threshold: {}%").format(int(joystick_threshold * 100)),
  5527. _("Configure keyboard"), _("Configure joysticks"),
  5528. _("Detect joysticks"), _("Import levelset"), _("Back")]
  5529. return cls.create(default)
  5530. def event_press_left(self):
  5531. # Variant of event_press_enter which passes True to the custom
  5532. # ``left`` parameter.
  5533. try:
  5534. self.choice = self.widgets.index(self.keyboard_focused_widget)
  5535. except ValueError:
  5536. pass
  5537. self.destroy()
  5538. _refresh_screen(0, 0)
  5539. self.event_choose(True)
  5540. def event_choose(self, left=False):
  5541. global fullscreen
  5542. global scale_method
  5543. global scale_proportional
  5544. global sound_volume
  5545. global music_volume
  5546. global stereo_enabled
  5547. global fps_enabled
  5548. global joystick_threshold
  5549. if self.choice == 0:
  5550. play_sound(select_sound)
  5551. fullscreen = not fullscreen
  5552. sge.game.fullscreen = fullscreen
  5553. OptionsMenu.create_page(default=self.choice)
  5554. elif self.choice == 1:
  5555. choices = [None, "noblur", "smooth"] + sge.SCALE_METHODS
  5556. if scale_method in choices:
  5557. i = choices.index(scale_method)
  5558. else:
  5559. i = 0
  5560. play_sound(select_sound)
  5561. i += -1 if left else 1
  5562. i %= len(choices)
  5563. scale_method = choices[i]
  5564. sge.game.scale_method = scale_method
  5565. OptionsMenu.create_page(default=self.choice)
  5566. elif self.choice == 2:
  5567. play_sound(select_sound)
  5568. scale_proportional = not scale_proportional
  5569. sge.game.scale_proportional = scale_proportional
  5570. OptionsMenu.create_page(default=self.choice)
  5571. elif self.choice == 3:
  5572. # This somewhat complicated method is to prevent rounding
  5573. # irregularities.
  5574. if left:
  5575. v = int(sound_volume*100) - 5
  5576. if v < 0:
  5577. v = 100
  5578. else:
  5579. v = int(sound_volume*100) + 5
  5580. if v > 100:
  5581. v = 0
  5582. sound_volume = v / 100
  5583. play_sound(coin_sound)
  5584. OptionsMenu.create_page(default=self.choice)
  5585. elif self.choice == 4:
  5586. # This somewhat complicated method is to prevent rounding
  5587. # irregularities.
  5588. if left:
  5589. v = int(music_volume*100) - 5
  5590. if v < 0:
  5591. v = 100
  5592. else:
  5593. v = int(music_volume*100) + 5
  5594. if v > 100:
  5595. v = 0
  5596. music_volume = v / 100
  5597. play_music(sge.game.current_room.music)
  5598. OptionsMenu.create_page(default=self.choice)
  5599. elif self.choice == 5:
  5600. stereo_enabled = not stereo_enabled
  5601. play_sound(confirm_sound)
  5602. OptionsMenu.create_page(default=self.choice)
  5603. elif self.choice == 6:
  5604. play_sound(select_sound)
  5605. fps_enabled = not fps_enabled
  5606. OptionsMenu.create_page(default=self.choice)
  5607. elif self.choice == 7:
  5608. play_sound(select_sound)
  5609. # This somewhat complicated method is to prevent rounding
  5610. # irregularities.
  5611. if left:
  5612. threshold = ((int(joystick_threshold*100) - 5) % 100) / 100
  5613. else:
  5614. threshold = ((int(joystick_threshold*100) + 5) % 100) / 100
  5615. if not threshold:
  5616. threshold = 0.0001
  5617. joystick_threshold = threshold
  5618. xsge_gui.joystick_threshold = threshold
  5619. OptionsMenu.create_page(default=self.choice)
  5620. elif self.choice == 8:
  5621. play_sound(confirm_sound)
  5622. KeyboardMenu.create_page()
  5623. elif self.choice == 9:
  5624. play_sound(confirm_sound)
  5625. JoystickMenu.create_page()
  5626. elif self.choice == 10:
  5627. sge.joystick.refresh()
  5628. play_sound(heal_sound)
  5629. OptionsMenu.create_page(default=self.choice)
  5630. elif self.choice == 11:
  5631. if HAVE_TK:
  5632. play_sound(confirm_sound)
  5633. fnames = tkinter_filedialog.askopenfilenames(
  5634. filetypes=[(_("ReTux levelset files"), ".rtz"),
  5635. (_("all files"), ".*")])
  5636. for fname in fnames:
  5637. w = 400
  5638. h = 128
  5639. margin = 16
  5640. x = SCREEN_SIZE[0] / 2 - w / 2
  5641. y = SCREEN_SIZE[1] / 2 - h / 2
  5642. c = sge.gfx.Color("black")
  5643. window = xsge_gui.Window(gui_handler, x, y, w, h,
  5644. background_color=c, border=False)
  5645. x = margin
  5646. y = margin
  5647. text = _("Importing {}...").format(fname)
  5648. c = sge.gfx.Color("white")
  5649. xsge_gui.Label(
  5650. window, x, y, 1, text, font=font, width=(w - 2 * margin),
  5651. height=(h - 3 * margin -
  5652. xsge_gui.progressbar_container_sprite.height),
  5653. color=c)
  5654. x = margin
  5655. y = h - margin - xsge_gui.progressbar_container_sprite.height
  5656. progressbar = xsge_gui.ProgressBar(window, x, y, 0,
  5657. width=(w - 2 * margin))
  5658. window.show()
  5659. gui_handler.event_step(0, 0)
  5660. _refresh_screen(0, 0)
  5661. try:
  5662. with zipfile.ZipFile(fname, 'r') as rtz:
  5663. infolist = rtz.infolist()
  5664. for i in range(len(infolist)):
  5665. member = infolist[i]
  5666. rtz.extract(member, DATA)
  5667. rtz.extract(member, os.path.join(LOCAL, "data"))
  5668. progressbar.progress = (i + 1) / len(infolist)
  5669. progressbar.redraw()
  5670. sge.game.pump_input()
  5671. gui_handler.event_step(0, 0)
  5672. _refresh_screen(0, 0)
  5673. except Exception as e:
  5674. show_error(str(e))
  5675. window.destroy()
  5676. sge.game.pump_input()
  5677. gui_handler.event_step(0, 0)
  5678. _refresh_screen(0, 0)
  5679. sge.game.pump_input()
  5680. sge.game.input_events = []
  5681. else:
  5682. play_sound(kill_sound)
  5683. e = _("This feature requires Tkinter, which was not "
  5684. "successfully imported. Please make sure Tkinter is "
  5685. "installed and try again.")
  5686. show_error(e)
  5687. OptionsMenu.create_page(default=self.choice)
  5688. else:
  5689. play_sound(cancel_sound)
  5690. write_to_disk()
  5691. MainMenu.create(default=3)
  5692. class KeyboardMenu(Menu):
  5693. page = 0
  5694. @classmethod
  5695. def create_page(cls, default=0, page=0):
  5696. page %= min(len(left_key), len(right_key), len(up_key), len(down_key),
  5697. len(jump_key), len(action_key), len(sneak_key),
  5698. len(menu_key), len(pause_key))
  5699. def format_key(key):
  5700. if key:
  5701. return " ".join(key)
  5702. else:
  5703. return None
  5704. cls.items = [_("Player {}").format(page + 1),
  5705. _("Left: {}").format(format_key(left_key[page])),
  5706. _("Right: {}").format(format_key(right_key[page])),
  5707. _("Up: {}").format(format_key(up_key[page])),
  5708. _("Down: {}").format(format_key(down_key[page])),
  5709. _("Jump: {}").format(format_key(jump_key[page])),
  5710. _("Action: {}").format(format_key(action_key[page])),
  5711. _("Sneak: {}").format(format_key(sneak_key[page])),
  5712. _("Menu: {}").format(format_key(menu_key[page])),
  5713. _("Pause: {}").format(format_key(pause_key[page])),
  5714. _("Back")]
  5715. self = cls.create(default)
  5716. self.page = page
  5717. return self
  5718. def event_choose(self):
  5719. def bind_key(key, new_key, self=self):
  5720. # Allow making a control exclusively one key.
  5721. if new_key in key:
  5722. while key:
  5723. key.pop(0)
  5724. for other_key in [
  5725. left_key[self.page], right_key[self.page],
  5726. up_key[self.page], down_key[self.page],
  5727. jump_key[self.page], action_key[self.page],
  5728. sneak_key[self.page], menu_key[self.page],
  5729. pause_key[self.page]]:
  5730. if new_key in other_key:
  5731. other_key.remove(new_key)
  5732. key.append(new_key)
  5733. while len(key) > 2:
  5734. key.pop(0)
  5735. text = _("Press the key you wish to bind to this function, or the "
  5736. "Escape key to cancel.")
  5737. if self.choice == 0:
  5738. play_sound(select_sound)
  5739. self.__class__.create_page(default=self.choice, page=(self.page + 1))
  5740. elif self.choice == 1:
  5741. k = wait_key(text)
  5742. if k is not None:
  5743. bind_key(left_key[self.page], k)
  5744. play_sound(confirm_sound)
  5745. else:
  5746. play_sound(cancel_sound)
  5747. self.__class__.create_page(default=self.choice, page=self.page)
  5748. elif self.choice == 2:
  5749. k = wait_key(text)
  5750. if k is not None:
  5751. bind_key(right_key[self.page], k)
  5752. play_sound(confirm_sound)
  5753. else:
  5754. play_sound(cancel_sound)
  5755. self.__class__.create_page(default=self.choice, page=self.page)
  5756. elif self.choice == 3:
  5757. k = wait_key(text)
  5758. if k is not None:
  5759. bind_key(up_key[self.page], k)
  5760. play_sound(confirm_sound)
  5761. else:
  5762. play_sound(cancel_sound)
  5763. self.__class__.create_page(default=self.choice, page=self.page)
  5764. elif self.choice == 4:
  5765. k = wait_key(text)
  5766. if k is not None:
  5767. bind_key(down_key[self.page], k)
  5768. play_sound(confirm_sound)
  5769. else:
  5770. play_sound(cancel_sound)
  5771. self.__class__.create_page(default=self.choice, page=self.page)
  5772. elif self.choice == 5:
  5773. k = wait_key(text)
  5774. if k is not None:
  5775. bind_key(jump_key[self.page], k)
  5776. play_sound(confirm_sound)
  5777. else:
  5778. play_sound(cancel_sound)
  5779. self.__class__.create_page(default=self.choice, page=self.page)
  5780. elif self.choice == 6:
  5781. k = wait_key(text)
  5782. if k is not None:
  5783. bind_key(action_key[self.page], k)
  5784. play_sound(confirm_sound)
  5785. else:
  5786. play_sound(cancel_sound)
  5787. self.__class__.create_page(default=self.choice, page=self.page)
  5788. elif self.choice == 7:
  5789. k = wait_key(text)
  5790. if k is not None:
  5791. bind_key(sneak_key[self.page], k)
  5792. play_sound(confirm_sound)
  5793. else:
  5794. play_sound(cancel_sound)
  5795. self.__class__.create_page(default=self.choice, page=self.page)
  5796. elif self.choice == 8:
  5797. k = wait_key(text)
  5798. if k is not None:
  5799. bind_key(menu_key[self.page], k)
  5800. play_sound(confirm_sound)
  5801. else:
  5802. play_sound(cancel_sound)
  5803. self.__class__.create_page(default=self.choice, page=self.page)
  5804. elif self.choice == 9:
  5805. k = wait_key(text)
  5806. if k is not None:
  5807. bind_key(pause_key[self.page], k)
  5808. play_sound(confirm_sound)
  5809. else:
  5810. play_sound(cancel_sound)
  5811. self.__class__.create_page(default=self.choice, page=self.page)
  5812. else:
  5813. play_sound(cancel_sound)
  5814. OptionsMenu.create_page(default=7)
  5815. class JoystickMenu(Menu):
  5816. page = 0
  5817. @classmethod
  5818. def create_page(cls, default=0, page=0):
  5819. page %= min(len(left_js), len(right_js), len(up_js), len(down_js),
  5820. len(jump_js), len(action_js), len(sneak_js), len(menu_js),
  5821. len(pause_js))
  5822. def format_js(js):
  5823. js_template = "{},{},{}"
  5824. sL = []
  5825. for j in js:
  5826. sL.append(js_template.format(*j))
  5827. if sL:
  5828. return " ".join(sL)
  5829. else:
  5830. return _("None")
  5831. cls.items = [_("Player {}").format(page + 1),
  5832. _("Left: {}").format(format_js(left_js[page])),
  5833. _("Right: {}").format(format_js(right_js[page])),
  5834. _("Up: {}").format(format_js(up_js[page])),
  5835. _("Down: {}").format(format_js(down_js[page])),
  5836. _("Jump: {}").format(format_js(jump_js[page])),
  5837. _("Action: {}").format(format_js(action_js[page])),
  5838. _("Sneak: {}").format(format_js(sneak_js[page])),
  5839. _("Menu: {}").format(format_js(menu_js[page])),
  5840. _("Pause: {}").format(format_js(pause_js[page])),
  5841. _("Back")]
  5842. self = cls.create(default)
  5843. self.page = page
  5844. return self
  5845. def event_choose(self):
  5846. def bind_js(js, new_js, self=self):
  5847. # Allow making a control exclusively one key.
  5848. if new_js in js:
  5849. while js:
  5850. js.pop(0)
  5851. for other_js in [
  5852. left_js[self.page], right_js[self.page],
  5853. up_js[self.page], down_js[self.page],
  5854. jump_js[self.page], action_js[self.page],
  5855. sneak_js[self.page], menu_js[self.page],
  5856. pause_js[self.page]]:
  5857. if new_js in other_js:
  5858. other_js.remove(new_js)
  5859. js.append(new_js)
  5860. while len(js) > 2:
  5861. js.pop(0)
  5862. text = _("Press the joystick button, axis, or hat direction you wish "
  5863. "to bind to this function, or the Escape key to cancel.")
  5864. if self.choice == 0:
  5865. play_sound(select_sound)
  5866. self.__class__.create_page(default=self.choice, page=(self.page + 1))
  5867. elif self.choice == 1:
  5868. js = wait_js(text)
  5869. if js is not None:
  5870. bind_js(left_js[self.page], js)
  5871. play_sound(confirm_sound)
  5872. else:
  5873. play_sound(cancel_sound)
  5874. self.__class__.create_page(default=self.choice, page=self.page)
  5875. elif self.choice == 2:
  5876. js = wait_js(text)
  5877. if js is not None:
  5878. bind_js(right_js[self.page], js)
  5879. play_sound(confirm_sound)
  5880. else:
  5881. play_sound(cancel_sound)
  5882. self.__class__.create_page(default=self.choice, page=self.page)
  5883. elif self.choice == 3:
  5884. js = wait_js(text)
  5885. if js is not None:
  5886. bind_js(up_js[self.page], js)
  5887. play_sound(confirm_sound)
  5888. else:
  5889. play_sound(cancel_sound)
  5890. self.__class__.create_page(default=self.choice, page=self.page)
  5891. elif self.choice == 4:
  5892. js = wait_js(text)
  5893. if js is not None:
  5894. bind_js(down_js[self.page], js)
  5895. play_sound(confirm_sound)
  5896. else:
  5897. play_sound(cancel_sound)
  5898. self.__class__.create_page(default=self.choice, page=self.page)
  5899. elif self.choice == 5:
  5900. js = wait_js(text)
  5901. if js is not None:
  5902. bind_js(jump_js[self.page], js)
  5903. play_sound(confirm_sound)
  5904. else:
  5905. play_sound(cancel_sound)
  5906. self.__class__.create_page(default=self.choice, page=self.page)
  5907. elif self.choice == 6:
  5908. js = wait_js(text)
  5909. if js is not None:
  5910. bind_js(action_js[self.page], js)
  5911. play_sound(confirm_sound)
  5912. else:
  5913. play_sound(cancel_sound)
  5914. self.__class__.create_page(default=self.choice, page=self.page)
  5915. elif self.choice == 7:
  5916. js = wait_js(text)
  5917. if js is not None:
  5918. bind_js(sneak_js[self.page], js)
  5919. play_sound(confirm_sound)
  5920. else:
  5921. play_sound(cancel_sound)
  5922. self.__class__.create_page(default=self.choice, page=self.page)
  5923. elif self.choice == 8:
  5924. js = wait_js(text)
  5925. if js is not None:
  5926. bind_js(menu_js[self.page], js)
  5927. play_sound(confirm_sound)
  5928. else:
  5929. play_sound(cancel_sound)
  5930. self.__class__.create_page(default=self.choice, page=self.page)
  5931. elif self.choice == 9:
  5932. js = wait_js(text)
  5933. if js is not None:
  5934. bind_js(pause_js[self.page], js)
  5935. play_sound(confirm_sound)
  5936. else:
  5937. play_sound(cancel_sound)
  5938. self.__class__.create_page(default=self.choice, page=self.page)
  5939. else:
  5940. play_sound(cancel_sound)
  5941. OptionsMenu.create_page(default=8)
  5942. class ModalMenu(xsge_gui.MenuDialog):
  5943. items = []
  5944. @classmethod
  5945. def create(cls, default=0):
  5946. if cls.items:
  5947. self = cls.from_text(
  5948. gui_handler, sge.game.width / 2, sge.game.height / 2,
  5949. cls.items, font_normal=font,
  5950. color_normal=menu_text_color,
  5951. color_selected=sge.gfx.Color("white"),
  5952. background_color=menu_color, margin=9, halign="center",
  5953. valign="middle", outline_normal=sge.gfx.Color("black"),
  5954. outline_selected=sge.gfx.Color("black"),
  5955. outline_thickness_normal=text_outline_thickness,
  5956. outline_thickness_selected=text_outline_thickness,
  5957. selection_prefix="«", selection_suffix="»")
  5958. default %= len(self.widgets)
  5959. self.keyboard_focused_widget = self.widgets[default]
  5960. _refresh_screen(0, 0)
  5961. self.show()
  5962. return self
  5963. def event_change_keyboard_focus(self):
  5964. play_sound(select_sound)
  5965. class PauseMenu(ModalMenu):
  5966. @classmethod
  5967. def create(cls, default=0):
  5968. if LEVEL or RECORD:
  5969. items = [_("Continue"), _("Configure keyboard"),
  5970. _("Configure joysticks"), _("Abort")]
  5971. elif current_worldmap:
  5972. items = [_("Continue"), _("Configure keyboard"),
  5973. _("Configure joysticks"), _("Return to Map"),
  5974. _("Return to Title Screen")]
  5975. else:
  5976. items = [_("Continue"), _("Configure keyboard"),
  5977. _("Configure joysticks"), _("Return to Title Screen")]
  5978. self = cls.from_text(
  5979. gui_handler, sge.game.width / 2, sge.game.height / 2,
  5980. items, font_normal=font, color_normal=menu_text_color,
  5981. color_selected=sge.gfx.Color("white"), background_color=menu_color,
  5982. margin=9, halign="center", valign="middle",
  5983. outline_normal=sge.gfx.Color("black"),
  5984. outline_selected=sge.gfx.Color("black"),
  5985. outline_thickness_normal=text_outline_thickness,
  5986. outline_thickness_selected=text_outline_thickness,
  5987. selection_prefix="«", selection_suffix="»")
  5988. default %= len(self.widgets)
  5989. self.keyboard_focused_widget = self.widgets[default]
  5990. _refresh_screen(0, 0)
  5991. self.show()
  5992. return self
  5993. def event_choose(self):
  5994. self.hide()
  5995. _refresh_screen(0, 0)
  5996. if self.choice == 1:
  5997. play_sound(confirm_sound)
  5998. ModalKeyboardMenu.create_page()
  5999. PauseMenu.create(default=self.choice)
  6000. elif self.choice == 2:
  6001. play_sound(confirm_sound)
  6002. ModalJoystickMenu.create_page()
  6003. PauseMenu.create(default=self.choice)
  6004. elif self.choice == 3:
  6005. rush_save()
  6006. if current_worldmap:
  6007. play_sound(kill_sound)
  6008. sge.game.current_room.return_to_map()
  6009. elif self.choice == 4:
  6010. rush_save()
  6011. sge.game.start_room.start()
  6012. else:
  6013. play_sound(select_sound)
  6014. sge.snd.Music.unpause()
  6015. class ModalKeyboardMenu(ModalMenu, KeyboardMenu):
  6016. def event_choose(self):
  6017. self.hide()
  6018. _refresh_screen(0, 0)
  6019. if self.choice is not None and self.choice < len(self.items) - 1:
  6020. super().event_choose()
  6021. else:
  6022. play_sound(cancel_sound)
  6023. class ModalJoystickMenu(ModalMenu, JoystickMenu):
  6024. def event_choose(self):
  6025. self.hide()
  6026. _refresh_screen(0, 0)
  6027. if self.choice is not None and self.choice < len(self.items) - 1:
  6028. super().event_choose()
  6029. else:
  6030. play_sound(cancel_sound)
  6031. class WorldmapMenu(ModalMenu):
  6032. items = [_("Continue"), _("Configure keyboard"), _("Configure joysticks"),
  6033. _("Return to Title Screen")]
  6034. def event_choose(self):
  6035. self.hide()
  6036. _refresh_screen(0, 0)
  6037. if self.choice == 1:
  6038. play_sound(confirm_sound)
  6039. ModalKeyboardMenu.create_page()
  6040. WorldmapMenu.create(default=self.choice)
  6041. elif self.choice == 2:
  6042. play_sound(confirm_sound)
  6043. ModalJoystickMenu.create_page()
  6044. WorldmapMenu.create(default=self.choice)
  6045. elif self.choice == 3:
  6046. rush_save()
  6047. sge.game.start_room.start()
  6048. else:
  6049. play_sound(select_sound)
  6050. sge.snd.Music.unpause()
  6051. class DialogLabel(xsge_gui.ProgressiveLabel):
  6052. def event_add_character(self):
  6053. if not self.text[-1].isspace():
  6054. play_sound(type_sound)
  6055. class DialogBox(xsge_gui.Dialog):
  6056. def __init__(self, parent, text, portrait=None, rate=TEXT_SPEED):
  6057. width = sge.game.width / 2
  6058. x_padding = 16
  6059. y_padding = 16
  6060. label_x = 8
  6061. label_y = 8
  6062. if portrait is not None:
  6063. x_padding += 8
  6064. label_x += 8
  6065. portrait_w = portrait.width
  6066. portrait_h = portrait.height
  6067. label_x += portrait_w
  6068. else:
  6069. portrait_w = 0
  6070. portrait_h = 0
  6071. label_w = max(1, width - portrait_w - x_padding)
  6072. height = max(1, portrait_h + y_padding,
  6073. font.get_height(text, width=label_w) + y_padding)
  6074. x = sge.game.width / 2 - width / 2
  6075. y = sge.game.height / 2 - height / 2
  6076. super().__init__(parent, x, y, width, height,
  6077. background_color=menu_color, border=False)
  6078. label_h = max(1, height - y_padding)
  6079. self.label = DialogLabel(self, label_x, label_y, 0, text, font=font,
  6080. width=label_w, height=label_h,
  6081. color=sge.gfx.Color("white"), rate=rate)
  6082. if portrait is not None:
  6083. xsge_gui.Widget(self, 8, 8, 0, sprite=portrait)
  6084. def event_press_enter(self):
  6085. if len(self.label.text) < len(self.label.full_text):
  6086. self.label.text = self.label.full_text
  6087. else:
  6088. self.destroy()
  6089. def event_press_escape(self):
  6090. self.destroy()
  6091. room = sge.game.current_room
  6092. if (isinstance(room, Level) and
  6093. room.timeline_skip_target is not None and
  6094. room.timeline_step < room.timeline_skip_target):
  6095. room.timeline_skipto(room.timeline_skip_target)
  6096. def get_object(x, y, cls=None, **kwargs):
  6097. cls = TYPES.get(cls, xsge_tiled.Decoration)
  6098. return cls(x, y, **kwargs)
  6099. def get_scaled_copy(obj):
  6100. s = obj.sprite.copy()
  6101. if obj.image_xscale < 0:
  6102. s.mirror()
  6103. if obj.image_yscale < 0:
  6104. s.flip()
  6105. s.width *= abs(obj.image_xscale)
  6106. s.height *= abs(obj.image_yscale)
  6107. s.rotate(obj.image_rotation)
  6108. s.origin_x = obj.image_origin_x
  6109. s.origin_y = obj.image_origin_y
  6110. if obj.image_blend:
  6111. blend_mode = obj.image_blend_mode
  6112. if blend_mode is None:
  6113. blend_mode = sge.BLEND_RGB_MULTIPLY
  6114. s.draw_rectangle(0, 0, s.width, s.height, fill=obj.image_blend,
  6115. blend_mode=blend_mode)
  6116. if obj.image_alpha < 255:
  6117. c = sge.gfx.Color((255, 255, 255, obj.image_alpha))
  6118. s.draw_rectangle(0, 0, s.width, s.height, fill=c,
  6119. blend_mode=sge.BLEND_RGBA_MULTIPLY)
  6120. return s
  6121. def get_jump_speed(height, gravity=GRAVITY):
  6122. # Get the speed to achieve a given height using a kinematic
  6123. # equation: v[f]^2 = v[i]^2 + 2ad
  6124. return -math.sqrt(2 * gravity * height)
  6125. def set_gui_controls():
  6126. # Set the controls for xsge_gui.
  6127. xsge_gui.next_widget_keys = ["down", "tab", "s", "kp_2"]
  6128. xsge_gui.previous_widget_keys = ["up", "w", "kp_8"]
  6129. xsge_gui.left_keys = ["left", "a", "kp_4"]
  6130. xsge_gui.right_keys = ["right", "d", "kp_6"]
  6131. xsge_gui.up_keys = []
  6132. xsge_gui.down_keys = []
  6133. xsge_gui.enter_keys = ["enter", "kp_enter", "space", "end"]
  6134. xsge_gui.escape_keys = ["escape"]
  6135. xsge_gui.next_widget_joystick_events = [
  6136. (0, "axis+", 1), (0, "hat_down", 0)]
  6137. xsge_gui.previous_widget_joystick_events = [
  6138. (0, "axis-", 1), (0, "hat_up", 0)]
  6139. xsge_gui.left_joystick_events = [(0, "axis-", 0), (0, "hat_left", 0)]
  6140. xsge_gui.right_joystick_events = [(0, "axis+", 0), (0, "hat_right", 0)]
  6141. xsge_gui.up_joystick_events = []
  6142. xsge_gui.down_joystick_events = []
  6143. xsge_gui.enter_joystick_events = [
  6144. (0, "button", 0), (0, "button", 1), (0, "button", 2), (0, "button", 3),
  6145. (0, "button", 9)]
  6146. xsge_gui.escape_joystick_events = [(0, "button", 8)]
  6147. def wait_key(text):
  6148. # Wait for a key press and return it.
  6149. sge.game.pump_input()
  6150. sge.game.input_events = []
  6151. while True:
  6152. # Input events
  6153. sge.game.pump_input()
  6154. while sge.game.input_events:
  6155. event = sge.game.input_events.pop(0)
  6156. if isinstance(event, sge.input.KeyPress):
  6157. sge.game.pump_input()
  6158. sge.game.input_events = []
  6159. if event.key == "escape":
  6160. return None
  6161. else:
  6162. return event.key
  6163. # Regulate speed
  6164. sge.game.regulate_speed(fps=10)
  6165. # Project text
  6166. sge.game.project_text(font, text, sge.game.width / 2,
  6167. sge.game.height / 2, width=sge.game.width,
  6168. height=sge.game.height,
  6169. color=sge.gfx.Color("white"),
  6170. halign="center", valign="middle",
  6171. outline=sge.gfx.Color("black"),
  6172. outline_thickness=text_outline_thickness)
  6173. # Refresh
  6174. _refresh_screen(0, 0)
  6175. def wait_js(text):
  6176. # Wait for a joystick press and return it.
  6177. sge.game.pump_input()
  6178. sge.game.input_events = []
  6179. while True:
  6180. # Input events
  6181. sge.game.pump_input()
  6182. while sge.game.input_events:
  6183. event = sge.game.input_events.pop(0)
  6184. if isinstance(event, sge.input.KeyPress):
  6185. if event.key == "escape":
  6186. sge.game.pump_input()
  6187. sge.game.input_events = []
  6188. return None
  6189. elif isinstance(event, sge.input.JoystickEvent):
  6190. if (event.input_type not in {"axis0", "hat_center_x",
  6191. "hat_center_y"} and
  6192. event.value >= joystick_threshold):
  6193. sge.game.pump_input()
  6194. sge.game.input_events = []
  6195. return (event.js_id, event.input_type, event.input_id)
  6196. # Regulate speed
  6197. sge.game.regulate_speed(fps=10)
  6198. # Project text
  6199. sge.game.project_text(font, text, sge.game.width / 2,
  6200. sge.game.height / 2, width=sge.game.width,
  6201. height=sge.game.height,
  6202. color=sge.gfx.Color("white"),
  6203. halign="center", valign="middle",
  6204. outline=sge.gfx.Color("black"),
  6205. outline_thickness=text_outline_thickness)
  6206. # Refresh
  6207. _refresh_screen(0, 0)
  6208. def show_error(message):
  6209. if sge.game.current_room is not None:
  6210. sge.game.pump_input()
  6211. sge.game.input_events = []
  6212. sge.game.mouse.visible = True
  6213. xsge_gui.show_message(message=message, title=_("Error"),
  6214. buttons=[_("Ok")], width=640)
  6215. sge.game.mouse.visible = False
  6216. else:
  6217. print(message)
  6218. def play_sound(sound, x=None, y=None, force=True):
  6219. if sound_volume and sound:
  6220. if x is None or y is None:
  6221. sound.play(volume=sound_volume, force=force)
  6222. else:
  6223. current_view = None
  6224. view_x = 0
  6225. view_y = 0
  6226. dist = 0
  6227. for view in sge.game.current_room.views:
  6228. vx = view.x + view.width / 2
  6229. vy = view.y + view.height / 2
  6230. new_dist = math.hypot(vx - x, vy - y)
  6231. if current_view is None or new_dist < dist:
  6232. current_view = view
  6233. view_x = vx
  6234. view_y = vy
  6235. dist = new_dist
  6236. bl = min(x, view_x)
  6237. bw = abs(x - view_x)
  6238. bt = min(y, view_y)
  6239. bh = abs(y - view_y)
  6240. for obj in sge.game.current_room.get_objects_at(bl, bt, bw, bh):
  6241. if isinstance(obj, Player):
  6242. new_dist = math.hypot(obj.x - x, obj.y - y)
  6243. if new_dist < dist:
  6244. view_x = obj.x
  6245. view_y = obj.y
  6246. dist = new_dist
  6247. if dist <= SOUND_MAX_RADIUS:
  6248. volume = 1
  6249. elif dist < SOUND_ZERO_RADIUS:
  6250. rng = SOUND_ZERO_RADIUS - SOUND_MAX_RADIUS
  6251. reldist = rng - (dist - SOUND_MAX_RADIUS)
  6252. volume = min(1, abs(reldist / rng))
  6253. else:
  6254. # No point in continuing; it's too far away
  6255. return
  6256. if stereo_enabled:
  6257. hdist = x - view_x
  6258. if abs(hdist) < SOUND_CENTERED_RADIUS:
  6259. balance = 0
  6260. else:
  6261. rng = SOUND_TILTED_RADIUS - SOUND_CENTERED_RADIUS
  6262. balance = max(-SOUND_TILT_LIMIT,
  6263. min(hdist / rng, SOUND_TILT_LIMIT))
  6264. else:
  6265. balance = 0
  6266. sound.play(volume=(volume * sound_volume), balance=balance,
  6267. force=force)
  6268. def play_music(music, force_restart=False):
  6269. """Play the given music file, starting with its start piece."""
  6270. if music_volume and music:
  6271. music_object = loaded_music.get(music)
  6272. if music_object is None:
  6273. try:
  6274. music_object = sge.snd.Music(os.path.join(DATA, "music",
  6275. music))
  6276. except OSError:
  6277. sge.snd.Music.clear_queue()
  6278. sge.snd.Music.stop()
  6279. return
  6280. else:
  6281. loaded_music[music] = music_object
  6282. assert music_object is not None
  6283. music_object.volume = music_volume
  6284. name, ext = os.path.splitext(music)
  6285. music_start = ''.join([name, "-start", ext])
  6286. music_start_object = loaded_music.get(music_start)
  6287. if music_start_object is None:
  6288. try:
  6289. music_start_object = sge.snd.Music(os.path.join(DATA, "music",
  6290. music_start))
  6291. except OSError:
  6292. pass
  6293. else:
  6294. loaded_music[music_start] = music_start_object
  6295. if music_start_object is not None:
  6296. music_start_object.volume = music_volume
  6297. if (force_restart or (not music_object.playing and
  6298. (music_start_object is None or
  6299. not music_start_object.playing))):
  6300. sge.snd.Music.clear_queue()
  6301. sge.snd.Music.stop()
  6302. if music_start_object is not None:
  6303. music_start_object.play()
  6304. music_object.queue(loops=None)
  6305. else:
  6306. music_object.play(loops=None)
  6307. else:
  6308. sge.snd.Music.clear_queue()
  6309. sge.snd.Music.stop()
  6310. def load_levelset(fname):
  6311. global current_levelset
  6312. global start_cutscene
  6313. global worldmap
  6314. global loaded_worldmaps
  6315. global levels
  6316. global loaded_levels
  6317. global tuxdolls_available
  6318. global main_area
  6319. def do_refresh():
  6320. # Refresh the screen, return whether the user pressed a key.
  6321. sge.game.pump_input()
  6322. r = False
  6323. while sge.game.input_events:
  6324. event = sge.game.input_events.pop(0)
  6325. if isinstance(event, sge.input.QuitRequest):
  6326. sge.game.end()
  6327. r = True
  6328. elif isinstance(event, (sge.input.KeyPress,
  6329. sge.input.JoystickButtonPress)):
  6330. r = True
  6331. gui_handler.event_step(0, 0)
  6332. _refresh_screen(0, 0)
  6333. return r
  6334. if current_levelset != fname:
  6335. current_levelset = fname
  6336. try:
  6337. with open(os.path.join(DATA, "levelsets", fname), 'r') as f:
  6338. data = json.load(f)
  6339. except Exception as e:
  6340. show_error(str(e))
  6341. start_cutscene = data.get("start_cutscene")
  6342. worldmap = data.get("worldmap")
  6343. levels = data.get("levels", [])
  6344. tuxdolls_available = data.get("tuxdolls_available", [])
  6345. main_area = None
  6346. def set_new_game():
  6347. global level_timers
  6348. global cleared_levels
  6349. global tuxdolls_found
  6350. global watched_timelines
  6351. global current_worldmap
  6352. global current_worldmap_space
  6353. global worldmap_entry_space
  6354. global current_level
  6355. global current_checkpoints
  6356. global score
  6357. if current_levelset is None:
  6358. load_levelset(DEFAULT_LEVELSET)
  6359. level_timers = {}
  6360. cleared_levels = []
  6361. tuxdolls_found = []
  6362. watched_timelines = []
  6363. current_worldmap = worldmap
  6364. current_worldmap_space = None
  6365. worldmap_entry_space = None
  6366. current_level = None
  6367. current_checkpoints = {}
  6368. score = 0
  6369. def write_to_disk():
  6370. # Write our saves and settings to disk.
  6371. keys_cfg = {"left": left_key, "right": right_key, "up": up_key,
  6372. "down": down_key, "jump": jump_key, "action": action_key,
  6373. "sneak": sneak_key, "menu": menu_key, "pause": pause_key}
  6374. js_cfg = {"left": left_js, "right": right_js, "up": up_js,
  6375. "down": down_js, "jump": jump_js, "action": action_js,
  6376. "sneak": sneak_js, "menu": menu_js, "pause": pause_js}
  6377. cfg = {"version": 1, "fullscreen": fullscreen,
  6378. "scale_method": scale_method,
  6379. "scale_proportional": scale_proportional,
  6380. "sound_volume": sound_volume, "music_volume": music_volume,
  6381. "stereo_enabled": stereo_enabled, "fps_enabled": fps_enabled,
  6382. "joystick_threshold": joystick_threshold, "keys": keys_cfg,
  6383. "joystick": js_cfg}
  6384. with open(os.path.join(CONFIG, "config.json"), 'w') as f:
  6385. json.dump(cfg, f, indent=4)
  6386. with open(os.path.join(LOCAL, "save_slots.json"), 'w') as f:
  6387. json.dump(save_slots, f, indent=4)
  6388. def save_game():
  6389. global save_slots
  6390. if current_save_slot is not None:
  6391. if levels:
  6392. completion = int(100 * (len(cleared_levels) + len(tuxdolls_found)) /
  6393. (len(levels) + len(tuxdolls_available)))
  6394. if completion == 0 and (cleared_levels or tuxdolls_found):
  6395. completion = 1
  6396. elif (completion == 100 and
  6397. (len(cleared_levels) < len(levels) or
  6398. len(tuxdolls_found) < len(tuxdolls_available))):
  6399. completion = 99
  6400. else:
  6401. completion = 100
  6402. save_slots[current_save_slot] = {
  6403. "levelset": current_levelset, "level_timers": level_timers,
  6404. "cleared_levels": cleared_levels, "tuxdolls_found": tuxdolls_found,
  6405. "watched_timelines": watched_timelines,
  6406. "current_worldmap": current_worldmap,
  6407. "current_worldmap_space": current_worldmap_space,
  6408. "worldmap_entry_space": worldmap_entry_space,
  6409. "current_level": current_level,
  6410. "current_checkpoints": current_checkpoints, "score": score,
  6411. "completion": completion}
  6412. write_to_disk()
  6413. def load_game():
  6414. global level_timers
  6415. global cleared_levels
  6416. global tuxdolls_found
  6417. global watched_timelines
  6418. global current_worldmap
  6419. global current_worldmap_space
  6420. global worldmap_entry_space
  6421. global current_level
  6422. global current_checkpoints
  6423. global score
  6424. if (current_save_slot is not None and
  6425. save_slots[current_save_slot] is not None and
  6426. save_slots[current_save_slot].get("levelset") is not None):
  6427. slot = save_slots[current_save_slot]
  6428. level_timers = slot.get("level_timers", {})
  6429. cleared_levels = slot.get("cleared_levels", [])
  6430. tuxdolls_found = slot.get("tuxdolls_found", [])
  6431. watched_timelines = slot.get("watched_timelines", [])
  6432. current_worldmap = slot.get("current_worldmap")
  6433. current_worldmap_space = slot.get("current_worldmap_space")
  6434. worldmap_entry_space = slot.get("worldmap_entry_space")
  6435. current_level = slot.get("current_level", 0)
  6436. current_checkpoints = slot.get("current_checkpoints", {})
  6437. score = slot.get("score", 0)
  6438. load_levelset(slot["levelset"])
  6439. else:
  6440. set_new_game()
  6441. def rush_save():
  6442. global level_timers
  6443. global cleared_levels
  6444. global score
  6445. global main_area
  6446. if main_area is not None:
  6447. if not cleared_levels and current_checkpoints.get(main_area) is None:
  6448. level_timers[main_area] = level_time_bonus
  6449. won = (isinstance(sge.game.current_room, Level) and
  6450. sge.game.current_room.won)
  6451. if won:
  6452. score += sge.game.current_room.points
  6453. sge.game.current_room.points = 0
  6454. if main_area not in cleared_levels:
  6455. cleared_levels.append(main_area)
  6456. if won or level_timers.setdefault(main_area, 0) < 0:
  6457. score += level_timers[main_area]
  6458. level_timers[main_area] = 0
  6459. save_game()
  6460. main_area = None
  6461. def start_levelset():
  6462. global current_level
  6463. global main_area
  6464. global level_cleared
  6465. global current_areas
  6466. current_areas = {}
  6467. main_area = None
  6468. level_cleared = True
  6469. if start_cutscene and current_level is None:
  6470. current_level = 0
  6471. level = Level.load(start_cutscene, True)
  6472. if level is not None:
  6473. level.start()
  6474. else:
  6475. return False
  6476. elif current_worldmap:
  6477. m = Worldmap.load(current_worldmap)
  6478. m.start()
  6479. else:
  6480. if current_level is None:
  6481. current_level = 0
  6482. if current_level < len(levels):
  6483. level = Level.load(levels[current_level], True)
  6484. if level is not None:
  6485. checkpoint = current_checkpoints.get(level.fname)
  6486. if checkpoint is not None:
  6487. area_name, area_spawn = checkpoint.split(':', 1)
  6488. level.spawn = area_spawn
  6489. level.start()
  6490. else:
  6491. return False
  6492. else:
  6493. print("Invalid save file: current level does not exist.")
  6494. return False
  6495. return True
  6496. def warp(dest):
  6497. if dest == "__map__":
  6498. sge.game.current_room.return_to_map(True)
  6499. else:
  6500. cr = sge.game.current_room
  6501. if ":" in dest:
  6502. level_f, spawn = dest.split(':', 1)
  6503. else:
  6504. level_f = None
  6505. spawn = dest
  6506. if level_f == "__main__":
  6507. level_f = main_area
  6508. if level_f:
  6509. level = sge.game.current_room.__class__.load(level_f, True)
  6510. else:
  6511. level = cr
  6512. if level is not None:
  6513. level.spawn = spawn
  6514. level.name = cr.name
  6515. level.points = cr.points
  6516. for nobj in level.objects[:]:
  6517. if isinstance(nobj, Player):
  6518. for cobj in cr.objects[:]:
  6519. if (isinstance(cobj, Player) and
  6520. cobj.player == nobj.player):
  6521. nobj.hp = cobj.hp
  6522. nobj.coins = cobj.coins
  6523. nobj.facing = cobj.facing
  6524. nobj.image_xscale = cobj.image_xscale
  6525. nobj.image_yscale = cobj.image_yscale
  6526. held_object = cobj.held_object
  6527. if held_object is not None:
  6528. cobj.drop_object()
  6529. cr.remove(held_object)
  6530. level.add(held_object)
  6531. nobj.pickup(held_object)
  6532. break
  6533. level.start()
  6534. else:
  6535. # Error occurred; restart the game.
  6536. rush_save()
  6537. sge.game.start_room.start()
  6538. def _data_decode(data, encoding, compression):
  6539. # Decode encoded data and return a list of integers it represents.
  6540. #
  6541. # Arguments:
  6542. #
  6543. # - ``data`` -- The data to decode.
  6544. # - ``encoding`` -- The encoding of the data. Can be ``"base64"``
  6545. # or ``"csv"``.
  6546. # - ``compression`` -- The compression method used. Valid
  6547. # compression methods are ``"gzip"`` and ``"zlib"``.
  6548. # Set to ``None`` for no compression.
  6549. if isinstance(data, str):
  6550. if encoding == "csv":
  6551. return [int(i) for i in data.strip().split(",")]
  6552. elif encoding == "base64":
  6553. data = base64.b64decode(data.strip().encode("latin1"))
  6554. if compression == "gzip":
  6555. data = gzip.decompress(data)
  6556. elif compression == "zlib":
  6557. data = zlib.decompress(data)
  6558. elif compression:
  6559. e = 'Compression type "{}" not supported.'.format(compression)
  6560. raise ValueError(e)
  6561. ndata = [i for i in data]
  6562. data = []
  6563. for i in range(0, len(ndata), 4):
  6564. n = (ndata[i] + ndata[i + 1] * (2 ** 8) +
  6565. ndata[i + 2] * (2 ** 16) + ndata[i + 3] * (2 ** 24))
  6566. data.append(n)
  6567. return data
  6568. else:
  6569. e = 'Encoding type "{}" not supported.'.format(encoding)
  6570. raise ValueError(e)
  6571. else:
  6572. return data
  6573. def _refresh_screen(time_passed, delta_mult):
  6574. # Wrapper for sge.game.refresh() which also calls the paused step
  6575. # events, in case they make any changes to the screen by way of
  6576. # window projections. Prevents flickering bugs.
  6577. sge.game.event_step(time_passed, delta_mult)
  6578. sge.game.current_room.event_step(time_passed, delta_mult)
  6579. for obj in sge.game.current_room.objects[:]:
  6580. obj.event_step(time_passed, delta_mult)
  6581. sge.game.refresh()
  6582. TYPES = {"solid_left": SolidLeft, "solid_right": SolidRight,
  6583. "solid_top": SolidTop, "solid_bottom": SolidBottom, "solid": Solid,
  6584. "slope_topleft": SlopeTopLeft, "slope_topright": SlopeTopRight,
  6585. "slope_bottomleft": SlopeBottomLeft,
  6586. "slope_bottomright": SlopeBottomRight,
  6587. "moving_platform": MovingPlatform, "spike_left": SpikeLeft,
  6588. "spike_right": SpikeRight, "spike_top": SpikeTop,
  6589. "spike_bottom": SpikeBottom, "death": Death, "level_end": LevelEnd,
  6590. "creatures": get_object, "hazards": get_object,
  6591. "special_blocks": get_object, "decoration_small": get_object,
  6592. "map_objects": get_object, "player": Player,
  6593. "walking_snowball": WalkingSnowball,
  6594. "bouncing_snowball": BouncingSnowball, "crystallo": Crystallo,
  6595. "walking_iceblock": WalkingIceblock, "spiky": Spiky,
  6596. "bomb": WalkingBomb, "jumpy": Jumpy,
  6597. "flying_snowball": FlyingSnowball, "flying_spiky": FlyingSpiky,
  6598. "icicle": Icicle, "steady_icicle": SteadyIcicle,
  6599. "raccot_icicle": RaccotIcicle, "krush": Krush, "krosh": Krosh,
  6600. "circoflame": CircoflamePath, "circoflamecenter": CircoflameCenter,
  6601. "snowman": Snowman, "raccot": Raccot, "fireflower": FireFlower,
  6602. "iceflower": IceFlower, "tuxdoll": TuxDoll, "rock": Rock,
  6603. "fixed_spring": FixedSpring, "spring": Spring,
  6604. "rusty_spring": RustySpring, "lantern": Lantern,
  6605. "timeline_switcher": TimelineSwitcher, "iceblock": Iceblock,
  6606. "boss_block": BossBlock, "brick": Brick, "coinbrick": CoinBrick,
  6607. "emptyblock": EmptyBlock, "itemblock": ItemBlock,
  6608. "hiddenblock": HiddenItemBlock, "infoblock": InfoBlock,
  6609. "thin_ice": ThinIce, "lava": Lava, "lava_surface": LavaSurface,
  6610. "goal": Goal, "goal_top": GoalTop, "coin": Coin, "warp": Warp,
  6611. "moving_platform_path": MovingPlatformPath,
  6612. "triggered_moving_platform_path": TriggeredMovingPlatformPath,
  6613. "flying_snowball_path": FlyingSnowballPath,
  6614. "flying_spiky_path": FlyingSpikyPath, "spawn": Spawn,
  6615. "checkpoint": Checkpoint, "bell": Bell, "door": Door,
  6616. "warp_spawn": WarpSpawn, "object_warp_spawn": ObjectWarpSpawn,
  6617. "map_player": MapPlayer, "map_level": MapSpace, "map_warp": MapWarp,
  6618. "map_path": MapPath, "map_water": MapWater}
  6619. print(_("Initializing game system..."))
  6620. Game(SCREEN_SIZE[0], SCREEN_SIZE[1], fps=FPS, delta=DELTA, delta_min=DELTA_MIN,
  6621. delta_max=DELTA_MAX, window_text="reTux {}".format(__version__),
  6622. window_icon=os.path.join(DATA, "images", "misc", "icon.png"))
  6623. print(_("Initializing GUI system..."))
  6624. xsge_gui.init()
  6625. gui_handler = xsge_gui.Handler()
  6626. if HELL:
  6627. menu_color = sge.gfx.Color((255, 64, 0, 192))
  6628. menu_text_color = sge.gfx.Color((255, 224, 160))
  6629. else:
  6630. menu_color = sge.gfx.Color((128, 128, 255, 192))
  6631. menu_text_color = sge.gfx.Color((160, 224, 255))
  6632. # Load sprites
  6633. print(_("Loading images..."))
  6634. d = os.path.join(DATA, "images", "objects", "tux")
  6635. tux_body_stand_sprite = sge.gfx.Sprite(
  6636. "tux_body_stand", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6637. tux_arms_stand_sprite = sge.gfx.Sprite(
  6638. "tux_arms_stand", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6639. tux_body_walk_sprite = sge.gfx.Sprite(
  6640. "tux_body_walk", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6641. tux_arms_walk_sprite = sge.gfx.Sprite(
  6642. "tux_arms_walk", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6643. tux_body_run_sprite = sge.gfx.Sprite(
  6644. "tux_body_run", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6645. tux_arms_run_sprite = sge.gfx.Sprite(
  6646. "tux_arms_run", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6647. tux_body_skid_sprite = sge.gfx.Sprite(
  6648. "tux_body_skid", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6649. tux_arms_skid_sprite = sge.gfx.Sprite(
  6650. "tux_arms_skid", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6651. tux_body_jump_sprite = sge.gfx.Sprite(
  6652. "tux_body_jump", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6653. tux_arms_jump_sprite = sge.gfx.Sprite(
  6654. "tux_arms_jump", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6655. tux_body_fall_sprite = tux_body_jump_sprite.copy()
  6656. tux_arms_fall_sprite = sge.gfx.Sprite(
  6657. "tux_arms_fall", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6658. tux_body_kick_sprite = sge.gfx.Sprite(
  6659. "tux_body_kick", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6660. tux_arms_kick_sprite = sge.gfx.Sprite(
  6661. "tux_arms_kick", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6662. tux_arms_grab_sprite = sge.gfx.Sprite(
  6663. "tux_arms_grab", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6664. tux_arms_skid_grab_sprite = sge.gfx.Sprite(
  6665. "tux_arms_skid_grab", d, origin_x=TUX_ORIGIN_X, origin_y=TUX_ORIGIN_Y)
  6666. tux_die_sprite = sge.gfx.Sprite("tux_die", d, origin_x=29, origin_y=11, fps=8)
  6667. tux_offscreen_sprite = sge.gfx.Sprite("tux_offscreen", d, origin_x=16)
  6668. if GOD:
  6669. def supertux_shader(x, y, red, green, blue, alpha):
  6670. threshold = 55
  6671. if (abs(red - green) < threshold and abs(red - blue) < threshold
  6672. and abs(green - blue) < threshold):
  6673. red = int(68 + 185*red/255)
  6674. green = int(82 + 173*green/255)
  6675. blue = int(253 * blue / 255)
  6676. return (red, green, blue, alpha)
  6677. for s in [tux_body_stand_sprite, tux_body_walk_sprite, tux_body_run_sprite,
  6678. tux_body_skid_sprite, tux_body_jump_sprite, tux_body_fall_sprite,
  6679. tux_body_kick_sprite, tux_arms_stand_sprite, tux_arms_walk_sprite,
  6680. tux_arms_run_sprite, tux_arms_skid_sprite, tux_arms_jump_sprite,
  6681. tux_arms_fall_sprite, tux_arms_kick_sprite,
  6682. tux_arms_grab_sprite, tux_arms_skid_grab_sprite, tux_die_sprite,
  6683. tux_offscreen_sprite]:
  6684. s.draw_shader(0, 0, s.width, s.height, supertux_shader)
  6685. tux_stand_sprite = tux_body_stand_sprite.copy()
  6686. tux_walk_sprite = tux_body_walk_sprite.copy()
  6687. tux_run_sprite = tux_body_run_sprite.copy()
  6688. tux_skid_sprite = tux_body_skid_sprite.copy()
  6689. tux_jump_sprite = tux_body_jump_sprite.copy()
  6690. tux_fall_sprite = tux_body_fall_sprite.copy()
  6691. tux_kick_sprite = tux_body_kick_sprite.copy()
  6692. for bs, a in [(tux_stand_sprite, tux_arms_stand_sprite),
  6693. (tux_walk_sprite, tux_arms_walk_sprite),
  6694. (tux_run_sprite, tux_arms_run_sprite),
  6695. (tux_skid_sprite, tux_arms_skid_sprite),
  6696. (tux_jump_sprite, tux_arms_jump_sprite),
  6697. (tux_fall_sprite, tux_arms_fall_sprite),
  6698. (tux_kick_sprite, tux_arms_kick_sprite)]:
  6699. for i in range(bs.frames):
  6700. bs.draw_sprite(a, i, bs.origin_x, bs.origin_y, i)
  6701. d = os.path.join(DATA, "images", "objects", "enemies")
  6702. snowball_walk_sprite = sge.gfx.Sprite("snowball", d, origin_x=19, origin_y=4,
  6703. fps=8, bbox_x=-13, bbox_y=0,
  6704. bbox_width=26, bbox_height=32)
  6705. bouncing_snowball_sprite = sge.gfx.Sprite(
  6706. "bouncing_snowball", d, origin_x=17, origin_y=0, fps=8, bbox_x=-13,
  6707. bbox_y=0, bbox_width=26, bbox_height=32)
  6708. snowball_squished_sprite = sge.gfx.Sprite("snowball_squished", d, origin_x=17,
  6709. origin_y=-19, bbox_x=-13, bbox_y=19,
  6710. bbox_width=26, bbox_height=13)
  6711. crystallo_walk_sprite = sge.gfx.Sprite(
  6712. "crystallo", d, origin_x=23, origin_y=-5, fps=8, bbox_x=-20, bbox_y=8,
  6713. bbox_width=40, bbox_height=24)
  6714. crystallo_squished_sprite = sge.gfx.Sprite(
  6715. "crystallo_squished", d, origin_x=23, origin_y=-17, bbox_x=-20, bbox_y=19,
  6716. bbox_width=40, bbox_height=13)
  6717. iceblock_walk_sprite = sge.gfx.Sprite(
  6718. "iceblock", d, origin_x=18, origin_y=6, fps=10, bbox_x=-13, bbox_y=1,
  6719. bbox_width=25, bbox_height=31)
  6720. iceblock_flat_sprite = sge.gfx.Sprite("iceblock_flat", d, origin_x=18,
  6721. origin_y=3, bbox_x=-16, bbox_y=4,
  6722. bbox_width=31, bbox_height=28)
  6723. spiky_walk_sprite = sge.gfx.Sprite("spiky", d, origin_x=22, origin_y=10, fps=8,
  6724. bbox_x=-13, bbox_y=0, bbox_width=26,
  6725. bbox_height=32)
  6726. spiky_iced_sprite = sge.gfx.Sprite("spiky_iced", d, origin_x=22, origin_y=10,
  6727. fps=THAW_FPS, bbox_x=-13, bbox_y=0,
  6728. bbox_width=26, bbox_height=32)
  6729. spiky_iced_sprite.append_frame()
  6730. spiky_iced_sprite.draw_sprite(spiky_walk_sprite, 1, spiky_walk_sprite.origin_x,
  6731. spiky_walk_sprite.origin_y, frame=1)
  6732. bomb_walk_sprite = sge.gfx.Sprite("bomb", d, origin_x=21, origin_y=8, fps=8,
  6733. bbox_x=-13, bbox_y=0, bbox_width=26,
  6734. bbox_height=32)
  6735. bomb_iced_sprite = sge.gfx.Sprite("bomb_iced", d, origin_x=21, origin_y=8,
  6736. fps=THAW_FPS, bbox_x=-13, bbox_y=0,
  6737. bbox_width=26, bbox_height=32)
  6738. bomb_iced_sprite.append_frame()
  6739. bomb_iced_sprite.draw_sprite(bomb_walk_sprite, 1, bomb_iced_sprite.origin_x,
  6740. bomb_iced_sprite.origin_y, frame=1)
  6741. bomb_ticking_sprite = sge.gfx.Sprite(
  6742. "bomb_ticking", d, origin_x=21, origin_y=5, bbox_x=-13, bbox_y=3,
  6743. bbox_width=26, bbox_height=29)
  6744. bomb_ticking_sprite.fps = bomb_ticking_sprite.frames / BOMB_TICK_TIME
  6745. jumpy_sprite = sge.gfx.Sprite("jumpy", d, origin_x=24, origin_y=13, bbox_x=-16,
  6746. bbox_y=0, bbox_width=32, bbox_height=32)
  6747. jumpy_bounce_sprite = sge.gfx.Sprite(
  6748. "jumpy_bounce", d, origin_x=24, origin_y=13, bbox_x=-16, bbox_y=0,
  6749. bbox_width=32, bbox_height=32)
  6750. jumpy_iced_sprite = sge.gfx.Sprite("jumpy_iced", d, origin_x=24, origin_y=13,
  6751. fps=THAW_FPS, bbox_x=-16, bbox_y=0,
  6752. bbox_width=32, bbox_height=32)
  6753. jumpy_iced_sprite.append_frame()
  6754. jumpy_iced_sprite.draw_sprite(jumpy_sprite, 0, jumpy_sprite.origin_x,
  6755. jumpy_sprite.origin_y, frame=1)
  6756. flying_snowball_sprite = sge.gfx.Sprite(
  6757. "flying_snowball", d, origin_x=20, origin_y=11, fps=15, bbox_x=-13,
  6758. bbox_y=0, bbox_width=26, bbox_height=32)
  6759. flying_snowball_squished_sprite = sge.gfx.Sprite(
  6760. "flying_snowball_squished", d, origin_x=20, origin_y=-11, bbox_x=-13,
  6761. bbox_y=11, bbox_width=26, bbox_height=21)
  6762. flying_spiky_sprite = sge.gfx.Sprite("flying_spiky", d, origin_x=24,
  6763. origin_y=14, fps=15, bbox_x=-13, bbox_y=0,
  6764. bbox_width=26, bbox_height=32)
  6765. flying_spiky_iced_sprite = sge.gfx.Sprite(
  6766. "flying_spiky_iced", d, origin_x=24, origin_y=14, fps=THAW_FPS, bbox_x=-13,
  6767. bbox_y=0, bbox_width=26, bbox_height=32)
  6768. flying_spiky_iced_sprite.append_frame()
  6769. flying_spiky_iced_sprite.draw_sprite(flying_spiky_sprite, 0,
  6770. flying_spiky_sprite.origin_x,
  6771. flying_spiky_sprite.origin_y, frame=1)
  6772. icicle_sprite = sge.gfx.Sprite("icicle", d, bbox_x=0, bbox_y=0, bbox_width=32,
  6773. bbox_height=48)
  6774. icicle_broken_sprite = sge.gfx.Sprite("icicle_broken", d, bbox_x=0, bbox_y=32,
  6775. bbox_width=32, bbox_height=16)
  6776. krush_sprite = sge.gfx.Sprite("krush", d, origin_x=1, bbox_x=0, bbox_y=0,
  6777. bbox_width=64, bbox_height=64)
  6778. krosh_sprite = sge.gfx.Sprite("krosh", d, origin_x=2, bbox_x=0, bbox_y=0,
  6779. bbox_width=128, bbox_height=128)
  6780. circoflame_sprite = sge.gfx.Sprite("circoflame", d, origin_x=16, origin_y=16,
  6781. fps=8, bbox_x=-8, bbox_y=-8, bbox_width=16,
  6782. bbox_height=16)
  6783. snowman_stand_sprite = sge.gfx.Sprite("snowman_stand", d, origin_x=28,
  6784. origin_y=43, bbox_x=-17, bbox_y=-40,
  6785. bbox_width=34, bbox_height=72)
  6786. snowman_walk_sprite = sge.gfx.Sprite("snowman_walk", d, origin_x=28,
  6787. origin_y=43, bbox_x=-17, bbox_y=-40,
  6788. bbox_width=34, bbox_height=72)
  6789. snowman_jump_sprite = sge.gfx.Sprite("snowman_jump", d, origin_x=28,
  6790. origin_y=43, bbox_x=-17, bbox_y=-40,
  6791. bbox_width=34, bbox_height=72)
  6792. snowman_hurt_walk_sprite = sge.gfx.Sprite("snowman_hurt_walk", d, origin_x=28,
  6793. origin_y=43, bbox_x=-17, bbox_y=-8,
  6794. bbox_width=34, bbox_height=40)
  6795. snowman_hurt_jump_sprite = sge.gfx.Sprite("snowman_hurt_jump", d, origin_x=28,
  6796. origin_y=43, bbox_x=-17, bbox_y=-8,
  6797. bbox_width=34, bbox_height=40)
  6798. raccot_stand_sprite = sge.gfx.Sprite("raccot_stand", d, origin_x=41,
  6799. origin_y=74, bbox_x=-30, bbox_y=-64,
  6800. bbox_width=60, bbox_height=96)
  6801. raccot_walk_sprite = sge.gfx.Sprite("raccot_walk", d, origin_x=54, origin_y=76,
  6802. bbox_x=-30, bbox_y=-64, bbox_width=60,
  6803. bbox_height=96)
  6804. raccot_stomp_sprite = sge.gfx.Sprite("raccot_stomp", d, origin_x=41,
  6805. origin_y=77, bbox_x=-30, bbox_y=-64,
  6806. bbox_width=60, bbox_height=96)
  6807. raccot_hop_sprite = sge.gfx.Sprite("raccot_hop", d, origin_x=41, origin_y=74,
  6808. bbox_x=-30, bbox_y=-64, bbox_width=60,
  6809. bbox_height=96)
  6810. raccot_jump_sprite = sge.gfx.Sprite("raccot_jump", d, origin_x=60, origin_y=72,
  6811. bbox_x=-30, bbox_y=-64, bbox_width=60,
  6812. bbox_height=96)
  6813. d = os.path.join(DATA, "images", "objects", "bonus")
  6814. bonus_empty_sprite = sge.gfx.Sprite("bonus_empty", d)
  6815. bonus_full_sprite = sge.gfx.Sprite("bonus_full", d, fps=8)
  6816. brick_sprite = sge.gfx.Sprite("brick", d)
  6817. brick_shard_sprite = sge.gfx.Sprite("brick_shard", d)
  6818. coin_sprite = sge.gfx.Sprite("coin", d, fps=8)
  6819. fire_flower_sprite = sge.gfx.Sprite("fire_flower", d, origin_x=16, origin_y=16,
  6820. fps=8, bbox_x=-8, bbox_y=-8, bbox_width=16,
  6821. bbox_height=24)
  6822. ice_flower_sprite = sge.gfx.Sprite("ice_flower", d, origin_x=16, origin_y=16,
  6823. fps=4, bbox_x=-8, bbox_y=-8, bbox_width=16,
  6824. bbox_height=24)
  6825. tuxdoll_sprite = sge.gfx.Sprite("tuxdoll", d, origin_x=16, origin_y=16,
  6826. bbox_x=-16, bbox_y=-16, bbox_width=32,
  6827. bbox_height=32)
  6828. tuxdoll_transparent_sprite = tuxdoll_sprite.copy()
  6829. eraser = sge.gfx.Sprite(width=tuxdoll_transparent_sprite.width,
  6830. height=tuxdoll_transparent_sprite.height)
  6831. eraser.draw_rectangle(0, 0, eraser.width, eraser.height,
  6832. fill=sge.gfx.Color((0, 0, 0, 128)))
  6833. tuxdoll_transparent_sprite.draw_sprite(eraser, 0, 0, 0,
  6834. blend_mode=sge.BLEND_RGBA_SUBTRACT)
  6835. del eraser
  6836. tuxdoll_shadow_sprite = tuxdoll_sprite.copy()
  6837. darkener = sge.gfx.Sprite(width=tuxdoll_shadow_sprite.width,
  6838. height=tuxdoll_shadow_sprite.height)
  6839. darkener.draw_rectangle(0, 0, darkener.width, darkener.height,
  6840. fill=sge.gfx.Color("black"))
  6841. tuxdoll_shadow_sprite.draw_sprite(darkener, 0, 0, 0,
  6842. blend_mode=sge.BLEND_RGB_MINIMUM)
  6843. del darkener
  6844. d = os.path.join(DATA, "images", "objects", "decoration")
  6845. lava_body_sprite = sge.gfx.Sprite("lava_body", d, transparent=False, fps=5)
  6846. lava_surface_sprite = sge.gfx.Sprite("lava_surface", d, fps=5)
  6847. goal_sprite = sge.gfx.Sprite("goal", d, fps=8)
  6848. goal_top_sprite = sge.gfx.Sprite("goal_top", d, fps=8)
  6849. d = os.path.join(DATA, "images", "objects", "spring")
  6850. fixed_spring_sprite = sge.gfx.Sprite(
  6851. "fixed_spring", d, origin_x=16, origin_y=16, bbox_x=-16, bbox_y=-7,
  6852. bbox_width=32, bbox_height=23)
  6853. fixed_spring_expand_sprite = sge.gfx.Sprite(
  6854. "fixed_spring_expand", d, origin_x=16, origin_y=16, fps=16, bbox_x=-16,
  6855. bbox_y=-7, bbox_width=32, bbox_height=23)
  6856. spring_sprite = sge.gfx.Sprite("spring", d, origin_x=16, origin_y=16,
  6857. bbox_x=-16, bbox_y=-7, bbox_width=32,
  6858. bbox_height=23)
  6859. spring_expand_sprite = sge.gfx.Sprite(
  6860. "spring_expand", d, origin_x=16, origin_y=16, fps=16, bbox_x=-16,
  6861. bbox_y=-7, bbox_width=32, bbox_height=23)
  6862. rusty_spring_sprite = sge.gfx.Sprite(
  6863. "rusty_spring", d, origin_x=16, origin_y=16, bbox_x=-16, bbox_y=-7,
  6864. bbox_width=32, bbox_height=23)
  6865. rusty_spring_expand_sprite = sge.gfx.Sprite(
  6866. "rusty_spring_expand", d, origin_x=16, origin_y=26, fps=16, bbox_x=-16,
  6867. bbox_y=-7, bbox_width=32, bbox_height=23)
  6868. rusty_spring_dead_sprite = sge.gfx.Sprite(
  6869. "rusty_spring_dead", d, origin_x=16, origin_y=26, bbox_x=-16, bbox_y=-7,
  6870. bbox_width=32, bbox_height=23)
  6871. d = os.path.join(DATA, "images", "objects", "misc")
  6872. platform_sprite = sge.gfx.Sprite("platform", d)
  6873. rock_sprite = sge.gfx.Sprite("rock", d)
  6874. lantern_sprite = sge.gfx.Sprite("lantern", d, origin_x=20, origin_y=9, fps=10,
  6875. bbox_x=-16, bbox_y=0, bbox_width=32,
  6876. bbox_height=32)
  6877. iceblock_sprite = sge.gfx.Sprite("iceblock", d)
  6878. iceblock_melt_sprite = sge.gfx.Sprite("iceblock_melt", d, fps=30)
  6879. thin_ice_sprite = sge.gfx.Sprite("thin_ice", d, fps=0)
  6880. thin_ice_break_sprite = sge.gfx.Sprite("thin_ice_break", d, fps=8)
  6881. boss_block_sprite = sge.gfx.Sprite("boss_block", d, transparent=False,
  6882. origin_x=16, origin_y=16)
  6883. bell_sprite = sge.gfx.Sprite("bell", d, origin_x=-1, fps=10, bbox_x=0,
  6884. bbox_width=32, bbox_height=32)
  6885. door_sprite = sge.gfx.Sprite("door", d, origin_x=25, origin_y=68, fps=10)
  6886. door_back_sprite = sge.gfx.Sprite("door_back", d, origin_x=21, origin_y=41,
  6887. transparent=False)
  6888. d = os.path.join(DATA, "images", "portraits")
  6889. portrait_sprites = {}
  6890. for fname in os.listdir(d):
  6891. root, ext = os.path.splitext(fname)
  6892. try:
  6893. portrait = sge.gfx.Sprite(root, d)
  6894. except OSError:
  6895. pass
  6896. else:
  6897. portrait_sprites[root] = portrait
  6898. d = os.path.join(DATA, "images", "misc")
  6899. logo_sprite = sge.gfx.Sprite("logo", d, origin_x=140)
  6900. fire_bullet_sprite = sge.gfx.Sprite("fire_bullet", d, origin_x=8, origin_y=8,
  6901. fps=8, bbox_x=-8, bbox_width=16)
  6902. ice_bullet_sprite = sge.gfx.Sprite("ice_bullet", d, origin_x=8, origin_y=7,
  6903. bbox_width=32)
  6904. ice_bullet_break_sprite = sge.gfx.Sprite("ice_bullet_break", d, origin_x=8,
  6905. origin_y=7, fps=24)
  6906. explosion_sprite = sge.gfx.Sprite("explosion", d, origin_x=32, origin_y=19,
  6907. fps=15, bbox_x=-28, bbox_y=-11,
  6908. bbox_width=56, bbox_height=48)
  6909. smoke_puff_sprite = sge.gfx.Sprite("smoke_puff", d, width=48, height=48,
  6910. origin_x=24, origin_y=24, fps=24)
  6911. smoke_plume_sprite = sge.gfx.Sprite("smoke_plume", d, width=64, height=64,
  6912. origin_x=32, origin_y=32, fps=30)
  6913. fireball_smoke_sprite = sge.gfx.Sprite("smoke_plume", d, width=16, height=16,
  6914. origin_x=8, origin_y=8, fps=30)
  6915. item_spawn_cloud_sprite = sge.gfx.Sprite("smoke_plume", d, width=80, height=80,
  6916. origin_x=40, origin_y=40, fps=30)
  6917. item_spawn_cloud_sprite.delete_frame(0)
  6918. light_sprite = sge.gfx.Sprite("light", d, origin_x=192, origin_y=192)
  6919. light_small_sprite = sge.gfx.Sprite("light_small", d, origin_x=64, origin_y=64)
  6920. light_tiny_sprite = sge.gfx.Sprite("light_tiny", d, origin_x=32, origin_y=32)
  6921. heart_empty_sprite = sge.gfx.Sprite("heart_empty", d, origin_y=-1)
  6922. heart_full_sprite = sge.gfx.Sprite("heart_full", d, origin_y=-1)
  6923. fire_flower_light_sprite = light_small_sprite.copy()
  6924. blender = sge.gfx.Sprite(width=fire_flower_light_sprite.width,
  6925. height=fire_flower_light_sprite.height)
  6926. blender.draw_rectangle(0, 0, blender.width, blender.height,
  6927. fill=sge.gfx.Color("#F1670B"))
  6928. fire_flower_light_sprite.draw_sprite(blender, 0, 0, 0,
  6929. blend_mode=sge.BLEND_RGB_MULTIPLY)
  6930. del blender
  6931. ice_flower_light_sprite = light_small_sprite.copy()
  6932. blender = sge.gfx.Sprite(width=ice_flower_light_sprite.width,
  6933. height=ice_flower_light_sprite.height)
  6934. blender.draw_rectangle(0, 0, blender.width, blender.height,
  6935. fill=sge.gfx.Color("#7CF8FA"))
  6936. ice_flower_light_sprite.draw_sprite(blender, 0, 0, 0,
  6937. blend_mode=sge.BLEND_RGB_MULTIPLY)
  6938. del blender
  6939. fireball_light_sprite = light_tiny_sprite.copy()
  6940. blender = sge.gfx.Sprite(width=fireball_light_sprite.width,
  6941. height=fireball_light_sprite.height)
  6942. blender.draw_rectangle(0, 0, blender.width, blender.height,
  6943. fill=sge.gfx.Color("#FF5B11"))
  6944. fireball_light_sprite.draw_sprite(blender, 0, 0, 0,
  6945. blend_mode=sge.BLEND_RGB_MULTIPLY)
  6946. del blender
  6947. explosion_light_sprite = light_small_sprite.copy()
  6948. blender = sge.gfx.Sprite(width=fire_flower_light_sprite.width,
  6949. height=fire_flower_light_sprite.height)
  6950. blender.draw_rectangle(0, 0, blender.width, blender.height,
  6951. fill=sge.gfx.Color("#FFBC00"))
  6952. explosion_light_sprite.draw_sprite(blender, 0, 0, 0,
  6953. blend_mode=sge.BLEND_RGB_MULTIPLY)
  6954. del blender
  6955. circoflame_light_sprite = light_tiny_sprite.copy()
  6956. blender = sge.gfx.Sprite(width=fireball_light_sprite.width,
  6957. height=fireball_light_sprite.height)
  6958. blender.draw_rectangle(0, 0, blender.width, blender.height,
  6959. fill=sge.gfx.Color("#D5CD49"))
  6960. circoflame_light_sprite.draw_sprite(blender, 0, 0, 0,
  6961. blend_mode=sge.BLEND_RGB_MULTIPLY)
  6962. del blender
  6963. coin_icon_sprite = coin_sprite.copy()
  6964. coin_icon_sprite.width = 16
  6965. coin_icon_sprite.height = 16
  6966. coin_icon_sprite.origin_y = -1
  6967. d = os.path.join(DATA, "images", "worldmap")
  6968. worldmap_tux_sprite = sge.gfx.Sprite(
  6969. "tux", d, origin_x=1, origin_y=12, bbox_x=0, bbox_y=0, bbox_width=32,
  6970. bbox_height=32)
  6971. worldmap_tux_walk_sprite = sge.gfx.Sprite(
  6972. "tux_walk", d, origin_x=1, origin_y=12, fps=16, bbox_x=0, bbox_y=0,
  6973. bbox_width=32, bbox_height=32)
  6974. worldmap_level_complete_sprite = sge.gfx.Sprite("level_complete", d)
  6975. worldmap_level_incomplete_sprite = sge.gfx.Sprite("level_incomplete", d, fps=8)
  6976. worldmap_warp_sprite = sge.gfx.Sprite("warp", d, fps=3)
  6977. worldmap_water_sprite = sge.gfx.Sprite("water", d, transparent=False, fps=8)
  6978. # Load backgrounds
  6979. d = os.path.join(DATA, "images", "backgrounds")
  6980. layers = []
  6981. if not NO_BACKGROUNDS:
  6982. layers = [
  6983. sge.gfx.BackgroundLayer(
  6984. sge.gfx.Sprite("arctis1-middle", d), 0, 0, -100000,
  6985. xscroll_rate=0.5, yscroll_rate=0.5, repeat_left=True,
  6986. repeat_right=True),
  6987. sge.gfx.BackgroundLayer(
  6988. sge.gfx.Sprite("arctis1-bottom", d, transparent=False), 0, 352,
  6989. -100000, xscroll_rate=0.5, yscroll_rate=0.5, repeat_left=True,
  6990. repeat_right=True, repeat_down=True),
  6991. sge.gfx.BackgroundLayer(
  6992. sge.gfx.Sprite("arctis2-middle", d), 0, 0, -100010,
  6993. xscroll_rate=0.25, yscroll_rate=0.25, repeat_left=True,
  6994. repeat_right=True),
  6995. sge.gfx.BackgroundLayer(
  6996. sge.gfx.Sprite("arctis2-bottom", d, transparent=False), 0, 352,
  6997. -100010, xscroll_rate=0.25, yscroll_rate=0.25, repeat_left=True,
  6998. repeat_right=True, repeat_down=True),
  6999. sge.gfx.BackgroundLayer(
  7000. sge.gfx.Sprite("arctis3", d, transparent=False), 0, 0, -100020,
  7001. xscroll_rate=0, yscroll_rate=0, repeat_left=True,
  7002. repeat_right=True)]
  7003. backgrounds["arctis"] = sge.gfx.Background(layers,
  7004. sge.gfx.Color((109, 92, 230)))
  7005. if not NO_BACKGROUNDS:
  7006. cave_edge_spr = sge.gfx.Sprite("cave-edge", d, transparent=False)
  7007. layers = [
  7008. sge.gfx.BackgroundLayer(
  7009. sge.gfx.Sprite("cave-middle", d, transparent=False), 0, 128,
  7010. -100000, xscroll_rate=0.7, yscroll_rate=0.7, repeat_left=True,
  7011. repeat_right=True),
  7012. sge.gfx.BackgroundLayer(
  7013. cave_edge_spr, 0, 0, -100000, xscroll_rate=0.7, yscroll_rate=0.7,
  7014. repeat_left=True, repeat_right=True, repeat_up=True),
  7015. sge.gfx.BackgroundLayer(
  7016. cave_edge_spr, 0, 256, -100000, xscroll_rate=0.7, yscroll_rate=0.7,
  7017. repeat_left=True, repeat_right=True, repeat_down=True)]
  7018. del cave_edge_spr
  7019. backgrounds["cave"] = sge.gfx.Background(layers, sge.gfx.Color("#024"))
  7020. if not NO_BACKGROUNDS:
  7021. nightsky_bottom_spr = sge.gfx.Sprite("nightsky-bottom", d,
  7022. transparent=False)
  7023. layers = [
  7024. sge.gfx.BackgroundLayer(
  7025. sge.gfx.Sprite("nightsky1-middle", d), 0, 306, -100000,
  7026. xscroll_rate=0.5, yscroll_rate=0.5, repeat_left=True,
  7027. repeat_right=True),
  7028. sge.gfx.BackgroundLayer(
  7029. nightsky_bottom_spr, 0, 664, -100000, xscroll_rate=0.5,
  7030. yscroll_rate=0.5, repeat_left=True, repeat_right=True,
  7031. repeat_down=True),
  7032. sge.gfx.BackgroundLayer(
  7033. sge.gfx.Sprite("nightsky2-middle", d, transparent=False), 0, 0,
  7034. -100010, xscroll_rate=0.25, yscroll_rate=0.25, repeat_left=True,
  7035. repeat_right=True),
  7036. sge.gfx.BackgroundLayer(
  7037. sge.gfx.Sprite("nightsky2-top", d, transparent=False), 0, -600,
  7038. -100010, xscroll_rate=0.25, yscroll_rate=0.25, repeat_left=True,
  7039. repeat_right=True, repeat_up=True),
  7040. sge.gfx.BackgroundLayer(
  7041. nightsky_bottom_spr, 0, 600, -100010, xscroll_rate=0.25,
  7042. yscroll_rate=0.25, repeat_left=True, repeat_right=True,
  7043. repeat_down=True)]
  7044. del nightsky_bottom_spr
  7045. backgrounds["nightsky"] = sge.gfx.Background(layers, sge.gfx.Color("#002"))
  7046. if not NO_BACKGROUNDS:
  7047. layers = [
  7048. sge.gfx.BackgroundLayer(
  7049. sge.gfx.Sprite("bluemountain-middle", d, transparent=False), 0,
  7050. -128, -100000, xscroll_rate=0.1, yscroll_rate=0.1,
  7051. repeat_left=True, repeat_right=True),
  7052. sge.gfx.BackgroundLayer(
  7053. sge.gfx.Sprite("bluemountain-top", d, transparent=False), 0, -704,
  7054. -100000, xscroll_rate=0.1, yscroll_rate=0.1, repeat_left=True,
  7055. repeat_right=True, repeat_up=True),
  7056. sge.gfx.BackgroundLayer(
  7057. sge.gfx.Sprite("bluemountain-bottom", d, transparent=False), 0,
  7058. 448, -100000, xscroll_rate=0.1, yscroll_rate=0.1, repeat_left=True,
  7059. repeat_right=True, repeat_down=True)]
  7060. backgrounds["bluemountain"] = sge.gfx.Background(layers,
  7061. sge.gfx.Color((86, 142, 206)))
  7062. if not NO_BACKGROUNDS:
  7063. layers = [
  7064. sge.gfx.BackgroundLayer(
  7065. sge.gfx.Sprite("snowmountains", d), 0, 0, -100000,
  7066. xscroll_rate=0.25, yscroll_rate=0, repeat_left=True,
  7067. repeat_right=True),
  7068. sge.gfx.BackgroundLayer(
  7069. sge.gfx.Sprite("snowmountains-bottom", d, transparent=False), 0,
  7070. 720, -100000, xscroll_rate=0.25, yscroll_rate=0, repeat_left=True,
  7071. repeat_right=True, repeat_down=True),
  7072. sge.gfx.BackgroundLayer(
  7073. sge.gfx.Sprite("snowmountains-sky", d, transparent=False), 0, 0,
  7074. -100010, xscroll_rate=0, yscroll_rate=0, repeat_left=True,
  7075. repeat_right=True)]
  7076. backgrounds["snowmountains"] = sge.gfx.Background(layers,
  7077. sge.gfx.Color("#DAD8F5"))
  7078. if not NO_BACKGROUNDS:
  7079. layers = [
  7080. sge.gfx.BackgroundLayer(
  7081. sge.gfx.Sprite("cloud-mountains-midground", d), 0, -366, -100010,
  7082. xscroll_rate=0.25, yscroll_rate=0.1, repeat_left=True,
  7083. repeat_right=True),
  7084. sge.gfx.BackgroundLayer(
  7085. sge.gfx.Sprite("cloud-mountains-bottom", d), 0, 723, -100000,
  7086. xscroll_rate=0.25, yscroll_rate=0.1, repeat_left=True,
  7087. repeat_right=True, repeat_down=True),
  7088. sge.gfx.BackgroundLayer(
  7089. sge.gfx.Sprite("cloud-mountains-foreground", d), 0, -366, -100000,
  7090. xscroll_rate=0.2, yscroll_rate=0.1, repeat_left=True,
  7091. repeat_right=True),
  7092. sge.gfx.BackgroundLayer(
  7093. sge.gfx.Sprite("cloud-mountains-background", d), 0, -366, -100020,
  7094. xscroll_rate=0.1, yscroll_rate=0.1, repeat_left=True,
  7095. repeat_right=True)]
  7096. backgrounds["cloudmountains"] = sge.gfx.Background(layers,
  7097. sge.gfx.Color("#769094"))
  7098. castle_spr = sge.gfx.Sprite("castle", d)
  7099. castle_bottom_spr = sge.gfx.Sprite("castle-bottom", d, transparent=False)
  7100. for i in list(backgrounds.keys()):
  7101. if not NO_BACKGROUNDS:
  7102. layers = backgrounds[i].layers + [
  7103. sge.gfx.BackgroundLayer(castle_spr, 0, -64, -99000,
  7104. xscroll_rate=0.75, yscroll_rate=1,
  7105. repeat_left=True, repeat_right=True,
  7106. repeat_up=True),
  7107. sge.gfx.BackgroundLayer(castle_bottom_spr, 0, 544, -99000,
  7108. xscroll_rate=0.75, yscroll_rate=1,
  7109. repeat_left=True, repeat_right=True,
  7110. repeat_down=True)]
  7111. backgrounds["{}_castle".format(i)] = sge.gfx.Background(
  7112. layers, backgrounds[i].color)
  7113. else:
  7114. backgrounds["{}_castle".format(i)] = sge.gfx.Background(
  7115. [], sge.gfx.Color("#221833"))
  7116. del castle_spr
  7117. del castle_bottom_spr
  7118. # Load fonts
  7119. print(_("Loading fonts..."))
  7120. chars = ([None] + [chr(i) for i in range(33, 127)] + ['\u2190', ' ']
  7121. + [chr(i) for i in range(161, 384)])
  7122. #/ The default ReTux font is a sprite font which supports Latin
  7123. #/ character sets. If your translation needs more characters (as would
  7124. #/ be the case for Japanese or Chinese, for example), please add a font
  7125. #/ file to the data/fonts directory for use with the language being
  7126. #/ translated to and specify the font file name. This will cause the
  7127. #/ font file to be used instead of the default sprite font. If the
  7128. #/ sprite font is sufficient, leave this as-is.
  7129. if _("font_file") == "font_file":
  7130. font_sprite = sge.gfx.Sprite.from_tileset(
  7131. os.path.join(DATA, "images", "misc", "font.png"), columns=16, rows=20,
  7132. width=16, height=18)
  7133. font = sge.gfx.Font.from_sprite(font_sprite, chars, size=18)
  7134. font_small_sprite = sge.gfx.Sprite.from_tileset(
  7135. os.path.join(DATA, "images", "misc", "font_small.png"), columns=16,
  7136. rows=20, width=8, height=9)
  7137. font_small = sge.gfx.Font.from_sprite(font_small_sprite, chars, size=9)
  7138. font_big_sprite = sge.gfx.Sprite.from_tileset(
  7139. os.path.join(DATA, "images", "misc", "font_big.png"), columns=16,
  7140. rows=20, width=20, height=22)
  7141. font_big = sge.gfx.Font.from_sprite(font_big_sprite, chars, size=22)
  7142. else:
  7143. text_outline_thickness = 1
  7144. fname = os.path.join(DATA, "fonts", _("font_file"))
  7145. font = sge.gfx.Font(fname, size=18)
  7146. font_small = sge.gfx.Font(fname, size=9)
  7147. font_big = sge.gfx.Font(fname, size=22)
  7148. del fname
  7149. # Load sounds
  7150. jump_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "jump.wav"))
  7151. bigjump_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "bigjump.wav"))
  7152. skid_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "skid.wav"))
  7153. hurt_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "hurt.wav"))
  7154. kill_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "kill.wav"))
  7155. brick_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "brick.wav"))
  7156. coin_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "coin.wav"))
  7157. find_powerup_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "upgrade.wav"))
  7158. tuxdoll_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "tuxdoll.wav"))
  7159. s = sge.snd.Sound(os.path.join(DATA, "sounds", "ice_crack-0.wav"))
  7160. ice_crack_sounds = [
  7161. s,
  7162. sge.snd.Sound(os.path.join(DATA, "sounds", "ice_crack-1.wav"), parent=s),
  7163. sge.snd.Sound(os.path.join(DATA, "sounds", "ice_crack-2.wav"), parent=s),
  7164. sge.snd.Sound(os.path.join(DATA, "sounds", "ice_crack-3.wav"), parent=s)]
  7165. ice_shatter_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7166. "ice_shatter.wav"))
  7167. heal_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "heal.wav"))
  7168. shoot_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "shoot.wav"))
  7169. fire_dissipate_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7170. "fire_dissipate.wav"))
  7171. icebullet_break_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7172. "icebullet_break.wav"))
  7173. squish_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "squish.wav"))
  7174. stomp_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "stomp.wav"))
  7175. sizzle_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "sizzle.ogg"))
  7176. spring_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "spring.wav"))
  7177. rusty_spring_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7178. "rusty_spring.wav"))
  7179. kick_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "kick.wav"))
  7180. iceblock_bump_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7181. "iceblock_bump.wav"))
  7182. icicle_shake_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7183. "icicle_shake.wav"))
  7184. icicle_crash_sound = sge.snd.Sound(os.path.join(DATA, "sounds",
  7185. "icicle_crash.wav"))
  7186. explosion_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "explosion.wav"))
  7187. fall_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "fall.wav"))
  7188. yeti_gna_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "yeti_gna.wav"))
  7189. yeti_roar_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "yeti_roar.wav"))
  7190. pop_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "pop.wav"))
  7191. bell_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "bell.wav"))
  7192. pipe_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "pipe.ogg"))
  7193. warp_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "warp.wav"))
  7194. door_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "door.wav"))
  7195. door_shut_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "door_shut.wav"))
  7196. pause_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "select.ogg"))
  7197. select_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "select.ogg"))
  7198. confirm_sound = coin_sound
  7199. cancel_sound = pop_sound
  7200. error_sound = hurt_sound
  7201. type_sound = sge.snd.Sound(os.path.join(DATA, "sounds", "type.wav"))
  7202. # Load music
  7203. level_win_music = sge.snd.Music(os.path.join(DATA, "music", "leveldone.ogg"))
  7204. loaded_music["leveldone.ogg"] = level_win_music
  7205. # Create objects
  7206. coin_animation = sge.dsp.Object(0, 0, sprite=coin_sprite, visible=False,
  7207. tangible=False)
  7208. bonus_animation = sge.dsp.Object(0, 0, sprite=bonus_empty_sprite,
  7209. visible=False, tangible=False)
  7210. lava_animation = sge.dsp.Object(0, 0, sprite=lava_body_sprite, visible=False,
  7211. tangible=False)
  7212. goal_animation = sge.dsp.Object(0, 0, sprite=goal_sprite, visible=False,
  7213. tangible=False)
  7214. # Create rooms
  7215. if LEVEL:
  7216. sge.game.start_room = LevelTester.load(LEVEL, True)
  7217. if sge.game.start_room is None:
  7218. sys.exit()
  7219. elif RECORD:
  7220. sge.game.start_room = LevelRecorder.load(RECORD, True)
  7221. if sge.game.start_room is None:
  7222. sys.exit()
  7223. else:
  7224. sge.game.start_room = TitleScreen.load(
  7225. os.path.join("special", "title_screen.json"), True)
  7226. sge.game.mouse.visible = False
  7227. if not os.path.exists(CONFIG):
  7228. os.makedirs(CONFIG)
  7229. if not os.path.exists(LOCAL):
  7230. os.makedirs(LOCAL)
  7231. # Save error messages to a text file (so they aren't lost).
  7232. if not PRINT_ERRORS:
  7233. stderr = os.path.join(LOCAL, "stderr.txt")
  7234. if not os.path.isfile(stderr) or os.path.getsize(stderr) > 1000000:
  7235. sys.stderr = open(stderr, 'w')
  7236. else:
  7237. sys.stderr = open(stderr, 'a')
  7238. dt = datetime.datetime.now()
  7239. sys.stderr.write("\n{}-{}-{} {}:{}:{}\n".format(
  7240. dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second))
  7241. del dt
  7242. try:
  7243. with open(os.path.join(CONFIG, "config.json")) as f:
  7244. cfg = json.load(f)
  7245. except (OSError, ValueError):
  7246. cfg = {}
  7247. finally:
  7248. cfg_version = cfg.get("version", 0)
  7249. fullscreen = cfg.get("fullscreen", fullscreen)
  7250. sge.game.fullscreen = fullscreen
  7251. scale_method = cfg.get("scale_method", scale_method)
  7252. sge.game.scale_method = scale_method
  7253. scale_proportional = cfg.get("scale_proportional", scale_proportional)
  7254. sge.game.scale_proportional = scale_proportional
  7255. sound_volume = cfg.get("sound_volume", sound_volume)
  7256. music_volume = cfg.get("music_volume", music_volume)
  7257. stereo_enabled = cfg.get("stereo_enabled", stereo_enabled)
  7258. fps_enabled = cfg.get("fps_enabled", fps_enabled)
  7259. joystick_threshold = cfg.get("joystick_threshold", joystick_threshold)
  7260. xsge_gui.joystick_threshold = joystick_threshold
  7261. if cfg_version >= 1:
  7262. keys_cfg = cfg.get("keys", {})
  7263. left_key = keys_cfg.get("left", left_key)
  7264. right_key = keys_cfg.get("right", right_key)
  7265. up_key = keys_cfg.get("up", up_key)
  7266. down_key = keys_cfg.get("down", down_key)
  7267. jump_key = keys_cfg.get("jump", jump_key)
  7268. action_key = keys_cfg.get("action", action_key)
  7269. sneak_key = keys_cfg.get("sneak", sneak_key)
  7270. menu_key = keys_cfg.get("menu", menu_key)
  7271. pause_key = keys_cfg.get("pause", pause_key)
  7272. js_cfg = cfg.get("joystick", {})
  7273. left_js = [[tuple(j) for j in js]
  7274. for js in js_cfg.get("left", left_js)]
  7275. right_js = [[tuple(j) for j in js]
  7276. for js in js_cfg.get("right", right_js)]
  7277. up_js = [[tuple(j) for j in js] for js in js_cfg.get("up", up_js)]
  7278. down_js = [[tuple(j) for j in js]
  7279. for js in js_cfg.get("down", down_js)]
  7280. jump_js = [[tuple(j) for j in js]
  7281. for js in js_cfg.get("jump", jump_js)]
  7282. action_js = [[tuple(j) for j in js]
  7283. for js in js_cfg.get("action", action_js)]
  7284. sneak_js = [[tuple(j) for j in js]
  7285. for js in js_cfg.get("sneak", sneak_js)]
  7286. menu_js = [[tuple(j) for j in js]
  7287. for js in js_cfg.get("menu", menu_js)]
  7288. pause_js = [[tuple(j) for j in js]
  7289. for js in js_cfg.get("pause", pause_js)]
  7290. else:
  7291. keys_cfg = cfg.get("keys", {})
  7292. if "left" in keys_cfg:
  7293. left_key = [keys_cfg["left"]]
  7294. if "right" in keys_cfg:
  7295. right_key = [keys_cfg["right"]]
  7296. if "up" in keys_cfg:
  7297. up_key = [keys_cfg["up"]]
  7298. if "down" in keys_cfg:
  7299. down_key = [keys_cfg["down"]]
  7300. if "jump" in keys_cfg:
  7301. jump_key = [keys_cfg["jump"]]
  7302. if "action" in keys_cfg:
  7303. action_key = [keys_cfg["action"]]
  7304. if "sneak" in keys_cfg:
  7305. sneak_key = [keys_cfg["sneak"]]
  7306. if "pause" in keys_cfg:
  7307. pause_key = [keys_cfg["pause"]]
  7308. js_cfg = cfg.get("joystick", {})
  7309. if "left" in js_cfg:
  7310. left_js = [[tuple(j)] for j in js_cfg["left"]]
  7311. if "right" in js_cfg:
  7312. right_js = [[tuple(j)] for j in js_cfg["right"]]
  7313. if "up" in js_cfg:
  7314. up_js = [[tuple(j)] for j in js_cfg["up"]]
  7315. if "down" in js_cfg:
  7316. down_js = [[tuple(j)] for j in js_cfg["down"]]
  7317. if "jump" in js_cfg:
  7318. jump_js = [[tuple(j)] for j in js_cfg["jump"]]
  7319. if "action" in js_cfg:
  7320. action_js = [[tuple(j)] for j in js_cfg["action"]]
  7321. if "sneak" in js_cfg:
  7322. sneak_js = [[tuple(j)] for j in js_cfg["sneak"]]
  7323. if "pause" in js_cfg:
  7324. pause_js = [[tuple(j)] for j in js_cfg["pause"]]
  7325. set_gui_controls()
  7326. try:
  7327. with open(os.path.join(LOCAL, "save_slots.json")) as f:
  7328. loaded_slots = json.load(f)
  7329. except (OSError, ValueError):
  7330. pass
  7331. else:
  7332. for i in range(min(len(loaded_slots), len(save_slots))):
  7333. save_slots[i] = loaded_slots[i]
  7334. if __name__ == '__main__':
  7335. print(_("Starting game..."))
  7336. if HAVE_TK:
  7337. tkwindow = Tk()
  7338. tkwindow.withdraw()
  7339. try:
  7340. sge.game.start()
  7341. finally:
  7342. write_to_disk()
  7343. shutil.rmtree(DATA)