Page MenuHomePhabricator (Chris)

No OneTemporary

Size
113 KB
Referenced Files
None
Subscribers
None
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a1ede31..f248689 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,11 @@
cmake_minimum_required(VERSION 3.14)
project(SDL_Game)
set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
include_directories(include)
find_package(Boost REQUIRED COMPONENTS filesystem)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(lib)
link_libraries(mingw32 SDL2main SDL2 SDL2_image SDL2_gfx SDL2_ttf)
add_executable(SDL_Game src/SDL_FontCache.c src/SDL_FontCache.h src/main.cpp src/Game.cpp src/Game.h src/Entity.h src/Component.h src/System.h src/systems/RenderSystem.cpp src/systems/RenderSystem.h src/components/Visibility.cpp src/components/Visibility.h src/components/Velocity.cpp src/components/Velocity.h src/systems/MovementSystem.cpp src/systems/MovementSystem.h src/components/Position.h src/components/PathIndex.h src/GameData.h src/components/Sequence.cpp src/components/Sequence.h src/systems/BloonsSpawnSystem.cpp src/systems/BloonsSpawnSystem.h src/GameData.cpp src/components/Action.h src/systems/EventSystem.cpp src/systems/EventSystem.h src/components/Draggable.h src/systems/DraggingSystem.cpp src/systems/DraggingSystem.h src/Settings.h src/systems/RemoveEntitiesSystem.cpp src/systems/RemoveEntitiesSystem.h src/components/MoveEntityEvent.h src/components/RangeShadow.h src/Physics.cpp src/Physics.h src/systems/CollisionSystem.cpp src/systems/CollisionSystem.h src/systems/ShotsSpawnSystem.cpp src/systems/ShotsSpawnSystem.h src/components/IntegerComponents.h src/components/FloatComponents.h src/components/AttackSpeed.h src/components/AttackSpeed.cpp src/components/PoppedBloons.h src/systems/DamageSystem.cpp src/systems/DamageSystem.h src/components/FlagComponents.h src/System.cpp src/Entity.cpp src/components/DamageEvent.h src/systems/MenuSystem.cpp src/systems/MenuSystem.h src/components/Regrow.h src/systems/LoadLevelSystem.cpp src/systems/LoadLevelSystem.h src/components/Goo.h src/components/Upgrades.h src/systems/PopEffectSystem.cpp src/systems/PopEffectSystem.h)
target_link_libraries(SDL_Game ${Boost_LIBRARIES})
\ No newline at end of file
diff --git a/scripts/convert_levels.py b/scripts/convert_levels.py
index cc9ab06..2c1f53f 100644
--- a/scripts/convert_levels.py
+++ b/scripts/convert_levels.py
@@ -1,42 +1,42 @@
from struct import pack
i = 1
kinds = {
'red': 0,
'blue': 1,
'green': 2,
'yellow': 3,
'pink': 4,
'purple': 5,
'white': 6,
'black': 7,
'lead': 8,
'zebra': 9,
'rainbow': 10,
'ceramic': 11,
'moab': 12,
'bfb': 13,
'zomg': 14,
'ddt': 15,
'bad': 16
}
while True:
try:
level = open(f"assets/Levels/level{i}.csv").read().split('\n')[1:]
with open(f'assets/Levels/level{i}.data', 'wb') as out_file:
for sequence in level:
delay, kind, amount, bps = sequence.split(',')
- delay = int(delay)
+ delay = int(float(delay) * 60)
amount = int(amount)
bps = float(bps)
kind = kind.split(' ')
out_file.write(pack('<I', delay))
out_file.write(pack('<I', kinds[kind[0]]))
out_file.write(pack('<I', amount))
out_file.write(pack('<f', bps))
out_file.write(bytes([len(kind) > 1 and 'r' in kind[1]]))
out_file.write(bytes([len(kind) > 1 and 'c' in kind[1]]))
out_file.write(bytes([len(kind) > 1 and 'f' in kind[1]]))
except FileNotFoundError:
break
i += 1
diff --git a/src/Game.cpp b/src/Game.cpp
index ce9d4e7..5080434 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,661 +1,664 @@
//
// Created by Ido Mozes on 18/06/2019.
//
#include <array>
#include "Game.h"
using namespace boost::filesystem;
Game::Game(bool fullscreen, float mapScale) {
gameData.mapScale = mapScale;
gameData.fullscreen = fullscreen;
path p = path("../assets/Bloons");
directory_iterator it{p};
for (auto &p :it) {
gameData.assets[p.path().filename().string().substr(0, p.path().filename().string().length() - 4)] = IMG_Load(
p.path().string().c_str());
}
p = path("../assets/Sprites");
it = directory_iterator{p};
for (auto &p :it) {
gameData.assets[p.path().filename().string().substr(0, p.path().filename().string().length() - 4)] = IMG_Load(
p.path().string().c_str());
}
p = path("../assets/Icons");
it = directory_iterator{p};
for (auto &p :it) {
gameData.assets[p.path().filename().string().substr(0, p.path().filename().string().length() - 4)] = IMG_Load(
p.path().string().c_str());
}
gameData.assets["map"] = IMG_Load("../assets/map0.jpg");
gameData.assets["upgrade_bar"] = IMG_Load("../assets/upgrade_bar.png");
gameData.assets["menu"] = IMG_Load("../assets/menu.jpg");
gameData.assets["UpgradesBackground"] = IMG_Load("../assets/upgrade_bar_items.png");
renderSystem = new RenderSystem();
renderSystem->init(gameData);
loadMap();
std::initializer_list<std::tuple<int, std::string, Point, float>> sprites[]{
{{MAP, "map", {SIDEBAR_WIDTH, 0}, 1}},
{},
{},
{},
{},
{},
{},
{
{OTHER, "upgrade_bar", {0, 0}, 1},
{OTHER, "menu", {MAP_WIDTH + SIDEBAR_WIDTH, 0}, 1},
{OTHER, "Cash", {MAP_WIDTH + SIDEBAR_WIDTH + 20, 9}, 0.6},
{OTHER, "Lives", {MAP_WIDTH + SIDEBAR_WIDTH + 20, 31}, 0.6},
{UPGRADES_BACKGROUND, "UpgradesBackground", {0, 0}, 1}
},
};
for (int i = 0; i < N_LAYERS; i++) {
for (auto[kind, surfaceName, position, scale] :sprites[i]) {
auto spriteEntity = new Entity();
auto[texture, surface]=gameData.getTexture(surfaceName);
spriteEntity->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X), int(position.Y), int(surface->w * scale)});
if (kind != OTHER)
spriteEntity->addComponent<Kind>(kind);
layers[i].emplace_back(spriteEntity);
}
}
// Buttons
EntityP button(new Entity());
auto[texture, surface]=gameData.getTexture("Play");
Point position{SIDEBAR_WIDTH + MAP_WIDTH + 67, 480};
button->addComponent<Visibility>(texture, surface, SDL_Rect{int(position.X - surface->w / 4),
int(position.Y - surface->h / 4),
int(surface->w / 2)});
button->addComponent<Action>(CLICK);
button->addComponent<Kind>(PLAY_FAST_FORWARD);
layers[MENU_LAYER].emplace_back(button);
gameData.playButton = button;
auto nextStrategyButton = new Entity();
std::tie(texture, surface) = gameData.getTexture("RightArrow");
position = {116, 21};
nextStrategyButton->addComponent<Visibility>(texture, surface, SDL_Rect{int(position.X - 7.5),
int(position.Y - 7.5), 15});
nextStrategyButton->addComponent<Action>(CLICK);
nextStrategyButton->addComponent<Kind>(NEXT_STRATEGY);
layers[MENU_LAYER].emplace_back(nextStrategyButton);
auto previousStrategyButton = new Entity();
std::tie(texture, surface) = gameData.getTexture("LeftArrow");
position = {32, 21};
previousStrategyButton->addComponent<Visibility>(texture, surface, SDL_Rect{int(position.X - 7.5),
int(position.Y - 7.5), 15});
previousStrategyButton->addComponent<Action>(CLICK);
previousStrategyButton->addComponent<Kind>(PREVIOUS_STRATEGY);
layers[MENU_LAYER].emplace_back(previousStrategyButton);
for (int j = 0; j < 3; ++j) {
auto upgradePath = new Entity();
upgradePath->addComponent<Visibility>(texture, surface, SDL_Rect{0, 0, 0});
upgradePath->addComponent<Action>(CLICK);
upgradePath->addComponent<Kind>(UPGRADE_PATH_1 + j);
layers[MENU_LAYER].emplace_back(upgradePath);
}
-
+ auto sellTowerButton = new Entity();
+ sellTowerButton->addComponent<Action>(CLICK);
+ sellTowerButton->addComponent<Kind>(SELL_TOWER);
+ layers[MENU_LAYER].emplace_back(sellTowerButton);
// Towers
auto tower = new Entity();
tower->addComponent<Kind>(SUPER_MONKEY);
tower->addComponent<ShotKind>(DART);
std::tie(texture, surface) = gameData.getTexture("SuperMonkey");
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 85};
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(150);
tower->addComponent<AttackSpeed>(17.2);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(170);
tower->addComponent<Type>(TOWER_T);
tower->addComponent<Cost>(2975);
auto &superMonkeyUpgrades = tower->addComponent<Upgrades>();
std::string image = "SuperMonkey_LaserBlasts";
std::tie(texture, surface) = gameData.getTexture(image);
auto[lockedTexture, lockedSurface] = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Laser Blasts", 2295, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 1},
{SHOT_KIND_UPGRADE, LASER}}));
image = "SuperMonkey_PlasmaVision";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Plasma Blasts", 4250, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 1},
{ATTACK_SPEED_UPGRADE, 2},
{SHOT_KIND_UPGRADE, PLASMA}}));
image = "SuperMonkey_SunAvatar";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Sun Avatar", 17850, surface, texture, lockedTexture, {{SHOTS_AMOUNT_UPGRADE, 3},
{PIERCE_UPGRADE, 3},
{SHOT_KIND_UPGRADE, SUN}}));
image = "SuperMonkey_SuperRange";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Super Range", 850, surface, texture, lockedTexture,
{{RANGE_UPGRADE, 1.25},
{DISTANCE_UPGRADE, 1.25}}));
image = "SuperMonkey_EpicRange";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Epic Range", 1190, surface, texture, lockedTexture, {{RANGE_UPGRADE, 1.25},
{DISTANCE_UPGRADE, 1.25},
{PIERCE_UPGRADE, 1}}));
image = "SuperMonkey_RoboMonkey";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Robo Monkey", 7650, surface, texture, lockedTexture, {{SHOTS_AMOUNT_UPGRADE, 2},
{DAMAGE_UPGRADE, 4}}));
image = "SuperMonkey_Ultravision";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
superMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Ultravision", 1020, surface, texture, lockedTexture, {{RANGE_UPGRADE, 1.1},
{DISTANCE_UPGRADE, 1.1},
{CAMO_UPGRADE, 0}}));
superMonkeyUpgrades.selectedPathUpgrades[0] = 1;
superMonkeyUpgrades.selectedPathUpgrades[1] = 1;
superMonkeyUpgrades.selectedPathUpgrades[2] = 0;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 85};
tower->addComponent<Kind>(SNIPER_MONKEY);
tower->addComponent<ShotKind>(BULLET);
std::tie(texture, surface) = gameData.getTexture("SniperMonkey");
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(500);
tower->addComponent<AttackSpeed>(0.45);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(500);
tower->addComponent<Cost>(300);
tower->addComponent<Type>(TOWER_T);
auto &sniperMonkeyUpgrades = tower->addComponent<Upgrades>();
image = "SniperMonkey_FullMetalJacket";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Full Metal Jacket", 295, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 3},
{SHOT_KIND_UPGRADE, ENHANCED_BULLET}}));
image = "SniperMonkey_LargeCalibre";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Large Calibre", 1275, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 3}}));
image = "SniperMonkey_DeadlyPrecision";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Deadly Precision", 2250, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 11}}));
image = "SniperMonkey_NightVisionGoggles";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Night Vision Goggles", 225, surface, texture, lockedTexture, {{CAMO_UPGRADE, 0}}));
image = "SniperMonkey_FasterFiring";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Faster Firing", 340, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.54}}));
image = "SniperMonkey_EvenFasterFiring";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Even Faster Firing", 340, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.66}}));
image = "SniperMonkey_Semi-Automatic";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Semi-Automatic", 2975, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 3}}));
image = "SniperMonkey_FullAutoRifle";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Full Auto Rifle", 4035, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 2},
{SHOT_KIND_UPGRADE, ENHANCED_BULLET}}));
sniperMonkeyUpgrades.selectedPathUpgrades[0] = 3;
sniperMonkeyUpgrades.selectedPathUpgrades[1] = 1;
sniperMonkeyUpgrades.selectedPathUpgrades[2] = 2;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 141};
tower->addComponent<Kind>(DART_MONKEY);
tower->addComponent<ShotKind>(DART);
std::tie(texture, surface) = gameData.getTexture("DartMonkey");
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(100);
tower->addComponent<AttackSpeed>(0.95);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(150);
tower->addComponent<Cost>(170);
tower->addComponent<Type>(TOWER_T);
auto &upgrades = tower->addComponent<Upgrades>();
image = "DartMonkey_SharpShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[0].emplace_back(
Upgrade("Sharp Shots", 120, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 1}}));
image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[0].emplace_back(
Upgrade("Razor Sharp Shots", 185, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 2}}));
image = "DartMonkey_Spike-O-Pult";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[0].emplace_back(
Upgrade("Spike-O-Pult", 225, surface, texture, lockedTexture, {{DISTANCE_UPGRADE, 6},
{ATTACK_SPEED_UPGRADE, 0.76},
{SHOT_KIND_UPGRADE, SPIKE},
{PIERCE_UPGRADE, 14}}));
image = "DartMonkey_Juggernaut";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[0].emplace_back(
Upgrade("Juggernaut", 1530, surface, texture, lockedTexture,
{{RANGE_UPGRADE, 1.25},
{ATTACK_SPEED_UPGRADE, 1.25},
{SHOT_KIND_UPGRADE, JUGGERNAUT},
{PIERCE_UPGRADE, 86}}));
image = "DartMonkey_LongRangeDarts_QuickShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[1].emplace_back(
Upgrade("Quick Shots", 85, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.15}}));
image = "DartMonkey_VeryQuickShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[1].emplace_back(
Upgrade("Very Quick Shots", 160, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.33}}));
image = "DartMonkey_TripleDarts";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[1].emplace_back(
Upgrade("Triple Shot", 340, surface, texture, lockedTexture, {{SHOTS_AMOUNT_UPGRADE, 3}}));
image = "DartMonkey_LongRangeDarts_QuickShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[2].emplace_back(
Upgrade("Long Range Darts", 75, surface, texture, lockedTexture, {{DISTANCE_UPGRADE, 1.25},
{RANGE_UPGRADE, 1.25}}));
image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[2].emplace_back(
Upgrade("Enhanced Eyesight", 170, surface, texture, lockedTexture, {{DISTANCE_UPGRADE, 1.2},
{RANGE_UPGRADE, 1.2},
{CAMO_UPGRADE, 0}}));
image = "DartMonkey_Crossbow";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades.paths[2].emplace_back(
Upgrade("Crossbow", 530, surface, texture, lockedTexture, {{DISTANCE_UPGRADE, 1.16},
{RANGE_UPGRADE, 1.16},
{PIERCE_UPGRADE, 1},
{DAMAGE_UPGRADE, 2}}));
upgrades.selectedPathUpgrades[0] = 2;
upgrades.selectedPathUpgrades[1] = 1;
upgrades.selectedPathUpgrades[2] = 1;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 197};
tower->addComponent<Kind>(BOMB_TOWER);
tower->addComponent<ShotKind>(BOMB);
std::tie(texture, surface) = gameData.getTexture("BombTower");
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(100);
tower->addComponent<AttackSpeed>(0.63);
tower->addComponent<Pierce>(18);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(120);
tower->addComponent<Cost>(555);
tower->addComponent<Spread>(20);
tower->addComponent<Type>(TOWER_T);
auto &bombShooterUpgrades = tower->addComponent<Upgrades>();
image = "BombShooter_BiggerBombs";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[0].emplace_back(
Upgrade("Bigger Bombs", 340, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 10},
{SPREAD_UPGRADE, 1.25}}));
image = "BombShooter_HeavyBombs";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[0].emplace_back(
Upgrade("Heavy Bombs", 680, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 1}}));
image = "BombShooter_ReallyBigBombs";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[0].emplace_back(
Upgrade("Really Big Bombs", 1020, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 20},
{SPREAD_UPGRADE, 1.5}}));
image = "BombShooter_BloonImpact";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[0].emplace_back(
Upgrade("Bloon Impact", 2720, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 1},
{SHOT_KIND_UPGRADE, ENHANCED_BOMB},
{ADD_GOO_UPGRADE, STUN}}));
image = "BombShooter_BloonCrush";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[0].emplace_back(
Upgrade("Bloon Crush", 46750, surface, texture, lockedTexture, {{SPREAD_UPGRADE, 1.5},
{MOAB_CLASS_AFFECTING_UPGRADE, 0},
{GOO_DURATION_UPGRADE, 120},
{DAMAGE_UPGRADE, 9}}));
image = "BombShooter_FasterReload";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[1].emplace_back(
Upgrade("Faster Reload", 210, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.2}}));
image = "BombShooter_MissileLauncher";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[1].emplace_back(
Upgrade("Missile Launcher", 340, surface, texture, lockedTexture, {{SHOT_KIND_UPGRADE, MISSILE},
{RANGE_UPGRADE, 1.15},
{DISTANCE_UPGRADE, 1.15}}));
image = "BombShooter_MOABMauler";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[1].emplace_back(
Upgrade("MOAB Mauler", 765, surface, texture, lockedTexture, {{SHOT_KIND_UPGRADE, MOAB_MAULER}}));
image = "BombShooter_MOABAssassin";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[1].emplace_back(
Upgrade("MOAB Assassin", 2720, surface, texture, lockedTexture, {{SHOT_KIND_UPGRADE, MOAB_ASSASSIN}}));
image = "BombShooter_MOABEliminator";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[1].emplace_back(
Upgrade("MOAB Eliminator", 21250, surface, texture, lockedTexture, {{SHOT_KIND_UPGRADE, MOAB_ELIMINATOR}}));
image = "BombShooter_ExtraRange";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
bombShooterUpgrades.paths[2].emplace_back(
Upgrade("Extra Range", 170, surface, texture, lockedTexture, {{RANGE_UPGRADE, 1.5},
{DISTANCE_UPGRADE, 1.5}}));
bombShooterUpgrades.selectedPathUpgrades[0] = 2;
bombShooterUpgrades.selectedPathUpgrades[1] = 4;
bombShooterUpgrades.selectedPathUpgrades[2] = 0;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 197};
tower->addComponent<Kind>(GLUE_GUNNER);
tower->addComponent<ShotKind>(GOO_SHOT);
std::tie(texture, surface) = gameData.getTexture("GlueGunner");
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(150);
tower->addComponent<AttackSpeed>(0.97);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(0);
tower->addComponent<Distance>(170);
tower->addComponent<Cost>(230);
tower->addComponent<Spread>(70);
tower->addComponent<Type>(TOWER_T);
tower->addComponent<Goo>(GUM, 120);
auto &upgrades2 = tower->addComponent<Upgrades>();
image = "GlueGunner_GlueSoak_BiggerGlobs";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[0].emplace_back(
Upgrade("Glue Soak", 170, surface, texture, lockedTexture, {{GOO_SOAK_UPGRADE, 0}}));
image = "GlueGunner_CorrosiveGlue";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[0].emplace_back(
Upgrade("Corrosive Glue", 225, surface, texture, lockedTexture, {{GOO_KIND_UPGRADE, CORROSIVE}}));
image = "GlueGunner_BloonDissolver";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[0].emplace_back(
Upgrade("Bloon Dissolver", 2805, surface, texture, lockedTexture, {{DAMAGE_UPGRADE, 1},
{CORROSIVE_INTERVAL_UPGRADE, 60}}));
image = "GlueGunner_BloonLiquefier";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[0].emplace_back(
Upgrade("Bloon Liquefier", 5950, surface, texture, lockedTexture,
{{CORROSIVE_INTERVAL_UPGRADE, 60 / 10.0}}));
image = "GlueGunner_GlueSoak_BiggerGlobs";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[1].emplace_back(
Upgrade("Bigger Globs", 85, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 1}}));
image = "GlueGunner_GlueSplatter";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[1].emplace_back(
Upgrade("Glue Splatter", 1530, surface, texture, lockedTexture, {{PIERCE_UPGRADE, 4}}));
image = "GlueGunner_GlueHose";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[1].emplace_back(
Upgrade("Glue Hose", 2760, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 3}}));
image = "GlueGunner_StickierGlue";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[2].emplace_back(
Upgrade("Stickier Glue", 100, surface, texture, lockedTexture, {{GOO_DURATION_UPGRADE, 240}}));
image = "GlueGunner_StrongerGlue";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[2].emplace_back(
Upgrade("Stronger Glue", 340, surface, texture, lockedTexture, {{GOO_STICKINESS_UPGRADE, 0.3}}));
image = "GlueGunner_SuperGlue";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
upgrades2.paths[2].emplace_back(
Upgrade("Super Glue", 2720, surface, texture, lockedTexture, {{GOO_KIND_UPGRADE, GLUE}}));
upgrades2.selectedPathUpgrades[0] = 3;
upgrades2.selectedPathUpgrades[1] = 2;
upgrades2.selectedPathUpgrades[2] = 1;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 141};
tower->addComponent<Kind>(TACK_SHOOTER);
tower->addComponent<ShotKind>(TACK);
std::tie(texture, surface) = gameData.getTexture("TackShooter");
tower->addComponent<Visibility>(texture, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(90);
tower->addComponent<AttackSpeed>(0.6);
tower->addComponent<ShotsAmount>(8);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(100);
tower->addComponent<Cost>(305);
tower->addComponent<Type>(TOWER_T);
auto &tackShooterUpgrades = tower->addComponent<Upgrades>();
image = "TackShooter_FasterShooting";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[0].emplace_back(
Upgrade("Faster Shooting", 125, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.33}}));
image = "TackShooter_EvenFasterShooting";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[0].emplace_back(
Upgrade("Even Faster Shooting", 225, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 2}}));
image = "TackShooter_HotShots";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[0].emplace_back(
Upgrade("Hot Shots", 510, surface, texture, lockedTexture, {{SHOT_KIND_UPGRADE, HOT_TACK},
{DAMAGE_UPGRADE, 1}}));
image = "TackShooter_LongRangeTacks";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[1].emplace_back(
Upgrade("Long Range Tacks", 85, surface, texture, lockedTexture, {{RANGE_UPGRADE, 1.15},
{DISTANCE_UPGRADE, 1.15}}));
image = "TackShooter_SuperRangeTacks";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[1].emplace_back(
Upgrade("Super Range Tacks", 190, surface, texture, lockedTexture, {{RANGE_UPGRADE, 1.25},
{DISTANCE_UPGRADE, 1.25}}));
image = "TackShooter_BladeShooter";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[1].emplace_back(
Upgrade("Blade Shooter", 465, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.18},
{PIERCE_UPGRADE, 2},
{DAMAGE_UPGRADE, 2},
{SHOT_KIND_UPGRADE, BLADE}}));
image = "TackShooter_MoreTacks";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[2].emplace_back(
Upgrade("More Tacks", 85, surface, texture, lockedTexture, {{SHOTS_AMOUNT_UPGRADE, 10}}));
image = "TackShooter_EvenMoreTacks";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[2].emplace_back(
Upgrade("Even More Tacks", 85, surface, texture, lockedTexture, {{SHOTS_AMOUNT_UPGRADE, 12}}));
image = "TackShooter_TackSprayer";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[2].emplace_back(
Upgrade("Tack Sprayer", 350, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 1.33},
{SHOTS_AMOUNT_UPGRADE, 16}}));
image = "TackShooter_Overdrive";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[2].emplace_back(
Upgrade("Overdrive", 2125, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 3}}));
image = "TackShooter_TheTackZone";
std::tie(texture, surface) = gameData.getTexture(image);
std::tie(lockedTexture, lockedSurface) = gameData.getTexture(image + "Locked");
tackShooterUpgrades.paths[2].emplace_back(
Upgrade("The Tack Zone", 17000, surface, texture, lockedTexture, {{ATTACK_SPEED_UPGRADE, 2},
{SHOT_KIND_UPGRADE, ENHANCED_TACK},
{RANGE_UPGRADE, 2},
{SHOTS_AMOUNT_UPGRADE, 32},
{DISTANCE_UPGRADE, 2},
{PIERCE_UPGRADE, 8}}));
tackShooterUpgrades.selectedPathUpgrades[0] = 2;
tackShooterUpgrades.selectedPathUpgrades[1] = 2;
tackShooterUpgrades.selectedPathUpgrades[2] = 2;
layers[MENU_LAYER].emplace_back(tower);
// Systems
systems.emplace_back(new LoadLevelSystem);
systems.emplace_back(new BloonsSpawnSystem);
systems.emplace_back(new ShotsSpawnSystem);
systems.emplace_back(new EventSystem);
systems.emplace_back(new DraggingSystem);
systems.emplace_back(new CollisionSystem);
systems.emplace_back(new DamageSystem);
systems.emplace_back(new PopEffectSystem);
systems.emplace_back(new MenuSystem);
systems.emplace_back(new MovementSystem);
systems.emplace_back(new RemoveEntitiesSystem);
systems.emplace_back(renderSystem);
}
Game::~Game() {
SDL_Quit();
}
void Game::update() {
for (auto &system : systems) {
system->update(layers.begin(), gameData);
}
}
void Game::loadMap() {
std::string fileName = "../assets/map" + std::to_string(gameData.map);
std::ifstream obstaclesFile(fileName + "_obstacles.data", std::ios::binary);
int x = 0, y = 0;
for (int i = 0; i < ceilf(MAP_WIDTH * MAP_HEIGHT / 8.0); ++i) {
unsigned char byte;
obstaclesFile.read((char *) &byte, 1);
for (int j = 0; j < 8; ++j) {
char bit = (byte & 2 << j) >> j;
gameData.mapData[x][y] = bit;
x++;
if (x == MAP_WIDTH) {
x = 0;
y++;
}
if (x * y >= MAP_WIDTH * MAP_HEIGHT)
goto readPathFile;
}
}
readPathFile:
gameData.path.clear();
std::ifstream pathFile(fileName + "_path.data", std::ios::binary);
unsigned int length;
TempPoint startingPoint, finishPoint;
pathFile.read((char *) &startingPoint, 4);
pathFile.read((char *) &length, 4);
gameData.path.resize(length);
pathFile.read(&gameData.path[0], length);
pathFile.read((char *) &finishPoint, 4);
gameData.startingPoint = {float(startingPoint.X), float(startingPoint.Y)};
gameData.finishPoint = {float(finishPoint.X), float(finishPoint.Y)};
}
diff --git a/src/GameData.h b/src/GameData.h
index 482e88a..9db543d 100644
--- a/src/GameData.h
+++ b/src/GameData.h
@@ -1,51 +1,50 @@
//
// Created by Ido Mozes on 03/07/2019.
//
#ifndef SDL_GAME_GAMEDATA_H
#define SDL_GAME_GAMEDATA_H
#include <vector>
#include <memory>
#include <unordered_map>
#include "Component.h"
#include "Entity.h"
#include "Settings.h"
#include "SDL.h"
#include "SDL_FontCache.h"
constexpr char FREE = 0;
constexpr char OBSTACLE = 1;
constexpr char TOWER = 2;
class GameData {
public:
int FPS = 60;
bool isRunning = true;
- bool lost = false;
bool isDragging = false;
- int cash = 100000;
+ int cash = 800;
bool levelRunning = false;
bool levelReady = false;
int lives = 200;
int level = 1;
- int finalLevel = 2;
+ int finalLevel = 100;
int map = 0;
float mapScale;
bool fullscreen;
std::vector<char> path;
char mapData[MAP_WIDTH][MAP_HEIGHT];
Point startingPoint;
Point finishPoint;
std::unordered_map<std::string, SDL_Surface *> assets;
std::unordered_map<std::string, SDL_Texture *> assetsCache;
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
EntityP selected;
FC_Font* fonts[FONTS_LENGTH];
EntityP playButton;
~GameData();
std::tuple<SDL_Texture *,SDL_Surface *> getTexture(const std::string &name);
};
#endif //SDL_GAME_GAMEDATA_H
diff --git a/src/components/Action.h b/src/components/Action.h
index 02c6206..f80370b 100644
--- a/src/components/Action.h
+++ b/src/components/Action.h
@@ -1,36 +1,38 @@
//
// Created by Ido Mozes on 08/07/2019.
//
#ifndef SDL_GAME_ACTION_H
#define SDL_GAME_ACTION_H
#include "../Component.h"
enum ActionType {
DRAG, CLICK, DROP, SELECT
};
enum VisibleObjects {
PLAY_FAST_FORWARD,
NEXT_STRATEGY,
PREVIOUS_STRATEGY,
UPGRADE_PATH_1,
UPGRADE_PATH_2,
UPGRADE_PATH_3,
- MAP, UPGRADES_BACKGROUND, OTHER
+ MAP, UPGRADES_BACKGROUND,
+ SELL_TOWER,
+ OTHER
};
class Action : public Component {
public:
bool disabled;
ActionType actionType;
static constexpr ComponentType type = ComponentType::ACTION;
Action(ActionType actionType, bool disabled = false) : actionType(actionType), disabled(disabled) {}
};
#endif //SDL_GAME_ACTION_H
diff --git a/src/main.cpp b/src/main.cpp
index 7962e92..50511be 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,25 +1,27 @@
//#include <iostream>
#include "SDL.h"
#include "Game.h"
int main(int argc, char *argv[]) {
int FPS = 60, frameDelay = 1000 / FPS;
Uint32 frameStart;
int frameTime;
+ std::cout << "Loading game..." << std::endl;
Game game(false, 1.5);
+ std::cout << "Game loaded!" << std::endl;
while (game.running()) {
if (game.gameData.FPS != FPS) {
FPS = game.gameData.FPS;
frameDelay = 1000 / FPS;
}
frameStart = SDL_GetTicks();
game.update();
frameTime = SDL_GetTicks() - frameStart;
if (frameDelay > frameTime) {
SDL_Delay(frameDelay - frameTime);
}
}
return 0;
}
\ No newline at end of file
diff --git a/src/systems/BloonsSpawnSystem.cpp b/src/systems/BloonsSpawnSystem.cpp
index 0a5aca2..655a4c0 100644
--- a/src/systems/BloonsSpawnSystem.cpp
+++ b/src/systems/BloonsSpawnSystem.cpp
@@ -1,42 +1,42 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#include "BloonsSpawnSystem.h"
void BloonsSpawnSystem::update(Entities *layers, GameData &gameData) {
- if(!gameData.levelRunning)
+ if (!gameData.levelRunning)
return;
for (auto &entity: layers[SEQUENCES_LAYER]) {
auto[sequence, kind] = entity->getComponents<Sequence, Kind>().value();
- auto [regrowP,camoP,fortifiedP] = entity->getComponentsP<Regrow,Camo,Fortified>();
+ auto[regrowP, camoP, fortifiedP] = entity->getComponentsP<Regrow, Camo, Fortified>();
int amount = sequence.getAmountReady();
- if(amount == SEQUENCE_FINISHED)
- {
+ if (amount == SEQUENCE_FINISHED) {
entity->addComponent<RemoveEntityEvent>();
continue;
}
for (int j = 0; j < amount; ++j) {
EntityP bloon(new Entity());
bloon->addComponent<Type>(BLOON_T);
bloon->addComponent<Position>(gameData.startingPoint);
bloon->addComponent<PathIndex>(0);
bloon->addComponents(kind);
if (regrowP)
bloon->addComponent<Regrow>(*regrowP);
if (camoP)
bloon->addComponent<Camo>();
if (fortifiedP)
bloon->addComponent<Fortified>();
bloon->addComponent<Lives>(getBloonProperty<TOTAL_LIVES>(bloon));
- auto [texture,surface] = gameData.getTexture(getSurfaceName(bloon));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(bloon));
bloon->addComponent<Visibility>(texture, surface,
SDL_Rect{0, 0, int(surface->w / 3), int(surface->h / 3)});
- bloon->addComponent<Range>(std::max(surface->w / 6, surface->h / 6));
+ bloon->addComponent<Range>(kind.value < MOAB ? std::max(surface->w / 6, surface->h / 6) :
+ (surface->w / 6.0 + surface->h / 6.0) / 2);
layers[BLOONS_LAYER].emplace_back(bloon);
}
}
}
diff --git a/src/systems/CollisionSystem.cpp b/src/systems/CollisionSystem.cpp
index f241875..2b8bab9 100644
--- a/src/systems/CollisionSystem.cpp
+++ b/src/systems/CollisionSystem.cpp
@@ -1,240 +1,252 @@
//
// Created by Ido Mozes on 11/07/2019.
//
#include "CollisionSystem.h"
inline float getLeft(EntityP &entity) {
return entity->getComponent<Position>()->value.X - entity->getComponent<Range>()->value;
}
int binarySearch(Entities &layer, float left, int low, int high) {
if (high <= low)
return left > getLeft(layer[low]) ? (low + 1) : low;
int mid = (low + high) / 2;
if (left == getLeft(layer[mid]))
return mid + 1;
if (left > getLeft(layer[mid]))
return binarySearch(layer, left, mid + 1, high);
return binarySearch(layer, left, low, mid - 1);
}
int
binarySearch(std::vector<std::tuple<EntityP, EntityP, float>> collided, float distance,
int low, int high) {
if (high <= low)
return distance > std::get<2>(collided[low]) ? (low + 1) : low;
int mid = (low + high) / 2;
if (distance == std::get<2>(collided[mid]))
return mid + 1;
if (distance > std::get<2>(collided[mid]))
return binarySearch(collided, distance, mid + 1, high);
return binarySearch(collided, distance, low, mid - 1);
}
void insertionSort(Entities &layer) {
// can be optimized with binary search instead of comparison to every item
for (int i = 1, j = 0; i < layer.size(); j = i++) {
auto &entity = layer[i];
EntityP entityP(entity);
auto &previousEntity = layer[i - 1];
auto[position, range] = entity->getComponents<Position, Range>().value();
float entityLeft = position.value.X - range.value;
if (entityLeft >= getLeft(layer[j]))
continue;
int loc = binarySearch(layer, entityLeft, 0, j);
while (j >= loc) {
layer[j + 1] = layer[j];
--j;
}
if (j != i - 1) {
layer[j + 1] = entityP;
}
}
}
void CollisionSystem::update(Entities *layers, GameData &gameData) {
if (!gameData.levelRunning)
return;
Entities &bloonsLayer = layers[BLOONS_LAYER];
Entities &shotsLayer = layers[SHOTS_LAYER];
insertionSort(bloonsLayer);
insertionSort(shotsLayer);
std::vector<std::tuple<EntityP, EntityP, float>> collided;
int bloonIndex = 0;
for (auto &shot : shotsLayer) {
+ switch (shot->getComponent<Kind>()->value) {
+ case GOO_SPLASH:
+ case BOMB_EXPLOSION:
+ case MISSILE_EXPLOSION:
+ case ENHANCED_BOMB_EXPLOSION:
+ case MOAB_MAULER_EXPLOSION:
+ case MOAB_ASSASSIN_EXPLOSION:
+ case MOAB_ELIMINATOR_EXPLOSION:
+ shot->addComponent<RemoveEntityEvent>();
+ break;
+ }
int collidedIndex = collided.size();
auto[shotPosition, poppedBloons] = shot->getComponents<Position, PoppedBloons>().value();
auto[shotX, shotY] = shotPosition.value;
auto shotRange = shot->getComponent<Range>()->value;
float shotLeft = shotX - shotRange;
float shotRight = shotX + shotRange;
float shotTop = shotY - shotRange;
float shotBottom = shotY + shotRange;
for (int i = bloonIndex; i < bloonsLayer.size(); ++i) {
auto &bloon = bloonsLayer[i];
auto bloonPosition = bloon->getComponent<Position>()->value;
auto[bloonX, bloonY] = bloonPosition;
auto bloonRange = bloon->getComponent<Range>()->value;
float bloonLeft = bloonX - bloonRange;
float bloonRight = bloonX + bloonRange;
float bloonTop = bloonY - bloonRange;
float bloonBottom = bloonY + bloonRange;
if (bloonRight < shotLeft) {
bloonIndex++;
continue;
}
if (shotRight < bloonLeft)
break;
if (shotBottom >= bloonTop and shotTop <= bloonBottom and !poppedBloons.value.count(bloon.get())) {
poppedBloons.value.emplace(bloon.get());
float distance = twoPointsDistance(shotPosition.value, bloonPosition);
if (collidedIndex == collided.size())
collided.emplace_back(shot, bloon, distance);
else {
int index = binarySearch(collided, distance, collidedIndex, collided.size() - 1);
if (index < collided.size())
collided.emplace(collided.begin() + index, shot, bloon, distance);
else
collided.emplace_back(shot, bloon, distance);
}
}
}
}
for (auto[shot, bloon, _] :collided) {
auto[pierce, damage, kind, position] = shot->getComponents<Pierce, Damage, Kind, Position>().value();
if (bloon->getComponent<Camo>() and !shot->getComponent<Camo>())
continue;
int bloonKind = bloon->getComponent<Kind>()->value;
if (pierce.value > 0) {
switch (kind.value) {
case LASER:
if (bloonKind != PURPLE_BLOON and bloonKind != LEAD_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case PLASMA:
if (bloonKind != PURPLE_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case SUN:
case HOT_TACK:
case ENHANCED_TACK:
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case DART:
case TACK:
case BLADE:
case SPIKE:
if (bloonKind != LEAD_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case JUGGERNAUT:
bloon->addComponent<DamageEvent>(bloonKind == CERAMIC_BLOON ? 5 : damage.value, shot);
break;
case BOMB_EXPLOSION:
case MISSILE_EXPLOSION:
case ENHANCED_BOMB_EXPLOSION:
case MOAB_MAULER_EXPLOSION:
case MOAB_ASSASSIN_EXPLOSION:
case MOAB_ELIMINATOR_EXPLOSION: {
bool breakFlag = false;
- shot->addComponent<RemoveEntityEvent>();
switch (kind.value) {
case BOMB_EXPLOSION:
case MISSILE_EXPLOSION:
case MOAB_MAULER_EXPLOSION:
if (bloonKind == BLACK_BLOON or bloonKind == ZEBRA_BLOON)
breakFlag = true;
break;
+
}
if (breakFlag)
break;
auto shotGooP = shot->getComponent<Goo>();
if (shotGooP and (bloonKind < MOAB or shot->getComponent<MoabClassAffecting>()))
- bloon->addComponent<Goo>(*shotGooP);
+ bloon->addComponent<Goo>(STUN, bloonKind < MOAB ? shotGooP->ttl : 45, 0, true);
int _damage = damage.value;
if (bloonKind >= MOAB)
switch (kind.value) {
case MOAB_MAULER_EXPLOSION:
_damage += 15;
break;
case MOAB_ASSASSIN_EXPLOSION:
_damage += 20;
break;
case MOAB_ELIMINATOR_EXPLOSION:
_damage += 80;
break;
}
if (bloonKind == CERAMIC_BLOON)
switch (kind.value) {
case MOAB_ASSASSIN_EXPLOSION:
case MOAB_ELIMINATOR_EXPLOSION:
_damage = 24;
break;
}
bloon->addComponent<DamageEvent>(_damage, shot);
break;
}
case GOO_SPLASH: {
bloon->addComponent<DamageEvent>(damage.value, shot);
if (bloonKind < MOAB and !bloon->getComponent<Goo>()) {
bloon->addComponent<Goo>(*shot->getComponent<Goo>());
- auto [texture,surface] = gameData.getTexture(getSurfaceName(bloon));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(bloon));
auto &visibility = *bloon->getComponent<Visibility>();
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.reloadTexture(texture, surface);
}
- shot->addComponent<RemoveEntityEvent>();
break;
}
case BOMB:
case ENHANCED_BOMB:
case MISSILE:
case MOAB_MAULER:
case MOAB_ASSASSIN:
case MOAB_ELIMINATOR:
case GOO_SHOT:
if (shot->getComponent<RemoveEntityEvent>())
break;
EntityP explosion(new Entity());
explosion->addComponent<Position>(position.value.X, position.value.Y);
explosion->addComponent<Type>(SHOT_T);
switch (kind.value) {
case BOMB:
case ENHANCED_BOMB:
case MISSILE:
case MOAB_MAULER:
case MOAB_ASSASSIN:
case MOAB_ELIMINATOR:
explosion->addComponent<Kind>(kind.value + 1);
if (auto shotGooP = shot->getComponent<Goo>())
explosion->addComponent<Goo>(*shotGooP);
break;
case GOO_SHOT:
explosion->addComponent<Kind>(GOO_SPLASH);
explosion->addComponent<Goo>(*shot->getComponent<Goo>());
break;
}
+ if (shot->getComponent<MoabClassAffecting>())
+ explosion->addComponent<MoabClassAffecting>();
explosion->addComponent<Pierce>(pierce);
explosion->addComponent<PoppedBloons>();
explosion->addComponent<Range>(shot->getComponent<Spread>()->value);
explosion->addComponents(damage);
- auto [texture,surface] = gameData.getTexture(getSurfaceName(explosion));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(explosion));
explosion->addComponent<Visibility>(texture, surface, SDL_Rect{0, 0, surface->w / 2});
layers[SHOTS_LAYER].emplace_back(explosion);
shot->addComponent<RemoveEntityEvent>();
break;
}
if (--pierce.value == 0)
shot->addComponent<RemoveEntityEvent>();
}
}
}
\ No newline at end of file
diff --git a/src/systems/EventSystem.cpp b/src/systems/EventSystem.cpp
index 7ddf37e..305894f 100644
--- a/src/systems/EventSystem.cpp
+++ b/src/systems/EventSystem.cpp
@@ -1,285 +1,320 @@
//
// Created by Ido Mozes on 08/07/2019.
//
#include "EventSystem.h"
void EventSystem::update(Entities *layers, GameData &gameData) {
SDL_PumpEvents();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT: {
gameData.isRunning = false;
break;
}
+ case SDL_KEYUP:
+ switch (event.key.keysym.sym){
+ case SDLK_SPACE:
+ if (gameData.levelRunning)
+ gameData.FPS = 240 - gameData.FPS;
+ else
+ gameData.levelRunning = true;
+ auto[texture, surface] = gameData.getTexture(
+ std::string("FastForward") +
+ (gameData.FPS == 180 ? "Enabled" : ""));
+ gameData.playButton->getComponent<Visibility>()->reloadTexture(texture, surface);
+ break;
+ }
+ break;
case SDL_MOUSEBUTTONDOWN: {
-
int mouseX, mouseY, originalMouseX;
SDL_GetMouseState(&mouseX, &mouseY);
mouseX = originalMouseX = int(mouseX / gameData.mapScale);
mouseY = int(mouseY / gameData.mapScale);
bool entityClicked = false;
Entities newEntities[N_LAYERS];
for (int i = N_LAYERS - 1; i >= 0; --i) {
for (auto &entity: layers[i]) {
- if (auto components = entity->getComponents<Action, Visibility>()) {
- auto[action, visibility] = components.value();
-
-
- int entityX, entityY, w, h;
- SDL_Rect *dstRect = visibility.getDstRect();
- entityX = dstRect->x;
- entityY = dstRect->y;
- w = dstRect->w;
- h = dstRect->h;
+ if (auto actionP = entity->getComponent<Action>()) {
+ auto &action = *actionP;
+ auto visibilityP = entity->getComponent<Visibility>();
auto buttonKindP = entity->getComponent<Kind>();
+ int entityX, entityY, w, h;
+ if (visibilityP) {
+ SDL_Rect *dstRect = visibilityP->getDstRect();
+ entityX = dstRect->x;
+ entityY = dstRect->y;
+ w = dstRect->w;
+ h = dstRect->h;
+ } else {
+ if (buttonKindP->value == SELL_TOWER and gameData.selected and !gameData.isDragging) {
+ entityX = 4;
+ entityY = 446;
+ w = 138;
+ h = 21;
+ } else continue;
+ }
+
if (action.actionType == CLICK and buttonKindP->value >= UPGRADE_PATH_1 and
buttonKindP->value <= UPGRADE_PATH_3) {
entityX = 4;
entityY = 63 + 135 * (buttonKindP->value - UPGRADE_PATH_1);
w = 138;
h = 81;
}
if (auto positionP = entity->getComponent<Position>()) {
auto &position = *positionP;
entityX = int(position.value.X - w / 2.0);
entityY = int(position.value.Y - h / 2.0);
mouseX = originalMouseX - SIDEBAR_WIDTH;
} else
mouseX = originalMouseX;
if (entityX <= mouseX and mouseX <= entityX + w and entityY <= mouseY and
mouseY <= entityY + h) {
if (action.disabled or (action.actionType != DROP and gameData.isDragging)) {
entityClicked = true;
continue;
}
switch (action.actionType) {
case DRAG: {
auto[type, kind, shotKind, range, attackInterval, pierce, damage, distance, cost, upgrades] =
entity->getComponents<Type, Kind, ShotKind, Range, AttackSpeed, Pierce, Damage, Distance, Cost, Upgrades>().value();
auto draggable = new Entity();
auto[texture, surface] = gameData.getTexture(getSurfaceName(entity));
draggable->addComponent<Visibility>(texture, surface, SDL_Rect{
originalMouseX - int(surface->w / 3.0), mouseY - int(surface->h / 3.0),
int(surface->w / 1.5), int(surface->h / 1.5)});
draggable->addComponent<Draggable>();
draggable->addComponent<Action>(DROP);
draggable->addComponents(type, kind, shotKind, range, attackInterval,
pierce, damage, distance, cost, upgrades);
draggable->addComponent<Strategy>(FIRST);
if (auto spreadP = entity->getComponent<Spread>())
draggable->addComponent<Spread>(*spreadP);
if (auto gooP = entity->getComponent<Goo>())
draggable->addComponent<Goo>(*gooP);
if (entity->getComponent<Camo>())
draggable->addComponent<Camo>();
if (auto shotsAmountP = entity->getComponent<ShotsAmount>())
draggable->addComponent<ShotsAmount>(*shotsAmountP);
EntityP ptr(draggable);
gameData.selected = ptr;
auto rangeShadow = new Entity();
rangeShadow->addComponent<RangeShadow>(ptr);
newEntities[SHADOW_LAYER].emplace_back(rangeShadow);
newEntities[MENU_LAYER].emplace_back(ptr);
gameData.isDragging = true;
goto entityClicked;
}
case CLICK: {
- if (entity->getComponent<Visibility>()->hidden)
+ if (visibilityP and visibilityP->hidden)
break;
switch (buttonKindP->value) {
case PLAY_FAST_FORWARD: {
if (gameData.levelRunning)
gameData.FPS = 240 - gameData.FPS;
else
gameData.levelRunning = true;
auto[texture, surface] = gameData.getTexture(
std::string("FastForward") +
(gameData.FPS == 180 ? "Enabled" : ""));
- visibility.reloadTexture(texture,surface);
+ entity->getComponent<Visibility>()->reloadTexture(texture, surface);
break;
}
case NEXT_STRATEGY: {
auto &strategy = gameData.selected->getComponent<Strategy>()->value;
strategy = (strategy + 1) % 4;
break;
}
case PREVIOUS_STRATEGY: {
auto &strategy = gameData.selected->getComponent<Strategy>()->value;
strategy -= 1;
if (strategy == -1)
strategy = 3;
break;
}
case UPGRADE_PATH_1:
case UPGRADE_PATH_2:
case UPGRADE_PATH_3: {
auto &upgradeP = *entity->getComponent<UpgradeP>();
auto &upgrades = *gameData.selected->getComponent<Upgrades>();
int cost = upgradeP.value->cost;
if (cost > gameData.cash or upgradeP.value->locked)
break;
else {
gameData.cash -= cost;
gameData.selected->getComponent<Cost>()->value += cost;
}
int currentPath = buttonKindP->value - UPGRADE_PATH_1;
if (upgrades.selectedPathUpgrades[currentPath] ==
upgrades.paths[currentPath].size()) {
upgrades.chosenPath = currentPath;
for (int j = 1; j <= 2; ++j) {
int nextPathIndex = (currentPath + j) % 3;
auto &nextPath = upgrades.paths[nextPathIndex];
if (upgrades.selectedPathUpgrades[nextPathIndex])
nextPath[nextPath.size() -
upgrades.selectedPathUpgrades[nextPathIndex]].locked = true;
}
}
for (auto[kind, value]:upgradeP.value->improvements) {
switch (kind) {
case RANGE_UPGRADE:
gameData.selected->getComponent<Range>()->value *= value;
break;
case SPREAD_UPGRADE:
gameData.selected->getComponent<Spread>()->value *= value;
break;
case PIERCE_UPGRADE:
gameData.selected->getComponent<Pierce>()->value += value;
break;
case DAMAGE_UPGRADE:
gameData.selected->getComponent<Damage>()->value += value;
break;
case DISTANCE_UPGRADE:
gameData.selected->getComponent<Distance>()->value *= value;
break;
case ATTACK_SPEED_UPGRADE: {
auto &attackSpeed = *gameData.selected->getComponent<AttackSpeed>();
attackSpeed.interval /= value;
attackSpeed.timeToRecharge = 0;
break;
}
case CAMO_UPGRADE:
gameData.selected->addComponent<Camo>();
break;
case ADD_GOO_UPGRADE: {
gameData.selected->addComponent<Goo>(value, 60, 60, true);
break;
}
case GOO_KIND_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.kind = value;
goo.ttl = goo.kind == GLUE ? 60 : 120;
break;
}
case GOO_DURATION_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.ttl = value;
break;
}
case CORROSIVE_INTERVAL_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.interval = value;
goo.timetoRecharge = value;
break;
}
case CORROSIVE_DAMAGE_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.damage = value;
break;
}
case GOO_SOAK_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.soak = true;
break;
}
case GOO_STICKINESS_UPGRADE: {
auto &goo = *gameData.selected->getComponent<Goo>();
goo.stickness = value;
break;
}
case SHOTS_AMOUNT_UPGRADE:
gameData.selected->addComponent<ShotsAmount>(value);
break;
case SHOT_KIND_UPGRADE:
gameData.selected->getComponent<ShotKind>()->value = value;
break;
case MOAB_CLASS_AFFECTING_UPGRADE:
gameData.selected->addComponent<MoabClassAffecting>();
break;
}
}
auto &path = gameData.selected->getComponent<Upgrades>()->paths[
buttonKindP->value - UPGRADE_PATH_1];
path.erase(path.begin());
break;
}
+ case SELL_TOWER: {
+ gameData.cash+=int(gameData.selected->getComponent<Cost>()->value * 0.75);
+ gameData.selected->addComponent<RemoveEntityEvent>();
+ auto [selectedX,selectedY] = gameData.selected->getComponent<Position>()->value;
+ for (int x = std::max(int(selectedX) - 20, 0);
+ x < std::min(int(selectedX) + 21, MAP_WIDTH); ++x) {
+ for (int y = std::max(int(selectedY) - 20, 0);
+ y < std::min(int(selectedY) + 21, MAP_HEIGHT); ++y) {
+ if (gameData.mapData[x][y] == TOWER)
+ gameData.mapData[x][y] = FREE;
+ }
+ }
+ gameData.selected.reset();
+ break;
+ }
}
goto entityClicked;
}
case DROP: {
auto &draggable = *entity->getComponent<Draggable>();
if (draggable.isPlaceable) {
entity->removeComponent<Draggable>();
entity->getComponent<Action>()->actionType = SELECT;
gameData.isDragging = false;
if (mouseX > SIDEBAR_WIDTH + MAP_WIDTH or mouseX < SIDEBAR_WIDTH) {
entity->addComponent<RemoveEntityEvent>();
gameData.selected.reset();
} else {
for (int x = std::max(mouseX - SIDEBAR_WIDTH - 20, 0);
x < std::min(mouseX - SIDEBAR_WIDTH + 21, MAP_WIDTH); ++x) {
for (int y = std::max(mouseY - 20, 0);
y < std::min(mouseY + 21, MAP_HEIGHT); ++y) {
if (gameData.mapData[x][y] == FREE)
gameData.mapData[x][y] = TOWER;
}
}
if (i == MENU_LAYER) {
gameData.cash -= entity->getComponent<Cost>()->value;
entity->addComponent<MoveEntityEvent>(TOWERS_LAYER);
auto &visibility = *entity->getComponent<Visibility>();
SDL_Rect *dstRect = entity->getComponent<Visibility>()->getDstRect();
entity->addComponent<Position>(
dstRect->x - SIDEBAR_WIDTH + dstRect->w / 2,
dstRect->y + dstRect->h / 2);
}
}
}
goto entityClicked;
}
case SELECT: {
if (entity == gameData.selected)
goto entityClicked;
gameData.selected = entity;
auto rangeShadow = new Entity();
rangeShadow->addComponent<RangeShadow>(entity);
newEntities[SHADOW_LAYER].emplace_back(rangeShadow);
goto entityClicked;
}
}
}
}
}
}
if (!entityClicked)
gameData.selected.reset();
entityClicked:
for (int i = 0; i < N_LAYERS; ++i) {
if (!newEntities[i].empty())
layers[i] += newEntities[i];
}
break;
}
- default:
- break;
}
}
}
diff --git a/src/systems/LoadLevelSystem.cpp b/src/systems/LoadLevelSystem.cpp
index feddc53..7ee7d90 100644
--- a/src/systems/LoadLevelSystem.cpp
+++ b/src/systems/LoadLevelSystem.cpp
@@ -1,29 +1,30 @@
//
// Created by Ido Mozes on 18/07/2019.
//
#include "LoadLevelSystem.h"
void LoadLevelSystem::update(Entities *layers, GameData &gameData) {
- if (gameData.levelReady or gameData.level > gameData.finalLevel)
+ if (gameData.levelReady or gameData.level > gameData.finalLevel or gameData.lives == 0)
return;
-
+ if (gameData.level != 1)
+ gameData.cash += 100 + gameData.level - 1;
std::string fileName = "../assets/Levels/level" + std::to_string(gameData.level) + ".data";
std::ifstream levelFile(fileName, std::ios::binary);
Sequence_S sequenceS;
while (levelFile.peek() != EOF) {
levelFile.read((char *) &sequenceS, 19);
auto s = new Entity();
s->addComponent<Type>(SEQUENCE_T);
s->addComponent<Sequence>(sequenceS.amount, sequenceS.bps, sequenceS.delay);
s->addComponent<Kind>(sequenceS.kind);
if (sequenceS.regrow)
s->addComponent<Regrow>(sequenceS.kind);
if (sequenceS.camo)
s->addComponent<Camo>();
if (sequenceS.fortified)
s->addComponent<Fortified>();
layers[SEQUENCES_LAYER].emplace_back(s);
}
gameData.levelReady = true;
}
diff --git a/src/systems/MovementSystem.cpp b/src/systems/MovementSystem.cpp
index 220be71..52b53fb 100644
--- a/src/systems/MovementSystem.cpp
+++ b/src/systems/MovementSystem.cpp
@@ -1,168 +1,167 @@
//
// Created by Ido Mozes on 02/07/2019.
//
#include "MovementSystem.h"
enum Directions {
RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP, TOP_RIGHT
};
std::tuple<int, int> getDelta(int direction) {
switch (direction) {
case RIGHT:
return std::make_tuple(1, 0);
case BOTTOM_RIGHT:
return std::make_tuple(1, 1);
case BOTTOM:
return std::make_tuple(0, 1);
case BOTTOM_LEFT:
return std::make_tuple(-1, 1);
case LEFT:
return std::make_tuple(-1, 0);
case TOP_LEFT:
return std::make_tuple(-1, -1);
case TOP:
return std::make_tuple(0, -1);
case TOP_RIGHT:
return std::make_tuple(1, -1);
default:
return std::make_tuple(0, 0);
}
}
void MovementSystem::update(Entities *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
for (auto &entity: layers[i]) {
// Move all entities with Visibility and Position
if (auto components = entity->getComponents<Visibility, Position>()) {
auto[visibility, position]=components.value();
float deltaX = 0, deltaY = 0;
auto pathIndexP = entity->getComponent<PathIndex>();
if (auto velocityP = entity->getComponent<Velocity>()) {
auto &velocity = *velocityP;
if (auto accelerationP = entity->getComponent<Acceleration>()) {
auto &acceleration = *accelerationP;
velocity.changeVelocity(acceleration.value.X, acceleration.value.Y);
}
deltaX = velocity.value.X;
deltaY = velocity.value.Y;
if (auto distanceP = entity->getComponent<Distance>()) {
auto &distance = distanceP->value;
auto[alpha, R] = cartesianToPolar(deltaX, deltaY);
if (distance == 0) {
entity->addComponent<RemoveEntityEvent>();
} else if (distance >= R) {
distance -= R;
} else {
std::tie(deltaX, deltaY) = polarToCartesian(alpha, distance);
distance = 0;
}
}
} else if (pathIndexP) {
if (auto regrowP = entity->getComponent<Regrow>()) {
auto[lives, kind] = entity->getComponents<Lives, Kind>().value();
regrowP->regrowTime -= 1;
if (regrowP->regrowTime == 0 and kind.value < regrowP->kind) {
switch (kind.value) {
case RED_BLOON:
case BLUE_BLOON:
case GREEN_BLOON:
case YELLOW_BLOON:
kind.value += 1;
break;
case PINK_BLOON: {
switch (regrowP->kind) {
case PURPLE_BLOON:
case BLACK_BLOON:
case WHITE_BLOON:
kind.value = regrowP->kind;
break;
case LEAD_BLOON:
case ZEBRA_BLOON:
case RAINBOW_BLOON:
case CERAMIC_BLOON:
kind.value = BLACK_BLOON;
break;
}
break;
}
case BLACK_BLOON:
if (regrowP->kind == LEAD_BLOON)
kind.value = LEAD_BLOON;
else
kind.value = ZEBRA_BLOON;
break;
case WHITE_BLOON:
kind.value = ZEBRA_BLOON;
case ZEBRA_BLOON:
case RAINBOW_BLOON:
kind.value += 1;
break;
}
lives.value = getBloonProperty<TOTAL_LIVES>(entity);
- auto[texture,surface] = gameData.getTexture(getSurfaceName(entity));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(entity));
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.reloadTexture(texture, surface);
entity->getComponent<Range>()->value = std::max(surface->w / 6, surface->h / 6);
regrowP->regrowTime = 60;
}
}
auto &pathIndex = *pathIndexP;
float speed = getSpeed(entity);
if (auto gooP = entity->getComponent<Goo>()) {
- if (gooP->kind == CORROSIVE and --gooP->timetoRecharge == 0){
+ if (gooP->kind == CORROSIVE and --gooP->timetoRecharge == 0) {
entity->addComponent<DamageEvent>(gooP->damage, EntityP(nullptr));
gooP->timetoRecharge = gooP->interval;
}
gooP->ttl -= 1;
- if (gooP->ttl == 0) {
+ if (gooP->ttl == 0 and !entity->getComponent<RemoveEntityEvent>()) {
entity->removeComponent<Goo>();
- auto[texture,surface] = gameData.getTexture(getSurfaceName(entity));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(entity));
auto &visibility = *entity->getComponent<Visibility>();
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.reloadTexture(texture, surface);
}
}
pathIndex.progress += speed;
float deltaIndex = pathIndex.progress - pathIndex.index;
if (entity->getComponent<Kind>()->value >= MOAB) {
float angle = 0;
Point tempPosition = position.value;
for (int j = std::max(0, pathIndex.index - 10);
j < std::min(pathIndex.index + 10, int(gameData.path.size() - 1)); ++j) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[j]);
tempPosition.X += tempDeltaX;
tempPosition.Y += tempDeltaY;
}
angle = radToDeg(twoPointsAngle(position.value, tempPosition));
visibility.angle = angle;
}
while (deltaIndex >= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2))) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[pathIndex.index]);
deltaX += tempDeltaX;
deltaY += tempDeltaY;
deltaIndex -= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2));
if (pathIndex.index < gameData.path.size() - 1)
pathIndex.index++;
}
}
position.changePosition(deltaX, deltaY);
if (position.value.X > MAP_WIDTH or position.value.X < 0 or position.value.Y > MAP_HEIGHT or
position.value.Y < 0) {
if (pathIndexP) {
gameData.lives -= entity->getComponent<Lives>()->value;
if (gameData.lives <= 0) {
- gameData.lost = true;
gameData.lives = 0;
}
}
entity->addComponent<RemoveEntityEvent>();
}
}
}
}
}
diff --git a/src/systems/RemoveEntitiesSystem.cpp b/src/systems/RemoveEntitiesSystem.cpp
index 2ec251a..70a9c30 100644
--- a/src/systems/RemoveEntitiesSystem.cpp
+++ b/src/systems/RemoveEntitiesSystem.cpp
@@ -1,30 +1,30 @@
//
// Created by Ido Mozes on 10/07/2019.
//
#include "RemoveEntitiesSystem.h"
void RemoveEntitiesSystem::update(Entities *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
layers[i].erase(
std::remove_if(layers[i].begin(), layers[i].end(), [&layers, &gameData](auto &entity) {
if (auto moveEntityEventP = entity->template getComponent<MoveEntityEvent>()) {
int newLayer = moveEntityEventP->toLayer;
entity->template removeComponent<MoveEntityEvent>();
layers[newLayer].emplace_back(std::move(entity));
}
if (entity and entity->template getComponent<RangeShadow>() and
entity->template getComponent<RangeShadow>()->entity != gameData.selected)
return true;
return !entity or entity->template getComponent<RemoveEntityEvent>();
}),
layers[i].end());
}
if (layers[SEQUENCES_LAYER].empty() and layers[BLOONS_LAYER].empty()) {
gameData.levelReady = gameData.levelRunning = false;
- if (gameData.level <= gameData.finalLevel)
+ if (gameData.level <= gameData.finalLevel and gameData.lives>0)
gameData.level += 1;
auto [texture,surface] = gameData.getTexture("Play");
gameData.playButton->getComponent<Visibility>()->reloadTexture(texture,surface);
}
}
\ No newline at end of file
diff --git a/src/systems/RenderSystem.cpp b/src/systems/RenderSystem.cpp
index 861095d..8fb0b21 100644
--- a/src/systems/RenderSystem.cpp
+++ b/src/systems/RenderSystem.cpp
@@ -1,162 +1,181 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "RenderSystem.h"
std::string formatCommas(int num) {
std::string numWithCommas = std::to_string(num);
int insertPosition = numWithCommas.length() - 3;
while (insertPosition > 0) {
numWithCommas.insert(insertPosition, ",");
insertPosition -= 3;
}
return numWithCommas;
}
void RenderSystem::init(GameData &gameData) {
if (gameData.window != nullptr)
SDL_DestroyWindow(gameData.window);
if (gameData.renderer != nullptr)
SDL_DestroyRenderer(gameData.renderer);
gameData.window = SDL_CreateWindow("BloonsTD", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, /* NOLINT(hicpp-signed-bitwise)*/
int((MAP_WIDTH + SIDEBAR_WIDTH + MENU_WIDTH) * gameData.mapScale),
int(MAP_HEIGHT * gameData.mapScale),
gameData.fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
gameData.renderer = SDL_CreateRenderer(gameData.window, -1, 0);
SDL_SetRenderDrawColor(gameData.renderer, 255, 255, 255, 255);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
gameData.fonts[WHITE12] = FC_CreateFont();
FC_LoadFont(gameData.fonts[WHITE12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL);
gameData.fonts[BLACK12] = FC_CreateFont();
FC_LoadFont(gameData.fonts[BLACK12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
gameData.fonts[RED12] = FC_CreateFont();
FC_LoadFont(gameData.fonts[RED12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
FC_MakeColor(255, 49, 49, 255), TTF_STYLE_NORMAL);
gameData.fonts[WHITE8] = FC_CreateFont();
FC_LoadFont(gameData.fonts[WHITE8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL);
gameData.fonts[BLACK8] = FC_CreateFont();
FC_LoadFont(gameData.fonts[BLACK8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
gameData.fonts[RED8] = FC_CreateFont();
FC_LoadFont(gameData.fonts[RED8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
FC_MakeColor(255, 49, 49, 255), TTF_STYLE_NORMAL);
}
void RenderSystem::update(Entities *layers, GameData &gameData) {
SDL_RenderClear(gameData.renderer);
for (int i = 0; i < N_LAYERS; ++i) {
if (i == SEQUENCES_LAYER)
continue;
for (auto &entity: layers[i]) {
if (i == MENU_LAYER) {
auto[visibilityP, kindP, actionP] = entity->getComponentsP<Visibility, Kind, Action>();
if (visibilityP and visibilityP->hidden)
continue;
- if (kindP and actionP and actionP->actionType == CLICK)
- switch (kindP->value) {
- case UPGRADE_PATH_1:
- case UPGRADE_PATH_2:
- case UPGRADE_PATH_3: {
- auto &upgradeP = *entity->getComponent<UpgradeP>();
- int path = kindP->value - UPGRADE_PATH_1;
- int cost = upgradeP.value->cost;
- FC_Draw(gameData.fonts[WHITE12], gameData.renderer, 24 * gameData.mapScale,
- (67 + path * 135) * gameData.mapScale, upgradeP.value->name.c_str());
+ if (kindP and actionP)
+ switch (actionP->actionType) {
+ case CLICK:
+ switch (kindP->value) {
+ case UPGRADE_PATH_1:
+ case UPGRADE_PATH_2:
+ case UPGRADE_PATH_3: {
+ auto &upgradeP = *entity->getComponent<UpgradeP>();
+ int path = kindP->value - UPGRADE_PATH_1;
+ int cost = upgradeP.value->cost;
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, 25 * gameData.mapScale,
+ (68 + path * 135) * gameData.mapScale, upgradeP.value->name.c_str());
+ int font = WHITE8;
+ if (cost > gameData.cash)
+ font = RED8;
+ FC_Draw(gameData.fonts[font], gameData.renderer, 47 * gameData.mapScale,
+ (81 + path * 135) * gameData.mapScale, "$%s", formatCommas(cost).c_str());
+ break;
+ }
+ }
+ break;
+ case DRAG: {
+ int cost = entity->getComponent<Cost>()->value;
int font = WHITE8;
if (cost > gameData.cash)
font = RED8;
- FC_Draw(gameData.fonts[font], gameData.renderer, 47 * gameData.mapScale,
- (80 + path * 135) * gameData.mapScale, "$%s", formatCommas(cost).c_str());
- break;
+ FC_Draw(gameData.fonts[font], gameData.renderer,
+ (visibilityP->getDstRect()->x +(visibilityP->getDstRect()->w/2)- 50) * gameData.mapScale,
+ (visibilityP->getDstRect()->y+(visibilityP->getDstRect()->h/2) ) * gameData.mapScale, "$%s",
+ formatCommas(cost).c_str());
}
+ break;
}
}
auto rangeShadowP = entity->getComponent<RangeShadow>();
auto &currentEntity = rangeShadowP ? rangeShadowP->entity : entity;
if (auto visibilityP = currentEntity->getComponent<Visibility>()) {
auto &visibility = *visibilityP;
SDL_Rect *dstRect = visibility.getDstRect();
SDL_Rect newDstRect = {int(dstRect->x * gameData.mapScale), int(dstRect->y * gameData.mapScale),
int(dstRect->w * gameData.mapScale), int(dstRect->h * gameData.mapScale)};
SDL_Point entityCenter;
auto positionP = currentEntity->getComponent<Position>();
if (positionP) {
auto &position = *positionP;
entityCenter.x = (position.value.X + SIDEBAR_WIDTH) * gameData.mapScale;
entityCenter.y = position.value.Y * gameData.mapScale;
newDstRect.x = int((position.value.X + SIDEBAR_WIDTH) * gameData.mapScale - newDstRect.w / 2.0);
newDstRect.y = int(position.value.Y * gameData.mapScale - newDstRect.h / 2.0);
} else {
entityCenter.x = int(dstRect->x * gameData.mapScale + (dstRect->w * gameData.mapScale) / 2.0);
entityCenter.y = int(dstRect->y * gameData.mapScale + (dstRect->h * gameData.mapScale) / 2.0);
}
if (currentEntity != entity) {
auto draggableP = currentEntity->getComponent<Draggable>();
bool isRed = draggableP ? !draggableP->isPlaceable : false;
float range = currentEntity->getComponent<Range>()->value;
filledCircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range * gameData.mapScale,
isRed ? 255 : 0, 0, 0,
100);
aacircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range * gameData.mapScale,
isRed ? 255 : 0, 0, 0, 150);
}
if (entity == currentEntity) {
SDL_RenderCopyEx(gameData.renderer, visibility.getTexture(), nullptr, &newDstRect, visibility.angle,
nullptr, SDL_FLIP_NONE);
}
}
}
}
if (gameData.cash > 99999999)
gameData.cash = 99999999;
FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 45) * gameData.mapScale,
14 * gameData.mapScale,
formatCommas(gameData.cash).c_str());
FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 45) * gameData.mapScale,
36 * gameData.mapScale,
std::to_string(gameData.lives).c_str());
FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 160) * gameData.mapScale,
22 * gameData.mapScale,
"Level: %s", std::to_string(gameData.level).c_str());
if (gameData.selected and !gameData.isDragging) {
std::string strategy;
int x;
switch (gameData.selected->getComponent<Strategy>()->value) {
case CLOSEST:
strategy = "Close";
x = 56;
break;
case FIRST:
strategy = "First";
x = 57;
break;
case LAST:
strategy = "Last";
x = 59;
break;
case STRONGEST:
strategy = "Strong";
x = 51;
break;
}
FC_Draw(gameData.fonts[WHITE12], gameData.renderer, x * gameData.mapScale, 16 * gameData.mapScale,
strategy.c_str());
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, 25 * gameData.mapScale, 451 * gameData.mapScale,
+ "Sell for:");
+ FC_Draw(gameData.fonts[WHITE8], gameData.renderer, 25 * gameData.mapScale, 463 * gameData.mapScale,
+ "$%s", formatCommas(int(gameData.selected->getComponent<Cost>()->value * 0.75)).c_str());
}
SDL_RenderPresent(gameData.renderer);
}
diff --git a/src/systems/ShotsSpawnSystem.cpp b/src/systems/ShotsSpawnSystem.cpp
index 5c11cff..c6943ba 100644
--- a/src/systems/ShotsSpawnSystem.cpp
+++ b/src/systems/ShotsSpawnSystem.cpp
@@ -1,201 +1,203 @@
//
// Created by Ido Mozes on 11/07/2019.
//
#include "ShotsSpawnSystem.h"
void ShotsSpawnSystem::update(Entities *layers, GameData &gameData) {
if (!gameData.levelRunning)
return;
for (auto &entity: layers[TOWERS_LAYER]) {
auto[kind, shotKind, towerRange, towerPosition, strategy, attackSpeed, pierce, damage, distance, visibility] =
entity->getComponents<Kind, ShotKind, Range, Position, Strategy, AttackSpeed, Pierce, Damage, Distance, Visibility>().value();
auto camoP = entity->getComponent<Camo>();
float minDistance = MAP_HEIGHT + MAP_WIDTH;
float minProgress = gameData.path.size();
float maxProgress = -1;
float maxLives = -1;
Entity *closestBloon = nullptr, *firstBloon = nullptr, *lastBloon = nullptr, *strongestBloon = nullptr;
for (auto &gameEntity: layers[BLOONS_LAYER]) {
if (gameEntity->getComponent<Camo>() and !camoP)
continue;
float distance;
if (gameEntity->getComponent<Type>()->value == BLOON_T) {
auto[bloonRange, bloonPosition, pathIndex, lives] = gameEntity->getComponents<Range, Position, PathIndex, Lives>().value();
distance =
twoPointsDistance(bloonPosition.value, towerPosition.value) - bloonRange.value;
if (distance > towerRange.value)
continue;
if (distance < minDistance) {
minDistance = distance;
closestBloon = gameEntity.get();
}
if (pathIndex.progress < minProgress) {
minProgress = pathIndex.progress;
lastBloon = gameEntity.get();
}
if (lives.value > maxLives or (lives.value == maxLives and pathIndex.progress > maxProgress)) {
maxLives = lives.value;
strongestBloon = gameEntity.get();
}
if (pathIndex.progress > maxProgress) {
maxProgress = pathIndex.progress;
firstBloon = gameEntity.get();
}
}
}
if (closestBloon or firstBloon or lastBloon or strongestBloon) {
Entity *target;
switch (strategy.value) {
case CLOSEST:
target = closestBloon;
break;
case FIRST:
target = firstBloon;
break;
case LAST:
target = lastBloon;
break;
case STRONGEST:
target = strongestBloon;
break;
}
int amount = attackSpeed.getAmountReady();
float angle = twoPointsAngle(towerPosition.value, target->getComponent<Position>()->value);
for (int i = 0; i < amount; ++i) {
switch (shotKind.value) {
case BOMB:
case ENHANCED_BOMB:
case MISSILE:
case MOAB_MAULER:
case MOAB_ASSASSIN:
case MOAB_ELIMINATOR:
case GOO_SHOT:
case LASER:
case PLASMA:
case SUN:
case SPIKE:
case JUGGERNAUT:
case DART: {
int shotsAmount = 1;
auto shotsAmountP = entity->getComponent<ShotsAmount>();
float shotAngle = angle;
float deltaAngle = 10;
if (shotsAmountP) {
shotsAmount = shotsAmountP->value;
shotAngle -= degToRad((deltaAngle * (shotsAmount - 1)) / 2.0);
deltaAngle = degToRad(deltaAngle);
}
for (int j = 0; j < shotsAmount; ++j) {
EntityP shot(new Entity());
shot->addComponent<Position>(towerPosition.value.X, towerPosition.value.Y);
shot->addComponent<Type>(SHOT_T);
shot->addComponent<Kind>(shotKind.value);
auto[velocityX, velocityY] = polarToCartesian(shotAngle, getSpeed(shot));
shot->addComponent<Velocity>(velocityX, velocityY);
shot->addComponent<Range>(5);
- if (auto spreadP = entity->getComponent<Spread>()) {
+ if (auto spreadP = entity->getComponent<Spread>())
shot->addComponent<Spread>(*spreadP);
- if (auto gooP = entity->getComponent<Goo>())
- shot->addComponent<Goo>(*gooP);
- }
+ if (auto gooP = entity->getComponent<Goo>())
+ shot->addComponent<Goo>(*gooP);
+
+ if (entity->getComponent<MoabClassAffecting>())
+ shot->addComponent<MoabClassAffecting>();
shot->addComponents(pierce, damage, distance);
if (camoP)
shot->addComponent<Camo>();
shot->addComponent<PoppedBloons>();
- auto[texture,surface] = gameData.getTexture(getSurfaceName(shot));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(shot));
float scale = 1;
switch (shotKind.value) {
case MISSILE:
scale = 1.5;
break;
case DART:
case MOAB_MAULER:
case MOAB_ASSASSIN:
case MOAB_ELIMINATOR:
scale = 2;
break;
case BOMB:
case ENHANCED_BOMB:
case SPIKE:
case JUGGERNAUT:
scale = 4;
break;
case LASER:
scale = 10;
break;
case PLASMA:
case SUN:
scale = 6;
break;
}
shot->addComponent<Visibility>(texture, surface,
SDL_Rect{0, 0, int(surface->w / scale)},
radToDeg(shotAngle));
layers[SHOTS_LAYER].emplace_back(shot);
shotAngle += deltaAngle;
}
break;
}
case BULLET:
case ENHANCED_BULLET: {
if (shotKind.value == BULLET and target->getComponent<Kind>()->value == LEAD_BLOON)
break;
EntityP shot(new Entity());
shot->addComponent<PoppedBloons>();
shot->addComponent<Kind>(shotKind.value);
target->addComponent<DamageEvent>(
target->getComponent<Camo>() ? damage.value * 2 : damage.value, shot);
break;
}
case TACK:
case HOT_TACK:
case BLADE:
case ENHANCED_TACK: {
int shotsAmount = entity->getComponent<ShotsAmount>()->value;
for (int j = 0; j < shotsAmount; ++j) {
EntityP shot(new Entity());
shot->addComponent<Type>(SHOT_T);
shot->addComponent<Kind>(shotKind.value);
angle = (2 * M_PI / shotsAmount) * j;
auto[velocityX, velocityY] = polarToCartesian(angle, getSpeed(shot));
shot->addComponent<Velocity>(velocityX, velocityY);
auto[deltaX, deltaY] = polarToCartesian(angle, 10);
shot->addComponent<Position>(towerPosition.value.X + deltaX,
towerPosition.value.Y + deltaY);
shot->addComponent<Range>(5);
shot->addComponents(pierce, damage, distance);
shot->addComponent<PoppedBloons>();
- auto [texture,surface] = gameData.getTexture(getSurfaceName(shot));
+ auto[texture, surface] = gameData.getTexture(getSurfaceName(shot));
int scale = 1;
switch (shotKind.value) {
case TACK:
case HOT_TACK:
case ENHANCED_TACK:
scale = 5;
break;
case BLADE:
scale = 4;
break;
}
shot->addComponent<Visibility>(texture, surface,
SDL_Rect{0, 0, surface->w / scale},
radToDeg(angle));
layers[SHOTS_LAYER].emplace_back(shot);
}
break;
}
}
}
if (shotKind.value != TACK and shotKind.value != ENHANCED_TACK and shotKind.value != BLADE and
shotKind.value != HOT_TACK)
visibility.angle = radToDeg(angle) + 90;
} else
attackSpeed.recharge();
}
}
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Fri, Sep 12, 2:08 AM (22 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42798
Default Alt Text
(113 KB)

Event Timeline