Compare commits

..

7 Commits

Author SHA1 Message Date
Maurice d365763209 last onewfds 2026-10-18 14:24:36 -04:00
Maurice fbe079559a pull 2026-10-18 14:15:50 -04:00
Maurice 335f32fcea engine cpp changed 2026-10-18 14:02:24 -04:00
Maurice a65e3d4f24 ewrtgf 2026-10-18 13:02:29 -04:00
Maurice ac982a2b21 last one 2026-10-18 12:57:14 -04:00
Maurice e1337c2560 werfdvew 2026-10-18 12:49:34 -04:00
Maurice 170d953fe2 finaliisss 2026-10-18 12:49:24 -04:00
8 changed files with 126 additions and 15 deletions
Vendored
BIN
View File
Binary file not shown.
+5 -1
View File
@@ -58,5 +58,9 @@ void Character::Render(Shader& shader) {
for (int i = 0; i < transforms.size(); i++)
shader.SetMat4(("finalBonesMatrices[" + std::to_string(i) + "]").c_str(), transforms[i]);
}
body->rig->Render(shader, body->t.GetTranslationMatrix() * body->t.GetRotationMatrix());
shader.Set4F("colorOverride", colorOverride);
// body->t's scale is the hitbox only; the rig is drawn at visualScale so it fills that hitbox
body->rig->Render(shader, body->t.GetTranslationMatrix() * body->t.GetRotationMatrix()
* glm::scale(glm::mat4(1.0f), glm::vec3(visualScale)));
shader.Set4F("colorOverride", glm::vec4(0.0f)); // don't leak into other rig draws
}
+2
View File
@@ -116,6 +116,8 @@ class Character : public Object, public Renderable {
public:
CharacterMind* mind = nullptr;
CharacterBody* body = nullptr;
glm::vec4 colorOverride = glm::vec4(0.0f); // rgb + strength (0 = keep the rig's own texture)
float visualScale = 1.4f; // rig is ~1.8 tall natively; hitbox is 2.5
Character();
void Render(Shader& shader) override;
+114 -14
View File
@@ -130,6 +130,7 @@ void Engine::Initiate()
Texture* blanktex = new Texture("assets/blanktexture.png");
Texture* magic = new Texture("assets/itsmagicbitch.jpg");
Texture* snowflake = new Texture("assets/snowflake.png");
Texture* shockwavetex = new Texture("assets/shockwave.png");
Texture* leontex = new Texture("assets/revolvertex.png");
// ------------------------------------------------------------------------
@@ -191,11 +192,15 @@ void Engine::Initiate()
struct RemotePlayer {
Character* character = nullptr;
double lastSeen = 0.0;
p* body = nullptr; // host only: lets the host's cubes collide with this player
glm::vec3 targetPos = glm::vec3(0.0f); // latest pose received; the model eases toward it every frame
glm::quat targetRot = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
};
std::map<int, RemotePlayer> remotes;
auto SpawnRemote = [&]() -> Character* {
Character* c = new Character();
c->name = "remote";
c->colorOverride = glm::vec4(0.02f, 0.08f, 0.5f, 1.0f); // dark blue
c->castshadow = true;
c->body->t.ScaleBy({ 0.7f, 2.5f, 0.7f });
c->body->SetRig(testrig);
@@ -328,6 +333,23 @@ void Engine::Initiate()
grappleText->t2d.size = { 0.0f, 0.0f, 560.0f, 40.0f };
grappleText->BindToRenderSystem();
// Shot cooldown bar at the bottom of the screen: full and white when ready, empties on a shot
// and refills as the cooldown runs down.
const float shotBarWidth = 300.0f, shotBarHeight = 8.0f;
Box* shotBarBack = new Box();
shotBarBack->Color = { 0.0f, 0.0f, 0.0f };
shotBarBack->Opacity = 0.45f;
shotBarBack->t2d.position = { 0.5f, 1.0f, 0.0f, -30.0f };
shotBarBack->t2d.center = { 0.5f, 1.0f };
shotBarBack->t2d.size = { 0.0f, 0.0f, shotBarWidth + 4.0f, shotBarHeight + 4.0f };
shotBarBack->BindToRenderSystem();
Box* shotBar = new Box();
shotBar->Color = { 1.0f, 1.0f, 1.0f };
shotBar->t2d.position = { 0.5f, 1.0f, -shotBarWidth * 0.5f, -32.0f };
shotBar->t2d.center = { 0.0f, 1.0f };
shotBar->t2d.size = { 0.0f, 0.0f, shotBarWidth, shotBarHeight };
shotBar->BindToRenderSystem();
// ------------------------------------------------------------------------
// World meshes
// ------------------------------------------------------------------------
@@ -443,6 +465,18 @@ void Engine::Initiate()
float shockedTime = 0.0f;
const float grappleRange = 30.0f; // metres from the player
const float grappleSpeed = 28.0f; // launch speed toward the hit point
// Visible grapple rope: a thin black box stretched from the hand to the latch point
const float grappleLineShowTime = 0.5f;
float grappleLineTime = 0.0f;
glm::vec3 grappleLineEnd(0.0f);
Mesh* grappleLine = CreateCubeMesh();
grappleLine->name = "grappleline";
grappleLine->meshTexture = blanktex;
grappleLine->colorOverride = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); // black
grappleLine->t.TranslateTo({ 0.0f, -1000.0f, 0.0f }); // hidden until a grapple latches
grappleLine->BindToRenderSystem();
mainf->AddChild(grappleLine);
const float grappleLift = 10.0f; // extra upward speed added on top of the pull
const float grappleCooldownTime = 2.5f; // seconds
float grappleCooldown = 0.0f;
@@ -466,7 +500,7 @@ void Engine::Initiate()
// 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 float cubeCooldownTime = 0.1f; // 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)
@@ -518,7 +552,7 @@ void Engine::Initiate()
Mesh* blastMesh = CreateCubeMesh();
blastMesh->name = "shockblast";
blastMesh->meshTexture = blanktex;
blastMesh->meshTexture = shockwavetex;
blastMesh->t.TranslateTo(hiddenPos);
blastMesh->BindToRenderSystem();
mainf->AddChild(blastMesh);
@@ -567,6 +601,15 @@ void Engine::Initiate()
shockActive = false;
shockMesh->t.TranslateTo(hiddenPos);
}
else if (lan && lan->IsActive()) {
// Another player's blast: launch the cubes (host: the real ones; client: its local copies)
for (p* obj : physicsengine->GetObjects()) {
bool isCube = false;
for (const HostCube& hc : hostCubes) if (obj->pointer == hc.mesh) isCube = true;
for (auto& kv : puppetCubes) if (obj->pointer == kv.second.mesh) isCube = true;
if (isCube) launch(obj->pointer->t.GetTranslation(), obj);
}
}
blastMesh->t.TranslateTo(center);
blastMesh->t.ScaleTo(glm::vec3(0.5f));
blastTime = blastShowTime;
@@ -576,7 +619,7 @@ void Engine::Initiate()
const int maxHealth = 100;
const int bodyDamage = 50;
const int punchDamage = 25;
const float headFraction = 0.93f; // top 7% of the hitbox counts as the head
const float headFraction = 0.8f; // top 20% of the hitbox counts as the head
// Random respawn spot on the floor (NPC and player), kept clear of every map prop
std::mt19937 respawnRng(std::random_device{}());
@@ -805,8 +848,12 @@ void Engine::Initiate()
}
if (found) {
characterBody->linearvelocity = glm::normalize(bestPoint - ray.origin) * grappleSpeed;
// Extra upward fling so the grapple gains height instead of just pulling sideways
characterBody->linearvelocity.y = std::max(characterBody->linearvelocity.y, 0.0f) + grappleLift;
characterBody->WakeUp();
grappleCooldown = grappleCooldownTime;
grappleLineTime = grappleLineShowTime;
grappleLineEnd = bestPoint;
camera->AddScreenshake(0.1f);
}
}
@@ -934,7 +981,6 @@ void Engine::Initiate()
}
rain->t.TranslateTo(camera->t.GetTranslation() + glm::vec3(0.0f, 20.0f, 0.0f));
for (int i = 0; i < 30; i++) rain->Emit();
if (!sliding) {
testcharacter->body->t.RotateToQuaternion(
@@ -973,6 +1019,26 @@ void Engine::Initiate()
}
fpsCounter->text = std::to_string(measuredFps) + " FPS";
}
{
const float ready = 1.0f - glm::clamp(shootCooldown / shootCooldownTime, 0.0f, 1.0f);
shotBar->t2d.size = { 0.0f, 0.0f, shotBarWidth * ready, shotBarHeight };
shotBar->Opacity = ready > 0.0f ? 1.0f : 0.0f;
}
// Grapple rope: starts at the hand (follows the camera), ends on the latch point
grappleLineTime = std::max(grappleLineTime - deltatime, 0.0f);
if (grappleLineTime > 0.0f) {
const glm::vec3 handPos = camera->t.GetTranslation() + camera->t.GetFrontVector() * 0.6f
+ camera->t.GetRightVector() * 0.3f - camera->t.GetUpVector() * 0.3f;
const glm::vec3 delta = grappleLineEnd - handPos;
const float len = glm::length(delta);
if (len > 0.01f) {
grappleLine->t.TranslateTo(handPos + delta * 0.5f);
grappleLine->t.ScaleTo({ 0.05f, 0.05f, len });
// Plain shortest-arc rotation of the box's long (z) axis onto the rope; LookAt breaks on straight up/down
grappleLine->t.RotateToQuaternion(RotationBetweenVectors(glm::vec3(0.0f, 0.0f, 1.0f), delta));
}
}
else grappleLine->t.TranslateTo(hiddenPos);
hitMarkerTime = std::max(hitMarkerTime - deltatime, 0.0f);
for (Box* b : hitMarker) b->Opacity = hitMarkerTime / hitMarkerDuration;
// Damage flash: any HP loss restarts a short green fade-out
@@ -1090,14 +1156,25 @@ void Engine::Initiate()
m->t.ScaleTo(1.0f);
m->BindToRenderSystem();
mainf->AddChild(m);
// Solid for the local player, but moved only by the host's stream (like the floor, never simulated here)
p* cubeBody = new p(m);
cubeBody->velocity = false;
physicsengine->AddObject(cubeBody);
// Simulated locally exactly like the host's cubes (so the local player pushes them with
// no lag); the host's stream below only corrects drift.
physicsengine->AddObject(new p(m));
m->t.TranslateTo(nc.position);
m->t.RotateToQuaternion(nc.rotation);
it = puppetCubes.emplace(nc.id, PuppetCube{ m, now }).first;
}
it->second.mesh->t.TranslateTo(nc.position);
it->second.mesh->t.RotateToQuaternion(nc.rotation);
else {
t& ct = it->second.mesh->t;
const glm::vec3 err = nc.position - ct.GetTranslation();
if (glm::length(err) > 3.0f) {
ct.TranslateTo(nc.position);
ct.RotateToQuaternion(nc.rotation);
}
else {
ct.TranslateBy(err * 0.35f);
ct.RotateToQuaternion(glm::slerp(ct.GetRotationQuaternion(), nc.rotation, 0.35f));
}
}
it->second.lastSeen = now;
}
for (auto it = puppetCubes.begin(); it != puppetCubes.end();) {
@@ -1128,13 +1205,21 @@ void Engine::Initiate()
rp.character = SpawnRemote();
rp.character->body->t.TranslateTo(u.state.position);
rp.character->body->t.RotateToQuaternion(u.state.rotation);
rp.targetPos = u.state.position;
rp.targetRot = u.state.rotation;
if (lan->GetMode() == Lan::Mode::Host) {
// Solid for the host's cubes (kinematic: moved by the client's stream only)
rp.body = new p(rp.character->body);
rp.body->velocity = false;
physicsengine->AddObject(rp.body);
}
it = remotes.emplace(u.id, rp).first;
}
else {
// Smooth over the 60 Hz updates
t& rt = it->second.character->body->t;
rt.TranslateTo(glm::mix(rt.GetTranslation(), u.state.position, 0.5f));
rt.RotateToQuaternion(glm::slerp(rt.GetRotationQuaternion(), u.state.rotation, 0.5f));
// Packets arrive in bursts (several in one frame, then none), so just remember the
// newest pose here; the per-frame smoothing below does the actual moving.
it->second.targetPos = u.state.position;
it->second.targetRot = u.state.rotation;
}
it->second.lastSeen = now;
}
@@ -1143,10 +1228,25 @@ void Engine::Initiate()
for (auto it = remotes.begin(); it != remotes.end();) {
if (now - it->second.lastSeen > 2.0) {
rendersystem->RemoveRenderable(it->second.character);
if (it->second.body) physicsengine->RemoveObject(it->second.character->body);
delete it->second.character;
it = remotes.erase(it);
}
else {
// Frame-rate independent ease toward the latest pose (no matter how packets were batched)
t& rt = it->second.character->body->t;
const glm::vec3 before = rt.GetTranslation();
if (glm::distance(before, it->second.targetPos) > 6.0f)
rt.TranslateTo(it->second.targetPos); // respawn / big jump: snap
else {
const float k = 1.0f - std::exp(-deltatime * 25.0f);
rt.TranslateTo(glm::mix(before, it->second.targetPos, k));
}
rt.RotateToQuaternion(glm::slerp(rt.GetRotationQuaternion(), it->second.targetRot,
1.0f - std::exp(-deltatime * 25.0f)));
// Kinematic body: its velocity makes cubes get pushed like the client's own body does there
if (it->second.body && deltatime > 0.0f)
it->second.body->linearvelocity = (rt.GetTranslation() - before) / deltatime;
it->second.character->Step(deltatime);
++it;
}
+2
View File
@@ -37,6 +37,7 @@ class Mesh : public Object , public Renderable, public t_package , public MeshDa
protected:
public:
Texture* meshTexture = nullptr;
glm::vec4 colorOverride = glm::vec4(0.0f); // rgb + strength; 0 = use the texture's own colour
VAO vao;
VBO vbo;
@@ -469,6 +470,7 @@ public:
else glBindTexture(GL_TEXTURE_2D, 0);
ShaderProgram.SetMat4("modl", topass);
ShaderProgram.Set4F("colorOverride", colorOverride);
vao.Bind();
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
vao.Unbind();
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

+3
View File
@@ -39,6 +39,7 @@ uniform vec3 viewPos;
uniform sampler2DShadow shadowMap;
uniform sampler2D tex;
uniform vec4 colorOverride = vec4(0.0); // rgb replaces the texture colour when w > 0
float ShadowCalculation(vec4 fragposlightspace)
{
@@ -79,6 +80,8 @@ void main()
discard;
}
lightless.rgb = mix(lightless.rgb, colorOverride.rgb, colorOverride.w);
vec3 norm = normalize(normal);
vec3 viewDir = normalize(viewPos - FragPos);