- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass / water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon - Client: title / join / character creator / play screens, prediction + reconciliation, interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection) - Shared deterministic zone generator, movement, protocol, roster, appearance - Paper-doll character art pipeline (PixelLab base walk + per-item animation edits) - Brandling roster from the company list (logo colors, jev types), Blender safari props - Headless test bot
50 lines
1.6 KiB
C++
50 lines
1.6 KiB
C++
#pragma once
|
|
// Player movement, run identically by the server (authoritative) and the
|
|
// client (prediction + reconciliation). Pure function of state + input.
|
|
|
|
#include "Protocol.h"
|
|
#include "Zone.h"
|
|
|
|
namespace bs {
|
|
|
|
constexpr float kWalkSpeed = 4.0f; // world units / second
|
|
constexpr float kRunSpeed = 6.6f;
|
|
|
|
struct MoveState {
|
|
glm::vec2 pos{0.0f};
|
|
float yaw = 0.0f;
|
|
bool moving = false;
|
|
bool running = false;
|
|
};
|
|
|
|
inline void stepMovement(const Zone& zone, MoveState& s, const InputCmd& in, float dt) {
|
|
glm::vec2 dir = in.move();
|
|
float amount = std::min(glm::length(dir), 1.0f);
|
|
s.moving = amount > 0.05f;
|
|
s.running = s.moving && in.run();
|
|
if (!s.moving) return;
|
|
dir = glm::normalize(dir) * amount;
|
|
s.yaw = yawFromDir(dir);
|
|
glm::vec2 step = dir * (s.running ? kRunSpeed : kWalkSpeed) * dt;
|
|
// Axis-separated resolution slides along obstacles.
|
|
glm::vec2 p = s.pos;
|
|
if (zone.walkable(p, p + glm::vec2(step.x, 0.0f))) p.x += step.x;
|
|
if (zone.walkable(p, p + glm::vec2(0.0f, step.y))) p.y += step.y;
|
|
s.pos = p;
|
|
}
|
|
|
|
// Quantizes a world direction exactly like the network does, so prediction
|
|
// uses the same input the server will see.
|
|
inline InputCmd makeInput(uint32_t seq, glm::vec2 worldDir, bool run) {
|
|
InputCmd c;
|
|
c.seq = seq;
|
|
float len = glm::length(worldDir);
|
|
if (len > 1.0f) worldDir /= len;
|
|
c.moveX = int8_t(std::lround(glm::clamp(worldDir.x, -1.0f, 1.0f) * 127.0f));
|
|
c.moveZ = int8_t(std::lround(glm::clamp(worldDir.y, -1.0f, 1.0f) * 127.0f));
|
|
c.flags = run ? 1 : 0;
|
|
return c;
|
|
}
|
|
|
|
} // namespace bs
|