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 <carriecapp9@gmail.com>
This commit is contained in:
Zachary Hall 2025-03-23 09:06:21 -07:00 committed by GitHub
parent ac9f00cb32
commit 20dfc6ba71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 119 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

View File

@ -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);
}

View File

@ -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};

View File

@ -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");

View File

@ -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();
}
}

View File

@ -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;
}