Page MenuHomePhabricator (Chris)

No OneTemporary

Size
96 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline