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 0000000..f6a14aa
Binary files /dev/null and b/src/assets/fire-spirit.png differ
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 @@
+
+
diff --git a/src/assets/snake.png b/src/assets/snake.png
new file mode 100644
index 0000000..ed2f0b2
Binary files /dev/null and b/src/assets/snake.png differ
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 0000000..f6e8b93
Binary files /dev/null and b/src/assets/tiny-dungeon/tilemap_packed.png differ
diff --git a/src/combat_screen.zig b/src/combat_screen.zig
index da23ded..5daa3a4 100644
--- a/src/combat_screen.zig
+++ b/src/combat_screen.zig
@@ -10,6 +10,7 @@ const GenerationalArrayList = @import("./generational_array_list.zig").Generatio
const Engine = @import("./engine/root.zig");
const Nanoseconds = Engine.Nanoseconds;
const Vec2 = Engine.Vec2;
+const Sprite = Engine.Graphics.Sprite;
const Line = Engine.Math.Line;
const Rect = Engine.Math.Rect;
const Range = Engine.Math.Range;
@@ -55,10 +56,11 @@ const Player = struct {
max_health: u32 = 0,
last_shot_at: ?Nanoseconds = null,
invincible_until: ?Nanoseconds = null,
- gun: ?Gun = null,
+ gun: Gun = .pistol,
pub fn getRect(self: Player) Rect {
- return getCenteredRect(self.kinetic.pos, 16);
+ const pos = self.kinetic.pos;
+ return Rect.initCentered(pos.x, pos.y, 16, 24);
}
};
@@ -90,6 +92,8 @@ const Enemy = struct {
kinetic: Kinetic,
speed: f32,
size: f32,
+ sprite: Sprite,
+
target: ?Vec2 = null,
distance_to_target: ?f32 = null,
@@ -147,18 +151,8 @@ const Wave = struct {
};
};
-const Pickup = struct {
- const Kind = enum {
- money
- };
-
- pos: Vec2,
- kind: Kind,
-};
-
const world_size = Vec2.init(20 * 16, 15 * 16);
const invincibility_duration_s = 0.5;
-const pickup_spawn_duration_s = Range.init(1, 5);
const wave_infos = [_]Wave.Info{
.{
.kind = .regular,
@@ -196,24 +190,15 @@ bullets: std.ArrayList(Bullet) = .empty,
lasers: std.ArrayList(Laser) = .empty,
bombs: std.ArrayList(Bomb) = .empty,
-pickups: std.ArrayList(Pickup) = .empty,
-next_pickup_spawn_at: Nanoseconds,
-
wave_timer: Nanoseconds = 0,
waves: std.ArrayList(Wave) = .empty,
spawned_waves: std.ArrayList(usize) = .empty,
pub fn init(gpa: Allocator, seed: u64, assets: *Assets, state: State) CombatScreen {
- var rng = RNGState.init(seed);
-
- const next_pickup_spawn_at_s = pickup_spawn_duration_s.random(rng.random());
- const next_pickup_spawn_at: Nanoseconds = @intFromFloat(next_pickup_spawn_at_s * std.time.ns_per_s);
-
return CombatScreen{
.gpa = gpa,
.assets = assets,
- .next_pickup_spawn_at = next_pickup_spawn_at,
.player = .{
.kinetic = .{
.pos = world_size.divideScalar(2)
@@ -222,7 +207,7 @@ pub fn init(gpa: Allocator, seed: u64, assets: *Assets, state: State) CombatScre
.health = state.max_health,
.max_health = state.max_health,
},
- .rng = rng
+ .rng = RNGState.init(seed)
};
}
@@ -235,7 +220,6 @@ pub fn deinit(self: *CombatScreen) void {
self.bullets.deinit(self.gpa);
self.enemies.deinit(self.gpa);
self.waves.deinit(self.gpa);
- self.pickups.deinit(self.gpa);
self.spawned_waves.deinit(self.gpa);
self.lasers.deinit(self.gpa);
self.bombs.deinit(self.gpa);
@@ -244,6 +228,7 @@ pub fn deinit(self: *CombatScreen) void {
const EnemyOptions = struct {
pos: ?Vec2 = null,
+ sprite: Sprite,
size: f32 = 20
};
@@ -290,18 +275,8 @@ pub fn spawnEnemy(self: *CombatScreen, opts: EnemyOptions) !Enemy.Id {
return try self.enemies.insert(self.gpa, Enemy{
.kinetic = .{ .pos = pos },
.speed = 50,
- .size = opts.size
- });
-}
-
-pub fn spawnPickup(self: *CombatScreen) !void {
- const margin = 10;
- const spawn_area = (Rect{ .pos = .zero, .size = world_size }).grow(-margin);
- const pos = Vec2.initRandomRect(self.rng.random(), spawn_area);
-
- try self.pickups.append(self.gpa, Pickup{
- .pos = pos,
- .kind = .money
+ .size = opts.size,
+ .sprite = opts.sprite
});
}
@@ -310,7 +285,49 @@ const TickResult = struct {
player_finished: bool
};
+fn drawMap(self: *CombatScreen, frame: *Engine.Frame) void {
+ const tilemap = self.assets.dungeon_tilemap;
+ const texture_info = Engine.Graphics.getTextureInfo(tilemap.texture);
+ const tilemap_size = Vec2.initFromInt(u32, texture_info.width, texture_info.height);
+
+ for (self.assets.map.layers) |layer| {
+ if (layer.variant != .tile) {
+ continue;
+ }
+
+ const tile_layer = layer.variant.tile;
+ for (0..tile_layer.height) |y| {
+ for (0..tile_layer.width) |x| {
+ const tile_gid = tile_layer.get(@intCast(x), @intCast(y)) orelse continue;
+ if (tile_gid == 0) continue;
+
+ const tile = self.assets.map.getTile(self.assets.tilesets, tile_gid) orelse continue;
+ const tile_id_f32: f32 = @floatFromInt(tile.id);
+ const width_in_tiles = tilemap_size.x / tilemap.tile_size.x;
+ const tile_x = @rem(tile_id_f32, width_in_tiles);
+ const tile_y = @divFloor(tile_id_f32, width_in_tiles);
+
+ frame.drawRectangle(.{
+ .rect = .{
+ .pos = Vec2.initFromInt(usize, x, y).multiply(tilemap.tile_size),
+ .size = tilemap.tile_size
+ },
+ .color = rgb(255, 255, 255),
+ .sprite = .{
+ .texture = tilemap.texture,
+ .uv = tilemap.getTileUV(tile_x, tile_y)
+ },
+ .uv_flip_diagonal = tile.flip_diagonal,
+ .uv_flip_vertical = tile.flip_vertical,
+ .uv_flip_horizontal = tile.flip_horizontal,
+ });
+ }
+ }
+ }
+}
+
pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResult {
+
const dt = frame.deltaTime();
var result = TickResult{
@@ -357,7 +374,9 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul
while (wave.enemies_spawned < expected_enemies) {
switch (wave.kind) {
.regular => {
- _ = 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});
}