diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b44570..d6eef9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,9 +35,11 @@ FetchContent_Declare( FetchContent_MakeAvailable(SDL3 SDL3_image SDL3_ttf) #list all files in Actors folder -set(ACTORS src/Actors/Actor.cpp) - -add_executable(Witch-Game main.cpp src/Game.cpp ${ACTORS}) +set(ACTORS src/Actors/Actor.cpp src/Actors/MovingActor.cpp src/Actors/ActorFactory.cpp) +#set(COMMANDS src/GameCommands/GameCommand.h src/GameCommands/GameCommand.h src/GameCommands/MoveUpCommand.h src/GameCommands/EndGameCommand.h ) +set(COMPONENTS src/Components/MoveComponent.cpp ) +set(DATA_STRUCTURES src/DataStructures/NormalizedDirection.cpp) +add_executable(Witch-Game main.cpp src/Game.cpp ${ACTORS} ${COMPONENTS} ${DATA_STRUCTURES}) target_compile_features(Witch-Game PRIVATE cxx_std_17) target_link_libraries(Witch-Game SDL3 SDL3_image SDL3_ttf) diff --git a/main.cpp b/main.cpp index 1b14a4b..3cf4455 100644 --- a/main.cpp +++ b/main.cpp @@ -1,8 +1,77 @@ -#include +#define SDL_MAIN_USE_CALLBACKS +#include +#include +#include #include "src/Game.h" -int main(int argc, char **argv) { - std::cout << "Hello, world!" << std::endl; - Game * game = new Game(); - delete game; +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + SDL_SetAppMetadata("Example Input Joystick Polling", "1.0", "com.example.input-joystick-polling"); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("Witch-Game", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + *appstate = new Game(window, renderer); + + + return SDL_APP_CONTINUE; /* carry on with the program! */ } + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + Game* game = (Game*) appstate; + switch(event->type) + { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + case SDL_EVENT_KEY_DOWN: + game->HandleKeyboardInput(*event); + break; + case SDL_EVENT_KEY_UP: + game->HandleKeyboardInput(*event); + break; + case SDL_EVENT_GAMEPAD_ADDED: + game->AddGamepad(event->gdevice.which); + break; + case SDL_EVENT_GAMEPAD_REMOVED: + game->RemoveGamepad(event->gdevice.which); + break; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + game->HandleGamepadButton(*event); + break; + case SDL_EVENT_GAMEPAD_BUTTON_UP: + game->HandleGamepadButton(*event); + break; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + game->HandleGamepadAxis(*event); + break; + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + Game* game = (Game*) appstate; + game ->Run(); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + delete appstate; + /* SDL will clean up the window/renderer for us. */ +} \ No newline at end of file diff --git a/src/Actors/Actor.cpp b/src/Actors/Actor.cpp index 62e0ecf..80271ca 100644 --- a/src/Actors/Actor.cpp +++ b/src/Actors/Actor.cpp @@ -1,19 +1,13 @@ -// SPDX-FileCopyrightText: 2025 -// SPDX-License-Identifier: MIT - #include "Actor.h" -Actor::Actor() +void Actor::SetPosition(float x, float y) { - + m_Hitbox.x = x; + m_Hitbox.y = y; } -Actor::Actor(const Actor& other) +void Actor::MoveBy(float x, float y) { - -} - -Actor::~Actor() -{ - -} + m_Hitbox.x += x; + m_Hitbox.y += y; +} \ No newline at end of file diff --git a/src/Actors/Actor.h b/src/Actors/Actor.h index 4b45627..14066d7 100644 --- a/src/Actors/Actor.h +++ b/src/Actors/Actor.h @@ -1,32 +1,23 @@ -// SPDX-FileCopyrightText: 2025 -// SPDX-License-Identifier: MIT - #ifndef ACTOR_H #define ACTOR_H -/** - * @todo write docs - */ -class Actor +#include +#include + +class Actor : public std::enable_shared_from_this { public: - /** - * Default constructor - */ - Actor(); - - /** - * Copy constructor - * - * @param other TODO - */ - Actor(const Actor& other); - - /** - * Destructor - */ - ~Actor(); + virtual ~Actor() {}; + //virtual void Update(float deltaTime) {}; + void SetPosition(float x, float y); + void MoveBy(float x, float y); + SDL_FRect GetHitbox(){return m_Hitbox;} + friend class ActorFactory; +protected: + virtual void Init() {}; + Actor(SDL_FRect hitbox) : m_Hitbox(hitbox) {}; + SDL_FRect m_Hitbox; }; #endif // ACTOR_H diff --git a/src/Actors/ActorFactory.cpp b/src/Actors/ActorFactory.cpp new file mode 100644 index 0000000..bcda890 --- /dev/null +++ b/src/Actors/ActorFactory.cpp @@ -0,0 +1,17 @@ +#include "ActorFactory.h" + +#include + +std::shared_ptr ActorFactory::CreateActor(SDL_FRect hitbox) +{ + auto actor = std::make_shared(Actor(hitbox)); + actor ->Init(); + return actor; +} + +std::shared_ptr ActorFactory::CreateMovingActor(SDL_FRect hitbox) +{ + auto actor = std::make_shared(MovingActor(hitbox)); + actor ->Init(); + return actor; +} \ No newline at end of file diff --git a/src/Actors/ActorFactory.h b/src/Actors/ActorFactory.h new file mode 100644 index 0000000..c1df167 --- /dev/null +++ b/src/Actors/ActorFactory.h @@ -0,0 +1,18 @@ +#ifndef ACTOR_FACTORY_H +#define ACTOR_FACTORY_H + +#include +#include +#include "Actor.h" +#include "MovingActor.h" + +class ActorFactory +{ +public: + static std::shared_ptr CreateActor(SDL_FRect hitbox); + static std::shared_ptr CreateMovingActor(SDL_FRect hitbox); +}; + + + +#endif \ No newline at end of file diff --git a/src/Actors/MovingActor.cpp b/src/Actors/MovingActor.cpp new file mode 100644 index 0000000..3d47c88 --- /dev/null +++ b/src/Actors/MovingActor.cpp @@ -0,0 +1,26 @@ +#include "MovingActor.h" + +void MovingActor::Init() +{ + //m_MoveComponent = std::make_unique(MoveComponent(shared_from_this())); + m_MoveComponent = std::make_shared(MoveComponent(shared_from_this())); +} + +MovingActor::MovingActor(SDL_FRect hitbox) : Actor(hitbox) +{ + //m_MoveComponent = std::make_unique(MoveComponent(shared_from_this())); + //m_MoveComponent = std::make_shared(MoveComponent(shared_from_this())); +} + +/* +MovingActor::MovingActor(const MovingActor& other) : Actor(other.m_Hitbox) +{ + //m_MoveComponent = std::make_unique(MoveComponent(shared_from_this())); + m_MoveComponent = std::make_shared(MoveComponent(shared_from_this())); +} + +MovingActor& MovingActor::operator=(const MovingActor& other) +{ + //m_MoveComponent = std::make_unique(MoveComponent(shared_from_this())); + return *this; +}*/ \ No newline at end of file diff --git a/src/Actors/MovingActor.h b/src/Actors/MovingActor.h new file mode 100644 index 0000000..f2d5525 --- /dev/null +++ b/src/Actors/MovingActor.h @@ -0,0 +1,30 @@ +#ifndef MOVING_ACTOR_H +#define MOVING_ACTOR_H + +#include "Actor.h" +#include +#include +#include "../Components/MoveComponent.h" +#include "../DataStructures/NormalizedDirection.h" + +class MovingActor : public Actor +{ +public: + ~MovingActor() = default; + //void Update(float deltaTime) override {}; + void Update(float deltaTime) {m_MoveComponent->Update(deltaTime);}; + void SetDirection(NormalizedDirection direction) {m_MoveComponent->SetDirection(direction);} + + friend class ActorFactory; +protected: + MovingActor(SDL_FRect hitbox); + //MovingActor(const MovingActor&) = default; + //MovingActor& operator=(const MovingActor&) = default; + void Init() override; + + //std::unique_ptr m_MoveComponent; + std::shared_ptr m_MoveComponent; + +}; + +#endif \ No newline at end of file diff --git a/src/ByteCode/Skills/Boon.h b/src/ByteCode/Skills/Boon.h new file mode 100644 index 0000000..ae9b473 --- /dev/null +++ b/src/ByteCode/Skills/Boon.h @@ -0,0 +1,31 @@ +#ifndef BOON_H +#define BOON_H + +enum BoonType +{ + damage = 0, + defense = 1, + movement = 2, + tick = 3, + cooldown = 4 +}; + +/* +defense: gets checked when attacked, can reduce damage or cause effects on target +boons can remove themself (aegis) +damage: gets checked when calculating damage (fury, might) +tick: gets checked every second (hot, dot) +movement: gets checked when moving (swiftness, cripple) +cooldown: quickness, alactiry, slow +poison = defense? + +combo: chill = movement + cooldown +*/ + +class Boon +{ + +}; + + +#endif \ No newline at end of file diff --git a/src/ByteCode/Skills/Skill.h b/src/ByteCode/Skills/Skill.h new file mode 100644 index 0000000..2dd65e7 --- /dev/null +++ b/src/ByteCode/Skills/Skill.h @@ -0,0 +1,14 @@ +#ifndef SKILL_H +#define SKILL_H + +#include "SkillEffects.h" + +class Skill +{ +public: + +private: + +}; + +#endif \ No newline at end of file diff --git a/src/ByteCode/Skills/SkillEffects.h b/src/ByteCode/Skills/SkillEffects.h new file mode 100644 index 0000000..9a390d7 --- /dev/null +++ b/src/ByteCode/Skills/SkillEffects.h @@ -0,0 +1,147 @@ +#ifndef SKILL_EFFECTS_H +#define SKILL_EFFECTS_H + +#include + +enum Target +{ + self = 0, + ally = 1, + enemy = 2, + all = 3 +}; + +enum Effect +{ + damage = 0, + heal = 1, + buff = 2, + reflect = 3, + destroyProjectiles = 4, + dodge = 5, + //field = 6 doesn't make sense, fiels needs effects +}; + +enum ComboField +{ + +}; + +enum ComboFinisher +{ + +}; + +enum SkillType +{ + hit = 0, + dash = 1, + projectile = 2, + targeted = 3, //teleports, aoes like lava font + shroud = 4, + summoning = 5 +}; + +enum Area +{ + rectangle = 0, + circle = 1, + semi_circle = 2 //sword hit in front of character +}; + +enum SkillType +{ + //hit = 0, + dash = 1, + //jump = 2, + teleport = 3, + projectile = 4, + roundAoE = 5, + rectangleAoE = 6, + hitAoE = 7, + shroud = 8, + summoning = 9 +}; + +enum Aim +{ + self = 0, + mouse = 1, //targeted enemy if one is targeted + backwards = 2, //away from targeted enemy + moveDirection = 3, + closestTarget = 4 +}; + +enum Buffs +{ + //raise / lower + //percentage / value +}; + +enum Attributes +{ + piercing, + rooting +}; + +//dazed, stunned,... ??? + +struct HitAoE //hit an enemy with a sword in front of you +{ + int radius; + int angle; +}; + +struct RoundAoE +{ + int radius; +}; + +struct RectangleAoE +{ + int width; + int lengt; +}; + +/* +TODO: 3 projectiles (fire 1), 2 hits (air 1) +*/ + +struct SkillEffect +{ + Target target; + Effect effect; + Area area; //maybe better in Dash? + int effect_value; //damage or heal modifier, buff id, + int duration; + //damage? + //buff? how? -> buffId? + //aoe? +}; + +struct Dash +{ + int speed; + int distance; + std::vector dash_effects; + std::vector end_effects; + //an effect can be a skill? like shoot projectile at the end of a dash +}; + +struct Teleport +{ + +}; + +struct Projectile +{ + int speed; + int distance; +}; + +struct Shroud +{ + +}; + +#endif \ No newline at end of file diff --git a/src/Components/AIComponent.h b/src/Components/AIComponent.h new file mode 100644 index 0000000..e69de29 diff --git a/src/Components/AnimSpriteComponent.h b/src/Components/AnimSpriteComponent.h new file mode 100644 index 0000000..e69de29 diff --git a/src/Components/MoveComponent.cpp b/src/Components/MoveComponent.cpp new file mode 100644 index 0000000..1f35380 --- /dev/null +++ b/src/Components/MoveComponent.cpp @@ -0,0 +1,64 @@ +#include "MoveComponent.h" +#include + + +void MoveComponent::Update(float deltaTime) +{ + float distance = m_Speed * deltaTime; + if(m_ForcedDistance > 0) + { + if(m_ForcedDistance < distance) + { + distance = m_ForcedDistance; + m_ForcedDistance = 0; + //TODO: get normal speed value + } + else + { + m_ForcedDistance -= distance; + } + } + float xDistance = m_Direction.GetX() * distance; + float yDistance = m_Direction.GetY() * distance; + + //TODO: collision + bool collision = false; + if(collision) + { + m_ForcedDistance = 0; + } + else + { + m_Actor.lock()->MoveBy(xDistance, yDistance); + } + if(m_ForcedDistance == 0) + { + //delete direction + m_Direction = NormalizedDirection(); + } +} + +void MoveComponent::SetForced(const NormalizedDirection & direction, float speed, float distance) +{ + m_Direction = direction; + m_Speed = speed; + m_ForcedDistance = distance; +} + +void MoveComponent::SetDirection(const NormalizedDirection & direction) +{ + //only allow setting a direction if not being kicked + if(m_ForcedDistance == 0) + { + m_Direction = direction; + } +} + +void MoveComponent::SetSpeed(float speed) +{ + //only allow setting a speed if not being kicked + if(m_ForcedDistance == 0) + { + m_Speed = speed; + } +} diff --git a/src/Components/MoveComponent.h b/src/Components/MoveComponent.h new file mode 100644 index 0000000..844eb90 --- /dev/null +++ b/src/Components/MoveComponent.h @@ -0,0 +1,26 @@ +#ifndef MOVE_COMPONENT_H +#define MOVE_COMPONENT_H + +#include +#include +#include +#include "../Actors/Actor.h" +#include "../DataStructures/NormalizedDirection.h" + +class MoveComponent +{ +public: + MoveComponent(std::weak_ptr actor): m_Actor(actor), m_Speed(10) {}; //TODO: speed + void Update(float deltaTime); + void SetDirection(const NormalizedDirection & direction); + void SetSpeed(float speed); + void SetForced(const NormalizedDirection & direction, float speed, float distance); + +private: + std::weak_ptr m_Actor; + NormalizedDirection m_Direction; + float m_Speed; + float m_ForcedDistance; +}; + +#endif \ No newline at end of file diff --git a/src/Components/SpriteComponent.h b/src/Components/SpriteComponent.h new file mode 100644 index 0000000..8de5521 --- /dev/null +++ b/src/Components/SpriteComponent.h @@ -0,0 +1,17 @@ +#ifndef SPRITE_COMPONENT_H +#define SPRITE_COMPONENT_H + +#include +#include + +class SpriteComponent +{ +public: + SpriteComponent(/* args */); + ~SpriteComponent(); + +private: + std::unique_ptr Texture; +}; + +#endif \ No newline at end of file diff --git a/src/DataStructures/CharacterStats.h b/src/DataStructures/CharacterStats.h new file mode 100644 index 0000000..a89b473 --- /dev/null +++ b/src/DataStructures/CharacterStats.h @@ -0,0 +1,22 @@ +#ifndef CHARACTER_STATS_H +#define CHARACTER_STATS_H + + +struct CharacterStats +{ + int BaseHealth; + int Vitality; + int Toughness; + int Power; + int Precission; + int Ferocity; + int HealingPower; + int Concentration; + int ConditionDamage; + int Expertise; + int MovementSpeed; +}; + + + +#endif \ No newline at end of file diff --git a/src/DataStructures/NormalizedDirection.cpp b/src/DataStructures/NormalizedDirection.cpp new file mode 100644 index 0000000..9320480 --- /dev/null +++ b/src/DataStructures/NormalizedDirection.cpp @@ -0,0 +1,17 @@ +#include "NormalizedDirection.h" +#include + +NormalizedDirection::NormalizedDirection(float x, float y, bool isNormalized) +{ + if(isNormalized || (x == 0 && y == 0)) + { + m_x = x; + m_y = y; + } + else + { + float normalizeValue = 1 / sqrt(pow(x,2) + pow(y,2)); + m_x = x * normalizeValue; + m_y = y * normalizeValue; + } +} diff --git a/src/DataStructures/NormalizedDirection.h b/src/DataStructures/NormalizedDirection.h new file mode 100644 index 0000000..aacd496 --- /dev/null +++ b/src/DataStructures/NormalizedDirection.h @@ -0,0 +1,17 @@ +#ifndef NORMALIZED_DIRECTION_H +#define NORMALIZED_DIRECTION_H + +class NormalizedDirection +{ +public: + NormalizedDirection() : m_x(0), m_y(0) {}; + NormalizedDirection(float x, float y, bool isNormalized); + + float GetX() {return m_x;} + float GetY() {return m_y;} +private: + float m_x; + float m_y; +}; + +#endif \ No newline at end of file diff --git a/src/EditorCommands/EditorCommand.h b/src/EditorCommands/EditorCommand.h new file mode 100644 index 0000000..06f2a2c --- /dev/null +++ b/src/EditorCommands/EditorCommand.h @@ -0,0 +1,13 @@ +#ifndef EDITOR_COMMAND_H +#define EDITOR_COMMAND_H + +class EditorCommand +{ +public: + EditorCommand(); + virtual ~EditorCommand(); + virtual void Execute() = 0; + virtual void Undo() = 0; +}; + +#endif \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 4b91356..55a61a5 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,22 +1,153 @@ -// SPDX-FileCopyrightText: 2025 -// SPDX-License-Identifier: MIT - #include "Game.h" -#include -#include -#include +#include +#include +#include +#include "Actors/ActorFactory.h" +#include "GameCommands/EndGameCommand.h" +#include "GameCommands/MoveUpCommand.h" +#include "GameCommands/MoveLeftCommand.h" +#include "GameCommands/MoveDownCommand.h" +#include "GameCommands/MoveRightCommand.h" +#include "GameCommands/MoveXCommand.h" +#include "GameCommands/MoveYCommand.h" -Game::Game() +Game::Game(SDL_Window *window, SDL_Renderer *renderer) + : m_Window(window, SDL_DestroyWindow), + m_Renderer(renderer, SDL_DestroyRenderer), + m_Gamepad1(nullptr, SDL_CloseGamepad) { - SDL_assert(1< 0); + SDL_FRect player1Hitbox; + player1Hitbox.x = 0; + player1Hitbox.y = 0; + player1Hitbox.w = 20; + player1Hitbox.h = 20; + m_Player1 = ActorFactory::CreateMovingActor(player1Hitbox); - int i = 7; - i++; - //std::cout << i << std::endl; -} - -Game::~Game() -{ + //TODO: read keybinds from config if it exists. otherwise initialze standard and write file + m_KeyboardCommandMap.emplace(SDL_SCANCODE_ESCAPE, new EndGameCommand(this)); + m_KeyboardCommandMap.emplace(SDL_SCANCODE_W, new MoveUpCommand(m_Player1DirectionInput, m_ButtonInput)); + m_KeyboardCommandMap.emplace(SDL_SCANCODE_A, new MoveLeftCommand(m_Player1DirectionInput, m_ButtonInput)); + m_KeyboardCommandMap.emplace(SDL_SCANCODE_S, new MoveDownCommand(m_Player1DirectionInput, m_ButtonInput)); + m_KeyboardCommandMap.emplace(SDL_SCANCODE_D, new MoveRightCommand(m_Player1DirectionInput, m_ButtonInput)); + m_Gamepad1ButtonCommandMap.emplace(SDL_GAMEPAD_BUTTON_DPAD_UP, new MoveUpCommand(m_Player1DirectionInput, m_ButtonInput)); + m_Gamepad1ButtonCommandMap.emplace(SDL_GAMEPAD_BUTTON_DPAD_LEFT, new MoveLeftCommand(m_Player1DirectionInput, m_ButtonInput)); + m_Gamepad1ButtonCommandMap.emplace(SDL_GAMEPAD_BUTTON_DPAD_DOWN, new MoveDownCommand(m_Player1DirectionInput, m_ButtonInput)); + m_Gamepad1ButtonCommandMap.emplace(SDL_GAMEPAD_BUTTON_DPAD_RIGHT, new MoveRightCommand(m_Player1DirectionInput, m_ButtonInput)); + m_Gamepad1AxisCommandMap.emplace(SDL_GAMEPAD_AXIS_LEFTX, new MoveXCommand(m_Player1DirectionInput)); + m_Gamepad1AxisCommandMap.emplace(SDL_GAMEPAD_AXIS_LEFTY, new MoveYCommand(m_Player1DirectionInput)); } +void Game::Run() +{ + float deltaTime = (SDL_GetTicks() - m_TickCount) / 1000.0f; + m_TickCount = SDL_GetTicks(); + HandleMovement(); + m_Player1->Update(deltaTime); + + SDL_SetRenderDrawColor(m_Renderer.get(), 0, 0, 0, 255); + SDL_RenderClear(m_Renderer.get()); + + SDL_SetRenderDrawColor(m_Renderer.get(), 90, 90, 90, 255); + SDL_FRect player = m_Player1->GetHitbox(); + SDL_RenderFillRect(m_Renderer.get(), &player); + SDL_RenderPresent(m_Renderer.get()); + +} + +void Game::HandleMovement() +{ + //if both direction buttons are pressed, normalize the direction vector + if((m_Player1DirectionInput.y == 1 || m_Player1DirectionInput.y == -1) + && (m_Player1DirectionInput.x == 1 || m_Player1DirectionInput.x == -1)) + { + float x = m_Player1DirectionInput.x* c_NormalizedDiagonal; + float y = m_Player1DirectionInput.y * c_NormalizedDiagonal; + NormalizedDirection player1InputDirection(x, y, true); + m_Player1->SetDirection(player1InputDirection); + } + else if(abs(m_Player1DirectionInput.x) < c_GamepadAxisDeadzone && abs(m_Player1DirectionInput.y) < c_GamepadAxisDeadzone) + { + m_Player1->SetDirection(NormalizedDirection()); + } + else + { + NormalizedDirection player1InputDirection(m_Player1DirectionInput.x, m_Player1DirectionInput.y, true); + m_Player1->SetDirection(player1InputDirection); + } +} + +void Game::HandleKeyboardInput(const SDL_Event &event) +{ + if(!event.key.repeat) + { + auto commandIterator = m_KeyboardCommandMap.find(event.key.scancode); + if(commandIterator != m_KeyboardCommandMap.end()) + { + if(event.key.down) + { + commandIterator->second->Down(); + } + else + { + commandIterator->second->Up(); + } + } + } +} + +void Game::HandleGamepadButton(const SDL_Event &event) +{ + auto commandIterator = m_Gamepad1ButtonCommandMap.find((SDL_GamepadButton)event.gbutton.button); + if(commandIterator != m_Gamepad1ButtonCommandMap.end()) + { + if(event.gbutton.down) + { + commandIterator->second->Down(); + } + else + { + commandIterator->second->Up(); + } + } +} + +void Game::HandleGamepadAxis(const SDL_Event &event) +{ + auto commandIterator = m_Gamepad1AxisCommandMap.find((SDL_GamepadAxis)event.gaxis.axis); + if(commandIterator != m_Gamepad1AxisCommandMap.end()) + { + float value = (float)event.gaxis.value / std::numeric_limits::max(); + if(!m_ButtonInput || abs(value) > c_GamepadAxisDeadzone) + { + commandIterator->second->Execute(value); + m_ButtonInput = false; + } + } +} + +void Game::AddGamepad(SDL_JoystickID id) +{ + if(!m_Gamepad1) + { + m_Gamepad1.reset(SDL_OpenGamepad(id)); + if (!m_Gamepad1) { + SDL_Log("Failed to open gamepad ID %u: %s", (unsigned int) id, SDL_GetError()); + } + } +} + +void Game::RemoveGamepad(SDL_JoystickID id) +{ + if (m_Gamepad1 && (SDL_GetGamepadID(m_Gamepad1.get()) == id)) { + SDL_CloseGamepad(m_Gamepad1.get()); /* our joystick was unplugged. */ + m_Gamepad1.release(); + } +} + +void Game::EndGame() +{ + SDL_Event * event = new SDL_Event(); + event->type = SDL_EVENT_QUIT; + SDL_PushEvent(event); +} \ No newline at end of file diff --git a/src/Game.h b/src/Game.h index 222f0e1..6cf7494 100644 --- a/src/Game.h +++ b/src/Game.h @@ -1,24 +1,48 @@ -// SPDX-FileCopyrightText: 2025 -// SPDX-License-Identifier: MIT - #ifndef GAME_H #define GAME_H -/** - * @todo write docs - */ +#include +#include +#include +#include "GameCommands/GameCommand.h" +#include "GameCommands/AxisCommand.h" +#include "Actors/MovingActor.h" + class Game { public: - /** - * Default constructor - */ - Game(); + Game(SDL_Window *window, SDL_Renderer *renderer); + ~Game() {}; + void Run(); + void HandleMovement(); + void HandleKeyboardInput(const SDL_Event &event); + void HandleGamepadButton(const SDL_Event &event); + void HandleGamepadAxis(const SDL_Event &event); + void AddGamepad(SDL_JoystickID id); + void RemoveGamepad(SDL_JoystickID id); + void EndGame(); +private: + const float c_GamepadAxisDeadzone = 0.2; //TODO: make this a setting? - /** - * Destructor - */ - ~Game(); + bool m_running = true; + bool m_ButtonInput = true; + uint64_t m_TickCount; + std::map m_KeyboardCommandMap; + std::map m_Gamepad1ButtonCommandMap; + std::map m_Gamepad2ButtonCommandMap; + std::map m_Gamepad1AxisCommandMap; + std::map m_Gamepad2AxisCommandMap; + + std::shared_ptr m_Player1; + std::shared_ptr m_Player2; + SDL_FPoint m_Player1DirectionInput; + SDL_FPoint m_Player2DirectionInput; + const float c_NormalizedDiagonal = 1 / sqrt(2); + + std::unique_ptr m_Window; + std::unique_ptr m_Renderer; + std::unique_ptr m_Gamepad1; + //std::unique_ptr m_Gamepad2; }; diff --git a/src/GameCommands/AxisCommand.h b/src/GameCommands/AxisCommand.h new file mode 100644 index 0000000..eaacbc3 --- /dev/null +++ b/src/GameCommands/AxisCommand.h @@ -0,0 +1,11 @@ +#ifndef AXIS_COMMAND_H +#define AXIS_COMMAND_H + +class AxisCommand +{ +public: + virtual void Execute(float value) = 0; +}; + + +#endif \ No newline at end of file diff --git a/src/GameCommands/DodgeCommand.h b/src/GameCommands/DodgeCommand.h new file mode 100644 index 0000000..e69de29 diff --git a/src/GameCommands/EndGameCommand.h b/src/GameCommands/EndGameCommand.h new file mode 100644 index 0000000..a341324 --- /dev/null +++ b/src/GameCommands/EndGameCommand.h @@ -0,0 +1,17 @@ +#ifndef END_GAME_COMMAND_H +#define END_GAME_COMMAND_H + +#include "GameCommand.h" +#include "../Game.h" + +class EndGameCommand : public GameCommand +{ +public: + EndGameCommand(Game* game) : m_game(game){} + void Down() override {m_game->EndGame();} + void Up() override {} +private: + Game* m_game; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/GameCommand.h b/src/GameCommands/GameCommand.h new file mode 100644 index 0000000..db94195 --- /dev/null +++ b/src/GameCommands/GameCommand.h @@ -0,0 +1,13 @@ +#ifndef GAME_COMMAND_H +#define GAME_COMMAND_H + +class GameCommand +{ +public: + GameCommand() {}; + virtual ~GameCommand() {}; + virtual void Down() = 0; + virtual void Up() = 0; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveDownCommand.h b/src/GameCommands/MoveDownCommand.h new file mode 100644 index 0000000..c8cd0fc --- /dev/null +++ b/src/GameCommands/MoveDownCommand.h @@ -0,0 +1,19 @@ +#ifndef MOVE_DOWN_COMMAND_H +#define MOVE_DOWN_COMMAND_H + +#include "GameCommand.h" +#include + +class MoveDownCommand : public GameCommand +{ +public: + MoveDownCommand(SDL_FPoint & directionInput, bool & buttonInput) : m_directionInput(&directionInput), m_buttonInput(&buttonInput) {}; + ~MoveDownCommand() {}; + void Down() override { m_directionInput->y += 1; *m_buttonInput = true;} + void Up() override { m_directionInput->y -= 1; *m_buttonInput = true;} +private: + SDL_FPoint * m_directionInput; + bool * m_buttonInput; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveLeftCommand.h b/src/GameCommands/MoveLeftCommand.h new file mode 100644 index 0000000..1f15253 --- /dev/null +++ b/src/GameCommands/MoveLeftCommand.h @@ -0,0 +1,22 @@ +#ifndef MOVE_LEFT_COMMAND_H +#define MOVE_LEFT_COMMAND_H + +#include "GameCommand.h" +#include +#include +#include +#include "../Components/MoveComponent.h" + +class MoveLeftCommand : public GameCommand +{ +public: + MoveLeftCommand(SDL_FPoint & directionInput, bool & buttonInput) : m_directionInput(&directionInput), m_buttonInput(&buttonInput) {}; + ~MoveLeftCommand() {}; + void Down() override { m_directionInput->x -= 1; *m_buttonInput = true;} + void Up() override { m_directionInput->x += 1; *m_buttonInput = true;} +private: + SDL_FPoint * m_directionInput; + bool * m_buttonInput; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveRightCommand.h b/src/GameCommands/MoveRightCommand.h new file mode 100644 index 0000000..55a7c07 --- /dev/null +++ b/src/GameCommands/MoveRightCommand.h @@ -0,0 +1,22 @@ +#ifndef MOVE_RIGHT_COMMAND_H +#define MOVE_RIGHT_COMMAND_H + +#include "GameCommand.h" +#include +#include +#include +#include "../Components/MoveComponent.h" + +class MoveRightCommand : public GameCommand +{ +public: + MoveRightCommand(SDL_FPoint & directionInput, bool & buttonInput) : m_directionInput(&directionInput), m_buttonInput(&buttonInput) {}; + ~MoveRightCommand() {}; + void Down() override { m_directionInput->x += 1; *m_buttonInput = true;} + void Up() override { m_directionInput->x -= 1; *m_buttonInput = true;} +private: + SDL_FPoint * m_directionInput; + bool * m_buttonInput; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveUpCommand.h b/src/GameCommands/MoveUpCommand.h new file mode 100644 index 0000000..0c5fdf7 --- /dev/null +++ b/src/GameCommands/MoveUpCommand.h @@ -0,0 +1,22 @@ +#ifndef MOVE_UP_COMMAND_H +#define MOVE_UP_COMMAND_H + +#include "GameCommand.h" +#include +#include +#include +#include "../Components/MoveComponent.h" + +class MoveUpCommand : public GameCommand +{ +public: + MoveUpCommand(SDL_FPoint & directionInput, bool & buttonInput) : m_directionInput(&directionInput), m_buttonInput(&buttonInput) {}; + ~MoveUpCommand() {}; + void Down() override { m_directionInput->y -= 1; *m_buttonInput = true;} + void Up() override { m_directionInput->y += 1; *m_buttonInput = true;} +private: + SDL_FPoint * m_directionInput; + bool * m_buttonInput; +}; + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveXCommand.h b/src/GameCommands/MoveXCommand.h new file mode 100644 index 0000000..1472e6f --- /dev/null +++ b/src/GameCommands/MoveXCommand.h @@ -0,0 +1,20 @@ +#ifndef MOVE_X_COMMAND_H +#define MOVE_X_COMMAND_H + + +#include "AxisCommand.h" +#include + +class MoveXCommand : public AxisCommand +{ +public: + MoveXCommand(SDL_FPoint & directionInput) : m_directionInput(&directionInput){}; + ~MoveXCommand() {}; + void Execute(float value) override { m_directionInput->x = value;} +private: + SDL_FPoint * m_directionInput; +}; + + + +#endif \ No newline at end of file diff --git a/src/GameCommands/MoveYCommand.h b/src/GameCommands/MoveYCommand.h new file mode 100644 index 0000000..4d3705e --- /dev/null +++ b/src/GameCommands/MoveYCommand.h @@ -0,0 +1,20 @@ +#ifndef MOVE_Y_COMMAND_H +#define MOVE_Y_COMMAND_H + + +#include "AxisCommand.h" +#include + +class MoveYCommand : public AxisCommand +{ +public: + MoveYCommand(SDL_FPoint & directionInput) : m_directionInput(&directionInput){}; + ~MoveYCommand() {}; + void Execute(float value) override { m_directionInput->y = value;} +private: + SDL_FPoint * m_directionInput; +}; + + + +#endif \ No newline at end of file diff --git a/src/GameCommands/Skill1Command.h b/src/GameCommands/Skill1Command.h new file mode 100644 index 0000000..a6c9517 --- /dev/null +++ b/src/GameCommands/Skill1Command.h @@ -0,0 +1,22 @@ +#ifndef SKILL_1_COMMAND_H +#define SKILL_1_COMMAND_H + +class Skill1Command +{ +private: + /* data */ +public: + Skill1Command(/* args */); + ~Skill1Command(); +}; + +Skill1Command::Skill1Command(/* args */) +{ +} + +Skill1Command::~Skill1Command() +{ +} + + +#endif \ No newline at end of file