diff --git a/Engine.cpp b/Engine.cpp index 47d8f91..65a7b6f 100644 --- a/Engine.cpp +++ b/Engine.cpp @@ -101,6 +101,7 @@ Engine::Engine() rendersystem = new RenderSystem(); rendersystem->Initialize(); physicsengine = new Physics(); + physicsengine->gravity *= 1.25f; // slightly stronger gravity to suit the larger map mainf = new Folder(); camera = new Camera(); camera->t.TranslateTo({ 0.0f, 5.0f, 0.0f }); @@ -248,6 +249,29 @@ void Engine::Initiate() crosshair->Dot = true; crosshair->Apply(); + // Hit marker: an X around the crosshair built from small squares (Box has no rotation), + // white for a normal hit and red for a headshot / kill. Fades out after each hit. + std::vector hitMarker; + for (int corner = 0; corner < 4; ++corner) { + const float sx = (corner & 1) ? 1.0f : -1.0f, sy = (corner & 2) ? 1.0f : -1.0f; + for (int step = 0; step < 3; ++step) { + const float d = 7.0f + step * 3.5f; + Box* b = new Box(); + b->Opacity = 0.0f; + b->t2d.center = { 0.5f, 0.5f }; + b->t2d.position = { 0.5f, 0.5f, sx * d, sy * d }; + b->t2d.size = { 0.0f, 0.0f, 3.5f, 3.5f }; + b->BindToRenderSystem(); + hitMarker.push_back(b); + } + } + float hitMarkerTime = 0.0f; + const float hitMarkerDuration = 0.25f; + auto ShowHitMarker = [&](bool strong) { + hitMarkerTime = hitMarkerDuration; + for (Box* b : hitMarker) b->Color = strong ? glm::vec3(1.0f, 0.15f, 0.15f) : glm::vec3(1.0f); + }; + TextBox* fpsCounter = new TextBox(); fpsCounter->font = arial; fpsCounter->text = "TEST1231231123"; @@ -270,6 +294,18 @@ void Engine::Initiate() netText->t2d.size = { 0.0f, 0.0f, 360.0f, 40.0f }; netText->BindToRenderSystem(); + // Full-screen green flash when the player takes damage (drawn before the HUD text) + Box* damageFlash = new Box(); + damageFlash->Color = { 0.0f, 1.0f, 0.2f }; + damageFlash->Opacity = 0.0f; + damageFlash->t2d.position = { 0.0f, 0.0f, 0.0f, 0.0f }; + damageFlash->t2d.center = { 0.0f, 0.0f }; + damageFlash->t2d.size = { 1.0f, 1.0f, 0.0f, 0.0f }; + damageFlash->BindToRenderSystem(); + int lastPlayerHealth = 100; + float damageFlashTime = 0.0f; + const float damageFlashDuration = 0.35f; + TextBox* hpText = new TextBox(); hpText->font = arial; hpText->text = "100/100 HP"; @@ -322,10 +358,11 @@ void Engine::Initiate() // Punch hitbox only: intentionally not bound to the render system, so it isn't drawn mainf->AddChild(debugcube); + const float mapScale = 1.5f; // uniform scale applied to the floor and every map prop Mesh* floor = CreateCubeMesh(); floor->name = "floor"; floor->meshTexture = blanktex; - floor->t.ScaleTo({ 80.0f, 2.0f, 40.0f }); + floor->t.ScaleTo({ 80.0f * mapScale, 2.0f, 40.0f * mapScale }); floor->t.TranslateTo({ 0.0f, -1.0f, 0.0f }); floor->BindToRenderSystem(); mainf->AddChild(floor); @@ -340,6 +377,8 @@ void Engine::Initiate() // pos = centre of the block's footprint on the floor; it sits on y = 0 and is `size.y` tall. // Walls are frictionless (player friction is 0 too) so you slide along them. auto AddBlock = [&](const char* name, glm::vec3 pos, glm::vec3 size, bool slippery = false) { + pos *= mapScale; + size *= mapScale; Mesh* m = CreateCubeMesh(); m->name = name; m->meshTexture = blanktex; @@ -362,15 +401,15 @@ void Engine::Initiate() } }; - // Map is a rectangle: x in [-40, 40], z in [-20, 20] + // 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); // 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, -2.0f, 0.0f }, { 7.0f, 7.0f, 7.0f }); - AddBlock("cube", { 24.0f, 2.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, 7.0f, 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 }); @@ -511,8 +550,9 @@ void Engine::Initiate() // Random respawn spot on the floor (NPC and player), kept clear of every map prop std::mt19937 respawnRng(std::random_device{}()); + const float respawnHeight = 10.0f; // respawn in the air and drop to the floor auto RandomSpawn = [&]() -> glm::vec3 { - std::uniform_real_distribution spreadX(-37.0f, 37.0f), spreadZ(-17.0f, 17.0f); + std::uniform_real_distribution spreadX(-37.0f * mapScale, 37.0f * mapScale), spreadZ(-17.0f * mapScale, 17.0f * mapScale); const float margin = 1.5f; for (;;) { const float x = spreadX(respawnRng), z = spreadZ(respawnRng); @@ -522,7 +562,7 @@ void Engine::Initiate() z > f.minZ - margin && z < f.maxZ + margin) { blocked = true; break; } } if (blocked) continue; - return { x, 0.9f, z }; + return { x, respawnHeight, z }; } }; @@ -560,6 +600,7 @@ void Engine::Initiate() const bool headshot = heightFrac >= headFraction; const int damage = headshot ? maxHealth : bodyDamage; camera->AddScreenshake(0.05f); + ShowHitMarker(headshot); if (targetRemoteId >= 0) { // Another player: their machine owns their health, so tell it @@ -654,6 +695,7 @@ void Engine::Initiate() npcBody->WakeUp(); npcBody->linearvelocity += playerFront * 5.0f + glm::vec3(0.0f, 3.0f, 0.0f); camera->AddScreenshake(0.15f); // landing a punch feels impactful + ShowHitMarker(false); npc->body->Health -= punchDamage; std::cout << "PUNCH - NPC HP " << npc->body->Health << "\n"; @@ -676,6 +718,7 @@ void Engine::Initiate() ph.knockback = playerFront * 5.0f + glm::vec3(0.0f, 3.0f, 0.0f); if (lan && lan->IsActive()) lan->SendHit((uint8_t)kv.first, ph); camera->AddScreenshake(0.15f); + ShowHitMarker(false); std::cout << "PUNCH - player " << kv.first << "\n"; } } @@ -883,6 +926,14 @@ void Engine::Initiate() } fpsCounter->text = std::to_string(measuredFps) + " FPS"; } + 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 + if (testcharacter->body->Health < lastPlayerHealth) + damageFlashTime = damageFlashDuration; + lastPlayerHealth = testcharacter->body->Health; + damageFlashTime = std::max(damageFlashTime - deltatime, 0.0f); + damageFlash->Opacity = 0.45f * (damageFlashTime / damageFlashDuration); // Player respawn: at 0 HP or after falling off the map, teleport to a random spot if (testcharacter->body->Health <= 0 || testcharacter->body->t.GetTranslation().y < -20.0f) { testcharacter->body->Health = maxHealth; diff --git a/Keyboard.h b/Keyboard.h index 3e3c373..5d4b5ab 100644 --- a/Keyboard.h +++ b/Keyboard.h @@ -16,8 +16,8 @@ public: Invalid }; - Event() : type(Type::Invalid), code(0u) {} - Event(Type typ, unsigned char cod) : type(typ), code(cod) {} + Event() : type(Type::Invalid), code(0) {} + Event(Type typ, int cod) : type(typ), code(cod) {} bool IsPress() const noexcept { return type == Type::Press; }; @@ -28,7 +28,7 @@ public: return type != Type::Invalid; }; - unsigned char GetCode() const noexcept { + int GetCode() const noexcept { return code; } @@ -37,7 +37,7 @@ public: } private: Type type; - unsigned char code; + int code; }; public: @@ -45,8 +45,8 @@ public: ~Keyboard() = default; Keyboard(const Keyboard&) = delete; - bool IsKeyDown(const unsigned char key) const noexcept { - return keys[key]; + bool IsKeyDown(const int key) const noexcept { + return key >= 0 && key < (int)numerodekeys && keys[key]; } bool IsKeyEmpty() const noexcept { @@ -92,12 +92,14 @@ public: TrimBuffer(charbuffer); } - void KeyDown(const unsigned char key) noexcept { + void KeyDown(const int key) noexcept { + if (key < 0 || key >= (int)numerodekeys) return; // GLFW_KEY_UNKNOWN etc. keybuffer.push(Event(Event::Type::Press, key)); keys[key] = true; TrimBuffer(keybuffer); } - void KeyUp(const unsigned char key) noexcept { + void KeyUp(const int key) noexcept { + if (key < 0 || key >= (int)numerodekeys) return; keybuffer.push(Event(Event::Type::Release, key)); keys[key] = false; TrimBuffer(keybuffer); @@ -129,7 +131,7 @@ private: } bool AutorepeatEnabled = false; - static constexpr unsigned int numerodekeys = 256u; + static constexpr unsigned int numerodekeys = 512u; // GLFW key codes go up to ~348 static constexpr unsigned int bufferSize = 16u; std::bitset keys;