Added cycle weapons command and the ability to hold more than one weapon.

This commit is contained in:
Ethan Adams 2024-06-27 00:02:26 -04:00
parent 1f38730c96
commit 90a843946a
14 changed files with 139 additions and 47 deletions

View file

@ -14,7 +14,7 @@ enum class AIState {
Patrol Patrol
}; };
class AI { class AI : public std::enable_shared_from_this<AI> {
public: public:
AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>& raycaster); AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>& raycaster);
void update(); void update();

View file

@ -9,7 +9,8 @@
class Camera; class Camera;
struct PhysicsComponent; 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 class Entity
{ {
public: public:
@ -18,7 +19,7 @@ public:
position(glm::vec3(0.0f)), position(glm::vec3(0.0f)),
scale(glm::vec3(1.0f)), scale(glm::vec3(1.0f)),
rotation(0.0f), rotation(0.0f),
speed(5.0f) {}; speed(20.0f) {};
virtual ~Entity() {}; virtual ~Entity() {};
void setPosition(const glm::vec3& position); void setPosition(const glm::vec3& position);

View file

@ -3,6 +3,7 @@
#include <unordered_map> #include <unordered_map>
#include <SDL_timer.h> #include <SDL_timer.h>
#include <optional>
#include "gameplay/entity.h" #include "gameplay/entity.h"
#include "utility/mousestate.h" #include "utility/mousestate.h"
@ -12,6 +13,9 @@ class AI;
class Weapon; class Weapon;
class EventManager; 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 class GameActor : public Entity
{ {
public: public:
@ -19,12 +23,12 @@ public:
~GameActor(); ~GameActor();
void addComponent(std::shared_ptr<Component> component); void addComponent(std::shared_ptr<Component> component);
void equipWeapon(std::shared_ptr<Weapon> weapon); void pickupWeapon(std::shared_ptr<Weapon> weapon);
void attachEventManager(std::shared_ptr<EventManager> eventManager); void hookEventManager(std::shared_ptr<EventManager> eventManager);
void update(float deltaTime) override; void update(float deltaTime) override;
void render(const std::shared_ptr<Camera>& camera) override; void render(const std::shared_ptr<Camera>& camera) override;
const std::shared_ptr<Weapon>& getHeldWeapon() const; const std::optional<std::shared_ptr<Weapon>>& getHeldWeapon() const;
const int getActorID() const { return actorid; } const int getActorID() const { return actorid; }
void setRotation(const float& rotation) override; void setRotation(const float& rotation) override;
@ -39,13 +43,19 @@ public:
void moveBackward(); void moveBackward();
void strafeLeft(); void strafeLeft();
void strafeRight(); void strafeRight();
void fireWeapon(); void fireWeapon() const;
void cycleUpWeapons();
void cycleDownWeapons();
void cycleWeapons(const MouseState&);
void followMouse(const MouseState&); void followMouse(const MouseState&);
private: private:
using component_vector_t = std::vector<std::shared_ptr<Component>>;
using weapon_vector_t = std::vector<std::shared_ptr<Weapon>>;
int actorid; int actorid;
std::vector<std::shared_ptr<Component>> components; component_vector_t components;
std::shared_ptr<AI> ai; weapon_vector_t weapons;
std::shared_ptr<Weapon> currentWeapon = nullptr; size_t currentWeaponIndex = 0;
std::shared_ptr<EventManager> eventManager; std::shared_ptr<EventManager> eventManager;
}; };

View file

@ -15,7 +15,8 @@ enum {
MOUSE_BUTTON_LEFT = 1, MOUSE_BUTTON_LEFT = 1,
MOUSE_BUTTON_RIGHT = 2, MOUSE_BUTTON_RIGHT = 2,
MOUSE_BUTTON_MIDDLE = 4, MOUSE_BUTTON_MIDDLE = 4,
MOUSE_MOTION = 8 MOUSE_MOTION = 8,
MOUSE_SCROLL = 16
}; };
class Keyboard class Keyboard
@ -49,6 +50,8 @@ protected:
mouse_state.x = static_cast<float>(e.motion.x); mouse_state.x = static_cast<float>(e.motion.x);
mouse_state.y = static_cast<float>(e.motion.y); mouse_state.y = static_cast<float>(e.motion.y);
} }
if (e.type == SDL_MOUSEWHEEL)
mouse_state.scroll = e.wheel.preciseY;
} }
MouseState mouse_state; MouseState mouse_state;
std::unordered_map<Uint8, bool> mouseButtons; std::unordered_map<Uint8, bool> mouseButtons;
@ -85,14 +88,17 @@ public:
void bindMouseMotion(MouseCommand* command) { void bindMouseMotion(MouseCommand* command) {
mouseMotionCommand = command; mouseMotionCommand = command;
} }
void bindMouseScroll(MouseCommand* command) {
mouseScrollCommand = command;
}
void handleInput(); void handleInput();
private: private:
GameActor* actor = nullptr; GameActor* actor = nullptr;
// final int is delay
std::unordered_map<SDL_Keycode, CommandWithDelay> keyCommands; std::unordered_map<SDL_Keycode, CommandWithDelay> keyCommands;
std::unordered_map<Uint8, MouseCommandWithDelay> mouseCommands; std::unordered_map<Uint8, MouseCommandWithDelay> mouseCommands;
MouseCommand* mouseMotionCommand = nullptr; MouseCommand* mouseMotionCommand = nullptr;
MouseCommand* mouseScrollCommand = nullptr;
}; };
#endif // _H_INPUT_H #endif // _H_INPUT_H

View file

@ -30,6 +30,8 @@ public:
Weapon(const WeaponData* data, const std::shared_ptr<Shader>& weaponShader, const std::shared_ptr<Shader>& bulletShader, ResourceManager* resourceManager); Weapon(const WeaponData* data, const std::shared_ptr<Shader>& weaponShader, const std::shared_ptr<Shader>& bulletShader, ResourceManager* resourceManager);
void setWielder(const std::shared_ptr<GameActor>& wielder) { this->wielder = wielder; } void setWielder(const std::shared_ptr<GameActor>& wielder) { this->wielder = wielder; }
void wield() { wielded = true; }
void putaway() { wielded = false; }
void addComponent(const std::shared_ptr<Component>& component); void addComponent(const std::shared_ptr<Component>& component);
void attachScript(const std::shared_ptr<WeaponScript>& script); void attachScript(const std::shared_ptr<WeaponScript>& script);
@ -79,6 +81,8 @@ private:
std::shared_ptr<UTIL::RandomGenerator> bulletModifer; std::shared_ptr<UTIL::RandomGenerator> bulletModifer;
std::shared_ptr<GameActor> wielder; std::shared_ptr<GameActor> wielder;
// is the weapon currently active
bool wielded = false;
int lastFireTime = 0; int lastFireTime = 0;
Direction lastDir; Direction lastDir;

View file

@ -86,6 +86,27 @@ public:
void execute(GameActor& actor) override; 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 class ShootCommand : public MouseCommand
{ {
public: public:

View file

@ -4,6 +4,8 @@
struct MouseState { struct MouseState {
float x = 0.0f; float x = 0.0f;
float y = 0.0f; float y = 0.0f;
float scroll = 0.0f;
}; };
#endif #endif

View file

@ -11,11 +11,11 @@ AI::AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>
void AI::attachBehaviourScript(const std::shared_ptr<AIScript>& behaviour) void AI::attachBehaviourScript(const std::shared_ptr<AIScript>& 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! // pay special attention each ai script has control of only their own instance of ai!
this->behaviour = behaviour; this->behaviour = behaviour;
this->behaviour->lua["raycaster"] = raycaster; this->behaviour->lua["raycaster"] = raycaster;
this->behaviour->lua["ai"] = std::shared_ptr<AI>(this); this->behaviour->lua["ai"] = shared_from_this();
} }
void AI::update() void AI::update()

View file

@ -41,6 +41,7 @@ bool Game::init()
inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, new ShootCommand()); inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, new ShootCommand());
inputHandler->bindMouseMotion(new FollowMouseCommand()); inputHandler->bindMouseMotion(new FollowMouseCommand());
inputHandler->bindMouseScroll(new CycleCommand());
game_state |= GAME_RUNNING; game_state |= GAME_RUNNING;
resourceManager = std::make_shared<ResourceManager>(); resourceManager = std::make_shared<ResourceManager>();

View file

@ -14,21 +14,25 @@ void GameActor::addComponent(std::shared_ptr<Component> component)
components.push_back(component); components.push_back(component);
} }
const std::shared_ptr<Weapon>& GameActor::getHeldWeapon() const const std::optional<std::shared_ptr<Weapon>>& 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> weapon) void GameActor::pickupWeapon(std::shared_ptr<Weapon> weapon)
{ {
currentWeapon = std::move(weapon); weapon->setWielder(std::shared_ptr<GameActor>(this));
if (currentWeapon) weapons.push_back(weapon);
currentWeapon->setWielder(std::shared_ptr<GameActor>(this)); getHeldWeapon().value()->putaway();
currentWeaponIndex = weapons.size() - 1;
getHeldWeapon().value()->wield();
} }
void GameActor::attachEventManager(std::shared_ptr<EventManager> eventManager) void GameActor::hookEventManager(std::shared_ptr<EventManager> eventManager)
{ {
this->eventManager = eventManager; this->eventManager = eventManager;
for (auto& weapon : weapons)
weapon->hookEventManager(eventManager);
} }
void GameActor::setRotation(const float& rotation) void GameActor::setRotation(const float& rotation)
@ -48,8 +52,8 @@ void GameActor::update(float deltaTime)
for (auto& component : components) for (auto& component : components)
component->update(); component->update();
if (currentWeapon) for (auto& weapon : weapons)
currentWeapon->update(deltaTime); weapon->update(deltaTime);
} }
void GameActor::render(const std::shared_ptr<Camera>& camera) void GameActor::render(const std::shared_ptr<Camera>& camera)
@ -65,8 +69,8 @@ void GameActor::render(const std::shared_ptr<Camera>& camera)
component->bind(); component->bind();
component->render(); component->render();
} }
if (currentWeapon) for (auto& weapon : weapons)
currentWeapon->render(camera); weapon->render(camera);
isMoving = false; 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; } 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 // top-down shooter mode controls
void GameActor::strafeLeft() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; } void GameActor::fireWeapon()const { if (auto& weapon = getHeldWeapon()) weapon.value()->shoot(); }
void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; } void GameActor::cycleUpWeapons() {
void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; } if (!weapons.empty()) {
void GameActor::moveForward() { if (physics) physics->rigidBody.applyForce(glm::vec3(cos(glm::radians(rotation)), sin(glm::radians(rotation)), 0.f), 1000.f); isMoving = true; } weapons[currentWeaponIndex]->putaway();
void GameActor::fireWeapon() { if (currentWeapon) currentWeapon->shoot(); } 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) 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); 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))); //setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
this->rotation = newRotation; 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;
}

View file

@ -34,6 +34,9 @@ void InputHandler::handleInput()
} }
} }
} }
if (!mouseMotionCommand) return; if (mouseMotionCommand)
mouseMotionCommand->execute(*actor, mouse_state); 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.
} }

View file

@ -16,6 +16,8 @@
#include "utility/raycaster.h" #include "utility/raycaster.h"
#include "utility/debugdraw.h" #include "utility/debugdraw.h"
#include <execution>
// Scene xml files, should contain a node called <player> that holds the sprite location // Scene xml files, should contain a node called <player> that holds the sprite location
/* /*
like this: like this:
@ -67,12 +69,13 @@ void Scene::loadDebugShooterScene()
} }
else else
entitySprite = resourceManager->loadSpriteAnimated(entityData.sprite, entityData.frameSize); entitySprite = resourceManager->loadSpriteAnimated(entityData.sprite, entityData.frameSize);
auto defaultWeapon = resourceManager->loadWeapon("pistolGun", weaponShader, bubbleShader);
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader); auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader);
entityWeapon->hookEventManager(eventManager);
entity->addComponent(std::make_shared<SpriteComponent>(entitySprite, eventManager)); entity->addComponent(std::make_shared<SpriteComponent>(entitySprite, eventManager));
entity->equipWeapon(entityWeapon); entity->pickupWeapon(defaultWeapon);
entity->attachEventManager(eventManager); entity->pickupWeapon(entityWeapon);
entity->hookEventManager(eventManager);
entity->setPosition(glm::vec3(entityData.x * mapData.tileSize, entityData.y * mapData.tileSize, 0.f)); entity->setPosition(glm::vec3(entityData.x * mapData.tileSize, entityData.y * mapData.tileSize, 0.f));
entity->setScale(glm::vec3(mapData.tileSize, mapData.tileSize, 1.f)); entity->setScale(glm::vec3(mapData.tileSize, mapData.tileSize, 1.f));
@ -119,7 +122,7 @@ std::shared_ptr<GameActor> Scene::getPlayer() const
void Scene::update(float deltaTime) void Scene::update(float deltaTime)
{ {
for (auto& [id, e] : entities) for (const auto& [id, e] : entities)
{ {
e->update(deltaTime); e->update(deltaTime);
if (camera->getTarget() == e.get()) if (camera->getTarget() == e.get())
@ -151,7 +154,8 @@ void Scene::hookSceneEvents()
std::shared_ptr<GameActor> shooter = getGameActorByID(collideEvent->ownerID); std::shared_ptr<GameActor> shooter = getGameActorByID(collideEvent->ownerID);
std::shared_ptr<GameActor> target = getGameActorByID(collideEvent->victimID); std::shared_ptr<GameActor> target = getGameActorByID(collideEvent->victimID);
if (shooter && target) if (shooter && target)
shooter->getHeldWeapon()->onHitCallback(target, collideEvent->bullet, collideEvent->normal); if (auto& weapon = shooter->getHeldWeapon())
weapon.value()->onHitCallback(target, collideEvent->bullet, collideEvent->normal);
}); });
} }

View file

@ -102,23 +102,29 @@ void Weapon::onHitCallback(std::shared_ptr<GameActor> target, std::shared_ptr<Ph
void Weapon::update(float deltaTime) void Weapon::update(float deltaTime)
{ {
Entity::update(deltaTime); Entity::update(deltaTime);
if (wielded)
{
// move the weapon into place as the wielder rotates and moves // move the weapon into place as the wielder rotates and moves
if (wielder) if (wielder)
adjustWeapon(); adjustWeapon();
for (auto& component : components) for (auto& component : components)
component->update(); component->update();
}
bulletManager->update(deltaTime); bulletManager->update(deltaTime);
} }
void Weapon::render(const std::shared_ptr<Camera>& camera) void Weapon::render(const std::shared_ptr<Camera>& camera)
{ {
Entity::render(camera); Entity::render(camera);
if (wielded)
{
for (auto& component : components) for (auto& component : components)
{ {
component->bind(); component->bind();
component->render(); component->render();
} }
}
bulletManager->render(camera); bulletManager->render(camera);
} }

View file

@ -11,6 +11,9 @@ void MoveForwardCommand::execute(GameActor& actor) { actor.moveForward(); }
void MoveBackwardCommand::execute(GameActor& actor) { actor.moveBackward(); } void MoveBackwardCommand::execute(GameActor& actor) { actor.moveBackward(); }
void StrafeLeftCommand::execute(GameActor& actor) { actor.strafeLeft(); } void StrafeLeftCommand::execute(GameActor& actor) { actor.strafeLeft(); }
void StrafeRightCommand::execute(GameActor& actor) { actor.strafeRight(); } 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); } void FollowMouseCommand::execute(GameActor& actor, const MouseState& mouse_state) { actor.followMouse(mouse_state); }