Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions assets/cubyz/blocks/chain.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.{
.tags = .{.mineable},
.blockHealth = 2.5,
.drops = .{
.{.items = .{.auto}},
},
.item = .{
.texture = "chain.png",
},
.rotation = "cubyz:direction",
.model = "cubyz:cross",
.transparent = true,
.collide = false,
.climbable = true,
.climbingSpeed = 170,
.texture = "cubyz:chain",
}
17 changes: 17 additions & 0 deletions assets/cubyz/blocks/rope.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.{
.tags = .{.mineable},
.blockHealth = 2.5,
.item = .{
.texture = "rope.png",
},
.drops = .{
.{.items = .{.auto}},
},
.rotation = "cubyz:direction",
.model = "cubyz:cross",
.transparent = true,
.collide = false,
.climbable = true,
.climbingSpeed = 100,
.texture = "cubyz:rope",
}
Binary file added assets/cubyz/blocks/textures/chain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cubyz/blocks/textures/rope.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cubyz/items/textures/chain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cubyz/items/textures/rope.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion src/blocks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ var _bounciness: [maxBlockCount]f32 = undefined;
var _density: [maxBlockCount]f32 = undefined;
var _terminalVelocity: [maxBlockCount]f32 = undefined;
var _mobility: [maxBlockCount]f32 = undefined;
var _climbable: [maxBlockCount]bool = undefined;
var _climbingSpeed: [maxBlockCount]f32 = undefined;

var _allowOres: [maxBlockCount]bool = undefined;
var _onTick: [maxBlockCount]ServerBlockCallback = undefined;
Expand Down Expand Up @@ -134,7 +136,8 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 {
_terminalVelocity[size] = zon.get(f32, "terminalVelocity", 90);
_mobility[size] = zon.get(f32, "mobility", 1.0);
_allowOres[size] = zon.get(bool, "allowOres", false);

_climbable[size] = zon.get(bool, "climbable", false);
_climbingSpeed[size] = zon.get(f32, "climbingSpeed", 1.0);
_blockEntity[size] = block_entity.getByID(zon.get(?[]const u8, "blockEntity", null));

const oreProperties = zon.getChild("ore");
Expand Down Expand Up @@ -437,6 +440,14 @@ pub const Block = packed struct { // MARK: Block
return _opaqueVariant[self.typ];
}

pub inline fn climbable(self: Block) bool {
return _climbable[self.typ];
}

pub inline fn climbingSpeed(self: Block) f32 {
return _climbingSpeed[self.typ];
}

pub inline fn friction(self: Block) f32 {
return _friction[self.typ];
}
Expand Down
175 changes: 105 additions & 70 deletions src/game.zig
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,7 @@ pub const collision = struct {
};
}

const VolumeProperties = struct {
terminalVelocity: f64,
density: f64,
maxDensity: f64,
mobility: f64,
};
const VolumeProperties = struct {terminalVelocity: f64, density: f64, maxDensity: f64, mobility: f64, climbable: bool, climbingSpeed: f32};

fn overlapVolume(a: Box, b: Box) f64 {
const min = @max(a.min, b.min);
Expand All @@ -255,6 +250,8 @@ pub const collision = struct {
var maxDensity: f64 = defaults.maxDensity;
var mobilitySum: f64 = 0;
var volumeSum: f64 = 0;
var climbable: bool = false;
var climbingSpeed: f32 = 1;

var x: i32 = minX;
while(x <= maxX) : (x += 1) {
Expand Down Expand Up @@ -284,10 +281,14 @@ pub const collision = struct {
densitySum += filledVolume*block.density();
maxDensity = @max(maxDensity, block.density());
mobilitySum += filledVolume*block.mobility();
climbable = climbable or block.climbable();
climbingSpeed = @max(block.climbingSpeed(), climbingSpeed);
} else {
invTerminalVelocitySum += gridVolume/defaults.terminalVelocity;
densitySum += gridVolume*defaults.density;
mobilitySum += gridVolume*defaults.mobility;
climbable = defaults.climbable;
climbingSpeed = defaults.climbingSpeed;
}
}
}
Expand All @@ -298,6 +299,8 @@ pub const collision = struct {
.density = densitySum/volumeSum,
.maxDensity = maxDensity,
.mobility = mobilitySum/volumeSum,
.climbable = climbable,
.climbingSpeed = climbingSpeed,
};
}

Expand Down Expand Up @@ -437,7 +440,7 @@ pub const Player = struct { // MARK: Player
pub var selectionPosition2: ?Vec3i = null;

pub var currentFriction: f32 = 0;
pub var volumeProperties: collision.VolumeProperties = .{.density = 0, .maxDensity = 0, .mobility = 0, .terminalVelocity = 0};
pub var volumeProperties: collision.VolumeProperties = .{.density = 0, .maxDensity = 0, .mobility = 0, .terminalVelocity = 0, .climbable = false, .climbingSpeed = 1.0};

pub var onGround: bool = false;
pub var jumpCooldown: f64 = 0;
Expand Down Expand Up @@ -824,6 +827,8 @@ pub fn update(deltaTime: f64) void { // MARK: update()
const mobility = if(Player.isFlying.load(.monotonic)) 1.0 else Player.volumeProperties.mobility;
const density = if(Player.isFlying.load(.monotonic)) 0.0 else Player.volumeProperties.density;
const maxDensity = if(Player.isFlying.load(.monotonic)) 0.0 else Player.volumeProperties.maxDensity;
const isClimbing = if(Player.isFlying.load(.monotonic)) false else Player.volumeProperties.climbable;
const climbingSpeed = if(Player.isFlying.load(.monotonic)) 1.0 else Player.volumeProperties.climbingSpeed;

const baseFrictionCoefficient: f32 = Player.currentFriction;
var jumping = false;
Expand All @@ -839,80 +844,110 @@ pub fn update(deltaTime: f64) void { // MARK: update()

if(main.Window.grabbed) {
const walkingSpeed: f64 = if(Player.crouching) 2.5 else 4.5;
if(KeyBoard.key("forward").value > 0.0) {
if(KeyBoard.key("sprint").pressed and !Player.crouching) {
if(Player.isGhost.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 128)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(128*KeyBoard.key("forward").value));
} else if(Player.isFlying.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 32)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(32*KeyBoard.key("forward").value));
} else {
movementSpeed = @max(movementSpeed, 8)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(8*KeyBoard.key("forward").value));
}
} else {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(walkingSpeed*KeyBoard.key("forward").value));
const horizontalClimbSpeed = 8;
if(isClimbing and !Player.onGround) {
// Climbing Controls
if(KeyBoard.key("forward").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed*horizontalClimbSpeed)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(walkingSpeed*horizontalClimbSpeed*KeyBoard.key("forward").value));
}
}
if(KeyBoard.key("backward").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("backward").value;
movementDir += forward*@as(Vec3d, @splat(-walkingSpeed*KeyBoard.key("backward").value));
}
if(KeyBoard.key("left").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("left").value;
movementDir += right*@as(Vec3d, @splat(walkingSpeed*KeyBoard.key("left").value));
}
if(KeyBoard.key("right").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("right").value;
movementDir += right*@as(Vec3d, @splat(-walkingSpeed*KeyBoard.key("right").value));
}
if(KeyBoard.key("jump").pressed) {
if(Player.isFlying.load(.monotonic)) {
if(KeyBoard.key("sprint").pressed) {
if(KeyBoard.key("backward").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed*horizontalClimbSpeed)*KeyBoard.key("backward").value;
movementDir += forward*@as(Vec3d, @splat(-walkingSpeed*horizontalClimbSpeed*KeyBoard.key("backward").value));
}
if(KeyBoard.key("left").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed*horizontalClimbSpeed)*KeyBoard.key("left").value;
movementDir += right*@as(Vec3d, @splat(walkingSpeed*horizontalClimbSpeed*KeyBoard.key("left").value));
}
if(KeyBoard.key("right").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed*horizontalClimbSpeed)*KeyBoard.key("right").value;
movementDir += right*@as(Vec3d, @splat(-walkingSpeed*horizontalClimbSpeed*KeyBoard.key("right").value));
}
if(KeyBoard.key("jump").pressed) {
movementSpeed = @max(movementSpeed, climbingSpeed);
movementDir[2] += climbingSpeed;
}
if(KeyBoard.key("fall").pressed) {
movementSpeed = @max(movementSpeed, climbingSpeed);
movementDir[2] -= climbingSpeed;
}
} else {
// Normal Controls
if(KeyBoard.key("forward").value > 0.0) {
if(KeyBoard.key("sprint").pressed and !Player.crouching) {
if(Player.isGhost.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 60);
movementDir[2] += 60;
movementSpeed = @max(movementSpeed, 128)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(128*KeyBoard.key("forward").value));
} else if(Player.isFlying.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 32)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(32*KeyBoard.key("forward").value));
} else {
movementSpeed = @max(movementSpeed, 25);
movementDir[2] += 25;
movementSpeed = @max(movementSpeed, 8)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(8*KeyBoard.key("forward").value));
}
} else {
movementSpeed = @max(movementSpeed, 5.5);
movementDir[2] += 5.5;
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("forward").value;
movementDir += forward*@as(Vec3d, @splat(walkingSpeed*KeyBoard.key("forward").value));
}
} else if((Player.onGround or Player.jumpCoyote > 0.0) and Player.jumpCooldown <= 0) {
jumping = true;
Player.jumpCooldown = Player.jumpCooldownConstant;
if(!Player.onGround) {
Player.eye.coyote = 0;
}
if(KeyBoard.key("backward").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("backward").value;
movementDir += forward*@as(Vec3d, @splat(-walkingSpeed*KeyBoard.key("backward").value));
}
if(KeyBoard.key("left").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("left").value;
movementDir += right*@as(Vec3d, @splat(walkingSpeed*KeyBoard.key("left").value));
}
if(KeyBoard.key("right").value > 0.0) {
movementSpeed = @max(movementSpeed, walkingSpeed)*KeyBoard.key("right").value;
movementDir += right*@as(Vec3d, @splat(-walkingSpeed*KeyBoard.key("right").value));
}
if(KeyBoard.key("jump").pressed) {
if(Player.isFlying.load(.monotonic)) {
if(KeyBoard.key("sprint").pressed) {
if(Player.isGhost.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 60);
movementDir[2] += 60;
} else {
movementSpeed = @max(movementSpeed, 25);
movementDir[2] += 25;
}
} else {
movementSpeed = @max(movementSpeed, 5.5);
movementDir[2] += 5.5;
}
} else if((Player.onGround or Player.jumpCoyote > 0.0) and Player.jumpCooldown <= 0) {
jumping = true;
Player.jumpCooldown = Player.jumpCooldownConstant;
if(!Player.onGround) {
Player.eye.coyote = 0;
}
Player.jumpCoyote = 0;
} else if(!KeyBoard.key("fall").pressed) {
movementSpeed = @max(movementSpeed, walkingSpeed);
movementDir[2] += walkingSpeed;
}
Player.jumpCoyote = 0;
} else if(!KeyBoard.key("fall").pressed) {
movementSpeed = @max(movementSpeed, walkingSpeed);
movementDir[2] += walkingSpeed;
} else {
Player.jumpCooldown = 0;
}
} else {
Player.jumpCooldown = 0;
}
if(KeyBoard.key("fall").pressed) {
if(Player.isFlying.load(.monotonic)) {
if(KeyBoard.key("sprint").pressed) {
if(Player.isGhost.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 60);
movementDir[2] -= 60;
if(KeyBoard.key("fall").pressed) {
if(Player.isFlying.load(.monotonic)) {
if(KeyBoard.key("sprint").pressed) {
if(Player.isGhost.load(.monotonic)) {
movementSpeed = @max(movementSpeed, 60);
movementDir[2] -= 60;
} else {
movementSpeed = @max(movementSpeed, 25);
movementDir[2] -= 25;
}
} else {
movementSpeed = @max(movementSpeed, 25);
movementDir[2] -= 25;
movementSpeed = @max(movementSpeed, 5.5);
movementDir[2] -= 5.5;
}
} else {
movementSpeed = @max(movementSpeed, 5.5);
movementDir[2] -= 5.5;
} else if(!KeyBoard.key("jump").pressed) {
movementSpeed = @max(movementSpeed, walkingSpeed);
movementDir[2] -= walkingSpeed;
}
} else if(!KeyBoard.key("jump").pressed) {
movementSpeed = @max(movementSpeed, walkingSpeed);
movementDir[2] -= walkingSpeed;
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/physics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const playerDensity = 1.2;

pub fn calculateProperties() void {
if(main.renderer.mesh_storage.getBlockFromRenderThread(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
Player.volumeProperties = collision.calculateVolumeProperties(.client, Player.super.pos, Player.outerBoundingBox, .{.density = 0.001, .terminalVelocity = airTerminalVelocity, .maxDensity = 0.001, .mobility = 1.0});
Player.volumeProperties = collision.calculateVolumeProperties(.client, Player.super.pos, Player.outerBoundingBox, .{.density = 0.001, .terminalVelocity = airTerminalVelocity, .maxDensity = 0.001, .mobility = 1.0, .climbable = false, .climbingSpeed = 1.0});

const groundFriction = if(!Player.onGround and !Player.isFlying.load(.monotonic)) 0 else collision.calculateSurfaceProperties(.client, Player.super.pos, Player.outerBoundingBox, 20).friction;
const volumeFrictionCoeffecient: f32 = @floatCast(gravity/Player.volumeProperties.terminalVelocity);
Expand All @@ -31,13 +31,15 @@ pub fn update(deltaTime: f64, inputAcc: Vec3d, jumping: bool) void { // MARK: up
if(main.renderer.mesh_storage.getBlockFromRenderThread(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
const effectiveGravity = gravity*(playerDensity - Player.volumeProperties.density)/playerDensity;
const volumeFrictionCoeffecient: f32 = @floatCast(gravity/Player.volumeProperties.terminalVelocity);
const isClimbing = Player.volumeProperties.climbable and !Player.onGround;
var acc = inputAcc;
if(!Player.isFlying.load(.monotonic)) {
if(!Player.isFlying.load(.monotonic) and !isClimbing) {
acc[2] -= effectiveGravity;
}

const baseFrictionCoefficient: f32 = Player.currentFriction;
var directionalFrictionCoefficients: Vec3f = @splat(0);
const climbingFriction = 10;

// This our model for movement on a single frame:
// dv/dt = a - λ·v
Expand All @@ -51,6 +53,10 @@ pub fn update(deltaTime: f64, inputAcc: Vec3d, jumping: bool) void { // MARK: up
Player.super.vel[i] = @max(jumpVelocity, Player.super.vel[i] + jumpVelocity);
frictionCoefficient = volumeFrictionCoeffecient;
}

if(isClimbing and !Player.isFlying.load(.monotonic)) { // High friction while climbing
frictionCoefficient = climbingFriction;
}
const v_0 = Player.super.vel[i];
const a = acc[i];
// Here the solution can be easily derived:
Expand Down