From a4d803fc57abf972b3e961a6678fb734a86ac2df Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 1 Feb 2026 03:48:44 +0200 Subject: [PATCH] add background --- libs/tiled/src/layer.zig | 2 +- libs/tiled/src/tilemap.zig | 15 +- src/assets.zig | 166 ++++++++++------ src/assets/dungeon.tsx | 4 + src/assets/fire-spirit.png | Bin 0 -> 320 bytes src/assets/game.tiled-project | 14 ++ src/assets/map.tmx | 23 +++ src/assets/snake.png | Bin 0 -> 406 bytes src/assets/tiny-dungeon/License.txt | 22 +++ src/assets/tiny-dungeon/Tilesheet.txt | 9 + src/assets/tiny-dungeon/tilemap_packed.png | Bin 0 -> 5294 bytes src/combat_screen.zig | 217 ++++++++++----------- src/engine/graphics.zig | 45 ++++- src/engine/math.zig | 5 + src/game.zig | 7 - 15 files changed, 345 insertions(+), 184 deletions(-) create mode 100644 src/assets/dungeon.tsx create mode 100644 src/assets/fire-spirit.png create mode 100644 src/assets/game.tiled-project create mode 100644 src/assets/map.tmx create mode 100644 src/assets/snake.png create mode 100644 src/assets/tiny-dungeon/License.txt create mode 100644 src/assets/tiny-dungeon/Tilesheet.txt create mode 100644 src/assets/tiny-dungeon/tilemap_packed.png diff --git a/libs/tiled/src/layer.zig b/libs/tiled/src/layer.zig index 184380a..22c3b66 100644 --- a/libs/tiled/src/layer.zig +++ b/libs/tiled/src/layer.zig @@ -136,6 +136,7 @@ pub const TileVariant = struct { const encoded_tiles = try lexer.nextExpectText(); const tiles = try parseEncoding(encoding, scratch.allocator(), encoded_tiles); try temp_tiles.appendSlice(scratch.allocator(), tiles.items); + continue; } else if (node.isTag("chunk")) { const chunk = try initChunkDataFromXml(arena, scratch, lexer, encoding); @@ -158,7 +159,6 @@ pub const TileVariant = struct { .fixed = try arena.dupe(u32, temp_tiles.items) }; } - } fn parseEncoding( diff --git a/libs/tiled/src/tilemap.zig b/libs/tiled/src/tilemap.zig index 3bf4e47..87e0e3a 100644 --- a/libs/tiled/src/tilemap.zig +++ b/libs/tiled/src/tilemap.zig @@ -73,6 +73,9 @@ pub const TilesetReference = struct { pub const Tile = struct { tileset: *const Tileset, id: u32, + flip_horizontal: bool, + flip_vertical: bool, + flip_diagonal: bool, pub fn getProperties(self: Tile) Property.List { return self.tileset.getTileProperties(self.id) orelse .empty; @@ -216,13 +219,19 @@ fn getTilesetByGid(self: *const Tilemap, gid: u32) ?TilesetReference { } pub fn getTile(self: *const Tilemap, tilesets: Tileset.List, gid: u32) ?Tile { - const tileset_ref = self.getTilesetByGid(gid & GlobalTileId.Flag.clear) orelse return null; + const Flag = GlobalTileId.Flag; + + const gid_without_flags = gid & Flag.clear; + const tileset_ref = self.getTilesetByGid(gid_without_flags) orelse return null; const tileset = tilesets.get(tileset_ref.source) orelse return null; - const id = gid - tileset_ref.first_gid; + const id = gid_without_flags - tileset_ref.first_gid; return Tile{ .tileset = tileset, - .id = id + .id = id, + .flip_horizontal = (gid & @intFromEnum(Flag.flipped_horizontally)) > 0, + .flip_vertical = (gid & @intFromEnum(Flag.flipped_vertically)) > 0, + .flip_diagonal = (gid & @intFromEnum(Flag.flipped_diagonally)) > 0 }; } diff --git a/src/assets.zig b/src/assets.zig index 119f1f0..00934e2 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -24,7 +24,6 @@ pub const Tilemap = struct { texture: Gfx.TextureId, tile_size: Engine.Vec2, - pub fn getTileUV(self: Tilemap, tile_x: f32, tile_y: f32) Rect { const texture_info = Engine.Graphics.getTextureInfo(self.texture); const tilemap_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height); @@ -39,14 +38,17 @@ pub const Tilemap = struct { arena: std.heap.ArenaAllocator, font_id: FontName.EnumArray, -wood01: Audio.Data.Id, +pistol_mask: Gfx.TextureId, +bomb_mask: Gfx.TextureId, +laser_mask: Gfx.TextureId, +dungeon_tilemap: Tilemap, + +skeleton: Gfx.Sprite, +snake: Gfx.Sprite, +fire_spirit: Gfx.Sprite, + map: tiled.Tilemap, tilesets: tiled.Tileset.List, -move_sound: []Audio.Data.Id, - -terrain_tilemap: Tilemap, -players_tilemap: Tilemap, -weapons_tilemap: Tilemap, pub fn init(gpa: std.mem.Allocator) !Assets { var arena = std.heap.ArenaAllocator.init(gpa); @@ -58,11 +60,83 @@ pub fn init(gpa: std.mem.Allocator) !Assets { .italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")), }); - const wood01 = try Audio.load(.{ - .format = .vorbis, - .data = @embedFile("assets/wood01.ogg"), + const pistol_mask_image = try STBImage.load(@embedFile("assets/pistol-mask.png")); + defer pistol_mask_image.deinit(); + const pistol_mask_texture = try Gfx.addTexture(&.{ + .{ + .width = pistol_mask_image.width, + .height = pistol_mask_image.height, + .rgba = pistol_mask_image.rgba8_pixels + } }); + const bomb_mask_image = try STBImage.load(@embedFile("assets/bomb-mask.png")); + defer bomb_mask_image.deinit(); + const bomb_mask_texture = try Gfx.addTexture(&.{ + .{ + .width = bomb_mask_image.width, + .height = bomb_mask_image.height, + .rgba = bomb_mask_image.rgba8_pixels + } + }); + + const laser_mask_image = try STBImage.load(@embedFile("assets/laser-mask.png")); + defer laser_mask_image.deinit(); + const laser_mask_texture = try Gfx.addTexture(&.{ + .{ + .width = laser_mask_image.width, + .height = laser_mask_image.height, + .rgba = laser_mask_image.rgba8_pixels + } + }); + + const creatures_image = try STBImage.load(@embedFile("assets/tiny-creatures/tilemap_packed.png")); + defer creatures_image.deinit(); + const creatures_texture = try Gfx.addTexture(&.{ + .{ + .width = creatures_image.width, + .height = creatures_image.height, + .rgba = creatures_image.rgba8_pixels + } + }); + + const snake_image = try STBImage.load(@embedFile("assets/snake.png")); + defer snake_image.deinit(); + const snake_texture = try Gfx.addTexture(&.{ + .{ + .width = snake_image.width, + .height = snake_image.height, + .rgba = snake_image.rgba8_pixels + } + }); + + const fire_spirit_image = try STBImage.load(@embedFile("assets/fire-spirit.png")); + defer fire_spirit_image.deinit(); + const fire_spirit_texture = try Gfx.addTexture(&.{ + .{ + .width = fire_spirit_image.width, + .height = fire_spirit_image.height, + .rgba = fire_spirit_image.rgba8_pixels + } + }); + + const creatures_tilemap = Tilemap{ + .texture = creatures_texture, + .tile_size = .init(16, 16) + }; + const snake = Gfx.Sprite{ + .texture = snake_texture, + .uv = .unit + }; + const skeleton = Gfx.Sprite{ + .texture = creatures_texture, + .uv = creatures_tilemap.getTileUV(1, 0) + }; + const fire_spirit = Gfx.Sprite{ + .texture = fire_spirit_texture, + .uv = .unit + }; + var scratch = std.heap.ArenaAllocator.init(gpa); defer scratch.deinit(); @@ -80,76 +154,42 @@ pub fn init(gpa: std.mem.Allocator) !Assets { gpa, &scratch, &xml_buffers, - @embedFile("assets/tileset.tsx") + @embedFile("assets/dungeon.tsx") ); var tilesets: tiled.Tileset.List = .empty; - try tilesets.add(gpa, "tilemap.tsx", tileset); + try tilesets.add(gpa, "dungeon.tsx", tileset); - const players_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Players/tilemap_packed.png")); - defer players_image.deinit(); - const players_texture = try Gfx.addTexture(&.{ + const dungeon_image = try STBImage.load(@embedFile("assets/tiny-dungeon/tilemap_packed.png")); + defer dungeon_image.deinit(); + const dungeon_texture = try Gfx.addTexture(&.{ .{ - .width = players_image.width, - .height = players_image.height, - .rgba = players_image.rgba8_pixels + .width = dungeon_image.width, + .height = dungeon_image.height, + .rgba = dungeon_image.rgba8_pixels } }); - const tileset_image = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Tiles/tilemap_packed.png")); - defer tileset_image.deinit(); - const tileset_texture = try Gfx.addTexture(&.{ - .{ - .width = tileset_image.width, - .height = tileset_image.height, - .rgba = tileset_image.rgba8_pixels - } - }); - - const weapons_tileset = try STBImage.load(@embedFile("assets/kenney_desert-shooter-pack_1.0/PNG/Weapons/tilemap_packed.png")); - defer weapons_tileset.deinit(); - const weapons_texture = try Gfx.addTexture(&.{ - .{ - .width = weapons_tileset.width, - .height = weapons_tileset.height, - .rgba = weapons_tileset.rgba8_pixels - } - }); - - const move_c = try Audio.load(.{ - .data = @embedFile("assets/kenney_desert-shooter-pack_1.0/Sounds/move-c.ogg"), - .format = .vorbis, - }); - const move_d = try Audio.load(.{ - .data = @embedFile("assets/kenney_desert-shooter-pack_1.0/Sounds/move-d.ogg"), - .format = .vorbis, - }); - const move_sound = try arena.allocator().dupe(Audio.Data.Id, &.{ move_c, move_d }); - return Assets{ .arena = arena, .font_id = font_id_array, - .wood01 = wood01, + .pistol_mask = pistol_mask_texture, + .bomb_mask = bomb_mask_texture, + .laser_mask = laser_mask_texture, + .snake = snake, + .skeleton = skeleton, + .fire_spirit = fire_spirit, .map = map, .tilesets = tilesets, - .move_sound = move_sound, - .terrain_tilemap = .{ - .texture = tileset_texture, - .tile_size = .initFromInt(u32, tileset.tile_width, tileset.tile_height) - }, - .players_tilemap = .{ - .texture = players_texture, - .tile_size = .init(24, 24) - }, - .weapons_tilemap = .{ - .texture = weapons_texture, - .tile_size = .init(24, 24) - }, + .dungeon_tilemap = .{ + .texture = dungeon_texture, + .tile_size = .init(16, 16) + } }; } pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void { - self.map.deinit(); self.tilesets.deinit(gpa); + self.map.deinit(); self.arena.deinit(); } diff --git a/src/assets/dungeon.tsx b/src/assets/dungeon.tsx new file mode 100644 index 0000000..10bb6c1 --- /dev/null +++ b/src/assets/dungeon.tsx @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/fire-spirit.png b/src/assets/fire-spirit.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a14aa84b8e2e70414d4fa5d42169d21fbb8eeb GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e4T*7G*0Ti)wRpNX zhFF|VPLRkr;Nj7@ch$psiQf<2e(rIRZdfBIxj_1mOV6{vbI(}Bo@hD$)1bHSX?>@8 zgUD?|9*1i@*E3djCvogz_4zU9T*6B43#t}}8E)SHaj1cH_Jh?g&+z=VmXwg_i*LLA zFU!i9nfY-1Ax_R`$ER}TXgqqzUzh01-ZMpIQ6dwA@Ct+e@RRN)K=&|sy85}Sb4q9e E0KNoj?EnA( literal 0 HcmV?d00001 diff --git a/src/assets/game.tiled-project b/src/assets/game.tiled-project new file mode 100644 index 0000000..d0eb592 --- /dev/null +++ b/src/assets/game.tiled-project @@ -0,0 +1,14 @@ +{ + "automappingRulesFile": "", + "commands": [ + ], + "compatibilityVersion": 1100, + "extensionsPath": "extensions", + "folders": [ + "." + ], + "properties": [ + ], + "propertyTypes": [ + ] +} diff --git a/src/assets/map.tmx b/src/assets/map.tmx new file mode 100644 index 0000000..88723c1 --- /dev/null +++ b/src/assets/map.tmx @@ -0,0 +1,23 @@ + + + + + +2147483701,51,51,51,52,52,51,51,51,51,51,52,52,52,51,51,51,51,51,53, +536870963,49,49,49,49,49,49,49,49,50,50,50,50,50,49,49,49,49,49,2684354611, +1610612788,49,43,43,49,49,49,49,49,50,50,50,50,50,49,49,43,43,49,2684354611, +1610612788,49,43,43,49,49,50,49,49,49,50,50,50,50,49,49,43,43,50,2684354611, +1610612788,50,50,49,49,49,50,50,49,49,49,49,49,49,49,49,49,50,50,3758096436, +1610612788,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,3758096436, +1610612788,49,49,49,49,49,49,50,50,50,50,49,49,50,49,49,49,49,49,3758096436, +536870963,49,49,49,49,49,49,49,50,50,50,50,50,50,49,49,49,49,49,2684354611, +536870963,49,50,50,49,49,49,49,49,49,50,50,50,50,49,49,49,50,50,3758096436, +536870963,49,50,50,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,3758096436, +1610612788,50,50,49,49,49,49,50,50,49,49,49,49,49,49,50,50,50,49,2684354611, +1610612788,49,43,43,49,49,50,50,50,50,50,49,49,49,49,50,43,43,49,2684354611, +536870963,49,43,43,49,50,50,50,49,49,49,49,49,49,49,50,43,43,49,3758096436, +536870963,49,49,49,49,50,50,49,49,49,49,49,49,49,49,49,49,49,49,2684354611, +3221225525,1073741875,1073741875,1073741875,1073741875,1073741876,1073741876,1073741875,1073741875,1073741875,1073741875,1073741875,1073741875,1073741876,1073741876,1073741875,1073741875,1073741875,1073741876,1073741877 + + + diff --git a/src/assets/snake.png b/src/assets/snake.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2f0b246bb0ec46ffe30daf9a805b2e7b3abd2c GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e4NDP5ccd36bj#Dl zF~s8Z(#f`bO$HKe_b1Iev~V_ifr;V8h1_k2E125m9XKoyY2}gh=mg^p2^&}8SdE{* zF3vxw#&f}rUq7Gw!*hY1CqMuD&}%EdEKr11bB4DI=f69B2k*T5$EOm>*q%J;6!SkX z*JT2t-h4|{E=g$=y%$@!BsRmtqVb_RK4&+;hnPLw8ZDXEuXELwlyk>H{y$ zv=|$9zGmjUdu&z7r5Y~%hq0`(36UDaYdfvsP6EO|*Pb{*e92{G|8y8kZab`jWxZ)z4*}Q$iB}fP<89 literal 0 HcmV?d00001 diff --git a/src/assets/tiny-dungeon/License.txt b/src/assets/tiny-dungeon/License.txt new file mode 100644 index 0000000..1b269f6 --- /dev/null +++ b/src/assets/tiny-dungeon/License.txt @@ -0,0 +1,22 @@ + + + Tiny Dungeon (1.0) + + Created/distributed by Kenney (www.kenney.nl) + Creation date: 05-07-2022 + + ------------------------------ + + License: (Creative Commons Zero, CC0) + http://creativecommons.org/publicdomain/zero/1.0/ + + This content is free to use in personal, educational and commercial projects. + Support us by crediting Kenney or www.kenney.nl (this is not mandatory) + + ------------------------------ + + Donate: http://support.kenney.nl + Patreon: http://patreon.com/kenney/ + + Follow on Twitter for updates: + http://twitter.com/KenneyNL \ No newline at end of file diff --git a/src/assets/tiny-dungeon/Tilesheet.txt b/src/assets/tiny-dungeon/Tilesheet.txt new file mode 100644 index 0000000..3ecbdd2 --- /dev/null +++ b/src/assets/tiny-dungeon/Tilesheet.txt @@ -0,0 +1,9 @@ +Tilesheet information: + +Tile size • 16px × 16px +Space between tiles • 1px × 1px +--- +Total tiles (horizontal) • 12 tiles +Total tiles (vertical) • 11 tiles +--- +Total tiles in sheet • 132 tiles \ No newline at end of file diff --git a/src/assets/tiny-dungeon/tilemap_packed.png b/src/assets/tiny-dungeon/tilemap_packed.png new file mode 100644 index 0000000000000000000000000000000000000000..f6e8b937c99fe45b1b75490cc050ee5929954259 GIT binary patch literal 5294 zcmWkyc{tSn6Mye67Q3t~XYO1nckW}YTkb27LaucuN96dpLyn5%s&z}7Ba(7$=$mq_ zTXK^ll`A0|KfmXhnb$M(%=4N*=6PnGnG_2%0|t6-dH?`}k)f{DDUwdLjuvrh{RJNU zJ_RaIZBuOk>N8Ok7wS{r#=^u#?-Z-w^c}Cd9M>oue=1!5d5Aw54Ou!F3MGTQuMYTo z1tz5@fqo)6-V6EeYkEpL3k@eHC;OdogK;)(zQ$J0k)+;fQAyoUHO24czG(%GTB5Q^ z=6T1bys%&^Gi!idne+gF^}tA1+vd(xzf+29gEFH>*ve+`v4ST9-Jhe5)hX2rpO6L* z2ZsLl;}ZZs8lEqzcNjQx7QfB4(pl6T7syJ2^pj|=X=rQQ~(HxBut{Z!oY1KqIq zdppnqaw;ypCOq#L1d1j=0!_sdU!f~gH1|h3rEt&A-2gD3vM}C+5GYEM=7LIuJA@IO zFzu4BVE8Zw4$-o$W8Ka_)sGd&Kj5B~EdTs@f(HaDWpp=Junszi@MZSkJ)S_^f9I&k zlR|)-m0%I{U_tG=*BVtkSGPuS@8bcAFsLAoQ#r%mbAZ%#$u7DURFXmwx8j$)CT$mh zdPxJhYL?J=oMHM~1pbcCT%)?LeTCam$1~fBnm(fNM4ua`*I|jA3T@MM2Fm;QVzxt+ zprvgwa8MDh>wWM-jH8;aCQ(q0Hgier*)%R(G3_`F7pVS zYoZ2G78$qXq7DVPMR=~V@dlzwgp*MQRBN^4@fu{CKCD9G}1)~nr{rdkCUUA_|L zA8;mols{EAOL<2yDG#ydOd3wJaNJ$!9der>h;lXtF_r z4n5gQOH4IxP29D|)_Fc&5g1N)RyP5Bo>j6WzW zBf|QSPaAMi^35LlxxMF?J9^LybSc7eMLRI|s@i53hhJW5Hv+V}*_6EpT-I}YNJVvR?a9j+jS`M3hUw@<} zl#rHj^5~m1z5tzoOA!Xb+F$f+Lj;q^j-^w zZC`Rf1673oZf7d)yr9pE1q8$qtc)z^y`nLF+2Nq0Qdot*E>e=}CJ%{|PZDIaC;_a% zxvOs4@vEjk@aFeVX@K1-aw@T4=iz|}+qIxWF+oGhn-FK$B-vDPFAwF=1{j$eMg=yP zhl4Q2mM2PLR&qhf9E$tQu$?*X*qr-l_02A|mk8Yr=B>;hDcZa5g3eLLRG-wsu~eSb z>7(FRr;LikW!A2JXBeAs9S7;Aiw;Su{7yeCO zCt(~weJw2vl}^0(-i-yG&Zt0X(1^psu?>A+xmEZ~bIsz)@~?lV8JgyeG#DNVC|Nqk zKiskldn&3F`8ZM^6Cvn;|dsck&pBy@v`}3(GU~r2%gjy$fBBDpDE1C*YL;@01B~Fsh-44> zKt6wr6uA`qW?p3gP-j1!evj;RH-{%n*CWVOh_y#XnyI`@l~ zU31JYZJs?f(E!TRA?hIeE@-a;f6o%=ac)@d$L zzp0C5aPsrKA+xHJ*>G*hH^)Lo*;$$+2j9KV#+77nde=Q-3Mx;xOvAmzs;*j&Hw-a=bpQ%#{D~$wK#N zY^)Q_Bn3C5vx_+Mr_;EBa${^@&q1Jh*q!p;Y&pK2ymCdao#h;spxNSp<_!}ZZO0ce)2&3&S z>%!TolIaH&`i1KdSi_cG7D%N{A{Z8Ug30q6hc*;JN3jHeT;#$0T;Q~T(_p%-apXl} zR(oDXj`DUvjxyZPD*_N_YX1Q*gqCY59Wa-JLPVs97BJ`L%Q8tGjk0iugz+5CE9k)} z;ruI1VK943ddY(P;1n!;Be>*zD+Z?tiOCMfv+I$ z?b-e@)jGH{*=Mg{$dh?G$F4eJwh#CForGcyjCg4!MkCT6F5bDGG&jjQH}QUY%h${@ zKn+~FJ$4vf*cL(+%+wn4HFJmOFxI6;buM*tcKV(Cr>UAC9WrfXNt_0+N$H1Mj`$_iMEQO6yp6cP8j27(1i^V%$Z> z-st&>+88=8^c5y9q%wYElJwecqW533W0N^7a5DCn5fD}I$|Py>+QTfq9!flSQlD2{ z?In|d1?{Ud5zM3#E(SQ^(uptJoJ5zFZ zNgm`qj>bUkTz)cMx>X^wmZW0~VJHR-{UeckkWZ~#uY5eLVKug3NIZYhk9m>VEBT`_ za;lzuTAE?ydtjbkRd_Tn$yni?5_^PDAs-XTxLKohI5ZMB^DU{SS4xB&-RuGpyCH?-i-abfta8s-ln~}eesytQaF!;W8PXScjC)%sji<00cG*% zJA29_m5#n*3rK#H;MwYpiKB__zn81$9UN9L?Cj-t-C5^(Fx=WT@#+Wgd448B!AcAN z7vr^!i3m=7F!7=)BJlDEVgM*dTB*4y1>RoIln$hY`t9WqOgGc=9{oQ}C}j@s*S(pd zr#)ij5Pa>^d3wi`7$*nx!Y*|4?tvTV*?P$!P}XzyarEF1`msF*pqCi{$8JZqG;f&P zQj+_%eWZ~|`Wf0$uGKE^Yi=Z9z$hx>>}QP$xxKv&je$X~m1MX^oWT^51!oK%_ijAl z6VTC==QLc|tQ=X7UPSTL@ALn99O9bVSK*^;u>98#C?9m@N?Kh(Ifut}fqPDzT9gXn zfP^D&#!r8YU(P7;`L)d{<7@kO=mY|FjXqc>V_dK1yBe4K{Woc|<6q}Z?^V<)!Jqk(BRl{hSaYCZe5^tuUN0~FB_RuUURu`Te79g zd#er)mwA7qz|34b%lO9P{Yl>fOG91{#kiq(7ZNWyS5I+mgbONPV$}GZ2AkaN*cKF9 z=i8VwvECS_1H*~Z0sWYS68i|l;%~G5gUyMNerC4dO`p+E~I!*9jh#o`ch4(!4^;V(7^%yycP3mq45)jBq`9(MMy5RmN2EibZiUsa5r}lSpw?Ma`&AzW%por zv`p^;!n2s`39inesz%26ozv_5LBV}6WPK&!0(h-Zjx@wz=>%jT!mRJcey@T!))$i- z0<5j(tQWH?!}TiY)i%34RJV51aL2*Xiuwbg?CV}}i=c=vnP(`otXu4)IWgj4-O}6J zniDjQhGILB?6JJNTGb1y@QWixp^V9cUD_;Mk zv+a{rPs*i3+-HA(o-4IKs2lIJbIsKbS$&c}jQeU^!k1L^-XYol!@gwj;<5EN%l}V(`K)d$4~Ag>uemX z)i7HVO?W^q)qDv!HyH8Gomo}qCUa0|q64kLos}Qk2l1e=0i-gMK1O|KZl0}Ghg&9) z2O4PmZLWRp)hwbr4*jS|-Nu7e4^3fOOv|@224)vM`TDd#ltX$XhBaCI31tYEr#`A#X(Qx1c71Cp^o0y*VNwsYhg%CD&|xq=TN&`rQ2P06kk>+#Jj+5^(`?y6MJQs2gAd1FP3!mJ2Wns{dY2`y{XkRHNj2^|ei98-D zPMPQ&-KE>aRPL`LY0sVhCf+KoA76IWuAZ2rSY@b!*4m~xq{DAX{FyhL<=m)e{5Jiay<_J2|qYX zH+w7#uH!CdZ6YaJw=4bsL$juEQN7`uPH!IdS-GE7GIllH{!WF>B;m0A10S!Oi!8i( zVD~jrK!a6GLn87VbN<3Fo7_$E(?iQA5gF@=2^G$D+C1j>!+NLg{x_ze0JaYJh1J)w z(XLKCCo}OU?itVTU_@6Nf>TksL=(y2HAJBJNVp(;=V{sqp36v8DN#Z?4B7iA0NT>z z*o>yda<#3qMKH>5C;V|^0j!8-)vz-_9gT*PwP7->Y)lr84sm$Ks)^LLKTwr!okw;h zg5`~zHpFlE;sbMTJby7hU^pUrH|JB zxSuw==Byqv#NPj9=`Gr+nzupzTE2{b#@ELqN#l_MTaSpHqj_=;nAPhSayuSk(BX8M kpUhLipUS26pU??1PF|X=d$rx|^dSh0^vrZ?aL#f618n+ { - _ = try self.spawnEnemy(.{}); + _ = try self.spawnEnemy(.{ + .sprite = self.assets.skeleton + }); }, .snake => { var arena = std.heap.ArenaAllocator.init(self.gpa); @@ -367,7 +386,9 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul const rand = self.rng.random(); const snake_length = rand.intRangeAtMost(u32, wave.min_group_size, wave.max_group_size); - const head_id = try self.spawnEnemy(.{ }); + const head_id = try self.spawnEnemy(.{ + .sprite = self.assets.snake + }); try enemies.append(arena.allocator(), head_id); const head = self.enemies.getAssumeExists(head_id); @@ -379,6 +400,7 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul for (0..snake_length) |i| { const tail_pos = head_pos.sub(dir_to_center.multiplyScalar(@floatFromInt(i * gap))); const tail_id = try self.spawnEnemy(.{ + .sprite = self.assets.snake, .pos = tail_pos }); try enemies.append(arena.allocator(), tail_id); @@ -407,6 +429,7 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul const enemy_id = try self.spawnEnemy(.{ .pos = center.add(Vec2.initAngle(angle).multiplyScalar(distance)), + .sprite = self.assets.fire_spirit, .size = 10 }); try enemies.append(arena.allocator(), enemy_id); @@ -434,19 +457,9 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul } } - if (self.wave_timer >= self.next_pickup_spawn_at) { - const next_pickup_spawn_at_s = pickup_spawn_duration_s.random(self.rng.random()); - const next_pickup_spawn_at: Nanoseconds = @intFromFloat(next_pickup_spawn_at_s * std.time.ns_per_s); - self.next_pickup_spawn_at = self.wave_timer + next_pickup_spawn_at; - try self.spawnPickup(); - } - frame.graphics.canvas_size = world_size; - frame.drawRectangle(.{ - .rect = .init(0, 0, world_size.x, world_size.y), - .color = rgb(20, 20, 20) - }); + self.drawMap(frame); var dir = Vec2.init(0, 0); if (frame.isKeyDown(.W)) { @@ -500,45 +513,52 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul if (frame.isMouseDown(.left) and cooldown_complete) { self.player.last_shot_at = frame.time_ns; - if (self.player.gun) |gun| { - switch (gun) { - .pistol => { - try self.bullets.append(self.gpa, .{ - .kinetic = .{ - .pos = self.player.kinetic.pos, - }, - .dir = bullet_dir, - .speed = 50 - }); - }, - .bomb => { - try self.bombs.append(self.gpa, .{ - .kinetic = .{ - .pos = self.player.kinetic.pos, - .vel = bullet_dir.multiplyScalar(800) - }, - .size = 10, - .explode_at = frame.time_ns + std.time.ns_per_s, - .explosion_radius = 50 - }); - }, - .laser => { - try self.lasers.append(self.gpa, .{ - .origin = self.player.kinetic.pos, - .dir = bullet_dir, - .size = 10, - .created_at = frame.time_ns, - .duration = std.time.ns_per_s - }); - } + switch (self.player.gun) { + .pistol => { + try self.bullets.append(self.gpa, .{ + .kinetic = .{ + .pos = self.player.kinetic.pos, + }, + .dir = bullet_dir, + .speed = 50 + }); + }, + .bomb => { + try self.bombs.append(self.gpa, .{ + .kinetic = .{ + .pos = self.player.kinetic.pos, + .vel = bullet_dir.multiplyScalar(800) + }, + .size = 10, + .explode_at = frame.time_ns + std.time.ns_per_s, + .explosion_radius = 50 + }); + }, + .laser => { + try self.lasers.append(self.gpa, .{ + .origin = self.player.kinetic.pos, + .dir = bullet_dir, + .size = 10, + .created_at = frame.time_ns, + .duration = std.time.ns_per_s + }); } } } } + frame.drawRectangle(.{ .rect = self.player.getRect(), - .color = rgb(255, 255, 255) + .color = rgb(255, 255, 255), + .sprite = .{ + .texture = switch (self.player.gun) { + .pistol => self.assets.pistol_mask, + .bomb => self.assets.bomb_mask, + .laser => self.assets.laser_mask, + }, + .uv = .unit + } }); for (self.bullets.items) |*bullet| { @@ -775,40 +795,19 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul } enemy.kinetic.update(dt, .{}); + frame.drawRectangle(.{ .rect = enemy_rect, - .color = rgb(20, 200, 20) + .color = rgba(20, 20, 200, 0.2), }); - } - } - - { - var index: usize = 0; - while (index < self.pickups.items.len) { - var destroy: bool = false; - const pickup = self.pickups.items[index]; - - const pickup_rect = Rect.initCentered(pickup.pos.x, pickup.pos.y, 10, 10); - - if (pickup_rect.hasOverlap(self.player.getRect())) { - switch (pickup.kind) { - .money => { - state.money += 1; - } - } - destroy = true; - } + const pos = enemy.kinetic.pos; + const sprite_size = enemy.sprite.getSize(); frame.drawRectangle(.{ - .rect = pickup_rect, - .color = rgb(20, 20, 200) + .rect = Rect.initCentered(pos.x, pos.y, sprite_size.x, sprite_size.y), + .color = rgb(255, 255, 255), + .sprite = enemy.sprite }); - - if (destroy) { - _ = self.pickups.swapRemove(index); - } else { - index += 1; - } } } @@ -842,7 +841,7 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul frame.drawTextFormat(.init(10, 30), text_opts, "{d}", .{ state.money }); frame.drawTextFormat(.init(10, 50), text_opts, "{d}/{d}", .{ self.player.health, self.player.max_health }); - frame.drawTextFormat(.init(10, 70), text_opts, "{?}", .{ self.player.gun }); + frame.drawTextFormat(.init(10, 70), text_opts, "{}", .{ self.player.gun }); result.player_died = (self.player.health == 0); if (self.enemies.count == 0 and self.waves.items.len == 0 and self.spawned_waves.items.len == wave_infos.len) { diff --git a/src/engine/graphics.zig b/src/engine/graphics.zig index 7b20316..0496baa 100644 --- a/src/engine/graphics.zig +++ b/src/engine/graphics.zig @@ -46,6 +46,9 @@ pub const Command = union(enum) { sprite: ?Sprite = null, rotation: f32 = 0, origin: Vec2 = .init(0, 0), + uv_flip_diagonal: bool = false, + uv_flip_horizontal: bool = false, + uv_flip_vertical: bool = false }; pub const DrawCircle = struct { @@ -88,6 +91,12 @@ pub const TextureInfo = Texture.Info; pub const Sprite = struct { texture: TextureId, uv: Rect, + + pub fn getSize(self: Sprite) Vec2 { + const texture_info = getTextureInfo(self.texture); + const texture_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height); + return self.uv.size.multiply(texture_size); + } }; var gpa: std.mem.Allocator = undefined; @@ -333,7 +342,41 @@ fn drawRectangle(opts: Command.DrawRectangle) void { if (opts.sprite) |sprite| { const uv = sprite.uv; - const quad = [4]Vertex{ .{ .pos = pos.add(top_left), .uv = .init(uv.left(), uv.top()) }, .{ .pos = pos.add(top_right), .uv = .init(uv.right(), uv.top()) }, .{ .pos = pos.add(bottom_right), .uv = .init(uv.right(), uv.bottom()) }, .{ .pos = pos.add(bottom_left), .uv = .init(uv.left(), uv.bottom()) } }; + var uv_top_left = Vec2.init(uv.left(), uv.top()); + var uv_top_right = Vec2.init(uv.right(), uv.top()); + var uv_bottom_right = Vec2.init(uv.right(), uv.bottom()); + var uv_bottom_left = Vec2.init(uv.left(), uv.bottom()); + + if (opts.uv_flip_diagonal) { + std.mem.swap(Vec2, &uv_bottom_left, &uv_top_right); + } + if (opts.uv_flip_horizontal) { + std.mem.swap(Vec2, &uv_top_left, &uv_top_right); + std.mem.swap(Vec2, &uv_bottom_left, &uv_bottom_right); + } + if (opts.uv_flip_vertical) { + std.mem.swap(Vec2, &uv_top_left, &uv_bottom_left); + std.mem.swap(Vec2, &uv_top_right, &uv_bottom_right); + } + + const quad = [4]Vertex{ + .{ + .pos = pos.add(top_left), + .uv = uv_top_left + }, + .{ + .pos = pos.add(top_right), + .uv = uv_top_right, + }, + .{ + .pos = pos.add(bottom_right), + .uv = uv_bottom_right + }, + .{ + .pos = pos.add(bottom_left), + .uv = uv_bottom_left + } + }; drawQuad(quad, opts.color, sprite.texture); } else { const quad = .{ pos.add(top_left), pos.add(top_right), pos.add(bottom_right), pos.add(bottom_left) }; diff --git a/src/engine/math.zig b/src/engine/math.zig index 885907c..720b738 100644 --- a/src/engine/math.zig +++ b/src/engine/math.zig @@ -368,6 +368,11 @@ pub const Rect = struct { pos: Vec2, size: Vec2, + pub const unit = Rect{ + .pos = .zero, + .size = .init(1, 1), + }; + pub const zero = Rect{ .pos = Vec2.zero, .size = Vec2.zero diff --git a/src/game.zig b/src/game.zig index 38b4f69..0f419c7 100644 --- a/src/game.zig +++ b/src/game.zig @@ -124,10 +124,6 @@ pub fn debug(self: *Game) !void { } defer imgui.endWindow(); - if (imgui.button("Spawn enemy")) { - _ = try self.combat_screen.spawnEnemy(.{}); - } - if (imgui.button("Restart")) { try self.restartAndShowCombatScreen(); } @@ -144,10 +140,7 @@ pub fn debug(self: *Game) !void { const screen = &self.combat_screen; - const time_left_til_pickup = screen.next_pickup_spawn_at - screen.wave_timer; - imgui.textFmt("Waves: {}\n", .{screen.waves.items.len}); imgui.textFmt("Bullets: {}\n", .{screen.bullets.items.len}); imgui.textFmt("Enemies: {}\n", .{screen.enemies.count}); - imgui.textFmt("Time until next pickup: {d:.2}s\n", .{@as(f32, @floatFromInt(time_left_til_pickup)) / std.time.ns_per_s}); }