From 90a843946a3af5a5ebfe588d8fe15cb51c2289bc Mon Sep 17 00:00:00 2001 From: Ethan Adams Date: Thu, 27 Jun 2024 00:02:26 -0400 Subject: [PATCH] Added cycle weapons command and the ability to hold more than one weapon. --- YuppleMayham/include/gameplay/ai.h | 4 +- YuppleMayham/include/gameplay/entity.h | 5 +- YuppleMayham/include/gameplay/gameactor.h | 24 ++++--- YuppleMayham/include/gameplay/input.h | 10 ++- .../include/gameplay/weapons/weapon.h | 4 ++ YuppleMayham/include/utility/command.h | 21 +++++++ YuppleMayham/include/utility/mousestate.h | 2 + YuppleMayham/src/gameplay/ai.cpp | 4 +- YuppleMayham/src/gameplay/game.cpp | 1 + YuppleMayham/src/gameplay/gameactor.cpp | 63 ++++++++++++++----- YuppleMayham/src/gameplay/input.cpp | 7 ++- YuppleMayham/src/gameplay/scene.cpp | 14 +++-- YuppleMayham/src/gameplay/weapons/weapon.cpp | 22 ++++--- YuppleMayham/src/utility/command.cpp | 5 +- 14 files changed, 139 insertions(+), 47 deletions(-) diff --git a/YuppleMayham/include/gameplay/ai.h b/YuppleMayham/include/gameplay/ai.h index ac04ff7..89b08da 100644 --- a/YuppleMayham/include/gameplay/ai.h +++ b/YuppleMayham/include/gameplay/ai.h @@ -14,7 +14,7 @@ enum class AIState { Patrol }; -class AI { +class AI : public std::enable_shared_from_this { public: AI(const std::shared_ptr& actor, const std::shared_ptr& raycaster); void update(); @@ -30,7 +30,7 @@ public: private: AIState state; std::shared_ptr raycaster; - std::shared_ptr behaviour; + std::shared_ptr behaviour; std::shared_ptr actor; std::shared_ptr target; }; diff --git a/YuppleMayham/include/gameplay/entity.h b/YuppleMayham/include/gameplay/entity.h index 6c5b623..4741c54 100644 --- a/YuppleMayham/include/gameplay/entity.h +++ b/YuppleMayham/include/gameplay/entity.h @@ -9,7 +9,8 @@ class Camera; struct PhysicsComponent; - +// TODO: Allow speed to be changed and add speed as creation value in XML File! +// TODO: Create Entity System that loads entity types and creates them in scene according to name. class Entity { public: @@ -18,7 +19,7 @@ public: position(glm::vec3(0.0f)), scale(glm::vec3(1.0f)), rotation(0.0f), - speed(5.0f) {}; + speed(20.0f) {}; virtual ~Entity() {}; void setPosition(const glm::vec3& position); diff --git a/YuppleMayham/include/gameplay/gameactor.h b/YuppleMayham/include/gameplay/gameactor.h index d117840..1f52643 100644 --- a/YuppleMayham/include/gameplay/gameactor.h +++ b/YuppleMayham/include/gameplay/gameactor.h @@ -3,6 +3,7 @@ #include #include +#include #include "gameplay/entity.h" #include "utility/mousestate.h" @@ -12,6 +13,9 @@ class AI; class Weapon; class EventManager; +// TODO: Finish weapon cycling code and add default weapon to every actor +// TODO: Add ammo system, then work on some basic UI design + class GameActor : public Entity { public: @@ -19,12 +23,12 @@ public: ~GameActor(); void addComponent(std::shared_ptr component); - void equipWeapon(std::shared_ptr weapon); - void attachEventManager(std::shared_ptr eventManager); + void pickupWeapon(std::shared_ptr weapon); + void hookEventManager(std::shared_ptr eventManager); void update(float deltaTime) override; void render(const std::shared_ptr& camera) override; - const std::shared_ptr& getHeldWeapon() const; + const std::optional>& getHeldWeapon() const; const int getActorID() const { return actorid; } void setRotation(const float& rotation) override; @@ -39,13 +43,19 @@ public: void moveBackward(); void strafeLeft(); void strafeRight(); - void fireWeapon(); + void fireWeapon() const; + void cycleUpWeapons(); + void cycleDownWeapons(); + void cycleWeapons(const MouseState&); void followMouse(const MouseState&); private: + using component_vector_t = std::vector>; + using weapon_vector_t = std::vector>; + int actorid; - std::vector> components; - std::shared_ptr ai; - std::shared_ptr currentWeapon = nullptr; + component_vector_t components; + weapon_vector_t weapons; + size_t currentWeaponIndex = 0; std::shared_ptr eventManager; }; diff --git a/YuppleMayham/include/gameplay/input.h b/YuppleMayham/include/gameplay/input.h index 71112e9..5a59814 100644 --- a/YuppleMayham/include/gameplay/input.h +++ b/YuppleMayham/include/gameplay/input.h @@ -15,7 +15,8 @@ enum { MOUSE_BUTTON_LEFT = 1, MOUSE_BUTTON_RIGHT = 2, MOUSE_BUTTON_MIDDLE = 4, - MOUSE_MOTION = 8 + MOUSE_MOTION = 8, + MOUSE_SCROLL = 16 }; class Keyboard @@ -49,6 +50,8 @@ protected: mouse_state.x = static_cast(e.motion.x); mouse_state.y = static_cast(e.motion.y); } + if (e.type == SDL_MOUSEWHEEL) + mouse_state.scroll = e.wheel.preciseY; } MouseState mouse_state; std::unordered_map mouseButtons; @@ -85,14 +88,17 @@ public: void bindMouseMotion(MouseCommand* command) { mouseMotionCommand = command; } + void bindMouseScroll(MouseCommand* command) { + mouseScrollCommand = command; + } void handleInput(); private: GameActor* actor = nullptr; - // final int is delay std::unordered_map keyCommands; std::unordered_map mouseCommands; MouseCommand* mouseMotionCommand = nullptr; + MouseCommand* mouseScrollCommand = nullptr; }; #endif // _H_INPUT_H \ No newline at end of file diff --git a/YuppleMayham/include/gameplay/weapons/weapon.h b/YuppleMayham/include/gameplay/weapons/weapon.h index 800690a..03fb8d8 100644 --- a/YuppleMayham/include/gameplay/weapons/weapon.h +++ b/YuppleMayham/include/gameplay/weapons/weapon.h @@ -30,6 +30,8 @@ public: Weapon(const WeaponData* data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager); void setWielder(const std::shared_ptr& wielder) { this->wielder = wielder; } + void wield() { wielded = true; } + void putaway() { wielded = false; } void addComponent(const std::shared_ptr& component); void attachScript(const std::shared_ptr& script); @@ -79,6 +81,8 @@ private: std::shared_ptr bulletModifer; std::shared_ptr wielder; + // is the weapon currently active + bool wielded = false; int lastFireTime = 0; Direction lastDir; diff --git a/YuppleMayham/include/utility/command.h b/YuppleMayham/include/utility/command.h index 6e3e0e1..b7f3ab1 100644 --- a/YuppleMayham/include/utility/command.h +++ b/YuppleMayham/include/utility/command.h @@ -86,6 +86,27 @@ public: void execute(GameActor& actor) override; }; +class CycleUpCommand : public Command +{ +public: + CycleUpCommand() {} + void execute(GameActor& actor) override; +}; + +class CycleDownCommand : public Command +{ +public: + CycleDownCommand() {} + void execute(GameActor& actor) override; +}; + +class CycleCommand : public MouseCommand +{ +public: + CycleCommand() {} + void execute(GameActor& actor, const MouseState& mouse_state) override; +}; + class ShootCommand : public MouseCommand { public: diff --git a/YuppleMayham/include/utility/mousestate.h b/YuppleMayham/include/utility/mousestate.h index b7b3797..a04cd90 100644 --- a/YuppleMayham/include/utility/mousestate.h +++ b/YuppleMayham/include/utility/mousestate.h @@ -4,6 +4,8 @@ struct MouseState { float x = 0.0f; float y = 0.0f; + + float scroll = 0.0f; }; #endif \ No newline at end of file diff --git a/YuppleMayham/src/gameplay/ai.cpp b/YuppleMayham/src/gameplay/ai.cpp index c63f57c..f7f394c 100644 --- a/YuppleMayham/src/gameplay/ai.cpp +++ b/YuppleMayham/src/gameplay/ai.cpp @@ -11,11 +11,11 @@ AI::AI(const std::shared_ptr& actor, const std::shared_ptr void AI::attachBehaviourScript(const std::shared_ptr& behaviour) { - // passing out instance of raycaster this Ai into our scripting api + // passing out instance of raycaster and this AI into our scripting api // pay special attention each ai script has control of only their own instance of ai! this->behaviour = behaviour; this->behaviour->lua["raycaster"] = raycaster; - this->behaviour->lua["ai"] = std::shared_ptr(this); + this->behaviour->lua["ai"] = shared_from_this(); } void AI::update() diff --git a/YuppleMayham/src/gameplay/game.cpp b/YuppleMayham/src/gameplay/game.cpp index 54be37b..26d38cf 100644 --- a/YuppleMayham/src/gameplay/game.cpp +++ b/YuppleMayham/src/gameplay/game.cpp @@ -41,6 +41,7 @@ bool Game::init() inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, new ShootCommand()); inputHandler->bindMouseMotion(new FollowMouseCommand()); + inputHandler->bindMouseScroll(new CycleCommand()); game_state |= GAME_RUNNING; resourceManager = std::make_shared(); diff --git a/YuppleMayham/src/gameplay/gameactor.cpp b/YuppleMayham/src/gameplay/gameactor.cpp index 0fce811..9c834dc 100644 --- a/YuppleMayham/src/gameplay/gameactor.cpp +++ b/YuppleMayham/src/gameplay/gameactor.cpp @@ -14,21 +14,25 @@ void GameActor::addComponent(std::shared_ptr component) components.push_back(component); } -const std::shared_ptr& GameActor::getHeldWeapon() const +const std::optional>& GameActor::getHeldWeapon() const { - return currentWeapon; + return (weapons.empty() || currentWeaponIndex >= weapons.size()) ? std::nullopt : std::make_optional(weapons[currentWeaponIndex]); } -void GameActor::equipWeapon(std::shared_ptr weapon) +void GameActor::pickupWeapon(std::shared_ptr weapon) { - currentWeapon = std::move(weapon); - if (currentWeapon) - currentWeapon->setWielder(std::shared_ptr(this)); + weapon->setWielder(std::shared_ptr(this)); + weapons.push_back(weapon); + getHeldWeapon().value()->putaway(); + currentWeaponIndex = weapons.size() - 1; + getHeldWeapon().value()->wield(); } -void GameActor::attachEventManager(std::shared_ptr eventManager) +void GameActor::hookEventManager(std::shared_ptr eventManager) { this->eventManager = eventManager; + for (auto& weapon : weapons) + weapon->hookEventManager(eventManager); } void GameActor::setRotation(const float& rotation) @@ -48,8 +52,8 @@ void GameActor::update(float deltaTime) for (auto& component : components) component->update(); - if (currentWeapon) - currentWeapon->update(deltaTime); + for (auto& weapon : weapons) + weapon->update(deltaTime); } void GameActor::render(const std::shared_ptr& camera) @@ -65,8 +69,8 @@ void GameActor::render(const std::shared_ptr& camera) component->bind(); component->render(); } - if (currentWeapon) - currentWeapon->render(camera); + for (auto& weapon : weapons) + weapon->render(camera); isMoving = false; } @@ -76,11 +80,28 @@ void GameActor::moveLeft() { if (physics) physics->rigidBody.applyForce(glm::vec void GameActor::moveRight(){ if (physics) physics->rigidBody.applyForce(glm::vec3( 1.f, 0.f, 0.f), 1500.25f); isMoving = true; } // top-down shooter mode controls -void GameActor::strafeLeft() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; } -void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; } -void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; } -void GameActor::moveForward() { if (physics) physics->rigidBody.applyForce(glm::vec3(cos(glm::radians(rotation)), sin(glm::radians(rotation)), 0.f), 1000.f); isMoving = true; } -void GameActor::fireWeapon() { if (currentWeapon) currentWeapon->shoot(); } +void GameActor::fireWeapon()const { if (auto& weapon = getHeldWeapon()) weapon.value()->shoot(); } +void GameActor::cycleUpWeapons() { + if (!weapons.empty()) { + weapons[currentWeaponIndex]->putaway(); + currentWeaponIndex = (currentWeaponIndex + 1) % weapons.size(); + weapons[currentWeaponIndex]->wield(); + } +} +void GameActor::cycleDownWeapons() { + if (!weapons.empty()) { + weapons[currentWeaponIndex]->putaway(); + currentWeaponIndex = (currentWeaponIndex + weapons.size() - 1) % weapons.size(); + weapons[currentWeaponIndex]->wield(); + } +} +void GameActor::cycleWeapons(const MouseState& mouse_state) +{ + if (mouse_state.scroll < 0) + cycleUpWeapons(); + else if (mouse_state.scroll > 0) + cycleDownWeapons(); +} void GameActor::followMouse(const MouseState& mouse_state) { glm::vec2 direction = glm::vec2(mouse_state.x, mouse_state.y) - glm::vec2(localPosition.x + 0.5f * scale.x, localPosition.y + 0.5f * scale.y); @@ -91,3 +112,13 @@ void GameActor::followMouse(const MouseState& mouse_state) //setRotation(glm::degrees(glm::atan(direction.y, direction.x))); this->rotation = newRotation; } +void GameActor::strafeLeft() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; } +void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; } +void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; } +void GameActor::moveForward() { + if (physics) { + physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed; + physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed; + } + isMoving = true; +} diff --git a/YuppleMayham/src/gameplay/input.cpp b/YuppleMayham/src/gameplay/input.cpp index e62a751..b4e1edc 100644 --- a/YuppleMayham/src/gameplay/input.cpp +++ b/YuppleMayham/src/gameplay/input.cpp @@ -34,6 +34,9 @@ void InputHandler::handleInput() } } } - if (!mouseMotionCommand) return; - mouseMotionCommand->execute(*actor, mouse_state); + if (mouseMotionCommand) + mouseMotionCommand->execute(*actor, mouse_state); + if (mouseScrollCommand) + mouseScrollCommand->execute(*actor, mouse_state); + mouse_state.scroll = 0.0f; // clear mouse scroll since we have handled the event. } diff --git a/YuppleMayham/src/gameplay/scene.cpp b/YuppleMayham/src/gameplay/scene.cpp index 8cf63d1..8be8daf 100644 --- a/YuppleMayham/src/gameplay/scene.cpp +++ b/YuppleMayham/src/gameplay/scene.cpp @@ -16,6 +16,8 @@ #include "utility/raycaster.h" #include "utility/debugdraw.h" +#include + // Scene xml files, should contain a node called that holds the sprite location /* like this: @@ -67,12 +69,13 @@ void Scene::loadDebugShooterScene() } else entitySprite = resourceManager->loadSpriteAnimated(entityData.sprite, entityData.frameSize); + auto defaultWeapon = resourceManager->loadWeapon("pistolGun", weaponShader, bubbleShader); auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader); - entityWeapon->hookEventManager(eventManager); entity->addComponent(std::make_shared(entitySprite, eventManager)); - entity->equipWeapon(entityWeapon); - entity->attachEventManager(eventManager); + entity->pickupWeapon(defaultWeapon); + entity->pickupWeapon(entityWeapon); + entity->hookEventManager(eventManager); entity->setPosition(glm::vec3(entityData.x * mapData.tileSize, entityData.y * mapData.tileSize, 0.f)); entity->setScale(glm::vec3(mapData.tileSize, mapData.tileSize, 1.f)); @@ -119,7 +122,7 @@ std::shared_ptr Scene::getPlayer() const void Scene::update(float deltaTime) { - for (auto& [id, e] : entities) + for (const auto& [id, e] : entities) { e->update(deltaTime); if (camera->getTarget() == e.get()) @@ -151,7 +154,8 @@ void Scene::hookSceneEvents() std::shared_ptr shooter = getGameActorByID(collideEvent->ownerID); std::shared_ptr target = getGameActorByID(collideEvent->victimID); if (shooter && target) - shooter->getHeldWeapon()->onHitCallback(target, collideEvent->bullet, collideEvent->normal); + if (auto& weapon = shooter->getHeldWeapon()) + weapon.value()->onHitCallback(target, collideEvent->bullet, collideEvent->normal); }); } diff --git a/YuppleMayham/src/gameplay/weapons/weapon.cpp b/YuppleMayham/src/gameplay/weapons/weapon.cpp index e69d741..0f9709c 100644 --- a/YuppleMayham/src/gameplay/weapons/weapon.cpp +++ b/YuppleMayham/src/gameplay/weapons/weapon.cpp @@ -102,22 +102,28 @@ void Weapon::onHitCallback(std::shared_ptr target, std::shared_ptrupdate(); + for (auto& component : components) + component->update(); + } bulletManager->update(deltaTime); } void Weapon::render(const std::shared_ptr& camera) { Entity::render(camera); - for (auto& component : components) + if (wielded) { - component->bind(); - component->render(); + for (auto& component : components) + { + component->bind(); + component->render(); + } } bulletManager->render(camera); } diff --git a/YuppleMayham/src/utility/command.cpp b/YuppleMayham/src/utility/command.cpp index 48d2290..14812de 100644 --- a/YuppleMayham/src/utility/command.cpp +++ b/YuppleMayham/src/utility/command.cpp @@ -11,6 +11,9 @@ void MoveForwardCommand::execute(GameActor& actor) { actor.moveForward(); } void MoveBackwardCommand::execute(GameActor& actor) { actor.moveBackward(); } void StrafeLeftCommand::execute(GameActor& actor) { actor.strafeLeft(); } void StrafeRightCommand::execute(GameActor& actor) { actor.strafeRight(); } -void ShootCommand::execute(GameActor& actor, const MouseState& mouse_state) { actor.fireWeapon(); } +void CycleUpCommand::execute(GameActor& actor) { actor.cycleUpWeapons(); } +void CycleDownCommand::execute(GameActor& actor) { actor.cycleDownWeapons(); } +void ShootCommand::execute(GameActor& actor, const MouseState& mouse_state) { actor.fireWeapon(); } +void CycleCommand::execute(GameActor& actor, const MouseState& mouse_state) { actor.cycleWeapons(mouse_state); } void FollowMouseCommand::execute(GameActor& actor, const MouseState& mouse_state) { actor.followMouse(mouse_state); }