Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F86464
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
94 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e1f502..99bd018 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)
+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/CashSystem.cpp src/systems/CashSystem.h src/components/Regrow.h src/systems/LoadLevelSystem.cpp src/systems/LoadLevelSystem.h src/components/Goo.h)
target_link_libraries(SDL_Game ${Boost_LIBRARIES})
\ No newline at end of file
diff --git a/scripts/convert_levels.py b/scripts/convert_levels.py
new file mode 100644
index 0000000..5927536
--- /dev/null
+++ b/scripts/convert_levels.py
@@ -0,0 +1,42 @@
+from struct import pack
+
+i = 1
+kinds = {
+ 'red': 0,
+ 'blue': 1,
+ 'green': 2,
+ 'yellow': 3,
+ 'pink': 4,
+ 'purple': 5,
+ 'white': 6,
+ 'black': 7,
+ 'lead': 8,
+ 'zebra': 9,
+ 'rainbow': 10,
+ 'ceramic': 11,
+ 'moab': 12,
+ 'bfb': 13,
+ 'zomg': 14,
+ 'ddt': 15,
+ 'bad': 16
+}
+while True:
+ try:
+ level = open(f"assets/Levels/level{i}.csv").read().split('\n')
+ with open(f'assets/Levels/level{i}.data', 'wb') as out_file:
+ for sequence in level:
+ delay, kind, amount, bps = sequence.split(',')
+ delay = int(delay)
+ amount = int(amount)
+ bps = int(bps)
+ kind = kind.split(' ')
+ out_file.write(pack('<I', delay))
+ out_file.write(pack('<I', kinds[kind[0]]))
+ out_file.write(pack('<I', amount))
+ out_file.write(pack('<I', bps))
+ out_file.write(bytes([len(kind) > 1 and 'r' in kind[1]]))
+ out_file.write(bytes([len(kind) > 1 and 'c' in kind[1]]))
+ out_file.write(bytes([len(kind) > 1 and 'f' in kind[1]]))
+ except FileNotFoundError:
+ break
+ i += 1
diff --git a/src/Component.h b/src/Component.h
index d41bb4d..c4c9470 100644
--- a/src/Component.h
+++ b/src/Component.h
@@ -1,65 +1,62 @@
//
// 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,
DISTANCE,
SEQUENCE,
ACTION,
DRAGGABLE,
RANGE_SHADOW,
STRATEGY,
REGROW,
CAMO,
FORTIFIED,
- GLUE,
- GUM,
- CORROSIVE,
+ GOO,
COST,
- VALUE,
POPPED_BLOONS,
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 9d3bd86..12ef694 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,159 +1,251 @@
//
// 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.jpg");
gameData.assets["upgrade_bar2"] = IMG_Load("../assets/upgrade_bar2.jpg");
gameData.assets["menu"] = IMG_Load("../assets/menu.jpg");
renderSystem = new RenderSystem();
renderSystem->init(gameData);
loadMap();
- std::initializer_list<std::pair<std::string, Point>> sprites[]{
- {{"map", {SIDEBAR_WIDTH, 0}}},
+ std::initializer_list<std::tuple<std::string, Point, float>> sprites[]{
+ {{"map", {SIDEBAR_WIDTH, 0}, 1}},
{},
{},
{},
{},
{},
- {{"upgrade_bar", {0, 0}}, {"menu", {MAP_WIDTH + SIDEBAR_WIDTH, 0}}},
+ {
+ {"upgrade_bar", {0, 0}, 1},
+ {"menu", {MAP_WIDTH + SIDEBAR_WIDTH, 0}, 1},
+ {"Cash", {MAP_WIDTH + SIDEBAR_WIDTH + 20,9}, 0.6},
+ {"Lives", {MAP_WIDTH + SIDEBAR_WIDTH + 20, 31}, 0.6}
+ },
};
for (int i = 0; i < N_LAYERS; i++) {
- for (auto &sprite :sprites[i]) {
+ for (auto[surfaceName, position, scale] :sprites[i]) {
auto spriteEntity = new Entity();
- spriteEntity->addComponent<Visibility>(gameData.renderer, gameData.assets[sprite.first],
- SDL_Rect{int(sprite.second.X), int(sprite.second.Y),
- gameData.assets[sprite.first]->w,
- gameData.assets[sprite.first]->h});
+ SDL_Surface *surface = gameData.assets[surfaceName];
+ spriteEntity->addComponent<Visibility>(gameData.renderer, surface,
+ SDL_Rect{int(position.X), int(position.Y), int(surface->w * scale)});
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;
// Towers
auto tower = new Entity();
tower->addComponent<Kind>(SUPER_MONKEY);
tower->addComponent<ShotKind>(DART);
- SDL_Surface *surface = gameData.assets["SuperMonkey"];
+ surface = gameData.assets["SuperMonkey"];
+ position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 85};
tower->addComponent<Visibility>(gameData.renderer, surface,
- SDL_Rect{SIDEBAR_WIDTH + MAP_WIDTH + 10, 100, int(surface->w / 1.5), 0});
+ 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>(30);
+ tower->addComponent<Camo>();
+ tower->addComponent<AttackSpeed>(15);
tower->addComponent<Pierce>(4);
- tower->addComponent<Damage>(100);
+ tower->addComponent<Damage>(50);
tower->addComponent<Distance>(150);
tower->addComponent<Type>(TOWER_T);
+ tower->addComponent<Cost>(3000);
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
+ position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 85};
tower->addComponent<Kind>(SNIPER_MONKEY);
tower->addComponent<ShotKind>(GUN);
surface = gameData.assets["SniperMonkey"];
tower->addComponent<Visibility>(gameData.renderer, surface,
- SDL_Rect{SIDEBAR_WIDTH + MAP_WIDTH + 100, 100, int(surface->w / 1.5), 0});
+ 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>(2);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(3);
tower->addComponent<Distance>(600);
+ tower->addComponent<Cost>(200);
tower->addComponent<Type>(TOWER_T);
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>(65);
+ tower->addComponent<AttackSpeed>(2);
+ tower->addComponent<Pierce>(2);
+ tower->addComponent<Damage>(1);
+ tower->addComponent<Distance>(80);
+ tower->addComponent<Cost>(80);
+ tower->addComponent<Type>(TOWER_T);
+ layers[MENU_LAYER].emplace_back(tower);
- // Sequences
- auto s = new Entity();
- s->addComponent<Sequence>(1, 0.5, 0);
- s->addComponent<Kind>(BAD);
- s->addComponent<Type>(SEQUENCE_T);
- layers[SEQUENCES_LAYER].emplace_back(s);
-// s = new Entity();
-// s->addComponent<Sequence>(100, 20.2, 60 * 5);
-// s->addComponent<Kind>(std::string("Blue"));
-// s->addComponent<Type>(SEQUENCE_T);
-// s->addComponent<Speed>(1.5);
-// layers[SEQUENCES_LAYER].emplace_back(s);
+ 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<AttackSpeed>(2);
+ tower->addComponent<Pierce>(1);
+ tower->addComponent<Damage>(2);
+ tower->addComponent<Distance>(500);
+ tower->addComponent<Cost>(300);
+ 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>(70);
+ tower->addComponent<AttackSpeed>(2);
+ tower->addComponent<Pierce>(1);
+ tower->addComponent<Damage>(0);
+ tower->addComponent<Distance>(100);
+ tower->addComponent<Cost>(250);
+ tower->addComponent<Spread>(30);
+ tower->addComponent<Type>(TOWER_T);
+ tower->addComponent<Goo>(CORROSIVE,120);
+ 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);
+ 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<Pierce>(1);
+ tower->addComponent<Damage>(2);
+ tower->addComponent<Distance>(80);
+ tower->addComponent<Cost>(150);
+ tower->addComponent<Type>(TOWER_T);
+ 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 CashSystem);
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, 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 706aa4e..0d6c885 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,50 +1,57 @@
//
// 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/CashSystem.h"
+#include "systems/LoadLevelSystem.h"
#include "GameData.h"
#include "boost/filesystem.hpp"
#include <iostream>
-struct TempPoint{short X;short Y;};
+
+struct TempPoint {
+ short X;
+ short Y;
+};
+
class Game {
std::vector<std::unique_ptr<System>> systems;
Entities layers[N_LAYERS];
- RenderSystem * renderSystem;
+ RenderSystem *renderSystem;
+public:
GameData gameData;
-
-public:
- explicit Game(bool fullscreen, float mapScale=1.5);
+ 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/GameData.h b/src/GameData.h
index 2ea6bba..6a3852d 100644
--- a/src/GameData.h
+++ b/src/GameData.h
@@ -1,42 +1,49 @@
//
// Created by Ido Mozes on 03/07/2019.
//
#ifndef SDL_GAME_GAMEDATA_H
#define SDL_GAME_GAMEDATA_H
#include <vector>
#include <memory>
#include <unordered_map>
#include "Component.h"
#include "Entity.h"
#include "Settings.h"
#include "SDL.h"
#include "SDL_FontCache.h"
constexpr char FREE = 0;
constexpr char OBSTACLE = 1;
constexpr char TOWER = 2;
class GameData {
public:
+ int FPS = 60;
bool isRunning = true;
+ bool lost = false;
bool isDragging = false;
- int money = 0;
- int level = 0;
+ int cash = 1000;
+ bool levelRunning = false;
+ bool levelReady = false;
+ int lives = 200;
+ int level = 1;
+ int finalLevel = 2;
int map = 0;
float mapScale;
bool fullscreen;
std::vector<char> path;
char mapData[MAP_WIDTH][MAP_HEIGHT];
Point startingPoint;
Point finishPoint;
std::unordered_map<std::string, SDL_Surface *> assets;
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
EntityP selected;
FC_Font* font;
+ EntityP playButton;
~GameData();
};
#endif //SDL_GAME_GAMEDATA_H
diff --git a/src/System.cpp b/src/System.cpp
index e9d5672..aec9e45 100644
--- a/src/System.cpp
+++ b/src/System.cpp
@@ -1,194 +1,245 @@
//
// 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, glueP, gumP, corrosiveP, lives] = entity->getComponentsP<Regrow, Camo, Fortified, Glue, Gum, Corrosive, Lives>();
+ 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 (gumP)
- surfaceName += "Gum";
- else if (glueP)
- surfaceName += "Glue";
- else if (corrosiveP)
- surfaceName += "Slime";
+ if (gooP)
+ switch (gooP->kind) {
+ case GUM:
+ surfaceName += "Gum";
+ break;
+ case GLUE:
+ 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 DART:
+ case RADIAL_DART:
surfaceName = "Dart";
break;
- case GUN:
- surfaceName = "Dart";
+ case BOMB:
+ surfaceName = "Bomb";
+ break;
+ case EXPLOSION:
+ surfaceName = "Explosion";
+ break;
+ case GOO_SPLASH:
+ switch (entity->getComponent<Goo>()->kind) {
+ case GUM:
+ surfaceName = "SplashGum";
+ break;
+ case GLUE:
+ surfaceName = "SplashGlue";
+ break;
+ case CORROSIVE:
+ surfaceName = "SplashSlime";
+ break;
+ }
+ break;
+ case GOO_SHOT:
+ switch (entity->getComponent<Goo>()->kind) {
+ case GUM:
+ surfaceName = "SplashGum";
+ break;
+ case GLUE:
+ surfaceName = "SplashGlue";
+ break;
+ case CORROSIVE:
+ surfaceName = "SplashSlime";
+ break;
+ }
break;
}
break;
}
-
}
return surfaceName;
}
float getSpeed(EntityP &entity) {
float speed;
switch (entity->getComponent<Type>()->value) {
case BLOON_T: {
- auto[glueP, gumP, corrosiveP] = entity->getComponentsP<Glue, Gum, Corrosive>();
+ 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 (gumP or corrosiveP)
- speed *= 0.8;
- else if (glueP)
- speed = 0;
+ if (gooP)
+ switch (gooP->kind) {
+ case CORROSIVE:
+ case GUM:
+ speed *= 0.5;
+ break;
+ case GLUE:
+ speed = 0;
+ }
break;
}
case SHOT_T: {
switch (entity->getComponent<Kind>()->value) {
case DART:
+ case RADIAL_DART:
speed = 12;
break;
- case GUN:
- speed = 65;
+ case BOMB:
+ case GOO_SHOT:
+ speed = 10;
break;
}
break;
}
}
return speed;
}
diff --git a/src/System.h b/src/System.h
index 9cddc6d..aa06014 100644
--- a/src/System.h
+++ b/src/System.h
@@ -1,147 +1,155 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#ifndef SDL2_GAME_SYSTEM_H
#define SDL2_GAME_SYSTEM_H
#include <initializer_list>
#include <vector>
#include <cmath>
#include "Entity.h"
#include "GameData.h"
#include "components/IntegerComponents.h"
#include "components/FloatComponents.h"
#include "components/FlagComponents.h"
#include "components/Sequence.h"
#include "components/Position.h"
#include "components/Visibility.h"
#include "components/PathIndex.h"
#include "components/Velocity.h"
#include "components/Draggable.h"
#include "components/Action.h"
#include "components/RangeShadow.h"
#include "components/AttackSpeed.h"
#include "components/DamageEvent.h"
#include "components/PoppedBloons.h"
#include "components/MoveEntityEvent.h"
+#include "components/Regrow.h"
+#include "components/Goo.h"
typedef std::vector<EntityP> Entities;
inline void
operator+=(Entities &originalVector, Entities &newVector) {
originalVector.insert(originalVector.end(), std::make_move_iterator(newVector.begin()),
std::make_move_iterator(newVector.end()));
}
std::string getSurfaceName(EntityP &entity);
enum ReturnValue {
TOTAL_LIVES, MIN_LIVES, SELF_LIVES, YIELD
};
float getSpeed(EntityP &entity);
template<ReturnValue returnValue>
int getBloonProperty(EntityP &entity) {
int totalLives = 0;
int selfLives = 0;
int minLives = 0;
int yield = 0;
int additionalPreviousFortifiedLives = 0;
auto fortifiedP = entity->getComponent<Fortified>();
switch (entity->getComponent<Kind>()->value) {
case RED_BLOON:
- yield = totalLives = 1;
+ yield = totalLives = selfLives = 1;
break;
case BLUE_BLOON:
- yield = totalLives = 2;
+ yield = totalLives = selfLives = 2;
break;
case GREEN_BLOON:
- yield = totalLives = 3;
+ yield = totalLives = selfLives = 3;
break;
case YELLOW_BLOON:
- yield = totalLives = 4;
+ yield = totalLives = selfLives = 4;
break;
case PINK_BLOON:
- yield = totalLives = 5;
+ yield = totalLives = selfLives = 5;
break;
case PURPLE_BLOON:
+ selfLives = 1;
yield = totalLives = 11;
break;
case WHITE_BLOON:
+ selfLives = 1;
yield = totalLives = 11;
break;
case BLACK_BLOON:
+ selfLives = 1;
yield = totalLives = 11;
break;
case ZEBRA_BLOON:
+ selfLives = 1;
yield = totalLives = 23;
break;
case LEAD_BLOON:
+ selfLives = 1;
yield = totalLives = 23;
break;
case RAINBOW_BLOON:
+ selfLives = 1;
yield = totalLives = 47;
break;
case CERAMIC_BLOON:
selfLives = 10;
yield = 95;
totalLives = 104;
break;
case MOAB:
additionalPreviousFortifiedLives = 40;
yield = 381;
selfLives = 200;
totalLives = 616;
break;
case BFB:
additionalPreviousFortifiedLives = 960;
yield = 1525;
selfLives = 1200;
totalLives = 3164;
break;
case DDT:
additionalPreviousFortifiedLives = 40;
yield = 381;
selfLives = 400;
totalLives = 816;
break;
case ZOMG:
additionalPreviousFortifiedLives = 8640;
yield = 6101;
selfLives = 4000;
totalLives = 16656;
break;
case BAD:
additionalPreviousFortifiedLives = 26600;
yield = 13346;
selfLives = 20000;
totalLives = 55760;
break;
}
minLives = totalLives - selfLives + 1;
if (fortifiedP) {
totalLives += additionalPreviousFortifiedLives + selfLives;
selfLives *= 2;
minLives += additionalPreviousFortifiedLives;
}
switch (returnValue) {
case TOTAL_LIVES:
return totalLives;
case MIN_LIVES:
return minLives;
case SELF_LIVES:
return selfLives;
case YIELD:
return yield;
}
}
class System {
public:
virtual void update(Entities *entities, GameData &gameData) = 0;
};
#endif //SDL2_GAME_SYSTEM_H
diff --git a/src/components/Action.h b/src/components/Action.h
index da2d893..333cc05 100644
--- a/src/components/Action.h
+++ b/src/components/Action.h
@@ -1,27 +1,29 @@
//
// Created by Ido Mozes on 08/07/2019.
//
#ifndef SDL_GAME_ACTION_H
#define SDL_GAME_ACTION_H
#include "../Component.h"
enum ActionType {
DRAG, CLICK, DROP, SELECT
};
-
+enum Buttons {
+ PLAY_FAST_FORWARD
+};
class Action : public Component {
public:
bool disabled;
ActionType actionType;
static constexpr ComponentType type = ComponentType::ACTION;
explicit Action(ActionType actionType, bool disabled = false) :actionType(actionType),
disabled(disabled) {}
};
#endif //SDL_GAME_ACTION_H
diff --git a/src/components/FlagComponents.h b/src/components/FlagComponents.h
index a3bd900..10b46f2 100644
--- a/src/components/FlagComponents.h
+++ b/src/components/FlagComponents.h
@@ -1,20 +1,17 @@
//
// 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(Regrow,REGROW);
-FLAG_COMPONENT(Glue,GLUE);
-FLAG_COMPONENT(Gum,GUM);
-FLAG_COMPONENT(Corrosive,CORROSIVE);
+
#endif //SDL_GAME_FLAGCOMPONENTS_H
diff --git a/src/components/Goo.h b/src/components/Goo.h
new file mode 100644
index 0000000..ee99b08
--- /dev/null
+++ b/src/components/Goo.h
@@ -0,0 +1,20 @@
+//
+// Created by Ido Mozes on 18/07/2019.
+//
+
+#ifndef SDL_GAME_GOO_H
+#define SDL_GAME_GOO_H
+
+#include "../Component.h"
+
+class Goo : public Component {
+public:
+ int kind;
+ int ttl;
+ int timetoRecharge = 60;
+ static constexpr ComponentType type = ComponentType::GOO;
+
+ Goo(int kind, int ttl) : kind(kind), ttl(ttl) {}
+};
+
+#endif //SDL_GAME_GOO_H
diff --git a/src/components/IntegerComponents.h b/src/components/IntegerComponents.h
index bffa3b4..746d092 100644
--- a/src/components/IntegerComponents.h
+++ b/src/components/IntegerComponents.h
@@ -1,66 +1,74 @@
//
// 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,
- ZEBRA_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{
DART,
- BOMB,
- SPRAY,
+ RADIAL_DART,
GUN,
+ BOMB,
+ EXPLOSION,
+ GOO_SHOT,
+ GOO_SPLASH,
+ GLUE,
+ GUM,
+ CORROSIVE,
};
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(Value,VALUE);
INTEGER_COMPONENT(Kind,KIND);
INTEGER_COMPONENT(ShotKind,SHOT_KIND);
#endif //SDL_GAME_INTEGERCOMPONENTS_H
diff --git a/src/components/PathIndex.h b/src/components/PathIndex.h
index 02a51c7..e29a090 100644
--- a/src/components/PathIndex.h
+++ b/src/components/PathIndex.h
@@ -1,21 +1,23 @@
//
// Created by Ido Mozes on 03/07/2019.
//
#ifndef SDL_GAME_PATHINDEX_H
#define SDL_GAME_PATHINDEX_H
#include "../Component.h"
class PathIndex : public Component {
public:
int index;
float progress;
static constexpr ComponentType type = ComponentType::PATH_INDEX;
- explicit PathIndex(int index) :index(index), progress(index) {}
+ explicit PathIndex(int index) : index(index), progress(index) {}
+
+ PathIndex(int index, float progress) : index(index), progress(progress) {}
};
#endif //SDL_GAME_PATHINDEX_H
diff --git a/src/components/Regrow.h b/src/components/Regrow.h
new file mode 100644
index 0000000..30e0635
--- /dev/null
+++ b/src/components/Regrow.h
@@ -0,0 +1,17 @@
+//
+// Created by Ido Mozes on 18/07/2019.
+//
+
+#ifndef SDL_GAME_REGROW_H
+#define SDL_GAME_REGROW_H
+#include "../Component.h"
+
+class Regrow: public Component{
+public:
+ int kind;
+ int regrowTime = 60;
+ static constexpr ComponentType type = ComponentType::REGROW;
+ explicit Regrow(int kind):kind(kind){}
+ Regrow(const Regrow & other):kind(other.kind){}
+};
+#endif //SDL_GAME_REGROW_H
diff --git a/src/main.cpp b/src/main.cpp
index e754cb5..7962e92 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,21 +1,25 @@
//#include <iostream>
#include "SDL.h"
#include "Game.h"
int main(int argc, char *argv[]) {
- const int FPS = 60, frameDelay = 1000 / FPS;
+ int FPS = 60, frameDelay = 1000 / FPS;
Uint32 frameStart;
int frameTime;
Game game(false, 1.5);
while (game.running()) {
+ if (game.gameData.FPS != FPS) {
+ FPS = game.gameData.FPS;
+ frameDelay = 1000 / FPS;
+ }
frameStart = SDL_GetTicks();
game.update();
frameTime = SDL_GetTicks() - frameStart;
if (frameDelay > frameTime) {
SDL_Delay(frameDelay - frameTime);
}
}
return 0;
}
\ No newline at end of file
diff --git a/src/systems/BloonsSpawnSystem.cpp b/src/systems/BloonsSpawnSystem.cpp
index 8d55041..c913125 100644
--- a/src/systems/BloonsSpawnSystem.cpp
+++ b/src/systems/BloonsSpawnSystem.cpp
@@ -1,35 +1,42 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#include "BloonsSpawnSystem.h"
void BloonsSpawnSystem::update(Entities *layers, GameData &gameData) {
+ if(!gameData.levelRunning)
+ return;
for (auto &entity: layers[SEQUENCES_LAYER]) {
auto[sequence, kind] = entity->getComponents<Sequence, Kind>().value();
auto [regrowP,camoP,fortifiedP] = entity->getComponentsP<Regrow,Camo,Fortified>();
int amount = sequence.getAmountReady();
+ if(amount == SEQUENCE_FINISHED)
+ {
+ entity->addComponent<RemoveEntityEvent>();
+ continue;
+ }
for (int j = 0; j < amount; ++j) {
EntityP bloon(new Entity());
bloon->addComponent<Type>(BLOON_T);
bloon->addComponent<Position>(gameData.startingPoint);
bloon->addComponent<PathIndex>(0);
bloon->addComponents(kind);
if (regrowP)
- bloon->addComponent<Regrow>();
+ bloon->addComponent<Regrow>(*regrowP);
if (camoP)
bloon->addComponent<Camo>();
if (fortifiedP)
bloon->addComponent<Fortified>();
bloon->addComponent<Lives>(getBloonProperty<TOTAL_LIVES>(bloon));
SDL_Surface *surface = gameData.assets[getSurfaceName(bloon)];
bloon->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{0, 0, int(surface->w / 3), int(surface->h / 3)});
bloon->addComponent<Range>(std::max(surface->w / 6, surface->h / 6));
layers[BLOONS_LAYER].emplace_back(bloon);
}
}
}
diff --git a/src/systems/CashSystem.cpp b/src/systems/CashSystem.cpp
new file mode 100644
index 0000000..a8f56d9
--- /dev/null
+++ b/src/systems/CashSystem.cpp
@@ -0,0 +1,21 @@
+//
+// Created by Ido Mozes on 17/07/2019.
+//
+
+#include "CashSystem.h"
+
+void CashSystem::update(Entities *layers, GameData &gameData) {
+ for (auto &entity: layers[MENU_LAYER]) {
+ if (auto components = entity->getComponents<Cost, Action, Visibility>()) {
+ auto[cost, action, visibility] = components.value();
+ if (action.actionType == DRAG or action.actionType == CLICK) {
+ bool disabled = gameData.cash < cost.value;
+ if(disabled != action.disabled){
+ action.disabled = disabled;
+ visibility.loadTexture(gameData.renderer,gameData.assets[getSurfaceName(entity) + (disabled? "Disabled" : "")]);
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/systems/CashSystem.h b/src/systems/CashSystem.h
new file mode 100644
index 0000000..0ee6c99
--- /dev/null
+++ b/src/systems/CashSystem.h
@@ -0,0 +1,12 @@
+//
+// Created by Ido Mozes on 17/07/2019.
+//
+
+#ifndef SDL_GAME_CASHSYSTEM_H
+#define SDL_GAME_CASHSYSTEM_H
+#include "../System.h"
+class CashSystem : public System {
+public:
+ void update(Entities *layers, GameData &gameData) override;
+};
+#endif //SDL_GAME_CASHSYSTEM_H
diff --git a/src/systems/CollisionSystem.cpp b/src/systems/CollisionSystem.cpp
index ec94d66..49cffab 100644
--- a/src/systems/CollisionSystem.cpp
+++ b/src/systems/CollisionSystem.cpp
@@ -1,117 +1,164 @@
//
// 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] = shot->getComponents<Pierce, Damage>().value();
- if (pierce.value > 0) {
- bloon->addComponent<DamageEvent>(damage.value,shot);
- if (--pierce.value == 0)
+ auto[pierce, damage, kind, position] = shot->getComponents<Pierce, Damage, Kind, Position>().value();
+ int bloonKind = bloon->getComponent<Kind>()->value;
+ if (kind.value == EXPLOSION or kind.value == GOO_SPLASH or pierce.value > 0) {
+ switch (kind.value) {
+ case DART:
+ if (bloonKind != LEAD_BLOON)
+ bloon->addComponent<DamageEvent>(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 (!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);
+ }
+ break;
+ }
+ case BOMB:
+ case GOO_SHOT:
+ 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>(1);
+ 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 kind.value == GOO_SPLASH 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 3f327be..31a2061 100644
--- a/src/systems/DamageSystem.cpp
+++ b/src/systems/DamageSystem.cpp
@@ -1,208 +1,205 @@
//
// 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, BloonKinds kind, EntityP &shot, int lives, float progress,
- bool regrow, bool camo,
- bool fortified, bool gum,
- bool glue, bool corrosive) {
+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());
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);
- newBloon->getComponent<PathIndex>()->progress = progress;
+ newBloon->addComponent<PathIndex>(0, progress);
if (regrow)
- newBloon->addComponent<Regrow>();
+ newBloon->addComponent<Regrow>(regrow);
if (camo)
newBloon->addComponent<Camo>();
if (fortified)
newBloon->addComponent<Fortified>();
- if (gum)
- newBloon->addComponent<Gum>();
- else if (glue)
- newBloon->addComponent<Glue>();
- else if (corrosive)
- newBloon->addComponent<Corrosive>();
+ if (gooP)
+ 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.money += getBloonProperty<YIELD>(bloon);
+ gameData.cash += getBloonProperty<YIELD>(bloon);
bloon->addComponent<RemoveEntityEvent>();
return;
}
auto &kind = bloon->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.money += 1;
+ gameData.cash += 1;
bloon->addComponent<RemoveEntityEvent>();
}
- auto[regrowP, camoP, fortifiedP, glueP, gumP, corrosiveP] = bloon->getComponentsP<Regrow, Camo, Fortified, Glue, Gum, Corrosive>();
+ auto[regrowP, camoP, fortifiedP, gooP] = bloon->getComponentsP<Regrow, Camo, Fortified, Goo>();
switch (kind) {
case RED_BLOON:
- lives -= damage;
+ 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;
- gameData.money += kind - ((fortifiedP ? lives / 2 : lives) - 1);
+ 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, camoP, false,
- gumP, glueP, corrosiveP);
+ gameData.path.size() - 1, progress + 10),
+ regrowP ? regrowP->kind : 0, camoP, false, gooP);
damageBloon(newBloon, shot, 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 - 10) : std::fminf(
- gameData.path.size() - 1, progress + 10), regrowP, camoP, false,
- gumP, glueP, corrosiveP);
+ gameData.path.size() - 1, progress + 10),
+ regrowP ? regrowP->kind : 0, camoP, false,gooP);
damageBloon(newBloon, shot, 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, BloonKinds(kind - 1), shot, lives / 2,
+ EntityP newBloon = spawnBloon(newBloons, gameData, kind - 1, shot, lives / 2,
i == 0 ? std::fmaxf(0, progress - 10) : std::fminf(
- gameData.path.size() - 1, progress + 10), regrowP, camoP, false,
- gumP, glueP, corrosiveP);
+ gameData.path.size() - 1, progress + 10),
+ regrowP ? regrowP->kind : 0, camoP, false,gooP);
damageBloon(newBloon, shot, 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 : BloonKinds(kind - 1),
+ 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, kind == DDT, fortifiedP, false, false, false);
+ kind == DDT ? CERAMIC_BLOON : 0, kind == DDT, fortifiedP, nullptr);
damageBloon(newBloon, shot, 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());
if (shot)
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, 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.money += getBloonProperty<YIELD>(bloon);
+ 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 232a898..2533ca9 100644
--- a/src/systems/EventSystem.cpp
+++ b/src/systems/EventSystem.cpp
@@ -1,137 +1,156 @@
//
// 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();
if (action.disabled or (action.actionType != DROP and gameData.isDragging))
continue;
int entityX, entityY, w, h;
SDL_Rect *dstRect = visibility.getDstRect();
entityX = dstRect->x;
entityY = dstRect->y;
w = dstRect->w;
h = dstRect->h;
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) {
switch (action.actionType) {
case DRAG: {
- auto[type, kind,shotKind, range, attackInterval, pierce, damage, distance] =
- entity->getComponents<Type, Kind,ShotKind, Range, AttackSpeed, Pierce, Damage, Distance>().value();
+ auto[type, kind, shotKind, range, attackInterval, pierce, damage, distance, cost] =
+ entity->getComponents<Type, Kind, ShotKind, Range, AttackSpeed, Pierce, Damage, Distance, Cost>().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);
+ pierce, damage, distance, cost);
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>();
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: {
+ switch (entity->getComponent<Kind>()->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"
+ : "")]);
+ }
+ }
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;
}
}
}
}
}
}
gameData.selected.reset();
entityClicked:
for (int i = 0; i < N_LAYERS; ++i) {
if (!newEntities[i].empty())
layers[i] += newEntities[i];
}
break;
}
default:
break;
}
}
}
diff --git a/src/systems/LoadLevelSystem.cpp b/src/systems/LoadLevelSystem.cpp
new file mode 100644
index 0000000..0dc6705
--- /dev/null
+++ b/src/systems/LoadLevelSystem.cpp
@@ -0,0 +1,29 @@
+//
+// Created by Ido Mozes on 18/07/2019.
+//
+
+#include "LoadLevelSystem.h"
+
+void LoadLevelSystem::update(Entities *layers, GameData &gameData) {
+ if (gameData.levelReady or gameData.level > gameData.finalLevel)
+ return;
+
+ std::string fileName = "../assets/Levels/level" + std::to_string(gameData.level) + ".data";
+ std::ifstream levelFile(fileName, std::ios::binary);
+ Sequence_S sequenceS;
+ while (!levelFile.eof()) {
+ levelFile.read((char *) &sequenceS, 19);
+ auto s = new Entity();
+ s->addComponent<Type>(SEQUENCE_T);
+ s->addComponent<Sequence>(sequenceS.amount, sequenceS.bps, sequenceS.delay);
+ s->addComponent<Kind>(sequenceS.kind);
+ if (sequenceS.regrow)
+ s->addComponent<Regrow>(sequenceS.kind);
+ if (sequenceS.camo)
+ s->addComponent<Camo>();
+ if (sequenceS.fortified)
+ s->addComponent<Fortified>();
+ layers[SEQUENCES_LAYER].emplace_back(s);
+ }
+ gameData.levelReady = true;
+}
diff --git a/src/systems/LoadLevelSystem.h b/src/systems/LoadLevelSystem.h
new file mode 100644
index 0000000..1152096
--- /dev/null
+++ b/src/systems/LoadLevelSystem.h
@@ -0,0 +1,25 @@
+//
+// Created by Ido Mozes on 18/07/2019.
+//
+
+#ifndef SDL_GAME_LOADLEVELSYSTEM_H
+#define SDL_GAME_LOADLEVELSYSTEM_H
+#include "boost/filesystem.hpp"
+#include "../System.h"
+
+struct Sequence_S {
+ int delay;
+ int kind;
+ int amount;
+ int bps;
+ char regrow;
+ char camo;
+ char fortified;
+};
+
+class LoadLevelSystem : public System {
+public:
+ void update(Entities *layers, GameData &gameData) override;
+};
+
+#endif //SDL_GAME_LOADLEVELSYSTEM_H
diff --git a/src/systems/MovementSystem.cpp b/src/systems/MovementSystem.cpp
index 6b1942f..8d45f79 100644
--- a/src/systems/MovementSystem.cpp
+++ b/src/systems/MovementSystem.cpp
@@ -1,95 +1,167 @@
//
// Created by Ido Mozes on 02/07/2019.
//
#include "MovementSystem.h"
enum Directions {
RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP, TOP_RIGHT
};
std::tuple<int, int> getDelta(int direction) {
switch (direction) {
case RIGHT:
return std::make_tuple(1, 0);
case BOTTOM_RIGHT:
return std::make_tuple(1, 1);
case BOTTOM:
return std::make_tuple(0, 1);
case BOTTOM_LEFT:
return std::make_tuple(-1, 1);
case LEFT:
return std::make_tuple(-1, 0);
case TOP_LEFT:
return std::make_tuple(-1, -1);
case TOP:
return std::make_tuple(0, -1);
case TOP_RIGHT:
return std::make_tuple(1, -1);
default:
return std::make_tuple(0, 0);
}
}
void MovementSystem::update(Entities *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
for (auto &entity: layers[i]) {
// Move all entities with Visibility and Position
if (auto components = entity->getComponents<Visibility, Position>()) {
auto[visibility, position]=components.value();
float deltaX = 0, deltaY = 0;
+ auto pathIndexP = entity->getComponent<PathIndex>();
if (auto velocityP = entity->getComponent<Velocity>()) {
auto &velocity = *velocityP;
if (auto accelerationP = entity->getComponent<Acceleration>()) {
auto &acceleration = *accelerationP;
velocity.changeVelocity(acceleration.value.X, acceleration.value.Y);
}
deltaX = velocity.value.X;
deltaY = velocity.value.Y;
if (auto distanceP = entity->getComponent<Distance>()) {
auto &distance = distanceP->value;
auto[alpha, R] = cartesianToPolar(deltaX, deltaY);
if (distance == 0) {
entity->addComponent<RemoveEntityEvent>();
} else if (distance >= R) {
distance -= R;
} else {
std::tie(deltaX, deltaY) = polarToCartesian(alpha, distance);
distance = 0;
}
}
- } else if (auto pathIndexP = entity->getComponent<PathIndex>()) {
+ } else if (pathIndexP) {
+ if (auto regrowP = entity->getComponent<Regrow>()) {
+ auto[lives, kind] = entity->getComponents<Lives, Kind>().value();
+ regrowP->regrowTime -= 1;
+ if (regrowP->regrowTime == 0 and kind.value < regrowP->kind) {
+ switch (kind.value) {
+ case RED_BLOON:
+ case BLUE_BLOON:
+ case GREEN_BLOON:
+ case YELLOW_BLOON:
+ kind.value += 1;
+ break;
+ case PINK_BLOON: {
+ switch (regrowP->kind) {
+ case PURPLE_BLOON:
+ case BLACK_BLOON:
+ case WHITE_BLOON:
+ kind.value = regrowP->kind;
+ break;
+ case LEAD_BLOON:
+ case ZEBRA_BLOON:
+ case RAINBOW_BLOON:
+ case CERAMIC_BLOON:
+ kind.value = BLACK_BLOON;
+ break;
+ }
+ break;
+ }
+ case BLACK_BLOON:
+ if (regrowP->kind == LEAD_BLOON)
+ kind.value = LEAD_BLOON;
+ else
+ kind.value = ZEBRA_BLOON;
+ break;
+ case WHITE_BLOON:
+ kind.value = ZEBRA_BLOON;
+ case ZEBRA_BLOON:
+ case RAINBOW_BLOON:
+ kind.value += 1;
+ break;
+ }
+ lives.value = getBloonProperty<TOTAL_LIVES>(entity);
+ SDL_Surface *surface = gameData.assets[getSurfaceName(entity)];
+ visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
+ visibility.loadTexture(gameData.renderer, surface);
+ entity->getComponent<Range>()->value = std::max(surface->w / 6, surface->h / 6);
+ regrowP->regrowTime = 60;
+ }
+ }
auto &pathIndex = *pathIndexP;
- pathIndex.progress += getSpeed(entity);
+ float speed = getSpeed(entity);
+ if (auto gooP = entity->getComponent<Goo>()) {
+ if (gooP->kind == CORROSIVE and --gooP->timetoRecharge == 0){
+ entity->addComponent<DamageEvent>(1, EntityP(nullptr));
+ gooP->timetoRecharge = 60;
+ }
+ gooP->ttl -= 1;
+ if (gooP->ttl == 0) {
+ entity->removeComponent<Goo>();
+ SDL_Surface *surface = gameData.assets[getSurfaceName(entity)];
+ auto &visibility = *entity->getComponent<Visibility>();
+ visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
+ visibility.loadTexture(gameData.renderer, surface);
+ }
+ }
+ pathIndex.progress += speed;
float deltaIndex = pathIndex.progress - pathIndex.index;
- if(entity->getComponent<Kind>()->value>=MOAB) {
+ if (entity->getComponent<Kind>()->value >= MOAB) {
float angle = 0;
Point tempPosition = position.value;
- for (int j = std::max(0,pathIndex.index-10);
+ for (int j = std::max(0, pathIndex.index - 10);
j < std::min(pathIndex.index + 10, int(gameData.path.size() - 1)); ++j) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[j]);
tempPosition.X += tempDeltaX;
tempPosition.Y += tempDeltaY;
}
- angle = radToDeg(twoPointsAngle( position.value,tempPosition));
- visibility.angle=angle;
+ angle = radToDeg(twoPointsAngle(position.value, tempPosition));
+ visibility.angle = angle;
}
while (deltaIndex >= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2))) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[pathIndex.index]);
deltaX += tempDeltaX;
deltaY += tempDeltaY;
deltaIndex -= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2));
if (pathIndex.index < gameData.path.size() - 1)
pathIndex.index++;
}
}
position.changePosition(deltaX, deltaY);
if (position.value.X > MAP_WIDTH or position.value.X < 0 or position.value.Y > MAP_HEIGHT or
position.value.Y < 0) {
+ if (pathIndexP) {
+ gameData.lives -= entity->getComponent<Lives>()->value;
+ if (gameData.lives <= 0) {
+ gameData.lost = true;
+ gameData.lives = 0;
+ }
+
+ }
entity->addComponent<RemoveEntityEvent>();
}
}
}
}
}
diff --git a/src/systems/RemoveEntitiesSystem.cpp b/src/systems/RemoveEntitiesSystem.cpp
index cb914c4..6ea5f2a 100644
--- a/src/systems/RemoveEntitiesSystem.cpp
+++ b/src/systems/RemoveEntitiesSystem.cpp
@@ -1,23 +1,29 @@
//
// Created by Ido Mozes on 10/07/2019.
//
#include "RemoveEntitiesSystem.h"
void RemoveEntitiesSystem::update(Entities *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
layers[i].erase(
std::remove_if(layers[i].begin(), layers[i].end(), [&layers, &gameData](auto &entity) {
if (auto moveEntityEventP = entity->template getComponent<MoveEntityEvent>()) {
int newLayer = moveEntityEventP->toLayer;
entity->template removeComponent<MoveEntityEvent>();
layers[newLayer].emplace_back(std::move(entity));
}
if (entity and entity->template getComponent<RangeShadow>() and
entity->template getComponent<RangeShadow>()->entity != gameData.selected)
return true;
return !entity or entity->template getComponent<RemoveEntityEvent>();
}),
layers[i].end());
}
+ if (layers[SEQUENCES_LAYER].empty() and layers[BLOONS_LAYER].empty()) {
+ gameData.levelReady = gameData.levelRunning = false;
+ if (gameData.level <= gameData.finalLevel)
+ gameData.level += 1;
+ gameData.playButton->getComponent<Visibility>()->loadTexture(gameData.renderer, gameData.assets["Play"]);
+ }
}
\ No newline at end of file
diff --git a/src/systems/RenderSystem.cpp b/src/systems/RenderSystem.cpp
index bc0b5db..1d38d9f 100644
--- a/src/systems/RenderSystem.cpp
+++ b/src/systems/RenderSystem.cpp
@@ -1,76 +1,91 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "RenderSystem.h"
-
+std::string formatCommas(int num){
+ std::string numWithCommas = std::to_string(num);
+ int insertPosition = numWithCommas.length() - 3;
+ while (insertPosition > 0) {
+ numWithCommas.insert(insertPosition, ",");
+ insertPosition-=3;
+ }
+ return numWithCommas;
+}
void RenderSystem::init(GameData &gameData) {
if (gameData.window != nullptr)
SDL_DestroyWindow(gameData.window);
if (gameData.renderer != nullptr)
SDL_DestroyRenderer(gameData.renderer);
gameData.window = SDL_CreateWindow("BloonsTD", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, /* NOLINT(hicpp-signed-bitwise)*/
int((MAP_WIDTH + SIDEBAR_WIDTH + MENU_WIDTH) * gameData.mapScale),
int(MAP_HEIGHT * gameData.mapScale),
gameData.fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
gameData.renderer = SDL_CreateRenderer(gameData.window, -1, 0);
SDL_SetRenderDrawColor(gameData.renderer, 255, 255, 255, 255);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
gameData.font = FC_CreateFont();
FC_LoadFont(gameData.font, gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
- FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
+ FC_MakeColor(255,255,255, 255), TTF_STYLE_NORMAL);
}
void RenderSystem::update(Entities *layers, GameData &gameData) {
SDL_RenderClear(gameData.renderer);
for (int i = 0; i < N_LAYERS; ++i) {
if (i == SEQUENCES_LAYER)
continue;
for (auto &entity: layers[i]) {
auto rangeShadowP = entity->getComponent<RangeShadow>();
auto ¤tEntity = rangeShadowP ? rangeShadowP->entity : entity;
if (auto visibilityP = currentEntity->getComponent<Visibility>()) {
auto &visibility = *visibilityP;
SDL_Rect *dstRect = visibility.getDstRect();
SDL_Rect newDstRect = {int(dstRect->x * gameData.mapScale), int(dstRect->y * gameData.mapScale),
int(dstRect->w * gameData.mapScale), int(dstRect->h * gameData.mapScale)};
SDL_Point entityCenter;
auto positionP = currentEntity->getComponent<Position>();
if (positionP) {
auto &position = *positionP;
entityCenter.x = (position.value.X + SIDEBAR_WIDTH) * gameData.mapScale;
entityCenter.y = position.value.Y * gameData.mapScale;
newDstRect.x = int((position.value.X + SIDEBAR_WIDTH) * gameData.mapScale - newDstRect.w / 2.0);
newDstRect.y = int(position.value.Y * gameData.mapScale - newDstRect.h / 2.0);
} else {
entityCenter.x = int(dstRect->x * gameData.mapScale + (dstRect->w * gameData.mapScale) / 2.0);
entityCenter.y = int(dstRect->y * gameData.mapScale + (dstRect->h * gameData.mapScale) / 2.0);
}
if (currentEntity != entity) {
auto draggableP = currentEntity->getComponent<Draggable>();
bool isRed = draggableP ? !draggableP->isPlaceable : false;
float range = currentEntity->getComponent<Range>()->value;
filledCircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range* gameData.mapScale, isRed ? 255 : 0, 0, 0,
100);
aacircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range* gameData.mapScale, isRed ? 255 : 0, 0, 0, 150);
}
if (entity == currentEntity) {
SDL_RenderCopyEx(gameData.renderer, visibility.getTexture(), nullptr, &newDstRect, visibility.angle,
nullptr, SDL_FLIP_NONE);
}
}
}
}
- FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 10) * gameData.mapScale, 10 * gameData.mapScale,
- "Cash: %s", std::to_string(gameData.money).c_str());
+ if(gameData.cash > 99999999)
+ gameData.cash = 99999999;
+
+ FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 45) * gameData.mapScale, 14 * gameData.mapScale,
+ formatCommas(gameData.cash).c_str());
+ FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 45) * gameData.mapScale, 36 * gameData.mapScale,
+ std::to_string(gameData.lives).c_str());
+ FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 160) * gameData.mapScale, 22 * gameData.mapScale,
+ "Level: %s",std::to_string(gameData.level).c_str());
SDL_RenderPresent(gameData.renderer);
}
diff --git a/src/systems/ShotsSpawnSystem.cpp b/src/systems/ShotsSpawnSystem.cpp
index 82847cf..b7a6241 100644
--- a/src/systems/ShotsSpawnSystem.cpp
+++ b/src/systems/ShotsSpawnSystem.cpp
@@ -1,82 +1,116 @@
//
// 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;
Entity *closestBloon = nullptr, *firstBloon = nullptr, *lastBloon = 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] = gameEntity->getComponents<Range, Position, PathIndex>().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 (pathIndex.progress > maxProgress) {
maxProgress = pathIndex.progress;
firstBloon = gameEntity.get();
}
}
}
if (closestBloon or firstBloon or lastBloon) {
Entity *target;
switch (strategy.value) {
case CLOSEST:
target = closestBloon;
break;
case FIRST:
target = firstBloon;
break;
case LAST:
target = lastBloon;
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 DART:{
+ switch (shotKind.value) {
+ case BOMB:
+ case GOO_SHOT:
+ case DART: {
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(angle, getSpeed(shot));
shot->addComponent<Velocity>(velocityX, velocityY);
shot->addComponent<Range>(5);
+ if (shotKind.value != DART) {
+ shot->addComponent<Spread>(*entity->getComponent<Spread>());
+ if (auto gooP = entity->getComponent<Goo>())
+ shot->addComponent<Goo>(*gooP);
+ }
shot->addComponents(pierce, damage, distance);
shot->addComponent<PoppedBloons>();
SDL_Surface *surface = gameData.assets[getSurfaceName(shot)];
- shot->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 25},
+ shot->addComponent<Visibility>(gameData.renderer, surface,
+ SDL_Rect{0, 0, surface->w / (shotKind.value == DART ? 2 : 4)},
radToDeg(angle));
layers[SHOTS_LAYER].emplace_back(shot);
break;
}
- case GUN:
- {
- target->addComponent<DamageEvent>(damage.value,EntityP(nullptr));
+ case GUN: {
+ target->addComponent<DamageEvent>(damage.value, EntityP(nullptr));
+ break;
}
+ case RADIAL_DART: {
+ SDL_Surface *surface = gameData.assets["Dart"];
+ for (int j = 0; j < 8; ++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;
+ auto[velocityX, velocityY] = polarToCartesian(angle, getSpeed(shot));
+ shot->addComponent<Velocity>(velocityX, velocityY);
+ shot->addComponent<Range>(5);
+ shot->addComponents(pierce, damage, distance);
+ shot->addComponent<PoppedBloons>();
+ shot->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 2},
+ radToDeg(angle));
+ layers[SHOTS_LAYER].emplace_back(shot);
+ }
+ break;
+ }
}
-
}
- visibility.angle = radToDeg(angle) + 90;
+ if (shotKind.value != RADIAL_DART)
+ 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, 6:34 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42925
Default Alt Text
(94 KB)
Attached To
Mode
R74 BloonsTD
Attached
Detach File
Event Timeline
Log In to Comment