diff --git a/assets/cubyz/ui/hud/energy.png b/assets/cubyz/ui/hud/energy.png new file mode 100644 index 00000000..360ecfbf Binary files /dev/null and b/assets/cubyz/ui/hud/energy.png differ diff --git a/assets/cubyz/ui/hud/half_energy.png b/assets/cubyz/ui/hud/half_energy.png new file mode 100644 index 00000000..69de9679 Binary files /dev/null and b/assets/cubyz/ui/hud/half_energy.png differ diff --git a/assets/cubyz/ui/hud/no_energy.png b/assets/cubyz/ui/hud/no_energy.png new file mode 100644 index 00000000..76767ee0 Binary files /dev/null and b/assets/cubyz/ui/hud/no_energy.png differ diff --git a/src/Inventory.zig b/src/Inventory.zig index b8f84ac8..a54844c3 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -486,6 +486,7 @@ pub const Command = struct { // MARK: Command create = 3, useDurability = 4, addHealth = 5, + addEnergy = 6, }; const InventoryAndSlot = struct { @@ -542,6 +543,11 @@ pub const Command = struct { // MARK: Command cause: main.game.DamageType, previous: f32, }, + addEnergy: struct { + target: ?*main.server.User, + energy: f32, + previous: f32, + }, }; const SyncOperationType = enum(u8) { @@ -550,6 +556,7 @@ pub const Command = struct { // MARK: Command useDurability = 2, health = 3, kill = 4, + energy = 5, }; const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation @@ -574,6 +581,10 @@ pub const Command = struct { // MARK: Command kill: struct { target: ?*main.server.User, }, + energy: struct { + target: ?*main.server.User, + energy: f32, + }, pub fn executeFromData(reader: *utils.BinaryReader) !void { switch(try deserialize(reader)) { @@ -617,6 +628,9 @@ pub const Command = struct { // MARK: Command .kill => { 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; }