From e8c8d5bd0eddd40c31b53a36154a12186074b514 Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Thu, 19 Jun 2025 22:16:11 +0300 Subject: [PATCH 1/6] Add new tower: ThunderRod Signed-off-by: Mihai Dumitrescu --- src/Rpg/gamengine/RPGEngine.cpp | 1 + src/Rpg/menues/StartTowerDefenseMenu.cpp | 4 +++ src/TowerDefense/gamengine/GameEngine.cpp | 36 ++++++++++++++----- src/TowerDefense/gamengine/GameEngine.h | 1 + src/TowerDefense/tower/ThunderRod.cpp | 22 ++++++++++++ src/TowerDefense/tower/ThunderRod.h | 17 +++++++++ src/TowerDefense/tower/TowerSelectionMenu.cpp | 9 ++++- 7 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 src/TowerDefense/tower/ThunderRod.cpp create mode 100644 src/TowerDefense/tower/ThunderRod.h diff --git a/src/Rpg/gamengine/RPGEngine.cpp b/src/Rpg/gamengine/RPGEngine.cpp index e17f766..a1edf9a 100755 --- a/src/Rpg/gamengine/RPGEngine.cpp +++ b/src/Rpg/gamengine/RPGEngine.cpp @@ -148,6 +148,7 @@ RPGEngine::RPGEngine(sf::RenderWindow& window, GameManager* gameManager) mAvailableTowers.push_back(2); mAvailableTowers.push_back(1); + mAvailableTowers.push_back(3); // Testing std::unique_ptr woodItem = std::make_unique(); diff --git a/src/Rpg/menues/StartTowerDefenseMenu.cpp b/src/Rpg/menues/StartTowerDefenseMenu.cpp index edb6b4d..57b4d86 100644 --- a/src/Rpg/menues/StartTowerDefenseMenu.cpp +++ b/src/Rpg/menues/StartTowerDefenseMenu.cpp @@ -176,6 +176,8 @@ void StartTowerDefenseMenu::render(sf::RenderWindow& window) { mTowerSlots[i].setFillColor(sf::Color::Green); else if (mSelectedTowers[i] == 2) mTowerSlots[i].setFillColor(sf::Color::Red); + else if (mSelectedTowers[i] == 3) + mTowerSlots[i].setFillColor(sf::Color::Cyan); } else { mTowerSlots[i].setFillColor(sf::Color(0, 0, 0, 220)); } @@ -188,6 +190,8 @@ void StartTowerDefenseMenu::render(sf::RenderWindow& window) { mSelectingTowerSlots[i].setFillColor(sf::Color::Green); else if (mAvailableTowers[i] == 2) mSelectingTowerSlots[i].setFillColor(sf::Color::Red); + else if (mAvailableTowers[i] == 3) + mSelectingTowerSlots[i].setFillColor(sf::Color::Cyan); } else { mSelectingTowerSlots[i].setFillColor(sf::Color(0, 0, 0, 220)); } diff --git a/src/TowerDefense/gamengine/GameEngine.cpp b/src/TowerDefense/gamengine/GameEngine.cpp index 533eaae..8acc3ea 100644 --- a/src/TowerDefense/gamengine/GameEngine.cpp +++ b/src/TowerDefense/gamengine/GameEngine.cpp @@ -164,12 +164,24 @@ void GameEngine::processEvents() { newTower = new LaserTower(mousePos, mProjectiles); else if (mAvailableTowers[0] == 2) newTower = new FlameTurret(mousePos, mProjectiles); + else if (mAvailableTowers[0] == 3) + newTower = new ThunderRod(mousePos, mProjectiles, mEnemies); break; case 1: if (mAvailableTowers[1] == 1) newTower = new LaserTower(mousePos, mProjectiles); else if (mAvailableTowers[1] == 2) newTower = new FlameTurret(mousePos, mProjectiles); + else if (mAvailableTowers[1] == 3) + newTower = new ThunderRod(mousePos, mProjectiles, mEnemies); + break; + case 2: + if (mAvailableTowers[2] == 1) + newTower = new LaserTower(mousePos, mProjectiles); + else if (mAvailableTowers[2] == 2) + newTower = new FlameTurret(mousePos, mProjectiles); + else if (mAvailableTowers[2] == 3) + newTower = new ThunderRod(mousePos, mProjectiles, mEnemies); break; } @@ -268,12 +280,24 @@ void GameEngine::handleNonTowerClick(const sf::Vector2f& worldPos) { newTower = new LaserTower(worldPos, mProjectiles); else if (mAvailableTowers[0] == 2) newTower = new FlameTurret(worldPos, mProjectiles); + else if (mAvailableTowers[0] == 3) + newTower = new ThunderRod(worldPos, mProjectiles, mEnemies); break; case 1: - if (mAvailableTowers[0] == 1) + if (mAvailableTowers[1] == 1) newTower = new LaserTower(worldPos, mProjectiles); else if (mAvailableTowers[1] == 2) newTower = new FlameTurret(worldPos, mProjectiles); + else if (mAvailableTowers[1] == 3) + newTower = new ThunderRod(worldPos, mProjectiles, mEnemies); + break; + case 2: + if (mAvailableTowers[2] == 1) + newTower = new LaserTower(worldPos, mProjectiles); + else if (mAvailableTowers[2] == 2) + newTower = new FlameTurret(worldPos, mProjectiles); + else if (mAvailableTowers[2] == 3) + newTower = new ThunderRod(worldPos, mProjectiles, mEnemies); break; } @@ -390,18 +414,12 @@ void GameEngine::update() { // Update projectiles for (auto projectile = mProjectiles.begin(); projectile != mProjectiles.end();) { - if (projectile->getTarget() == nullptr) { - projectile = mProjectiles.erase(projectile); - continue; - } projectile->update(dt); - if (projectile->hasHitTarget()) { - projectile->getTarget()->takeDamage(projectile->getDamage()); + if (projectile->shouldRemove()) projectile = mProjectiles.erase(projectile); - } else { + else ++projectile; - } } // Update enemies diff --git a/src/TowerDefense/gamengine/GameEngine.h b/src/TowerDefense/gamengine/GameEngine.h index 0aee10d..4c2835e 100644 --- a/src/TowerDefense/gamengine/GameEngine.h +++ b/src/TowerDefense/gamengine/GameEngine.h @@ -9,6 +9,7 @@ #include "../tower/TowerMenu.h" #include "../tower/LaserTower.h" #include "../tower/FlameTurret.h" +#include "../tower/ThunderRod.h" #include "../player/Player.h" #include "../player/PlayerMenu.h" #include "../menues/SmallMenu.h" diff --git a/src/TowerDefense/tower/ThunderRod.cpp b/src/TowerDefense/tower/ThunderRod.cpp new file mode 100644 index 0000000..57d61ac --- /dev/null +++ b/src/TowerDefense/tower/ThunderRod.cpp @@ -0,0 +1,22 @@ +#include "ThunderRod.h" +#include + +static float distance(const sf::Vector2f& a, const sf::Vector2f& b) { + return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); +} + +ThunderRod::ThunderRod(const sf::Vector2f& position, std::vector& projectiles, std::vector& enemies) + : Tower(position, projectiles), mMaxChains(3), mChainRange(100.f), mEnemies(enemies) { + mShape.setFillColor(sf::Color::Cyan); + + mRange = 120.0f; + mFireRate = 2.25f; + mDamage = 14.0f; + mCost = 200; + + // mUpgrades.clear(); + // mUpgrades.push_back(Upgrade("Chain Damage", 200, 5, [this]() { mDamage += 5; })); + // mUpgrades.push_back(Upgrade("Chain Range", 150, 10, [this]() { mChainRange += 15.0f; })); + // mUpgrades.push_back(Upgrade("Chain Count", 300, 1, [this]() { mMaxChains++; })); +} + diff --git a/src/TowerDefense/tower/ThunderRod.h b/src/TowerDefense/tower/ThunderRod.h new file mode 100644 index 0000000..d1159f7 --- /dev/null +++ b/src/TowerDefense/tower/ThunderRod.h @@ -0,0 +1,17 @@ +#pragma once +#include "Tower.h" +#include +#include "../enemy/Enemy.h" +#include "../projectiles/FlameProjectile.h" +// #include "../projectilehtningProjectile.h" + +class ThunderRod : public Tower { +public: + ThunderRod(const sf::Vector2f& position, std::vector& projectiles, std::vector& enemies); + +private: + int mMaxChains; + float mChainRange; + std::vector& mEnemies; +}; + diff --git a/src/TowerDefense/tower/TowerSelectionMenu.cpp b/src/TowerDefense/tower/TowerSelectionMenu.cpp index 9247cb5..b6d3063 100644 --- a/src/TowerDefense/tower/TowerSelectionMenu.cpp +++ b/src/TowerDefense/tower/TowerSelectionMenu.cpp @@ -10,6 +10,7 @@ TowerSelectionMenu::TowerSelectionMenu(std::vector& availableTowers) if (!mFont.loadFromFile("assets/fonts/gameFont.ttf")) std::cout << "Couldn't load font2 ffsk" << std::endl; + initializeHexagon(); initializeLabels(); initializeStats(); @@ -40,6 +41,8 @@ void TowerSelectionMenu::initializeLabels() { label.setString("Laser"); else if (mAvailableTowers[i] == 2) label.setString("Flame"); + else if (mAvailableTowers[i] == 3) + label.setString("Thunder"); else label.setString(""); } else { @@ -186,6 +189,8 @@ void TowerSelectionMenu::updateHover(const sf::Vector2f& mousePosition, int crys towerCost = 100; else if (mAvailableTowers[i] == 2) towerCost = 150; + else if (mAvailableTowers[i] == 3) + towerCost = 200; sf::Color normalColor; @@ -268,8 +273,10 @@ void TowerSelectionMenu::formatStats(int i) { if (mAvailableTowers[i] == 2) mTowerIcon.setFillColor(sf::Color(255, 84, 84, 255)); - else if(mAvailableTowers[i] == 1) + else if (mAvailableTowers[i] == 1) mTowerIcon.setFillColor(sf::Color::Green); + else if (mAvailableTowers[i] == 3) + mTowerIcon.setFillColor(sf::Color::Cyan); } bool TowerSelectionMenu::isPointInConvexShape(const sf::ConvexShape& shape, const sf::Vector2f& point) const { From f04475c60969bf2bed662b9c47baaa72d4c6bd3c Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Thu, 19 Jun 2025 22:17:44 +0300 Subject: [PATCH 2/6] Make fireProjectile() method virtual Signed-off-by: Mihai Dumitrescu --- src/TowerDefense/tower/Tower.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TowerDefense/tower/Tower.h b/src/TowerDefense/tower/Tower.h index d4a0071..75f6d2a 100644 --- a/src/TowerDefense/tower/Tower.h +++ b/src/TowerDefense/tower/Tower.h @@ -50,7 +50,7 @@ class Tower { float mDamage; std::vector& mProjectiles; - void fireProjectile(); + virtual void fireProjectile(); std::vector mUpgrades; }; From d6b299fadcf4ef88b3dc7df615b2fa494b80194d Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Thu, 19 Jun 2025 22:18:55 +0300 Subject: [PATCH 3/6] Change the fire mechanic based on the new virtual method Signed-off-by: Mihai Dumitrescu --- src/TowerDefense/tower/FlameTurret.cpp | 6 ++++-- src/TowerDefense/tower/FlameTurret.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/TowerDefense/tower/FlameTurret.cpp b/src/TowerDefense/tower/FlameTurret.cpp index f5d4ff3..fe04eff 100644 --- a/src/TowerDefense/tower/FlameTurret.cpp +++ b/src/TowerDefense/tower/FlameTurret.cpp @@ -7,6 +7,8 @@ static float distance(const sf::Vector2f& a, const sf::Vector2f& b) { FlameTurret::FlameTurret(const sf::Vector2f& position, std::vector& projectiles) : Tower(position, projectiles) { + + mShape.setFillColor(sf::Color(120, 200, 50)); mRange = 100.0f; mFireRate = 2.0f; mDamage = 10.0f; @@ -28,7 +30,7 @@ void FlameTurret::update(float dt, std::vector& enemies) { float dist = distance(mShape.getPosition(), enemy.getPosition()); if (dist <= mRange) { selectedEnemy = &enemy; - fireFlame(); + fireProjectile(); mTimeSinceLastShot = 0.0f; break; } @@ -40,7 +42,7 @@ void FlameTurret::update(float dt, std::vector& enemies) { mTarget = nullptr; } -void FlameTurret::fireFlame() { +void FlameTurret::fireProjectile() { if (!mTarget) return; // Fire the flame projectile diff --git a/src/TowerDefense/tower/FlameTurret.h b/src/TowerDefense/tower/FlameTurret.h index 301f3d5..690bfdb 100644 --- a/src/TowerDefense/tower/FlameTurret.h +++ b/src/TowerDefense/tower/FlameTurret.h @@ -11,6 +11,6 @@ class FlameTurret : public Tower { void update(float dt, std::vector& enemies) override; private: - void fireFlame(); + void fireProjectile() override; }; From 501ebe663401b7ac4575bdda8453cff45e727280 Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Thu, 19 Jun 2025 22:20:25 +0300 Subject: [PATCH 4/6] Introduce a remove check of the projectile Signed-off-by: Mihai Dumitrescu --- src/TowerDefense/projectiles/Projectile.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TowerDefense/projectiles/Projectile.h b/src/TowerDefense/projectiles/Projectile.h index fbb0821..cfc9cc1 100644 --- a/src/TowerDefense/projectiles/Projectile.h +++ b/src/TowerDefense/projectiles/Projectile.h @@ -13,6 +13,7 @@ class Projectile { sf::Vector2f getPosition() const; float getDamage() const; Enemy* getTarget() const; + virtual bool shouldRemove() const {return mHitTarget || !mTarget;}; protected: sf::RectangleShape mShape; @@ -21,7 +22,7 @@ class Projectile { float mDamage; bool mHitTarget; bool mAlive; - + bool checkCollision(const Enemy* target) const; }; From 8e9be87953086e99c36838a3691ed7f46b44f5bc Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Thu, 19 Jun 2025 22:21:23 +0300 Subject: [PATCH 5/6] Make the damage call take place in projectile class instead of GameEngine Signed-off-by: Mihai Dumitrescu --- src/TowerDefense/projectiles/Projectile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TowerDefense/projectiles/Projectile.cpp b/src/TowerDefense/projectiles/Projectile.cpp index 5633026..97108ec 100644 --- a/src/TowerDefense/projectiles/Projectile.cpp +++ b/src/TowerDefense/projectiles/Projectile.cpp @@ -48,7 +48,7 @@ void Projectile::update(float dt) { // Check if the projectile has hit the target if (!mHitTarget && checkCollision(mTarget)) { - //mTarget->takeDamage(mDamage); + mTarget->takeDamage(mDamage); mHitTarget = true; } } From 59c95e082e734c0593905c14e463191ed29ba2bf Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Sun, 6 Jul 2025 22:40:19 +0300 Subject: [PATCH 6/6] Add base class for LightingChain Signed-off-by: Mihai Dumitrescu --- .../projectiles/LightingChain.cpp | 36 +++++++++++++++++++ src/TowerDefense/projectiles/LightingChain.h | 14 ++++++++ src/TowerDefense/tower/ThunderRod.cpp | 27 +++++++++++++- src/TowerDefense/tower/ThunderRod.h | 10 +++++- 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/TowerDefense/projectiles/LightingChain.cpp create mode 100644 src/TowerDefense/projectiles/LightingChain.h diff --git a/src/TowerDefense/projectiles/LightingChain.cpp b/src/TowerDefense/projectiles/LightingChain.cpp new file mode 100644 index 0000000..bf10963 --- /dev/null +++ b/src/TowerDefense/projectiles/LightingChain.cpp @@ -0,0 +1,36 @@ +#include "LightingChain.h" +#include +#include + +LightingChain::LightingChain(const sf::Vector2f& position, Enemy* target, float speed, float damage) + : Projectile(position, target, speed, damage) { + mLaserLine.setPrimitiveType(sf::Lines); + mLaserLine.resize(2); + + mLaserLine[0].position = position; + mLaserLine[0].color = sf::Color::Red; + + mLaserLine[1].position = target->getPosition(); + mLaserLine[1].color = sf::Color::Red; +} + +void LightingChain::update(float dt) { + if (mTarget == nullptr || mTarget->isDead()) { + mHitTarget = true; + return; + } + + mLaserLine[0].position = getPosition(); + mLaserLine[1].position = mTarget->getPosition(); + + if (checkCollision(mTarget)) { + mTarget->takeDamage(getDamage()); + mHitTarget = true; + } +} + +void LightingChain::render(sf::RenderWindow& window) { + if (!mHitTarget) + window.draw(mLaserLine); +} + diff --git a/src/TowerDefense/projectiles/LightingChain.h b/src/TowerDefense/projectiles/LightingChain.h new file mode 100644 index 0000000..a397349 --- /dev/null +++ b/src/TowerDefense/projectiles/LightingChain.h @@ -0,0 +1,14 @@ +#pragma once +#include "Projectile.h" + +class LightingChain : public Projectile { +public: + LightingChain(const sf::Vector2f& position, Enemy* target, float speed, float damage); + + void update(float dt) override; + void render(sf::RenderWindow& window) override; + +private: + sf::VertexArray mLaserLine; +}; + diff --git a/src/TowerDefense/tower/ThunderRod.cpp b/src/TowerDefense/tower/ThunderRod.cpp index 57d61ac..03d170b 100644 --- a/src/TowerDefense/tower/ThunderRod.cpp +++ b/src/TowerDefense/tower/ThunderRod.cpp @@ -6,7 +6,8 @@ static float distance(const sf::Vector2f& a, const sf::Vector2f& b) { } ThunderRod::ThunderRod(const sf::Vector2f& position, std::vector& projectiles, std::vector& enemies) - : Tower(position, projectiles), mMaxChains(3), mChainRange(100.f), mEnemies(enemies) { + : Tower(position, projectiles), mMaxChains(3), mChainRange(100.f), mEnemies(enemies), + mFireCooldown(1.25f), mProjectileSpeed(200.0f), mPosition(position), mDamage(5) { mShape.setFillColor(sf::Color::Cyan); mRange = 120.0f; @@ -20,3 +21,27 @@ ThunderRod::ThunderRod(const sf::Vector2f& position, std::vector& pr // mUpgrades.push_back(Upgrade("Chain Count", 300, 1, [this]() { mMaxChains++; })); } +void ThunderRod::update(float dt, std::vector& enemies) +{ + mFireCooldown -= dt; + if (mFireCooldown <= 0.0f) { + Enemy* closestEnemy = nullptr; + float closestDistance = mRange; + + for (auto& enemy : enemies) { + float distance = std::hypot(enemy.getPosition().x - mPosition.x, enemy.getPosition().y - mPosition.y); + if (distance < closestDistance) { + closestEnemy = &enemy; + closestDistance = distance; + } + } + + if (closestEnemy != nullptr) { + mProjectiles.emplace_back(LightingChain(mPosition, closestEnemy, mProjectileSpeed, mDamage)); + mFireCooldown = mFireRate; + } + } + + for (auto& projectile : mProjectiles) + projectile.update(dt); +} diff --git a/src/TowerDefense/tower/ThunderRod.h b/src/TowerDefense/tower/ThunderRod.h index d1159f7..1e4dcf7 100644 --- a/src/TowerDefense/tower/ThunderRod.h +++ b/src/TowerDefense/tower/ThunderRod.h @@ -2,16 +2,24 @@ #include "Tower.h" #include #include "../enemy/Enemy.h" -#include "../projectiles/FlameProjectile.h" +#include "../projectiles/LightingChain.h" // #include "../projectilehtningProjectile.h" class ThunderRod : public Tower { public: ThunderRod(const sf::Vector2f& position, std::vector& projectiles, std::vector& enemies); + void update(float dt, std::vector& enemies) override; private: int mMaxChains; float mChainRange; std::vector& mEnemies; + + float mFireCooldown; + float mProjectileSpeed; + sf::Vector2f mPosition; + float mDamage; + bool mIsHighDamage; + bool mIsContinuous; };