diff --git a/assets/atk_speed_powerup.png b/assets/atk_speed_powerup.png new file mode 100644 index 0000000..9bcce69 Binary files /dev/null and b/assets/atk_speed_powerup.png differ diff --git a/assets/characterSelection.png b/assets/characterSelection.png index d88de55..68e958a 100644 Binary files a/assets/characterSelection.png and b/assets/characterSelection.png differ diff --git a/assets/heart_powerup.png b/assets/heart_powerup.png new file mode 100644 index 0000000..0a2a92d Binary files /dev/null and b/assets/heart_powerup.png differ diff --git a/assets/movement_speed_powerup.png b/assets/movement_speed_powerup.png new file mode 100644 index 0000000..e0876ec Binary files /dev/null and b/assets/movement_speed_powerup.png differ diff --git a/assets/startscreen.png b/assets/startscreen.png index 926abe8..a1fe40d 100644 Binary files a/assets/startscreen.png and b/assets/startscreen.png differ diff --git a/include/AnimationHandler.h b/include/AnimationHandler.h index 6fc8081..069fc2f 100644 --- a/include/AnimationHandler.h +++ b/include/AnimationHandler.h @@ -1,5 +1,5 @@ #pragma once -#include +#include struct Animation { uint8_t index; diff --git a/include/AssetManager.h b/include/AssetManager.h index d87dc63..6e14f85 100644 --- a/include/AssetManager.h +++ b/include/AssetManager.h @@ -1,13 +1,21 @@ -#include "Entity.h" - +#pragma once #include #include #include #include +#include "Entity.h" + class Vector2D; class Manager; +enum class PowerupType +{ + HEART, + WALKINGSPEED, + SHOOTINGSPEED +}; + class AssetManager { public: @@ -16,6 +24,10 @@ public: ~AssetManager(); void createProjectile(Vector2D pos, Vector2D velocity, int scale, int range, int speed, const char* texturePath, TeamLabel teamLabel); + void createPowerup(Vector2D pos, PowerupType type); + + Vector2D calculateSpawnPosition(); + PowerupType calculateType(); //texture management void addTexture(std::string id, const char* path); diff --git a/include/Constants.h b/include/Constants.h index 6490de9..f3463ad 100644 --- a/include/Constants.h +++ b/include/Constants.h @@ -9,7 +9,8 @@ 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 std::size_t MAX_STATS = 8; +constexpr std::size_t MAX_TEAMS = 8; constexpr int SCREEN_SIZE_HEIGHT = 640; constexpr int SCREEN_SIZE_WIDTH = 800; @@ -20,3 +21,10 @@ constexpr int TILE_SIZE = 32; constexpr int MAP_SIZE_X = 25; constexpr int MAP_SIZE_Y = 20; + +constexpr int SPAWN_ATTEMPTS = 20; + +constexpr int BUFF_DURATION = 6000; + +constexpr int BUFF_VALUE = 1; + diff --git a/include/Entity.h b/include/Entity.h index 9d768b5..ad0f2b5 100644 --- a/include/Entity.h +++ b/include/Entity.h @@ -23,7 +23,8 @@ enum class GroupLabel ENEMIES, COLLIDERS, PROJECTILE, - HEARTS + HEARTS, + POWERUPS }; enum class TeamLabel diff --git a/include/KeyboardController.h b/include/KeyboardController.h index c5bc756..9079b73 100644 --- a/include/KeyboardController.h +++ b/include/KeyboardController.h @@ -3,6 +3,7 @@ #include "Component.h" #include "Vector2D.h" +#include "Constants.h" #include "SoundManager.h" class TransformComponent; @@ -23,8 +24,8 @@ public: //for attack cooldown in between shots uint32_t lastFireTime = 0; - uint32_t fireCooldown = 800; //in ms can be adjusted to change possible attack-speed - + uint32_t fireCooldown = 1000; //in ms can be adjusted to change possible attack-speed + KeyboardController() = default; KeyboardController(SDL_Scancode up, SDL_Scancode down, SDL_Scancode left, SDL_Scancode right, SDL_Scancode fire, Vector2D fireVelocity); ~KeyboardController() = default; @@ -32,6 +33,8 @@ public: void init() override; void update() override; + void modifyAtkSpeed(int8_t modifier); + private: //for creation of projectiles TransformComponent* player; //for starting position of projectile diff --git a/include/PowerupComponent.h b/include/PowerupComponent.h new file mode 100644 index 0000000..f83ea29 --- /dev/null +++ b/include/PowerupComponent.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Component.h" +#include "AssetManager.h" + +class PowerupComponent : public Component +{ +public: + PowerupComponent(PowerupType type); + ~PowerupComponent() {}; + + void update() override; + void heartEffect(Entity* player); + void movementSpeedEffect(Entity* player); + void atkSpeedEffect(Entity* player); + +private: + void (PowerupComponent::*pickupFunc)(Entity* player); +}; \ No newline at end of file diff --git a/include/ProjectileComponent.h b/include/ProjectileComponent.h index 76a5e7b..0e57a69 100644 --- a/include/ProjectileComponent.h +++ b/include/ProjectileComponent.h @@ -2,6 +2,7 @@ #include "Component.h" #include "Vector2D.h" +#include "Constants.h" class TransformComponent; diff --git a/include/StatEffectsComponent.h b/include/StatEffectsComponent.h new file mode 100644 index 0000000..9dd8c5b --- /dev/null +++ b/include/StatEffectsComponent.h @@ -0,0 +1,27 @@ +#pragma once +#include "Component.h" +#include "Constants.h" +#include +#include + +enum class Stats +{ + MOVEMENT_SPEED, + ATTACK_SPEED +}; + +class StatEffectsComponent : public Component{ +public: + StatEffectsComponent() {}; + ~StatEffectsComponent() {}; + + void init() override; + void update() override; + + void modifyStatDur(Stats stat, uint8_t duration); + + void modifyStatValue(Stats stat, int modifier); + +private: + std::array buffs = { 0 }; +}; \ No newline at end of file diff --git a/include/TextureDict.h b/include/TextureDict.h index 847e649..0a4b6ed 100644 --- a/include/TextureDict.h +++ b/include/TextureDict.h @@ -2,15 +2,23 @@ #include #include +#include "AssetManager.h" class TextureDict { public: - const std::map textureDictionary = { + const std::map tileDictionary = { {1, "assets/water.png"}, {2, "assets/dirt.png"}, {3, "assets/grass.png"}, {7, "assets/grass_water_left.png"}, {9, "assets/grass_water_right.png"} }; + + + std::map powerupDictionary = { + {PowerupType::HEART, "assets/heart_powerup.png"}, + {PowerupType::WALKINGSPEED, "assets/movement_speed_powerup.png"}, + {PowerupType::SHOOTINGSPEED, "assets/atk_speed_powerup.png"} + }; }; diff --git a/include/TextureManager.h b/include/TextureManager.h index a65415c..bc1601d 100644 --- a/include/TextureManager.h +++ b/include/TextureManager.h @@ -2,15 +2,10 @@ #include #include +#include +#include #include -struct cmp_str -{ - bool operator()(char const *a, char const *b) const { - return strcmp(a, b) < 0; - } -}; - class TextureManager { public: @@ -21,7 +16,7 @@ class TextureManager } } - std::map texture_cache; + std::map texture_cache; SDL_Texture* loadTexture(const char* fileName); static std::vector splitSpriteSheet(SDL_Texture* spriteSheet, int width, int height, int spritesOnSheet); diff --git a/include/TransformComponent.h b/include/TransformComponent.h index 8a16b91..e57d747 100644 --- a/include/TransformComponent.h +++ b/include/TransformComponent.h @@ -2,6 +2,7 @@ #include "Component.h" #include "Vector2D.h" +#include "Constants.h" class TransformComponent : public Component { @@ -23,4 +24,5 @@ public: void init() override; void update() override; + void modifySpeed(int8_t modifier); }; diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index b5bda9d..1dff706 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -1,11 +1,21 @@ #include "AssetManager.h" -#include "Entity.h" #include "TextureManager.h" #include "SoundManager.h" #include "Components.h" #include "Game.h" +#include "TransformComponent.h" + +#include "CollisionHandler.h" +#include "ColliderComponent.h" +#include "Constants.h" +#include "Entity.h" +#include "Game.h" +#include "Vector2D.h" +#include "PowerupComponent.h" +#include + AssetManager::AssetManager(Manager* manager) : man(manager) {} AssetManager::~AssetManager() {} @@ -36,4 +46,57 @@ void AssetManager::createProjectile(Vector2D pos, Vector2D velocity, int scale, projectile.addComponent("projectile", 0.6f); projectile.addGroup((size_t)GroupLabel::PROJECTILE); projectile.setTeam(teamLabel); +} + +void AssetManager::createPowerup(Vector2D pos, PowerupType type) { + TextureDict textureDict; + + auto& powerups(man->addEntity()); + powerups.addComponent(pos.x, pos.y, 32, 32, 1); //32x32 is standard size for objects + auto it = textureDict.powerupDictionary.find(type); + if (it == textureDict.powerupDictionary.end()) { + std::cout << "it end" << std::endl; + } + + try { + powerups.addComponent(it->second.data()); + } + catch (std::runtime_error e) { + std::cout << e.what() << std::endl; + } + + powerups.addComponent("powerup", 0.6f); + powerups.addComponent(type); + powerups.addGroup((size_t)GroupLabel::POWERUPS); +} + +Vector2D AssetManager::calculateSpawnPosition() +{ + Vector2D spawnPos = Vector2D(-1, -1); + bool conflict = false; + for (int i = 0; i <= SPAWN_ATTEMPTS; i++) + { + SDL_Rect spawnRect; + spawnRect.h = spawnRect.w = 32; + spawnRect.x = rand() % (SCREEN_SIZE_WIDTH - spawnRect.w); + spawnRect.y = rand() % (SCREEN_SIZE_HEIGHT - spawnRect.h); + conflict = false; + for (auto cc : Game::collisionHandler->getColliders({ GroupLabel::MAPTILES })) + { + if (SDL_HasIntersection(&spawnRect, &cc->collider) && strcmp(cc->tag, "projectile")) + { + conflict = true; + break; + } + } + if (conflict) continue; + spawnPos = Vector2D(spawnRect.x, spawnRect.y); + } + return spawnPos; +} + +PowerupType AssetManager::calculateType() +{ + PowerupType type = PowerupType(rand() % 3); + return type; } \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index e4ec8f7..a9e264c 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -10,6 +10,7 @@ #include "HealthComponent.h" #include "Map.h" #include "TextureManager.h" +#include "StatEffectsComponent.h" #include "Constants.h" Map* map; @@ -29,7 +30,6 @@ auto& player1(manager.addEntity()); auto& player2(manager.addEntity()); auto& wall(manager.addEntity()); -//auto& projectile (manager.addEntity()); Game::Game() = default; @@ -153,18 +153,20 @@ void Game::init(const char* title, int xpos, int ypos, int width, int height, bo 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(SDL_SCANCODE_W, SDL_SCANCODE_S, SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_E, Vector2D(2, 0));//custom keycontrols can be added player1.addComponent("player", 0.8f); //adds tag (for further use, reference tag) player1.addComponent(5, Direction::LEFT); + player1.addComponent(); 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(SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_RCTRL, Vector2D(-2, 0)); player2.addComponent("enemy", 0.8f); player2.addComponent(5, Direction::RIGHT); + player2.addComponent(); player2.addGroup((size_t) GroupLabel::PLAYERS); } @@ -269,6 +271,7 @@ auto& tiles(manager.getGroup((size_t)GroupLabel::MAPTILES)); auto& players(manager.getGroup((size_t)GroupLabel::PLAYERS)); auto& projectiles(manager.getGroup((size_t)GroupLabel::PROJECTILE)); auto& hearts(manager.getGroup((size_t)GroupLabel::HEARTS)); +auto& powerups(manager.getGroup((size_t)GroupLabel::POWERUPS)); void Game::handleEvents() { @@ -289,9 +292,16 @@ void Game::update() Vector2D playerPos = player1.getComponent().position; Vector2D enemyPos = player2.getComponent().position; + int powerupSpawn = rand() % 500; + manager.refresh(); manager.update(); + if (powerupSpawn == 0) + { + assets->createPowerup(assets->calculateSpawnPosition(), assets->calculateType()); + } + // 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) { @@ -304,13 +314,14 @@ void Game::render() { SDL_RenderClear(renderer); for (auto& t : tiles) - { t->draw(); - } - for (auto& p : players) - { + + for (auto& p : powerups) p->draw(); - } + + for (auto& p : players) + p->draw(); + for (auto& p : projectiles) p->draw(); diff --git a/src/KeyboardController.cpp b/src/KeyboardController.cpp index c699ae3..26324af 100644 --- a/src/KeyboardController.cpp +++ b/src/KeyboardController.cpp @@ -63,15 +63,20 @@ void KeyboardController::update() if (fireVelocity.x > 0) { sprite->setDirection(Direction::RIGHT); Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - 1, 180, 1, "assets/egg.png", this->entity->getTeam()); + 1, 180, 2, "assets/egg.png", this->entity->getTeam()); } else { sprite->setDirection(Direction::LEFT); Game::assets->createProjectile(Vector2D(player->position.x, player->position.y), fireVelocity, - 1, 180, 1, "assets/egg.png", this->entity->getTeam()); + 1, 180, 2, "assets/egg.png", this->entity->getTeam()); } lastFireTime = currentTicks; } } +} + +void KeyboardController::modifyAtkSpeed(int8_t modifier) +{ + this->fireCooldown -= modifier * 400; } \ No newline at end of file diff --git a/src/PowerupComponent.cpp b/src/PowerupComponent.cpp new file mode 100644 index 0000000..d1dba7e --- /dev/null +++ b/src/PowerupComponent.cpp @@ -0,0 +1,57 @@ +#include "PowerupComponent.h" +#include "Game.h" +#include "CollisionHandler.h" +#include "Entity.h" +#include "HealthComponent.h" +#include "StatEffectsComponent.h" +#include "Constants.h" +#include + +PowerupComponent::PowerupComponent(PowerupType type) +{ + switch (type) + { + case PowerupType::HEART: + this->pickupFunc = (&PowerupComponent::heartEffect); + break; + case PowerupType::WALKINGSPEED: + this->pickupFunc = (&PowerupComponent::movementSpeedEffect); + break; + case PowerupType::SHOOTINGSPEED: + this->pickupFunc = (&PowerupComponent::atkSpeedEffect); + break; + default: + break; + } +} + +void PowerupComponent::update() +{ + Entity* player; + if ((player = Game::collisionHandler->getAnyIntersection( + entity, + Vector2D(0, 0), + { GroupLabel::PLAYERS }, + {}, + true)) != nullptr) + { + (this->*pickupFunc)(player); + this->entity->destroy(); + } +} + +void PowerupComponent::heartEffect(Entity* player) +{ + if(player->getComponent().getHealth() < 5) + player->getComponent().modifyHealth(1); +} + +void PowerupComponent::movementSpeedEffect(Entity* player) +{ + player->getComponent().modifyStatDur(Stats::MOVEMENT_SPEED, (uint8_t) BUFF_DURATION); +} + +void PowerupComponent::atkSpeedEffect(Entity* player) +{ + player->getComponent().modifyStatDur(Stats::ATTACK_SPEED, (uint8_t) BUFF_DURATION); +} \ No newline at end of file diff --git a/src/SpriteComponent.cpp b/src/SpriteComponent.cpp index 52c6f44..bd7e3f0 100644 --- a/src/SpriteComponent.cpp +++ b/src/SpriteComponent.cpp @@ -76,10 +76,5 @@ void SpriteComponent::playAnimation(AnimationType type) void SpriteComponent::setDirection(Direction direction) { - if (direction == Direction::RIGHT) { - this->flipped = true; - return; - } - - this->flipped = false; + this->flipped = direction == Direction::RIGHT; } \ No newline at end of file diff --git a/src/StatEffectsComponent.cpp b/src/StatEffectsComponent.cpp new file mode 100644 index 0000000..3d48989 --- /dev/null +++ b/src/StatEffectsComponent.cpp @@ -0,0 +1,42 @@ +#include "StatEffectsComponent.h" +#include "Entity.h" +#include "TransformComponent.h" +#include "KeyboardController.h" +#include +#include + +void StatEffectsComponent::init() +{} + +void StatEffectsComponent::update() +{ + for (int i = 0; i < MAX_STATS; i++) + { + if (this->buffs.at(i) == 0) continue; + if (this->buffs.at(i) - 1 == 0) + { + this->modifyStatValue((Stats)i, BUFF_VALUE * -1); + } + this->buffs.at(i) -= 1; + } +} + +void StatEffectsComponent::modifyStatDur(Stats stat, uint8_t duration) +{ + if(this->buffs.at((uint8_t)stat) == 0) this->modifyStatValue(stat, BUFF_VALUE); + this->buffs.at((uint8_t)stat) += duration; +} + +void StatEffectsComponent::modifyStatValue(Stats stat, int modifier) //modifier is basically there so the modifyfuncs in the components know if stats should be increased or decreased +{ + switch (stat) + { + case Stats::MOVEMENT_SPEED: + this->entity->getComponent().modifySpeed(modifier); + break; + case Stats::ATTACK_SPEED: + this->entity->getComponent().modifyAtkSpeed(modifier); + break; + default: break; + } +} \ No newline at end of file diff --git a/src/TextureManager.cpp b/src/TextureManager.cpp index c3d0ab0..757952f 100644 --- a/src/TextureManager.cpp +++ b/src/TextureManager.cpp @@ -1,5 +1,6 @@ #include "TextureManager.h" +#include #include #include @@ -13,7 +14,7 @@ SDL_Texture* TextureManager::loadTexture(const char* fileName) } auto texture = IMG_LoadTexture(Game::renderer, fileName); if (texture == NULL) throw std::runtime_error(std::string("Couldn't load texture '") + fileName + "'"); - this->texture_cache.emplace(fileName, texture); + this->texture_cache.emplace(std::string(fileName), texture); printf("Loaded texture at '%s'\n", fileName); return texture; } diff --git a/src/TileComponent.cpp b/src/TileComponent.cpp index accd38a..c6e5cec 100644 --- a/src/TileComponent.cpp +++ b/src/TileComponent.cpp @@ -15,13 +15,11 @@ TileComponent::TileComponent(int x, int y, int w, int h, int id) this->tileRect.h = h; tileID = id; - auto it = textureDict.textureDictionary.find(tileID); //every id has its own distinct texture (in texturedict.h) - if (it == textureDict.textureDictionary.end()) { + auto it = textureDict.tileDictionary.find(tileID); //every id has its own distinct texture (in texturedict.h) + if (it == textureDict.tileDictionary.end()) { std::cout << "it end" << std::endl; return; } - bool test = it == textureDict.textureDictionary.end(); - // std::cout << it->second.data() << std::endl; this->path = it->second.data(); } diff --git a/src/TransformComponent.cpp b/src/TransformComponent.cpp index 18ad88d..a143c7b 100644 --- a/src/TransformComponent.cpp +++ b/src/TransformComponent.cpp @@ -62,21 +62,26 @@ void TransformComponent::update() // TODO: move to separate functions - if (this->entity->hasGroup((size_t) GroupLabel::PLAYERS)) { + 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})) & + (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})) & + (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::LEFT) || intersections.test((size_t)direction::RIGHT)) + positionChange.x = 0; - if (intersections.test((size_t) direction::UP) || intersections.test((size_t) direction::DOWN)) + if (intersections.test((size_t)direction::UP) || intersections.test((size_t)direction::DOWN)) positionChange.y = 0; } position += positionChange; -}; \ No newline at end of file +} + +void TransformComponent::modifySpeed(int8_t modifier) +{ + this->speed += modifier; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2e7a993..f33d4c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,12 @@ #include "Game.h" #include "Constants.h" +#include Game* game = nullptr; int main(int argc, char* argv[]) { + srand(time(NULL)); const int frameDelay = 1000 / FPS; Uint32 frameStart;