1.44mb-gamejam

submission for the 1.44mb game jam - https://2pgarcade.com/contest-144mb.html
git clone git://brookjeynes.dev/bjeynes/1.44mb-gamejam.git
Log | Files | Refs

commit 248f0a5e5bd5d84234854f7aff8183236b37d90f
parent 263def7734ce19f24df9469a82c7362a6c394ef3
Author: brookjeynes <me@brookjeynes.dev>
Date:   Thu, 30 Jul 2026 15:03:37 +1000

refactor: game state

Signed-off-by: brookjeynes <me@brookjeynes.dev>

Diffstat:
Msrc/bullet.zig | 2+-
Msrc/enemy.zig | 6+-----
Msrc/main.zig | 273+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/player.zig | 8++------
4 files changed, 173 insertions(+), 116 deletions(-)

diff --git a/src/bullet.zig b/src/bullet.zig @@ -13,7 +13,7 @@ pub fn Pool(comptime upper: usize) type { return struct { const Self = @This(); - bullets: [upper]Bullet = undefined, + bullets: [upper]Bullet, count: usize = 0, fire_timer: Timer, diff --git a/src/enemy.zig b/src/enemy.zig @@ -14,11 +14,7 @@ size: raylib.Vector2 = .{ .x = 40, .y = 40 }, direction: raylib.Vector2 = .{}, position: raylib.Vector2 = .{}, -bullet_pool: bullet.Pool(max_spawned_bullets) = .{ - .fire_timer = .{ - .duration = 2.0, - }, -}, +bullet_pool: bullet.Pool(max_spawned_bullets), pub fn handleMovement(self: *Enemy, target: raylib.Vector2) void { const delta: f32 = raylib.GetFrameTime(); diff --git a/src/main.zig b/src/main.zig @@ -1,5 +1,3 @@ -const std = @import("std"); - const raylib = @import("raylib"); const raymath = @import("raymath"); @@ -8,50 +6,101 @@ const Enemy = @import("./enemy.zig"); const Player = @import("./player.zig"); const Timer = @import("./timer.zig"); -const Screen = enum(u8) { +const max_enemies: u8 = 255; + +const Entity = union(enum) { + enemy: Enemy, +}; + +const Screen = enum(u3) { title, gameplay, upgrade, }; -pub fn main() !void { - const screen_width: f16 = 800.0; - const screen_height: f16 = 1000.0; - const fps: u8 = 60; +const GameState = struct { + screen: struct { + width: u16 = 800, + height: u16 = 1000, + current: Screen = .title, + title: [*]const u8 = "1.44mb game jam", + }, + fps: u8 = 60, + time: f32 = 0, + + enemies: [max_enemies]Entity, + enemy_count: u8 = 0, + + pub fn spawn_enemy(self: *GameState, enemy: Entity) void { + if (self.enemy_count == self.enemies.len) return; + + self.enemies[self.enemy_count] = enemy; + self.enemy_count += 1; + } + + pub fn despawn_enemy(self: *GameState, enemy_idx: u8) void { + for (enemy_idx..self.enemy_count) |i| { + self.enemies[i] = self.enemies[i + 1]; + } + + if (self.enemy_count > 0) self.enemy_count -= 1; + } + + pub fn reset(self: *GameState) void { + self.screen.current = .gameplay; + self.time = 0; + self.enemy_count = 0; + } + + pub fn difficulty(self: GameState) f32 { + return @min(5.0, 1.0 + self.time * 0.02); + } +}; - var current_screen: Screen = .title; +pub fn main() !void { + var game_state: GameState = .{ + .screen = .{}, + .enemies = undefined, + }; - raylib.InitWindow(screen_width, screen_height, "1.44mb game jam"); + raylib.InitWindow( + game_state.screen.width, + game_state.screen.height, + game_state.screen.title, + ); defer raylib.CloseWindow(); - raylib.SetTargetFPS(fps); + raylib.SetTargetFPS(game_state.fps); var player: Player = .{ + .bullet_pool = .{ + .bullets = undefined, + .fire_timer = .{ + .duration = Player.bullet_spawn_cooldown, + }, + }, .position = .{ - .x = screen_width / 2, - .y = screen_height / 2, + .x = game_state.screen.width / 2, + .y = game_state.screen.height / 2, }, }; var enemy_spawn_time: Timer = .{ .duration = 0.7 }; - var enemies: [255]Enemy = undefined; - var enemy_count: u8 = 0; - var game_time: f32 = 0; - - const button_size: raylib.Vector2 = .{ .x = 100, .y = 70 }; - const button_position: raylib.Vector2 = .{ - .x = (screen_width / 2) - (button_size.x / 2), - .y = (screen_height / 2) - (button_size.y / 2), + + const start_button_size: raylib.Vector2 = .{ .x = 100, .y = 70 }; + const start_button_position: raylib.Vector2 = .{ + .x = (game_state.screen.width / 2) - (start_button_size.x / 2), + .y = (game_state.screen.height / 2) - (start_button_size.y / 2), }; - const button_bounds: raylib.Rectangle = .{ - .x = button_position.x, - .y = button_position.y, - .width = button_size.x, - .height = button_size.y, + const start_button_bounds: raylib.Rectangle = .{ + .x = start_button_position.x, + .y = start_button_position.y, + .width = start_button_size.x, + .height = start_button_size.y, }; while (!raylib.WindowShouldClose()) { - switch (current_screen) { + switch (game_state.screen.current) { .title => { const mouse_point = raylib.GetMousePosition(); @@ -61,23 +110,21 @@ pub fn main() !void { raylib.ClearBackground(raylib.RAYWHITE); raylib.DrawRectangleV( - button_position, - button_size, + start_button_position, + start_button_size, raylib.BLACK, ); raylib.DrawText( "start", - @intFromFloat(button_position.x + (button_size.x / 2)), - @intFromFloat(button_position.y + (button_size.y / 2)), + @intFromFloat(start_button_position.x + (start_button_size.x / 2)), + @intFromFloat(start_button_position.y + (start_button_size.y / 2)), 18, raylib.WHITE, ); - if (raylib.CheckCollisionPointRec(mouse_point, button_bounds)) { + if (raylib.CheckCollisionPointRec(mouse_point, start_button_bounds)) { if (raylib.IsMouseButtonReleased(raylib.MOUSE_BUTTON_LEFT)) { - current_screen = .gameplay; - game_time = 0; - enemy_count = 0; + game_state.reset(); enemy_spawn_time = .{ .duration = 0.7 }; player.health = Player.max_health; } @@ -86,29 +133,31 @@ pub fn main() !void { .upgrade => {}, .gameplay => { const delta = raylib.GetFrameTime(); + const difficulty = game_state.difficulty(); - game_time += delta; + game_state.time += delta; - const difficulty: f32 = @min(5.0, 1.0 + game_time * 0.02); enemy_spawn_time.duration = @max(0.15, 0.7 / difficulty); player.tick(); player.handleMovement(); - for (0..enemy_count) |i| { - enemies[i].handleMovement(player.position); + for (0..game_state.enemy_count) |i| { + const entity = &game_state.enemies[i]; + switch (entity.*) { + .enemy => |*e| e.handleMovement(player.position), + } } - if (enemy_spawn_time.tick(delta) and enemy_count < enemies.len) { - enemies[enemy_count] = spawnEnemy( + if (enemy_spawn_time.tick(delta)) { + game_state.spawn_enemy(.{ .enemy = spawnEnemy( .{ - .x = screen_width, - .y = screen_height, + .x = game_state.screen.width, + .y = game_state.screen.height, }, player, difficulty, - ); - enemy_count += 1; + ) }); } raylib.BeginDrawing(); @@ -118,77 +167,85 @@ pub fn main() !void { player.render(); - var enemy_idx: usize = 0; - while (enemy_idx < enemy_count) { - const enemy = &enemies[enemy_idx]; - - for (0..enemy.bullet_pool.count) |bullet_idx| { - const bullet = enemy.bullet_pool.bullets[bullet_idx]; - - const is_collision = raylib.CheckCollisionCircleRec( - .{ - .x = bullet.position.x, - .y = bullet.position.y, - }, - Bullet.radius, - .{ - .x = player.position.x, - .y = player.position.y, - .width = player.size.x, - .height = player.size.y, - }, - ); - - if (is_collision) { - enemy.bullet_pool.despawn(bullet_idx); - player.health -= Enemy.bullet_damage; - - if (player.health < 0) { - current_screen = .title; + var enemy_idx: u8 = 0; + while (enemy_idx < game_state.enemy_count) { + const entity = &game_state.enemies[enemy_idx]; + + switch (entity.*) { + .enemy => |*enemy| { + for (0..enemy.bullet_pool.count) |bullet_idx| { + const bullet = enemy.bullet_pool.bullets[bullet_idx]; + + const is_collision = raylib.CheckCollisionCircleRec( + .{ + .x = bullet.position.x, + .y = bullet.position.y, + }, + Bullet.radius, + .{ + .x = player.position.x, + .y = player.position.y, + .width = player.size.x, + .height = player.size.y, + }, + ); + + if (is_collision) { + enemy.bullet_pool.despawn(bullet_idx); + player.health -= Enemy.bullet_damage; + + if (player.health < 0) { + game_state.screen.current = .title; + } + } } - } - } - for (0..player.bullet_pool.count) |bullet_idx| { - const bullet = player.bullet_pool.bullets[bullet_idx]; - - const is_collision = raylib.CheckCollisionCircleRec( - .{ - .x = bullet.position.x, - .y = bullet.position.y, - }, - Bullet.radius, - .{ - .x = enemy.position.x, - .y = enemy.position.y, - .width = enemy.size.x, - .height = enemy.size.y, - }, - ); - - if (is_collision) { - player.bullet_pool.despawn(bullet_idx); - - for (enemy_idx..enemy_count) |i| { - enemies[i] = enemies[i + 1]; - } - if (enemy_count != 0) { - enemy_count -= 1; + for (0..player.bullet_pool.count) |bullet_idx| { + const bullet = player.bullet_pool.bullets[bullet_idx]; + + const is_collision = raylib.CheckCollisionCircleRec( + .{ + .x = bullet.position.x, + .y = bullet.position.y, + }, + Bullet.radius, + .{ + .x = enemy.position.x, + .y = enemy.position.y, + .width = enemy.size.x, + .height = enemy.size.y, + }, + ); + + if (is_collision) { + player.bullet_pool.despawn(bullet_idx); + + game_state.despawn_enemy(enemy_idx); + continue; + } } - continue; - } + }, } enemy_idx += 1; } - for (0..enemy_count) |idx| { - enemies[idx].render(); + for (0..game_state.enemy_count) |idx| { + const entity = &game_state.enemies[idx]; + switch (entity.*) { + .enemy => |*e| e.render(), + } } const health_percent: f16 = @max(0, @min(1, player.health / Player.max_health)); const damage_percent = 1 - health_percent; - raylib.DrawRectangle(0, 0, screen_width, screen_height, raylib.ColorAlpha(raylib.RED, damage_percent)); + raylib.DrawRectangle( + 0, + 0, + game_state.screen.width, + game_state.screen.height, + raylib.ColorAlpha(raylib.RED, damage_percent), + ); }, } } @@ -199,7 +256,15 @@ fn spawnEnemy(screen: raylib.Vector2, player: Player, difficulty: f32) Enemy { const player_cx = player.position.x + player.size.x / 2; const player_cy = player.position.y + player.size.y / 2; - var enemy = Enemy{ .position = .{ .x = 0, .y = 0 } }; + var enemy = Enemy{ + .bullet_pool = .{ + .bullets = undefined, + .fire_timer = .{ + .duration = 2.0, + }, + }, + .position = .{ .x = 0, .y = 0 }, + }; while (true) { const rnd_x = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); const rnd_y = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); diff --git a/src/player.zig b/src/player.zig @@ -9,7 +9,7 @@ pub const Player = @This(); const max_spawned_bullets = 2000; const bullet_speed: f32 = 300.0; -const bullet_spawn_cooldown: f32 = 0.4; +pub const bullet_spawn_cooldown: f32 = 0.4; const acceleration: f32 = 10000.0; const friction: f32 = 10000.0; const max_delta: f32 = 1.0 / 30.0; @@ -25,11 +25,7 @@ health_regen_timer: Timer = .{ .duration = 5.0, }, -bullet_pool: bullet.Pool(max_spawned_bullets) = .{ - .fire_timer = .{ - .duration = bullet_spawn_cooldown, - }, -}, +bullet_pool: bullet.Pool(max_spawned_bullets), pub fn render(self: *Player) void { raylib.DrawRectangleV(