Page MenuHomePhabricator (Chris)

No OneTemporary

Size
94 KB
Referenced Files
None
Subscribers
None
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 &currentEntity = rangeShadowP ? rangeShadowP->entity : entity;
if (auto visibilityP = currentEntity->getComponent<Visibility>()) {
auto &visibility = *visibilityP;
SDL_Rect *dstRect = visibility.getDstRect();
SDL_Rect newDstRect = {int(dstRect->x * gameData.mapScale), int(dstRect->y * gameData.mapScale),
int(dstRect->w * gameData.mapScale), int(dstRect->h * gameData.mapScale)};
SDL_Point entityCenter;
auto positionP = currentEntity->getComponent<Position>();
if (positionP) {
auto &position = *positionP;
entityCenter.x = (position.value.X + SIDEBAR_WIDTH) * gameData.mapScale;
entityCenter.y = position.value.Y * gameData.mapScale;
newDstRect.x = int((position.value.X + SIDEBAR_WIDTH) * gameData.mapScale - newDstRect.w / 2.0);
newDstRect.y = int(position.value.Y * gameData.mapScale - newDstRect.h / 2.0);
} else {
entityCenter.x = int(dstRect->x * gameData.mapScale + (dstRect->w * gameData.mapScale) / 2.0);
entityCenter.y = int(dstRect->y * gameData.mapScale + (dstRect->h * gameData.mapScale) / 2.0);
}
if (currentEntity != entity) {
auto draggableP = currentEntity->getComponent<Draggable>();
bool isRed = draggableP ? !draggableP->isPlaceable : false;
float range = currentEntity->getComponent<Range>()->value;
filledCircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range* gameData.mapScale, isRed ? 255 : 0, 0, 0,
100);
aacircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range* gameData.mapScale, isRed ? 255 : 0, 0, 0, 150);
}
if (entity == currentEntity) {
SDL_RenderCopyEx(gameData.renderer, visibility.getTexture(), nullptr, &newDstRect, visibility.angle,
nullptr, SDL_FLIP_NONE);
}
}
}
}
- 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

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)

Event Timeline