337 lines
10 KiB
Zig
337 lines
10 KiB
Zig
const Player = @This();
|
|
const std = @import("std");
|
|
|
|
const c = @import("const.zig");
|
|
const glfw = @import("zglfw");
|
|
const math = @import("math.zig");
|
|
|
|
const Blocks = @import("assets/Blocks.zig");
|
|
const Chunks = @import("Chunks.zig");
|
|
const Iterator2 = math.Iterator2;
|
|
const Vector2 = math.Vector2;
|
|
const Vector3 = math.Vector3;
|
|
const Vector2Int = math.Vector2Int;
|
|
const Vector3Int = math.Vector3Int;
|
|
|
|
const AxisState = enum {
|
|
none,
|
|
positive,
|
|
negative,
|
|
both_positive,
|
|
both_negative,
|
|
|
|
pub fn getComponentFloat(self: AxisState) f32 {
|
|
return switch (self) {
|
|
.none => 0,
|
|
.positive, .both_positive => 1,
|
|
.negative, .both_negative => -1,
|
|
};
|
|
}
|
|
|
|
pub fn getComponentInt(self: AxisState) i32 {
|
|
return switch (self) {
|
|
.none => 0,
|
|
.positive, .both_positive => 1,
|
|
.negative, .both_negative => -1,
|
|
};
|
|
}
|
|
|
|
pub fn getComponentIntFrac(self: AxisState) i32 {
|
|
return switch (self) {
|
|
.none => 0,
|
|
.positive, .both_positive => std.math.maxInt(i32),
|
|
.negative, .both_negative => -std.math.maxInt(i32),
|
|
};
|
|
}
|
|
};
|
|
|
|
const ButtonState = struct {
|
|
current: bool = false,
|
|
pressed_this_frame: bool = false,
|
|
released_this_frame: bool = false,
|
|
|
|
pub fn reset(self: *ButtonState) void {
|
|
self.pressed_this_frame = false;
|
|
self.released_this_frame = false;
|
|
}
|
|
};
|
|
|
|
const Axis = struct {
|
|
state: AxisState = .none,
|
|
positive_key: glfw.Key,
|
|
negative_key: glfw.Key,
|
|
|
|
pub fn init(positive_key: glfw.Key, negative_key: glfw.Key) Axis {
|
|
return .{
|
|
.positive_key = positive_key,
|
|
.negative_key = negative_key,
|
|
};
|
|
}
|
|
|
|
pub fn onKeyDown(self: *Axis, key_code: glfw.Key) void {
|
|
if (key_code == self.positive_key) {
|
|
switch (self.state) {
|
|
.none => self.state = .positive,
|
|
.negative => self.state = .both_positive,
|
|
else => unreachable,
|
|
}
|
|
} else if (key_code == self.negative_key) {
|
|
switch (self.state) {
|
|
.none => self.state = .negative,
|
|
.positive => self.state = .both_negative,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn onKeyUp(self: *Axis, key_code: glfw.Key) void {
|
|
if (key_code == self.positive_key) {
|
|
switch (self.state) {
|
|
.positive => self.state = .none,
|
|
.both_positive, .both_negative => self.state = .negative,
|
|
else => unreachable,
|
|
}
|
|
} else if (key_code == self.negative_key) {
|
|
switch (self.state) {
|
|
.negative => self.state = .none,
|
|
.both_positive, .both_negative => self.state = .positive,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn getComponentFloat(self: Axis) f32 {
|
|
return self.state.getComponentFloat();
|
|
}
|
|
|
|
pub fn getComponentInt(self: Axis) i32 {
|
|
return self.state.getComponentInt();
|
|
}
|
|
|
|
pub fn getComponentIntFrac(self: Axis) i32 {
|
|
return self.state.getComponentIntFrac();
|
|
}
|
|
};
|
|
|
|
const Button = struct {
|
|
state: ButtonState = .{},
|
|
key: glfw.Key,
|
|
|
|
pub fn init(key: glfw.Key) Button {
|
|
return .{
|
|
.key = key,
|
|
};
|
|
}
|
|
|
|
pub fn onKeyDown(self: *Button, key_code: glfw.Key) void {
|
|
if (key_code == self.key) {
|
|
self.state.current = true;
|
|
self.state.pressed_this_frame = true;
|
|
}
|
|
}
|
|
|
|
pub fn onKeyUp(self: *Button, key_code: glfw.Key) void {
|
|
if (key_code == self.key) {
|
|
self.state.current = false;
|
|
self.state.released_this_frame = true;
|
|
}
|
|
}
|
|
|
|
pub fn reset(self: *Button) void {
|
|
self.state.reset();
|
|
}
|
|
|
|
pub inline fn isDown(self: Button) bool {
|
|
return self.state.current;
|
|
}
|
|
|
|
pub inline fn isPressed(self: Button) bool {
|
|
return self.state.pressed_this_frame;
|
|
}
|
|
|
|
pub inline fn isReleased(self: Button) bool {
|
|
return self.state.released_this_frame;
|
|
}
|
|
};
|
|
|
|
const MovementState = union(enum) {
|
|
ground: struct {
|
|
horizontal_velocity_sv: Vector2Int,
|
|
},
|
|
air: struct {
|
|
horizontal_velocity_sv: Vector2Int,
|
|
vertical_velocity_sv: i32,
|
|
},
|
|
flight: void,
|
|
};
|
|
|
|
position_sv: Vector3Int,
|
|
pitch_rad: f32,
|
|
yaw_rad: f32,
|
|
|
|
x_input: Axis = .init(.d, .a),
|
|
y_input: Axis = .init(.w, .s),
|
|
z_input: Axis = .init(.space, .left_shift),
|
|
jump_input: Button = .init(.space),
|
|
|
|
mouse_sensitivity: f32 = 0.002,
|
|
vertical_fov_deg: f32 = 80,
|
|
|
|
movement_state: MovementState,
|
|
|
|
pub const camera_height_vx = 1.62;
|
|
|
|
pub const horizontal_speed_sv = sv(11.0);
|
|
pub const vertical_speed_sv = sv(7.49);
|
|
|
|
pub const min_pitch_rad = -0.5 * std.math.pi;
|
|
pub const max_pitch_rad = 0.5 * std.math.pi;
|
|
|
|
pub const collision_half_width_sv = sv(0.4);
|
|
pub const collision_height_sv = sv(1.8);
|
|
|
|
pub const speed_sv = sv(5.612);
|
|
pub const step_up_sv = sv(0.53125);
|
|
pub const step_down_sv = sv(0.53125);
|
|
pub const jump_velocity_sv = sv(6.1237245);
|
|
pub const gravity_sv = sv(15);
|
|
pub const vertical_velocity_cap_sv = sv(30);
|
|
pub const ground_acceleration_sv = sv(56.12);
|
|
pub const air_acceleration_sv = sv(56.12);
|
|
pub const air_speed_limit_sv = sv(0.5612);
|
|
|
|
pub fn init(position_sv: Vector3Int, pitch_rad: f32, yaw_rad: f32) Player {
|
|
return .{
|
|
.position_sv = position_sv,
|
|
.pitch_rad = pitch_rad,
|
|
.yaw_rad = yaw_rad,
|
|
.movement_state = .flight,
|
|
};
|
|
}
|
|
|
|
pub fn onKeyDown(self: *Player, key: glfw.Key) void {
|
|
self.x_input.onKeyDown(key);
|
|
self.y_input.onKeyDown(key);
|
|
self.z_input.onKeyDown(key);
|
|
}
|
|
|
|
pub fn onKeyUp(self: *Player, key: glfw.Key) void {
|
|
self.x_input.onKeyUp(key);
|
|
self.y_input.onKeyUp(key);
|
|
self.z_input.onKeyUp(key);
|
|
}
|
|
|
|
pub fn onMouseMove(self: *Player, dx: f32, dy: f32) void {
|
|
self.pitch_rad = std.math.clamp(self.pitch_rad - dy * self.mouse_sensitivity, min_pitch_rad, max_pitch_rad);
|
|
self.yaw_rad = @mod(self.yaw_rad - dx * self.mouse_sensitivity, std.math.tau);
|
|
}
|
|
|
|
pub fn update(self: *Player, dt: f32, chunks: *const Chunks) void {
|
|
defer self.resetAllButtons();
|
|
|
|
// --- GATHER INPUTS -------------------------------------------------------
|
|
|
|
const horizontal_input_vector_frac = blk: {
|
|
var ret = Vector2
|
|
.init(self.x_input.getComponentFloat(), self.y_input.getComponentFloat())
|
|
.rotate(self.yaw_rad);
|
|
const len_squared = ret.lenSquared();
|
|
if (len_squared > 1) {
|
|
ret = ret.divScalar(@sqrt(len_squared));
|
|
}
|
|
break :blk ret.asVector2IntFrac();
|
|
};
|
|
const vertical_input_vector_frac = self.z_input.getComponentIntFrac();
|
|
|
|
// --- STATE TRANSITIONS ---------------------------------------------------
|
|
|
|
switch (self.movement_state) {
|
|
.ground => |ground| {
|
|
if (self.jump_input.isPressed()) {
|
|
self.movement_state = .{
|
|
.air = .{
|
|
.horizontal_velocity_sv = ground.horizontal_velocity_sv,
|
|
.vertical_velocity_sv = jump_velocity_sv,
|
|
},
|
|
};
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
// --- STATE HANDLING ------------------------------------------------------
|
|
|
|
switch (self.movement_state) {
|
|
.ground => {},
|
|
.air => {},
|
|
.flight => {
|
|
const horizontal_velocity_sv = horizontal_input_vector_frac.mulScalarFrac(horizontal_speed_sv);
|
|
const vertical_velocity_sv = math.mulFrac(vertical_input_vector_frac, vertical_speed_sv);
|
|
|
|
var horizontal_displacement_sv = horizontal_velocity_sv.mulScalarFloat(dt);
|
|
var vertical_displacement_sv = math.mulIntFloat(vertical_velocity_sv, dt);
|
|
|
|
var position_sv = self.position_sv;
|
|
var min_sv = position_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0));
|
|
var max_sv = position_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv));
|
|
|
|
if (vertical_displacement_sv > 0) {
|
|
if (chunks.sweepCastUp(min_sv, max_sv, vertical_displacement_sv)) |hit| {
|
|
vertical_displacement_sv -= hit.projected_distance_sv;
|
|
}
|
|
position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv));
|
|
min_sv = .add(min_sv, .init(0, 0, vertical_displacement_sv));
|
|
max_sv = .add(max_sv, .init(0, 0, vertical_displacement_sv));
|
|
}
|
|
|
|
var i: usize = 0;
|
|
while (i < 2) : (i += 1) {
|
|
if (chunks.sweepCastHorizontal(min_sv, max_sv, horizontal_displacement_sv)) |hit| {
|
|
const adjustment = hit.normal_frac
|
|
.asVector2Int()
|
|
.mulScalarFrac(hit.projected_distance_sv);
|
|
std.debug.print("i={} | pos={X} | n={} | d={X} | adj={X} | disp={X}->", .{
|
|
i,
|
|
position_sv.vector,
|
|
hit.normal_frac.asVector3Frac().vector,
|
|
hit.projected_distance_sv,
|
|
adjustment.vector,
|
|
horizontal_displacement_sv.vector,
|
|
});
|
|
horizontal_displacement_sv = .add(
|
|
horizontal_displacement_sv,
|
|
adjustment,
|
|
);
|
|
std.debug.print("{X}\n", .{horizontal_displacement_sv.vector});
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
position_sv = .add(position_sv, horizontal_displacement_sv.asVector3Int(0));
|
|
|
|
if (vertical_displacement_sv < 0.0) {
|
|
min_sv = .add(min_sv, horizontal_displacement_sv.asVector3Int(0));
|
|
max_sv = .add(max_sv, horizontal_displacement_sv.asVector3Int(0));
|
|
if (chunks.sweepCastDown(min_sv, max_sv, -vertical_displacement_sv)) |hit| {
|
|
vertical_displacement_sv += hit.projected_distance_sv;
|
|
}
|
|
position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv));
|
|
}
|
|
|
|
self.position_sv = position_sv;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn resetAllButtons(self: *Player) void {
|
|
inline for (@typeInfo(Player).@"struct".fields) |field| {
|
|
if (field.type == Button) {
|
|
@field(self, field.name).reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline fn sv(comptime vx: comptime_float) comptime_int {
|
|
return @intFromFloat(@round(vx * c.sv_per_vx));
|
|
}
|