From 8d643a3842244d23a50c2988d7df478a797cf35a Mon Sep 17 00:00:00 2001 From: Daanish Date: Wed, 25 Mar 2026 07:13:37 +0000 Subject: [PATCH] initial commit --- .vscode/settings.json | 4 + assets/fonts/charybdis.ttf | Bin 0 -> 20508 bytes assets/fonts/hardpixel.otf | Bin 0 -> 18496 bytes assets/fonts/tn.ttf | Bin 0 -> 399403 bytes assets/images/floor.aseprite | Bin 0 -> 614 bytes assets/images/floor.png | Bin 0 -> 742 bytes assets/images/menu_bg.png | Bin 0 -> 2647 bytes assets/images/player.aseprite | Bin 0 -> 5411 bytes assets/images/player.png | Bin 0 -> 7276 bytes assets/images/walls.aseprite | Bin 0 -> 4250 bytes assets/images/walls.png | Bin 0 -> 4677 bytes game/character.rb | 113 ++++++++++++++++++++++++ game/enemy/ai.rb | 0 game/enemy/base.rb | 0 game/maze.rb | 106 ++++++++++++++++++++++ game/scene.rb | 123 ++++++++++++++++++++++++++ game/state.rb | 7 ++ main.rb | 92 +++++++++++++++++++ menu/scene.rb | 67 ++++++++++++++ objects/items.rb | 0 objects/traps.rb | 0 run | 7 ++ settings/configuration.rb | 6 ++ settings/scene.rb | 15 ++++ utils/bus.rb | 57 ++++++++++++ utils/maze.rb | 162 ++++++++++++++++++++++++++++++++++ utils/scene.rb | 19 ++++ utils/utils.rb | 12 +++ 28 files changed, 790 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 assets/fonts/charybdis.ttf create mode 100644 assets/fonts/hardpixel.otf create mode 100644 assets/fonts/tn.ttf create mode 100644 assets/images/floor.aseprite create mode 100644 assets/images/floor.png create mode 100644 assets/images/menu_bg.png create mode 100644 assets/images/player.aseprite create mode 100644 assets/images/player.png create mode 100644 assets/images/walls.aseprite create mode 100644 assets/images/walls.png create mode 100644 game/character.rb create mode 100644 game/enemy/ai.rb create mode 100644 game/enemy/base.rb create mode 100644 game/maze.rb create mode 100644 game/scene.rb create mode 100644 game/state.rb create mode 100644 main.rb create mode 100644 menu/scene.rb create mode 100644 objects/items.rb create mode 100644 objects/traps.rb create mode 100755 run create mode 100644 settings/configuration.rb create mode 100644 settings/scene.rb create mode 100644 utils/bus.rb create mode 100644 utils/maze.rb create mode 100644 utils/scene.rb create mode 100644 utils/utils.rb diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fcc22d7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true +} \ No newline at end of file diff --git a/assets/fonts/charybdis.ttf b/assets/fonts/charybdis.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b6325cd5295adfc56c17c8b4b895e912056814ff GIT binary patch literal 20508 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 O;XX*kwndz7Fd6_5{g;IR literal 0 HcmV?d00001 diff --git a/assets/fonts/hardpixel.otf b/assets/fonts/hardpixel.otf new file mode 100644 index 0000000000000000000000000000000000000000..0912b3e888939d886ca334ea7bcaa0e90866aeaf GIT binary patch literal 18496 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 aXBH<0kUnsF2iXU5FY`7~4g~uZ%m4tef&Tme literal 0 HcmV?d00001 diff --git a/assets/fonts/tn.ttf b/assets/fonts/tn.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9d12258c9df2a930731c56bc69124985682efd72 GIT binary patch literal 399403 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 Q1ZQAwJRTtYGN_ohCKC5Ymrs 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 JGz6#_0sy_dqeB1y literal 0 HcmV?d00001 diff --git a/assets/images/floor.aseprite b/assets/images/floor.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..206cd5ad41004d32fe50385e1479a7f06278afea GIT binary patch literal 614 zcmYdeVqkc%l#xM%L4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#(u`g!0_uM3)of`1_lN( z1_lOp1+bM!c4RRyFtC7Jr@+9#prl~ps$r96Y1kiTeZNrc=7O|$hiCo%WH$3@K-NA3 z^C@}WnfgKADn>Sz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~%^4XOSV4|pU|{&q1XBeHyox!! zA&z{940wJsDjamUEFfjjwnL$p+1;;0|HJ?3JUb5s8@zjvu+~?2dEuq2x1Hn9gmHeY zuH9|R7IO93W1D%5vE^^ye^0l{z8$=pdQFhZ3fYGGo`bV0H^li&e_3r8-)8%uZPLx!%>SR*Vz-cbY6e)^~RDZ!$XhqYIi-FbD`rl$R) z@1nfg)vLN+o1Rb7^%D=bw=AxGaQDug%-sENp7hwQYJdIMzE-ilT72T<%A)c!dl|3J rw|c#272lTZ`o&`J>*61@|L6WvRUZie$X3w+ literal 0 HcmV?d00001 diff --git a/assets/images/floor.png b/assets/images/floor.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5be205beca554dd20cd9315bcf090bb4fc7f81 GIT binary patch literal 742 zcmeAS@N?(olHy`uVBq!ia0y~yU=RUe4mJh`hTcwDUIqpR#^NA%Cx&(BWL`2bFfH(O zaSXBOt)1fNf7?Nz)!yd-=fuYf+}ceo2}?a%m>ao5A2nHgE&saw>oxDI)8x2>N_(4| zb=>pjL?~|5T*O{(UZ(WbbK14toAdAQnR$0B^Xm_#RF zny`BxufzX?nsao_S{5FD_wJxqkAsBXGZDjz%ayx@G&)+lo|b$L5MlLQtk4E zzFef?@x4s9in4F-OxG**Dcc;M`hRw)=;a)brG+vNfA@!MTgC9gbgsgOz|*3$s&lOYreP-Vz}AlCF{*15FMsa?}fep-_FB0(Z@JdW>7aA| z&uY1u8TT;ib&PWy(|bygX!H6uz#!ZtBGOUtW~-Em@xU;=cl@F1vA3~1)9l6E_=VH7 zTg|#2cPM=L$d>obQg83))k#wsXN5_hFXUv3-`1UR=VMBbc|q!F4#vb6Da9`{KHlmG zcE8y1cbe<}gN}hBlYXwb(EgWw`3F6&r7uk!jbHs?{gCPR>YbSGeg*~x22WQ%mvv4F FO#ti}Wbgn0 literal 0 HcmV?d00001 diff --git a/assets/images/menu_bg.png b/assets/images/menu_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..91a1f551a750672d4329334d303f572ba726f198 GIT binary patch literal 2647 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 DeOo&V literal 0 HcmV?d00001 diff --git a/assets/images/player.png b/assets/images/player.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3e6317cd4a2b3b913213dcabc5aaa13a6f599b GIT binary patch literal 7276 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 Y_Q0G)37z{H7#J8lUHx3vIVCg!0KjHi(f|Me literal 0 HcmV?d00001 diff --git a/assets/images/walls.aseprite b/assets/images/walls.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..3d1926e4199140a1a63377b20cf229f2681e202b GIT binary patch literal 4250 zcmbOwz`*ceDI3=9ln z3=9nH3ScXd?BHNyV31V!3l;eTrO_y1Fkcx=8h}YFFzE;;oxr3gnCxa?5atv8&%hwU zFZQ2-K~zBeKLdk|h}?e$21!Yo{|pS$(jc~+oYFT2201yU{|pQYVjxmQQvE*zgQl!D zNWHeve+C8<6U%oD49Y6%Ul5WWKr~m^A*+z@VXO0%EIyRBC}qO%;%OEme?u zO*IgyqXtr=qXJ^9Yk+hqX@b})njl?j8X!_#3#3L(10=4h1u{WH3uKqJHi*>G0Fyc( zdv*1U{xLA<>4VJC&;;qy(g2Yf8X#R-U{X&XB%-Ej0dl#TC784Z`BcRQYz@ViLB6ZY2B3df8Aoc1XU0P}&Qb`*mtD*tYtF8%BqpAfWRWv~6 zYiNMj8X6!q`UW6>85)9IWn={MmxYDhe+Gsy8xT9q666;f8^_lS3@U1lAbZsu{xdMB zYJf;RLl9|g4I*u99RD&f*x5P%XJD|mcli%uyZvWiDANf4&%jWu9t9%RV*WEQ6l%tT z*$Mv{7_9TMKxA&te+CANd=P1!pZA}E!3NB>%L9qn=0HS1q+K3}G%W;)n-ziB)&(H8 zRbf6z7nromE&!VeCi6k0WquLJJ-HyZO&*A}%P$0}&jX9+7lYWjB_KBxf@G}jeXYle5Qv1_l>bufGfoZtlMS85rE${6QivAU}F~1^#DX@bV7&&%og7 z9sQqy!QVgfKLbNTBuGtWE{N^xll-57Au=lUKLbN}ILOVRu^?YK=I8!rU~tFqvdIRECGqwF9nHMF9wC1^&(J+*)0UIZ5M-lVZR7O+AIW#+bjl! zv;86vX}J_6ZnXr&wpjvV+b;#_vR?vHX}t&(7d8t)r2WEDkZTu#Nc$xq5y$x;m9FzZ zB903|CO9ny>2g>C5_bfvU)t{UpMl}+3Q)YfTLucBcPrxlGcbHy4oZigmVrX)%W{y& zhZP`^4=X?-@0KV1XJB~00u)E@SAgQ^(=t#h{jx0aKLf*;WgwA{VA-FGKw;Q-4`jlm zd!SgDcn@UDl)E6Z?;%Kh;scP$6CQv>dhezGXJD9g8)W{Zdmz)M+y${G-%a_?!0`Wa zz<&mYUr#}P`TGP!{(oHdpMl}`v-1B84F8^h$iI&(!0bwp_+wDo`1iOLr1vQ(h4`np zgYs0-r2h;Ib+rrsGcd5No&KMJA$j)y{|pQhX0G_pz;N*3O_2Kk|NqM}FfgzvFoO$m z2=SkRfq|95C$TcM=sYI_1FHg5mr^+e7w2S?lk|0XYxNZYuf4)l9Fl+_B<@S_HoU^Wqw!pg}j$!UpX~o zm;d*7rg!)5UjF^n-c{dowz0dHCDp30legVvR{!B_`Db_gznj$O+59a1cklM{XPeF5 zJUqN@@sGIbr@cqF+5h|SSx@}gMxNJ7HBV9={LqiT$MZSycJ+5ToeGuk7XIfRpC=j@ z{X3z)Q{X}Wr`SC|E+$pKQ+~eqE4SUrPK)Yye?7le|N7E;*W%~1V@Xm^tABjBIFCVo z*~MRX@7%d3EG_+Culrj26RYZXmsIRzCO=DZ`RTVN|4rxa_`2OQ++IIk8-86(#^~JK z{|Yhk+1cvP)bCf83ySO2?C3~7e(msaZo5wgesitvZ?4y#b3?vB;lTZqsYQ?KOP~K= z>7q2zL&Ys&>D*g4&z0_6At^C;j@tBV^6ifDz1Q;BuvhMR;=Eh?Q2DbTKSg<)?hD$< ztiQK^<=2Jh?}ymExqsr<)#JOr-kf`XUEJP(zfU}#8zg`4tNGG8_xbz(KABy#_r&jB z#&sFBGDrR%D_8sf@b5Ga6`@WS7WRAp4WCw8&N`pP-&-^9eR#F*tvb)YhJULnZ+<B{r_=UhMdTw{Jchu!pp$J3{OT{rK% z$={mK_g+8VseW74bYD>9$>Yzryt7Y~4${9|vnf`&Hp)iaXai4f&|aVE&D%NN3f$B? zxKCk6&)ev`b%~LYef!iS{i@eSg!%2=^yKiA+?364 z|8JT8^55V0ca1JhZ~dfd_wv3L>wmpv7I8c01Rwc$-T#bWOx&ZShW>k6XFi+n4ZKzV zl=Ymf<&y2EK1D|+PGwe~c9^-fvSPZxjO%imlCRax|2bLu2o_nsa-MeacIBLD`rM6w z7jNhKpI?5;Hul$=c}6?toeuPyma4Niq}z}wPU}GIz9_K6`wt_2^1$s8XdQpZoSrU?!w!=DcXD2Yro`iJOBH} zq;nglZT*ycf7k9!d%x~Jrfzz4s`mVK8T|g~6*U(7Z5P-E{x) zdMBUwR=)oKsndqr|A^^UoxUs)Xx}kCS?+6mrRB50|5KM6F8aw9H{bnA{nM|3E`P)0 z`YNx~KYcpG{rvpt$#PFW7oA!BQywn(-aKb{@tKu^O8>OIlMR~c-2Jm%9%lAx*vu7d zU2eMQ!xuM^mF7a7N4Ob(`b@EMu9}~6W!`k%UyIwznH1`P5b=(CI6?d^X&cWb@nEoFEcs5c}3iW zU(H`HR_~ej@58$3VRo;j!zX@z{<2?V{&M?0ua~;&NBnC3x^Vw=!K?BYf1kZ9|0V2H z@r_OO^Zsev&7b-{Vd+1QzY{%FgcMJhnanf4R&Dk7%$IAg%UErrcg3!nKXV>ig0 z-OJTo&*}{#_6L8qm&jP%f9k)(n?KccK64FJ|2G>(Kg-FGz4ZEY(wTYBb9zoOTPj(& zAIbS~OvrjCx=y~`0**~L7POgnE5C7!q^*j8M>h*QGGk+@S)=bk&oY!$qTvIl`Tkqme zCDnamu}pd^J-faNUQk{7`T8pF4e?i0xBhx@RdsKTX>8Eyc>n1O_Ai;PzsNi5>+RJb zVwLxW`lcee_>k!pHJkZjo$PN0=||M;mRb}({mKufz(?J$_`CC0{Zm->N`O1K#DjCC zy-??o?k3yE^$|Z`?utM1(D3$;UAq2zmH+Yy>aCpm#pvkPMfN3?Z}%=Zb})ZSef7^2 z>HELJrX5fJnYqLNOH+1#Xy$*fI)M+ByOxygnbxK<|69)1Irguu80BrECM{oP?^P%N z;bk5FKfRBR!YB6`RqmO_r}?sf(toa+d3i5(#_8Ctm0tAR|68@kIgR=6zOR5teaQIw zqJPr=u6Y_OZ3RyMYgL+GzCST~`TxVVap&t*e>sGibQ~^UT(&>*ue|Vab)WlTzqSc= zx-_k*dp}=JZKXHYuf?7I+eB}?fAFF|Z(rEoz{i`vxaa=g^Cy0;@}))gS&!?#yy}m# z%USr&{u;Az2du@L$o>qTvwSCsl=X-xGF8f#Y)xCFp zc>R=5=MP?oi|&v7wfNZcr}LZF2iJ$Z-+o0N6r3(eDk<@8-$Fg*``>;4aOyv0 zdzJNHa_no*KAWlkbHfjx)Zh1iJrlj3x%qD3BW3aa`l^gyp|>9b>Kki>Fy%oc-T$>zs4{C!BVFQr{!--+7v>=|A@5D?jAJW(xk4 zUwrKU1RLdJ{{`yS1y;&`lHRMF>~gNXXXQ^ht7T8BrtG_HttHgyk`y0sNj~g$P0POx z7QyvPK$-8C@>lm){Rbz4Xr+l3Ef?{_kZB7 ze>(p~O?%A$xxf6k;ZOLVYwN!My?g2J>!0hr?%&^fPtx?q`bBoD;{FCYum87VVR-$O z3#;w3UR;$oUGmREMTnityMD?%_aCvR{(JnL`0H`%Rr&4jYo`8sT))J=>a%^;uf^v7 f7ca5*`YB&ier2(J*7xXF{nLZ%|1d{RyTuCtKD1;K literal 0 HcmV?d00001 diff --git a/assets/images/walls.png b/assets/images/walls.png new file mode 100644 index 0000000000000000000000000000000000000000..d0193ffda930d4df16b07f8f9c2432a43bfacaac GIT binary patch literal 4677 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 literal 0 HcmV?d00001 diff --git a/game/character.rb b/game/character.rb new file mode 100644 index 0000000..e6a576e --- /dev/null +++ b/game/character.rb @@ -0,0 +1,113 @@ +class Character + SPEED = 2.5 + SIZE = [25, 30] + + def initialize + @world_x = 60.0 + @world_y = 60.0 + + # Load the full sheet as tiles + sheet = Gosu::Image.load_tiles("assets/images/player.png", 20, 40, retro: true) + + rows = [ + :idle_front, + :idle_back, + :idle_right, + :idle_left, + :walk_front, + :walk_back, + :walk_right, + :walk_left + ] + + @animations = {} + + rows.each_with_index do |name, row| + start_index = row * 6 + @animations[name] = sheet[start_index, 6] # slice 6 frames + end + + @current_animation = :idle_front + @facing = :front + end + + def rect + w, h = SIZE + [@world_x - w / 2, @world_y - h / 2, w, h] + end + + def collides?(rect) + return true if intersects?(self.rect, rect) + false + end + + def update + dx = 0.0 + dy = 0.0 + + dx -= 1 if Gosu.button_down?(Gosu::KB_A) + dx += 1 if Gosu.button_down?(Gosu::KB_D) + dy -= 1 if Gosu.button_down?(Gosu::KB_W) + dy += 1 if Gosu.button_down?(Gosu::KB_S) + + moving = dx != 0 || dy != 0 + + if moving + if dy < 0 + @facing = :back + elsif dy > 0 + @facing = :front + elsif dx < 0 + @facing = :left + elsif dx > 0 + @facing = :right + end + end + + @current_animation = moving ? :"walk_#{@facing}" : :"idle_#{@facing}" + + # Normalize vector if moving diagonally + if dx != 0 && dy != 0 + length = Math.sqrt(dx**2 + dy**2) + dx /= length + dy /= length + end + + # Apply speed + dx *= SPEED + dy *= SPEED + + # Horizontal + @world_x += dx + if $bus.get(:collides?, rect)&.include?(:wall) + @world_x -= dx + end + + # Vertical + @world_y += dy + if $bus.get(:collides?, rect)&.include?(:wall) + @world_y -= dy + end + + # Camera follow + cam_x = @world_x - SCREEN_SIZE[0] / 2.0 + cam_y = @world_y - SCREEN_SIZE[1] / 2.0 + $bus.emit(:player_move, [cam_x, cam_y]) + end + + def draw + cx = SCREEN_SIZE[0] / 2.0 - SIZE[0] / 2.0 + cy = SCREEN_SIZE[1] / 2.0 - SIZE[1] / 2.0 + + sprite_x = cx + SIZE[0] / 2.0 - 20 * 1.8 / 2 + sprite_y = cy + SIZE[1] / 2.0 - 50 * 1.8 / 2 + + elapsed = Gosu.milliseconds / 1000.0 + frame_index = ((elapsed * 7).floor) % 6 + @animations[@current_animation][frame_index].draw(sprite_x, sprite_y, sprite_y, 1.8, 1.8) + + return unless DEBUG + + Gosu.draw_rect(cx, cy, SIZE[0], SIZE[1], Gosu::Color.new(0x8000FF00), cy) + end +end \ No newline at end of file diff --git a/game/enemy/ai.rb b/game/enemy/ai.rb new file mode 100644 index 0000000..e69de29 diff --git a/game/enemy/base.rb b/game/enemy/base.rb new file mode 100644 index 0000000..e69de29 diff --git a/game/maze.rb b/game/maze.rb new file mode 100644 index 0000000..28d2e08 --- /dev/null +++ b/game/maze.rb @@ -0,0 +1,106 @@ +require_relative "../utils/maze" + +class Maze + def initialize + @maze = MazeData.new(80, 80) + @spritesheet = Gosu::Image.load_tiles("assets/images/walls.png", 20, 40, retro: true) + puts @spritesheet.size + end + + def collides?(rect) + x1 = ((rect[0] - 10) / 60).floor + y1 = ((rect[1] - 10) / 60).floor + x2 = ((rect[0] + rect[2] + 10) / 60).floor + y2 = ((rect[1] + rect[3] + 10) / 60).floor + + (x1..x2).each do |x| + (y1..y2).each do |y| + shapes = shapes_for(@maze.wall_type(x, y), x * 60, y * 60) + shapes.each do |shape| + return true if intersects?(shape, rect) + end + end + end + + false + end + + def shapes_for(mask, tx, ty) + return [] if mask == 0 + + half = 30 + band = 10 + + shapes = [] + + case mask & (W | E) + when W | E + shapes << [tx, ty + 30 - band / 2, 60, band] + when E + if (mask & (N | S)) == 0 + shapes << [tx, ty + 30 - band / 2, 60, band] + else + shapes << [tx + half, ty + 30 - band / 2, half, band] + end + when W + if (mask & (N | S)) == 0 + shapes << [tx, ty + 30 - band / 2, 60, band] + else + shapes << [tx, ty + 30 - band / 2, half, band] + end + end + + band = 20 + + case mask & (N | S) + when (N | S) + shapes << [tx + 30 - band / 2, ty, band, 60] + when S + shapes << [tx + 30 - band / 2, ty + half, band, half] + when N + shapes << [tx + 30 - band / 2, ty, band, half] + end + + shapes + end + + def draw(camera) + cam_x, cam_y = camera + tile_size = 60 + scale = 3 + screen_width, screen_height = SCREEN_SIZE + + # How many tiles fit on screen (+1 for partially visible tiles) + tiles_x = (screen_width / tile_size) + 2 + tiles_y = (screen_height / tile_size) + 2 + + # Which tile to start drawing (top-left corner of camera) + start_x = (cam_x / tile_size).floor - 1 + start_y = (cam_y / tile_size).floor - 1 + + (0...tiles_y).each do |j| + (0...tiles_x).each do |i| + gx = start_x + i + gy = start_y + j + + next if gx < 0 || gy < 0 || gx >= @maze.width || gy >= @maze.height + + index = @maze.wall_type(gx, gy) + next if index == 0 + + # Pixel position on screen + screen_x = gx * tile_size - cam_x + screen_y = gy * tile_size - cam_y + + @spritesheet[index - 1].draw(screen_x, screen_y, screen_y, scale, scale) + + next unless DEBUG + + shapes = shapes_for(index, gx * tile_size, gy * tile_size) + shapes.each do |shape| + Gosu.draw_rect(shape[0] - cam_x, shape[1] - cam_y, shape[2], shape[3], 0x44ff0000, shape[1] - cam_y) + end + end + end + end +end \ No newline at end of file diff --git a/game/scene.rb b/game/scene.rb new file mode 100644 index 0000000..303199d --- /dev/null +++ b/game/scene.rb @@ -0,0 +1,123 @@ +require_relative 'state' +require_relative 'maze' +require_relative 'character' + +require 'perlin' + +class Game < Scene + def initialize + super + @font = Gosu::Font.new(24) + @noise = Perlin::Generator.new(rand(1000), 1.0, 1) + @floor_image = Gosu::Image.new("assets/images/floor.png", retro: true) + + @maze = Maze.new + @character = Character.new + + @camera = [0, 0] + + $bus.on(:player_move) do |pos| + @camera = pos + end + + $bus.on_retrievable(:collides?) do |rect| + collisions = [] + collisions << :wall if @maze.collides?(rect) + collisions << :character if @character.collides?(rect) + next collisions + end + end + + def draw + draw_floor + @maze.draw(@camera) + @character.draw + draw_fog(@camera) + draw_debug! if DEBUG + end + + def draw_debug! + now = Gosu.milliseconds + @last_draw_time ||= now + @draw_count ||= 0 + @fps ||= 0 + + @draw_count += 1 + + if now - @last_draw_time >= 1000 + @fps = @draw_count + @draw_count = 0 + @last_draw_time = now + end + + @font.draw_text("FPS: #{@fps}", 5, 5, Float::INFINITY, 1, 1, Gosu::Color::YELLOW) + end + + def draw_fog(camera) + cam_x, cam_y = camera + cell = 10 + i_radius = 80.0 + o_radius = 360.0 + + # Player screen position + px = SCREEN_SIZE[0] / 2.0 + py = SCREEN_SIZE[1] / 2.0 + + tiles_x = (SCREEN_SIZE[0] / cell) + 2 + tiles_y = (SCREEN_SIZE[1] / cell) + 2 + + tiles_y.times do |j| + tiles_x.times do |i| + sx = i * cell + sy = j * cell + + + dist = Math.sqrt((sx - px)**2 + (sy - py)**2) + + if dist < i_radius + next # fully clear + elsif dist > o_radius + alpha = 255 + else + t = (dist - i_radius) / (o_radius - i_radius) # 0..1 + + world_x = (sx + cam_x) * 0.005 + world_y = (sy + cam_y) * 0.005 + + # Sample noise at world position so it scrolls with camera + noise = (@noise[world_x + Gosu.milliseconds / 5000.0, world_y + Gosu.milliseconds / 6000.0] + 1.0) / 2.0 + alpha = (t * (1.0 + noise) * 255).to_i.clamp(0, 255) + end + + Gosu.draw_rect(sx, sy, cell, cell, Gosu::Color.new(alpha, 0, 0, 0), 10000 + sy) + end + end + end + + def draw_floor + cam_x, cam_y = @camera + tile_size = 60 + + offset_x = cam_x % tile_size + offset_y = cam_y % tile_size + + tiles_x = (SCREEN_SIZE[0] / tile_size) + 2 + tiles_y = (SCREEN_SIZE[1] / tile_size) + 2 + + (0...tiles_y).each do |j| + (0...tiles_x).each do |i| + sx = i * tile_size - offset_x + sy = j * tile_size - offset_y + @floor_image.draw(sx, sy, -1000, 3, 3) + end + end + end + + def update + @character.update + end + + def button_down(id, _pos) + return $bus.emit(:change_scene, Menu.new) if id == Gosu::KB_ESCAPE + end +end \ No newline at end of file diff --git a/game/state.rb b/game/state.rb new file mode 100644 index 0000000..9c1e414 --- /dev/null +++ b/game/state.rb @@ -0,0 +1,7 @@ +class State + # This class holds state information + # for example the player inventory / items + # also their health, stealth, and memory bars + # it can handle mouse input for inventory management and item usage + # it can also handle keyboard input for quick item usage +end \ No newline at end of file diff --git a/main.rb b/main.rb new file mode 100644 index 0000000..bce9d98 --- /dev/null +++ b/main.rb @@ -0,0 +1,92 @@ +require 'gosu' + +require_relative 'settings/configuration' +require_relative 'utils/bus' +require_relative 'utils/utils' +require_relative 'utils/scene' +require_relative 'settings/scene' +require_relative 'menu/scene' +require_relative 'game/scene' + +$bus = EventBus.new + +# Main window +class Window < Gosu::Window + def initialize + super *SCREEN_SIZE, resizable: true + self.caption = "Mist" + + @scene = Menu.new + + $bus.on(:quit_game) { close! } + + $bus.on_retrievable(:mouse_pos) do + next mouse_relative(mouse_x, mouse_y) + end + + $bus.on(:change_scene) do |new_scene| + @scene.close + @scene = new_scene + end + end + + def button_down(id) + @scene.button_down(id, mouse_relative(mouse_x, mouse_y)) + end + + def update + @scene.update + end + + def compute_transform + scale_x = width.to_f / SCREEN_SIZE[0] + scale_y = height.to_f / SCREEN_SIZE[1] + + scale = [scale_x, scale_y].min + + if scale_x < scale_y + offset_x = 0 + offset_y = (height - SCREEN_SIZE[1] * scale) / 2 + else + offset_x = (width - SCREEN_SIZE[0] * scale) / 2 + offset_y = 0 + end + + [scale, offset_x, offset_y] + end + + def mouse_relative(sx, sy) + scale, offset_x, offset_y = compute_transform + + virtual_x = (sx - offset_x) / scale + virtual_y = (sy - offset_y) / scale + + return nil if virtual_x < 0 || virtual_x > SCREEN_SIZE[0] || + virtual_y < 0 || virtual_y > SCREEN_SIZE[1] + + return [virtual_x, virtual_y] + end + + def draw + scale, offset_x, offset_y = compute_transform + + Gosu.translate(offset_x, offset_y) do + Gosu.scale(scale) do + @scene.draw + end + end + + padding_color = Gosu::Color::BLACK + if offset_y > 0 + # Letterbox: top and bottom + Gosu.draw_rect(0, 0, width, offset_y, padding_color, Float::INFINITY) + Gosu.draw_rect(0, height - offset_y, width, offset_y, padding_color, Float::INFINITY) + else + # Pillarbox: left and right + Gosu.draw_rect(0, 0, offset_x, height, padding_color, Float::INFINITY) + Gosu.draw_rect(width - offset_x, 0, offset_x, height, padding_color, Float::INFINITY) + end + end +end + +Window.new.show \ No newline at end of file diff --git a/menu/scene.rb b/menu/scene.rb new file mode 100644 index 0000000..7b9164c --- /dev/null +++ b/menu/scene.rb @@ -0,0 +1,67 @@ +class Menu < Scene + OPTIONS = { + "Start" => -> { $bus.emit(:change_scene, Game.new) }, + "Settings" => -> { $bus.emit(:change_scene, Settings.new) }, + "Quit" => -> { $bus.emit(:quit_game) } + }.freeze + + def initialize + super + @font = Gosu::Font.new(32, name: "assets/fonts/tn.ttf") + @selected_index = 0 + @bg_image = Gosu::Image.new("assets/images/menu_bg.png", retro: true) + @last_mouse_pos = nil + end + + def update + pos = $bus.get(:mouse_pos) + return unless pos + + if @last_mouse_pos.nil? || ( (pos[0] - @last_mouse_pos[0]).abs > 2 || (pos[1] - @last_mouse_pos[1]).abs > 2 ) + hovered_option = item_at(pos) + @selected_index = hovered_option[0] if hovered_option + @last_mouse_pos = pos.dup + end + end + + def draw + @bg_image.draw(0, 0, 1) + + OPTIONS.each_with_index do |(text, _), index| + y = 260 + index * 50 + if index == @selected_index + @font.draw_text("[ #{text.ljust(10)} ]", 50, y, 1, 1, 1, Gosu::Color::YELLOW) + else + @font.draw_text(" #{text.ljust(10)} ", 50, y, 1, 1, 1, Gosu::Color::WHITE) + end + end + end + + def button_down(id, pos) + case id + when Gosu::KB_UP + @selected_index = (@selected_index - 1) % OPTIONS.size + when Gosu::KB_DOWN + @selected_index = (@selected_index + 1) % OPTIONS.size + when Gosu::KB_RETURN + OPTIONS.values[@selected_index].call + end + + return unless id == Gosu::MS_LEFT && pos + + selected_option = item_at(pos) + selected_option[1].call if selected_option + end + + private + + def item_at(pos) + OPTIONS.each_with_index do |(_, action), index| + y = 260 + index * 50 + if pos[1].between?(y, y + 32) && pos[0].between?(50, 50 + @font.text_width(" " * 14)) + return [index, action] + end + end + nil + end +end \ No newline at end of file diff --git a/objects/items.rb b/objects/items.rb new file mode 100644 index 0000000..e69de29 diff --git a/objects/traps.rb b/objects/traps.rb new file mode 100644 index 0000000..e69de29 diff --git a/run b/run new file mode 100755 index 0000000..6b031bb --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +# Dependencies +# Gosu for rendering and input +# Perlin noise for fog effect + +load 'main.rb' \ No newline at end of file diff --git a/settings/configuration.rb b/settings/configuration.rb new file mode 100644 index 0000000..116017c --- /dev/null +++ b/settings/configuration.rb @@ -0,0 +1,6 @@ +# Global constants + +SCREEN_SIZE = [720, 480].freeze +N, S, W, E = 1, 2, 4, 8 + +DEBUG = ARGV.include?("--debug") diff --git a/settings/scene.rb b/settings/scene.rb new file mode 100644 index 0000000..bca37e1 --- /dev/null +++ b/settings/scene.rb @@ -0,0 +1,15 @@ +class Settings < Scene + def initialize + super + @font = Gosu::Font.new(24) + end + + def draw + Gosu.draw_rect(0, 0, SCREEN_SIZE[0], SCREEN_SIZE[1], Gosu::Color::BLACK) + @font.draw_text("Settings Scene - Press ESC to return to Menu", 50, 50, 1, 1, 1, Gosu::Color::WHITE) + end + + def button_down(id, _pos) + $bus.emit(:change_scene, Menu.new) if id == Gosu::KB_ESCAPE + end +end \ No newline at end of file diff --git a/utils/bus.rb b/utils/bus.rb new file mode 100644 index 0000000..a485352 --- /dev/null +++ b/utils/bus.rb @@ -0,0 +1,57 @@ +class EventBus + def initialize + # store callbacks with owner: {event => [ {callback:, owner:} ] } + @events = Hash.new { |h, k| h[k] = [] } + @retrievable_events = {} + end + + # Emit an event + def emit(event, *args) + @events[event].each do |h| + h[:callback].call(*args) + end + end + + # Subscribe to an event + def on(event, owner: nil, &callback) + owner ||= callback.binding.eval("self") + @events[event] << { callback: callback, owner: owner } + end + + # Subscribe to an event that should only be called once + def once(event, owner = nil, &callback) + wrapper = nil + wrapper = proc do |*args| + callback.call(*args) + off(event, &wrapper) + end + on(event, owner: owner, &wrapper) + end + + def off(event, &callback) + @events[event].reject! { |h| h[:callback] == callback } + end + + # Register a retrievable event (only one allowed) + def on_retrievable(event, owner = nil, &callback) + owner ||= callback.binding.eval("self") + @retrievable_events[event] = { callback: callback, owner: owner } + end + + # Get data from a retrievable event + def get(event, *args) + if @retrievable_events[event] + @retrievable_events[event][:callback].call(*args) + else + nil + end + end + + # Unsubscribe from an object's events + def unlisten_owner(owner) + @events.each do |event, handlers| + handlers.reject! { |h| h[:owner] == owner } + end + @retrievable_events.delete_if { |_, h| h[:owner] == owner } + end +end \ No newline at end of file diff --git a/utils/maze.rb b/utils/maze.rb new file mode 100644 index 0000000..179e840 --- /dev/null +++ b/utils/maze.rb @@ -0,0 +1,162 @@ +class MazeData + def initialize(width, height) + @width = width + @height = height + + @grid = Array.new(height) { Array.new(width, 0) } + + @boss_rooms = [] + + crate_rooms! + carve_passages_from(0, 0) + fix_room_entrances! + end + + def width + @width * 2 + 1 + end + + def height + @height * 2 + 1 + end + + def wall_type(gx, gy) + mask = 0 + + return mask unless wall_at?(gx, gy) + + mask |= N if gy > 0 && wall_at?(gx, gy - 1) + mask |= S if gy < height - 1 && wall_at?(gx, gy + 1) + mask |= E if gx < width - 1 && wall_at?(gx + 1, gy ) + mask |= W if gx > 0 && wall_at?(gx - 1, gy ) + + return mask + end + + def wall_at?(gx, gy) + return true if gx == 0 || gy == 0 || gx == width - 1 || gy == height - 1 + + @boss_rooms.each do |rx, ry| + display_x1 = rx * 2 + 1 + display_y1 = ry * 2 + 1 + display_x2 = display_x1 + (BOSS_ROOM_SIZE - 1) * 2 + display_y2 = display_y1 + (BOSS_ROOM_SIZE - 1) * 2 + return false if gx.between?(display_x1, display_x2) && gy.between?(display_y1, display_y2) + end + + return false if gx.odd? && gy.odd? + + if gx.odd? && gy.even? + cx = (gx - 1) / 2 + cy = (gy - 1) / 2 + return (@grid[cy][cx] & S) == 0 + end + + if gx.even? && gy.odd? + cx = (gx - 1) / 2 + cy = (gy - 1) / 2 + return (@grid[cy][cx] & E) == 0 + end + + true + end + + private + + BOSS_ROOM_SIZE = 5 + BOSS_ROOM = 16 + N, S, W, E = 1, 2, 4, 8 + DX = { E => 1, W => -1, N => 0, S => 0 } + DY = { E => 0, W => 0, N => -1, S => 1 } + OPPOSITE = { E => W, W => E, N => S, S => N } + + def room_free?(x, y) + (y - 2...(y + BOSS_ROOM_SIZE + 2)).each do |j| + (x - 2...(x + BOSS_ROOM_SIZE + 2)).each do |i| + return false if @grid[j][i] & BOSS_ROOM != 0 + end + end + true + end + + def fix_room_entrances! + @boss_rooms.each do |x, y| + @grid[y][x + 2] |= N + @grid[y - 1][x + 2] |= S + end + end + + def crate_rooms! + base = Math.sqrt(@width * @height / 200.0).round + num_rooms = rand((base - 1)..(base)) + num_rooms = 1 if num_rooms < 1 + attempts = 0 + + while @boss_rooms.size < num_rooms && attempts < 10 + attempts += 1 + + x = rand(2..(@width - BOSS_ROOM_SIZE - 2)) + y = rand(2..(@height - BOSS_ROOM_SIZE - 2)) + + next unless room_free?(x, y) + + (y...(y + BOSS_ROOM_SIZE)).each do |j| + (x...(x + BOSS_ROOM_SIZE)).each do |i| + cell = N | S | E | W + cell |= BOSS_ROOM + + cell &= ~N if j == y + cell &= ~S if j == y + BOSS_ROOM_SIZE - 1 + cell &= ~W if i == x + cell &= ~E if i == x + BOSS_ROOM_SIZE - 1 + + @grid[j][i] = cell + end + end + + @boss_rooms << [x, y] + end + end + + def carve_passages_from(cx, cy) + directions = [N, S, E, W].shuffle + + directions.each do |direction| + nx, ny = cx + DX[direction], cy + DY[direction] + + if ny.between?(0, @height - 1) && nx.between?(0, @width - 1) && @grid[ny][nx] == 0 + @grid[cy][cx] |= direction + @grid[ny][nx] |= OPPOSITE[direction] + carve_passages_from(nx, ny) + end + end + end +end + +if __FILE__ == $0 + WALL_CHARS = { + 0 => " ", # NONE + 1 => "╵", # N + 2 => "╷", # S + 3 => "│", # NS + 4 => "╴", # W + 5 => "┘", # NW + 6 => "┐", # WS + 7 => "┤", # WNS + 8 => "╶", # E + 9 => "└", # NE + 10 => "┌", # ES + 11 => "├", # ENS + 12 => "─", # EW + 13 => "┴", # EWN + 14 => "┬", # EWS + 15 => "┼" # EWNS + } + maze = MazeData.new(80, 80) + (0...maze.height).each do |y| + (0...maze.width).each do |x| + print WALL_CHARS[maze.wall_type(x, y)] + end + puts + end +end \ No newline at end of file diff --git a/utils/scene.rb b/utils/scene.rb new file mode 100644 index 0000000..a730797 --- /dev/null +++ b/utils/scene.rb @@ -0,0 +1,19 @@ +class Scene + # Base class for all scenes. + + def initialize + end + + def update + end + + def draw + end + + def button_down(id, pos) + end + + def close + $bus.unlisten_owner(self) + end +end \ No newline at end of file diff --git a/utils/utils.rb b/utils/utils.rb new file mode 100644 index 0000000..d8118eb --- /dev/null +++ b/utils/utils.rb @@ -0,0 +1,12 @@ +def intersects?(rect1, rect2) + x1, y1, w1, h1 = rect1 + x2, y2, w2, h2 = rect2 + + # No overlap conditions + return false if x1 + w1 <= x2 # rect1 is left of rect2 + return false if x1 >= x2 + w2 # rect1 is right of rect2 + return false if y1 + h1 <= y2 # rect1 is above rect2 + return false if y1 >= y2 + h2 # rect1 is below rect2 + + true # overlap exists +end \ No newline at end of file