Compare commits
No commits in common. "880cf8ab4e94e9b0551d724c7e3a39a870d70741" and "4c425720cc21d90fee67f00b01b7b68ff223d509" have entirely different histories.
880cf8ab4e
...
4c425720cc
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
zig-out
|
||||
.zig-cache
|
||||
*.tiled-session
|
||||
|
||||
22
README.md
@ -1,12 +1,4 @@
|
||||
# Gaem
|
||||
|
||||
* Run & gun
|
||||
* Nuclear throne -esque
|
||||
* Single gun type
|
||||
* Single map
|
||||
* Single enemy type
|
||||
* Collect letters to win, up to 7 letters we have 8 character sprites
|
||||
* The word is WINNER
|
||||
# Game template
|
||||
|
||||
## Run
|
||||
|
||||
@ -24,3 +16,15 @@ Cross-compile for Windows from Linux:
|
||||
```sh
|
||||
zig build -Dtarget=x86_64-windows
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
* Use [Skribidi](https://github.com/memononen/Skribidi) instead of fontstash for text rendering
|
||||
* Support for audio formats (Might not need all of them, haven't decided):
|
||||
* QOA, maybe [qoa.h](https://github.com/phoboslab/qoa/blob/master/qoa.h)?
|
||||
* Flac, maybe [dr_flac.h](https://github.com/mackron/dr_libs/blob/master/dr_flac.h)?
|
||||
* Wav, maybe [dr_wav.h](https://github.com/mackron/dr_libs/blob/master/dr_wav.h)?
|
||||
* Mp3, maybe [dr_mp3.h](https://github.com/mackron/dr_libs/blob/master/dr_mp3.h)?
|
||||
* Gamepad support.
|
||||
* WASM Support. Currently a build config isn't provided for this target.
|
||||
* Update build config for other platforms to reduce binary size. All of the video and audio drivers aren't needed, only gamepads
|
||||
|
||||
@ -2,8 +2,6 @@ const std = @import("std");
|
||||
const sokol = @import("sokol");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const project_name = "game-2026-01-18";
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
@ -107,12 +105,12 @@ pub fn build(b: *std.Build) !void {
|
||||
// from here on different handling for native vs wasm builds
|
||||
if (target.result.cpu.arch.isWasm()) {
|
||||
try buildWasm(b, .{
|
||||
.name = project_name,
|
||||
.name = "sokol_template",
|
||||
.mod_main = mod_main,
|
||||
.dep_sokol = dep_sokol,
|
||||
});
|
||||
} else {
|
||||
try buildNative(b, project_name, mod_main, has_console);
|
||||
try buildNative(b, "sokol_template", mod_main, has_console);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,46 +8,6 @@ const Position = @import("./position.zig");
|
||||
|
||||
const Layer = @This();
|
||||
|
||||
pub const Bounds = struct {
|
||||
left: i32,
|
||||
right: i32,
|
||||
top: i32,
|
||||
bottom: i32,
|
||||
|
||||
pub const zero = Bounds{
|
||||
.left = 0,
|
||||
.right = 0,
|
||||
.top = 0,
|
||||
.bottom = 0,
|
||||
};
|
||||
|
||||
pub fn initFromRect(x: i32, y: i32, width: i32, height: i32) Bounds {
|
||||
return Bounds{
|
||||
.left = x,
|
||||
.right = x + width,
|
||||
.top = y,
|
||||
.bottom = y + height,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getWidth(self: Bounds) u32 {
|
||||
return @intCast(self.right - self.left + 1);
|
||||
}
|
||||
|
||||
pub fn getHeight(self: Bounds) u32 {
|
||||
return @intCast(self.bottom - self.top + 1);
|
||||
}
|
||||
|
||||
pub fn combine(lhs: Bounds, rhs: Bounds) Bounds {
|
||||
return Bounds{
|
||||
.left = @min(lhs.left, rhs.left),
|
||||
.right = @max(lhs.right, rhs.right),
|
||||
.top = @min(lhs.top, rhs.top),
|
||||
.bottom = @max(lhs.bottom, rhs.bottom)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const TileVariant = struct {
|
||||
pub const Chunk = struct {
|
||||
x: i32,
|
||||
@ -55,10 +15,6 @@ pub const TileVariant = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: []u32,
|
||||
|
||||
pub fn getBounds(self: Chunk) Bounds {
|
||||
return Bounds.initFromRect(self.x, self.y, @intCast(self.width), @intCast(self.height));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Data = union(enum) {
|
||||
@ -138,7 +94,7 @@ pub const TileVariant = struct {
|
||||
try temp_tiles.appendSlice(scratch.allocator(), tiles.items);
|
||||
|
||||
} else if (node.isTag("chunk")) {
|
||||
const chunk = try initChunkDataFromXml(arena, scratch, lexer, encoding);
|
||||
const chunk = try initChunkDataFromXml(scratch.allocator(), scratch, lexer, encoding);
|
||||
try temp_chunks.append(scratch.allocator(), chunk);
|
||||
|
||||
continue;
|
||||
@ -149,18 +105,11 @@ pub const TileVariant = struct {
|
||||
|
||||
try iter.finish("data");
|
||||
|
||||
if (temp_chunks.items.len > 0) {
|
||||
return .{
|
||||
.chunks = try arena.dupe(Chunk, temp_chunks.items)
|
||||
};
|
||||
} else {
|
||||
return .{
|
||||
.fixed = try arena.dupe(u32, temp_tiles.items)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn parseEncoding(
|
||||
encoding: Encoding,
|
||||
allocator: std.mem.Allocator,
|
||||
@ -187,46 +136,13 @@ pub const TileVariant = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn getBounds(self: TileVariant) Bounds {
|
||||
if (self.data == .fixed) {
|
||||
return Bounds.initFromRect(0, 0, @intCast(self.width), @intCast(self.height));
|
||||
|
||||
} else if (self.data == .chunks) {
|
||||
const chunks = self.data.chunks;
|
||||
|
||||
var result: Bounds = .zero;
|
||||
if (chunks.len > 0) {
|
||||
result = chunks[0].getBounds();
|
||||
for (chunks[1..]) |chunk| {
|
||||
result = result.combine(chunk.getBounds());
|
||||
}
|
||||
pub fn getBounds(self: TileVariant) void {
|
||||
_ = self; // autofix
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: TileVariant, x: i32, y: i32) ?u32 {
|
||||
if (self.data == .fixed) {
|
||||
pub fn get(self: TileVariant, x: usize, y: usize) ?u32 {
|
||||
if ((0 <= x and x < self.width) and (0 <= y and y < self.height)) {
|
||||
const x_u32: u32 = @intCast(x);
|
||||
const y_u32: u32 = @intCast(y);
|
||||
return self.data.fixed[y_u32 * self.width + x_u32];
|
||||
}
|
||||
} else if (self.data == .chunks) {
|
||||
for (self.data.chunks) |chunk| {
|
||||
const ox = x - chunk.x;
|
||||
const oy = y - chunk.y;
|
||||
if ((0 <= ox and ox < chunk.width) and (0 <= oy and oy < chunk.height)) {
|
||||
const ox_u32: u32 = @intCast(ox);
|
||||
const oy_u32: u32 = @intCast(oy);
|
||||
return chunk.data[oy_u32 * chunk.width + ox_u32];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
return self.data[y * self.width + x];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -74,15 +74,6 @@ pub const List = struct {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getBool(self: List, name: []const u8) ?bool {
|
||||
if (self.get(name)) |value| {
|
||||
if (value == .bool) {
|
||||
return value.bool;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(name: []const u8, value: Value) Property {
|
||||
|
||||
@ -215,18 +215,7 @@ fn getTilesetByGid(self: *const Tilemap, gid: u32) ?TilesetReference {
|
||||
return result;
|
||||
}
|
||||
|
||||
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 tileset = tilesets.get(tileset_ref.source) orelse return null;
|
||||
const id = gid - tileset_ref.first_gid;
|
||||
|
||||
return Tile{
|
||||
.tileset = tileset,
|
||||
.id = id
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getTileByPosition(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List, x: usize, y: usize) ?Tile {
|
||||
pub fn getTile(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List, x: usize, y: usize) ?Tile {
|
||||
assert(layer.variant == .tile);
|
||||
const tile_variant = layer.variant.tile;
|
||||
|
||||
@ -241,26 +230,6 @@ pub fn getTileByPosition(self: *const Tilemap, layer: *const Layer, tilesets: Ti
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getTileBounds(self: *const Tilemap) Layer.Bounds {
|
||||
var result: ?Layer.Bounds = null;
|
||||
|
||||
for (self.layers) |layer| {
|
||||
if (layer.variant != .tile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const layer_bounds = layer.variant.tile.getBounds();
|
||||
if (result == null) {
|
||||
result = layer_bounds;
|
||||
} else {
|
||||
result = layer_bounds.combine(result.?);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result orelse .zero;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const Tilemap) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
@ -4,11 +4,8 @@ const tiled = @import("tiled");
|
||||
|
||||
const Math = @import("./engine/math.zig");
|
||||
const Engine = @import("./engine/root.zig");
|
||||
const STBImage = @import("stb_image");
|
||||
const Gfx = Engine.Graphics;
|
||||
const Audio = Engine.Audio;
|
||||
const Vec2 = Engine.Vec2;
|
||||
const Rect = Engine.Math.Rect;
|
||||
|
||||
const Assets = @This();
|
||||
|
||||
@ -20,38 +17,12 @@ const FontName = enum {
|
||||
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
return .{
|
||||
.pos = Vec2.init(tile_x, tile_y).multiply(self.tile_size).divide(tilemap_size),
|
||||
.size = self.tile_size.divide(tilemap_size),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
font_id: FontName.EnumArray,
|
||||
wood01: Audio.Data.Id,
|
||||
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);
|
||||
errdefer arena.deinit();
|
||||
|
||||
const font_id_array: FontName.EnumArray = .init(.{
|
||||
.regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")),
|
||||
.bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")),
|
||||
@ -84,72 +55,17 @@ pub fn init(gpa: std.mem.Allocator) !Assets {
|
||||
);
|
||||
|
||||
var tilesets: tiled.Tileset.List = .empty;
|
||||
try tilesets.add(gpa, "tilemap.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(&.{
|
||||
.{
|
||||
.width = players_image.width,
|
||||
.height = players_image.height,
|
||||
.rgba = players_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 });
|
||||
try tilesets.add(gpa, "tileset.tsx", tileset);
|
||||
|
||||
return Assets{
|
||||
.arena = arena,
|
||||
.font_id = font_id_array,
|
||||
.wood01 = wood01,
|
||||
.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)
|
||||
},
|
||||
.tilesets = tilesets
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void {
|
||||
self.map.deinit();
|
||||
self.tilesets.deinit(gpa);
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
50
src/assets/game-2026-01-18.tiled-session
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"Map/SizeTest": {
|
||||
"height": 4300,
|
||||
"width": 2
|
||||
},
|
||||
"activeFile": "map.tmx",
|
||||
"expandedProjectPaths": [
|
||||
],
|
||||
"fileStates": {
|
||||
"map.tmx": {
|
||||
"scale": 1.5,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 49.33333333333337,
|
||||
"y": 182
|
||||
}
|
||||
},
|
||||
"tilemap.tsx": {
|
||||
"dynamicWrapping": false,
|
||||
"scaleInDock": 1.5,
|
||||
"scaleInEditor": 5.5
|
||||
},
|
||||
"tiles.tsx": {
|
||||
"dynamicWrapping": true,
|
||||
"scaleInDock": 1,
|
||||
"scaleInEditor": 1
|
||||
}
|
||||
},
|
||||
"last.imagePath": "/home/rokas/code/games/game-2026-01-18/src/assets/kenney_desert-shooter-pack_1.0/PNG/Tiles/Tilemap",
|
||||
"map.fixedSize": false,
|
||||
"map.lastUsedFormat": "tmx",
|
||||
"map.tileHeight": 16,
|
||||
"map.tileWidth": 16,
|
||||
"openFiles": [
|
||||
"tilemap.tsx",
|
||||
"map.tmx"
|
||||
],
|
||||
"project": "game-2026-01-18.tiled-project",
|
||||
"recentFiles": [
|
||||
"tilemap.tsx",
|
||||
"map.tmx",
|
||||
"tiles.tsx"
|
||||
],
|
||||
"tileset.lastUsedFormat": "tsx",
|
||||
"tileset.tileSize": {
|
||||
"height": 16,
|
||||
"width": 16
|
||||
},
|
||||
"tileset.type": 0
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 219 B |
|
After Width: | Height: | Size: 216 B |
|
After Width: | Height: | Size: 232 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 223 B |
|
After Width: | Height: | Size: 236 B |
|
After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 243 B |
|
After Width: | Height: | Size: 256 B |
|
After Width: | Height: | Size: 259 B |
|
After Width: | Height: | Size: 227 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 187 B |
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 136 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 130 B |
|
After Width: | Height: | Size: 163 B |
|
After Width: | Height: | Size: 145 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 148 B |
|
After Width: | Height: | Size: 182 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 130 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 130 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 125 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 113 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 113 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 186 B |
|
After Width: | Height: | Size: 188 B |
|
After Width: | Height: | Size: 186 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 169 B |
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 159 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 129 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 187 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 167 B |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 160 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 177 B |
|
After Width: | Height: | Size: 169 B |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 133 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 133 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 154 B |
|
After Width: | Height: | Size: 114 B |
|
After Width: | Height: | Size: 110 B |
|
After Width: | Height: | Size: 115 B |