mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-09 03:59:53 -04:00
Added fall damage and /kill (#959)
* add player health and fall damage and /kill * less latency for taking damage * clamp health * changes * final changes maybe * return serverFailure if target not found * fix error * remove /damage
This commit is contained in:
parent
967e24a1c4
commit
818f736311
@ -267,7 +267,7 @@ pub const Sync = struct { // MARK: Sync
|
||||
defer main.stackAllocator.free(users);
|
||||
|
||||
for (users) |user| {
|
||||
if (user == source) continue;
|
||||
if (user == source and op.ignoreSource()) continue;
|
||||
main.network.Protocols.inventory.sendSyncOperation(user.conn, syncData);
|
||||
}
|
||||
}
|
||||
@ -410,6 +410,14 @@ pub const Sync = struct { // MARK: Sync
|
||||
}
|
||||
};
|
||||
|
||||
pub fn addHealth(health: f32, cause: main.game.DamageType, side: Side, id: u32) void {
|
||||
if (side == .client) {
|
||||
Sync.ClientSide.executeCommand(.{.addHealth = .{.target = id, .health = health, .cause = cause}});
|
||||
} else {
|
||||
Sync.ServerSide.executeCommand(.{.addHealth = .{.target = id, .health = health, .cause = cause}}, null);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getInventory(id: u32, side: Side, user: ?*main.server.User) ?Inventory {
|
||||
return switch(side) {
|
||||
.client => ClientSide.getInventory(id),
|
||||
@ -438,6 +446,7 @@ pub const Command = struct { // MARK: Command
|
||||
depositOrDrop = 7,
|
||||
clear = 8,
|
||||
updateBlock = 9,
|
||||
addHealth = 10,
|
||||
};
|
||||
pub const Payload = union(PayloadType) {
|
||||
open: Open,
|
||||
@ -450,6 +459,7 @@ pub const Command = struct { // MARK: Command
|
||||
depositOrDrop: DepositOrDrop,
|
||||
clear: Clear,
|
||||
updateBlock: UpdateBlock,
|
||||
addHealth: AddHealth,
|
||||
};
|
||||
|
||||
const BaseOperationType = enum(u8) {
|
||||
@ -458,6 +468,7 @@ pub const Command = struct { // MARK: Command
|
||||
delete = 2,
|
||||
create = 3,
|
||||
useDurability = 4,
|
||||
addHealth = 5,
|
||||
};
|
||||
|
||||
const InventoryAndSlot = struct {
|
||||
@ -508,12 +519,20 @@ pub const Command = struct { // MARK: Command
|
||||
durability: u31,
|
||||
previousDurability: u32 = undefined,
|
||||
},
|
||||
addHealth: struct {
|
||||
target: ?*main.server.User,
|
||||
health: f32,
|
||||
cause: main.game.DamageType,
|
||||
previous: f32
|
||||
}
|
||||
};
|
||||
|
||||
const SyncOperationType = enum(u8) {
|
||||
create = 0,
|
||||
delete = 1,
|
||||
useDurability = 2,
|
||||
health = 3,
|
||||
kill = 4,
|
||||
};
|
||||
|
||||
const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation
|
||||
@ -531,6 +550,13 @@ pub const Command = struct { // MARK: Command
|
||||
inv: InventoryAndSlot,
|
||||
durability: u32
|
||||
},
|
||||
health: struct {
|
||||
target: ?*main.server.User,
|
||||
health: f32
|
||||
},
|
||||
kill: struct {
|
||||
target: ?*main.server.User
|
||||
},
|
||||
|
||||
pub fn executeFromData(data: []const u8) !void {
|
||||
std.debug.assert(data.len >= 1);
|
||||
@ -569,6 +595,12 @@ pub const Command = struct { // MARK: Command
|
||||
}
|
||||
|
||||
durability.inv.inv.update();
|
||||
},
|
||||
.health => |health| {
|
||||
main.game.Player.super.health = std.math.clamp(main.game.Player.super.health + health.health, 0, main.game.Player.super.maxHealth);
|
||||
},
|
||||
.kill => {
|
||||
main.game.Player.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,10 +609,22 @@ pub const Command = struct { // MARK: Command
|
||||
switch (self) {
|
||||
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| {
|
||||
const out = allocator.alloc(*main.server.User, 1);
|
||||
out[0] = data.target.?;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ignoreSource(self: SyncOperation) bool {
|
||||
return switch (self) {
|
||||
.create, .delete, .useDurability, .health => true,
|
||||
.kill => false
|
||||
};
|
||||
}
|
||||
|
||||
fn deserialize(fullData: []const u8) !SyncOperation {
|
||||
if (fullData.len == 0) {
|
||||
return error.Invalid;
|
||||
@ -630,6 +674,25 @@ pub const Command = struct { // MARK: Command
|
||||
}};
|
||||
|
||||
return out;
|
||||
},
|
||||
.health => {
|
||||
if (data.len != 4) {
|
||||
return error.Invalid;
|
||||
}
|
||||
|
||||
return .{.health = .{
|
||||
.target = null,
|
||||
.health = @bitCast(std.mem.readInt(u32, data[0..4], .big))
|
||||
}};
|
||||
},
|
||||
.kill => {
|
||||
if (data.len != 0) {
|
||||
return error.Invalid;
|
||||
}
|
||||
|
||||
return .{.kill = .{
|
||||
.target = null,
|
||||
}};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,7 +720,11 @@ pub const Command = struct { // MARK: Command
|
||||
.useDurability => |durability| {
|
||||
durability.inv.write(data.addMany(8)[0..8]);
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], durability.durability, .big);
|
||||
}
|
||||
},
|
||||
.health => |health| {
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(health.health), .big);
|
||||
},
|
||||
.kill => {},
|
||||
}
|
||||
return data.toOwnedSlice();
|
||||
}
|
||||
@ -729,6 +796,9 @@ pub const Command = struct { // MARK: Command
|
||||
info.source.ref().item = info.item;
|
||||
info.item.tool.durability = info.previousDurability;
|
||||
info.source.inv.update();
|
||||
},
|
||||
.addHealth => |info| {
|
||||
main.game.Player.super.health = info.previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -737,7 +807,7 @@ pub const Command = struct { // MARK: Command
|
||||
fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, data: []const u8) void {
|
||||
for(self.baseOperations.items) |step| {
|
||||
switch(step) {
|
||||
.move, .swap, .create => {},
|
||||
.move, .swap, .create, .addHealth => {},
|
||||
.delete => |info| {
|
||||
info.item.?.deinit();
|
||||
},
|
||||
@ -854,6 +924,30 @@ pub const Command = struct { // MARK: Command
|
||||
self.executeDurabilityUseOperation(allocator, side, info.source, info.durability);
|
||||
info.source.inv.update();
|
||||
},
|
||||
.addHealth => |*info| {
|
||||
if (side == .server) {
|
||||
info.previous = info.target.?.player.health;
|
||||
|
||||
info.target.?.player.health = std.math.clamp(info.target.?.player.health + info.health, 0, info.target.?.player.maxHealth);
|
||||
|
||||
if (info.target.?.player.health <= 0) {
|
||||
info.target.?.player.health = info.target.?.player.maxHealth;
|
||||
info.cause.sendMessage(info.target.?.name);
|
||||
|
||||
self.syncOperations.append(allocator, .{.kill = .{
|
||||
.target = info.target.?
|
||||
}});
|
||||
} else {
|
||||
self.syncOperations.append(allocator, .{.health = .{
|
||||
.target = info.target.?,
|
||||
.health = info.health
|
||||
}});
|
||||
}
|
||||
} else {
|
||||
info.previous = main.game.Player.super.health;
|
||||
main.game.Player.super.health = std.math.clamp(main.game.Player.super.health + info.health, 0, main.game.Player.super.maxHealth);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.baseOperations.append(allocator, op);
|
||||
}
|
||||
@ -1497,6 +1591,56 @@ pub const Command = struct { // MARK: Command
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const AddHealth = struct { // MARK: AddHealth
|
||||
target: u32,
|
||||
health: f32,
|
||||
cause: main.game.DamageType,
|
||||
|
||||
pub fn run(self: AddHealth, allocator: NeverFailingAllocator, cmd: *Command, side: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {
|
||||
var target: ?*main.server.User = null;
|
||||
|
||||
if (side == .server) {
|
||||
const userList = main.server.getUserListAndIncreaseRefCount(main.stackAllocator);
|
||||
defer main.server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
|
||||
for (userList) |user| {
|
||||
if (user.id == self.target) {
|
||||
target = user;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null) return error.serverFailure;
|
||||
|
||||
if (target.?.gamemode.raw == .creative) return;
|
||||
} else {
|
||||
if (main.game.Player.gamemode.raw == .creative) return;
|
||||
}
|
||||
|
||||
cmd.executeBaseOperation(allocator, .{.addHealth = .{
|
||||
.target = target,
|
||||
.health = self.health,
|
||||
.cause = self.cause,
|
||||
.previous = if (side == .server) target.?.player.health else main.game.Player.super.health
|
||||
}}, side);
|
||||
}
|
||||
|
||||
fn serialize(self: AddHealth, data: *main.List(u8)) void {
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], self.target, .big);
|
||||
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(self.health), .big);
|
||||
data.append(@intFromEnum(self.cause));
|
||||
}
|
||||
|
||||
fn deserialize(data: []const u8, _: Side, _: ?*main.server.User) !AddHealth {
|
||||
if(data.len != 9) return error.Invalid;
|
||||
|
||||
return .{
|
||||
.target = std.mem.readInt(u32, data[0..4], .big),
|
||||
.health = @bitCast(std.mem.readInt(u32, data[4..8], .big)),
|
||||
.cause = @enumFromInt(data[8]),
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const SourceType = enum(u8) {
|
||||
|
34
src/game.zig
34
src/game.zig
@ -326,6 +326,20 @@ pub const collision = struct {
|
||||
|
||||
pub const Gamemode = enum(u8) { survival = 0, creative = 1 };
|
||||
|
||||
pub const DamageType = enum(u8) {
|
||||
heal = 0, // For when you are adding health
|
||||
kill = 1,
|
||||
fall = 2,
|
||||
|
||||
pub fn sendMessage(self: DamageType, name: []const u8) void {
|
||||
switch (self) {
|
||||
.heal => main.server.sendMessage("{s}§#ffffff was healed", .{name}),
|
||||
.kill => main.server.sendMessage("{s}§#ffffff was killed", .{name}),
|
||||
.fall => main.server.sendMessage("{s}§#ffffff died of fall damage", .{name}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Player = struct { // MARK: Player
|
||||
pub var super: main.server.Entity = .{};
|
||||
pub var eyePos: Vec3d = .{0, 0, 0};
|
||||
@ -341,9 +355,6 @@ pub const Player = struct { // MARK: Player
|
||||
pub var inventory: Inventory = undefined;
|
||||
pub var selectedSlot: u32 = 0;
|
||||
|
||||
pub var maxHealth: f32 = 8;
|
||||
pub var health: f32 = 4.5;
|
||||
|
||||
pub var onGround: bool = false;
|
||||
pub var jumpCooldown: f64 = 0;
|
||||
const jumpCooldownConstant = 0.3;
|
||||
@ -442,6 +453,17 @@ pub const Player = struct { // MARK: Player
|
||||
inventory.placeBlock(selectedSlot);
|
||||
}
|
||||
|
||||
pub fn kill() void {
|
||||
Player.super.pos = world.?.spawn;
|
||||
Player.super.vel = .{0, 0, 0};
|
||||
|
||||
Player.super.health = Player.super.maxHealth;
|
||||
|
||||
Player.eyeVel = .{0, 0, 0};
|
||||
Player.eyeCoyote = 0;
|
||||
Player.eyeStep = .{false, false, false};
|
||||
}
|
||||
|
||||
pub fn breakBlock(deltaTime: f64) void {
|
||||
if(!main.Window.grabbed) return;
|
||||
inventory.breakBlock(selectedSlot, deltaTime);
|
||||
@ -955,6 +977,12 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
||||
} else {
|
||||
Player.super.pos[2] = box.min[2] - hitBox.max[2];
|
||||
}
|
||||
|
||||
const damage: f32 = @floatCast(@round(@max((Player.super.vel[2] * Player.super.vel[2]) / (2 * gravity) - 3, 0)) / 2);
|
||||
if (damage > 0.01) {
|
||||
Inventory.Sync.addHealth(-damage, .fall, .client, Player.id);
|
||||
}
|
||||
|
||||
Player.super.vel[2] = 0;
|
||||
|
||||
// Always unstuck upwards for now
|
||||
|
@ -47,14 +47,14 @@ pub fn render() void {
|
||||
var y: f32 = 0;
|
||||
var x: f32 = 0;
|
||||
var health: f32 = 0;
|
||||
while(health < main.game.Player.maxHealth) : (health += 1) {
|
||||
while(health < main.game.Player.super.maxHealth) : (health += 1) {
|
||||
if(x >= window.contentSize[0]) {
|
||||
x = 0;
|
||||
y += 20;
|
||||
}
|
||||
if(health + 1 <= main.game.Player.health) {
|
||||
if(health + 1 <= main.game.Player.super.health) {
|
||||
heartTexture.bindTo(0);
|
||||
} else if(health + 0.5 <= main.game.Player.health) {
|
||||
} else if(health + 0.5 <= main.game.Player.super.health) {
|
||||
halfHeartTexture.bindTo(0);
|
||||
} else {
|
||||
deadHeartTexture.bindTo(0);
|
||||
|
@ -10,13 +10,17 @@ const NeverFailingAllocator = main.utils.NeverFailingAllocator;
|
||||
pos: Vec3d = .{0, 0, 0},
|
||||
vel: Vec3d = .{0, 0, 0},
|
||||
rot: Vec3f = .{0, 0, 0},
|
||||
// TODO: Health and hunger
|
||||
|
||||
health: f32 = 8,
|
||||
maxHealth: f32 = 8,
|
||||
// TODO: Hunger
|
||||
// TODO: Name
|
||||
|
||||
pub fn loadFrom(self: *@This(), zon: ZonElement) void {
|
||||
self.pos = zon.get(Vec3d, "position", .{0, 0, 0});
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement {
|
||||
@ -24,5 +28,6 @@ pub fn save(self: *@This(), allocator: NeverFailingAllocator) ZonElement {
|
||||
zon.put("position", self.pos);
|
||||
zon.put("velocity", self.vel);
|
||||
zon.put("rotation", self.rot);
|
||||
zon.put("health", self.health);
|
||||
return zon;
|
||||
}
|
||||
|
@ -4,5 +4,6 @@ pub const clear = @import("clear.zig");
|
||||
pub const gamemode = @import("gamemode.zig");
|
||||
pub const help = @import("help.zig");
|
||||
pub const invite = @import("invite.zig");
|
||||
pub const kill = @import("kill.zig");
|
||||
pub const time = @import("time.zig");
|
||||
pub const tp = @import("tp.zig");
|
15
src/server/command/kill.zig
Normal file
15
src/server/command/kill.zig
Normal file
@ -0,0 +1,15 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const User = main.server.User;
|
||||
|
||||
pub const description = "Kills the player";
|
||||
pub const usage = "/kill";
|
||||
|
||||
pub fn execute(args: []const u8, source: *User) void {
|
||||
if(args.len != 0) {
|
||||
source.sendMessage("#ff0000Too many arguments for command /kill. Expected no arguments.", .{});
|
||||
return;
|
||||
}
|
||||
main.items.Inventory.Sync.addHealth(-std.math.floatMax(f32), .kill, .server, source.id);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user