// Complete C++ Tower Defence (Parts 1 + 2) for SFML 3. Build:
// g++ tower.cpp -o tower -lsfml-graphics -lsfml-window -lsfml-system
#include <SFML/Graphics.hpp>
#include <optional>
#include <vector>
#include <cmath>
#include <string>
#include <algorithm>
struct Enemy { sf::Vector2f pos; float speed; int hp, maxHp, bounty; int wp; bool alive; bool isBoss = false; };
struct Tower { sf::Vector2f pos; float range; int damage; float cooldown; };
struct Bullet { sf::Vector2f pos; Enemy* target; float speed; int damage; bool dead; };
int main() {
sf::RenderWindow window(sf::VideoMode({800, 600}), "C++ Tower Defence");
window.setFramerateLimit(60);
std::vector<sf::Vector2f> path = {
{-20, 120}, {300, 120}, {300, 320}, {560, 320}, {560, 120}, {820, 120}
};
std::vector<Enemy> enemies; enemies.reserve(4096); // reserve so Enemy* stays valid
std::vector<Tower> towers;
std::vector<Bullet> bullets;
int gold = 150, health = 20, wave = 0, toSpawn = 0;
float spawnGap = 0, rest = 3.f;
bool won = false, lost = false;
const int MAX_WAVE = 5, TOWER_COST = 50;
auto nearPath = [&](float x, float y, float r) {
for (size_t i = 0; i + 1 < path.size(); i++) {
sf::Vector2f a = path[i], b = path[i+1];
sf::Vector2f ab = b - a, ap = sf::Vector2f(x,y) - a;
float t = (ab.x*ap.x + ab.y*ap.y) / (ab.x*ab.x + ab.y*ab.y);
t = std::max(0.f, std::min(1.f, t));
sf::Vector2f c = a + ab * t;
float dx = x - c.x, dy = y - c.y;
if (std::sqrt(dx*dx + dy*dy) < r) return true;
}
return false;
};
auto nearest = [&](Tower& t) -> Enemy* {
Enemy* best = nullptr; float bd = t.range;
for (auto& e : enemies) {
if (!e.alive) continue;
float dx = e.pos.x - t.pos.x, dy = e.pos.y - t.pos.y;
float d = std::sqrt(dx*dx + dy*dy);
if (d < bd) { bd = d; best = &e; }
}
return best;
};
sf::Clock clock;
while (window.isOpen()) {
float dt = clock.restart().asSeconds();
if (dt > 0.05f) dt = 0.05f;
while (const std::optional event = window.pollEvent()) {
if (event->is<sf::Event::Closed>()) window.close();
if (const auto* m = event->getIf<sf::Event::MouseButtonPressed>()) {
if (m->button == sf::Mouse::Button::Left && !won && !lost) {
int gx = (m->position.x / 40) * 40, gy = (m->position.y / 40) * 40;
float cx = gx + 20.f, cy = gy + 20.f;
if (!nearPath(cx, cy, 30.f) && gold >= TOWER_COST) {
towers.push_back({ {cx, cy}, 110.f, 5, 0.f });
gold -= TOWER_COST;
}
}
}
}
if (!won && !lost) {
// wave director
bool cleared = true;
for (auto& e : enemies) if (e.alive) cleared = false;
if (toSpawn == 0 && cleared) {
rest -= dt;
if (rest <= 0) {
wave++;
if (wave > MAX_WAVE) won = true;
else { toSpawn = 4 + wave * 2; rest = 3.f; }
}
} else if (toSpawn > 0) {
spawnGap -= dt;
if (spawnGap <= 0) {
bool boss = (wave == MAX_WAVE && toSpawn == 1);
int hp = boss ? 200 : (16 + wave * 4);
enemies.push_back({ path[0], boss ? 45.f : 70.f, hp, hp, boss ? 60 : 8, 1, true });
toSpawn--; spawnGap = 0.8f;
}
}
// enemies
for (auto& e : enemies) {
if (!e.alive) continue;
if (e.wp >= (int)path.size()) { e.alive = false; if (--health <= 0) lost = true; continue; }
sf::Vector2f d = path[e.wp] - e.pos;
float len = std::sqrt(d.x*d.x + d.y*d.y);
if (len < 4.f) { e.wp++; }
else e.pos += d / len * e.speed * dt;
}
// towers fire
for (auto& t : towers) {
t.cooldown -= dt;
if (t.cooldown <= 0) {
Enemy* target = nearest(t);
if (target) { bullets.push_back({ t.pos, target, 340.f, t.damage, false }); t.cooldown = 0.7f; }
}
}
// bullets home
for (auto& b : bullets) {
if (!b.target->alive) { b.dead = true; continue; }
sf::Vector2f d = b.target->pos - b.pos;
float len = std::sqrt(d.x*d.x + d.y*d.y);
if (len < 8.f) {
b.target->hp -= b.damage;
if (b.target->hp <= 0) { b.target->alive = false; gold += b.target->bounty; }
b.dead = true;
} else b.pos += d / len * b.speed * dt;
}
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
[](const Bullet& b){ return b.dead; }), bullets.end());
}
// draw
window.clear(sf::Color(24, 40, 28));
for (size_t i = 0; i + 1 < path.size(); i++) {
sf::Vector2f a = path[i], b = path[i+1], d = b - a;
float len = std::sqrt(d.x*d.x + d.y*d.y);
sf::RectangleShape seg({len, 36.f});
seg.setPosition(a);
seg.setOrigin({0.f, 18.f});
seg.setRotation(sf::radians(std::atan2(d.y, d.x)));
seg.setFillColor(sf::Color(70, 62, 48));
window.draw(seg);
}
for (auto& t : towers) {
sf::CircleShape rng(t.range); rng.setOrigin({t.range, t.range}); rng.setPosition(t.pos);
rng.setFillColor(sf::Color(96, 165, 250, 24)); window.draw(rng);
sf::RectangleShape r({30.f, 30.f}); r.setOrigin({15.f, 15.f}); r.setPosition(t.pos);
r.setFillColor(sf::Color(96, 165, 250)); window.draw(r);
}
for (auto& e : enemies) if (e.alive) {
sf::CircleShape c(12.f); c.setOrigin({12.f, 12.f}); c.setPosition(e.pos);
c.setFillColor(e.maxHp > 100 ? sf::Color(200, 60, 200) : sf::Color(230, 80, 70));
window.draw(c);
}
for (auto& b : bullets) {
sf::CircleShape c(4.f); c.setOrigin({4.f, 4.f}); c.setPosition(b.pos);
c.setFillColor(sf::Color(250, 204, 21)); window.draw(c);
}
window.display();
}
}// Complete C++ Tower Defence (Parts 1 + 2) for SFML 3. Build:
// g++ tower.cpp -o tower -lsfml-graphics -lsfml-window -lsfml-system
#include <SFML/Graphics.hpp>
#include <optional>
#include <vector>
#include <cmath>
#include <string>
#include <algorithm>
struct Enemy { sf::Vector2f pos; float speed; int hp, maxHp, bounty; int wp; bool alive; bool isBoss = false; };
struct Tower { sf::Vector2f pos; float range; int damage; float cooldown; };
struct Bullet { sf::Vector2f pos; Enemy* target; float speed; int damage; bool dead; };
int main() {
sf::RenderWindow window(sf::VideoMode({800, 600}), "C++ Tower Defence");
window.setFramerateLimit(60);
std::vector<sf::Vector2f> path = {
{-20, 120}, {300, 120}, {300, 320}, {560, 320}, {560, 120}, {820, 120}
};
std::vector<Enemy> enemies; enemies.reserve(4096); // reserve so Enemy* stays valid
std::vector<Tower> towers;
std::vector<Bullet> bullets;
int gold = 150, health = 20, wave = 0, toSpawn = 0;
float spawnGap = 0, rest = 3.f;
bool won = false, lost = false;
const int MAX_WAVE = 5, TOWER_COST = 50;
auto nearPath = [&](float x, float y, float r) {
for (size_t i = 0; i + 1 < path.size(); i++) {
sf::Vector2f a = path[i], b = path[i+1];
sf::Vector2f ab = b - a, ap = sf::Vector2f(x,y) - a;
float t = (ab.x*ap.x + ab.y*ap.y) / (ab.x*ab.x + ab.y*ab.y);
t = std::max(0.f, std::min(1.f, t));
sf::Vector2f c = a + ab * t;
float dx = x - c.x, dy = y - c.y;
if (std::sqrt(dx*dx + dy*dy) < r) return true;
}
return false;
};
auto nearest = [&](Tower& t) -> Enemy* {
Enemy* best = nullptr; float bd = t.range;
for (auto& e : enemies) {
if (!e.alive) continue;
float dx = e.pos.x - t.pos.x, dy = e.pos.y - t.pos.y;
float d = std::sqrt(dx*dx + dy*dy);
if (d < bd) { bd = d; best = &e; }
}
return best;
};
sf::Clock clock;
while (window.isOpen()) {
float dt = clock.restart().asSeconds();
if (dt > 0.05f) dt = 0.05f;
while (const std::optional event = window.pollEvent()) {
if (event->is<sf::Event::Closed>()) window.close();
if (const auto* m = event->getIf<sf::Event::MouseButtonPressed>()) {
if (m->button == sf::Mouse::Button::Left && !won && !lost) {
int gx = (m->position.x / 40) * 40, gy = (m->position.y / 40) * 40;
float cx = gx + 20.f, cy = gy + 20.f;
if (!nearPath(cx, cy, 30.f) && gold >= TOWER_COST) {
towers.push_back({ {cx, cy}, 110.f, 5, 0.f });
gold -= TOWER_COST;
}
}
}
}
if (!won && !lost) {
// wave director
bool cleared = true;
for (auto& e : enemies) if (e.alive) cleared = false;
if (toSpawn == 0 && cleared) {
rest -= dt;
if (rest <= 0) {
wave++;
if (wave > MAX_WAVE) won = true;
else { toSpawn = 4 + wave * 2; rest = 3.f; }
}
} else if (toSpawn > 0) {
spawnGap -= dt;
if (spawnGap <= 0) {
bool boss = (wave == MAX_WAVE && toSpawn == 1);
int hp = boss ? 200 : (16 + wave * 4);
enemies.push_back({ path[0], boss ? 45.f : 70.f, hp, hp, boss ? 60 : 8, 1, true });
toSpawn--; spawnGap = 0.8f;
}
}
// enemies
for (auto& e : enemies) {
if (!e.alive) continue;
if (e.wp >= (int)path.size()) { e.alive = false; if (--health <= 0) lost = true; continue; }
sf::Vector2f d = path[e.wp] - e.pos;
float len = std::sqrt(d.x*d.x + d.y*d.y);
if (len < 4.f) { e.wp++; }
else e.pos += d / len * e.speed * dt;
}
// towers fire
for (auto& t : towers) {
t.cooldown -= dt;
if (t.cooldown <= 0) {
Enemy* target = nearest(t);
if (target) { bullets.push_back({ t.pos, target, 340.f, t.damage, false }); t.cooldown = 0.7f; }
}
}
// bullets home
for (auto& b : bullets) {
if (!b.target->alive) { b.dead = true; continue; }
sf::Vector2f d = b.target->pos - b.pos;
float len = std::sqrt(d.x*d.x + d.y*d.y);
if (len < 8.f) {
b.target->hp -= b.damage;
if (b.target->hp <= 0) { b.target->alive = false; gold += b.target->bounty; }
b.dead = true;
} else b.pos += d / len * b.speed * dt;
}
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
[](const Bullet& b){ return b.dead; }), bullets.end());
}
// draw
window.clear(sf::Color(24, 40, 28));
for (size_t i = 0; i + 1 < path.size(); i++) {
sf::Vector2f a = path[i], b = path[i+1], d = b - a;
float len = std::sqrt(d.x*d.x + d.y*d.y);
sf::RectangleShape seg({len, 36.f});
seg.setPosition(a);
seg.setOrigin({0.f, 18.f});
seg.setRotation(sf::radians(std::atan2(d.y, d.x)));
seg.setFillColor(sf::Color(70, 62, 48));
window.draw(seg);
}
for (auto& t : towers) {
sf::CircleShape rng(t.range); rng.setOrigin({t.range, t.range}); rng.setPosition(t.pos);
rng.setFillColor(sf::Color(96, 165, 250, 24)); window.draw(rng);
sf::RectangleShape r({30.f, 30.f}); r.setOrigin({15.f, 15.f}); r.setPosition(t.pos);
r.setFillColor(sf::Color(96, 165, 250)); window.draw(r);
}
for (auto& e : enemies) if (e.alive) {
sf::CircleShape c(12.f); c.setOrigin({12.f, 12.f}); c.setPosition(e.pos);
c.setFillColor(e.maxHp > 100 ? sf::Color(200, 60, 200) : sf::Color(230, 80, 70));
window.draw(c);
}
for (auto& b : bullets) {
sf::CircleShape c(4.f); c.setOrigin({4.f, 4.f}); c.setPosition(b.pos);
c.setFillColor(sf::Color(250, 204, 21)); window.draw(c);
}
window.display();
}
}