#ifndef CHARACTER_MIND_CLASS #define CHARACTER_MIND_CLASS #include #include #include #include "CharacterAction.h" class Character; class Camera; // Decides what a character does, as actions. A character without a mind does nothing. // AI goes here later, as another kind of mind. class CharacterMind { public: virtual ~CharacterMind() = default; // Called once a frame, adds whatever the character should do this frame virtual void ProduceActions(const Character& character, std::vector& actions) = 0; }; // The player: WASD, relative to where the camera faces, shift tapped to dash or held to sprint, space to jump, left click to attack and the number keys for abilities, // aimed at whatever's under the crosshair. Looking around doesn't turn the // character, it faces where it walks, but it swings the way the camera looks. class KeyboardMind : public CharacterMind { public: explicit KeyboardMind(const Camera& camera) : camera(camera) {} // Off while the keys are busy with something else, like typing in the console bool enabled = true; // Off while the mouse is busy with something else, like clicking on menus bool mouseEnabled = true; void ProduceActions(const Character& character, std::vector& actions) override; private: const Camera& camera; bool prevAttackDown = false; bool prevJumpDown = false; bool prevShiftDown = false; double shiftPressedAt = 0.0; // glfwGetTime when shift went down float dashTapTime = 0.2f; // real seconds shift can be held and still count as a tap, longer is a sprint bool prevAbilityDown[9] = {}; }; // An enemy with a rifle. It keeps its distance from its target, strafing side to side and facing them, shoots // whenever it gets a moment and steps in to stab when they get close. Shots go where they were when it started // aiming, so moving dodges them. class RiflemanMind : public CharacterMind { public: RiflemanMind(const Character* target, float arenaHalfSize) : target(target), arenaHalfSize(arenaHalfSize) {} const Character* target; // who it's after, nullptr just stands there float arenaHalfSize; // how far the ground goes from the middle each way, so it doesn't back off the edge float closest = 6.0f; // meters it backs off inside of float farthest = 12.0f; // and closes in outside of float stabReach = 1.6f; // meters it stabs from float rushDistance = 3.5f; // they're this close and its stab is ready, it goes in for it float shotWaitMin = 0.8f; // real seconds it waits between shots, picked at random between these float shotWaitMax = 1.8f; float sweepMin = 2.2f; // meters it sweeps from, its step carrying it the rest of the way float sweepMax = 4.5f; float sprintDistance = 18.0f; // farther than this it sprints to catch up float dodgeChance = 0.5f; // of dashing aside when it sees a shot coming float dashWaitMin = 2.0f; // real seconds between dashes, picked at random between these float dashWaitMax = 4.5f; void ProduceActions(const Character& character, std::vector& actions) override; private: std::mt19937 rng{ std::random_device{}() }; double lastTime = -1.0; float shotWait = 1.0f; float dashWait = 1.0f; unsigned int seenUse = 0; // the last of their ability uses it decided whether to dodge float strafeWait = 0.0f; // until it picks which way to strafe again float strafeSign = 1.0f; glm::vec3 aim = glm::vec3(0.0f, 0.0f, 1.0f); // where the ability it's in was aimed }; #endif