diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ed07f3..88a83a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,4 +30,10 @@ target_link_libraries(${PROJECT_NAME} PRIVATE SDL2_mixer::SDL2_mixer-static ) +if(CMAKE_BUILD_TYPE MATCHES "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -fno-omit-frame-pointer") + target_link_libraries(${PROJECT_NAME} PRIVATE "-fsanitize=address") +endif() + + file(COPY ${PROJECT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BINARY_DIR}) \ No newline at end of file diff --git a/include/AssetManager.h b/include/AssetManager.h index 02761cc..d87dc63 100644 --- a/include/AssetManager.h +++ b/include/AssetManager.h @@ -1,3 +1,5 @@ +#include "Entity.h" + #include #include #include @@ -13,7 +15,7 @@ public: AssetManager(Manager* manager); ~AssetManager(); - void createProjectile(Vector2D pos, Vector2D velocity, bool source, int scale, int range, int speed, const char* texturePath); + void createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, TeamLabel teamLabel); //texture management void addTexture(std::string id, const char* path); diff --git a/include/CollisionHandler.h b/include/CollisionHandler.h new file mode 100644 index 0000000..8cdaad5 --- /dev/null +++ b/include/CollisionHandler.h @@ -0,0 +1,67 @@ +#pragma once + +#include "ColliderComponent.h" +#include "Constants.h" +#include "Entity.h" +#include "SDL_rect.h" +#include "SpriteComponent.h" +#include "Vector2D.h" +#include "Manager.h" + +#include +#include +#include +#include +#include + +class ColliderComponent; +class Entity; + +constexpr uint8_t DIRECTION_C = 4; + +enum class direction +{ + LEFT = 0, + RIGHT, + UP, + DOWN +}; + +using IntersectionBitSet = std::bitset; + +class CollisionHandler +{ +private: + Manager& manager; + +public: + + CollisionHandler(Manager& mManager) : + manager(mManager) { }; + ~CollisionHandler(); + + static IntersectionBitSet getIntersection( // intersections relative to entityA + Entity* entityA, + Entity* entityB, + Vector2D posModA = Vector2D(0,0), + Vector2D posModB = Vector2D(0,0)); + static IntersectionBitSet getIntersectionWithBounds( // will fail to determine direction if speed high enough to switch from no collision to full overlap in one tick + Entity* entity, + Vector2D posMod = Vector2D(0,0)); + + // temporary function, remove once game.cpp cleaned up + std::vector getColliders( + std::initializer_list const& groupLabels, + std::initializer_list const& teamLabels = {}, + bool negateTeam = false); + + template + T getAnyIntersection( + Entity* entity, + Vector2D posMod = {}, + std::initializer_list const& groupLabels = {}, + std::initializer_list const& teamLabels = {}, + bool negateTeam = false); + + void update(); +}; \ No newline at end of file diff --git a/include/Component.h b/include/Component.h index bc788ac..095b370 100644 --- a/include/Component.h +++ b/include/Component.h @@ -2,16 +2,6 @@ class Entity; -enum class GroupLabel -{ - MAP, - PLAYERS, - ENEMIES, - COLLIDERS, - PROJECTILE, - HEARTS -}; - class Component { public: diff --git a/include/Constants.h b/include/Constants.h index f5e691c..6490de9 100644 --- a/include/Constants.h +++ b/include/Constants.h @@ -3,11 +3,13 @@ #include using Group = std::size_t; +using Team = std::size_t; constexpr int CHARACTER_COUNT = 4; constexpr std::size_t MAX_COMPONENTS = 32; constexpr std::size_t MAX_GROUPS = 32; +constexpr std::size_t MAX_TEAMS = 8; // constexpr int SCREEN_SIZE_HEIGHT = 640; constexpr int SCREEN_SIZE_WIDTH = 800; diff --git a/include/Defines.h b/include/Defines.h index 3f59c93..7b9637e 100644 --- a/include/Defines.h +++ b/include/Defines.h @@ -1,2 +1 @@ -#pragma once - +#pragma once \ No newline at end of file diff --git a/include/Direction.h b/include/Direction.h new file mode 100644 index 0000000..a971704 --- /dev/null +++ b/include/Direction.h @@ -0,0 +1,7 @@ +#pragma once + +enum class Direction +{ + LEFT, + RIGHT +}; \ No newline at end of file diff --git a/include/Entity.h b/include/Entity.h index 326ff7c..9d768b5 100644 --- a/include/Entity.h +++ b/include/Entity.h @@ -5,6 +5,7 @@ #include #include +#include "ColliderComponent.h" #include "ECS.h" #include "Constants.h" @@ -15,20 +16,49 @@ using ComponentBitSet = std::bitset; using GroupBitSet = std::bitset; using ComponentArray = std::array; +enum class GroupLabel +{ + MAPTILES, + PLAYERS, + ENEMIES, + COLLIDERS, + PROJECTILE, + HEARTS +}; + +enum class TeamLabel +{ + NONE, + BLUE, + RED +}; + class Entity { public: - explicit Entity(Manager& mManager) : manager(mManager) { } + explicit Entity(Manager& mManager) : + manager(mManager) { }; void update() const; void draw() const; bool isActive() const { return this->active; } - void destroy() { this->active = false; } + void destroy() { + this->active = false; + if (this->hasComponent()) { + this->getComponent().removeCollision(); + } + } bool hasGroup(Group mGroup); void addGroup(Group mGroup); void delGroup(Group mGroup); + std::bitset getGroupBitSet(); + + void setTeam(TeamLabel teamLabel); + TeamLabel getTeam(); + + Manager& getManager() { return manager; }; template bool hasComponent() const { @@ -63,4 +93,5 @@ private: ComponentArray componentArray = {}; ComponentBitSet componentBitSet; GroupBitSet groupBitSet; + TeamLabel teamLabel; }; \ No newline at end of file diff --git a/include/Game.h b/include/Game.h index 9ba7ac6..9a23997 100644 --- a/include/Game.h +++ b/include/Game.h @@ -8,7 +8,8 @@ #include "Vector2D.h" class AssetManager; -class ColliderComponent; +class CollisionHandler; +enum class TeamLabel; class Game { @@ -25,19 +26,18 @@ public: void clean(); bool running() const; - static void addTile(int id, int x, int y); + static void addTile(unsigned long id, int x, int y); static SDL_Renderer* renderer; static SDL_Event event; - static std::vector colliders; + static CollisionHandler* collisionHandler; static AssetManager* assets; - - bool getWinner(); private: + void setWinner(TeamLabel winningTeam); + TeamLabel getWinner(); + int counter = 0; bool isRunning = false; SDL_Window* window; - - //true for player1 win / false for player2 win; - bool winner; + TeamLabel winner; }; diff --git a/include/HealthComponent.h b/include/HealthComponent.h index 7cfd2e7..78b9451 100644 --- a/include/HealthComponent.h +++ b/include/HealthComponent.h @@ -1,5 +1,6 @@ #pragma once +#include "Direction.h" #include "Component.h" class Manager; @@ -8,22 +9,19 @@ class HealthComponent : public Component { public: - HealthComponent(int health, Manager* manager, bool player) : health(health), manager(manager), player(player) {} + HealthComponent(int health, Direction side) : health(health), side(side) {} ~HealthComponent() {} - void getDamage() { this->health--; } + void modifyHealth(int health = -1); int getHealth() { return this->health; } void init() override; - void createAllHearts(); + void refreshHearts(); void createHeartComponents(int x); - private: int health; - Manager* manager; - bool player; //true if player1 / false if player2 - + Direction side; }; \ No newline at end of file diff --git a/include/Manager.h b/include/Manager.h index f85f3b8..61a1c45 100644 --- a/include/Manager.h +++ b/include/Manager.h @@ -5,8 +5,7 @@ #include #include "Constants.h" - -class Entity; +#include "Entity.h" class Manager { @@ -18,9 +17,15 @@ public: void addToGroup(Entity* mEntity, Group mGroup); std::vector& getGroup(Group mGroup); + void addToTeam(Entity* mEntity, Team mTeam); + std::vector& getTeam(Team mTeam); + + std::vector getAll(); + Entity& addEntity(); private: std::vector> entities; - std::array, MAX_GROUPS> groupedEntities; + std::array, MAX_GROUPS> entitiesByGroup; + std::array, MAX_TEAMS> entitiesByTeam; }; \ No newline at end of file diff --git a/include/PlayerComponent.h b/include/PlayerComponent.h new file mode 100644 index 0000000..922fd04 --- /dev/null +++ b/include/PlayerComponent.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Component.h" + +class PlayerComponent : public Component +{ +public: +private: +}; \ No newline at end of file diff --git a/include/ProjectileComponent.h b/include/ProjectileComponent.h index 73088c1..76a5e7b 100644 --- a/include/ProjectileComponent.h +++ b/include/ProjectileComponent.h @@ -10,14 +10,12 @@ class ProjectileComponent : public Component //can maybe be split in separate .cpp file public: - ProjectileComponent(int range, int speed, Vector2D velocity, bool source) : range(range), speed(speed), velocity(velocity), source(source) {} + ProjectileComponent(int range, int speed, Vector2D direction) : range(range), speed(speed), direction(direction) {} ~ProjectileComponent() {} void init() override; void update() override; - bool getSource() { return this->source; } - private: TransformComponent* transformComponent; @@ -25,7 +23,5 @@ private: int speed = 0; int distance = 0; - const bool source; //true if from player1 / false if from player2 - - Vector2D velocity; + Vector2D direction; }; \ No newline at end of file diff --git a/include/SpriteComponent.h b/include/SpriteComponent.h index 109d824..a11865f 100644 --- a/include/SpriteComponent.h +++ b/include/SpriteComponent.h @@ -5,15 +5,10 @@ #include "AnimationHandler.h" #include "Component.h" +#include "Direction.h" class TransformComponent; -enum SpriteDirection -{ - LEFT, - RIGHT -}; - class SpriteComponent : public Component { public: @@ -43,5 +38,5 @@ public: void update() override; void draw() override; void playAnimation(AnimationType type); - void setDirection(SpriteDirection direction); + void setDirection(Direction direction); }; diff --git a/include/TransformComponent.h b/include/TransformComponent.h index 04b193f..deed7f9 100644 --- a/include/TransformComponent.h +++ b/include/TransformComponent.h @@ -7,7 +7,7 @@ class TransformComponent : public Component { public: Vector2D position; - Vector2D velocity; + Vector2D direction; int height = 32; int width = 32; diff --git a/include/Vector2D.h b/include/Vector2D.h index 96380f8..bc2d652 100644 --- a/include/Vector2D.h +++ b/include/Vector2D.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + class Vector2D { public: @@ -13,7 +16,10 @@ public: friend Vector2D& operator-(Vector2D& vector1, const Vector2D& vector2); friend Vector2D& operator*(Vector2D& vector1, const Vector2D& vector2); friend Vector2D& operator/(Vector2D& vector1, const Vector2D& vector2); + friend Vector2D& operator+=(Vector2D& vector1, const Vector2D& vector2); Vector2D& operator*(const int& i); Vector2D& zero(); -}; \ No newline at end of file +}; + +SDL_Rect operator+(const SDL_Rect& rect, const Vector2D& vector2D); \ No newline at end of file diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index ebe5bcf..b6988dd 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -1,5 +1,6 @@ #include "AssetManager.h" +#include "Entity.h" #include "TextureManager.h" #include "SoundManager.h" #include "Components.h" @@ -25,12 +26,13 @@ Mix_Chunk* AssetManager::getSound(std::string id) { return soundEffects.at(id); } -void AssetManager::createProjectile(Vector2D pos, Vector2D velocity, bool source, int scale, int range, int speed, const char* texturePath) { +void AssetManager::createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, TeamLabel teamLabel) { auto& projectile(man->addEntity()); projectile.addComponent(pos.x, pos.y, 32, 32, scale); //32x32 is standard size for objects projectile.addComponent(texturePath); - projectile.addComponent(range, speed, velocity, source); + projectile.addComponent(range, speed, velocity); projectile.addComponent("projectile", 0.6f); projectile.addGroup((size_t)GroupLabel::PROJECTILE); + projectile.setTeam(teamLabel); } \ No newline at end of file diff --git a/src/ColliderComponent.cpp b/src/ColliderComponent.cpp index 5ba9ad5..08475d0 100644 --- a/src/ColliderComponent.cpp +++ b/src/ColliderComponent.cpp @@ -1,5 +1,6 @@ #include "ColliderComponent.h" +#include "CollisionHandler.h" #include "Entity.h" #include "Game.h" #include "TransformComponent.h" @@ -30,13 +31,14 @@ void ColliderComponent::init() } transform = &entity->getComponent(); - Game::colliders.push_back(this); + //Game::collisionHandler->add(this); + this->update(); } void ColliderComponent::update() { - collider.x = transform->position.x; - collider.y = transform->position.y; + collider.x = transform->position.x - (transform->width - transform->width * transform->scale * this->hitboxScale) / 2; + collider.y = transform->position.y - (transform->width - transform->width * transform->scale * this->hitboxScale) / 2; collider.w = (transform->width * transform->scale) * this->hitboxScale; diff --git a/src/CollisionHandler.cpp b/src/CollisionHandler.cpp new file mode 100644 index 0000000..fc9ac42 --- /dev/null +++ b/src/CollisionHandler.cpp @@ -0,0 +1,148 @@ +#include "CollisionHandler.h" + +#include "ColliderComponent.h" +#include "Constants.h" +#include "Entity.h" +#include "Manager.h" +#include "Vector2D.h" + +#include +#include +#include +#include + +IntersectionBitSet CollisionHandler::getIntersection(Entity* entityA, Entity* entityB, Vector2D posModA, Vector2D posModB) +{ + if (!entityA->hasComponent() || + !entityB->hasComponent()) + return std::bitset(); + + SDL_Rect colliderA = entityA->getComponent().collider; + SDL_Rect colliderB = entityB->getComponent().collider; + + colliderA.x += posModA.x; + colliderA.y += posModA.y; + + colliderB.x += posModB.x; + colliderB.y += posModB.y; + + if (!SDL_HasIntersection( + &colliderA, + &colliderB)) + return std::bitset(); + + std::bitset intersections; + + // checks all 4 directions to allow checking full overlap + if (colliderA.x < colliderB.x + colliderB.w && + colliderA.x > colliderB.x) { + intersections.set((size_t) direction::LEFT); + } + + if (colliderA.x + colliderA.w < colliderB.x + colliderB.w && + colliderA.x + colliderA.w > colliderB.x) { + intersections.set((size_t) direction::RIGHT); + } + + if (colliderA.y < colliderB.y + colliderB.h && + colliderA.y > colliderB.y) + intersections.set((size_t) direction::UP); + + if (colliderA.y + colliderA.h < colliderB.y + colliderB.h && + colliderA.y + colliderA.h > colliderB.y) + intersections.set((size_t) direction::DOWN); + + return intersections; +} + +IntersectionBitSet CollisionHandler::getIntersectionWithBounds(Entity* entity, Vector2D posMod) +{ + if (!entity->hasComponent()) + return std::bitset(); + + SDL_Rect* collider = &entity->getComponent().collider; + + std::bitset intersections; + + // all 4 directions and both sides to allow checking for fully out of bounds + if (collider->x + posMod.x < 0 || + collider->x + posMod.x > SCREEN_SIZE_WIDTH) { + intersections.set((size_t) direction::LEFT); + } + + if (collider->x + collider->w + posMod.x < 0 || + collider->x + collider->w + posMod.x > SCREEN_SIZE_WIDTH) + intersections.set((size_t) direction::RIGHT); + + if (collider->y + posMod.y < 0 || + collider->y + posMod.y > SCREEN_SIZE_HEIGHT) + intersections.set((size_t) direction::UP); + + if (collider->y + collider->h + posMod.y < 0 || + collider->y + collider->h + posMod.y > SCREEN_SIZE_HEIGHT) + intersections.set((size_t) direction::DOWN); + + return intersections; +} + +std::vector CollisionHandler::getColliders( + std::initializer_list const& groupLabels, + std::initializer_list const& teamLabels, + bool negateTeam) +{ + std::vector colliders; + + std::bitset groupBitSet; + for (auto& groupLabel : groupLabels) { + groupBitSet.set((size_t) groupLabel); + } + + std::bitset teamBitSet; + for (auto& teamLabel : teamLabels) { + teamBitSet.set((size_t) teamLabel); + } + + for (auto& entity : manager.getAll()) { + if ((groupBitSet & entity->getGroupBitSet()).none()) + continue; + if (teamBitSet.any() && negateTeam != (teamBitSet.test((size_t) entity->getTeam()))) + continue; + if (!entity->hasComponent()) + continue; + colliders.emplace_back(&entity->getComponent()); + } + + return colliders; +} + +template<> +IntersectionBitSet CollisionHandler::getAnyIntersection( + Entity* entity, + Vector2D posMod, + std::initializer_list const& groupLabels, + std::initializer_list const& teamLabels, + bool negateTeam) +{ + IntersectionBitSet intersections; + for (auto& collider : getColliders(groupLabels, teamLabels)) { + intersections |= getIntersection(entity, collider->entity, posMod); + } + return intersections; +}; + +template<> +Entity* CollisionHandler::getAnyIntersection( + Entity* entity, + Vector2D posMod, + std::initializer_list const& groupLabels, + std::initializer_list const& teamLabels, + bool negateTeam) +{ + for (auto& collider : getColliders(groupLabels, teamLabels)) { + SDL_Rect rect = entity->getComponent().collider + posMod; + if (SDL_HasIntersection(&rect, &collider->collider)) { + return collider->entity; + } + } + return nullptr; +}; \ No newline at end of file diff --git a/src/Entity.cpp b/src/Entity.cpp index 8ec725d..0f523ea 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -2,6 +2,7 @@ #include "Manager.h" #include "Component.h" +#include void Entity::update() const { @@ -28,3 +29,19 @@ void Entity::delGroup(Group mGroup) { groupBitSet[mGroup] = false; } + +std::bitset Entity::getGroupBitSet() +{ + return groupBitSet; +} + +void Entity::setTeam(TeamLabel teamLabel) +{ + this->teamLabel = teamLabel; + manager.addToTeam(this, (size_t) teamLabel); +} + +TeamLabel Entity::getTeam() +{ + return teamLabel; +} diff --git a/src/Game.cpp b/src/Game.cpp index 88d4008..f7d34c6 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -2,8 +2,12 @@ #include +#include "CollisionHandler.h" #include "Components.h" #include "AssetManager.h" +#include "Direction.h" +#include "Entity.h" +#include "HealthComponent.h" #include "Map.h" #include "TextureManager.h" #include "Constants.h" @@ -13,14 +17,15 @@ Manager manager; AssetManager* Game::assets = new AssetManager(&manager); +CollisionHandler* Game::collisionHandler = new CollisionHandler(manager); + SDL_Renderer* Game::renderer = nullptr; SDL_Event Game::event; -std::vector Game::colliders; +auto& player1(manager.addEntity()); +auto& player2(manager.addEntity()); -auto& player(manager.addEntity()); -auto& enemy(manager.addEntity()); auto& wall(manager.addEntity()); //auto& projectile (manager.addEntity()); @@ -134,19 +139,23 @@ void Game::init(const char* title, int xpos, int ypos, int width, int height, bo //ecs implementation - player.addComponent(80,80,2); //posx, posy, scale - player.addComponent(playerSprite, true); //adds sprite (32x32px), path needed - player.addComponent(SDL_SCANCODE_W, SDL_SCANCODE_S, SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_E, Vector2D(1, 0));//custom keycontrols can be added - player.addComponent("player", 0.8f); //adds tag (for further use, reference tag) - player.addComponent(5, &manager, true); - player.addGroup((size_t)GroupLabel::PLAYERS); //tell programm what group it belongs to for rendering order - enemy.addComponent(600, 500, 2); - enemy.addComponent(enemySprite, true); - enemy.addComponent(SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_RCTRL, Vector2D(-1, 0)); - enemy.addComponent("enemy", 0.8f); - enemy.addComponent(5, &manager, false); - enemy.addGroup((size_t)GroupLabel::ENEMIES); + player1.setTeam(TeamLabel::BLUE); + player1.addComponent(80,80,2); //posx, posy, scale + player1.addComponent("assets/chicken_knight_spritesheet.png", true); //adds sprite (32x32px), path needed + player1.addComponent(SDL_SCANCODE_W, SDL_SCANCODE_S, SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_E, Vector2D(1, 0));//custom keycontrols can be added + player1.addComponent("player", 0.8f); //adds tag (for further use, reference tag) + player1.addComponent(5, Direction::LEFT); + player1.addGroup((size_t) GroupLabel::PLAYERS); //tell programm what group it belongs to for rendering order + + + player2.setTeam(TeamLabel::RED); + player2.addComponent(600, 500, 2); + player2.addComponent("assets/chicken_spritesheet.png", true); + player2.addComponent(SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_RCTRL, Vector2D(-1, 0)); + player2.addComponent("enemy", 0.8f); + player2.addComponent(5, Direction::RIGHT); + player2.addGroup((size_t) GroupLabel::PLAYERS); } @@ -246,9 +255,8 @@ void Game::selectCharacters(const char* &playerSprite, const char* &enemySprite) this->isRunning = true; } -auto& tiles(manager.getGroup((size_t)GroupLabel::MAP)); +auto& tiles(manager.getGroup((size_t)GroupLabel::MAPTILES)); auto& players(manager.getGroup((size_t)GroupLabel::PLAYERS)); -auto& enemies(manager.getGroup((size_t)GroupLabel::ENEMIES)); auto& projectiles(manager.getGroup((size_t)GroupLabel::PROJECTILE)); auto& hearts(manager.getGroup((size_t)GroupLabel::HEARTS)); @@ -268,82 +276,16 @@ void Game::handleEvents() void Game::update() { - Vector2D playerPos = player.getComponent().position; - Vector2D enemyPos = enemy.getComponent().position; + Vector2D playerPos = player1.getComponent().position; + Vector2D enemyPos = player2.getComponent().position; manager.refresh(); manager.update(); - for (auto cc : colliders) - { - if (SDL_HasIntersection(&player.getComponent().collider, &cc->collider) && strcmp(cc->tag, "player") && cc->hasCollision) - { - if (!cc->isProjectile) - { - player.getComponent().handleCollision(player.getComponent().position, player.getComponent().collider, cc->collider); - } - else - { - player.getComponent().position = playerPos; - } - } - if (SDL_HasIntersection(&enemy.getComponent().collider, &cc->collider) && strcmp(cc->tag, "enemy") && cc->hasCollision) - { - if (!cc->isProjectile) - { - enemy.getComponent().handleCollision(enemy.getComponent().position, enemy.getComponent().collider, cc->collider); - } - else - { - enemy.getComponent().position = enemyPos; - } - } - } - - //checking if projectiles hit player1 or player2 - for (auto& p : projectiles) { - if(SDL_HasIntersection(&enemy.getComponent().collider, &p->getComponent().collider) - && (p->getComponent().hasCollision) && !p->getComponent().getSource()) { - //std::cout << "Enemy hit!"; - p->getComponent().removeCollision(); - p->destroy(); - - enemy.getComponent().getDamage(); - - //display updated health | pretty scuffed but works ig - for(auto h : hearts) - h->destroy(); - - player.getComponent().createAllHearts(); - enemy.getComponent().createAllHearts(); - - if(enemy.getComponent().getHealth() < 1) { - std::cout << "Player1 wins!" << std::endl; - winner = true; - isRunning = false; - } - } - - if(SDL_HasIntersection(&player.getComponent().collider, &p->getComponent().collider) - && (p->getComponent().hasCollision) && p->getComponent().getSource()) { - //std::cout << "Player hit!"; - p->getComponent().removeCollision(); - p->destroy(); - - player.getComponent().getDamage(); - - //display updated health - for(auto h : hearts) - h->destroy(); - - player.getComponent().createAllHearts(); - enemy.getComponent().createAllHearts(); - - if(player.getComponent().getHealth() < 1) { - std::cout << "Player2 wins!" << std::endl; - winner = false; - isRunning = false; - } + // needs to be in game.cpp to have access to internal functions + for (auto& player : manager.getGroup((size_t) GroupLabel::PLAYERS)) { + if (player->getComponent().getHealth() <= 0) { + this->setWinner(player->getTeam()); } } } @@ -359,10 +301,6 @@ void Game::render() { p->draw(); } - for (auto& e : enemies) - { - e->draw(); - } for (auto& p : projectiles) p->draw(); @@ -380,12 +318,12 @@ void Game::clean() std::cout << "Game Cleaned!" << std::endl; } -void Game::addTile(int id, int x, int y) +void Game::addTile(unsigned long id, int x, int y) { auto& tile(manager.addEntity()); tile.addComponent(x, y, TILE_SIZE, TILE_SIZE, id); if (id == 1) tile.addComponent("water"); - tile.addGroup((size_t)GroupLabel::MAP); + tile.addGroup((size_t)GroupLabel::MAPTILES); } bool Game::running() const @@ -393,6 +331,13 @@ bool Game::running() const return isRunning; } -bool Game::getWinner() { +void Game::setWinner(TeamLabel winningTeam) +{ + this->winner = winningTeam; + this->isRunning = false; +} + +TeamLabel Game::getWinner() +{ return this->winner; } diff --git a/src/HealthComponent.cpp b/src/HealthComponent.cpp index 67b2070..77c9e0e 100644 --- a/src/HealthComponent.cpp +++ b/src/HealthComponent.cpp @@ -1,26 +1,43 @@ #include "HealthComponent.h" #include "Components.h" +#include "Direction.h" +#include "Entity.h" +#include "Game.h" +#include void HealthComponent::init() { - createAllHearts(); + refreshHearts(); } -void HealthComponent::createAllHearts() +void HealthComponent::modifyHealth(int health) { + this->health += health; + this->refreshHearts(); +} + +void HealthComponent::refreshHearts() +{ + // clear hearts if exist + for (auto& heart : this->entity->getManager().getGroup((size_t) GroupLabel::HEARTS)) { + if (heart->getTeam() == this->entity->getTeam()) { + heart->destroy(); + } + } + int x; //starting position for first health icon - if(player) { + if(side == Direction::LEFT) { x = 10; } else { x = 730; } for(int i = 0; i < health; i++) { - + //checks for player side - if(player) { + if (side == Direction::LEFT) { createHeartComponents(x); x += 50; continue; @@ -31,10 +48,11 @@ void HealthComponent::createAllHearts() } } - void HealthComponent::createHeartComponents(int x) +void HealthComponent::createHeartComponents(int x) { - auto& heart(manager->addEntity()); + auto& heart(this->entity->getManager().addEntity()); heart.addComponent(x,5,2); heart.addComponent("assets/heart.png"); heart.addGroup((size_t)GroupLabel::HEARTS); + heart.setTeam(this->entity->getTeam()); } \ No newline at end of file diff --git a/src/KeyboardController.cpp b/src/KeyboardController.cpp index 32e9f35..c699ae3 100644 --- a/src/KeyboardController.cpp +++ b/src/KeyboardController.cpp @@ -23,30 +23,30 @@ void KeyboardController::init() void KeyboardController::update() { - transform->velocity.x = 0; - transform->velocity.y = 0; + transform->direction.x = 0; + transform->direction.y = 0; sprite->playAnimation(IDLE); if (keystates[this->up]) { - transform->velocity.y = -1; + transform->direction.y = -1; sprite->playAnimation(WALK); SoundManager::playSound(STEPS); } if (keystates[this->left]) { - transform->velocity.x = -1; + transform->direction.x = -1; sprite->playAnimation(WALK); - sprite->setDirection(LEFT); + sprite->setDirection(Direction::LEFT); SoundManager::playSound(STEPS); } if (keystates[this->down]) { - transform->velocity.y = 1; + transform->direction.y = 1; sprite->playAnimation(WALK); SoundManager::playSound(STEPS); } if (keystates[this->right]) { - transform->velocity.x = 1; + transform->direction.x = 1; sprite->playAnimation(WALK); - sprite->setDirection(RIGHT); + sprite->setDirection(Direction::RIGHT); SoundManager::playSound(STEPS); } @@ -61,14 +61,14 @@ void KeyboardController::update() //checks player source via the firing velocity //TODO: adding actual projectile textures if (fireVelocity.x > 0) { - sprite->setDirection(RIGHT); + sprite->setDirection(Direction::RIGHT); Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - false, 1, 180, 1, "assets/egg.png"); + 1, 180, 1, "assets/egg.png", this->entity->getTeam()); } else { - sprite->setDirection(LEFT); + sprite->setDirection(Direction::LEFT); Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - true, 1, 180, 1, "assets/egg.png"); + 1, 180, 1, "assets/egg.png", this->entity->getTeam()); } lastFireTime = currentTicks; diff --git a/src/Manager.cpp b/src/Manager.cpp index ba800b1..f98ebdc 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -1,14 +1,11 @@ #include "Manager.h" #include +#include +#include "Constants.h" #include "Entity.h" -void Manager::update() -{ - for (auto& e : entities) e->update(); -} - void Manager::draw() { for (auto& e : entities) e->draw(); @@ -18,7 +15,7 @@ void Manager::refresh() { for (auto i(0u); i < MAX_GROUPS; i++) { - auto& v(groupedEntities[i]); + auto& v(entitiesByGroup[i]); v.erase( std::remove_if(std::begin(v), std::end(v), [i](Entity* mEntity) @@ -27,6 +24,17 @@ void Manager::refresh() }), std::end(v)); } + for (auto i(0u); i < MAX_TEAMS; i++) + { + auto& v(entitiesByTeam[i]); + v.erase( + std::remove_if(std::begin(v), std::end(v), + [i](Entity* mEntity) + { + return !mEntity->isActive() || (size_t)(mEntity->getTeam()) != i; + }), std::end(v)); + } + entities.erase(std::remove_if(std::begin(entities), std::end(entities), [](const std::unique_ptr& mEntity) { @@ -35,14 +43,38 @@ void Manager::refresh() std::end(entities)); } +void Manager::update() +{ + for (auto& e : entities) e->update(); +} + void Manager::addToGroup(Entity* mEntity, Group mGroup) { - groupedEntities[mGroup].emplace_back(mEntity); + entitiesByGroup.at(mGroup).emplace_back(mEntity); } std::vector& Manager::getGroup(Group mGroup) { - return groupedEntities[mGroup]; + return entitiesByGroup.at(mGroup); +} + +void Manager::addToTeam(Entity* mEntity, Team mTeam) +{ + entitiesByTeam.at(mTeam).emplace_back(mEntity); // +} + +std::vector& Manager::getTeam(Team mTeam) +{ + return entitiesByTeam.at(mTeam); +} + +std::vector Manager::getAll() +{ + std::vector entity_vec; + for (auto& entity_ptr : entities) { + entity_vec.emplace_back(entity_ptr.get()); + } + return entity_vec; } Entity& Manager::addEntity() diff --git a/src/PlayerComponent.cpp b/src/PlayerComponent.cpp new file mode 100644 index 0000000..3afa811 --- /dev/null +++ b/src/PlayerComponent.cpp @@ -0,0 +1 @@ +#include "PlayerComponent.h" \ No newline at end of file diff --git a/src/ProjectileComponent.cpp b/src/ProjectileComponent.cpp index 7b40298..e49f6ef 100644 --- a/src/ProjectileComponent.cpp +++ b/src/ProjectileComponent.cpp @@ -1,22 +1,45 @@ #include "ProjectileComponent.h" +#include "CollisionHandler.h" #include "Components.h" +#include "Entity.h" +#include "Game.h" +#include "HealthComponent.h" +#include "Vector2D.h" +#include +#include void ProjectileComponent::init() { transformComponent = &entity->getComponent(); + transformComponent->direction = direction; SoundManager::playSound(THROW_EGG); } void ProjectileComponent::update() { - transformComponent->velocity = velocity; - distance += speed; + IntersectionBitSet boundsIntersection = Game::collisionHandler->getIntersectionWithBounds(entity); + + if ((boundsIntersection | IntersectionBitSet("1100")).all() || (boundsIntersection | IntersectionBitSet("0011")).all()) { + this->entity->destroy(); + std::cout << "out of bounds" << std::endl; + } + if (distance > range) { - entity->destroy(); - entity->getComponent().removeCollision(); - //std::cout << "out of range" << std::endl; + this->entity->destroy(); + std::cout << "out of range" << std::endl; + } + + Entity* player; + if ((player = Game::collisionHandler->getAnyIntersection( + entity, + Vector2D(0,0), + {GroupLabel::PLAYERS}, + {entity->getTeam()}, + true)) != nullptr) { + player->getComponent().modifyHealth(); + this->entity->destroy(); } } \ No newline at end of file diff --git a/src/SpriteComponent.cpp b/src/SpriteComponent.cpp index 6af7d6b..58e2295 100644 --- a/src/SpriteComponent.cpp +++ b/src/SpriteComponent.cpp @@ -2,6 +2,7 @@ #include +#include "Direction.h" #include "TextureManager.h" #include "Entity.h" #include "TransformComponent.h" @@ -71,9 +72,9 @@ void SpriteComponent::playAnimation(AnimationType type) this->speed = animations.at(type)->speed; } -void SpriteComponent::setDirection(SpriteDirection direction) +void SpriteComponent::setDirection(Direction direction) { - if (direction == RIGHT) { + if (direction == Direction::RIGHT) { this->flipped = true; return; } diff --git a/src/TransformComponent.cpp b/src/TransformComponent.cpp index 21eb6e2..18ad88d 100644 --- a/src/TransformComponent.cpp +++ b/src/TransformComponent.cpp @@ -1,5 +1,14 @@ #include "TransformComponent.h" + +#include "CollisionHandler.h" +#include "ColliderComponent.h" #include "Constants.h" +#include "Entity.h" +#include "Game.h" +#include "Vector2D.h" +#include +#include +#include #include "SoundManager.h" @@ -38,37 +47,36 @@ TransformComponent::TransformComponent(float x, float y, int w, int h, int scale void TransformComponent::init() { - velocity.zero(); + direction.zero(); } void TransformComponent::update() { // if(velocity.x != 0 && velocity.y != 0) - float multiplier = velocity.x != 0 && velocity.y != 0 ? 0.707 : 1; //normalizes vector - - Vector2D newPos( - position.x + velocity.x * speed * multiplier, - position.y + velocity.y * speed * multiplier + float multiplier = direction.x != 0 && direction.y != 0 ? 0.707 : 1; // normalizes vector; only works if directions are in increments of 45° + Vector2D positionChange( + direction.x * speed * multiplier, + direction.y * speed * multiplier ); - if (newPos.x < 0) - { - newPos.x = 0; - } - else if (newPos.x + (this->width * this->scale) > SCREEN_SIZE_WIDTH) - { - newPos.x = SCREEN_SIZE_WIDTH - (this->width * this->scale); + // TODO: move to separate functions + + if (this->entity->hasGroup((size_t) GroupLabel::PLAYERS)) { + IntersectionBitSet intersections = + (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(positionChange.x, 0)) | + (Game::collisionHandler->getAnyIntersection(entity, Vector2D(positionChange.x, 0), {GroupLabel::MAPTILES, GroupLabel::COLLIDERS})) & + IntersectionBitSet("0011")) | + (CollisionHandler::getIntersectionWithBounds(entity, Vector2D(0, positionChange.y)) | + (Game::collisionHandler->getAnyIntersection(entity, Vector2D(0, positionChange.y), {GroupLabel::MAPTILES, GroupLabel::COLLIDERS})) & + IntersectionBitSet("1100")); + + if (intersections.test((size_t) direction::LEFT) || intersections.test((size_t) direction::RIGHT)) + positionChange.x = 0; + + if (intersections.test((size_t) direction::UP) || intersections.test((size_t) direction::DOWN)) + positionChange.y = 0; } - if (newPos.y < 0) - { - newPos.y = 0; - } - else if (newPos.y + (this->height * this->scale) > SCREEN_SIZE_HEIGHT) - { - newPos.y = SCREEN_SIZE_HEIGHT - (this->height * this->scale); - } - - position = newPos; -} + position += positionChange; +}; \ No newline at end of file diff --git a/src/Vector2D.cpp b/src/Vector2D.cpp index 7fae1a5..6368ff3 100644 --- a/src/Vector2D.cpp +++ b/src/Vector2D.cpp @@ -1,4 +1,5 @@ #include "Vector2D.h" +#include "SDL_rect.h" Vector2D::Vector2D() { @@ -36,6 +37,12 @@ Vector2D& operator/(Vector2D& vector1, const Vector2D& vector2) vector1.y /= vector2.y; return vector1; } +Vector2D& operator+=(Vector2D& vector1, const Vector2D& vector2) +{ + vector1.x += vector2.x; + vector1.y += vector2.y; + return vector1; +} Vector2D& Vector2D::operator*(const int& i) { this->x *= i; @@ -50,3 +57,11 @@ Vector2D& Vector2D::zero() return *this; } + +SDL_Rect operator+(const SDL_Rect& rect, const Vector2D& vector2D) +{ + SDL_Rect newRect = rect; + newRect.x += vector2D.x; + newRect.y += vector2D.y; + return newRect; +} \ No newline at end of file