diff --git a/assets/font/datcub/DatCub-Bold.ttf b/assets/font/datcub/DatCub-Bold.ttf new file mode 100644 index 0000000..efb1f3d Binary files /dev/null and b/assets/font/datcub/DatCub-Bold.ttf differ diff --git a/assets/font/datcub/DatCub-Light.ttf b/assets/font/datcub/DatCub-Light.ttf new file mode 100644 index 0000000..bb48db8 Binary files /dev/null and b/assets/font/datcub/DatCub-Light.ttf differ diff --git a/assets/font/datcub/DatCub-Regular.ttf b/assets/font/datcub/DatCub-Regular.ttf new file mode 100644 index 0000000..da7e791 Binary files /dev/null and b/assets/font/datcub/DatCub-Regular.ttf differ diff --git a/assets/font/datcub/License.txt b/assets/font/datcub/License.txt new file mode 100644 index 0000000..ce69f05 --- /dev/null +++ b/assets/font/datcub/License.txt @@ -0,0 +1,92 @@ +Copyright © 2021-2024 GGBotNet (https://ggbot.net), with Reserved Font Name "DatCub". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/font/pixeloid/License.txt b/assets/font/pixeloid/License.txt new file mode 100644 index 0000000..9a26398 --- /dev/null +++ b/assets/font/pixeloid/License.txt @@ -0,0 +1,92 @@ +Copyright © 2020-2025 GGBotNet (https://ggbot.net/fonts/), with Reserved Font Name "Pixeloid". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/font/pixeloid/PixeloidMono.ttf b/assets/font/pixeloid/PixeloidMono.ttf new file mode 100644 index 0000000..1b024e6 Binary files /dev/null and b/assets/font/pixeloid/PixeloidMono.ttf differ diff --git a/assets/font/pixeloid/PixeloidSans-Bold.ttf b/assets/font/pixeloid/PixeloidSans-Bold.ttf new file mode 100644 index 0000000..e1f5a88 Binary files /dev/null and b/assets/font/pixeloid/PixeloidSans-Bold.ttf differ diff --git a/assets/font/pixeloid/PixeloidSans.ttf b/assets/font/pixeloid/PixeloidSans.ttf new file mode 100644 index 0000000..2469e5b Binary files /dev/null and b/assets/font/pixeloid/PixeloidSans.ttf differ diff --git a/assets/font/quinquefive/License.txt b/assets/font/quinquefive/License.txt new file mode 100644 index 0000000..5efec84 --- /dev/null +++ b/assets/font/quinquefive/License.txt @@ -0,0 +1,92 @@ +Copyright © 2019-2025 GGBotNet (https://ggbot.net), with Reserved Font Name "QuinqueFive". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/font/quinquefive/QuinqueFive.ttf b/assets/font/quinquefive/QuinqueFive.ttf new file mode 100644 index 0000000..d918f72 Binary files /dev/null and b/assets/font/quinquefive/QuinqueFive.ttf differ diff --git a/build.zig b/build.zig index a27137c..ea0663a 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,8 @@ pub fn build(b: *std.Build) !void { "tranquil_tunnels_transparent.png", "Prototype_Character/Default/idle.png", "Prototype_Character/Default/walk.png", - "Prototype_Character/Default/death.png" + "Prototype_Character/Default/death.png", + "font/pixeloid/PixeloidSans.ttf" // "sokoban/sokoban_tilesheet.png", // "roboto-font/Roboto-Regular.ttf", // "tiled/map.tmx", @@ -130,7 +131,7 @@ pub fn build(b: *std.Build) !void { // TODO: Create a zip archive of all of the files. Would be useful for easier itch.io upload } else { const exe = buildNative(b, .{ - .name = "sokol_template_v2", + .name = "feed_the_eternal_fire", .root_module = exe_mod }); b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index bf65b84..70b94e5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ - .name = .sokol_template_v2, + .name = .feed_the_eternal_fire, .version = "0.0.0", - .fingerprint = 0xf440b4e6e51f5206, // Changing this has security and trust implications. + .fingerprint = 0x6d4902eeaed719f, // Changing this has security and trust implications. .minimum_zig_version = "0.16.0", .dependencies = .{ .sokol = .{ diff --git a/src/app.zig b/src/app.zig index e79e325..2a89bc9 100644 --- a/src/app.zig +++ b/src/app.zig @@ -2,6 +2,7 @@ const std = @import("std"); const CLI = @import("./cli.zig"); const log = std.log.scoped(.app); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const Math = @import("math"); const Vec2 = Math.Vec2; @@ -22,17 +23,20 @@ const App = @This(); const Prng = std.Random.DefaultPrng; gpa: Allocator, -camera_pos: Vec2 = .init(0, 0), +camera_pos: Vec2, prng: Prng, -minions: MinionSlotMap, +heat: u64 = 0, +entities: EntitySlotMap, + +font: Gfx.Font.Id, idle_animations: AnimationDirections, walk_animations: AnimationDirections, death_animations: AnimationDirections, -const MinionSlotMap = SlotMapType(u16, u16, Minion); -const MinionId = MinionSlotMap.Id; +const EntitySlotMap = SlotMapType(u16, u16, Entity); +const EntityId = EntitySlotMap.Id; const Tilesheet = struct { image: Gfx.ImageData, @@ -141,12 +145,51 @@ const AnimationDirection = enum { down }; -const Minion = struct { - pos: Vec2, - animation: AnimationName, - animation_dir: AnimationDirection, - animation_index: u32, - animation_timer: Platform.Nanoseconds, +const Entity = struct { + type: Type = .nil, + flags: Flags = .{}, + + minion_state: MinionState = .chop, + minion_target_station: ?EntityId = null, + work_progress: f32 = 0, + minion_chop_speed: f32 = 1, + minion_burn_speed: f32 = 1, + + station_subtype: StationSubtype = .chop, + station_occupant: ?EntityId = null, + + pos: Vec2 = .init(0, 0), + vel: Vec2 = .init(0, 0), + acc: Vec2 = .init(0, 0), + max_velocity: f32 = 0, + friction: f32 = 0, + max_walk_speed: f32 = 0, + + animation: AnimationName = .idle, + animation_dir: AnimationDirection = .down, + animation_index: u32 = 0, + animation_timer: Platform.Nanoseconds = 0, + + const MinionState = enum { + chop, + burn + }; + + const Type = enum { + nil, + minion, + station, + }; + + const StationSubtype = enum { + chop, + burn + }; + + const Flags = packed struct { + has_kinematics: bool = false, + has_animation: bool = false + }; }; fn initImageDataFromSTB(img: STBImage) Gfx.ImageData { @@ -158,8 +201,11 @@ fn initImageDataFromSTB(img: STBImage) Gfx.ImageData { } pub fn init(self: *App, plt: Platform.Init) !?u8 { - // const robot_font = Gfx.initFont(); - // Gfx.setFont(robot_font, plt.assets.readFile("roboto-font/Roboto-Regular.ttf")); + const font = Gfx.initFont(); + // Gfx.setFont(font, plt.assets.readFile("font/quinquefive/QuinqueFive.ttf")); + // Gfx.setFont(font, plt.assets.readFile("font/pixeloid/PixeloidMono.ttf")); + Gfx.setFont(font, plt.assets.readFile("font/pixeloid/PixeloidSans.ttf")); + // Gfx.setFont(font, plt.assets.readFile("font/pixeloid/PixeloidSans-Bold.ttf")); const idle_sheet = try Tilesheet.init( plt.arena, @@ -197,28 +243,107 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { .idle_animations = try AnimationDirections.init(plt.arena, idle_sheet, ns_per_s / idle_fps, 2), .walk_animations = try AnimationDirections.init(plt.arena, walk_sheet, ns_per_s / walk_fps, 4), .death_animations = try AnimationDirections.init(plt.arena, death_sheet, ns_per_s / death_fps, 3), - .minions = .init(.empty), - .prng = Prng.init(@truncate(@as(u96, @intCast(now.nanoseconds)))) - }; + .entities = .init(.empty), + .prng = Prng.init(@truncate(@as(u96, @intCast(now.nanoseconds)))), + .camera_pos = .init(0, 0), + .font = font + }; try self.spawnMinion(.init(0, 0)); - try self.spawnMinion(.init(32, 0)); + // try self.spawnMinion(.init(32, 0)); + + try self.spawnStation(-100, .chop); + try self.spawnStation(-150, .chop); + try self.spawnStation(100, .burn); + try self.spawnStation(150, .burn); return null; } pub fn spawnMinion(self: *App, pos: Vec2) !void { - const minion_id = try self.minions.insert(self.gpa); - const minion = self.minions.getAssumeExists(minion_id); - minion.* = Minion{ + const minion_id = try self.entities.insert(self.gpa); + const minion = self.entities.getAssumeExists(minion_id); + minion.* = Entity{ + .type = .minion, + .flags = .{ + .has_animation = true, + .has_kinematics = true + }, + .minion_state = .chop, + .pos = pos, - .animation = .death, - .animation_dir = .right, - .animation_index = 0, - .animation_timer = 0 + .friction = 0.998, + .max_velocity = 50, + .max_walk_speed = 300, + + .animation = .idle, }; } +pub fn spawnStation(self: *App, pos: f32, subtype: Entity.StationSubtype) !void { + const id = try self.entities.insert(self.gpa); + const station = self.entities.getAssumeExists(id); + station.* = Entity{ + .type = .station, + .station_subtype = subtype, + + .pos = .init(pos, 0), + }; +} + +fn getNearestFreeStation(self: *App, from: Vec2, subtype: Entity.StationSubtype) ?EntityId { + var result: ?EntityId = null; + var result_distance: f32 = 0; + + var station_iter = self.entities.iterator(); + while (station_iter.next()) |id| { + const station = self.entities.getAssumeExists(id); + if (station.type != .station) { + continue; + } + if (station.station_subtype != subtype) { + continue; + } + if (station.station_occupant != null) { + continue; + } + + const distance = station.pos.distanceSqr(from); + + if (result == null or result_distance > distance) { + result = id; + result_distance = distance; + } + } + + return result; +} + +fn bindEntityToStation(self: *App, other_id: EntityId, station_id: EntityId) void { + const station = self.entities.get(station_id) orelse return; + assert(station.type == .station); + + const other = self.entities.get(other_id) orelse return; + assert(other.type != .station); + + station.station_occupant = other_id; + other.minion_target_station = station_id; +} + +fn unbindEntityFromStation(self: *App, station_id: EntityId) void { + const station = self.entities.get(station_id) orelse return; + if (station.type != .station) { + return; + } + + if (station.station_occupant) |occupant_id| { + station.station_occupant = null; + if (self.entities.get(occupant_id)) |occupant| { + occupant.minion_target_station = null; + } + } +} + pub fn frame(self: *App, plt: Platform.Frame) !void { const input = plt.input; _ = input; // autofix @@ -242,55 +367,205 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { Gfx.setClearColor(.rgb(40, 40, 40)); Gfx.drawLine(.init(-800, 0), .init(800, 0), .white, 5); - const character_size = Vec2.init(64, 64); + var station_iter = self.entities.iterator(); + while (station_iter.next()) |minion_id| { + const station = self.entities.getAssumeExists(minion_id); + if (station.type != .station) { + continue; + } - var minion_iter = self.minions.iterator(); - while (minion_iter.next()) |minion_id| { - const minion = self.minions.getAssumeExists(minion_id); - - const animations = switch (minion.animation) { - .idle => self.idle_animations, - .walk => self.walk_animations, - .death => self.death_animations - }; - var flip_animation = false; - const animation = switch (minion.animation_dir) { - .right => animations.right, - .up => animations.up, - .down => animations.down, - .left => blk: { - flip_animation = true; - break :blk animations.right; + if (station.station_occupant) |station_occupant| { + if (!self.entities.exists(station_occupant)) { + station.station_occupant = null; } - }; - - minion.animation_timer += plt.dt; - while (minion.animation_timer >= animation.frame_duration) { - minion.animation_timer -= animation.frame_duration; - minion.animation_index += 1; } - - minion.animation_index = minion.animation_index % @as(u32, @intCast(animation.frames.len)); - const sprite = animation.frames[minion.animation_index]; - - var size = character_size; - var pos = minion.pos; - pos.y -= character_size.y*0.7; - if (flip_animation) { - size.x *= -1; - pos.x += character_size.x/2; - } else { - pos.x -= character_size.x/2; - } - - Gfx.drawRectangle( - minion.pos.sub(.init(5, 5)), - .init(10, 10), - .white - ); - Gfx.drawSprite(sprite, pos, size, .white); } + const character_size = Vec2.init(96, 96); + + var minion_iter = self.entities.iterator(); + while (minion_iter.next()) |minion_id| { + const minion = self.entities.getAssumeExists(minion_id); + if (minion.type != .minion) { + continue; + } + + if (minion.minion_target_station) |minion_target_station| { + if (!self.entities.exists(minion_target_station)) { + minion.minion_target_station = null; + } + } + + const goal_distance_threshold = 10; + var walk_goal: ?Vec2 = null; + var station_goal: ?Entity.StationSubtype = null; + + var is_at_station = false; + if (minion.minion_target_station) |station_id| { + const station = self.entities.getAssumeExists(station_id); + if (station.pos.distance(minion.pos) < goal_distance_threshold) { + is_at_station = true; + } + } + + if (!is_at_station) { + minion.work_progress = 0; + } + + switch (minion.minion_state) { + .chop => { + station_goal = .chop; + + if (is_at_station) { + minion.work_progress += minion.minion_chop_speed * dt; + } + + minion.work_progress = std.math.clamp(minion.work_progress, 0, 1); + if (minion.work_progress == 1) { + minion.minion_state = .burn; + minion.work_progress = 0; + } + }, + .burn => { + station_goal = .burn; + + if (is_at_station) { + minion.work_progress += minion.minion_burn_speed * dt; + } + + minion.work_progress = std.math.clamp(minion.work_progress, 0, 1); + if (minion.work_progress == 1) { + minion.minion_state = .chop; + minion.work_progress = 0; + self.heat += 1; + } + }, + } + + if (station_goal) |station_type| { + if (minion.minion_target_station) |station_id| { + const station = self.entities.getAssumeExists(station_id); + if (station.station_subtype != station_type) { + self.unbindEntityFromStation(station_id); + minion.minion_target_station = null; + } + } + + if (minion.minion_target_station == null) { + if (self.getNearestFreeStation(minion.pos, station_type)) |station_id| { + self.bindEntityToStation(minion_id, station_id); + } + } + + if (minion.minion_target_station) |station_id| { + const station = self.entities.getAssumeExists(station_id); + walk_goal = station.pos; + } + } + + if (walk_goal) |pos| { + const diff = minion.pos.x - pos.x; + if (diff < -goal_distance_threshold) { + minion.acc = .init(minion.max_walk_speed, 0); + } else if (diff > goal_distance_threshold) { + minion.acc = .init(-minion.max_walk_speed, 0); + } + } + + if (minion.acc.x > 0) { + minion.animation = .walk; + minion.animation_dir = .right; + } else if (minion.acc.x < 0) { + minion.animation = .walk; + minion.animation_dir = .left; + } else { + minion.animation = .idle; + minion.animation_dir = .down; + } + } + + var entity_iter = self.entities.iterator(); + while (entity_iter.next()) |id| { + const e = self.entities.getAssumeExists(id); + + if (e.flags.has_kinematics) { + e.vel = e.vel.add(e.acc.multiplyScalar(dt)); + e.vel = e.vel.limitLength(e.max_velocity); + e.pos = e.pos.add(e.vel.multiplyScalar(dt)); + + const friction_force = std.math.pow(f32, 1 - e.friction, dt); + e.vel = e.vel.multiplyScalar(friction_force); + + e.acc = .init(0, 0); + } + + if (e.flags.has_animation) { + const animations = switch (e.animation) { + .idle => self.idle_animations, + .walk => self.walk_animations, + .death => self.death_animations + }; + var flip_animation = false; + const animation = switch (e.animation_dir) { + .right => animations.right, + .up => animations.up, + .down => animations.down, + .left => blk: { + flip_animation = true; + break :blk animations.right; + } + }; + + e.animation_timer += plt.dt; + while (e.animation_timer >= animation.frame_duration) { + e.animation_timer -= animation.frame_duration; + e.animation_index += 1; + } + + e.animation_index = e.animation_index % @as(u32, @intCast(animation.frames.len)); + const sprite = animation.frames[e.animation_index]; + + var size = character_size; + var pos = e.pos; + pos.y -= character_size.y*0.7; + if (flip_animation) { + size.x *= -1; + pos.x += character_size.x/2; + } else { + pos.x -= character_size.x/2; + } + + Gfx.drawRectangle( + e.pos.sub(.init(5, 5)), + .init(10, 10), + .white + ); + Gfx.drawSprite(sprite, pos, size, .white); + + const is_working = e.minion_state == .chop or e.minion_state == .burn; + if (e.type == .minion and is_working and e.work_progress > 0) { + const progressbar_size = 100; + + const from = e.pos.add(.init(-progressbar_size/2, 20)); + Gfx.drawLine(from, from.add(.init(progressbar_size * e.work_progress, 0)), .rgb(0, 200, 20), 10); + } + } + + if (e.type == .station) { + if (e.station_subtype == .chop) { + Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(20, 200, 20)); + } else { + Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20)); + } + } + } + + Gfx.drawText(self.font, .{ + .height = 32, + .pos = .init(-50, -200), + .text = try std.fmt.allocPrint(plt.arena, "Heat: {}", .{self.heat}), + }); + if (ImGUI.beginWindow(.{ .name = "Debug", .size = .init(200, 100) })) { defer ImGUI.endWindow(); @@ -301,6 +576,6 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { } pub fn deinit(self: *App, plt: Platform.Deinit) void { - self.minions.deinit(self.gpa); + self.entities.deinit(self.gpa); _ = plt; // autofix } diff --git a/src/graphics.zig b/src/graphics.zig index d513ac6..8f1085f 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1313,13 +1313,16 @@ pub fn drawText(id: Font.Id, opts: DrawTextOptions) void { current_point.x += @as(f32, @floatFromInt(kerning)) * scale; } + const hmetrics = stb_font.getGlyphHMetrics(glyph_index); + const glyph_size = Vec2.initFromInt(i32, glyph.box.x1 - glyph.box.x0, glyph.box.y1 - glyph.box.y0); if (glyph.sprite != self.nil_sprite) { - const glyph_size = Vec2.initFromInt(i32, glyph.box.x1 - glyph.box.x0, glyph.box.y1 - glyph.box.y0); - const glyph_pos = current_point.sub(.initFromInt(i32, glyph.box.x0, -glyph.box.y0)); - drawSprite(glyph.sprite, glyph_pos, glyph_size, opts.color); + var draw_pos = current_point; + draw_pos.x -= @floatFromInt(glyph.box.x0); + draw_pos.x += @as(f32, @floatFromInt(hmetrics.left_side_bearing)) * scale; + draw_pos.y += @floatFromInt(glyph.box.y0); + drawSprite(glyph.sprite, draw_pos, glyph_size, opts.color); } - const hmetrics = stb_font.getGlyphHMetrics(glyph_index); current_point.x += @as(f32, @floatFromInt(hmetrics.advance_width)) * scale; prev_glyph = glyph_index; }