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 05a4504d913601357972796f35e886b3cfd12453
parent a9824dfff8d77abe9ae2fb41571093d09c1e15ba
Author: brookjeynes <me@brookjeynes.dev>
Date:   Fri, 31 Jul 2026 11:40:58 +1000

refactor: extract enemy type

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

Diffstat:
Dsrc/enemy.zig | 61-------------------------------------------------------------
Asrc/enemy_types/grunt.zig | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/entity.zig | 7+++++++
Msrc/main.zig | 118+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
4 files changed, 133 insertions(+), 112 deletions(-)

diff --git a/src/enemy.zig b/src/enemy.zig @@ -1,61 +0,0 @@ -const raylib = @import("raylib"); -const raymath = @import("raymath"); - -const bullet = @import("./bullet.zig"); - -pub const bullet_damage: f32 = 50.0; - -pub const Enemy = @This(); - -movement_speed: f32 = 40.0, -bullet_speed: f32 = 100.0, -size: raylib.Vector2 = .{ .x = 40, .y = 40 }, -direction: raylib.Vector2 = .{}, -position: raylib.Vector2 = .{}, - -bullet_pool: bullet.Pool, - -pub fn handleMovement(self: *Enemy, target: raylib.Vector2) void { - const delta: f32 = raylib.GetFrameTime(); - - const direction: raymath.Vector2 = raymath.Vector2Subtract(@bitCast(target), @bitCast(self.position)); - self.direction = @bitCast(raymath.Vector2Normalize(@bitCast(direction))); - self.position.x += self.direction.x * self.movement_speed * delta; - self.position.y += self.direction.y * self.movement_speed * delta; - - const max_x: f32 = @as(f32, @floatFromInt(raylib.GetScreenWidth())) - self.size.x; - const max_y: f32 = @as(f32, @floatFromInt(raylib.GetScreenHeight())) - self.size.y; - self.position.x = @max(0, @min(max_x, self.position.x)); - self.position.y = @max(0, @min(max_y, self.position.y)); - - self.shoot(); -} - -pub fn shoot(self: *Enemy) void { - if (self.bullet_pool.tick()) { - self.bullet_pool.spawn( - .{ - .x = self.position.x + (self.size.x / 2), - .y = self.position.y + (self.size.y / 2), - }, - .{ - .x = self.direction.x * self.bullet_speed, - .y = self.direction.y * self.bullet_speed, - }, - raylib.RED, - ); - } -} - -pub fn render(self: *Enemy) void { - raylib.DrawRectangleV( - self.position, - self.size, - raylib.RED, - ); - - self.bullet_pool.render(.{ - .x = @floatFromInt(raylib.GetScreenWidth()), - .y = @floatFromInt(raylib.GetScreenHeight()), - }); -} diff --git a/src/enemy_types/grunt.zig b/src/enemy_types/grunt.zig @@ -0,0 +1,59 @@ +const raylib = @import("raylib"); +const raymath = @import("raymath"); + +const bullet = @import("../bullet.zig"); + +pub const bullet_damage: f32 = 50.0; +pub const size: raylib.Vector2 = .{ .x = 40, .y = 40 }; + +pub const Grunt = @This(); + +movement_speed: f32 = 40.0, +bullet_speed: f32 = 100.0, +direction: raylib.Vector2 = .{}, +position: raylib.Vector2 = .{}, + +bullet_pool: bullet.Pool, + +pub fn shoot(self: *Grunt) void { + if (self.bullet_pool.tick()) { + self.bullet_pool.spawn( + .{ + .x = self.position.x + (size.x / 2), + .y = self.position.y + (size.y / 2), + }, + .{ + .x = self.direction.x * self.bullet_speed, + .y = self.direction.y * self.bullet_speed, + }, + raylib.RED, + ); + } +} + +pub fn handleMovement(self: *Grunt, target: raylib.Vector2) void { + const delta: f32 = raylib.GetFrameTime(); + + const direction: raymath.Vector2 = raymath.Vector2Subtract(@bitCast(target), @bitCast(self.position)); + self.direction = @bitCast(raymath.Vector2Normalize(@bitCast(direction))); + self.position.x += self.direction.x * self.movement_speed * delta; + self.position.y += self.direction.y * self.movement_speed * delta; + + const max_x: f32 = @as(f32, @floatFromInt(raylib.GetScreenWidth())) - size.x; + const max_y: f32 = @as(f32, @floatFromInt(raylib.GetScreenHeight())) - size.y; + self.position.x = @max(0, @min(max_x, self.position.x)); + self.position.y = @max(0, @min(max_y, self.position.y)); +} + +pub fn render(self: *Grunt) void { + raylib.DrawRectangleV( + self.position, + size, + raylib.RED, + ); + + self.bullet_pool.render(.{ + .x = @floatFromInt(raylib.GetScreenWidth()), + .y = @floatFromInt(raylib.GetScreenHeight()), + }); +} diff --git a/src/entity.zig b/src/entity.zig @@ -0,0 +1,7 @@ +const Grunt = @import("./enemy_types/grunt.zig"); + +pub const EntityType = enum { grunt }; + +pub const Entity = union(EntityType) { + grunt: Grunt, +}; diff --git a/src/main.zig b/src/main.zig @@ -2,16 +2,14 @@ const raylib = @import("raylib"); const raymath = @import("raymath"); const Bullet = @import("./bullet.zig"); -const Enemy = @import("./enemy.zig"); +const Grunt = @import("./enemy_types/grunt.zig"); +const Entity = @import("./entity.zig").Entity; +const EntityType = @import("./entity.zig").EntityType; const Player = @import("./player.zig"); const Timer = @import("./timer.zig"); const max_enemies: u8 = 255; -const Entity = union(enum) { - enemy: Enemy, -}; - const Screen = enum(u3) { title, gameplay, @@ -144,20 +142,26 @@ pub fn main() !void { for (0..game_state.enemy_count) |i| { const entity = &game_state.enemies[i]; + switch (entity.*) { - .enemy => |*e| e.handleMovement(player.position), + .grunt => |*e| { + e.handleMovement(player.position); + e.shoot(); + }, } } if (enemy_spawn_time.tick(delta)) { - game_state.spawn_enemy(.{ .enemy = spawnEnemy( + game_state.spawn_enemy(spawnEnemy( .{ .x = game_state.screen.width, .y = game_state.screen.height, }, player, difficulty, - ) }); + .grunt, + Grunt.size, + )); } raylib.BeginDrawing(); @@ -172,9 +176,9 @@ pub fn main() !void { 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]; + .grunt => |*grunt| { + for (0..grunt.bullet_pool.count) |bullet_idx| { + const bullet = grunt.bullet_pool.bullets[bullet_idx]; const is_collision = raylib.CheckCollisionCircleRec( .{ @@ -191,8 +195,8 @@ pub fn main() !void { ); if (is_collision) { - enemy.bullet_pool.despawn(bullet_idx); - player.health -= Enemy.bullet_damage; + grunt.bullet_pool.despawn(bullet_idx); + player.health -= Grunt.bullet_damage; if (player.health < 0) { game_state.screen.current = .title; @@ -210,10 +214,10 @@ pub fn main() !void { }, Bullet.radius, .{ - .x = enemy.position.x, - .y = enemy.position.y, - .width = enemy.size.x, - .height = enemy.size.y, + .x = grunt.position.x, + .y = grunt.position.y, + .width = Grunt.size.x, + .height = Grunt.size.y, }, ); @@ -233,7 +237,7 @@ pub fn main() !void { for (0..game_state.enemy_count) |idx| { const entity = &game_state.enemies[idx]; switch (entity.*) { - .enemy => |*e| e.render(), + .grunt => |*grunt| grunt.render(), } } @@ -251,43 +255,55 @@ pub fn main() !void { } } -fn spawnEnemy(screen: raylib.Vector2, player: Player, difficulty: f32) Enemy { +fn spawnEnemy( + screen: raylib.Vector2, + player: Player, + difficulty: f32, + enemy_type: EntityType, + enemy_size: raylib.Vector2, +) Entity { const min_dist: f32 = 200.0; const player_cx = player.position.x + player.size.x / 2; const player_cy = player.position.y + player.size.y / 2; - 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))); - const spawn_x = (rnd_x / 100.0) * (@as(f32, @floatCast(screen.x)) - enemy.size.x); - const spawn_y = (rnd_y / 100.0) * (@as(f32, @floatCast(screen.y)) - enemy.size.y); - - const center_x = spawn_x + enemy.size.x / 2; - const center_y = spawn_y + enemy.size.y / 2; - - const distance_between_player = raymath.Vector2Distance( - .{ .x = center_x, .y = center_y }, - .{ .x = player_cx, .y = player_cy }, - ); - - if (distance_between_player >= min_dist) { - enemy.position = .{ .x = spawn_x, .y = spawn_y }; - break; - } - } + const spawn_position: raylib.Vector2 = lbl: { + while (true) { + const rnd_x = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); + const rnd_y = @as(f32, @floatFromInt(raylib.GetRandomValue(0, 100))); + const spawn_x = (rnd_x / 100.0) * (@as(f32, @floatCast(screen.x)) - enemy_size.x); + const spawn_y = (rnd_y / 100.0) * (@as(f32, @floatCast(screen.y)) - enemy_size.y); - enemy.movement_speed = @min(160.0, 40.0 * difficulty); - enemy.bullet_speed = @min(300.0, 100.0 * difficulty); - enemy.bullet_pool.fire_timer.duration = @max(0.3, 2.0 / difficulty); + const center_x = spawn_x + enemy_size.x / 2; + const center_y = spawn_y + enemy_size.y / 2; - return enemy; + const distance_between_player = raymath.Vector2Distance( + .{ .x = center_x, .y = center_y }, + .{ .x = player_cx, .y = player_cy }, + ); + + if (distance_between_player >= min_dist) { + break :lbl .{ .x = spawn_x, .y = spawn_y }; + } + } + }; + + return switch (enemy_type) { + .grunt => lbl: { + var grunt = Grunt{ + .bullet_pool = .{ + .bullets = undefined, + .fire_timer = .{ + .duration = 2.0, + }, + }, + .position = spawn_position, + }; + + grunt.movement_speed = @min(160.0, 40.0 * difficulty); + grunt.bullet_speed = @min(300.0, 100.0 * difficulty); + grunt.bullet_pool.fire_timer.duration = @max(0.3, 2.0 / difficulty); + + break :lbl .{ .grunt = grunt }; + }, + }; }