Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F86272
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
113 KB
Referenced Files
None
Subscribers
None
View Options
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 ¤tEntity = 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
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Sep 12, 2:08 AM (17 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42798
Default Alt Text
(113 KB)
Attached To
Mode
R74 BloonsTD
Attached
Detach File
Event Timeline
Log In to Comment