From 4ae6a1d24c344f26b94989fa961f1faa25b4d76f Mon Sep 17 00:00:00 2001 From: claude Date: Sat, 24 Oct 2026 13:37:48 +0200 Subject: [PATCH] M10: byten, hemresan och levelcheck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resor med byten: en bana kan ha next som pekar på en delbana (kind leg, syns inte på bankartan). När man klivit av rätt tåg fortsätter samma omgång på nästa station med klocka, plånbok, sällskap och räknare; par-tiden adderas, dimma och folk är nya, en Byte!-banner visas och HUD:en visar resmålet via bytesstationen. Tidsgräns och missade tåg gäller hela resan. level.ValidateChains kräver sammanhängande kedjor. Nya resor: 007 Akihabara -> Ueno -> Asakusa och finalen 008 Tokyo (två våningar) -> Nippori -> Narita flygplats. Slutscen när sista huvudbanan klaras första gången: planet lyfter, resan summeras, eftertexter; Progress.StoryCleared sparas; flaggan -ending. tools/levelcheck ritar banorna och rapporterar problem. HUD-nivån kortas när mätarna tar plats; poängraden radbryts. Skärmdumpar och docs uppdaterade. Co-Authored-By: Claude Fable 5.1 --- README.md | 30 +-- cmd/metro-puzzle-jp/main.go | 3 +- doc/img/credits.png | Bin 0 -> 11810 bytes doc/img/ending-summary.png | Bin 0 -> 15259 bytes doc/img/ending.png | Bin 0 -> 13103 bytes doc/img/map-signs.png | Bin 15814 -> 15894 bytes doc/img/map.png | Bin 15792 -> 15872 bytes doc/img/terminal.png | Bin 26471 -> 26330 bytes doc/img/transfer.png | Bin 0 -> 21920 bytes doc/img/words.png | Bin 16005 -> 16010 bytes doc/mal-och-tester.md | 7 +- doc/roadmap.md | 42 +++- doc/spelbeskrivning.md | 3 + internal/game/assets.go | 4 + internal/game/game.go | 46 +++- internal/game/scene_ending.go | 178 +++++++++++++++ internal/game/scene_level.go | 79 +++++-- internal/game/scene_map.go | 12 +- internal/game/scene_result.go | 4 + internal/level/level.go | 83 ++++++- internal/level/level_test.go | 67 ++++++ internal/progress/progress.go | 17 +- internal/sim/legs_test.go | 101 +++++++++ internal/sim/party.go | 3 + internal/sim/run.go | 127 +++++++++-- resources/data/levels/007-akihabara.json | 179 ++++++++++++++++ resources/data/levels/007b-ueno.json | 148 +++++++++++++ resources/data/levels/008-tokyo-hemresa.json | 214 +++++++++++++++++++ resources/data/levels/008b-nippori.json | 150 +++++++++++++ resources/sprites/plane.png | Bin 0 -> 165 bytes resources/text/en.json | 17 +- resources/text/jp-dictionary.json | 7 + resources/text/sv.json | 17 +- tools/genart/main.go | 20 ++ tools/levelcheck/main.go | 186 ++++++++++++++++ tools/shots/main.go | 11 + 36 files changed, 1681 insertions(+), 74 deletions(-) create mode 100644 doc/img/credits.png create mode 100644 doc/img/ending-summary.png create mode 100644 doc/img/ending.png create mode 100644 doc/img/transfer.png create mode 100644 internal/game/scene_ending.go create mode 100644 internal/sim/legs_test.go create mode 100644 resources/data/levels/007-akihabara.json create mode 100644 resources/data/levels/007b-ueno.json create mode 100644 resources/data/levels/008-tokyo-hemresa.json create mode 100644 resources/data/levels/008b-nippori.json create mode 100644 resources/sprites/plane.png create mode 100644 tools/levelcheck/main.go diff --git a/README.md b/README.md index 5773f3f..aaa3415 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,14 @@ Storyn följer några polare på semester i Japan, från landningen på flygplatsen till avresan. Efter storyn låses ett oändligt läge och New Game+ upp. Hela speldesignen finns i [doc/spelbeskrivning.md](doc/spelbeskrivning.md). -**Status:** M9 klar (2026-10-24) – sju banor på en bankarta i -spelbrädesstil, med sidospår och flera våningar. Japanska skyltar och -ordinlärning, lämmeltåget, minikarta, tågkort, biljetter och minispel, -svårighet med tidsgräns, poäng och debuffar, tåg som rullar in till -avgångsmelodin, progression med XP, skillträd, items och sparfil, folk, -filer, reklam, sorl och vagnval. Och nu ett riktigt linjenät: tågen bedöms -efter var de faktiskt går, resan visar linjens riktiga stationer, och man -väljer **listläge** (nästa tåg visas) eller **skyltläge** (lista ut det -själv med skyltar och terminalen, +50 % poäng – åk åt fel håll eller för -långt bort och banan är förlorad). Nästa är M10: story-innehåll och -flerbytesbanor. Se +**Status:** M10 klar (2026-10-24) – hela storyn från flygplats till +flygplats: åtta huvudbanor och ett sidospår på en bankarta i spelbrädesstil, +med flera våningar, folk, reklam, sorl, vagnval och ett riktigt linjenät med +list- och skyltläge. De två sista resorna har **byten**: man kliver av på en +mellanstation och fortsätter i samma omgång, med klockan, plånboken och +sällskapet i behåll. Klarar man finalen (Tokyo → Nippori → Narita) visas +**hemresan**: planet lyfter, resan summeras och eftertexterna rullar. Nästa +är M10b (bussar) eller M11 (oändligt läge och New Game+). Se [doc/roadmap.md](doc/roadmap.md). ![Bankartan](doc/img/map.png) @@ -74,6 +71,13 @@ fyra rätt, ett går åt fel håll och ett tar omvägen via city. ![Terminalen i skyltläge](doc/img/terminal.png) +Resorna med byten fortsätter på nästa station när du klivit av: nytt +labyrintgolv, samma klocka och samma hungriga sällskap. + +![Byte i Ueno](doc/img/transfer.png) + +![Hemresan](doc/img/ending.png) + På de svåraste banorna har alla i sällskapet egna kort som måste ha saldo, alla behöver biljett, och när biljetten är köpt ska sällskapet placeras i vagnen efter sina önskemål: fönster eller gång, vänster eller höger sida, @@ -173,6 +177,7 @@ metro-puzzle-jp -mute # inget ljud metro-puzzle-jp -save /sökväg/save.json # annan sparfil (standard ~/.config/metro-puzzle-jp/save.json) metro-puzzle-jp -nosave # läs sparfilen men skriv aldrig (tester/skärmdumpar) metro-puzzle-jp -mode signs # navigeringsläge från start: list eller signs (annars sparfilens; N på bankartan växlar) +metro-puzzle-jp -ending # öppna slutscenen (hemresan) direkt (tester/skärmdumpar) metro-puzzle-jp -script "move right 4; shot bild.png; quit" # skriptläge, se internal/game/script.go metro-puzzle-jp -version ``` @@ -193,7 +198,8 @@ Y = hjälp, X = ordlista, Back/Select = karta. | Kommando | Gör | |----------|-----| | `go run ./tools/build …` | bygger (se ovan) | -| `go run ./tools/genart` | genererar placeholder-grafiken `resources/sprites/tiles.png` och `player.png` | +| `go run ./tools/genart` | genererar placeholder-grafiken `resources/sprites/tiles.png`, `player.png` och `plane.png` | +| `go run ./tools/levelcheck [ord…]` | ritar varje bana (per våning) som PNG i `build/levelcheck/` och rapporterar lösbarhet, onåbara rutor, perronger utanför spärrar, tågens bedömning i linjenätet och resornas byten; avslutar med fel om något är trasigt | | `go run ./tools/gensound` | genererar placeholder-ljuden `resources/sound/*.wav` (avgångsmelodi, dörrsignal) | | `go run ./tools/shots [ord…]` | tar dokumentationens skärmdumpar till `doc/img/` – **kör efter varje grafik-/UI-ändring och committa bilderna**. Med argument körs bara skott vars bana eller skript innehåller något av orden (t.ex. `go run ./tools/shots 005 ride.png`). På Linux körs spelet på en osynlig skärm (headless Wayfire + Xwayland) så att inget fönster syns eller tar fokus; `METRO_HEADLESS=0` använder skärmen | diff --git a/cmd/metro-puzzle-jp/main.go b/cmd/metro-puzzle-jp/main.go index bf5712d..4f0eb18 100644 --- a/cmd/metro-puzzle-jp/main.go +++ b/cmd/metro-puzzle-jp/main.go @@ -29,6 +29,7 @@ func main() { savePath := flag.String("save", "", "sparfil (standard: ~/.config/metro-puzzle-jp/save.json resp. %AppData%)") noSave := flag.Bool("nosave", false, "läs sparfilen men skriv aldrig (tester/skärmdumpar)") mode := flag.String("mode", "", "navigeringsläge från start: list eller signs (tomt = sparfilens)") + ending := flag.Bool("ending", false, "öppna slutscenen direkt (tester/skärmdumpar)") version := flag.Bool("version", false, "skriv ut version och avsluta") flag.Parse() @@ -37,7 +38,7 @@ func main() { return } - g, err := game.New(game.Options{LevelID: *levelID, SmokeFrames: *smoke, Script: *script, KnownWords: splitWords(*know), Points: *points, Seed: *seed, Phone: *phone, CardYen: *card, Debuffs: splitWords(*debuffs), Mute: *mute, SavePath: *savePath, NoSave: *noSave, Mode: *mode}) + g, err := game.New(game.Options{LevelID: *levelID, SmokeFrames: *smoke, Script: *script, KnownWords: splitWords(*know), Points: *points, Seed: *seed, Phone: *phone, CardYen: *card, Debuffs: splitWords(*debuffs), Mute: *mute, SavePath: *savePath, NoSave: *noSave, Mode: *mode, Ending: *ending}) if err != nil { log.Fatalf("kunde inte starta: %v", err) } diff --git a/doc/img/credits.png b/doc/img/credits.png new file mode 100644 index 0000000000000000000000000000000000000000..3f899ba79ec940708a5216f78a3fb4071d734dea GIT binary patch literal 11810 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 R!d?&1*D=;E{L%i{e*tC(YF7XN literal 0 HcmV?d00001 diff --git a/doc/img/ending-summary.png b/doc/img/ending-summary.png new file mode 100644 index 0000000000000000000000000000000000000000..687ac620a58bff82281fd6109d815ebf50d3b074 GIT binary patch literal 15259 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 delta 6291 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 diff --git a/doc/img/map.png b/doc/img/map.png index 54d8890cae45aec71208c8c3aa6ff210b9aa959f..528d5b79b24607cc48f9259c321b4ebdeb61a62c 100644 GIT binary patch delta 6632 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 diff --git a/doc/img/terminal.png b/doc/img/terminal.png index 2345059405181b6fea6ac88fe5f0ed483a06acbd..4a5ff0d692831329973142ac750ff6c41ee98cc9 100644 GIT binary patch literal 26330 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 z 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 literal 26471 zcmb@u1z23k(mzaq;O;KL-Q9x)cemgW+#P}^L4!kp!8J&L1c%_x;1Vq8-~@)j<;!k% z?{0R=AT2)yV6^RH53JMBU?wOQ26co&7C@5$W1nBRS zPhzUfP*B#aa#9kS-kJNE9I?0@7*b+3G?h?~oT2#%9tk|c{Qd*K4j1nX$642Wql`4| z>1Q7Y0+!2buYYPjxDD1aI$7FFJz|A|szw!SZ*RD}M0XOqX}lW%#sO9e04rf8OT&h; z-F<8(Z0Hwfmy-};D85&qS+Vc6|MpP{Mdrr0VI4EfX%u%0-hv)^obiJYj(oZVL5I@r zt6C9bMfV2p-a?2gz#ax5x+8Zn&!#@o&Vjh|V{Zj`OYd@P0>?MJqo>c))bpb!JgxBJ zpksc4+pxcC__@mM&c|1JJGimixlH-xqLskF?qqaSH2;&}cgEl=7AuzJDMO@ez1kUU zj%H1MFa}y(M@eJ%Fx-Ona+-SxTi%8>d#zaszCw7iypcu6VWXLL#JXp$W%N*?98W6Y z(_ongVEie6=-A%d zM|9vq*xDR*^R=NEj!AMSyP`hk#HsFWRfcQD45K8QSj)v_s?;KON&2yeK&O*)glof0 zqRU_H{CxFuXK*+SGCUsI4+2W9bF`lyWkg6XNdjVQ+C;p1=*X#}vp-_5Dm(ThoROY} zjbc&ur!-@x)DWIt*eVqVJZs;PH|pnl8r#e<^RJ$b2W+aJ4a0GD5SfRJFxR@;J}d38 zY9iDRQ_JOi!HVnj$xt7}OF-&>o3OK!YBoH`!5y5=Y1Z$*qdXG+3-z_uLdX1_$G@14 z?O`Sv=inPTols?Hh-qfFG2@&>m7k<4a!8SDa@kBP0K24L0#n^=G$aR$n2gRxxSX^CQ-(o^HlXozfWi8*lGn)*`eF6oKmIO)do?MUfd#& zQwG;8l|i&-E7>S^#Maptw-V{0(&d09>WHfC|D*3^XJRTqeK9z(hv+1Ga1>(*+|V)NR1aO^ux$P<-oJfKEHMMgum78BOowI+x(0>HB5@=m-y?#vBynHb{%5@ zRd?F_z>@(U>&dZ`z!L(~ZF?gCm)FI&6>nF|hqUpVZbTV^iyktAa@<&@R8^h*Z@9P0 z4wrZVRiXkY2V;GQZ6MHTZldC$2&Z+V-~dO;c|R!3=}6K%ab->@NLAggizh9rkGObKRvP-Q|E>UipX z8Tuww2+(A*Y&yDsxYWwl?)FA#R6xkh$}>F9scqO!&a-@AKBP1{akZloF4tDJ*K^6& z7`tPCq_#`^A)pV7!{j^~gR+6*h9ZJOeF^uYxDET#I6LU#?SyhBcfiRi{N75q*xl|s zCl6P3N}rK+9+i*jKwSGII(P#*^aP$%nKMsZ>(rpVwbi$g|ERd)+fvfpQXMC2(a0IFMFG@WY3C(3J!bZ9srzb90+JMX*+ zB7|J@t)#raf2UdNo8;!{bSftI)u5Pk^3#Rvnh@T8Egif{VS)x(i=#=-Z2o-mzKEj) zf>=l3?jDKjKgV}#OHgm7)Wx7bE-m0-mvGUp=o<;dp0cq>HV)Vw zV8oM~VZjsP@N{eG`Hz zc-${?Z>1-_HvfJPG6gNtSZw6G_F-cGi!Vbabw8GW)lIfX3M4I%V`0rEm1a` z8;}_I{Bdit0vXznNORln4o&Gc2HIH2GG?p{2t*sjz&nzK0x1FDL8roj{rgcg7}N(K z@@u8|93484DlilzHW-~|Jbu9^ydjcJvbf7$VPfDhO-iqB0F?r zs$c#rvoU>E_~M*IF?Y-xrYHfDs%q%s65wI4Vo(DVH2_y=#A&YzaGKaM3T#S<+=(#J z@s!5x|DuEcVKrpGvYM-@@Y~Rd?4F(}kvR8KIs@Q1z=#l-m`cVn60DT!)PJtL!ZMP6 zhz?N&Qa}tBO+4?Q2ETf4bv8nd)*=B%Xaa`;mun53QQ){MNZLwXzy5onRtP|_%rt~k=E+r9AaSU9r_ zV6(xe4rc5GC~%=2>{F_uzTdcqIrF=nYCj!yzU4e-B{{aMNZ|>dzWe0ef-D;{-G&^w zHw57++=~jf$|1CY%lOBIEIk-*42nn`#s&&C=MneI-wWqZ+dZ{FuP){%Px65k?CfJG z+%O~JTn}B)o&NJ*KYhjjd%;%`%1E^3oO*atQzl<*J<2_m749_krTy zO_QDtvKg^jYP+L~rrR7kw=>0gYt@v*LGW+k{6}K4PB~iqnh!$8c-6bj=NtrFo7{rj z8#fwaX|fr&Ek%5I=4tpuZ?mD<+2-Y+*cgLSTUGs^LEsd=-g9;vE?T6bXP>6KR(k!- ziqx1XfroD`h={x(VZb1LAdaJJJ!3b0wqxuM^w)-V{99G-f-rxn_fOp%J{sG+6^iTf zZwNhhxn*pj%}O{Z-Gj7TTparv(glt#FFVhHCpR4i&nkw4dNlB7H-K)}SNbfI^KZUO z4|F80rlUn5EmfcpSnCDXti~mQG1|VYlqq%p8pey7ELawrlKLmBBnys#gH7Dd6I-;XrHtI`Ee$ZNi*e0mqS*2h*BM85%Ut~FS>|0t8XZvq zW4b|9zw8PzwRkbT89CC_Wq|Pc4CF}VQ#biddbAktJt5xJ4UwEbtzTd#$`8**4p|f| z@W<_QUR_#m8KbPF(w>DLWuGNh&+PYWA2nt@S@i~_WThHStu=E!<-aKx3dv(p^kou? z6lW3Z=O@S~9^hs63@G{KQp_!V2(AJ3y;jlOCJEY)Zrz|5O%?D z8&h4`>l26pfg?%erR(}p8s0wLGaSLdK$@e@hab_XFj?8bG7k^seHd7=<9V-dp4(fx z?18x%w?37JHaSb#lBv&MC+3@&czR|7GIgv3GY&dAP;K7vdPyBn+Cqb@;k$|~&W~=% zBMjbS1wSy8`!T!A#)pY{cmfd{Bge04oWU0sQMf|_fM=yvQ;Z#;Rbs7f6bdxGQ%LbXw!AZL$7DR=tC0Z)nxZDtK z-0;(txwG@$aVr~0|VG$ewgI< zA(;CoxL?Cw(fNTs5%pn)tx7lB6mB3s9eqZvgBTnZkB_v$S|5z@6d^=7gp47~kbu}^ z?is=5OY)BLP;?5v!1zEXhjT@s;?bdw$Eu5pikAaK@f>JU@uITfOJ})WFmD7__-rAJ z1z5aZ9wOs`5}ezp&qxZd$KT&6frnP(93LLaOk%N0f&BkUdhdi68_L$^R4FuYxz^J7 z{^^zwz3^+!!1%Q z3jAw*VGXrCzXsV2g#eY0*pLSS!dLeWs&`O(&%bzO0 zF=d!2fYOjJ*txK=ZCD9aHC7jXHv7R+q;O?(H})ifLTX!Cm+I|the=j5_U_6|@`NqB zEW|o(rfQ8vr$$gS?*t6B{4D*^X}~3H!jmhwP@nfnj9oINgNNj7F84NAXf1E2OJ|_l zwd5DKa5PMF9>TTR1M6BFD0jG**(09Ck255Cu^;#Nv%zzwcw4A;*f2<+KpkK^bshHHzm zJkteFlhD)=(HxFjNiqz2r=P2a7&x}XNmp0ZgUXYpsBA?;68jRU8hK{E%wS7 z8mLhuHSVL?WT?u2`5c4W9a1bju^~j$(hWFCk$tVn7=o9!S_GF|n~9;ROt_ijDywBu zs6S(3&D2{ArGcSyLZNv(!ODyO>YA>wAgKT)*!O(!i~1UTGd&4Iz4yzBy@I((<V9L*>cU!! z5n1+ItBJAkQp0iE5>8KHdrDLkx;#5-5sCymqMs$8=^?e4=Ey$(rDBYXT3XcNX(jpx z*7vXW#_51ZEXYaa-Jv^-m#6toj!Oww8dyDz1q>1#;oic$skmVi>n{X)qQyhn)HT;> zx}Z_*&s@Cf8yfm>ppj)%*W0+VD&H?E%YRIyA&WthdQ?S0zm=@j=arCz#nhv)&=iD>HY<1n*%fOqe z{QS#e%`q1BO8h9OdBpPFWuNq9@ZQQv-)2XFD=e7r#Yc^T!A<2Z=5_m zQz$WBZ#)WnsY21FNG5^Nf+g|9tUpG_cb#g(!T)gwyeq`KuS&lV9X@9o{NS#y{^`4QFEDtoSPQ zwBk0e<}0rD8a0C+v(+NMrS(|Ml>1{5CLFZw@@b1odV)sSqU56Lued3NA=J+|8W?X8 zK|6cBXfs=$h(|9Pg06(O5~HmO*bf#zUiQ&g)J+1T-CdXr1Pj)pwKo_grUn2VZT`kub?1Mmkbehh8KpUp7 z?q~ys1@6|iaoKU?CEy}OQbsa&)K_fR${QqeCR0Yfmp7msYCS$YXkK+j->TQP+;>!$ z=kLt(?=fE{S3#`cOWr9zgwZs$4BzcwkuTNMF*pRg30L@<|1HVcgM=|3jF5|{Qkr(& z$G34DWSJP0@%6UUf(IgC!Naqwms@>ZTnfy6Eib-U-SL(&#~4 z9bUcUNrqbofmZCZR2g#B0DOxc9qCGZa`f2lJd{HsM@sb4FsJaco5O!^^k4V?t@(N@ z3$My(PJ(>emK&~j=l}t#+PR%d3<1_?RklnDY|wVeBP%?fvJ6a96Hk*9c!2B8yoQfcNq+n{E^86m^48VE$5?V5~RL@KfpV5qbuNoG5= z>B=u$+T1gH%jD)nzkaMzEuUVrJ7Ejkz{kj-aVZP^dT8hXTU!yxuch+_n72AWpnfGt zD^!68`}-8G7KCmur5h@TUxDy z9{!H~MEJTQmNa^VPmFI~Bm=-?zzPet6&MBvY+tk4Dch8>-ZvPW`n-KhhAf)YXV{rb z*Iy2psSvx~?QviTse$hFF>Iu~zW2Fm00B66S04Y~kk20;{lZ;x9q%0NpRNXYEoQ{* z*zef);v*5-Q8(6H?iF3jV!*~-;~CKZJdT<~=p{9YhUv+kbkH`C5&}8|ie13pM<2&g zV+Y;42oxyUN5#zwrbNx^5EJt0nFJ~G1^!q#m=fT{KxI6DCd!wh%>slki85e`|1!`2 zcGuS4UDs@E(>2^cBY~64G&4B!{x@fGymK| z<8nlstS96mpXcMZDuzQqUE{JG?ErP)XlCKXX*TB^fiV&rG;a6|n33Qu zakqaKDM>KU5a7dLIv=gRbQb5Q_^l|rf*||o*~PcZy(mE38^u@ru^}OYbBb@(69A7* zx|--eN^B0tWC+wofjzy|aW{Hh@A6w`R*m$s4!a(z;y(=BE|c1z*1_q+R0Pk6+vV_# z5&bAGMmT5aV7%Fbp5nWyu5lhIi{X+b{B5hHpO_n*|Hd`C0`?1RK zLsjSH#w7hzo-MDp?a;vE=}Z-+>6`m~H?;GvbAPrH#Q>DrXT+=CAk$;Qe9}@YqxR0=!rpW7)A|E*G&1oCy zv`&aQrj|H`xgqnF@X~A3GP-ZwL+=UZ`?sK}eQs;74FN)MNoi-BV8FG88z7@~*1uL` z&ac=h1Ig~Nu1Z4$pv=?UW*@o7#W#^TeuNf>M)+lgNvGDZ<)Sey`#A29U&xOvS_Yyc zSm8_oS{IdUivxnzulzklH^HT)mq4^c@# zV)O;)&acHu*G7*mQrZG_X!%@`WQ!TAS##U)*na=A*nlGE?;Cty)DF;k=azhbH}HRP z{ge6s_$@z0C#JE@? zfwS<9uT7-E^kz`%!MAP=e(2p-sq#99Ahs2jzgmTs-Z}odVLL$N-+l5_$Svo-+KZLV z6~`2+Dod6#F8MtHddIW)uvc+8aIf#!)}+7DllfyzC5uNR>`JtFk4Ig!zX~hqh98v2 zNH#RAp`D*!vTxE^Oh4+{acU|e=Mia5T&Y6SFjx{Y(%{n2@`sd@;h;T$SYHMbMrv=v z*o4lAB4+J~?DflY=FSNCa$^e@0ei_un!R~t__?Ztl8JqbY0jgevXi5apk8vqda*N> zpd-Qm&*-zqi7lL@10?!m=GoM#xmf$j4!)46pG~&MT;un==6=WWCGsrY?|T1mnN;ZO zB1~(xh4tvwbBcLP7aX}yMf*L|)6S*iNzCF<1nmUvc>Xu~rL4ZqZJ2SlBCpLLrH4Gf zk6g@9iaF`xT)|e*Bd{5W+}Jj>V?PL^9KnvZ(E%L^l@dvcb?65gz_SnfkD-|1PJ!^P zK}7*K2Z_?+9t(vPv*o#;{a?=F|G*}1r11wu~toL2V5nkMOha`R>ho4rdqfeoB3F6Z-fKzY41 z(xUeF0V0d0SKFnaLX^RilS{3EqC6&(CcG^NYdeCVM!px8`pm5ARB^N@Jly- zJSf<-^v+xb0^V?a)??;alI_PyEB80bHLRwVQhc6;%mW(eS#RwzGtTR+Zv(^S)xO&T zf(s6A!@>X)KC7JPm1{x|E4%IMB_8{5R3PgXvuB_GhY>Ra^Xf**dCV%~8}Iw^r6neJ zjaMBG1BhAK8|ve499EXo{3F}~>Z#{7+q^*@1F_yat#Me9hwj%JUOp@Fb}T)mCO3h; zHtWTEVrY`YapZna2W9Hu2Cn@Ucicn$+wIQ_Gx_h_*GO0pJ@8}aSq7_&nhk+>&aB=t zbE>ys^4ThRy;Af95SXjEeuC?`e_HhHF&=Tdd|-Q>`P8VP!YQq0BOHTY4aiD68?#&c z{U=6q^Ff#2wRb^IPx26QanXQ-$|b}aY{H%aNL!=D_{%BwWlOn3a(e2OG;~JLVCKYW z-g&F>Sx5BJ#^ZrqyX!c|%YJvr)7PE`wn`u$WY@y3q5KsI@70@Y^{t7p7{f?=JB!tC z@+WlWZMQyCh+^Qm#%_TJ= z+qAJU-;lMb`r$>`+I4$n?5)U-C&?KJaKQ7v%8a;UlIYCB!RK8FUzT>;hT!v*cTRI2 zH0YiK3`49|A~=hi{puiQ!LW)KAIi%c-_k5yS%cc)L5D)Fx`XO_`AF$YhZWP+kPrTl z4ujXxw8?^}@hNjw0kXKXmRiAcw@cU! z^<_o7X9tJ7dwwcf?Ly-1Moz?XX_p=zAv-NGfEtB?aAYs<5?Al_Z(vm|Xr*Y=8YCz9 zs*}s=7tC78-2dowfA+%DL+PAcXYtjSosYs{kl=C9=t)%hbABgqaEDV-=XNqK#fu*^ zLe>S|Q%v+}Flw#6*^wlepd;uQ*C2OchgRt#7HXDUd4Va+p=pL%b+UI@EIc*svdLF- zDWhd9R~`qUOnw=ExeGVT?mt)f!kvRg9&S2Pvkp0Oz1iuA<%Tq!SnI06?2a-Sz0I&J z&6~*hNt6$k#;nnsM!m8$;#>T*ukq>S{9K87x# zBN2I__*y zRjiG)Z*wRWFVH`sr|wFZrtNADXhhRy62^BX;b*nx|BBt_+2>1!AtFW>-`=d|^AxS* z94Rfnq(qLU*XQfR0sMvCkR4hx$D5OjYi+q|m1!{U7n^1%2|4Q(VTs$4LE=*_YbVH70Z!I~X@fw!ERm0gwL`1h}j7&|5{XD<6rWHlD<_7y703wa1$%R%mCT)w37k;RpWT#5N1NeYUk@v%2(03 zj851VRI@|r#C8}X`j5Vdf5skM1<@o%*E6I86t>7)y@U&iahS%77xwW_^EIKbWJ~C)4^`b4kn^a=H6|YvDBf(bb&e&VZIKoqRvR zQuplHnc^{2HP*FkA97Hf%;b*g_s16+S|v{;%+R!+5ae?ITh*3x>HhuE{mAO)C%Zi( zp+2r>1AuZWqV<6W0V#})I*XD3fmi>Y=Kc9(I&H_aA^8*A9VFX9bIl7&MW*ynUGK^B zQNn0Z9kE_BiGPOuf=fZk>WYo9T{IqdJS3rSV@0RDRR)hT4t;MmGN~{`3g>;h4#|Txa*_G{35&0B3f7 z0+MJBL4yAlhKg{i46XF{2;o0NSY4S&rqA%@3=Ft8pqh=l6{$+Wl+83SQXcvDu>r0z zG*_=utH|0I2DYpkvgnNg*_2bWc%d$DZrRSySm|WTu-8G=;I0qZir}p(eGI0VX=Ft-~)?!;=kXN?X zTRTy)eEU}rXYl1mlo63Q&pnMVpiAd14SijJO9ilPH1g+A=|2VWH+}sPNFAGv6MOJ! zV0cW45%FvdZ=ds;S!(F^b1joD*Q4~Qbg z#P%_;RH%Tjy1S1jdtgK!2x?Y-@_@MNpapYZI$jFtFIn4EGni4oiJ+RTHGQ83)7v6! zi{OLcnm|E-7z0^EPoT7S87n2e?- zktEHtsh)_4;;W&n98$WJDLr|H!lc$McV0FZeJbFAZ}J)R?-9x5>Eid6N6`WAwAc6@ z`Q-Ly%O=WCQi{)CK6;JX(M>)Xjwmx%#;K{LMajs^3yX~$gkmOcCP@({Zf0Aen)_y> zK*k|JXBXh#C}ctc9~9V5sdMdmcbTAT(stefw+i(?fnS0@(;IHS4?S}1s4VSd+Z9?iXK1MkiRRq_y0gPF+5Qi%mbj}?xd7l!%)sbbxIA`U|nJ_ zFePb~J{vc9mOw3+VDIXw6ZgHYano@;#RMrHQZs&%GOI$DqEuULDR@7ZT^Hkr2F2fK z9{ghnHn7~#M2})#%7|0tVE!n6kr3}oy>HI_;^5;MG_)sBUcB~V%3FAFPp{AT#qC`4C(v|1AD+z($`av6Jq{+X|~b7lFhWq4J$J>ZMm-L<;Gk1-D+ zB#q=39D04kUy3it_T3$~%fZ#C3g+Etc^WerUpV&_g!>}1C<`AltRrF=G&~DXRqKLO z*J?LS9HhfU0_)!44K%4~O5xbEN2sRBX1%3O-1{IQNOVp#ASPSni`s&`jyufxYZZGM|??lapLmbt#VcV!kTRFoLAW-AAiBXG9b@X zpSR{FVLM|Jztk3NPSE#_$(g8+7V;t6-a(s?PE#Zre9fPGm~HBI`+-spg$HO6xMf3< zbq$}79E9zLb<&Hccgb2T3}RXH=1j)yGu@6AU!S{SWi;yf-HNE?Y8+nw-3b1)2~t>h zzRv^>sKn6Swj)guQ;=uET@j1UNHC47nlDPG6fRj5i;r?OKwoi*TxlWOp}Q;18Z&xa z62t6wddO6CXYlLDK|&ELx7<%j%fUKt$)8O;DZ^)}EYT(lbz0G?b+>32zH%wa@L^68 zBZq}2dJw*E(6|xcAAqbK23q4VqnAL8c`c3|xcz$brK>`gO>kyOVWG62YuA{pE0J&1 zKGph8*Fg=^<_*D?UYo+%F>==eM2QQ<$FKNR)wZA4dne5@y|cH8&(p{+Jw<>`wW|lb z4Ctn{3gk=|M$k_SN9+ldtIYzhPPR^Ynof^AK055?qBDXA@A*`W^R~gW_2DeJCrgn! zbZlzw2fHjESrl9DFMQ>B>e+7(J28=NKL4`gJJ!Mc)boA_{oWS(3g6ENx!a zsnF=kJwD#cS0TDk6d$NCu$aC|`ioRUQ+p<{NDbmWIbQ1?kG58QdWEsY?ooby=067( zg2Zckd3Y?fiD;MZ798C%kfgg>7oI*pDZwY$$pK{B^;eZNFz>o z*_ca-&kzJTPzfco@*IxjgyV|OLT6>;3wYM^cKn!f1SxYi(W(T^v9I{rXC{76Q(`h; zJco`-Yf!6Qqy9bFE$I-!BH3#*HVYV*CKJAf@O1*m)soJU5hYu)Ui~6hBcdbI+tC=u zaypB~G$#T|7$vw26mCKr&M}Le0rA9O+CK=}4pzVN5{$p=%)Kg#+I?4E#~myZCwfVtUxY1?(VJi8~7tw_WKiUUa<~cN3=kN#yfTPE6r&$GLb| z9fyV!I+Zs15ijqv8{00`ueFXsh}oTyEwc4V&$u@{p|kuvk^2G(DSmFtPmS}HCA9g4 z2jH~G89|6%&k-1tQ@|vomBN*L`23mOs>98R_!A+vB=qf)M07ROuL4qCdv(UT{{$zSxZUVth1AfOLRIJH~$%y$KJzK}_o*rpi z?ZxP~>C*vxhBAlf|JKzX?K=mGV?PKT*sJn2Qk##Z6L*{r!(7Fu zAZP<$T~ET*uZtp&ZmPz)3HZ}Dyr3dT*p_6xLu!##Srxo15M`Pp$7idBn~KVjec~%4 zBj9H5k9}&GuGIfIl|W#L6igW;v^*KXE0lqIvClS|3^T;ac)qs_2eDUy4k}CP5$wNU z?(7XL90Yxx59mkLRzi<}-elZ?oMY@0m224&7EKDRzepansNoVSU6Ho^*IN9)69O1W z3f16o>04hBiVvn=Syk;g*x9DwL2?=U3}r=vPZ_^n%%2~)v7rKS{sV`YGx4o zX==P1v9eP@Xo7z7ZQjnmO&iX5eKM~j8K*M;_N`5R;QeM~{k`%inJ|6mp7+gaaj2k! ztE+3%S=-b)3Cis8InBjgiR>WJ2@Q#F=*0Lf*)rA8VcfEEk<5F#X)LS?T+P#)n@j+D zyTmMJb5~EC)K&E(W#y!r|vcWoB3Yh-Y-StgmO zTNWcRj2A}=D;Su4;v!n0Mx|_#ApML)3BEd(!&(I5tHalAVQXtEGA@=ZtHijvDK+2Y zy9SbhyT`f1D!qOr!IlnBrzK9nlM%+C;z58;)Fnm!OZ4!TOV$3Za^9k>G~%Uw#o~bd zq+Yar*!G5?QMa8(Kb#LJp%26hWI#1Nd8s^YsR^6v%`;h^XMBS8HCe;NEY&%vj8}fN znUsUC?d}frF}w9uJ84j|1fr-LFLZw_FJrAQ`^KF;dayZ_v|kCv-3U>m zm~U~b=kQ7mIq~;r_o(UC>D8_%)4B?=l-gY3D__i{uP}Wflpx#h5=(mdTrH^brM4Q6 zW+C?QplX4*Y~51zc*H`FaA71#%P?H;AFLk*|BChOz?%#3L>t$=!a3^|7l7@KXY5Mv zY<@s)XTiE>ce$Ny6deQ-%_0vpxoJBbJldOvc-Y1^NmIsue}JT-p2rn5kj`GBBlW%D z@%|u#E2FPR+L%@E$$IalmGKVvLv)|j*4WNQDWu=fzUt&!a*F_vo^~6LEVJ^NR zQ*oi%p6H0#7J%yre?@2G`~_>uKgxH>)Jj|1!(#;h1N(p?S3Xr<#@C)bA~+O+?=IyW zh6jL*v|S5l{lgE-ES>552paVlFp3kGe8@rl!(|uiqrrdJej|o@KUfS}nZ$r4`XAuS zfWZBCU-v(}e`z$~lEX1^z&42S)cx0KmIe~Hbys2kfNS*B_1i=?TQms_3n_shzw`5U zx>S4}+e0Yz^I|RW9Eo4Ri)!poulhe~via_3_M&Y-*>5Qit=hU>T0 zAwHhvdSQCWl`Zn->*u=C=BEl8MM|-NcMIzbL}4}bfTPw6Hq`r8fj*Rj$YPVp>LXOSuXroM?11FRz|zBsgfwEVRo9o|p}zp1_yd~yO?%(9 z#nlQ&Dcj01W%ypda#?Peq^z%OJ3hExj#IL5Lq zypCvndyyhvCnTcOB{9*Pu`WypA`JWYe#Ji|`_qnwOrG=m z*~dW&J$%TU#ueJF*>4rTAsZgu!HVsm2A(nbxyQ0Xnit9VhMm~3=BbP-W^L!34qj_x zw%k&HiWdW=DUb}``qGUSZbvRiFu$47(DZ5)ag4Znoeiu}<#KFcYYcK7{^qak_$+{y zj?YzBMv(3qwF_l{P4<9_8ogD8F zVg{merQfoLZBCnPFFa5OgMWqUSS2HoSOA!s4SgTzIc^n{LEV8{DR7MBYJ7Z>(-0(V z++oGXj8K?@<2C;t0U8aa&ds17%8D(+98OFYzJ!fF2qI+9cFcK&oOPCGZJ!1@N=`oaAvMmfY2%)(Y zxY%!_9#7!C#;u_4Ba2HFeIDD#jh(qqK7S6&wSGy5!6)kcbTmN&NBAu_f}_{;(psek8Qh1lXgjd~+5KTXD6BNuzAC7$5LQb4X)9DP+=DP?hDLqZ z*B?7hPolxA2Yq~~V$@{^eTLb#_O=(?>%A7Rpv_s&Mpz5_KKV~v zltPhOMR~n`!`uj^RSA8@%nvy>aPg)U=2zx=4zs6=DD8rz3N+jt!e|< za{1(-={*qPQJNB$)vi&T9SQ63z=Qo%oBgHz5CE2)`znt{FXtXS?5<7 z6VygJ$Da+y-%?I$QcWhwaSVkCN$MZc$1V0FY%)AL>5YtDdUn6oMxD5@j(1=)oSq?| zBU@B?J21?@m%-z4>b-V4+q|6To7i&M;`2=g_orM5f6tt-ZT!pB9GbPx+jR<>Z&dH) zb*w!L;t3<_)}u@COVcQ9rL50X?+igSdo-|+ynf%EjA101@I!?cr?S3FSt=|g^#HDS z1s086lnXLW)VFWj*g%(q!?mYvCKh;w7LPrS(&xwUtq&Chr+GM09NrG@TNB2SMPU0r zcQq-Rf3<5AM7=&<%z)&|*h5$-#@WH`L6ewcciPirImvz+IMWIGsvywV{f^DcMQRfbhUeVXFzkKs(% ziY9+(wn%ZZ{l&p7329H=dRaTkWcpbYf7KT`Qma#n!nk&}#f$sy^Ob$pn;e+x zpgFPZ+pfzse~4?FV`Y`b2XaM?a2e9z@Ggf{ejzax=?6_`8{zSvqW($p#0aNyQ!o>K zA1ZKqUwoAB(MONJzjE;#Vp~RSeqj-)U0eyjF56SDvam4ULh3cmEF9Q_a^2_NmdWOV z5I18yHumKleH%H(U(Bt|SHwh@b}{9(fcdwqjujBo^pBYz)E)ZY^!%U79Vo`=9yvg< zBkQ}p^S`VPpzItvzrJ9qTkQ)g#>X$!C0(hiRL6Ww^nc<@-tX4*5#dzjZ1g#gBN@Wy za@2Rsm-jcRN_XZ6EDKwYPHe;uUg%+Wm#68T+LePg{-8%}|?(44CF?X1iC0 zEoX>^YM6sLuQ)sZ`1mHK=(Sy29K2Gp9^W+bp&6PHdi=I#09-c@w*_wGfj>4GUV;FD zPL^%2$`^)>_YCy|t2P`Q=d(+W*D3i5o^6FklcQI!GMq#Mx}K1%r<=fIe&ISfi}nfJ z+p!Bfwm`z$LPc?D!^c_hj{&t{`PH2>4!4tbHFBRs!Sq;|%$i??c5->G2C2 z49B4tx_B56(Z6})yM^}K+e@Cs0~AQVi1ok@b|egb*n9gd#+h58I!C(LaLzURz@xIs zuDy0Ner$gZ8F<)YgS%!%(p{*xE-6S^utiVKFyUpiydl(H?te;E7uWV=R85o&6yN-m z`o&GZ7dj7Z&HV%wKDvAjWPL-ulE+RvKmz+e z?Ew6c;x@u6zaWxax0++KrT}R45&aWa>7y^+nIGlf#zS?&d1n@DoI8canaG<9@~jG( zTqRc>Emiol7q{;x z!~s|sb_3qDQkm`zS~#iIW-ZRI4QD_E_>zDdJPVieEkU#nTRKVd_wIb{-yQH*Ro25` zJG1ZkuD2r(NDC%-y&^e`+I3V#K9zj;NuUkhyx>vz@dBQjyZs<^#$#?| z8@w^*96j;cmsXEPekW|ddR%w;?XLEtfy1%Iyl-*N3p?6rJe6l6baZ_)B;9y(V^`L~ z5QIl-67m@_kSP~9(QIG;sBZKXl0;>8?WCZKYk_Z@Ma15_7ezLR(>Z}_~>YJ zx_y7?gX5$)N#8NI61A!(?G$b)7jg?a)qBNf>=d-i!Pd_~gx*o|?`bKf4yDx=g~Rhl=tPVw)~a2C)E-Et^WbI`M|9_h zXY7&BcuPfV*3TZ14wS!ebRQJil36M1Cm5Q}hhGVOIQ`InlKAZWI+;;XnKKHV{i|2c zv>8~}ws}pnHcHVP)B^&gG2sUQoNr%TaS3op&v+?MEdJo zM~YgjX|Aknta~Qgx^ybaM8=qyZ$*?HcOO5HH%WtX?4g`gKQ>wwRaSG1RoP1Lr;vAh zB+Bp~+_b({%3*jQuT>u_PONL~>n`IXIf!0k;Yfx?R;o{Ly6lPEJHL$Gws(EP0>fNZ zU)~GM`-H1{O$i%q%^$|)qoiU?v{>^hZ~@0LAK=y;`&SYE#`e5H>lZodDV8K>gSp1! zjbfKUL4ig_J-K@tZUISOR%+V!*{roXsK46;oI1a8YXE-Vf`t)I?cU8@ z?a;kyoN7p#^O7-GyEraGwx(?Xd#V&8|{O`|r z(1>ddS=Xym=Q#ml!x$_67dVk#;7#y|LSJ9h_{?OlV84TiCE=I*a?G-ka}eY@&SkEl z-Pefpd)lb)o1;bAKZV?*w*=qyE#KD3xb(U}0hSQuY}{-pQN!?%7V5{${TS*}pXQD! z><}Y7W-|^=qnD7t=!i;-!gnHT^??+5pU}cSroqVHGONiLX4E?;Ovu`2udThu5^aYU zQ~@aQiqQ%Ub$N*qDk&<$38fQ#cKa=Lm3+*n3E=!}l`)!KG9xmtqlq5sjB%YZ9o6pX~i^8Mj!B}fatqcxAkur(ko*A7-aiX~s8NM!FG zIknSWS1U}hz-zT%KbE9%96&8Ft+nBmp;7Ve0K$)EC)ti139Apy5}YEgkV7+uWrJAv zdy&i;I>wpedm6gV;J;n^_z@z16Z=&B?&6^S_t&)K8(%%K$ZLORWO2<PGh~{ zuk`;la^7)GC0iV~iy#OB0*11R6h);fp_pI^3Q`25h*A}aG%4yrAg~kxsYYNC5h8*_ zdX3U~!b(7-M5IXoLkk!rgpfoM$_u*tp2&SqbN`snB=ecMGdcI3?>%?Uk9GRHp1$iC z@pgyWn*P@I{;lR5=}EY9$wMl$zrVlu_>?)fN#dphmne7_Y@KgB_yty>9P3}t&OpX6 z7LX?mJ2KL44)cjH#w0-DU)!Es{y#hdV`H?h+;U_wA9CG}DuX8BS)tjO=@p)1&U{3uOP;1~X^^-V;#5u3duZ#`>nUUofr^#-Jt2lsC7lPDXt zcGdqr=Sjica1hWjxf?W@^SPivsp#TBD#$@opo8XkRc8qdmDYf=S|~WO+zNekaSScB zG>OrNX4ceMubP2^9$!N_NgsuJ6fu472(ADdjgthM<|IxQ#ZHTm<`aioX=96zTqt*{W-3tUT0~yR+aVI<R3R@N|QN2w$~l-`&Qe;&+I}JP|hF?3o5H+OkK?V7~2gj0w<;T%RZw0dM8{ z!C97BfUTYO!|ua(EzghmYpWkLh7jUQ^n>Udb4IDvuozBWuw|;K%;mtPWx>&-d5U&d zr>|G{!~dI{_Mb$mNjxv00wVZsL&;J}oYy<$O542L^rj1EOnV+Pe9TkY&3fo4Vpa|2 z2E{*VN_$3;*&8sUC}#AA67T_80yVV3floy=-=4jp`ouY_PvW)g_z6P|*|@)DL*zTZL2$Rw;&*8cz{}b8 zYKDSm&C`NDNfELOtNj(TvVzhZ-&M(c=4O+pR)jKpRA=i=B_nLmb6qR$8q<=$t}=O! z8%K9UjVEN2{abwtBekuLmIcfuky+YRetSCFCRmG36h70`$PsW{veBkX)>rO*Y`}sB zqd*zz{AaZPaGu?g{z_Wj!$$aw5S9Pbg_F`UE|wne#O^*)@2t75*+8S^AJ5Mp$MC^^y ze09pbJ=Dui3}%9TB#f$l7*n}2g0Qj;G^jk{)Vh@OWsv+3#LL8}$uPJ3F>K1*YqIrb6^y% zJ_m@xXC1k><3r#l%YU=xKN7j;49`;-_fsGk=kWF5v0wmm;O*qu*;z}aQuQIeg9|F+ z;ZB+5Sm-ETDW{_Z+Rg5=ctH;!*sb0IKFt)_K+yX;8ofQ{7Uu~at=aNX#cqk3B*Hi; z>jhM}g^%UXZ}Vx|C(TuO-5PXBcdqnRhjz<`DDizfZ;@}gPiTJsm`PO8Q(sgFM*m~0twOvd4`rGTJS_88E!%Ul|`v&T++rnGog8+c9Pq2Y{O=ZVu zU0seF^YkH!GOebF2#3C@)Gkw|^CG;Iks1SQmJZ73?SZxyCr}&dy^m=t zNi@HiTgliSYu5s-r1gLf&~dXkxX#i(E?qf%wHX~9xXfBa2U&JwUfygdHhX42N^{#A z-bx?2wGbaz0p@K?)l6@1@N{}swAX&TlSi)8=t-K6R`8uopSOJ2Q7#Do_621Y5CSjj zg3DJ&sh)3*ZZ)@;ei~=`XXZ(dtb^h?;~&+Q$!bv;Pr{MKpJ4Le;D_0LTY1!VfxLbWP23i8RbeWhKIhoq4QdWy(obJ|XgyQ3akxqb@4 zU%7kwDDgM-W?t`5!rz5xBmv%=Gk)%1bKRf6nyCBFX=m08#kGV=IXqwP8DVSh)H;EA zKb)JJsni$srgEZ&X`Vu|n9wM#%U2}ly`&!__&E~Je86>GGWM2U4%DxXE)Vs2Z2B*x474AsOK(#xouHAeL+A# z$YRf8?)CB(|4s=Ny~iyRV4DBX#O=~>exHxT`XD(ZN1gE))58GyToa~DZ`FFxYC;RM zENVNh_rMD!9`ot6xpC%%-$Y93;j8Y6kC+&un|rc5L;~QsdO;Ar)jHVXiSU|2M84C% za5zxhKXdqEdLJRiH8#+!Qdts*HL6Wl9Dt5k@6ou;x|V;b1`2`F(Nd(=1@F4YMdoQm zg8?UY%?w4|&P`DgFT^^^a)c%=ZlOvSg?mez;c8L0rzjoTvMnCV{#ZKVL8BSX&tu*t zM!8A!YRuGtd?$DLrUD&>?}}QTU|UzB#%cn(MIwF4|7w~X&*E&OmipDSu>mH!WIvD= zv~(v;;D4w=HyaaunXERuAX}v=*M9XlHP&#a`=E@@fjqEXdom4MLrV&RxFch4PhA>* zSq2t{36SfvV(KBHe4`%@Q5u4QqCbNWV9zTQwyu>NL%id>h*c}P-n%Ni0 z^V*Pc@)eKo#v&r`mVM{K+D zkHl8>Zp}0!Oc!=~*`GawoOFs_@b(k?5ZX*DE9;D!hcJ>ku^acBL7!F7ijc57ISnWa zP5_ZUU|N*}cdGd^@0)@#@jhr+1#Y9R;4+eUX>*H)Z~zLM30V(x9KM@8wrw)=Y=YjT zdQ1FNc~x6zLIT?|v3r_1DAmo!@H0vta=wE_80z&@#do;ACXM^C=-QYBBgWmY5hdny zkp!f}w}$0ee6uLS+icAXw_qTv5uLB zU5P<^Qr^=IgV1ti+eev|-7Bm6?M?M_f(mx7CC;w^I=^@GyvcUv>JuNTUa&3pZS=*F zXMKO2ca^9Ew0dL(Fodm>y5|;(qZCov&3)%ey8Wc_ui%%akSd3`0IiDi#_j(~!GA+Y z(fbWl`XcFbeRo%M1ku@ifo;Yxl~FX+>lkk)V8&pF0E3m=&jJ$K4^)c#Lnuyy3q%FS zS*7@BJT>)3A_W6qsL5cQ6Z203nyk$Kxcb*#z^5;Q8FO*Mz?Uq-2bezP#Un7DZws?C z+_Bkt!{+84r=;Jp69Y)C3|KcFxf;|2v(L}n_@r50&~uAk{WVEsNIiJDv0WlhZI`aY zhV0p{<+8+(R4$LBQ6e7?RNV6M#DiwH5o$KS(&~i0Dv16#RtT=#0h7x}969hJ;e5Ni z;{!$6F~4eYZhiTsx2;6wA7W%{!%E+3Fs`$t$uEbm>pdL6%QsnHa|XwlWKq31uUbh8 zgA2JT>d7SK>^F`jk;>;^4#X-OPoQa{uBf3E+n5Kc)$9o64}f@ zY>aQm($kI1=+ZjqFU*P41QqVNNb|E~`JKP&^LvTV)j2n*w~j&rsPNZIPkEUR?c z*)U#;h!YZsqXGC(nwNOt2j9KclmEZkN{vns?}kPLQIF8TaI4kV-g+>jGli$@L;ej{ Cf$}W? diff --git a/doc/img/transfer.png b/doc/img/transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..78561a71c201c3afff4fc3353d70cf4ded537677 GIT binary patch literal 21920 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 1 { + return c[1:] + } + return nil +} + +// isLastMain rapporterar om banan är storyns sista huvudbana. +func (g *Game) isLastMain(id string) bool { + var last *level.Level + for _, l := range g.Levels { + if !l.IsSide() && !l.IsLeg() && (last == nil || l.Order > last.Order) { + last = l + } + } + return last != nil && last.ID == id +} + func loadCatalog() (*progress.Catalog, error) { sk, err := res.ReadFile("data", "skills.json") if err != nil { @@ -240,7 +263,7 @@ func (g *Game) runOptions(id string, seed rng.Seed) sim.RunOptions { return sim.RunOptions{ Dict: g.Assets.Dict, Know: g.Know, Wallet: g.Wallet, Debuffs: g.Profile.Debuffs, Seed: seed, Extra: eff.Stats, FogRadius: eff.FogRadius, Landmarks: eff.Landmarks, FreeGates: eff.FreeGates, - RevealAll: eff.Reveal[id], InfiniteBattery: eff.Battery, Mode: g.Mode, + RevealAll: eff.Reveal[id], InfiniteBattery: eff.Battery, Mode: g.Mode, Chain: g.chainAfter(id), } } @@ -281,12 +304,13 @@ func (g *Game) startLevel(id string) (*LevelScene, error) { // LevelOutcome är vad en avslutad bana gav i progression (för resultatskärmen). type LevelOutcome struct { - XP int - LevelUps int - FirstClear bool - NewItems []string - NewPoints int - Unlocked []string // banor som öppnades + XP int + LevelUps int + FirstClear bool + NewItems []string + NewPoints int + Unlocked []string // banor som öppnades + StoryCleared bool // sista huvudbanan klarades för första gången: visa slutet } // levelEnded appliceras exakt en gång när en bana avgjorts: debuffar, @@ -323,6 +347,10 @@ func (g *Game) levelEnded(run *sim.Run, id string) LevelOutcome { out.Unlocked = append(out.Unlocked, l.ID) } } + if g.isLastMain(id) && !g.Progress.StoryCleared { + g.Progress.StoryCleared = true + out.StoryCleared = true + } } g.State.Current = nil g.Save() diff --git a/internal/game/scene_ending.go b/internal/game/scene_ending.go new file mode 100644 index 0000000..1a895b2 --- /dev/null +++ b/internal/game/scene_ending.go @@ -0,0 +1,178 @@ +package game + +import ( + "fmt" + "image/color" + "math" + "sort" + "strings" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/vector" + + "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/sim" +) + +// EndingScene är storyns slut: sällskapet flyger hem, resan summeras och +// eftertexterna rullar. Enter går vidare mellan delarna, Esc hoppar till +// bankartan. +type EndingScene struct { + t float64 // sekunder i aktuell del + phase int // 0 flyget, 1 summeringen, 2 eftertexterna + lines []string +} + +const ( + endingFlightSec = 7.0 // flyget innan summeringen kommer av sig själv + endingCreditsPxS = 14.0 // eftertexternas rullhastighet +) + +// NewEndingScene startar hemresan. +func NewEndingScene(g *Game) *EndingScene { + return &EndingScene{lines: strings.Split(g.Assets.T("ending.credits"), "\n")} +} + +func (e *EndingScene) Update(g *Game) error { + e.t += dt + switch { + case backJustPressed(): + g.SetScene(NewMapScene(g)) + return nil + case confirmJustPressed(): + e.phase++ + e.t = 0 + } + if e.phase == 0 && e.t > endingFlightSec { + e.phase, e.t = 1, 0 + } + if e.phase == 2 && e.t*endingCreditsPxS > float64(len(e.lines)*LineH+ScreenH) { + e.phase++ // eftertexterna har rullat förbi + } + if e.phase > 2 { + g.SetScene(NewMapScene(g)) + } + return nil +} + +func (e *EndingScene) Draw(g *Game, screen *ebiten.Image) { + a := g.Assets + e.drawSky(screen) + switch e.phase { + case 0: + e.drawFlight(g, screen) + a.DrawBig(screen, a.T("ending.title"), 24, 20, colText) + a.Draw(screen, a.T("ending.sub"), 24, 44, colDim) + case 1: + e.drawSummary(g, screen) + default: + e.drawCredits(g, screen) + } + a.Draw(screen, a.T("ending.hint"), 8, ScreenH-12, colDim) +} + +// drawSky är en skymningshimmel i band, från mörkblått upptill till orange +// vid horisonten, med långsamt drivande moln. +func (e *EndingScene) drawSky(screen *ebiten.Image) { + top, bottom := [3]float64{30, 32, 80}, [3]float64{230, 120, 70} + const bands = 12 + for i := 0; i < bands; i++ { + f := float64(i) / float64(bands-1) + col := color.RGBA{uint8(top[0] + (bottom[0]-top[0])*f), uint8(top[1] + (bottom[1]-top[1])*f), uint8(top[2] + (bottom[2]-top[2])*f), 255} + vector.DrawFilledRect(screen, 0, float32(i*ScreenH/bands), ScreenW, float32(ScreenH/bands+1), col, false) + } + cloud := color.RGBA{250, 220, 200, 120} + for i := 0; i < 5; i++ { + x := math.Mod(float64(i)*73+e.t*(4+float64(i)), ScreenW+60) - 30 + y := float64(40 + i*22) + vector.DrawFilledRect(screen, float32(x), float32(y), 28, 6, cloud, false) + vector.DrawFilledRect(screen, float32(x+6), float32(y-4), 14, 5, cloud, false) + } + vector.DrawFilledRect(screen, 0, ScreenH-20, ScreenW, 20, color.RGBA{24, 20, 37, 255}, false) // marken +} + +// drawFlight: planet lyfter åt höger medan sällskapet vinkar på marken. +func (e *EndingScene) drawFlight(g *Game, screen *ebiten.Image) { + a := g.Assets + f := min(1, e.t/endingFlightSec) + px := -40 + f*(ScreenW+80) + py := 120 - f*80 + 3*math.Sin(e.t*2) + var op ebiten.DrawImageOptions + op.GeoM.Scale(2, 2) + op.GeoM.Translate(px, py) + screen.DrawImage(a.Plane, &op) + for i := range memberTints { + var o ebiten.DrawImageOptions + t := memberTints[i] + o.ColorScale.Scale(t[0], t[1], t[2], 1) + o.GeoM.Translate(float64(ScreenW/2-50+i*20), ScreenH-20-TileSize+math.Abs(2*math.Sin(e.t*3+float64(i)))) + screen.DrawImage(a.PlayerFrame(sim.Up, true, e.t+float64(i)*0.3), &o) + } +} + +// drawSummary är resan i siffror: banor, stjärnor, poäng, ord och souvenirer. +func (e *EndingScene) drawSummary(g *Game, screen *ebiten.Image) { + a := g.Assets + p := g.Progress + vector.DrawFilledRect(screen, 16, 14, ScreenW-32, ScreenH-40, colPanel, false) + vector.DrawFilledRect(screen, 16, 14, ScreenW-32, 2, colGood, false) + a.DrawBig(screen, a.T("ending.summary"), 24, 20, colGood) + levels, cleared, score := 0, 0, 0 + for _, l := range g.Levels { + if l.IsLeg() { + continue + } + levels++ + if r := p.Levels[l.ID]; r != nil && r.Cleared { + cleared++ + score += r.Score + } + } + y := 44 + rows := []string{ + fmt.Sprintf("%s: %d/%d", a.T("ending.levels"), cleared, levels), + fmt.Sprintf("%s: %d/%d", a.T("ending.stars"), p.TotalStars(), 3*levels), + fmt.Sprintf("%s: %d", a.T("ending.score"), score), + fmt.Sprintf("%s: %d", a.T("ending.words"), g.Know.Count()), + } + for _, r := range rows { + a.Draw(screen, r, 24, y, colText) + y += LineH + } + var items []string + for id := range p.Items { + if it := g.Catalog.Item(id); it != nil { + items = append(items, it.Name.Get(UILang)) + } + } + sort.Strings(items) + souvenirs := a.T("ending.none") + if len(items) > 0 { + souvenirs = strings.Join(items, ", ") + } + for _, l := range a.wrapLine(plainLine(a.T("ending.items")+": "+souvenirs, colKnown), ScreenW-48) { + a.drawLines(screen, []line{l}, 24, y) + y += LineH + } + y += 4 + for _, l := range a.wrapLine(plainLine(a.T("ending.story_done"), colWarn), ScreenW-48) { + a.drawLines(screen, []line{l}, 24, y) + y += LineH + } +} + +// drawCredits rullar eftertexterna nerifrån och upp. +func (e *EndingScene) drawCredits(g *Game, screen *ebiten.Image) { + a := g.Assets + y0 := ScreenH - int(e.t*endingCreditsPxS) + for i, l := range e.lines { + y := y0 + i*LineH + if y < -LineH || y > ScreenH { + continue + } + col := colDim + if i == 0 || (i > 0 && e.lines[i-1] == "" && i+1 < len(e.lines) && e.lines[i+1] != "") { + col = colText // rubriker + } + a.Draw(screen, l, (ScreenW-a.Width(l))/2, y, col) + } +} diff --git a/internal/game/scene_level.go b/internal/game/scene_level.go index f185c6c..af87d72 100644 --- a/internal/game/scene_level.go +++ b/internal/game/scene_level.go @@ -26,12 +26,20 @@ const ( learnedShowSec = 3.0 progressedShowSec = 2.5 flashShowSec = 2.5 + transferBannerSec = 3.0 +) + +// Mätarnas bredd och mellanrum uppe till höger (per medlem). +const ( + meterW = 11 + meterGap = 3 ) // LevelScene är själva spelandet av en bana. All spellogik ligger i sim.Run; // scenen översätter input och ritar. type LevelScene struct { id string + level *level.Level // aktuell delbana (byts när resan fortsätter på nästa station) run *sim.Run cam *sim.Camera animT float64 @@ -58,14 +66,21 @@ func NewLevelScene(g *Game, id string, seed rng.Seed) (*LevelScene, error) { if err != nil { return nil, err } - s := &LevelScene{ - id: id, - run: sim.NewRun(lvl, g.runOptions(id, seed)), - cam: sim.NewCamera(ScreenW, ScreenH, float64(lvl.Width()*TileSize), float64(lvl.Height()*TileSize)), - } + s := &LevelScene{id: id, run: sim.NewRun(lvl, g.runOptions(id, seed))} + s.rng = rand.New(rand.NewPCG(1, 2)) + s.rebind(g) + return s, nil +} + +// rebind bygger om allt som hör till den aktuella delbanan: kamera, räls, +// affischer, melodifaser och bakgrundsprat. Körs vid start och vid byte. +func (s *LevelScene) rebind(g *Game) { + lvl := s.run.Level + s.level = lvl + s.cam = sim.NewCamera(ScreenW, ScreenH, float64(lvl.Width()*TileSize), float64(lvl.Height()*TileSize)) s.cam.Margin = TileSize // rälsen ligger i kantraden: visa en ruta utanför kartan s.lastPhase = map[level.Tile]level.Phase{} - s.rng = rand.New(rand.NewPCG(1, 2)) + s.particles = nil s.buildTracks() s.buildPosters() g.startChatter(lvl.Noise) @@ -73,7 +88,6 @@ func NewLevelScene(g *Game, id string, seed rng.Seed) (*LevelScene, error) { s.lastPhase[tr.tile], _ = s.run.PlatformPhase(tr.tile) } s.followPlayer() - return s, nil } // TrackPhaseName är fasen för närmaste perrong inom indikatoravståndet @@ -145,6 +159,9 @@ func (s *LevelScene) Update(g *Game) error { // Minispel: all input går dit; Esc avbryter minispelet, inte banan. mx, my := miniMoveXY() s.run.StepMini(dt, minigame.Input{Move: my, MoveX: mx, Confirm: confirmJustPressed(), Hold: confirmHeld(), Cancel: backJustPressed()}) + if s.run.Level != s.level { + s.rebind(g) // bytet: resan fortsätter på nästa station + } if s.run.Outcome != sim.Ongoing { s.outcome = g.levelEnded(s.run, s.id) g.SetScene(NewResultScene(s)) @@ -299,6 +316,14 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) { s.drawIndicator(screen, ox, oy) s.drawHUD(g, screen) + if r := s.run; r.TransferAt >= 0 && r.Time-r.TransferAt < transferBannerSec && r.Mini == nil && r.Outcome == sim.Ongoing { + txt := g.Assets.T("hud.transfer_done") + w := g.Assets.Width(txt) + 16 + x := (ScreenW - w) / 2 + vector.DrawFilledRect(screen, float32(x), 60, float32(w), LineH+8, colPanel, false) + vector.DrawFilledRect(screen, float32(x), 60, float32(w), 2, colWarn, false) + g.Assets.Draw(screen, txt, x+8, 64, colWarn) + } if s.run.Mini != nil { s.drawMinigame(g, screen) } @@ -346,27 +371,41 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) { a := g.Assets vector.DrawFilledRect(screen, 0, 0, ScreenW, 24, color.RGBA{24, 20, 37, 200}, false) - dest := r.Level.Destination.Get("romaji") - if r.Know.ShowsDestinationJP() { // hint från nivå 1: resmålet på japanska, att matcha mot skyltarna + dest := r.FinalDestination().Get("romaji") + if !r.IsLastLeg() { // resa med byte: den här delbanan slutar vid bytesstationen + dest += " " + a.T("hud.via") + " " + r.Level.Destination.Get("romaji") + } + if r.Know.ShowsDestinationJP() { // hint från nivå 1: delbanans mål på japanska, att matcha mot skyltarna dest += " " + r.Level.Destination.Get("ja") } name := r.Level.Name.Get(UILang) if r.Level.NumFloors() > 1 { name += " " + r.Level.FloorName(r.Player.Tile.Z) } + if r.Legs() > 1 { + name += fmt.Sprintf(" %d/%d", r.LegNo(), r.Legs()) + } a.Draw(screen, name, 4, 0, colText) destX := 4 + a.Width(name) + 12 a.Draw(screen, a.T("hud.destination")+": "+dest, destX, 0, colKnown) - a.Draw(screen, fmt.Sprintf("%s Lv%d·%d", a.T("hud.japanese"), r.Know.Level(), r.Know.Count()), - destX+a.Width(a.T("hud.destination")+": "+dest)+12, 0, colDim) + // Japanska-nivån får plats till vänster om mätarna; annars kortas den. + jpX := destX + a.Width(a.T("hud.destination")+": "+dest) + 12 + metersX := ScreenW - 4 - len(r.Party.Members)*(meterW+meterGap) + jp := fmt.Sprintf("%s Lv%d·%d", a.T("hud.japanese"), r.Know.Level(), r.Know.Count()) + if jpX+a.Width(jp) > metersX-4 { + jp = fmt.Sprintf("Lv%d", r.Know.Level()) + } + if jpX+a.Width(jp) <= metersX-4 { + a.Draw(screen, jp, jpX, 0, colDim) + } timeStr := fmt.Sprintf("%s %3.0fs", a.T("hud.time"), r.Time) - if r.Level.TimeLimitSec > 0 { + if r.TimeLimit() > 0 { left := r.TimeLeft() timeStr = fmt.Sprintf("%s %d:%02d", a.T("hud.time_left"), int(left)/60, int(left)%60) } wallet := fmt.Sprintf("%s %s %d¥ %s %d¥", timeStr, a.T("hud.card"), r.Wallet.Card, a.T("hud.cash_short"), r.Wallet.Cash) - if r.Level.MaxMissedTrains > 0 { - wallet += fmt.Sprintf(" %s %d/%d", a.T("hud.missed_short"), r.MissedTrains, r.Level.MaxMissedTrains) + if r.MaxMissed() > 0 { + wallet += fmt.Sprintf(" %s %d/%d", a.T("hud.missed_short"), r.MissedTrains, r.MaxMissed()) } if r.Wallet.Phone { wallet += fmt.Sprintf(" %s %.0f%%", a.T("hud.phone"), r.Wallet.Battery) @@ -375,7 +414,7 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) { wallet += " " + a.T("hud.ticket") + " " + r.Wallet.Ticket } walletCol := colDim - if r.Level.TimeLimitSec > 0 && r.TimeLeft() < 30 { + if r.TimeLimit() > 0 && r.TimeLeft() < 30 { walletCol = colBad } a.Draw(screen, wallet, 4, LineH, walletCol) @@ -385,9 +424,6 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) { return // resultatpanelen, minispelet eller butiken tar över; ingen inforuta eller minikarta } lines, accent, bar := s.infoLines(a) - if len(lines) == 0 { - s.drawModeHint(a, screen) - } if entry := a.Dict.Lookup(r.Learned); entry != nil && r.Time-r.LearnedAt < learnedShowSec { lines = append(lines, plainLine(fmt.Sprintf("%s: %s = %s (%s)", a.T("hud.learned"), r.Learned, entry.Translation(UILang), entry.Romaji), colKnown)) @@ -402,7 +438,7 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) { } lines = append(lines, a.wrapLine(plainLine(strings.Join(parts, " "), colWarn), ScreenW-8)...) } - if r.Flashing(flashShowSec) { + if r.Flashing(flashShowSec) && r.FlashKey != sim.FlashTransfer { // bytet har sin egen banner col := colBad if r.FlashKey == sim.FlashRescan { col = colWarn @@ -426,6 +462,9 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) { // (eller tåget under hen) annars skulle hamna bakom den. var box image.Rectangle hasBox := len(lines) > 0 + if !hasBox { + s.drawModeHint(a, screen) // lägesrutan bara när ingen inforuta visas + } if hasBox { h := 8 + LineH*len(lines) if bar { @@ -579,7 +618,7 @@ func (s *LevelScene) infoLines(a *Assets) (lines []line, accent color.Color, bar // drawMeters ritar sällskapets mat/dryck/toa-mätare uppe till höger. func (s *LevelScene) drawMeters(g *Game, screen *ebiten.Image) { members := s.run.Party.Members - const w, gap = 11, 3 + const w, gap = meterW, meterGap x0 := ScreenW - 4 - len(members)*(w+gap) for i, m := range members { x := float32(x0 + i*(w+gap)) diff --git a/internal/game/scene_map.go b/internal/game/scene_map.go index b85addb..5a6ae0b 100644 --- a/internal/game/scene_map.go +++ b/internal/game/scene_map.go @@ -33,9 +33,11 @@ type mapNode struct { func NewMapScene(g *Game) *MapScene { m := &MapScene{sides: map[string][]*level.Level{}} for _, l := range g.Levels { - if l.IsSide() { + switch { + case l.IsLeg(): // delbanor nås via sin resa + case l.IsSide(): m.sides[l.Parent] = append(m.sides[l.Parent], l) - } else { + default: m.mains = append(m.mains, l) } } @@ -192,8 +194,12 @@ func (m *MapScene) Draw(g *Game, screen *ebiten.Image) { name = a.T("map.side") + ": " + name } a.Draw(screen, name, 8, 134, colText) - info := fmt.Sprintf("→ %s %s %s %s %d", cur.Destination.Get("ja"), cur.Destination.Get("romaji"), + fd := level.FinalOf(g.Levels, cur).Destination + info := fmt.Sprintf("→ %s %s %s %s %d", fd.Get("ja"), fd.Get("romaji"), strings.Repeat("★", cur.Difficulty)+strings.Repeat("☆", 5-cur.Difficulty), a.T("map.party"), cur.Party) + if n := len(level.ChainOf(g.Levels, cur.ID)); n > 1 { + info += fmt.Sprintf(" %s %d", a.T("map.transfers"), n-1) + } if g.State.Current != nil && g.State.Current.LevelID == cur.ID { info += " " + a.T("map.resume") } diff --git a/internal/game/scene_result.go b/internal/game/scene_result.go index 1c49ed0..b71fb33 100644 --- a/internal/game/scene_result.go +++ b/internal/game/scene_result.go @@ -29,6 +29,10 @@ func (r *ResultScene) Update(g *Game) error { } g.SetScene(s) case confirmJustPressed() || backJustPressed(): + if r.from.outcome.StoryCleared { + g.SetScene(NewEndingScene(g)) // storyn klar: hemresan + return nil + } g.SetScene(NewMapScene(g)) } return nil diff --git a/internal/level/level.go b/internal/level/level.go index a3c9254..79edee7 100644 --- a/internal/level/level.go +++ b/internal/level/level.go @@ -248,6 +248,7 @@ type Level struct { Kind string `json:"kind"` // "main" (standard) eller "side" Order int `json:"order"` // huvudbanornas ordning på bankartan (1, 2, …); sidobanor ärver förälderns Parent string `json:"parent"` // sidobanans huvudbana – låses upp när den är klarad + Next string `json:"next"` // nästa delbana (kind "leg") i en resa med byten: när man klivit av här fortsätter banan där Rewards Rewards `json:"rewards"` Shop []string `json:"shop"` // items som kiosken säljer på banan (tom = seedat urval) Crowd int `json:"crowd"` // folk på stationen 0–3 (NPC:er) @@ -381,9 +382,9 @@ func (l *Level) Validate() error { switch l.Kind { case "": l.Kind = "main" - case "main", "side": + case "main", "side", "leg": default: - return fmt.Errorf("level %s: kind måste vara main eller side", l.ID) + return fmt.Errorf("level %s: kind måste vara main, side eller leg", l.ID) } if l.Kind == "side" && l.Parent == "" { return fmt.Errorf("level %s: sidobana utan parent", l.ID) @@ -782,6 +783,81 @@ func (l *Level) Annotate(net *nav.Network) error { // IsSide rapporterar om banan är en sidobana. func (l *Level) IsSide() bool { return l.Kind == "side" } +// IsLeg rapporterar om banan är en delbana i en resa med byten (nås bara +// via en annan banas next, visas inte på bankartan). +func (l *Level) IsLeg() bool { return l.Kind == "leg" } + +// ChainOf ger resan som börjar med banan id: banan själv följd av dess +// delbanor i ordning. nil om id är okänt. +func ChainOf(all []*Level, id string) []*Level { + byID := map[string]*Level{} + for _, l := range all { + byID[l.ID] = l + } + var out []*Level + for l := byID[id]; l != nil && len(out) <= len(all); l = byID[l.Next] { + out = append(out, l) + if l.Next == "" { + break + } + } + return out +} + +// FinalOf är resans sista delbana (banan själv utan byten) – dess +// destination är resans resmål. +func FinalOf(all []*Level, l *Level) *Level { + if c := ChainOf(all, l.ID); len(c) > 0 { + return c[len(c)-1] + } + return l +} + +// ValidateChains kontrollerar resorna med byten: next finns och är en +// delbana, varje delbana används exakt en gång, inga cykler, och på +// nätkopplade banor börjar nästa delbana där den förra slutade. +func ValidateChains(all []*Level) error { + byID := map[string]*Level{} + for _, l := range all { + byID[l.ID] = l + } + refs := map[string]int{} + for _, l := range all { + if l.Next == "" { + continue + } + n := byID[l.Next] + if n == nil { + return fmt.Errorf("level %s: next %q finns inte", l.ID, l.Next) + } + if !n.IsLeg() { + return fmt.Errorf("level %s: next %q måste ha kind leg", l.ID, l.Next) + } + refs[n.ID]++ + if l.Networked() && n.Networked() && n.Station != l.To { + return fmt.Errorf("level %s: bytet hänger inte ihop – slutar i %q men %s börjar i %q", l.ID, l.To, n.ID, n.Station) + } + } + for _, l := range all { + if l.IsLeg() && refs[l.ID] != 1 { + return fmt.Errorf("level %s: delbanan refereras av %d banor, ska vara exakt 1", l.ID, refs[l.ID]) + } + } + for _, l := range all { + if l.IsLeg() { + continue + } + seen := map[string]bool{} + for c := l; c != nil && c.Next != ""; c = byID[c.Next] { + if seen[c.ID] { + return fmt.Errorf("level %s: resan går i cirkel", l.ID) + } + seen[c.ID] = true + } + } + return nil +} + // LoadAll läser alla banor i en mapp, sorterade på id, och kopplar dem som // pekar på linjenätet till ../network.json (läses en gång). func LoadAll(dir string) ([]*Level, error) { @@ -810,6 +886,9 @@ func LoadAll(dir string) ([]*Level, error) { } out = append(out, l) } + if err := ValidateChains(out); err != nil { + return nil, err + } return out, nil } diff --git a/internal/level/level_test.go b/internal/level/level_test.go index b7d7ccb..1b3f130 100644 --- a/internal/level/level_test.go +++ b/internal/level/level_test.go @@ -665,3 +665,70 @@ func TestCarsWomenOnlyAndCrowdFields(t *testing.T) { t.Fatal("filer") } } + +// TestStoryChains: resorna med byten hänger ihop, storyn börjar och slutar på +// flygplatsen, och felaktiga kedjor ger fel. +func TestStoryChains(t *testing.T) { + dir, err := res.Path("data", "levels") + if err != nil { + t.Fatal(err) + } + all, err := LoadAll(dir) // kör ValidateChains + if err != nil { + t.Fatal(err) + } + var first, last *Level + legs, chains := 0, 0 + for _, l := range all { + switch { + case l.IsLeg(): + legs++ + if l.Order != 0 { + t.Errorf("%s: delbanor har ingen order", l.ID) + } + case !l.IsSide(): + if first == nil || l.Order < first.Order { + first = l + } + if last == nil || l.Order > last.Order { + last = l + } + } + if l.Next != "" { + chains++ + c := ChainOf(all, l.ID) + if len(c) < 2 || c[0] != l || FinalOf(all, l) != c[len(c)-1] { + t.Errorf("%s: ChainOf/FinalOf = %v", l.ID, c) + } + } + } + if legs == 0 || chains == 0 { + t.Fatal("storyn ska ha resor med byten") + } + if first == nil || first.Station != "narita-airport" { + t.Errorf("storyn ska börja på flygplatsen, första banan är %v", first) + } + if last == nil || FinalOf(all, last).To != "narita-airport" { + t.Errorf("storyn ska sluta på flygplatsen, sista banan slutar i %q", FinalOf(all, last).To) + } + + mk := func(id, kind, next, station, to string) *Level { + return &Level{ID: id, Kind: kind, Next: next, Station: station, To: to} + } + bad := map[string][]*Level{ + "saknad next": {mk("a", "main", "zzz", "", "")}, + "next inte leg": {mk("a", "main", "b", "", ""), mk("b", "main", "", "", "")}, + "oanvänd delbana": {mk("a", "main", "", "", ""), mk("b", "leg", "", "", "")}, + "delbana två gånger": {mk("a", "main", "b", "", ""), mk("c", "main", "b", "", ""), mk("b", "leg", "", "", "")}, + "cirkel": {mk("a", "main", "b", "", ""), mk("b", "leg", "c", "", ""), mk("c", "leg", "b", "", "")}, + "byte hänger inte": {mk("a", "main", "b", "x", "y"), mk("b", "leg", "", "z", "w")}, + } + for name, lv := range bad { + if err := ValidateChains(lv); err == nil { + t.Errorf("%s: skulle ha gett fel", name) + } + } + if err := ValidateChains([]*Level{mk("a", "main", "b", "x", "y"), mk("b", "leg", "", "y", "w")}); err != nil { + t.Errorf("giltig kedja: %v", err) + } +} diff --git a/internal/progress/progress.go b/internal/progress/progress.go index aec4e07..da2a581 100644 --- a/internal/progress/progress.go +++ b/internal/progress/progress.go @@ -19,12 +19,13 @@ type LevelResult struct { // Progress är det som växer mellan banor. type Progress struct { - XP int `json:"xp"` - SkillPoints int `json:"skillPoints"` - Skills map[string]bool `json:"skills"` - Items map[string]bool `json:"items"` - Equipped map[string]string `json:"equipped"` // slot -> item-id - Levels map[string]*LevelResult `json:"levels"` + XP int `json:"xp"` + SkillPoints int `json:"skillPoints"` + Skills map[string]bool `json:"skills"` + Items map[string]bool `json:"items"` + Equipped map[string]string `json:"equipped"` // slot -> item-id + Levels map[string]*LevelResult `json:"levels"` + StoryCleared bool `json:"storyCleared"` // sista huvudbanan klarad: slutet har visats, oändligt läge/NG+ (M11) låses upp } // New börjar från noll. @@ -235,8 +236,8 @@ func (p *Progress) Unlocked(all []*level.Level, id string) bool { me = l } } - if me == nil { - return false + if me == nil || me.IsLeg() { + return false // delbanor nås bara via sin resa } if me.IsSide() { return p.Cleared(me.Parent) diff --git a/internal/sim/legs_test.go b/internal/sim/legs_test.go new file mode 100644 index 0000000..a19b261 --- /dev/null +++ b/internal/sim/legs_test.go @@ -0,0 +1,101 @@ +package sim + +import ( + "path/filepath" + "testing" + + "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level" + "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res" + "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng" +) + +// loadChain laddar alla banor och ger resan som börjar med id. +func loadChain(t *testing.T, id string) []*level.Level { + t.Helper() + dir, err := res.Path("data", "levels") + if err != nil { + t.Fatal(err) + } + all, err := level.LoadAll(dir) + if err != nil { + t.Fatal(err) + } + c := level.ChainOf(all, id) + if len(c) < 2 { + t.Fatalf("%s är ingen resa med byten: %v", id, c) + } + return c +} + +// TestJourneyWithTransfer: bana 007 fortsätter på Ueno efter första tåget – +// klockan, plånboken och sällskapet följer med, par-tiden adderas, och +// resan vinns först när andra tåget nått Asakusa. +func TestJourneyWithTransfer(t *testing.T) { + c := loadChain(t, "007-akihabara") + head, leg := c[0], c[1] + r := NewRun(head, RunOptions{Seed: rng.FromInt(7), Chain: c[1:]}) + if r.Legs() != 2 || r.LegNo() != 1 || r.IsLastLeg() || r.FinalDestination().Get("ja") != "浅草" || r.TimeLimit() != head.TimeLimitSec { + t.Fatalf("resans start: legs=%d no=%d final=%s limit=%d", r.Legs(), r.LegNo(), r.FinalDestination().Get("ja"), r.TimeLimit()) + } + par1 := r.ParSec() + for _, m := range r.Party.Members { + m.Card = 100000 // egna kort med gott om saldo: testet handlar om bytet + } + r.Wallet.Card = 100000 + tr := boardAt(t, r, level.Platform) + ride(t, r) + if r.Outcome != Ongoing || r.Level != leg || r.LegNo() != 2 || !r.IsLastLeg() { + t.Fatalf("efter första tåget: outcome=%v level=%s legNo=%d", r.Outcome, r.Level.ID, r.LegNo()) + } + if r.TransferAt < 0 || r.FlashKey != FlashTransfer || r.Player.Tile != leg.SpawnPoint() || len(r.Rides) != 1 || r.Rides[0] != tr { + t.Fatalf("bytet: transferAt=%.1f flash=%q tile=%v rides=%d", r.TransferAt, r.FlashKey, r.Player.Tile, len(r.Rides)) + } + if r.Time < 10 || r.ParSec() <= par1 || r.Fog.Explored(leg) >= 0.5 { + t.Fatalf("klockan ska fortsätta (%.1f), par adderas (%.0f > %.0f) och dimman är ny (%.2f)", r.Time, r.ParSec(), par1, r.Fog.Explored(leg)) + } + if from, to := r.Party.FollowerTiles(1); from != leg.SpawnPoint() || to != leg.SpawnPoint() { + t.Fatalf("sällskapet ska stå på nya starten: %v %v", from, to) + } + boardAt(t, r, level.Platform) + ride(t, r) + if r.Outcome != Won || len(r.Rides) != 2 || r.Score.Total <= 0 { + t.Fatalf("resan ska vinnas efter andra tåget: outcome=%v (%s) rides=%d", r.Outcome, r.LostReason, len(r.Rides)) + } + if r.Score.Explore <= 0 || r.Score.Explore > ScoreExploreMax { + t.Fatalf("utforskat är snittet över delbanorna: %d", r.Score.Explore) + } +} + +// TestJourneyWrongTrainOnSecondLeg: fel tåg på bytesstationen förlorar resan. +func TestJourneyWrongTrainOnSecondLeg(t *testing.T) { + c := loadChain(t, "007-akihabara") + r := NewRun(c[0], RunOptions{Seed: rng.FromInt(8), Chain: c[1:]}) + for _, m := range r.Party.Members { + m.Card = 100000 + } + r.Wallet.Card = 100000 + boardAt(t, r, level.Platform2) // Yamanote mot Ueno – också rätt + ride(t, r) + if r.Outcome != Ongoing || r.LegNo() != 2 { + t.Fatalf("första tåget: %v", r.Outcome) + } + boardAt(t, r, level.WrongPlatform) // Ginza mot Shibuya: fel håll + ride(t, r) + if r.Outcome != Lost || r.LostReason != LostWrongDirection { + t.Fatalf("outcome=%v reason=%q", r.Outcome, r.LostReason) + } +} + +// TestChainLevelsLoad: alla delbanor går att läsa en och en också. +func TestChainLevelsLoad(t *testing.T) { + dir, err := res.Path("data", "levels") + if err != nil { + t.Fatal(err) + } + for _, id := range []string{"007b-ueno", "008b-nippori"} { + l, err := level.LoadFile(filepath.Join(dir, id+".json")) + if err != nil || !l.IsLeg() { + t.Fatalf("%s: %v", id, err) + } + } +} diff --git a/internal/sim/party.go b/internal/sim/party.go index 29d5ebf..59ced2c 100644 --- a/internal/sim/party.go +++ b/internal/sim/party.go @@ -105,6 +105,9 @@ func NewParty(n int, start level.Point, seed rng.Seed) *Party { return p } +// Reset ställer hela sällskapet på en ny ruta (byte till nästa delbana). +func (p *Party) Reset(at level.Point) { p.trail = []level.Point{at} } + // Size är antalet i sällskapet inkl. spelaren. func (p *Party) Size() int { return len(p.Members) } diff --git a/internal/sim/run.go b/internal/sim/run.go index 5a310ac..5272eeb 100644 --- a/internal/sim/run.go +++ b/internal/sim/run.go @@ -35,6 +35,18 @@ type Run struct { Boarded *level.Train Mode nav.Mode // navigeringsläge: listan visar nästa tåg, skyltläget ger bonus + // Resor med byten: Level är aktuell delbana; tiden, plånboken och + // sällskapet följer med mellan delbanorna. + LegIndex int // 0 = första delbanan + TransferAt float64 // när senaste bytet skedde (< 0 = inget) + Rides []*level.Train // tågen man åkt med hittills + calmCars int // resor i en lugn vagn (bonus) + crowdedRide bool // någon resa i en full vagn (svettig-risk) + exploreSum float64 // utforskad andel i avklarade delbanor + legsDone int + timeLimit int // resans tidsgräns (första delbanans), 0 = ingen + maxMissed int // resans gräns för missade tåg (första delbanans) + // Språk: ordboken, spelarens kunskap (delas mellan banor) och hjälpen. Dict *lang.Dictionary Know *lang.Knowledge @@ -116,6 +128,7 @@ const ( // Fler flash-nycklar. const ( FlashWomenOnly = "hud.women_only" + FlashTransfer = "hud.transfer_done" FlashEarly = "hud.early_board" FlashBump = "hud.bump" ) @@ -213,6 +226,8 @@ type RunOptions struct { InfiniteBattery bool // telefonen laddar inte ur Mode nav.Mode // navigeringsläge (nollvärdet = listläge) + + Chain []*level.Level // resans fortsättning: delbanorna efter den första (byten), i ordning } // DefaultSpeed är spelarens grundfart i rutor per sekund. @@ -261,7 +276,10 @@ func NewRun(l *level.Level, o RunOptions) *Run { Visits: map[level.Point]int{}, lastTile: l.SpawnPoint(), ProgressedAt: -1, - parSec: float64(l.ParSec), + parSec: parFor(l), + timeLimit: l.TimeLimitSec, + maxMissed: l.MaxMissedTrains, + TransferAt: -1, Party: NewParty(l.Party, l.SpawnPoint(), o.Seed.Derive("party")), baseSpeed: o.Speed, NoticeAt: -1, @@ -288,14 +306,91 @@ func NewRun(l *level.Level, o RunOptions) *Run { r.Fog.RevealAll(l) } r.Fog.Reveal(l, l.SpawnPoint()) - if r.parSec <= 0 { - n := len(l.Path(l.SpawnPoint(), l.CorrectPlatform(), l.CanEnter)) - r.parSec = float64(max(0, n-1))*ParSecPerTile + ParSlackSec - } r.Mode = o.Mode return r } +// parFor är "bra tid" för en delbana: parSec i bandatan, annars kortaste +// vägen i rutor × ParSecPerTile + ParSlackSec. +func parFor(l *level.Level) float64 { + if l.ParSec > 0 { + return float64(l.ParSec) + } + n := len(l.Path(l.SpawnPoint(), l.CorrectPlatform(), l.CanEnter)) + return float64(max(0, n-1))*ParSecPerTile + ParSlackSec +} + +// Legs är antalet delbanor i resan, LegNo den aktuella (1-baserad). +func (r *Run) Legs() int { return 1 + len(r.opts.Chain) } +func (r *Run) LegNo() int { return r.LegIndex + 1 } + +// IsLastLeg rapporterar om det inte blir fler byten. +func (r *Run) IsLastLeg() bool { return r.LegIndex >= len(r.opts.Chain) } + +// FinalDestination är resans slutmål (sista delbanans destination). +func (r *Run) FinalDestination() level.Text { + if n := len(r.opts.Chain); n > 0 { + return r.opts.Chain[n-1].Destination + } + return r.Level.Destination +} + +// TimeLimit och MaxMissed är resans gränser (0 = ingen). +func (r *Run) TimeLimit() int { return r.timeLimit } +func (r *Run) MaxMissed() int { return r.maxMissed } + +// nextLeg är nästa delbana, eller nil på den sista. +func (r *Run) nextLeg() *level.Level { + if r.IsLastLeg() { + return nil + } + return r.opts.Chain[r.LegIndex] +} + +// startLeg byter till nästa delbana: ny station, spelaren och sällskapet på +// dess start, ny dimma och nytt folk – men klockan, plånboken, mätarna och +// räknarna fortsätter. Par-tiden adderas. +func (r *Run) startLeg(l *level.Level) { + r.exploreSum += r.Fog.Explored(r.Level) + r.legsDone++ + r.LegIndex++ + r.Level = l + spawn := l.SpawnPoint() + r.Player = NewMover(spawn, r.baseSpeed) + r.Party.Reset(spawn) + r.Fog = NewFog(l, FogRadius+r.opts.FogRadius) + r.Fog.ExtraLandmarks = r.opts.Landmarks + if r.opts.RevealAll { + r.Fog.RevealAll(l) + } + r.Fog.Reveal(l, spawn) + r.NPCs = spawnNPCs(l, l.Crowd*NPCPerCrowd, r.npcRand) + r.platformPhase = map[level.Tile]level.Phase{} + for _, t := range level.PlatformTiles { + r.platformPhase[t], _ = r.PlatformPhase(t) + } + r.Revealed = map[level.Point]bool{} + r.Visits = map[level.Point]int{} + r.lastTile = spawn + r.Mini, r.MiniKind, r.PendingTrain, r.CarChosen = nil, MiniNone, nil, nil + r.Help, r.Facility = HelpState{}, FacilityState{} + r.DeniedAt, r.BumpedAt, r.WrongLane = -1, -1, false + r.parSec += parFor(l) + r.TransferAt = r.Time + r.flash(FlashTransfer) +} + +// rideDone bokför en avslutad tågresa (vagnbonus, svettig-risk). +func (r *Run) rideDone(tr *level.Train) { + r.Rides = append(r.Rides, tr) + if r.CarChosen != nil && r.CarChosen.Crowd == 0 { + r.calmCars++ + } + if r.CarChosen != nil && r.CarChosen.Crowd == 2 { + r.crowdedRide = true + } +} + // ParSec är tiden för tre stjärnor. func (r *Run) ParSec() float64 { return r.parSec } @@ -735,6 +830,11 @@ func (r *Run) StepMini(dt float64, in minigame.Input) { r.Blocked += g.Blocked switch { case g.Success(): + if next := r.nextLeg(); next != nil { + r.rideDone(tr) + r.startLeg(next) // bytet: resan fortsätter på nästa station + return + } r.finish(tr, true, "") case !tr.Correct: reason := LostWrongTrain @@ -801,22 +901,22 @@ func (r *Run) checkLimits() { if r.Outcome != Ongoing { return } - if r.Level.TimeLimitSec > 0 && r.Time > float64(r.Level.TimeLimitSec) { + if r.timeLimit > 0 && r.Time > float64(r.timeLimit) { r.Mini, r.MiniKind, r.PendingTrain = nil, MiniNone, nil r.finish(nil, false, LostTimeUp) return } - if r.Level.MaxMissedTrains > 0 && r.MissedTrains > r.Level.MaxMissedTrains { + if r.maxMissed > 0 && r.MissedTrains > r.maxMissed { r.finish(nil, false, LostMissedTrains) } } // TimeLeft är sekunder kvar av tidsgränsen (0 utan gräns). func (r *Run) TimeLeft() float64 { - if r.Level.TimeLimitSec == 0 { + if r.timeLimit == 0 { return 0 } - return max(0, float64(r.Level.TimeLimitSec)-r.Time) + return max(0, float64(r.timeLimit)-r.Time) } // computeScore fyller i poängen för en vunnen bana. @@ -825,15 +925,13 @@ func (r *Run) computeScore() { sc.Time = int(ScoreTimeMax * min(1, r.parSec/max(r.Time, 0.001))) sc.Money = max(0, ScoreMoneyMax-(r.Wallet.Spent-r.spentStart)/10) sc.Meters = int(ScoreMetersMax * r.Party.AvgMeters()) - sc.Explore = int(ScoreExploreMax * r.Fog.Explored(r.Level)) + sc.Explore = int(ScoreExploreMax * (r.exploreSum + r.Fog.Explored(r.Level)) / float64(r.legsDone+1)) sc.Penalty = PenaltyMissed*r.MissedTrains + PenaltyRescan*r.Rescans + PenaltyGrumpy*r.Grumpy + PenaltyEarlyBoard*r.EarlyBoard + PenaltyBlocked*r.Blocked if r.WomenOnlyViolation { sc.Penalty += PenaltyWomenOnly } - if r.CarChosen != nil && r.CarChosen.Crowd == 0 { - sc.Car = ScoreCarBonus - } + sc.Car = ScoreCarBonus * r.calmCars base := sc.Time + sc.Money + sc.Meters + sc.Explore + sc.Car if r.Mode == nav.Signs { sc.Bonus = base * ScoreSignsBonusPct / 100 @@ -860,7 +958,7 @@ func (r *Run) earnedDebuffs() []Debuff { if r.poisoned { out = append(out, Debuff{FoodPoisoning, FoodPoisonLevels}) } - if r.CarChosen != nil && r.CarChosen.Crowd == 2 && r.Seed.Derive("sweaty").New().Float64() < SweatyChance/r.Stats[StatLuck] { + if r.crowdedRide && r.Seed.Derive("sweaty").New().Float64() < SweatyChance/r.Stats[StatLuck] { out = append(out, Debuff{Sweaty, 1}) } return out @@ -968,6 +1066,7 @@ func (r *Run) SignHere() *level.SignText { // tillräckligt bra resultat lär man sig resmålets ord. func (r *Run) finish(tr *level.Train, won bool, reason string) { r.Boarded = tr + r.rideDone(tr) r.NewDebuffs = r.earnedDebuffs() if !won { r.Outcome = Lost diff --git a/resources/data/levels/007-akihabara.json b/resources/data/levels/007-akihabara.json new file mode 100644 index 0000000..5bb2739 --- /dev/null +++ b/resources/data/levels/007-akihabara.json @@ -0,0 +1,179 @@ +{ + "clutter": 2, + "crowd": 2, + "destination": { + "en": "Ueno", + "ja": "上野", + "romaji": "Ueno", + "sv": "Ueno" + }, + "difficulty": 4, + "fareYen": 170, + "grid": [ + "#################################", + "#S....#...........#...........#W#", + "#####.#.#########.#.#.###.###.#.#", + "#.....#.#...#...#.#.#...#...#...#", + "#.#####.#.#.#.#.#.#.#.#.#.#.###.#", + "#i..#.....#...#.#.#.#.#.#.#.#...#", + "#.#.#.#########.#.#.#.#.#.#.#.###", + "#.#...#.......#.#...#..i#.#.#...#", + "#.#####.#####.#.#######.#.#.#####", + "#.......#T#...#.......#.#K#.#...#", + "#########.#.#####.###.#.###.#.#.#", + "#...#....i..#...#.#.#.#.#...#.#.#", + "#.###.#######.#.#.#.#.#.#.#.#.#.#", + "#.#...#.......#.#...#.#...#.#.#.#", + "#.#.#####.#.###.#####.#######.#.#", + "#.#.....#.#.#...#...#.........#.#", + "#.#####.#.#.#.###.#.###.#######.#", + "#.......#.#.#.#...#...#.#.......#", + "#G###.###G#.#.#.#####.###.#####G#", + "#pG.....Gq#.....#QG.......#...GP#", + "#################################" + ], + "id": "007-akihabara", + "individualCards": true, + "kind": "main", + "maxMissedTrains": 3, + "name": { + "en": "Akihabara Station", + "ja": "秋葉原駅", + "sv": "Akihabara station" + }, + "next": "007b-ueno", + "noise": 1, + "order": 7, + "party": 4, + "rideStops": 3, + "shop": [ + "mask_tengu", + "onigiri_box", + "rail_pass" + ], + "signs": [ + { + "text": { + "en": "Akihabara Station. Track 4 Hibiya Line for Kita-Senju: south-east. Track 2 Yamanote Line for Ueno: south. Track 1 Yamanote Line for Tokyo and track 3 Hibiya Line for Naka-Meguro: south-west.", + "ja": "秋葉原駅 4番線 日比谷線 北千住方面 → 南東 2番線 山手線 上野方面 → 南 1番線 山手線 東京方面・3番線 日比谷線 中目黒方面 → 南西", + "sv": "Akihabara station. Spår 4 Hibiya-linjen mot Kita-Senju: sydost. Spår 2 Yamanote-linjen mot Ueno: söderut. Spår 1 Yamanote-linjen mot Tokyo och spår 3 Hibiya-linjen mot Naka-Meguro: sydväst." + }, + "x": 1, + "y": 5, + "z": 0 + }, + { + "text": { + "en": "\u003c- Track 3 Hibiya Line for Naka-Meguro Track 4 Hibiya Line for Kita-Senju -\u003e", + "ja": "← 3番線 日比谷線 中目黒方面 4番線 日比谷線 北千住方面 →", + "sv": "\u003c- Spår 3 Hibiya-linjen mot Naka-Meguro Spår 4 Hibiya-linjen mot Kita-Senju -\u003e" + }, + "x": 9, + "y": 11, + "z": 0 + }, + { + "text": { + "en": "Ueno: transfer to the Ginza Line for Asakusa.", + "ja": "上野 → 乗り換え 銀座線 浅草方面", + "sv": "Ueno: byte till Ginza-linjen mot Asakusa." + }, + "x": 23, + "y": 7, + "z": 0 + } + ], + "station": "akihabara", + "timeLimitSec": 600, + "to": "ueno", + "trains": [ + { + "cars": 4, + "destination": { + "en": "Kita-Senju", + "ja": "北千住", + "romaji": "Kita-Senju" + }, + "line": { + "en": "Hibiya Line", + "ja": "日比谷線", + "romaji": "Hibiya-sen" + }, + "lineId": "hibiya", + "platform": "P", + "schedule": { + "dwellSec": 9, + "intervalSec": 28, + "offsetSec": 8 + }, + "terminus": "kita-senju", + "track": "4" + }, + { + "cars": 3, + "destination": { + "en": "Naka-Meguro", + "ja": "中目黒", + "romaji": "Naka-Meguro" + }, + "line": { + "en": "Hibiya Line", + "ja": "日比谷線", + "romaji": "Hibiya-sen" + }, + "lineId": "hibiya", + "platform": "p", + "schedule": { + "dwellSec": 8, + "intervalSec": 28, + "offsetSec": 16 + }, + "terminus": "naka-meguro", + "track": "3" + }, + { + "cars": 4, + "destination": { + "en": "Ueno", + "ja": "上野", + "romaji": "Ueno" + }, + "line": { + "en": "Yamanote Line", + "ja": "山手線", + "romaji": "Yamanote-sen" + }, + "lineId": "yamanote", + "platform": "Q", + "schedule": { + "dwellSec": 9, + "intervalSec": 32, + "offsetSec": 12 + }, + "terminus": "ueno", + "track": "2" + }, + { + "cars": 4, + "destination": { + "en": "Tokyo", + "ja": "東京", + "romaji": "Tokyo" + }, + "line": { + "en": "Yamanote Line", + "ja": "山手線", + "romaji": "Yamanote-sen" + }, + "lineId": "yamanote", + "platform": "q", + "schedule": { + "dwellSec": 8, + "intervalSec": 32, + "offsetSec": 4 + }, + "terminus": "tokyo", + "track": "1" + } + ] +} \ No newline at end of file diff --git a/resources/data/levels/007b-ueno.json b/resources/data/levels/007b-ueno.json new file mode 100644 index 0000000..0d4778c --- /dev/null +++ b/resources/data/levels/007b-ueno.json @@ -0,0 +1,148 @@ +{ + "clutter": 1, + "crowd": 2, + "destination": { + "en": "Asakusa", + "ja": "浅草", + "romaji": "Asakusa", + "sv": "Asakusa" + }, + "difficulty": 4, + "fareYen": 170, + "grid": [ + "#################################", + "#S.#..\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..###", + "#..#..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..###", + "#\u003c\u003e################\u003c\u003e#######\u003c\u003e###", + "#..#..\u003ci.\u003c..\u003c..\u003c..#..#..\u003c..#..###", + "#..#..\u003e..\u003e..\u003e..\u003eW.#..#..\u003e..#..###", + "#\u003c\u003e#\u003c\u003e##########\u003c\u003e#\u003c\u003e#\u003c\u003e####\u003c\u003e###", + "#..\u003c..#..\u003c..\u003c..#..#..#..\u003c..\u003c..###", + "#..\u003e..#..\u003e..\u003e..#..#..#..\u003e..\u003e..###", + "#######\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#########", + "#..#..\u003c..\u003c..#..#..#..\u003c..#..\u003c..###", + "#..#..\u003e..\u003e..#..#..#..\u003e..#..\u003e..###", + "#\u003c\u003e#\u003c\u003e#######\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e####\u003c\u003e###", + "#..#K.\u003c..\u003c..#..#..\u003c.i#.T\u003c..\u003c..###", + "#..#i.\u003e..\u003e..#..#..\u003e..#..\u003e..\u003e..###", + "#\u003c\u003e#\u003c\u003e####\u003c\u003e#\u003c\u003e#\u003c\u003e##########\u003c\u003e###", + "#G.\u003c..\u003c..\u003c..#..\u003cG.\u003c..\u003c..\u003c..\u003c.G###", + "#pG\u003e..\u003e..\u003e..#..GQG\u003e..\u003e..\u003e..\u003eGP###", + "#################################", + "#################################", + "#################################" + ], + "id": "007b-ueno", + "individualCards": true, + "kind": "leg", + "name": { + "en": "Ueno Station", + "ja": "上野駅", + "sv": "Ueno station" + }, + "noise": 2, + "party": 4, + "rideStops": 3, + "signs": [ + { + "text": { + "en": "Ueno Station. Track 1 Ginza Line for Asakusa: south-east. Track 2 Ginza Line for Shibuya: south-west. Track 3 Hibiya Line for Kita-Senju: south.", + "ja": "上野駅 1番線 銀座線 浅草方面 → 南東 2番線 銀座線 渋谷方面 → 南西 3番線 日比谷線 北千住方面 → 南", + "sv": "Ueno station. Spår 1 Ginza-linjen mot Asakusa: sydost. Spår 2 Ginza-linjen mot Shibuya: sydväst. Spår 3 Hibiya-linjen mot Kita-Senju: söderut." + }, + "x": 7, + "y": 4, + "z": 0 + }, + { + "text": { + "en": "\u003c- Track 2 Ginza Line for Shibuya Track 1 Ginza Line for Asakusa -\u003e", + "ja": "← 2番線 銀座線 渋谷方面 1番線 銀座線 浅草方面 →", + "sv": "\u003c- Spår 2 Ginza-linjen mot Shibuya Spår 1 Ginza-linjen mot Asakusa -\u003e" + }, + "x": 20, + "y": 13, + "z": 0 + }, + { + "text": { + "en": "Track 1 Ginza Line for Asakusa: the gate in the south-east corner.", + "ja": "1番線 銀座線 浅草方面 → 南東口 改札", + "sv": "Spår 1 Ginza-linjen mot Asakusa: spärren i sydöstra hörnet." + }, + "x": 4, + "y": 14, + "z": 0 + } + ], + "station": "ueno", + "to": "asakusa", + "trains": [ + { + "cars": 3, + "destination": { + "en": "Asakusa", + "ja": "浅草", + "romaji": "Asakusa" + }, + "line": { + "en": "Ginza Line", + "ja": "銀座線", + "romaji": "Ginza-sen" + }, + "lineId": "ginza", + "platform": "P", + "schedule": { + "dwellSec": 8, + "intervalSec": 26, + "offsetSec": 10 + }, + "terminus": "asakusa", + "track": "1" + }, + { + "cars": 3, + "destination": { + "en": "Shibuya", + "ja": "渋谷", + "romaji": "Shibuya" + }, + "line": { + "en": "Ginza Line", + "ja": "銀座線", + "romaji": "Ginza-sen" + }, + "lineId": "ginza", + "platform": "p", + "schedule": { + "dwellSec": 8, + "intervalSec": 26, + "offsetSec": 18 + }, + "terminus": "shibuya", + "track": "2" + }, + { + "cars": 4, + "destination": { + "en": "Kita-Senju", + "ja": "北千住", + "romaji": "Kita-Senju" + }, + "line": { + "en": "Hibiya Line", + "ja": "日比谷線", + "romaji": "Hibiya-sen" + }, + "lineId": "hibiya", + "platform": "Q", + "schedule": { + "dwellSec": 8, + "intervalSec": 30, + "offsetSec": 6 + }, + "terminus": "kita-senju", + "track": "3" + } + ] +} \ No newline at end of file diff --git a/resources/data/levels/008-tokyo-hemresa.json b/resources/data/levels/008-tokyo-hemresa.json new file mode 100644 index 0000000..38fe937 --- /dev/null +++ b/resources/data/levels/008-tokyo-hemresa.json @@ -0,0 +1,214 @@ +{ + "clutter": 2, + "crowd": 2, + "destination": { + "en": "Nippori", + "ja": "日暮里", + "romaji": "Nippori", + "sv": "Nippori" + }, + "difficulty": 5, + "fareYen": 200, + "floors": [ + { + "grid": [ + "#################################", + "#S#.........#...........#.......#", + "#.#.#.###.#.#.#########.#.#####.#", + "#.#.#.#.#.#.#.#.#...#...#.....#.#", + "#.#.#.#.#.###.#.#.#.#.#####.###.#", + "#.#.....#.....#...#.#.......#...#", + "#.#####.###########.#########.###", + "#.....#...#.......#.....#...#...#", + "#####.###.###.###.#####.#.#.###.#", + "#...#.#.........#.......#.#.#...#", + "###.#.#########.#######.#.#.#.###", + "#...#...#......U#.......#.#.#...#", + "#.#####.#.#######.#######.#.###.#", + "#...#...#.........#.......#...#.#", + "###.#.#############.#########.#.#", + "#...#.#...#.........#...#.....#.#", + "#.###.#.#.#.###.#####.#.#.#####.#", + "#...#...#...#...#.....#.#...#...#", + "#.#.#########.###.#.#######.#.#.#", + "#.#...............#.............#", + "#################################" + ], + "name": "B1" + }, + { + "grid": [ + "#################################", + "#T#..................K#.........#", + "#.#.#######.#####.#####.###.###.#", + "#.#.............#.....#.#...#.#.#", + "#.#####.#######.###.#.#.#.###.#.#", + "#.#.....#.....#.....#...#.#...#.#", + "#.#.#####.###.#.#########.#.###.#", + "#...#.....#...#.#.......#.#.....#", + "#.###.#####.###.###.###.#.#####.#", + "#...#.#..W#...#...#...#.........#", + "###.#.#.#####.###.#.#.#####.#.###", + "#.....#i....#..D#.#.#.#...#.#...#", + "#.#####.###.###.#.###.#.#.#####.#", + "#.......#.#.....#.#...#.#.#.....#", + "#.#######.#######.#.###.#.#.###.#", + "#.....#..........i..#...#...#...#", + "#####.###.###########.#######.###", + "#...#...#.#...........#.....#...#", + "#G#.###.###.###########.#######G#", + "#p#....iGqG.#...GQG...........GP#", + "#################################" + ], + "name": "1F" + } + ], + "id": "008-tokyo-hemresa", + "individualCards": true, + "kind": "main", + "maxMissedTrains": 3, + "name": { + "en": "Tokyo Station, going home", + "ja": "東京駅", + "sv": "Tokyo station, hemresan" + }, + "next": "008b-nippori", + "noise": 2, + "order": 8, + "party": 4, + "rewards": { + "skillPoints": 2 + }, + "rideStops": 4, + "shop": [ + "suit", + "mask_tengu", + "onigiri_box", + "rail_pass" + ], + "signs": [ + { + "text": { + "en": "Tokyo Station. Track 4 Yamanote Line for Ueno: south-east. Track 3 Keihin-Tohoku Line for Omiya: south. Track 1 Chuo Line for Takao and track 5 Yamanote Line for Shinagawa: south-west. The platforms are on 1F.", + "ja": "東京駅 4番線 山手線 上野方面 → 南東 3番線 京浜東北線 大宮方面 → 南 1番線 中央線 高尾方面・5番線 山手線 品川方面 → 南西", + "sv": "Tokyo station. Spår 4 Yamanote-linjen mot Ueno: sydost. Spår 3 Keihin-Tohoku-linjen mot Omiya: söderut. Spår 1 Chuo-linjen mot Takao och spår 5 Yamanote-linjen mot Shinagawa: sydväst. Perrongerna ligger på 1F." + }, + "x": 7, + "y": 11, + "z": 1 + }, + { + "text": { + "en": "\u003c- Track 5 Yamanote Line for Shinagawa Track 4 Yamanote Line for Ueno -\u003e", + "ja": "← 5番線 山手線 品川方面 4番線 山手線 上野方面 →", + "sv": "\u003c- Spår 5 Yamanote-linjen mot Shinagawa Spår 4 Yamanote-linjen mot Ueno -\u003e" + }, + "x": 7, + "y": 19, + "z": 1 + }, + { + "text": { + "en": "Nippori: transfer to the Keisei Line for Narita Airport.", + "ja": "日暮里 → 乗り換え 京成本線 成田空港方面", + "sv": "Nippori: byte till Keisei-linjen mot Narita flygplats." + }, + "x": 17, + "y": 15, + "z": 1 + } + ], + "station": "tokyo", + "timeLimitSec": 720, + "to": "nippori", + "trains": [ + { + "cars": 4, + "destination": { + "en": "Ueno", + "ja": "上野", + "romaji": "Ueno" + }, + "line": { + "en": "Yamanote Line", + "ja": "山手線", + "romaji": "Yamanote-sen" + }, + "lineId": "yamanote", + "platform": "P", + "schedule": { + "dwellSec": 9, + "intervalSec": 30, + "offsetSec": 10 + }, + "terminus": "ueno", + "track": "4" + }, + { + "cars": 4, + "destination": { + "en": "Shinagawa", + "ja": "品川", + "romaji": "Shinagawa" + }, + "line": { + "en": "Yamanote Line", + "ja": "山手線", + "romaji": "Yamanote-sen" + }, + "lineId": "yamanote", + "platform": "p", + "schedule": { + "dwellSec": 8, + "intervalSec": 30, + "offsetSec": 20 + }, + "terminus": "shinagawa", + "track": "5" + }, + { + "cars": 4, + "destination": { + "en": "Omiya", + "ja": "大宮", + "romaji": "Omiya" + }, + "line": { + "en": "Keihin-Tohoku Line", + "ja": "京浜東北線", + "romaji": "Keihin-Tohoku-sen" + }, + "lineId": "keihin-tohoku", + "platform": "Q", + "schedule": { + "dwellSec": 9, + "intervalSec": 34, + "offsetSec": 4 + }, + "terminus": "omiya", + "track": "3" + }, + { + "cars": 3, + "destination": { + "en": "Takao", + "ja": "高尾", + "romaji": "Takao" + }, + "line": { + "en": "Chuo Line", + "ja": "中央線", + "romaji": "Chuo-sen" + }, + "lineId": "chuo", + "platform": "q", + "schedule": { + "dwellSec": 8, + "intervalSec": 28, + "offsetSec": 14 + }, + "terminus": "takao", + "track": "1" + } + ] +} \ No newline at end of file diff --git a/resources/data/levels/008b-nippori.json b/resources/data/levels/008b-nippori.json new file mode 100644 index 0000000..e34a47d --- /dev/null +++ b/resources/data/levels/008b-nippori.json @@ -0,0 +1,150 @@ +{ + "clutter": 1, + "crowd": 1, + "destination": { + "en": "Narita Airport", + "ja": "成田空港", + "romaji": "Narita Airport", + "sv": "Narita flygplats" + }, + "difficulty": 5, + "fareYen": 500, + "grid": [ + "#################################", + "#S#W....#.....#.....#...#.......#", + "#.#####.#.###.#.#.#.#.#.#.#.#.#.#", + "#.....#.#...#...#.....#...#.#...#", + "#####.#.###.###############.#####", + "#K#...#...#...#.......#...#.....#", + "#.#.#####.###.#.#.###.#.#.#####.#", + "#.#.#...#.....#.#....i#.#...#...#", + "#.#.#.#.###.###.#####.#.###.#.#.#", + "#.#...#...#...#.....#...#.#...#.#", + "#.#######.#######.#.#####.#####.#", + "#...#...#.#.....#.#.#.#.......#.#", + "#.#.###.#.#.###.#.#.#.#.#####.#.#", + "#.#.....#...#T#i..#.#...#.....#.#", + "#.#.#########.#.###.#####.#####.#", + "#.#.#.....#..i..#...#.....#.....#", + "#.###.#.#.#.#####.###.###.#.#####", + "#.....#.#.#.......#...#...#.....#", + "#G#####.#.#########.#.#########G#", + "#pG...#.........GQG.#.........GP#", + "#################################" + ], + "id": "008b-nippori", + "individualCards": true, + "kind": "leg", + "name": { + "en": "Nippori Station", + "ja": "日暮里駅", + "sv": "Nippori station" + }, + "noise": 1, + "party": 4, + "rideStops": 4, + "seatSelection": true, + "signs": [ + { + "text": { + "en": "Nippori Station. Track 1 Keisei Line for Narita Airport: south-east. Track 2 Keisei Line for Keisei-Ueno: south-west. Track 11 Yamanote Line for Ueno: south. Ticket required.", + "ja": "日暮里駅 1番線 京成本線 成田空港方面 → 南東 2番線 京成本線 京成上野方面 → 南西 11番線 山手線 上野方面 → 南", + "sv": "Nippori station. Spår 1 Keisei-linjen mot Narita flygplats: sydost. Spår 2 Keisei-linjen mot Keisei-Ueno: sydväst. Spår 11 Yamanote-linjen mot Ueno: söderut. Biljett krävs." + }, + "x": 15, + "y": 13, + "z": 0 + }, + { + "text": { + "en": "\u003c- Track 2 Keisei Line for Keisei-Ueno Track 1 Keisei Line for Narita Airport -\u003e", + "ja": "← 2番線 京成本線 京成上野方面 1番線 京成本線 成田空港方面 →", + "sv": "\u003c- Spår 2 Keisei-linjen mot Keisei-Ueno Spår 1 Keisei-linjen mot Narita flygplats -\u003e" + }, + "x": 13, + "y": 15, + "z": 0 + }, + { + "text": { + "en": "Narita Airport: track 1. Tickets at the machine.", + "ja": "成田空港 → 1番線 切符 → 券売機", + "sv": "Narita flygplats: spår 1. Biljetter i automaten." + }, + "x": 21, + "y": 7, + "z": 0 + } + ], + "station": "nippori", + "ticketRequired": true, + "to": "narita-airport", + "trains": [ + { + "cars": 4, + "destination": { + "en": "Narita Airport", + "ja": "成田空港", + "romaji": "Narita Airport" + }, + "line": { + "en": "Keisei Main Line", + "ja": "京成本線", + "romaji": "Keisei Honsen" + }, + "lineId": "keisei-main", + "platform": "P", + "schedule": { + "dwellSec": 10, + "intervalSec": 36, + "offsetSec": 12 + }, + "terminus": "narita-airport", + "track": "1" + }, + { + "cars": 3, + "destination": { + "en": "Keisei-Ueno", + "ja": "京成上野", + "romaji": "Keisei-Ueno" + }, + "line": { + "en": "Keisei Main Line", + "ja": "京成本線", + "romaji": "Keisei Honsen" + }, + "lineId": "keisei-main", + "platform": "p", + "schedule": { + "dwellSec": 8, + "intervalSec": 36, + "offsetSec": 22 + }, + "terminus": "keisei-ueno", + "track": "2" + }, + { + "cars": 4, + "destination": { + "en": "Ueno", + "ja": "上野", + "romaji": "Ueno" + }, + "line": { + "en": "Yamanote Line", + "ja": "山手線", + "romaji": "Yamanote-sen" + }, + "lineId": "yamanote", + "platform": "Q", + "schedule": { + "dwellSec": 8, + "intervalSec": 30, + "offsetSec": 6 + }, + "terminus": "ueno", + "track": "11" + } + ] +} \ No newline at end of file diff --git a/resources/sprites/plane.png b/resources/sprites/plane.png new file mode 100644 index 0000000000000000000000000000000000000000..a598abebfb0474f2aecdb830a3c38f356df593bf GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^3P8-m!3HFmMfUFmQaPS3jv*Cul9D764lpULzj@d!YZH}w49wOZe89n&bYKhX>;K7u#~30x8+)9!SRSma{rpfn&~AxH7e|5dBoU^{ z;(5Wnyhhwl0wv@o2spGYxR9V2$bWS)L#uP|9fOi$MpZ?}RqH1*CNOmP8as 1 && !matches(l.ID, os.Args[1:]) { + continue + } + problems += report(l, all, out) + } + if problems > 0 { + fmt.Printf("\n%d problem\n", problems) + os.Exit(1) + } + fmt.Println("\ninga problem") +} + +func matches(id string, words []string) bool { + for _, w := range words { + if strings.Contains(id, w) { + return true + } + } + return false +} + +// report skriver rapporten för en bana och ritar varje våning. +func report(l *level.Level, all []*level.Level, out string) (problems int) { + fmt.Printf("== %s (%s, %dx%d, %d våning(ar), svårighet %d, sällskap %d)\n", l.ID, l.Kind, l.Width(), l.Height(), l.NumFloors(), l.Difficulty, l.Party) + reach := l.Reachable() + if !l.Solvable() { + fmt.Println(" FEL: går inte att klara från start") + problems++ + } + for _, p := range l.Unreachable() { + fmt.Printf(" FEL: onåbar %c på %v\n", l.At(p), p) + problems++ + } + for _, p := range l.UngatedPlatforms() { + fmt.Printf(" FEL: perrong %v nås utan spärr\n", p) + problems++ + } + for _, tr := range l.Trains { + pts := l.Find(level.Tile(tr.Platform[0])) + dist := len(l.Path(l.SpawnPoint(), pts[0], l.CanEnter)) - 1 + v := "correct=" + fmt.Sprint(tr.Correct) + if tr.Verdict != nav.Unjudged { + v = tr.Verdict.String() + } + fmt.Printf(" tåg %s spår %-3s %-8s → %-10s %-16s %3d steg från start\n", tr.Platform, tr.Track, tr.Line.Get("romaji"), tr.Destination.Get("romaji"), v, dist) + } + if l.Next != "" { + var ids []string + for _, c := range level.ChainOf(all, l.ID) { + ids = append(ids, c.ID) + } + fmt.Printf(" resa: %s\n", strings.Join(ids, " → ")) + } + fmt.Printf(" skyltar %d, utforskbart %d rutor\n", len(l.Signs), len(reach)) + for z := 0; z < l.NumFloors(); z++ { + name := fmt.Sprintf("%s/%s-%d.png", out, l.ID, z) + fail(savePNG(name, draw(l, z, reach))) + fmt.Printf(" bild: %s\n", name) + } + return problems +} + +// draw ritar en våning: rutor i palettfärg, onåbara rutor rödmarkerade, +// perronger orange (rätt tåg grönkantad), spelarens kortaste väg prickad. +func draw(l *level.Level, z int, reach map[level.Point]bool) image.Image { + img := image.NewRGBA(image.Rect(0, 0, l.Width()*scale, l.Height()*scale)) + fill := func(x, y int, col color.RGBA, inset int) { + for dy := inset; dy < scale-inset; dy++ { + for dx := inset; dx < scale-inset; dx++ { + img.SetRGBA(x*scale+dx, y*scale+dy, col) + } + } + } + correct := map[level.Tile]bool{} + for _, tr := range l.Trains { + correct[level.Tile(tr.Platform[0])] = tr.Correct + } + for y := 0; y < l.Height(); y++ { + for x := 0; x < l.Width(); x++ { + p := level.Point{X: x, Y: y, Z: z} + t := l.At(p) + col, ok := palette[t] + switch { + case t.IsPlatform(): + col = color.RGBA{250, 150, 50, 255} + if correct[t] { + fill(x, y, color.RGBA{60, 220, 90, 255}, 0) + fill(x, y, col, 1) + continue + } + case !ok: + col = color.RGBA{255, 0, 255, 255} + } + fill(x, y, col, 0) + if t.Passable() && !reach[p] { + fill(x, y, color.RGBA{220, 40, 40, 255}, 2) + } + } + } + if path := l.Path(l.SpawnPoint(), l.CorrectPlatform(), l.CanEnter); path != nil { + for _, p := range path { + if p.Z == z { + fill(p.X, p.Y, color.RGBA{30, 30, 30, 255}, 3) + } + } + } + return img +} + +func savePNG(path string, img image.Image) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return png.Encode(f, img) +} + +func repoRoot() string { + dir, err := os.Getwd() + fail(err) + for d := dir; ; d = filepath.Dir(d) { + if _, err := os.Stat(filepath.Join(d, "go.mod")); err == nil { + return d + } + if filepath.Dir(d) == d { + return dir + } + } +} + +func fail(err error) { + if err != nil { + fmt.Fprintln(os.Stderr, "levelcheck:", err) + os.Exit(2) + } +} diff --git a/tools/shots/main.go b/tools/shots/main.go index 5f17616..01f3a38 100644 --- a/tools/shots/main.go +++ b/tools/shots/main.go @@ -122,6 +122,14 @@ func buildShots(levelsDir string) error { if err != nil { return err } + l7, err := level.LoadFile(filepath.Join(levelsDir, "007-akihabara.json")) + if err != nil { + return err + } + toPlatform7, err := moves(l7, l7.SpawnPoint(), l7.Find(level.Platform)[0]) // Hibiya mot Kita-Senju: byte i Ueno + if err != nil { + return err + } // Skyltbilderna tas med några kända ord så att markeringen och den // delvisa översättningen syns; hjälpen lär sedan in nästa ord. known := []string{"-know", "改札,番線,方面", "-seed", "7"} @@ -143,6 +151,9 @@ func buildShots(levelsDir string) error { {"006-ikebukuro", toMachine6 + "; wait 5; shot $OUT/terminal.png; " + "quit", []string{"-seed", "7", "-mode", "signs", "-know", "山手線,番線,方面,新宿,池袋,埼京線"}}, {"006-ikebukuro", toTrap6 + "; waittrain; wait 5; press enter; wait 100; pick calm; press enter; wait 60; shot $OUT/ride-real.png; waitover; wait 5; shot $OUT/too-far.png; quit", []string{"-seed", "7", "-mode", "signs"}}, {"", "wait 10; press n; wait 5; shot $OUT/map-signs.png; quit", []string{"-save", "$DEMO"}}, + {"007-akihabara", toPlatform7 + "; waittrain; wait 5; press enter; wait 100; pick calm; press enter; waitstop; press enter; wait 40; shot $OUT/transfer.png; quit", + []string{"-seed", "7", "-mode", "list", "-know", "上野,番線,方面,乗り換え"}}, + {"", "wait 150; shot $OUT/ending.png; press enter; wait 5; shot $OUT/ending-summary.png; press enter; wait 250; shot $OUT/credits.png; quit", []string{"-ending", "-save", "$DEMO"}}, } return nil }