Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F86372
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
96 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 650a5b5..a1ede31 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.14)
project(SDL_Game)
set(CMAKE_CXX_STANDARD 17)
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)
+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/src/Component.h b/src/Component.h
index 5ae0038..d44c79a 100644
--- a/src/Component.h
+++ b/src/Component.h
@@ -1,65 +1,66 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#ifndef SDL2_GAME_COMPONENT_H
#define SDL2_GAME_COMPONENT_H
#include <memory>
struct Point {
float X;
float Y;
};
class Entity;
typedef std::shared_ptr<Entity> EntityP;
enum ComponentType {
VISIBILITY,
POSITION,
VELOCITY,
SPEED,
ATTACK_SPEED,
ACCELERATION,
PATH_INDEX,
LIVES,
KIND,
SHOT_KIND,
TYPE,
RANGE,
DAMAGE,
PIERCE,
SPREAD,
SHOTS_AMOUNT,
DISTANCE,
SEQUENCE,
ACTION,
DRAGGABLE,
RANGE_SHADOW,
STRATEGY,
REGROW,
CAMO,
FORTIFIED,
GOO,
COST,
POPPED_BLOONS,
UPGRADES,
UPGRADE_P,
+ SEEN,
MOVE_ENTITY_EVENT,
REMOVE_ENTITY_EVENT,
DAMAGE_EVENT,
LENGTH
};
class Component {
public:
Component() = default;
virtual ~Component() = default;
};
#endif //SDL2_GAME_COMPONENT_H
diff --git a/src/Game.cpp b/src/Game.cpp
index 1dcc1b5..4a6ac9a 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,444 +1,498 @@
//
// 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();
SDL_Surface *surface = gameData.assets[surfaceName];
spriteEntity->addComponent<Visibility>(gameData.renderer, 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());
SDL_Surface *surface = gameData.assets["Play"];
Point position{SIDEBAR_WIDTH + MAP_WIDTH + 67, 480};
button->addComponent<Visibility>(gameData.renderer, 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();
surface = gameData.assets["RightArrow"];
position = {116, 21};
nextStrategyButton->addComponent<Visibility>(gameData.renderer, 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();
surface = gameData.assets["LeftArrow"];
position = {32, 21};
previousStrategyButton->addComponent<Visibility>(gameData.renderer, 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();
surface = gameData.assets["LeftArrow"];
upgradePath->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, 0});
upgradePath->addComponent<Action>(CLICK);
upgradePath->addComponent<Kind>(UPGRADE_PATH_1 + j);
layers[MENU_LAYER].emplace_back(upgradePath);
}
// Towers
auto tower = new Entity();
tower->addComponent<Kind>(SUPER_MONKEY);
tower->addComponent<ShotKind>(DART);
surface = gameData.assets["SuperMonkey"];
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 85};
tower->addComponent<Visibility>(gameData.renderer, 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>(15);
+ 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>(3000);
+ tower->addComponent<Cost>(2975);
auto &superMonkeyUpgrades = tower->addComponent<Upgrades>();
std::string image = "SuperMonkey_LaserBlasts";
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Laser Blasts", 2295, image, gameData.assets[image], {{PIERCE_UPGRADE, 1},
{SHOT_KIND_UPGRADE, LASER}}));
image = "SuperMonkey_PlasmaVision";
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Plasma Blasts", 4250, image, gameData.assets[image], {{PIERCE_UPGRADE, 1},
{ATTACK_SPEED_UPGRADE, 2},
{SHOT_KIND_UPGRADE, PLASMA}}));
image = "SuperMonkey_SunAvatar";
superMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Sun Avatar", 17850, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 3},
{PIERCE_UPGRADE, 3},
{SHOT_KIND_UPGRADE, SUN}}));
image = "SuperMonkey_SuperRange";
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Super Range", 850, image, gameData.assets[image],
{{RANGE_UPGRADE, 1.25},
{DISTANCE_UPGRADE, 1.25}}));
image = "SuperMonkey_EpicRange";
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Epic Range", 1190, image, gameData.assets[image], {{RANGE_UPGRADE, 1.25},
{DISTANCE_UPGRADE, 1.25},
{PIERCE_UPGRADE, 1}}));
image = "SuperMonkey_RoboMonkey";
superMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Robo Monkey", 7650, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 2},
{DAMAGE_UPGRADE, 4}}));
image = "SuperMonkey_Ultravision";
superMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Ultravision", 1020, image, gameData.assets[image], {{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);
surface = gameData.assets["SniperMonkey"];
tower->addComponent<Visibility>(gameData.renderer, 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>(1);
+ tower->addComponent<AttackSpeed>(0.45);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
tower->addComponent<Distance>(500);
- tower->addComponent<Cost>(200);
+ tower->addComponent<Cost>(300);
tower->addComponent<Type>(TOWER_T);
auto &sniperMonkeyUpgrades = tower->addComponent<Upgrades>();
image = "SniperMonkey_FullMetalJacket";
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Full Metal Jacket", 295, image, gameData.assets[image], {{DAMAGE_UPGRADE, 3},
{SHOT_KIND_UPGRADE, ENHANCED_BULLET}}));
image = "SniperMonkey_LargeCalibre";
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Large Calibre", 1275, image, gameData.assets[image], {{DAMAGE_UPGRADE, 3}}));
image = "SniperMonkey_DeadlyPrecision";
sniperMonkeyUpgrades.paths[0].emplace_back(
Upgrade("Deadly Precision", 2250, image, gameData.assets[image], {{DAMAGE_UPGRADE, 11}}));
image = "SniperMonkey_NightVisionGoggles";
sniperMonkeyUpgrades.paths[1].emplace_back(
Upgrade("Night Vision Goggles", 225, image, gameData.assets[image], {{CAMO_UPGRADE, 0}}));
image = "SniperMonkey_FasterFiring";
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Faster Firing", 340, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.54}}));
image = "SniperMonkey_EvenFasterFiring";
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Even Faster Firing", 340, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.66}}));
image = "SniperMonkey_Semi-Automatic";
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Semi-Automatic", 2975, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 3}}));
image = "SniperMonkey_FullAutoRifle";
sniperMonkeyUpgrades.paths[2].emplace_back(
Upgrade("Full Auto Rifle", 4035, image, gameData.assets[image], {{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);
surface = gameData.assets["DartMonkey"];
tower->addComponent<Visibility>(gameData.renderer, 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>(80);
+ tower->addComponent<Cost>(170);
tower->addComponent<Type>(TOWER_T);
auto &upgrades = tower->addComponent<Upgrades>();
image = "DartMonkey_SharpShots";
upgrades.paths[0].emplace_back(
Upgrade("Sharp Shots", 120, image, gameData.assets[image], {{PIERCE_UPGRADE, 1}}));
image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
upgrades.paths[0].emplace_back(
Upgrade("Razor Sharp Shots", 185, image, gameData.assets[image], {{PIERCE_UPGRADE, 2}}));
image = "DartMonkey_Spike-O-Pult";
upgrades.paths[0].emplace_back(
Upgrade("Spike-O-Pult", 225, image, gameData.assets[image], {{DISTANCE_UPGRADE, 6},
{ATTACK_SPEED_UPGRADE, 0.76},
{SHOT_KIND_UPGRADE, SPIKE},
{PIERCE_UPGRADE, 14}}));
image = "DartMonkey_Juggernaut";
upgrades.paths[0].emplace_back(
Upgrade("Juggernaut", 1530, image, gameData.assets[image],
{{RANGE_UPGRADE, 1.25},
{ATTACK_SPEED_UPGRADE, 1.25},
{SHOT_KIND_UPGRADE, JUGGERNAUT},
{PIERCE_UPGRADE, 86}}));
image = "DartMonkey_LongRangeDarts_QuickShots";
upgrades.paths[1].emplace_back(
Upgrade("Quick Shots", 85, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.15}}));
image = "DartMonkey_VeryQuickShots";
upgrades.paths[1].emplace_back(
Upgrade("Very Quick Shots", 160, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.33}}));
image = "DartMonkey_TripleDarts";
upgrades.paths[1].emplace_back(
Upgrade("Triple Shot", 340, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 3}}));
image = "DartMonkey_LongRangeDarts_QuickShots";
upgrades.paths[2].emplace_back(
Upgrade("Long Range Darts", 75, image, gameData.assets[image], {{DISTANCE_UPGRADE, 1.25},
{RANGE_UPGRADE, 1.25}}));
image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
upgrades.paths[2].emplace_back(
Upgrade("Enhanced Eyesight", 170, image, gameData.assets[image], {{DISTANCE_UPGRADE, 1.2},
{RANGE_UPGRADE, 1.2},
{CAMO_UPGRADE, 0}}));
image = "DartMonkey_Crossbow";
upgrades.paths[2].emplace_back(
Upgrade("Crossbow", 530, image, gameData.assets[image], {{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);
surface = gameData.assets["BombTower"];
tower->addComponent<Visibility>(gameData.renderer, 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>(70);
+ tower->addComponent<Range>(100);
tower->addComponent<AttackSpeed>(2);
tower->addComponent<Pierce>(1);
- tower->addComponent<Damage>(2);
- tower->addComponent<Distance>(500);
- tower->addComponent<Cost>(300);
+ tower->addComponent<Damage>(1);
+ tower->addComponent<Distance>(120);
+ tower->addComponent<Cost>(555);
tower->addComponent<Spread>(30);
tower->addComponent<Type>(TOWER_T);
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);
surface = gameData.assets["GlueGunner"];
tower->addComponent<Visibility>(gameData.renderer, 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.8);
+ tower->addComponent<Range>(150);
+ tower->addComponent<AttackSpeed>(0.97);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(0);
- tower->addComponent<Distance>(150);
- tower->addComponent<Cost>(250);
+ 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";
upgrades2.paths[0].emplace_back(
Upgrade("Glue Soak", 170, image, gameData.assets[image], {{GOO_SOAK_UPGRADE, 0}}));
image = "GlueGunner_CorrosiveGlue";
upgrades2.paths[0].emplace_back(
Upgrade("Corrosive Glue", 225, image, gameData.assets[image], {{GOO_KIND_UPGRADE, CORROSIVE}}));
image = "GlueGunner_BloonDissolver";
upgrades2.paths[0].emplace_back(
Upgrade("Bloon Dissolver", 2805, image, gameData.assets[image], {{DAMAGE_UPGRADE, 1},
{CORROSIVE_INTERVAL_UPGRADE, 60}}));
image = "GlueGunner_BloonLiquefier";
upgrades2.paths[0].emplace_back(
Upgrade("Bloon Liquefier", 5950, image, gameData.assets[image],
{{CORROSIVE_INTERVAL_UPGRADE, 60 / 10.0}}));
image = "GlueGunner_GlueSoak_BiggerGlobs";
upgrades2.paths[1].emplace_back(
Upgrade("Bigger Globs", 85, image, gameData.assets[image], {{PIERCE_UPGRADE, 1}}));
image = "GlueGunner_GlueSplatter";
upgrades2.paths[1].emplace_back(
Upgrade("Glue Splatter", 1530, image, gameData.assets[image], {{PIERCE_UPGRADE, 4}}));
image = "GlueGunner_GlueHose";
upgrades2.paths[1].emplace_back(
Upgrade("Glue Hose", 2760, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 3}}));
image = "GlueGunner_StickierGlue";
upgrades2.paths[2].emplace_back(
Upgrade("Stickier Glue", 100, image, gameData.assets[image], {{GOO_DURATION_UPGRADE, 240}}));
image = "GlueGunner_StrongerGlue";
upgrades2.paths[2].emplace_back(
Upgrade("Stronger Glue", 340, image, gameData.assets[image], {{GOO_STICKNESS_UPGRADE, 0.3}}));
image = "GlueGunner_SuperGlue";
upgrades2.paths[2].emplace_back(
Upgrade("Super Glue", 2720, image, gameData.assets[image], {{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>(RADIAL_DART);
+ tower->addComponent<ShotKind>(TACK);
surface = gameData.assets["TackShooter"];
tower->addComponent<Visibility>(gameData.renderer, 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>(65);
- tower->addComponent<AttackSpeed>(2);
+ tower->addComponent<Range>(90);
+ tower->addComponent<AttackSpeed>(0.6);
+ tower->addComponent<ShotsAmount>(8);
tower->addComponent<Pierce>(1);
- tower->addComponent<Damage>(2);
- tower->addComponent<Distance>(80);
- tower->addComponent<Cost>(150);
+ 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";
+ tackShooterUpgrades.paths[0].emplace_back(
+ Upgrade("Faster Shooting", 125, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.33}}));
+ image = "TackShooter_EvenFasterShooting";
+ tackShooterUpgrades.paths[0].emplace_back(
+ Upgrade("Even Faster Shooting", 225, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 2}}));
+ image = "TackShooter_HotShots";
+ tackShooterUpgrades.paths[0].emplace_back(
+ Upgrade("Hot Shots", 510, image, gameData.assets[image], {{SHOT_KIND_UPGRADE, HOT_TACK},{DAMAGE_UPGRADE, 1}}));
+
+ image = "TackShooter_LongRangeTacks";
+ tackShooterUpgrades.paths[1].emplace_back(
+ Upgrade("Long Range Tacks", 85, image, gameData.assets[image], {{RANGE_UPGRADE, 1.15},
+ {DISTANCE_UPGRADE, 1.15}}));
+ image = "TackShooter_SuperRangeTacks";
+ tackShooterUpgrades.paths[1].emplace_back(
+ Upgrade("Super Range Tacks", 190, image, gameData.assets[image], {{RANGE_UPGRADE, 1.25},
+ {DISTANCE_UPGRADE, 1.25}}));
+ image = "TackShooter_BladeShooter";
+ tackShooterUpgrades.paths[1].emplace_back(
+ Upgrade("Blade Shooter", 465, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.18},
+ {PIERCE_UPGRADE, 2},
+ {DAMAGE_UPGRADE, 2},
+ {SHOT_KIND_UPGRADE, BLADE}}));
+ image = "TackShooter_MoreTacks";
+ tackShooterUpgrades.paths[2].emplace_back(
+ Upgrade("More Tacks", 85, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 10}}));
+ image = "TackShooter_EvenMoreTacks";
+ tackShooterUpgrades.paths[2].emplace_back(
+ Upgrade("Even More Tacks", 85, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 12}}));
+ image = "TackShooter_TackSprayer";
+ tackShooterUpgrades.paths[2].emplace_back(
+ Upgrade("Tack Sprayer", 350, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.33},
+ {SHOTS_AMOUNT_UPGRADE, 16}}));
+ image = "TackShooter_Overdrive";
+ tackShooterUpgrades.paths[2].emplace_back(
+ Upgrade("Overdrive", 2125, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 3}}));
+ image = "TackShooter_TheTackZone";
+ tackShooterUpgrades.paths[2].emplace_back(
+ Upgrade("The Tack Zone", 17000, image, gameData.assets[image], {{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/Game.h b/src/Game.h
index a09eb90..61a7953 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,57 +1,58 @@
//
// Created by Ido Mozes on 18/06/2019.
//
#ifndef SDL2_GAME_GAME_H
#define SDL2_GAME_GAME_H
#include <iostream>
#include <cmath>
#include <fstream>
#include <memory>
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
#include <comdef.h>
#include "Entity.h"
#include "System.h"
#include "systems/RenderSystem.h"
#include "systems/MovementSystem.h"
#include "systems/EventSystem.h"
#include "systems/BloonsSpawnSystem.h"
#include "systems/ShotsSpawnSystem.h"
#include "systems/DraggingSystem.h"
#include "systems/RemoveEntitiesSystem.h"
#include "systems/CollisionSystem.h"
#include "systems/DamageSystem.h"
#include "systems/MenuSystem.h"
#include "systems/LoadLevelSystem.h"
+#include "systems/PopEffectSystem.h"
#include "GameData.h"
#include "boost/filesystem.hpp"
#include <iostream>
struct TempPoint {
short X;
short Y;
};
class Game {
std::vector<std::unique_ptr<System>> systems;
std::array<Entities, N_LAYERS> layers;
RenderSystem *renderSystem;
public:
GameData gameData;
explicit Game(bool fullscreen, float mapScale = 1.5);
~Game();
void update();
bool running() { return gameData.isRunning; }
void loadMap();
};
#endif //SDL2_GAME_GAME_H
diff --git a/src/Settings.h b/src/Settings.h
index e4fca4d..61f01b1 100644
--- a/src/Settings.h
+++ b/src/Settings.h
@@ -1,21 +1,21 @@
//
// Created by Ido Mozes on 09/07/2019.
//
#ifndef SDL_GAME_SETTINGS_H
#define SDL_GAME_SETTINGS_H
enum Layers {
- BACKGROUND_LAYER, SEQUENCES_LAYER, SHADOW_LAYER, SHOTS_LAYER, TOWERS_LAYER, BLOONS_LAYER, MENU_LAYER,
+ BACKGROUND_LAYER, SEQUENCES_LAYER, SHADOW_LAYER, SHOTS_LAYER, TOWERS_LAYER, BLOONS_LAYER,POP_LAYER, MENU_LAYER,
N_LAYERS
};
enum fonts{
WHITE12,BLACK12,RED12,
WHITE8,BLACK8,RED8,
FONTS_LENGTH
};
constexpr int SIDEBAR_WIDTH = 150;
constexpr int MENU_WIDTH = 250;
constexpr int MAP_WIDTH = 686;
constexpr int MAP_HEIGHT = 511;
constexpr float BPS = 1 / 3.0;
#endif //SDL_GAME_SETTINGS_H
diff --git a/src/System.cpp b/src/System.cpp
index 810a7b6..e016537 100644
--- a/src/System.cpp
+++ b/src/System.cpp
@@ -1,267 +1,279 @@
//
// Created by Ido Mozes on 15/07/2019.
//
#include "System.h"
std::string getSurfaceName(EntityP &entity) {
std::string surfaceName;
int damageStateLives = getBloonProperty<SELF_LIVES>(entity) / 5;
switch (entity->getComponent<Type>()->value) {
case BLOON_T: {
auto[regrowP, camoP, fortifiedP, gooP, lives] = entity->getComponentsP<Regrow, Camo, Fortified, Goo, Lives>();
switch (entity->getComponent<Kind>()->value) {
case RED_BLOON:
surfaceName = "Red";
break;
case BLUE_BLOON:
surfaceName = "Blue";
break;
case GREEN_BLOON:
surfaceName = "Green";
break;
case YELLOW_BLOON:
surfaceName = "Yellow";
break;
case PINK_BLOON:
surfaceName = "Pink";
break;
case PURPLE_BLOON:
surfaceName = "Purple";
break;
case WHITE_BLOON:
surfaceName = "White";
break;
case BLACK_BLOON:
surfaceName = "Black";
break;
case ZEBRA_BLOON:
surfaceName = "Zebra";
break;
case LEAD_BLOON:
surfaceName = "Lead";
break;
case RAINBOW_BLOON:
surfaceName = "Rainbow";
break;
case CERAMIC_BLOON:
surfaceName = "Ceramic";
break;
case MOAB:
surfaceName = "Moab";
break;
case BFB:
surfaceName = "Bfb";
break;
case DDT:
surfaceName = "Ddt";
break;
case ZOMG:
surfaceName = "Zomg";
break;
case BAD:
surfaceName = "Bad";
break;
}
if (regrowP)
surfaceName += "Regrow";
if (camoP)
surfaceName += "Camo";
if (fortifiedP) {
surfaceName += "Fortified";
}
if (damageStateLives) {
int maxLives = getBloonProperty<TOTAL_LIVES>(entity);
if (int damageState = (maxLives - lives->value) / damageStateLives)
surfaceName += "DamageState" + std::to_string(damageState);
}
if (gooP)
switch (gooP->kind) {
case GLUE:
surfaceName += "Gum";
break;
case GUM:
surfaceName += "Glue";
break;
case CORROSIVE:
surfaceName += "Slime";
break;
}
break;
}
case TOWER_T: {
switch (entity->getComponent<Kind>()->value) {
case DART_MONKEY:
surfaceName = "DartMonkey";
break;
case TACK_SHOOTER:
surfaceName = "TackShooter";
break;
case SUPER_MONKEY:
surfaceName = "SuperMonkey";
break;
case SNIPER_MONKEY:
surfaceName = "SniperMonkey";
break;
case BOMB_TOWER:
surfaceName = "BombTower";
break;
case GLUE_GUNNER:
surfaceName = "GlueGunner";
break;
}
break;
}
case SHOT_T: {
switch (entity->getComponent<Kind>()->value) {
case SPIKE:
surfaceName = "Spike";
break;
case JUGGERNAUT:
surfaceName = "Juggernaut";
break;
case LASER:
surfaceName = "Laser";
break;
case PLASMA:
surfaceName = "Plasma";
break;
case SUN:
surfaceName = "Sun";
break;
case DART:
- case RADIAL_DART:
surfaceName = "Dart";
break;
case BOMB:
surfaceName = "Bomb";
break;
case EXPLOSION:
surfaceName = "Explosion";
break;
+ case TACK:
+ case ENHANCED_TACK:
+ surfaceName = "Tack";
+ break;
+ case HOT_TACK:
+ surfaceName="HotTack";
+ break;
+ case BLADE:
+ surfaceName="Blade";
+ break;
case GOO_SPLASH:
switch (entity->getComponent<Goo>()->kind) {
case GLUE:
surfaceName = "SplashGum";
break;
case GUM:
surfaceName = "SplashGlue";
break;
case CORROSIVE:
surfaceName = "SplashSlime";
break;
}
break;
case GOO_SHOT:
switch (entity->getComponent<Goo>()->kind) {
case GLUE:
surfaceName = "GumShot";
break;
case GUM:
surfaceName = "GlueShot";
break;
case CORROSIVE:
surfaceName = "SlimeShot";
break;
}
break;
}
break;
}
}
return surfaceName;
}
float getSpeed(EntityP &entity) {
float speed;
switch (entity->getComponent<Type>()->value) {
case BLOON_T: {
auto gooP = entity->getComponent<Goo>();
switch (entity->getComponent<Kind>()->value) {
case RED_BLOON:
speed = 3 * BPS;
break;
case BLUE_BLOON:
speed = 4 * BPS;
break;
case GREEN_BLOON:
speed = 5 * BPS;
break;
case YELLOW_BLOON:
speed = 10 * BPS;
break;
case PINK_BLOON:
speed = 11 * BPS;
break;
case PURPLE_BLOON:
speed = 9.5 * BPS;
break;
case WHITE_BLOON:
speed = 6 * BPS;
break;
case BLACK_BLOON:
speed = 5 * BPS;
break;
case ZEBRA_BLOON:
speed = 5 * BPS;
break;
case LEAD_BLOON:
speed = 3 * BPS;
break;
case RAINBOW_BLOON:
speed = 7 * BPS;
break;
case CERAMIC_BLOON:
speed = 8 * BPS;
break;
case MOAB:
speed = 3 * BPS;
break;
case BFB:
speed = 1 * BPS;
break;
case DDT:
speed = 9 * BPS;
break;
case ZOMG:
speed = 0.5 * BPS;
break;
case BAD:
speed = 0.2 * BPS;
break;
}
if (gooP)
switch (gooP->kind) {
case CORROSIVE:
case GUM:
speed *= gooP->stickness;
break;
case GLUE:
speed = 0;
}
break;
}
case SHOT_T: {
switch (entity->getComponent<Kind>()->value) {
- case DART:
- case RADIAL_DART:
case SPIKE:
case JUGGERNAUT:
speed = 12;
break;
case LASER:
case PLASMA:
case SUN:
+ case TACK:
+ case BLADE:
+ case ENHANCED_TACK:
+ case HOT_TACK:
+ case DART:
speed = 22;
break;
case BOMB:
case GOO_SHOT:
- speed = 10;
+ speed = 15;
break;
}
break;
}
}
return speed;
}
diff --git a/src/components/FlagComponents.h b/src/components/FlagComponents.h
index 10b46f2..0f7cdd3 100644
--- a/src/components/FlagComponents.h
+++ b/src/components/FlagComponents.h
@@ -1,17 +1,18 @@
//
// Created by Ido Mozes on 15/07/2019.
//
#ifndef SDL_GAME_FLAGCOMPONENTS_H
#define SDL_GAME_FLAGCOMPONENTS_H
#define FLAG_COMPONENT(className, classType) class className : public FlagComponent { public: static constexpr ComponentType type = ComponentType::classType;}
#include "../Component.h"
class FlagComponent : public Component {};
FLAG_COMPONENT(RemoveEntityEvent,REMOVE_ENTITY_EVENT);
FLAG_COMPONENT(Camo,CAMO);
FLAG_COMPONENT(Fortified,FORTIFIED);
+FLAG_COMPONENT(Seen,SEEN);
#endif //SDL_GAME_FLAGCOMPONENTS_H
diff --git a/src/components/IntegerComponents.h b/src/components/IntegerComponents.h
index 44f447c..bb78167 100644
--- a/src/components/IntegerComponents.h
+++ b/src/components/IntegerComponents.h
@@ -1,89 +1,92 @@
//
// Created by Ido Mozes on 14/07/2019.
//
#ifndef SDL_GAME_INTEGERCOMPONENTS_H
#define SDL_GAME_INTEGERCOMPONENTS_H
#define INTEGER_COMPONENT(className, classType) class className : public IntegerComponent { public: static constexpr ComponentType type = ComponentType::classType; using IntegerComponent::IntegerComponent; }
#include "../Component.h"
class IntegerComponent : public Component {
public:
int value;
explicit IntegerComponent(int value) : value(value) {}
};
enum Types {
OBSTACLE_T, TOWER_T, BLOON_T, SHOT_T, SEQUENCE_T
};
enum Strategies {
FIRST, LAST, CLOSEST, STRONGEST
};
enum BloonKinds {
RED_BLOON,
BLUE_BLOON,
GREEN_BLOON,
YELLOW_BLOON,
PINK_BLOON,
PURPLE_BLOON,
WHITE_BLOON,
BLACK_BLOON,
LEAD_BLOON,
ZEBRA_BLOON,
RAINBOW_BLOON,
CERAMIC_BLOON,
MOAB,
BFB,
ZOMG,
DDT,
BAD
};
enum TowerKinds {
DART_MONKEY,
TACK_SHOOTER,
SUPER_MONKEY,
SNIPER_MONKEY,
BOMB_TOWER,
GLUE_GUNNER,
};
enum ShotKinds {
SPIKE,
JUGGERNAUT,
DART,
- RADIAL_DART,
+ TACK,
+ ENHANCED_TACK,
+ HOT_TACK,
+ BLADE,
BULLET,
ENHANCED_BULLET,
BOMB,
EXPLOSION,
GOO_SHOT,
GOO_SPLASH,
GLUE,
GUM,
CORROSIVE,
LASER,
PLASMA,
SUN
};
INTEGER_COMPONENT(Lives, LIVES);
INTEGER_COMPONENT(Damage, DAMAGE);
INTEGER_COMPONENT(Pierce, PIERCE);
INTEGER_COMPONENT(Type, TYPE);
INTEGER_COMPONENT(Strategy, STRATEGY);
INTEGER_COMPONENT(Cost, COST);
INTEGER_COMPONENT(Kind, KIND);
INTEGER_COMPONENT(ShotKind, SHOT_KIND);
INTEGER_COMPONENT(ShotsAmount, SHOTS_AMOUNT);
#endif //SDL_GAME_INTEGERCOMPONENTS_H
diff --git a/src/components/Upgrades.h b/src/components/Upgrades.h
index 43ef9d9..5fea56d 100644
--- a/src/components/Upgrades.h
+++ b/src/components/Upgrades.h
@@ -1,65 +1,66 @@
//
// Created by Ido Mozes on 20/07/2019.
//
#ifndef SDL_GAME_UPGRADES_H
#define SDL_GAME_UPGRADES_H
#include <vector>
#include <tuple>
#include <array>
#include "../Component.h"
#include "SDL.h"
enum UpgradeTypes {
RANGE_UPGRADE,
PIERCE_UPGRADE,
DISTANCE_UPGRADE,
DAMAGE_UPGRADE,
ATTACK_SPEED_UPGRADE,
+ SPREAD_UPGRADE,
CAMO_UPGRADE,
GOO_KIND_UPGRADE,
GOO_SOAK_UPGRADE,
GOO_DURATION_UPGRADE,
GOO_STICKNESS_UPGRADE,
CORROSIVE_INTERVAL_UPGRADE,
CORROSIVE_DAMAGE_UPGRADE,
SHOTS_AMOUNT_UPGRADE,
SHOT_KIND_UPGRADE
};
class Upgrade {
public:
std::vector<std::tuple<int, float>> improvements;
SDL_Surface *surface;
std::string surfaceName;
std::string name;
int cost;
bool locked = false;
Upgrade(const std::string &name, int cost, const std::string surfaceName, SDL_Surface *surface,
std::initializer_list<std::pair<int, float>> improvements) : name(name), cost(cost),
surfaceName(surfaceName), surface(surface) {
for (auto[kind, value]: improvements)
this->improvements.emplace_back(kind, value);
}
};
class UpgradeP : public Component {
public:
Upgrade* value;
static constexpr ComponentType type = ComponentType::UPGRADE_P;
std::string name;
UpgradeP(Upgrade* value) : value(value),name(value->name) {}
};
class Upgrades : public Component {
public:
std::array<std::vector<Upgrade>,3> paths;
int selectedPathUpgrades[3];
int chosenPath = -1;
static constexpr ComponentType type = ComponentType::UPGRADES;
};
#endif //SDL_GAME_UPGRADES_H
diff --git a/src/systems/CollisionSystem.cpp b/src/systems/CollisionSystem.cpp
index ad38dc3..502991a 100644
--- a/src/systems/CollisionSystem.cpp
+++ b/src/systems/CollisionSystem.cpp
@@ -1,184 +1,188 @@
//
// 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) {
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 (kind.value == EXPLOSION or 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 EXPLOSION:
if (bloonKind != BLACK_BLOON and bloonKind != ZEBRA_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case GOO_SPLASH: {
bloon->addComponent<DamageEvent>(damage.value, shot);
if (bloonKind < MOAB and !bloon->getComponent<Goo>()) {
bloon->addComponent<Goo>(*shot->getComponent<Goo>());
SDL_Surface *surface = gameData.assets[getSurfaceName(bloon)];
auto &visibility = *bloon->getComponent<Visibility>();
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.loadTexture(gameData.renderer, surface);
}
shot->addComponent<RemoveEntityEvent>();
break;
}
case BOMB:
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:
explosion->addComponent<Kind>(EXPLOSION);
break;
case GOO_SHOT:
explosion->addComponent<Kind>(GOO_SPLASH);
explosion->addComponent<Goo>(*shot->getComponent<Goo>());
break;
}
explosion->addComponent<Pierce>(pierce);
explosion->addComponent<PoppedBloons>();
explosion->addComponent<Range>(shot->getComponent<Spread>()->value);
explosion->addComponents(damage);
SDL_Surface *surface = gameData.assets[getSurfaceName(explosion)];
explosion->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 2});
layers[SHOTS_LAYER].emplace_back(explosion);
shot->addComponent<RemoveEntityEvent>();
break;
}
if (kind.value == EXPLOSION or --pierce.value == 0)
shot->addComponent<RemoveEntityEvent>();
}
}
}
\ No newline at end of file
diff --git a/src/systems/DamageSystem.cpp b/src/systems/DamageSystem.cpp
index 82e8725..902bb2b 100644
--- a/src/systems/DamageSystem.cpp
+++ b/src/systems/DamageSystem.cpp
@@ -1,217 +1,220 @@
//
// Created by Ido Mozes on 15/07/2019.
//
#include "DamageSystem.h"
bool didBloonPop(EntityP &bloon, int &lives, int &damage) {
if (bloon->getComponent<Fortified>()) {
if (damage == 1) {
lives -= damage;
if (lives % 2 == 1)
return false;
} else {
lives -= 2;
damage -= 2;
}
} else {
lives -= 1;
damage -= 1;
}
return true;
}
bool didBloonPop(EntityP &bloon, int &lives, int &damage, int minLives) {
if (lives - damage >= minLives) {
lives -= damage;
return false;
}
damage -= lives - minLives + 1;
lives = minLives - 1;
return true;
}
EntityP
spawnBloon(Entities &newBloons, GameData &gameData, int kind, EntityP &shot, int lives, float progress,
int regrow, bool camo, bool fortified, Goo *gooP) {
EntityP newBloon(new Entity());
- shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
+ if (shot)
+ shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
newBloon->addComponent<Type>(BLOON_T);
newBloon->addComponent<Kind>(kind);
newBloon->addComponent<Lives>(lives);
newBloon->addComponent<Position>(gameData.startingPoint);
newBloon->addComponent<PathIndex>(0, progress);
if (regrow)
newBloon->addComponent<Regrow>(regrow);
if (camo)
newBloon->addComponent<Camo>();
if (fortified)
newBloon->addComponent<Fortified>();
if (gooP and gooP->soak)
newBloon->addComponent<Goo>(*gooP);
SDL_Surface *surface = gameData.assets[getSurfaceName(newBloon)];
newBloon->addComponent<Range>(std::max(surface->w / 6, surface->h / 6));
newBloon->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 3, 0});
newBloons.emplace_back(newBloon);
return newBloon;
}
void damageBloon(EntityP &bloon, EntityP &shot, int damage, GameData &gameData, Entities &newBloons) {
if (damage == 0)
return;
auto &lives = bloon->getComponent<Lives>()->value;
if (damage >= lives) {
gameData.cash += getBloonProperty<YIELD>(bloon);
bloon->addComponent<RemoveEntityEvent>();
return;
}
auto &kind = bloon->getComponent<Kind>()->value;
- auto &shotKind = shot->getComponent<Kind>()->value;
+ int shotKind = 0;
+ if (shot)
+ shotKind = shot->getComponent<Kind>()->value;
float progress = bloon->getComponent<PathIndex>()->progress;
SDL_Surface *surface;
auto &visibility = *bloon->getComponent<Visibility>();
if (kind > PINK_BLOON) {
if (kind >= CERAMIC_BLOON and !didBloonPop(bloon, lives, damage, getBloonProperty<MIN_LIVES>(bloon))) {
surface = gameData.assets[getSurfaceName(bloon)];
visibility.loadTexture(gameData.renderer, surface);
return;
}
if (kind < CERAMIC_BLOON and !didBloonPop(bloon, lives, damage))
return;
}
if (kind > PINK_BLOON) {
gameData.cash += 1;
bloon->addComponent<RemoveEntityEvent>();
}
auto[regrowP, camoP, fortifiedP, gooP] = bloon->getComponentsP<Regrow, Camo, Fortified, Goo>();
switch (kind) {
case RED_BLOON:
lives -= 1;
break;
case BLUE_BLOON:
case GREEN_BLOON:
case YELLOW_BLOON:
case PINK_BLOON: {
lives -= damage;
if (fortifiedP and damage == 1 and lives % 2 == 1)
break;
if (gooP and !gooP->soak)
bloon->removeComponent<Goo>();
gameData.cash += kind - ((fortifiedP ? lives / 2 : lives) - 1);
kind = (fortifiedP ? lives / 2 : lives) - 1;
surface = gameData.assets[getSurfaceName(bloon)];
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.loadTexture(gameData.renderer, surface);
if (regrowP)
regrowP->regrowTime = 60;
bloon->getComponent<Range>()->value = std::max(surface->w / 6, surface->h / 6);
break;
}
case PURPLE_BLOON:
case WHITE_BLOON:
case BLACK_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, PINK_BLOON, shot, lives / 2,
i == 0 ? std::fmaxf(0, progress - 10) : std::fminf(
gameData.path.size() - 1, progress + 10),
regrowP ? regrowP->kind : 0, camoP, false, gooP);
damageBloon(newBloon, shot,
shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
damage / 2.0)), gameData, newBloons);
}
break;
}
case ZEBRA_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, i == 0 ? BLACK_BLOON : WHITE_BLOON, shot, lives / 2,
i == 0 ? std::fmaxf(0, progress - 12) : std::fminf(
gameData.path.size() - 1, progress + 12),
regrowP ? regrowP->kind : 0, camoP, false, gooP);
damageBloon(newBloon, shot,
shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
damage / 2.0)), gameData, newBloons);
}
break;
}
case LEAD_BLOON:
case RAINBOW_BLOON:
case CERAMIC_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, kind - 1, shot, lives / 2,
i == 0 ? std::fmaxf(0, progress - 14) : std::fminf(
gameData.path.size() - 1, progress + 14),
regrowP ? regrowP->kind : 0, camoP, false, gooP);
damageBloon(newBloon, shot,
shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
damage / 2.0)), gameData, newBloons);
}
break;
}
case MOAB:
case BFB:
case ZOMG:
case DDT: {
for (int i = 0; i < 4; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, kind == DDT ? CERAMIC_BLOON : kind - 1,
shot, lives / 4,
(i < 2 ? std::fmaxf(0, progress - 20 + 10 * (i % 2)) : std::fminf(
gameData.path.size() - 1, progress + 20 - 10 * (i % 2))),
kind == DDT ? CERAMIC_BLOON : 0, kind == DDT, fortifiedP, nullptr);
damageBloon(newBloon, shot,
shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 4.0) : damage -
ceilf(damage / 4.0) *
3),
gameData, newBloons);
}
break;
}
case BAD: {
for (int i = 0; i < 5; ++i) {
EntityP newBloon(new Entity());
shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
newBloon->addComponent<Type>(BLOON_T);
newBloon->addComponent<Kind>(i < 2 ? ZOMG : DDT);
if (fortifiedP)
newBloon->addComponent<Fortified>();
if (i >= 2)
newBloon->addComponent<Camo>();
newBloon->addComponent<Lives>(getBloonProperty<TOTAL_LIVES>(newBloon));
newBloon->addComponent<Position>(gameData.startingPoint);
newBloon->addComponent<PathIndex>(0);
newBloon->getComponent<PathIndex>()->progress =
i < 2 ? std::fmaxf(0, progress - 20 + 10 * (i % 2)) : std::fminf(
gameData.path.size() - 1, progress + 30 - 10 * (i % 3));
surface = gameData.assets[getSurfaceName(newBloon)];
newBloon->addComponent<Range>(std::max(surface->w / 6, surface->h / 6));
newBloon->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 3, 0});
newBloons.emplace_back(newBloon);
damageBloon(newBloon, shot,
shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 5.0) : damage -
ceilf(damage / 5.0) *
4), gameData,
newBloons);
}
break;
}
}
}
void DamageSystem::update(Entities *layers, GameData &gameData) {
if (!gameData.levelRunning)
return;
Entities newBloons;
for (auto &bloon: layers[BLOONS_LAYER]) {
if (auto damageEventP = bloon->getComponent<DamageEvent>()) {
auto &damageEvent = *damageEventP;
auto &lives = bloon->getComponent<Lives>()->value;
if (lives < damageEvent.damage) {
gameData.cash += getBloonProperty<YIELD>(bloon);
bloon->addComponent<RemoveEntityEvent>();
} else {
damageBloon(bloon, damageEvent.shot, damageEvent.damage, gameData, newBloons);
bloon->removeComponent<DamageEvent>();
}
}
}
layers[BLOONS_LAYER] += newBloons;
}
diff --git a/src/systems/EventSystem.cpp b/src/systems/EventSystem.cpp
index 64d2bb2..c0dcf58 100644
--- a/src/systems/EventSystem.cpp
+++ b/src/systems/EventSystem.cpp
@@ -1,273 +1,278 @@
//
// 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_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;
auto buttonKindP = entity->getComponent<Kind>();
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();
SDL_Surface *surface = gameData.assets[getSurfaceName(entity)];
draggable->addComponent<Visibility>(gameData.renderer, 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)
break;
switch (buttonKindP->value) {
case PLAY_FAST_FORWARD: {
if (gameData.levelRunning)
gameData.FPS = 240 - gameData.FPS;
else
gameData.levelRunning = true;
visibility.loadTexture(gameData.renderer,
gameData.assets[std::string("FastForward") +
(gameData.FPS == 180 ? "Enabled"
: "")]);
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<Range>()->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 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_STICKNESS_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;
}
}
auto &path = gameData.selected->getComponent<Upgrades>()->paths[
buttonKindP->value - UPGRADE_PATH_1];
path.erase(path.begin());
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/PopEffectSystem.cpp b/src/systems/PopEffectSystem.cpp
new file mode 100644
index 0000000..92e9cd9
--- /dev/null
+++ b/src/systems/PopEffectSystem.cpp
@@ -0,0 +1,30 @@
+//
+// Created by Ido Mozes on 22/07/2019.
+//
+
+#include "PopEffectSystem.h"
+const int range_from = 0;
+const int range_to = 360;
+std::random_device rand_dev;
+std::mt19937 generator(rand_dev());
+std::uniform_int_distribution<int> distr(range_from, range_to);
+
+void PopEffectSystem::update(Entities *layers, GameData &gameData) {
+ layers[POP_LAYER].clear();
+ for (auto &bloon: layers[BLOONS_LAYER]) {
+ if(bloon->getComponent<RemoveEntityEvent>() and bloon->getComponent<Seen>()){
+ auto &position = bloon->getComponent<Position>()->value;
+ if(position.X <0 or position.X >= MAP_WIDTH or position.Y <0 or position.Y >= MAP_HEIGHT)
+ continue;
+ auto pop = new Entity();
+ pop->addComponent<Position>(position);
+ SDL_Surface *surface = gameData.assets["Pop"];
+ pop->addComponent<Visibility>(gameData.renderer,surface,SDL_Rect{0,0,int(surface->w/1.5)},distr(generator));
+ layers[POP_LAYER].emplace_back(pop);
+ }
+ else
+ bloon->addComponent<Seen>();
+ }
+}
+
+
diff --git a/src/systems/PopEffectSystem.h b/src/systems/PopEffectSystem.h
new file mode 100644
index 0000000..6780ffe
--- /dev/null
+++ b/src/systems/PopEffectSystem.h
@@ -0,0 +1,13 @@
+//
+// Created by Ido Mozes on 22/07/2019.
+//
+
+#ifndef SDL_GAME_POPEFFECTSYSTEM_H
+#define SDL_GAME_POPEFFECTSYSTEM_H
+#include <random>
+#include "../System.h"
+class PopEffectSystem : public System {
+public:
+ void update(Entities *layers, GameData &gameData) override;
+};
+#endif //SDL_GAME_POPEFFECTSYSTEM_H
diff --git a/src/systems/ShotsSpawnSystem.cpp b/src/systems/ShotsSpawnSystem.cpp
index ee12d03..79d20bd 100644
--- a/src/systems/ShotsSpawnSystem.cpp
+++ b/src/systems/ShotsSpawnSystem.cpp
@@ -1,174 +1,191 @@
//
// 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 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 (shotKind.value == BOMB or shotKind.value == GOO_SHOT) {
shot->addComponent<Spread>(*entity->getComponent<Spread>());
if (auto gooP = entity->getComponent<Goo>())
shot->addComponent<Goo>(*gooP);
}
shot->addComponents(pierce, damage, distance);
if (camoP)
shot->addComponent<Camo>();
shot->addComponent<PoppedBloons>();
SDL_Surface *surface = gameData.assets[getSurfaceName(shot)];
int scale = 1;
switch (shotKind.value) {
case DART:
scale = 2;
break;
case BOMB:
scale = 4;
break;
case SPIKE:
case JUGGERNAUT:
scale = 4;
break;
case LASER:
scale = 10;
break;
case PLASMA:
case SUN:
scale = 6;
break;
}
shot->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{0, 0, 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 RADIAL_DART: {
- SDL_Surface *surface = gameData.assets["Dart"];
- for (int j = 0; j < 8; ++j) {
+ 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<Position>(towerPosition.value.X, towerPosition.value.Y);
shot->addComponent<Type>(SHOT_T);
shot->addComponent<Kind>(shotKind.value);
- angle = (M_PI / 4) * j;
+ 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>();
- if (camoP)
- shot->addComponent<Camo>();
- shot->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 2},
+ SDL_Surface *surface = gameData.assets[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>(gameData.renderer, surface,
+ SDL_Rect{0, 0, surface->w / scale},
radToDeg(angle));
layers[SHOTS_LAYER].emplace_back(shot);
}
break;
}
}
}
- if (shotKind.value != RADIAL_DART)
+ 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, 11:46 AM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42872
Default Alt Text
(96 KB)
Attached To
Mode
R74 BloonsTD
Attached
Detach File
Event Timeline
Log In to Comment