bigger commits

This commit is contained in:
2026-10-18 12:27:54 -04:00
parent 1643c2fdaf
commit 45025b6682
6 changed files with 322 additions and 25 deletions
+128 -20
View File
@@ -145,7 +145,7 @@ void Engine::Initiate()
Character* testcharacter = new Character();
testcharacter->name = "character";
testcharacter->castshadow = true;
testcharacter->body->t.ScaleBy({ 0.6f, 2.1f, 0.6f });
testcharacter->body->t.ScaleBy({ 0.7f, 2.5f, 0.7f });
testcharacter->body->t.TranslateTo({ 0.0f, 10.0f, 0.0f });
testcharacter->body->SetRig(testrig);
@@ -172,8 +172,8 @@ void Engine::Initiate()
Character* npc = new Character();
npc->name = "npc";
npc->castshadow = true;
npc->body->t.ScaleBy({ 0.6f, 2.1f, 0.6f });
npc->body->t.TranslateTo({ 3.0f, 1.05f, 3.0f });
npc->body->t.ScaleBy({ 0.7f, 2.5f, 0.7f });
npc->body->t.TranslateTo({ 3.0f, 1.25f, 3.0f });
npc->body->SetRig(testrig);
npc->body->SetAnimation(testanimation);
npc->BindToRenderSystem();
@@ -197,7 +197,7 @@ void Engine::Initiate()
Character* c = new Character();
c->name = "remote";
c->castshadow = true;
c->body->t.ScaleBy({ 0.6f, 2.1f, 0.6f });
c->body->t.ScaleBy({ 0.7f, 2.5f, 0.7f });
c->body->SetRig(testrig);
c->body->SetAnimation(testanimation);
c->BindToRenderSystem();
@@ -393,13 +393,13 @@ void Engine::Initiate()
// Map is a rectangle: x in [-60, 60], z in [-30, 30] (authored at 1x, scaled by mapScale)
// Centre dome
AddTiers("dome", { 0.0f, 0.0f }, 18.0f, 1.0f, 5);
AddTiers("dome", { 0.0f, 0.0f }, 18.0f, 2.0f, 5);
// Cones, opposite corners of the dome
AddTiers("cone", { -12.0f, -13.0f }, 6.0f, 1.0f, 5);
AddTiers("cone", { 12.0f, 13.0f }, 6.0f, 1.0f, 5);
// Big cubes either side of the dome
AddBlock("cube", { -24.0f, 0.0f, 0.0f }, { 7.0f, 7.0f, 7.0f });
AddBlock("cube", { 24.0f, 0.0f, 0.0f }, { 7.0f, 7.0f, 7.0f });
AddBlock("cube", { -24.0f, 0.0f, 0.0f }, { 7.0f, 8.4f, 7.0f });
AddBlock("cube", { 24.0f, 0.0f, 0.0f }, { 7.0f, 8.4f, 7.0f });
// Stairs climbing to a tall back wall (the ramp), back right
AddBlock("stair", { 29.0f, 0.0f, -12.0f }, { 2.5f, 1.0f, 10.0f });
AddBlock("stair", { 31.5f, 0.0f, -12.0f }, { 2.5f, 2.0f, 10.0f });
@@ -437,7 +437,10 @@ void Engine::Initiate()
// body = 50 damage (two shots). Respawns the target when it dies.
// Movement tuning
const float airControl = 0.5f; // fraction of ground acceleration available in the air
const float grappleRange = 12.0f; // metres from the player
const float shockedAirControl = 0.1f; // air control after being hit by another player's shockwave
const float shockedDuration = 2.0f; // seconds the reduced air control lasts
float shockedTime = 0.0f;
const float grappleRange = 30.0f; // metres from the player
const float grappleSpeed = 28.0f; // launch speed toward the hit point
const float grappleCooldownTime = 2.5f; // seconds
float grappleCooldown = 0.0f;
@@ -448,7 +451,7 @@ void Engine::Initiate()
const float slideDuration = 0.8f;
const float slideFalloffTime = 0.7f;
const float slideDropY = 0.65f; // standing centre height (0.9) minus lying (0.25)
const float standEyeOffset = 0.85f; // eyes 0.85 above body centre (body centre 1.05, height 2.1)
const float standEyeOffset = 1.05f; // eyes 1.05 above body centre (body centre 1.25, height 2.5)
const float slideEyeOffset = 0.25f; // lying down, eyes just above the floor
float eyeOffset = standEyeOffset;
bool sliding = false;
@@ -459,6 +462,33 @@ void Engine::Initiate()
float dashCooldown = 0.0f;
const float shootCooldownTime = 0.5f; // seconds between shots
float shootCooldown = 0.0f;
// Cubes (E). Whoever hosts (or an offline player) owns and simulates them and decides when they
// despawn; LAN clients only ask the host for one and draw the poses it streams back.
const float cubeCooldownTime = 0.5f; // seconds between cubes per player
const double cubeLifetime = 30.0; // seconds before a cube despawns
float cubeCooldown = 0.0f;
// Cubes also rain from the sky on their own (host / offline only; clients see them via the stream)
const float cubeRainInterval = 0.5f; // seconds between rain cubes
const float cubeRainHeight = 45.0f;
const size_t cubeRainMax = 200; // stop raining while this many cubes exist
float cubeRainTimer = 0.0f;
struct HostCube { uint32_t id; Mesh* mesh; double spawnTime; };
std::vector<HostCube> hostCubes;
uint32_t nextCubeId = 0;
std::map<uint8_t, double> lastCubeSpawn; // host: when each client last spawned one (rate limit)
struct PuppetCube { Mesh* mesh; double lastSeen; };
std::map<uint32_t, PuppetCube> puppetCubes; // client: the host's cubes, keyed by host id
auto SpawnCube = [&](const glm::vec3& at) {
Mesh* nc = CreateCubeMesh();
nc->name = "cube!";
nc->t.ScaleTo(1.0f);
nc->t.TranslateTo(at);
nc->BindToRenderSystem();
mainf->AddChild(nc);
physicsengine->AddObject(new p(nc));
hostCubes.push_back({ ++nextCubeId, nc, glfwGetTime() });
};
glm::vec3 slideDir(0.0f);
// Shockwave (key Q): projectile that explodes on the first surface it touches. The blast
@@ -508,6 +538,9 @@ void Engine::Initiate()
body->linearvelocity += dir * power * falloff;
body->WakeUp();
// Another player's blast (not our own) takes away most of our air control for a while
if (body == characterBody && fromNet) shockedTime = shockedDuration;
if (body == characterBody && sliding) {
// The slide overrides horizontal velocity every frame, so end it and stand back up
sliding = false;
@@ -673,15 +706,12 @@ void Engine::Initiate()
// Input � keyboard events
// --------------------------------------------------------------------
while (Keyboard::Event buffer = keyboard->ReadKey()) {
if (buffer.GetCode() == 'E' && buffer.IsPress()) {
Mesh* nc = CreateCubeMesh();
nc->name = "cube!";
nc->t.ScaleTo(1.0f);
nc->t.TranslateTo(camera->t.GetTranslation());
nc->t.RotateByEulerAngles({ 0.0f, 0.0f, 0.0f });
nc->BindToRenderSystem();
mainf->AddChild(nc);
physicsengine->AddObject(new p(nc));
if (buffer.GetCode() == 'E' && buffer.IsPress() && cubeCooldown <= 0.0f) {
cubeCooldown = cubeCooldownTime;
if (lan && lan->IsActive() && lan->GetMode() == Lan::Mode::Client)
lan->SendCubeSpawn(camera->t.GetTranslation()); // the host spawns it
else
SpawnCube(camera->t.GetTranslation());
}
if (buffer.GetCode() == 'F' && buffer.IsPress()) {
const glm::vec3 playerPos = testcharacter->body->t.GetTranslation();
@@ -808,6 +838,7 @@ void Engine::Initiate()
slideCooldown = std::max(slideCooldown - deltatime, 0.0f);
dashCooldown = std::max(dashCooldown - deltatime, 0.0f);
shootCooldown = std::max(shootCooldown - deltatime, 0.0f);
cubeCooldown = std::max(cubeCooldown - deltatime, 0.0f);
const float accel = 40.0f;
const float damp = 0.75f;
const float groundTopSpeed = movespeed * accel * refDt * damp / (1.0f - damp);
@@ -842,7 +873,7 @@ void Engine::Initiate()
const float current = glm::dot(glm::vec3(characterBody->linearvelocity.x, 0.0f,
characterBody->linearvelocity.z), wishDir);
const float add = glm::clamp(groundTopSpeed - current, 0.0f,
accel * airControl * movespeed * deltatime);
accel * (shockedTime > 0.0f ? shockedAirControl : airControl) * movespeed * deltatime);
characterBody->linearvelocity.x += wishDir.x * add;
characterBody->linearvelocity.z += wishDir.z * add;
}
@@ -860,6 +891,7 @@ void Engine::Initiate()
// Shockwave projectile: arc forward, explode on the first collider along this frame's path
shockCooldown = std::max(shockCooldown - deltatime, 0.0f);
shockedTime = std::max(shockedTime - deltatime, 0.0f);
if (shockActive) {
shockVel.y -= shockGravity * deltatime;
shockAge += deltatime;
@@ -983,6 +1015,36 @@ void Engine::Initiate()
// --------------------------------------------------------------------
// LAN: send our transform, apply the other player's
// --------------------------------------------------------------------
// Rain cubes from the sky at random spots over the map (host / offline)
if (!lan || !lan->IsActive() || lan->GetMode() == Lan::Mode::Host) {
cubeRainTimer -= deltatime;
if (cubeRainTimer <= 0.0f) {
cubeRainTimer += cubeRainInterval;
if (hostCubes.size() < cubeRainMax) {
glm::vec3 at = RandomSpawn();
at.y = cubeRainHeight;
SpawnCube(at);
std::uniform_real_distribution<float> spin(0.0f, 6.28318f);
hostCubes.back().mesh->t.RotateToEulerAngles({ spin(respawnRng), spin(respawnRng), spin(respawnRng) });
}
}
}
// Despawn cubes that have lived long enough (host / offline)
{
const double cubeNow = glfwGetTime();
for (size_t i = 0; i < hostCubes.size();) {
if (cubeNow - hostCubes[i].spawnTime > cubeLifetime) {
Mesh* m = hostCubes[i].mesh;
rendersystem->RemoveRenderable(m);
physicsengine->RemoveObject(m);
m->Delete();
hostCubes.erase(hostCubes.begin() + i);
}
else ++i;
}
}
if (lan && lan->IsActive()) {
NetState mine;
mine.position = testcharacter->body->t.GetTranslation();
@@ -994,7 +1056,53 @@ void Engine::Initiate()
std::vector<NetUpdate> updates;
std::vector<NetBlast> blasts;
std::vector<NetHit> hits;
lan->Receive(updates, &blasts, &hits);
std::vector<NetCubeRequest> cubeRequests;
std::vector<NetCube> cubeStates;
lan->Receive(updates, &blasts, &hits, &cubeRequests, &cubeStates);
// Host: spawn the cubes clients asked for (rate limited per client), then stream every cube's pose
for (const NetCubeRequest& rq : cubeRequests) {
double& last = lastCubeSpawn[rq.sender];
if (now - last < cubeCooldownTime * 0.9) continue;
last = now;
SpawnCube(glm::clamp(rq.position, glm::vec3(-200.0f), glm::vec3(200.0f)));
}
if (lan->GetMode() == Lan::Mode::Host) {
std::vector<NetCube> snapshot;
snapshot.reserve(hostCubes.size());
for (const HostCube& hc : hostCubes) {
NetCube nc;
nc.id = hc.id;
nc.position = hc.mesh->t.GetTranslation();
nc.rotation = hc.mesh->t.GetRotationQuaternion();
snapshot.push_back(nc);
}
lan->SendCubes(snapshot);
}
// Client: show the host's cubes, and drop the ones it stopped sending (despawned)
for (const NetCube& nc : cubeStates) {
auto it = puppetCubes.find(nc.id);
if (it == puppetCubes.end()) {
Mesh* m = CreateCubeMesh();
m->name = "cube!";
m->t.ScaleTo(1.0f);
m->BindToRenderSystem();
mainf->AddChild(m);
it = puppetCubes.emplace(nc.id, PuppetCube{ m, now }).first;
}
it->second.mesh->t.TranslateTo(nc.position);
it->second.mesh->t.RotateToQuaternion(nc.rotation);
it->second.lastSeen = now;
}
for (auto it = puppetCubes.begin(); it != puppetCubes.end();) {
if (now - it->second.lastSeen > 1.5) {
rendersystem->RemoveRenderable(it->second.mesh);
it->second.mesh->Delete();
it = puppetCubes.erase(it);
}
else ++it;
}
for (const NetBlast& b : blasts)
Explode(b.center, true, b.radius, b.power);
// Damage from other players (respawn handles dying, see the player respawn check)