From 20dfc6ba7138cf3febf8cc45ca536ca6a277f23c Mon Sep 17 00:00:00 2001 From: Zachary Hall Date: Sun, 23 Mar 2025 09:06:21 -0700 Subject: [PATCH] Add hunger, hunger bar, and related functions on the Player struct. (#1210) * Add hunger and hunger bar - New functions added to Player struct in game.zig to use and heal hunger. - If an attempt is made to use more hunger than there is available, the player is killed. - Hunger bar uses placeholder icons. - Hunger bar is based on the health bar - Hunger bar is anchored to the left of the hotbar, rather than the right like the health bar. * Don't kill player when hunger runs out. - Don't use hunger when there's not enough to use - Allow for code to know if hunger was used, so that is can handle not having enough hunger. * New hunger textures * Desaturate the highlight a little * Return true if hunger was already true. * Fix formatting * Rename hunger to energy, and fix other issues. * Make energy synchronized between client and server Also removes the functions that add/remove energy on the game.Player struct * Run formatter * Fix incorrect formatting by formatter. * Remove AddHunger struct and addHunger function - Keep base operation so things like accessories and food can use it when syncing * Fix formatting again. --------- Co-authored-by: Carrie --- assets/cubyz/ui/hud/energy.png | Bin 0 -> 237 bytes assets/cubyz/ui/hud/half_energy.png | Bin 0 -> 250 bytes assets/cubyz/ui/hud/no_energy.png | Bin 0 -> 150 bytes src/Inventory.zig | 46 ++++++++++++++++-- src/game.zig | 1 + src/gui/windows/_windowlist.zig | 1 + src/gui/windows/energybar.zig | 70 ++++++++++++++++++++++++++++ src/server/Entity.zig | 5 +- 8 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 assets/cubyz/ui/hud/energy.png create mode 100644 assets/cubyz/ui/hud/half_energy.png create mode 100644 assets/cubyz/ui/hud/no_energy.png create mode 100644 src/gui/windows/energybar.zig diff --git a/assets/cubyz/ui/hud/energy.png b/assets/cubyz/ui/hud/energy.png new file mode 100644 index 0000000000000000000000000000000000000000..360ecfbf30bde5440530cea703c2c029093e305d GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4ZSiz*43U_coN|D%$XDY3;Tx0B z|4C0dv~)saW22(>Magq%2U91tc1d(uDDN^A(@NqAdc?tcKF-MCjNQL!jjGpMKJEv~ zHcwou7xU@p+S5EBDVyIv`VYGFaweK?;?&QJO|vL|E#K9tk=HV_iD$`=nG$L8^`{FS zJd}5vb~62+v5~=^U-Bus$3DDyY~Qx_`n|;(b{{_QE4Sr;f8PDSd_wxq^9`&ES|0CO iyne&V!~`H5P*apVg9Wbm z)#>!>JDnqlIi=M2tv%1{CgMN<$8kPd#(nyR07Nt<3IGH_fK*D2(|Xi-n>#(SpmJ~E z0|>V&Os$lr(x?D%(rZ@w@d0O>EBSicmjJn3l9OI@INOnhlmT9?<#1<uiH;;dl#~ zZtoVn*oVS0+^R71e%3UL()_dmtQ{xI%RdHyCsWBg!li=SasU7T07*qoM6N<$f^=eR A9RL6T literal 0 HcmV?d00001 diff --git a/assets/cubyz/ui/hud/no_energy.png b/assets/cubyz/ui/hud/no_energy.png new file mode 100644 index 0000000000000000000000000000000000000000..76767ee06a99207b8096930f640171aa6f903d44 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4#d^9phDc1E+JBJqfB_G)zN&TQ z)CY{NVHd;xnk-%7xv+|}#-k{4@tHS=y|a$gvgx&X ye^_t11?@I>W7!tJUj*MMGT& { main.game.Player.kill(); }, + .energy => |energy| { + main.game.Player.super.energy = std.math.clamp(main.game.Player.super.energy + energy.energy, 0, main.game.Player.super.maxEnergy); + }, } } @@ -625,7 +639,7 @@ pub const Command = struct { // MARK: Command inline .create, .delete, .useDurability => |data| { return allocator.dupe(*main.server.User, Sync.ServerSide.inventories.items[data.inv.inv.id].users.items); }, - inline .health, .kill => |data| { + inline .health, .kill, .energy => |data| { const out = allocator.alloc(*main.server.User, 1); out[0] = data.target.?; return out; @@ -635,7 +649,7 @@ pub const Command = struct { // MARK: Command pub fn ignoreSource(self: SyncOperation) bool { return switch(self) { - .create, .delete, .useDurability, .health => true, + .create, .delete, .useDurability, .health, .energy => true, .kill => false, }; } @@ -686,6 +700,12 @@ pub const Command = struct { // MARK: Command .target = null, }}; }, + .energy => { + return .{.energy = .{ + .target = null, + .energy = @bitCast(try reader.readInt(u32)), + }}; + }, } } @@ -717,6 +737,9 @@ pub const Command = struct { // MARK: Command writer.writeInt(u32, @bitCast(health.health)); }, .kill => {}, + .energy => |energy| { + writer.writeInt(u32, @bitCast(energy.energy)); + }, } return writer.data.toOwnedSlice(); } @@ -793,6 +816,9 @@ pub const Command = struct { // MARK: Command .addHealth => |info| { main.game.Player.super.health = info.previous; }, + .addEnergy => |info| { + main.game.Player.super.energy = info.previous; + }, } } } @@ -800,7 +826,7 @@ pub const Command = struct { // MARK: Command fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, reader: *utils.BinaryReader) !void { for(self.baseOperations.items) |step| { switch(step) { - .move, .swap, .create, .addHealth => {}, + .move, .swap, .create, .addHealth, .addEnergy => {}, .delete => |info| { info.item.?.deinit(); }, @@ -941,6 +967,20 @@ pub const Command = struct { // MARK: Command main.game.Player.super.health = std.math.clamp(main.game.Player.super.health + info.health, 0, main.game.Player.super.maxHealth); } }, + .addEnergy => |*info| { + if(side == .server) { + info.previous = info.target.?.player.energy; + + info.target.?.player.energy = std.math.clamp(info.target.?.player.energy + info.energy, 0, info.target.?.player.maxEnergy); + self.syncOperations.append(allocator, .{.energy = .{ + .target = info.target.?, + .energy = info.energy, + }}); + } else { + info.previous = main.game.Player.super.energy; + main.game.Player.super.energy = std.math.clamp(main.game.Player.super.energy + info.energy, 0, main.game.Player.super.maxEnergy); + } + }, } self.baseOperations.append(allocator, op); } diff --git a/src/game.zig b/src/game.zig index 16ed353e..c36ac3fe 100644 --- a/src/game.zig +++ b/src/game.zig @@ -584,6 +584,7 @@ pub const Player = struct { // MARK: Player Player.super.vel = .{0, 0, 0}; Player.super.health = Player.super.maxHealth; + Player.super.energy = Player.super.maxEnergy; Player.eyePos = .{0, 0, 0}; Player.eyeVel = .{0, 0, 0}; diff --git a/src/gui/windows/_windowlist.zig b/src/gui/windows/_windowlist.zig index 96c634d3..e341da7b 100644 --- a/src/gui/windows/_windowlist.zig +++ b/src/gui/windows/_windowlist.zig @@ -9,6 +9,7 @@ pub const debug_network_advanced = @import("debug_network_advanced.zig"); pub const debug = @import("debug.zig"); pub const delete_world_confirmation = @import("delete_world_confirmation.zig"); pub const download_controller_mappings = @import("download_controller_mappings.zig"); +pub const energybar = @import("energybar.zig"); pub const error_prompt = @import("error_prompt.zig"); pub const gpu_performance_measuring = @import("gpu_performance_measuring.zig"); pub const graphics = @import("graphics.zig"); diff --git a/src/gui/windows/energybar.zig b/src/gui/windows/energybar.zig new file mode 100644 index 00000000..526d1c7e --- /dev/null +++ b/src/gui/windows/energybar.zig @@ -0,0 +1,70 @@ +const std = @import("std"); + +const main = @import("root"); +const graphics = main.graphics; +const draw = graphics.draw; +const Texture = graphics.Texture; +const Vec2f = main.vec.Vec2f; + +const gui = @import("../gui.zig"); +const GuiWindow = gui.GuiWindow; +const GuiComponent = gui.GuiComponent; + +const hotbar = @import("hotbar.zig"); + +pub var window = GuiWindow{ + .scale = 0.5, + .relativePosition = .{ + .{.attachedToWindow = .{.reference = &hotbar.window, .selfAttachmentPoint = .lower, .otherAttachmentPoint = .lower}}, + .{.attachedToWindow = .{.reference = &hotbar.window, .selfAttachmentPoint = .upper, .otherAttachmentPoint = .lower}}, + }, + .contentSize = Vec2f{160, 20}, + .isHud = true, + .showTitleBar = false, + .hasBackground = false, + .hideIfMouseIsGrabbed = false, + .closeable = false, +}; + +var energyTexture: Texture = undefined; +var halfEnergyTexture: Texture = undefined; +var noEnergyTexture: Texture = undefined; + +pub fn init() void { + energyTexture = Texture.initFromFile("assets/cubyz/ui/hud/energy.png"); + halfEnergyTexture = Texture.initFromFile("assets/cubyz/ui/hud/half_energy.png"); + noEnergyTexture = Texture.initFromFile("assets/cubyz/ui/hud/no_energy.png"); +} + +pub fn deinit() void { + energyTexture.deinit(); + halfEnergyTexture.deinit(); + noEnergyTexture.deinit(); +} + +pub fn render() void { + draw.setColor(0xffffffff); + var y: f32 = 0; + var x: f32 = 0; + var energy: f32 = 0; + while(energy < main.game.Player.super.maxEnergy) : (energy += 1) { + if(x >= window.contentSize[0]) { + x = 0; + y += 20; + } + if(energy + 1 <= main.game.Player.super.energy) { + energyTexture.bindTo(0); + } else if(energy + 0.5 <= main.game.Player.super.energy) { + halfEnergyTexture.bindTo(0); + } else { + noEnergyTexture.bindTo(0); + } + draw.boundImage(Vec2f{x, window.contentSize[1] - y - 20}, .{20, 20}); + x += 20; + } + y += 20; + if(y != window.contentSize[1]) { + window.contentSize[1] = y; + gui.updateWindowPositions(); + } +} diff --git a/src/server/Entity.zig b/src/server/Entity.zig index 0f7c2a66..5f0cac30 100644 --- a/src/server/Entity.zig +++ b/src/server/Entity.zig @@ -13,7 +13,8 @@ rot: Vec3f = .{0, 0, 0}, health: f32 = 8, maxHealth: f32 = 8, -// TODO: Hunger +energy: f32 = 8, +maxEnergy: f32 = 8, // TODO: Name pub fn loadFrom(self: *@This(), zon: ZonElement) void { @@ -21,6 +22,7 @@ pub fn loadFrom(self: *@This(), zon: ZonElement) void { self.vel = zon.get(Vec3d, "velocity", .{0, 0, 0}); self.rot = zon.get(Vec3f, "rotation", .{0, 0, 0}); self.health = zon.get(f32, "health", self.maxHealth); + self.energy = zon.get(f32, "energy", self.maxEnergy); } pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement { @@ -29,5 +31,6 @@ pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement { zon.put("velocity", self.vel); zon.put("rotation", self.rot); zon.put("health", self.health); + zon.put("energy", self.energy); return zon; }