Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F86426
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
136 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99bd018..650a5b5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.14)
project(SDL_Game)
set(CMAKE_CXX_STANDARD 17)
include_directories(include)
find_package(Boost REQUIRED COMPONENTS filesystem)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(lib)
link_libraries(mingw32 SDL2main SDL2 SDL2_image SDL2_gfx SDL2_ttf)
-add_executable(SDL_Game src/SDL_FontCache.c src/SDL_FontCache.h src/main.cpp src/Game.cpp src/Game.h src/Entity.h src/Component.h src/System.h src/systems/RenderSystem.cpp src/systems/RenderSystem.h src/components/Visibility.cpp src/components/Visibility.h src/components/Velocity.cpp src/components/Velocity.h src/systems/MovementSystem.cpp src/systems/MovementSystem.h src/components/Position.h src/components/PathIndex.h src/GameData.h src/components/Sequence.cpp src/components/Sequence.h src/systems/BloonsSpawnSystem.cpp src/systems/BloonsSpawnSystem.h src/GameData.cpp src/components/Action.h src/systems/EventSystem.cpp src/systems/EventSystem.h src/components/Draggable.h src/systems/DraggingSystem.cpp src/systems/DraggingSystem.h src/Settings.h src/systems/RemoveEntitiesSystem.cpp src/systems/RemoveEntitiesSystem.h src/components/MoveEntityEvent.h src/components/RangeShadow.h src/Physics.cpp src/Physics.h src/systems/CollisionSystem.cpp src/systems/CollisionSystem.h src/systems/ShotsSpawnSystem.cpp src/systems/ShotsSpawnSystem.h src/components/IntegerComponents.h src/components/FloatComponents.h src/components/AttackSpeed.h src/components/AttackSpeed.cpp src/components/PoppedBloons.h src/systems/DamageSystem.cpp src/systems/DamageSystem.h src/components/FlagComponents.h src/System.cpp src/Entity.cpp src/components/DamageEvent.h src/systems/CashSystem.cpp src/systems/CashSystem.h src/components/Regrow.h src/systems/LoadLevelSystem.cpp src/systems/LoadLevelSystem.h src/components/Goo.h)
+add_executable(SDL_Game src/SDL_FontCache.c src/SDL_FontCache.h src/main.cpp src/Game.cpp src/Game.h src/Entity.h src/Component.h src/System.h src/systems/RenderSystem.cpp src/systems/RenderSystem.h src/components/Visibility.cpp src/components/Visibility.h src/components/Velocity.cpp src/components/Velocity.h src/systems/MovementSystem.cpp src/systems/MovementSystem.h src/components/Position.h src/components/PathIndex.h src/GameData.h src/components/Sequence.cpp src/components/Sequence.h src/systems/BloonsSpawnSystem.cpp src/systems/BloonsSpawnSystem.h src/GameData.cpp src/components/Action.h src/systems/EventSystem.cpp src/systems/EventSystem.h src/components/Draggable.h src/systems/DraggingSystem.cpp src/systems/DraggingSystem.h src/Settings.h src/systems/RemoveEntitiesSystem.cpp src/systems/RemoveEntitiesSystem.h src/components/MoveEntityEvent.h src/components/RangeShadow.h src/Physics.cpp src/Physics.h src/systems/CollisionSystem.cpp src/systems/CollisionSystem.h src/systems/ShotsSpawnSystem.cpp src/systems/ShotsSpawnSystem.h src/components/IntegerComponents.h src/components/FloatComponents.h src/components/AttackSpeed.h src/components/AttackSpeed.cpp src/components/PoppedBloons.h src/systems/DamageSystem.cpp src/systems/DamageSystem.h src/components/FlagComponents.h src/System.cpp src/Entity.cpp src/components/DamageEvent.h src/systems/MenuSystem.cpp src/systems/MenuSystem.h src/components/Regrow.h src/systems/LoadLevelSystem.cpp src/systems/LoadLevelSystem.h src/components/Goo.h src/components/Upgrades.h)
target_link_libraries(SDL_Game ${Boost_LIBRARIES})
\ No newline at end of file
diff --git a/scripts/convert_levels.py b/scripts/convert_levels.py
index 5927536..cc9ab06 100644
--- a/scripts/convert_levels.py
+++ b/scripts/convert_levels.py
@@ -1,42 +1,42 @@
from struct import pack
i = 1
kinds = {
'red': 0,
'blue': 1,
'green': 2,
'yellow': 3,
'pink': 4,
'purple': 5,
'white': 6,
'black': 7,
'lead': 8,
'zebra': 9,
'rainbow': 10,
'ceramic': 11,
'moab': 12,
'bfb': 13,
'zomg': 14,
'ddt': 15,
'bad': 16
}
while True:
try:
- level = open(f"assets/Levels/level{i}.csv").read().split('\n')
+ level = open(f"assets/Levels/level{i}.csv").read().split('\n')[1:]
with open(f'assets/Levels/level{i}.data', 'wb') as out_file:
for sequence in level:
delay, kind, amount, bps = sequence.split(',')
delay = int(delay)
amount = int(amount)
- bps = int(bps)
+ bps = float(bps)
kind = kind.split(' ')
out_file.write(pack('<I', delay))
out_file.write(pack('<I', kinds[kind[0]]))
out_file.write(pack('<I', amount))
- out_file.write(pack('<I', bps))
+ out_file.write(pack('<f', bps))
out_file.write(bytes([len(kind) > 1 and 'r' in kind[1]]))
out_file.write(bytes([len(kind) > 1 and 'c' in kind[1]]))
out_file.write(bytes([len(kind) > 1 and 'f' in kind[1]]))
except FileNotFoundError:
break
i += 1
diff --git a/scripts/convert_towers.py b/scripts/convert_towers.py
new file mode 100644
index 0000000..3638da2
--- /dev/null
+++ b/scripts/convert_towers.py
@@ -0,0 +1,75 @@
+from struct import pack
+import json
+
+components = [
+ 'VISIBILITY',
+ 'POSITION',
+ 'VELOCITY',
+ 'SPEED',
+ 'ATTACK_SPEED',
+ 'ACCELERATION',
+ 'PATH_INDEX',
+ 'LIVES',
+ 'KIND',
+ 'SHOT_KIND',
+ 'TYPE',
+ 'RANGE',
+ 'DAMAGE',
+ 'PIERCE',
+ 'SPREAD',
+ 'SHOTS_AMOUNT',
+ 'DISTANCE',
+ 'SEQUENCE',
+ 'ACTION',
+ 'DRAGGABLE',
+ 'RANGE_SHADOW',
+ 'STRATEGY',
+ 'REGROW',
+ 'CAMO',
+ 'FORTIFIED',
+ 'GOO',
+ 'COST',
+ 'POPPED_BLOONS',
+ 'UPGRADES',
+ 'UPGRADE_P',
+]
+tower_kinds = [
+ 'DART_MONKEY',
+ 'TACK_SHOOTER',
+ 'SUPER_MONKEY',
+ 'SNIPER_MONKEY',
+ 'BOMB_TOWER',
+ 'GLUE_GUNNER',
+]
+shot_kinds = [
+ 'SPIKE',
+ 'JUGGERNAUT',
+ 'DART',
+ 'RADIAL_DART',
+ 'GUN',
+ 'BOMB',
+ 'EXPLOSION',
+ 'GOO_SHOT',
+ 'GOO_SPLASH',
+ 'GLUE',
+ 'GUM',
+ 'CORROSIVE',
+]
+towers = json.load("assets/Towers.json")
+with open(f'assets/Towers.data', 'wb') as out_file:
+ for tower in towers:
+ componentsMask = 0
+ for i, component in enumerate(components):
+ componentsMask |= (2 ** i) if component.lower() in towers[tower] else 0
+
+ # delay = int(delay)
+ # amount = int(amount)
+ # bps = float(bps)
+ # kind = kind.split(' ')
+ # out_file.write(pack('<I', delay))
+ # out_file.write(pack('<I', kinds[kind[0]]))
+ # out_file.write(pack('<I', amount))
+ # out_file.write(pack('<f', bps))
+ # out_file.write(bytes([len(kind) > 1 and 'r' in kind[1]]))
+ # out_file.write(bytes([len(kind) > 1 and 'c' in kind[1]]))
+ # out_file.write(bytes([len(kind) > 1 and 'f' in kind[1]]))
diff --git a/src/Component.h b/src/Component.h
index c4c9470..5ae0038 100644
--- a/src/Component.h
+++ b/src/Component.h
@@ -1,62 +1,65 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#ifndef SDL2_GAME_COMPONENT_H
#define SDL2_GAME_COMPONENT_H
#include <memory>
struct Point {
float X;
float Y;
};
class Entity;
typedef std::shared_ptr<Entity> EntityP;
enum ComponentType {
VISIBILITY,
POSITION,
VELOCITY,
SPEED,
ATTACK_SPEED,
ACCELERATION,
PATH_INDEX,
LIVES,
KIND,
SHOT_KIND,
TYPE,
RANGE,
DAMAGE,
PIERCE,
SPREAD,
+ SHOTS_AMOUNT,
DISTANCE,
SEQUENCE,
ACTION,
DRAGGABLE,
RANGE_SHADOW,
STRATEGY,
REGROW,
CAMO,
FORTIFIED,
GOO,
COST,
POPPED_BLOONS,
+ UPGRADES,
+ UPGRADE_P,
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/Entity.h b/src/Entity.h
index b8102f1..214f751 100644
--- a/src/Entity.h
+++ b/src/Entity.h
@@ -1,75 +1,76 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#ifndef SDL2_GAME_ENTITY_H
#define SDL2_GAME_ENTITY_H
#include <bitset>
#include <vector>
+#include <array>
#include <cmath>
#include <memory>
#include <optional>
#include "Component.h"
constexpr uint64_t createMask(std::initializer_list<int> types) noexcept {
uint64_t mask = 0;
for (int bit : types) {
mask |= (uint64_t) pow(2, bit);
}
return mask;
}
class Entity {
std::bitset<ComponentType::LENGTH> componentsMask;
- std::unique_ptr<Component> components[ComponentType::LENGTH];
+ std::array<std::unique_ptr<Component>,ComponentType::LENGTH> components;
public:
template<class ... T>
std::optional<std::tuple<T &...>> getComponents() {
constexpr uint64_t mask = createMask({T::type...});
if ((componentsMask.to_ullong() & mask) == mask) {
return std::tuple<T &...>((*(T *) components[T::type].get())...);
}
return std::nullopt;
}
template<class ... T>
std::tuple<T *...> getComponentsP() {
return std::tuple<T *...>(((T *) components[T::type].get())...);
}
template<class T>
T *getComponent() {
if (auto &c = components[T::type])
return (T *) c.get();
return nullptr;
}
template<typename ... Targs>
void addComponents(Targs &... args) {
std::initializer_list<std::tuple<ComponentType, Component *>> newComponents{
std::make_tuple(Targs::type, new Targs(args))...};
for (auto[t, p] :newComponents) {
componentsMask[t] = true;
components[t] = std::move(std::unique_ptr<Component>(p));
}
}
template<typename T, typename ... Targs>
- Component &addComponent(Targs &&... args) {
+ T &addComponent(Targs &&... args) {
T *c = new T(std::forward<Targs>(args)...);
components[T::type] = std::move(std::unique_ptr<Component>(c));
componentsMask[T::type] = true;
return *c;
}
template<typename T>
void removeComponent() {
components[T::type].reset(nullptr);
componentsMask[T::type] = false;
}
};
#endif //SDL2_GAME_ENTITY_H
diff --git a/src/Game.cpp b/src/Game.cpp
index 12ef694..1dcc1b5 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,251 +1,444 @@
//
// 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["upgrade_bar"] = IMG_Load("../assets/upgrade_bar.png");
gameData.assets["menu"] = IMG_Load("../assets/menu.jpg");
+ gameData.assets["UpgradesBackground"] = IMG_Load("../assets/upgrade_bar_items.png");
renderSystem = new RenderSystem();
renderSystem->init(gameData);
loadMap();
- std::initializer_list<std::tuple<std::string, Point, float>> sprites[]{
- {{"map", {SIDEBAR_WIDTH, 0}, 1}},
+ std::initializer_list<std::tuple<int, std::string, Point, float>> sprites[]{
+ {{MAP, "map", {SIDEBAR_WIDTH, 0}, 1}},
{},
{},
{},
{},
{},
{
- {"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}
+ {OTHER, "upgrade_bar", {0, 0}, 1},
+ {OTHER, "menu", {MAP_WIDTH + SIDEBAR_WIDTH, 0}, 1},
+ {OTHER, "Cash", {MAP_WIDTH + SIDEBAR_WIDTH + 20, 9}, 0.6},
+ {OTHER, "Lives", {MAP_WIDTH + SIDEBAR_WIDTH + 20, 31}, 0.6},
+ {UPGRADES_BACKGROUND, "UpgradesBackground", {0, 0}, 1}
},
};
for (int i = 0; i < N_LAYERS; i++) {
- for (auto[surfaceName, position, scale] :sprites[i]) {
+ for (auto[kind, surfaceName, position, scale] :sprites[i]) {
auto spriteEntity = new Entity();
SDL_Surface *surface = gameData.assets[surfaceName];
spriteEntity->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{int(position.X), int(position.Y), int(surface->w * scale)});
+ if (kind != OTHER)
+ spriteEntity->addComponent<Kind>(kind);
layers[i].emplace_back(spriteEntity);
}
}
// Buttons
EntityP button(new Entity());
SDL_Surface *surface = gameData.assets["Play"];
Point position{SIDEBAR_WIDTH + MAP_WIDTH + 67, 480};
- button->addComponent<Visibility>(gameData.renderer, surface,
- SDL_Rect{int(position.X - surface->w / 4), int(position.Y - surface->h / 4),
- int(surface->w / 2)});
+ button->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{int(position.X - surface->w / 4),
+ int(position.Y - surface->h / 4),
+ int(surface->w / 2)});
button->addComponent<Action>(CLICK);
button->addComponent<Kind>(PLAY_FAST_FORWARD);
layers[MENU_LAYER].emplace_back(button);
gameData.playButton = button;
+ auto nextStrategyButton = new Entity();
+ surface = gameData.assets["RightArrow"];
+ position = {116, 21};
+ nextStrategyButton->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{int(position.X - 7.5),
+ int(position.Y - 7.5), 15});
+ nextStrategyButton->addComponent<Action>(CLICK);
+ nextStrategyButton->addComponent<Kind>(NEXT_STRATEGY);
+ layers[MENU_LAYER].emplace_back(nextStrategyButton);
+
+ auto previousStrategyButton = new Entity();
+ surface = gameData.assets["LeftArrow"];
+ position = {32, 21};
+ previousStrategyButton->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{int(position.X - 7.5),
+ int(position.Y - 7.5), 15});
+ previousStrategyButton->addComponent<Action>(CLICK);
+ previousStrategyButton->addComponent<Kind>(PREVIOUS_STRATEGY);
+ layers[MENU_LAYER].emplace_back(previousStrategyButton);
+
+ for (int j = 0; j < 3; ++j) {
+ auto upgradePath = new Entity();
+ surface = gameData.assets["LeftArrow"];
+ upgradePath->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, 0});
+ upgradePath->addComponent<Action>(CLICK);
+ upgradePath->addComponent<Kind>(UPGRADE_PATH_1 + j);
+ layers[MENU_LAYER].emplace_back(upgradePath);
+ }
+
+
// Towers
auto tower = new Entity();
tower->addComponent<Kind>(SUPER_MONKEY);
tower->addComponent<ShotKind>(DART);
surface = gameData.assets["SuperMonkey"];
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 85};
tower->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
- tower->addComponent<Range>(100);
- tower->addComponent<Camo>();
+ tower->addComponent<Range>(150);
tower->addComponent<AttackSpeed>(15);
- tower->addComponent<Pierce>(4);
- tower->addComponent<Damage>(50);
- tower->addComponent<Distance>(150);
+ tower->addComponent<Pierce>(1);
+ tower->addComponent<Damage>(1);
+ tower->addComponent<Distance>(170);
tower->addComponent<Type>(TOWER_T);
tower->addComponent<Cost>(3000);
+ auto &superMonkeyUpgrades = tower->addComponent<Upgrades>();
+ std::string image = "SuperMonkey_LaserBlasts";
+ superMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Laser Blasts", 2295, image, gameData.assets[image], {{PIERCE_UPGRADE, 1},
+ {SHOT_KIND_UPGRADE, LASER}}));
+ image = "SuperMonkey_PlasmaVision";
+ superMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Plasma Blasts", 4250, image, gameData.assets[image], {{PIERCE_UPGRADE, 1},
+ {ATTACK_SPEED_UPGRADE, 2},
+ {SHOT_KIND_UPGRADE, PLASMA}}));
+ image = "SuperMonkey_SunAvatar";
+ superMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Sun Avatar", 17850, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 3},
+ {PIERCE_UPGRADE, 3},
+ {SHOT_KIND_UPGRADE, SUN}}));
+
+ image = "SuperMonkey_SuperRange";
+ superMonkeyUpgrades.paths[1].emplace_back(
+ Upgrade("Super Range", 850, image, gameData.assets[image],
+ {{RANGE_UPGRADE, 1.25},
+ {DISTANCE_UPGRADE, 1.25}}));
+ image = "SuperMonkey_EpicRange";
+ superMonkeyUpgrades.paths[1].emplace_back(
+ Upgrade("Epic Range", 1190, image, gameData.assets[image], {{RANGE_UPGRADE, 1.25},
+ {DISTANCE_UPGRADE, 1.25},
+ {PIERCE_UPGRADE, 1}}));
+ image = "SuperMonkey_RoboMonkey";
+ superMonkeyUpgrades.paths[1].emplace_back(
+ Upgrade("Robo Monkey", 7650, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 2},
+ {DAMAGE_UPGRADE, 4}}));
+
+ image = "SuperMonkey_Ultravision";
+ superMonkeyUpgrades.paths[2].emplace_back(
+ Upgrade("Ultravision", 1020, image, gameData.assets[image], {{RANGE_UPGRADE, 1.1},
+ {DISTANCE_UPGRADE, 1.1},
+ {CAMO_UPGRADE, 0}}));
+ superMonkeyUpgrades.selectedPathUpgrades[0] = 1;
+ superMonkeyUpgrades.selectedPathUpgrades[1] = 1;
+ superMonkeyUpgrades.selectedPathUpgrades[2] = 0;
layers[MENU_LAYER].emplace_back(tower);
+
+
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 85};
tower->addComponent<Kind>(SNIPER_MONKEY);
- tower->addComponent<ShotKind>(GUN);
+ tower->addComponent<ShotKind>(BULLET);
surface = gameData.assets["SniperMonkey"];
tower->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(500);
- tower->addComponent<AttackSpeed>(2);
+ tower->addComponent<AttackSpeed>(1);
tower->addComponent<Pierce>(1);
- tower->addComponent<Damage>(3);
- tower->addComponent<Distance>(600);
+ tower->addComponent<Damage>(1);
+ tower->addComponent<Distance>(500);
tower->addComponent<Cost>(200);
tower->addComponent<Type>(TOWER_T);
+ auto &sniperMonkeyUpgrades = tower->addComponent<Upgrades>();
+ image = "SniperMonkey_FullMetalJacket";
+ sniperMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Full Metal Jacket", 295, image, gameData.assets[image], {{DAMAGE_UPGRADE, 3},
+ {SHOT_KIND_UPGRADE, ENHANCED_BULLET}}));
+ image = "SniperMonkey_LargeCalibre";
+ sniperMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Large Calibre", 1275, image, gameData.assets[image], {{DAMAGE_UPGRADE, 3}}));
+ image = "SniperMonkey_DeadlyPrecision";
+ sniperMonkeyUpgrades.paths[0].emplace_back(
+ Upgrade("Deadly Precision", 2250, image, gameData.assets[image], {{DAMAGE_UPGRADE, 11}}));
+
+ image = "SniperMonkey_NightVisionGoggles";
+ sniperMonkeyUpgrades.paths[1].emplace_back(
+ Upgrade("Night Vision Goggles", 225, image, gameData.assets[image], {{CAMO_UPGRADE, 0}}));
+
+ image = "SniperMonkey_FasterFiring";
+ sniperMonkeyUpgrades.paths[2].emplace_back(
+ Upgrade("Faster Firing", 340, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.54}}));
+
+ image = "SniperMonkey_EvenFasterFiring";
+ sniperMonkeyUpgrades.paths[2].emplace_back(
+ Upgrade("Even Faster Firing", 340, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.66}}));
+ image = "SniperMonkey_Semi-Automatic";
+ sniperMonkeyUpgrades.paths[2].emplace_back(
+ Upgrade("Semi-Automatic", 2975, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 3}}));
+ image = "SniperMonkey_FullAutoRifle";
+ sniperMonkeyUpgrades.paths[2].emplace_back(
+ Upgrade("Full Auto Rifle", 4035, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 2},
+ {SHOT_KIND_UPGRADE, ENHANCED_BULLET}}));
+ sniperMonkeyUpgrades.selectedPathUpgrades[0] =3;
+ sniperMonkeyUpgrades.selectedPathUpgrades[1] = 1;
+ sniperMonkeyUpgrades.selectedPathUpgrades[2] = 2;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 141};
tower->addComponent<Kind>(DART_MONKEY);
tower->addComponent<ShotKind>(DART);
surface = gameData.assets["DartMonkey"];
tower->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
- tower->addComponent<Range>(65);
- tower->addComponent<AttackSpeed>(2);
- tower->addComponent<Pierce>(2);
+ tower->addComponent<Range>(100);
+ tower->addComponent<AttackSpeed>(0.95);
+ tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(1);
- tower->addComponent<Distance>(80);
+ tower->addComponent<Distance>(150);
tower->addComponent<Cost>(80);
tower->addComponent<Type>(TOWER_T);
+ auto &upgrades = tower->addComponent<Upgrades>();
+ image = "DartMonkey_SharpShots";
+ upgrades.paths[0].emplace_back(
+ Upgrade("Sharp Shots", 120, image, gameData.assets[image], {{PIERCE_UPGRADE, 1}}));
+ image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
+ upgrades.paths[0].emplace_back(
+ Upgrade("Razor Sharp Shots", 185, image, gameData.assets[image], {{PIERCE_UPGRADE, 2}}));
+ image = "DartMonkey_Spike-O-Pult";
+ upgrades.paths[0].emplace_back(
+ Upgrade("Spike-O-Pult", 225, image, gameData.assets[image], {{DISTANCE_UPGRADE, 6},
+ {ATTACK_SPEED_UPGRADE, 0.76},
+ {SHOT_KIND_UPGRADE, SPIKE},
+ {PIERCE_UPGRADE, 14}}));
+ image = "DartMonkey_Juggernaut";
+ upgrades.paths[0].emplace_back(
+ Upgrade("Juggernaut", 1530, image, gameData.assets[image],
+ {{RANGE_UPGRADE, 1.25},
+ {ATTACK_SPEED_UPGRADE, 1.25},
+ {SHOT_KIND_UPGRADE, JUGGERNAUT},
+ {PIERCE_UPGRADE, 86}}));
+
+ image = "DartMonkey_LongRangeDarts_QuickShots";
+ upgrades.paths[1].emplace_back(
+ Upgrade("Quick Shots", 85, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.15}}));
+ image = "DartMonkey_VeryQuickShots";
+ upgrades.paths[1].emplace_back(
+ Upgrade("Very Quick Shots", 160, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 1.33}}));
+ image = "DartMonkey_TripleDarts";
+ upgrades.paths[1].emplace_back(
+ Upgrade("Triple Shot", 340, image, gameData.assets[image], {{SHOTS_AMOUNT_UPGRADE, 3}}));
+
+ image = "DartMonkey_LongRangeDarts_QuickShots";
+ upgrades.paths[2].emplace_back(
+ Upgrade("Long Range Darts", 75, image, gameData.assets[image], {{DISTANCE_UPGRADE, 1.25},
+ {RANGE_UPGRADE, 1.25}}));
+ image = "DartMonkey_EnhancedEyesight_RazorSharpShots";
+ upgrades.paths[2].emplace_back(
+ Upgrade("Enhanced Eyesight", 170, image, gameData.assets[image], {{DISTANCE_UPGRADE, 1.2},
+ {RANGE_UPGRADE, 1.2},
+ {CAMO_UPGRADE, 0}}));
+ image = "DartMonkey_Crossbow";
+ upgrades.paths[2].emplace_back(
+ Upgrade("Crossbow", 530, image, gameData.assets[image], {{DISTANCE_UPGRADE, 1.16},
+ {RANGE_UPGRADE, 1.16},
+ {PIERCE_UPGRADE, 1},
+ {DAMAGE_UPGRADE, 2}}));
+
+ upgrades.selectedPathUpgrades[0] = 2;
+ upgrades.selectedPathUpgrades[1] = 1;
+ upgrades.selectedPathUpgrades[2] = 1;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 65, 197};
tower->addComponent<Kind>(BOMB_TOWER);
tower->addComponent<ShotKind>(BOMB);
surface = gameData.assets["BombTower"];
tower->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{int(position.X - surface->w / 3), int(position.Y - surface->h / 3),
int(surface->w / 1.5), 0});
tower->addComponent<Action>(DRAG);
tower->addComponent<Range>(70);
tower->addComponent<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<Range>(100);
+ tower->addComponent<AttackSpeed>(0.8);
tower->addComponent<Pierce>(1);
tower->addComponent<Damage>(0);
- tower->addComponent<Distance>(100);
+ tower->addComponent<Distance>(150);
tower->addComponent<Cost>(250);
- tower->addComponent<Spread>(30);
+ tower->addComponent<Spread>(70);
tower->addComponent<Type>(TOWER_T);
- tower->addComponent<Goo>(CORROSIVE,120);
+ tower->addComponent<Goo>(GUM, 120);
+ auto &upgrades2 = tower->addComponent<Upgrades>();
+ image = "GlueGunner_GlueSoak_BiggerGlobs";
+ upgrades2.paths[0].emplace_back(
+ Upgrade("Glue Soak", 170, image, gameData.assets[image], {{GOO_SOAK_UPGRADE, 0}}));
+ image = "GlueGunner_CorrosiveGlue";
+ upgrades2.paths[0].emplace_back(
+ Upgrade("Corrosive Glue", 225, image, gameData.assets[image], {{GOO_KIND_UPGRADE, CORROSIVE}}));
+ image = "GlueGunner_BloonDissolver";
+ upgrades2.paths[0].emplace_back(
+ Upgrade("Bloon Dissolver", 2805, image, gameData.assets[image], {{DAMAGE_UPGRADE, 1},
+ {CORROSIVE_INTERVAL_UPGRADE, 60}}));
+ image = "GlueGunner_BloonLiquefier";
+ upgrades2.paths[0].emplace_back(
+ Upgrade("Bloon Liquefier", 5950, image, gameData.assets[image],
+ {{CORROSIVE_INTERVAL_UPGRADE, 60 / 10.0}}));
+
+ image = "GlueGunner_GlueSoak_BiggerGlobs";
+ upgrades2.paths[1].emplace_back(
+ Upgrade("Bigger Globs", 85, image, gameData.assets[image], {{PIERCE_UPGRADE, 1}}));
+ image = "GlueGunner_GlueSplatter";
+ upgrades2.paths[1].emplace_back(
+ Upgrade("Glue Splatter", 1530, image, gameData.assets[image], {{PIERCE_UPGRADE, 4}}));
+ image = "GlueGunner_GlueHose";
+ upgrades2.paths[1].emplace_back(
+ Upgrade("Glue Hose", 2760, image, gameData.assets[image], {{ATTACK_SPEED_UPGRADE, 3}}));
+
+ image = "GlueGunner_StickierGlue";
+ upgrades2.paths[2].emplace_back(
+ Upgrade("Stickier Glue", 100, image, gameData.assets[image], {{GOO_DURATION_UPGRADE, 240}}));
+ image = "GlueGunner_StrongerGlue";
+ upgrades2.paths[2].emplace_back(
+ Upgrade("Stronger Glue", 340, image, gameData.assets[image], {{GOO_STICKNESS_UPGRADE, 0.3}}));
+ image = "GlueGunner_SuperGlue";
+ upgrades2.paths[2].emplace_back(
+ Upgrade("Super Glue", 2720, image, gameData.assets[image], {{GOO_KIND_UPGRADE, GLUE}}));
+
+ upgrades2.selectedPathUpgrades[0] = 3;
+ upgrades2.selectedPathUpgrades[1] = 2;
+ upgrades2.selectedPathUpgrades[2] = 1;
layers[MENU_LAYER].emplace_back(tower);
tower = new Entity();
position = {SIDEBAR_WIDTH + MAP_WIDTH + 190, 141};
tower->addComponent<Kind>(TACK_SHOOTER);
tower->addComponent<ShotKind>(RADIAL_DART);
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 MenuSystem);
systems.emplace_back(new MovementSystem);
systems.emplace_back(new RemoveEntitiesSystem);
systems.emplace_back(renderSystem);
}
Game::~Game() {
SDL_Quit();
}
void Game::update() {
for (auto &system : systems) {
- system->update(layers, gameData);
+ system->update(layers.begin(), gameData);
}
}
void Game::loadMap() {
std::string fileName = "../assets/map" + std::to_string(gameData.map);
std::ifstream obstaclesFile(fileName + "_obstacles.data", std::ios::binary);
int x = 0, y = 0;
for (int i = 0; i < ceilf(MAP_WIDTH * MAP_HEIGHT / 8.0); ++i) {
unsigned char byte;
obstaclesFile.read((char *) &byte, 1);
for (int j = 0; j < 8; ++j) {
char bit = (byte & 2 << j) >> j;
gameData.mapData[x][y] = bit;
x++;
if (x == MAP_WIDTH) {
x = 0;
y++;
}
if (x * y >= MAP_WIDTH * MAP_HEIGHT)
goto readPathFile;
}
}
readPathFile:
gameData.path.clear();
std::ifstream pathFile(fileName + "_path.data", std::ios::binary);
unsigned int length;
TempPoint startingPoint, finishPoint;
pathFile.read((char *) &startingPoint, 4);
pathFile.read((char *) &length, 4);
gameData.path.resize(length);
pathFile.read(&gameData.path[0], length);
pathFile.read((char *) &finishPoint, 4);
gameData.startingPoint = {float(startingPoint.X), float(startingPoint.Y)};
gameData.finishPoint = {float(finishPoint.X), float(finishPoint.Y)};
}
diff --git a/src/Game.h b/src/Game.h
index 0d6c885..a09eb90 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,57 +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/MenuSystem.h"
#include "systems/LoadLevelSystem.h"
#include "GameData.h"
#include "boost/filesystem.hpp"
#include <iostream>
struct TempPoint {
short X;
short Y;
};
class Game {
std::vector<std::unique_ptr<System>> systems;
- Entities layers[N_LAYERS];
+ std::array<Entities, N_LAYERS> layers;
RenderSystem *renderSystem;
public:
GameData gameData;
explicit Game(bool fullscreen, float mapScale = 1.5);
~Game();
void update();
bool running() { return gameData.isRunning; }
void loadMap();
};
#endif //SDL2_GAME_GAME_H
diff --git a/src/GameData.cpp b/src/GameData.cpp
index 1a19385..b5d5769 100644
--- a/src/GameData.cpp
+++ b/src/GameData.cpp
@@ -1,13 +1,16 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#include "GameData.h"
GameData::~GameData() {
for(auto & item:assets){
SDL_FreeSurface(item.second);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
- FC_FreeFont(font);
+ for (int i = 0; i < FONTS_LENGTH; ++i) {
+ FC_FreeFont(fonts[i]);
+ }
+
}
diff --git a/src/GameData.h b/src/GameData.h
index 6a3852d..85c4659 100644
--- a/src/GameData.h
+++ b/src/GameData.h
@@ -1,49 +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 cash = 1000;
+ int cash = 100000;
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;
+ FC_Font* fonts[FONTS_LENGTH];
EntityP playButton;
~GameData();
};
#endif //SDL_GAME_GAMEDATA_H
diff --git a/src/Physics.h b/src/Physics.h
index a356ecc..4539f49 100644
--- a/src/Physics.h
+++ b/src/Physics.h
@@ -1,35 +1,38 @@
//
// Created by Ido Mozes on 11/07/2019.
//
#ifndef SDL_GAME_PHYSICS_H
#define SDL_GAME_PHYSICS_H
#include <tuple>
#include <cmath>
#include "Component.h"
inline std::tuple<float, float> cartesianToPolar(float X, float Y) {
float alpha = atan2f(Y, X);
float R = sqrtf(powf(X, 2) + powf(Y, 2));
return std::make_tuple(alpha, R);
}
inline std::tuple<float, float> polarToCartesian(float alpha, float R) {
return std::make_tuple(R * cosf(alpha), R * sinf(alpha));
}
inline float twoPointsDistance(Point point1, Point point2) {
return sqrtf(powf(point1.X - point2.X, 2) +
powf(point1.Y - point2.Y, 2));
}
inline float twoPointsAngle(Point point1, Point point2) {
return atan2f(point2.Y - point1.Y, point2.X - point1.X);
}
inline float radToDeg(float radians) {
return radians * (180.0 / 3.141592653589793238463);
}
+inline float degToRad(float deg) {
+ return deg / (180.0 / 3.141592653589793238463);
+}
#endif //SDL_GAME_PHYSICS_H
diff --git a/src/Settings.h b/src/Settings.h
index 8359337..e4fca4d 100644
--- a/src/Settings.h
+++ b/src/Settings.h
@@ -1,16 +1,21 @@
//
// Created by Ido Mozes on 09/07/2019.
//
#ifndef SDL_GAME_SETTINGS_H
#define SDL_GAME_SETTINGS_H
enum Layers {
BACKGROUND_LAYER, SEQUENCES_LAYER, SHADOW_LAYER, SHOTS_LAYER, TOWERS_LAYER, BLOONS_LAYER, MENU_LAYER,
N_LAYERS
};
+enum fonts{
+ WHITE12,BLACK12,RED12,
+ WHITE8,BLACK8,RED8,
+ FONTS_LENGTH
+};
constexpr int SIDEBAR_WIDTH = 150;
constexpr int MENU_WIDTH = 250;
constexpr int MAP_WIDTH = 686;
constexpr int MAP_HEIGHT = 511;
constexpr float BPS = 1 / 3.0;
#endif //SDL_GAME_SETTINGS_H
diff --git a/src/System.cpp b/src/System.cpp
index aec9e45..810a7b6 100644
--- a/src/System.cpp
+++ b/src/System.cpp
@@ -1,245 +1,267 @@
//
// Created by Ido Mozes on 15/07/2019.
//
#include "System.h"
std::string getSurfaceName(EntityP &entity) {
std::string surfaceName;
int damageStateLives = getBloonProperty<SELF_LIVES>(entity) / 5;
switch (entity->getComponent<Type>()->value) {
case BLOON_T: {
auto[regrowP, camoP, fortifiedP, gooP, lives] = entity->getComponentsP<Regrow, Camo, Fortified, Goo, Lives>();
switch (entity->getComponent<Kind>()->value) {
case RED_BLOON:
surfaceName = "Red";
break;
case BLUE_BLOON:
surfaceName = "Blue";
break;
case GREEN_BLOON:
surfaceName = "Green";
break;
case YELLOW_BLOON:
surfaceName = "Yellow";
break;
case PINK_BLOON:
surfaceName = "Pink";
break;
case PURPLE_BLOON:
surfaceName = "Purple";
break;
case WHITE_BLOON:
surfaceName = "White";
break;
case BLACK_BLOON:
surfaceName = "Black";
break;
case ZEBRA_BLOON:
surfaceName = "Zebra";
break;
case LEAD_BLOON:
surfaceName = "Lead";
break;
case RAINBOW_BLOON:
surfaceName = "Rainbow";
break;
case CERAMIC_BLOON:
surfaceName = "Ceramic";
break;
case MOAB:
surfaceName = "Moab";
break;
case BFB:
surfaceName = "Bfb";
break;
case DDT:
surfaceName = "Ddt";
break;
case ZOMG:
surfaceName = "Zomg";
break;
case BAD:
surfaceName = "Bad";
break;
}
if (regrowP)
surfaceName += "Regrow";
if (camoP)
surfaceName += "Camo";
if (fortifiedP) {
surfaceName += "Fortified";
}
if (damageStateLives) {
int maxLives = getBloonProperty<TOTAL_LIVES>(entity);
if (int damageState = (maxLives - lives->value) / damageStateLives)
surfaceName += "DamageState" + std::to_string(damageState);
}
if (gooP)
switch (gooP->kind) {
- case GUM:
+ case GLUE:
surfaceName += "Gum";
break;
- case GLUE:
+ case GUM:
surfaceName += "Glue";
break;
case CORROSIVE:
surfaceName += "Slime";
break;
}
break;
}
case TOWER_T: {
switch (entity->getComponent<Kind>()->value) {
case DART_MONKEY:
surfaceName = "DartMonkey";
break;
case TACK_SHOOTER:
surfaceName = "TackShooter";
break;
case SUPER_MONKEY:
surfaceName = "SuperMonkey";
break;
case SNIPER_MONKEY:
surfaceName = "SniperMonkey";
break;
case BOMB_TOWER:
surfaceName = "BombTower";
break;
case GLUE_GUNNER:
surfaceName = "GlueGunner";
break;
}
break;
}
case SHOT_T: {
switch (entity->getComponent<Kind>()->value) {
+ case SPIKE:
+ surfaceName = "Spike";
+ break;
+ case JUGGERNAUT:
+ surfaceName = "Juggernaut";
+ break;
+ case LASER:
+ surfaceName = "Laser";
+ break;
+ case PLASMA:
+ surfaceName = "Plasma";
+ break;
+ case SUN:
+ surfaceName = "Sun";
+ break;
case DART:
case RADIAL_DART:
surfaceName = "Dart";
break;
case BOMB:
surfaceName = "Bomb";
break;
case EXPLOSION:
surfaceName = "Explosion";
break;
case GOO_SPLASH:
switch (entity->getComponent<Goo>()->kind) {
- case GUM:
+ case GLUE:
surfaceName = "SplashGum";
break;
- case GLUE:
+ case GUM:
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";
+ surfaceName = "GumShot";
+ break;
+ case GUM:
+ surfaceName = "GlueShot";
break;
case CORROSIVE:
- surfaceName = "SplashSlime";
+ surfaceName = "SlimeShot";
break;
}
break;
}
break;
}
}
return surfaceName;
}
float getSpeed(EntityP &entity) {
float speed;
switch (entity->getComponent<Type>()->value) {
case BLOON_T: {
auto gooP = entity->getComponent<Goo>();
switch (entity->getComponent<Kind>()->value) {
case RED_BLOON:
speed = 3 * BPS;
break;
case BLUE_BLOON:
speed = 4 * BPS;
break;
case GREEN_BLOON:
speed = 5 * BPS;
break;
case YELLOW_BLOON:
speed = 10 * BPS;
break;
case PINK_BLOON:
speed = 11 * BPS;
break;
case PURPLE_BLOON:
speed = 9.5 * BPS;
break;
case WHITE_BLOON:
speed = 6 * BPS;
break;
case BLACK_BLOON:
speed = 5 * BPS;
break;
case ZEBRA_BLOON:
speed = 5 * BPS;
break;
case LEAD_BLOON:
speed = 3 * BPS;
break;
case RAINBOW_BLOON:
speed = 7 * BPS;
break;
case CERAMIC_BLOON:
speed = 8 * BPS;
break;
case MOAB:
speed = 3 * BPS;
break;
case BFB:
speed = 1 * BPS;
break;
case DDT:
speed = 9 * BPS;
break;
case ZOMG:
speed = 0.5 * BPS;
break;
case BAD:
speed = 0.2 * BPS;
break;
}
if (gooP)
switch (gooP->kind) {
case CORROSIVE:
case GUM:
- speed *= 0.5;
+ speed *= gooP->stickness;
break;
case GLUE:
speed = 0;
}
break;
}
case SHOT_T: {
switch (entity->getComponent<Kind>()->value) {
case DART:
case RADIAL_DART:
+ case SPIKE:
+ case JUGGERNAUT:
speed = 12;
break;
+ case LASER:
+ case PLASMA:
+ case SUN:
+ speed = 22;
+ break;
case BOMB:
case GOO_SHOT:
speed = 10;
break;
}
break;
}
}
return speed;
}
diff --git a/src/System.h b/src/System.h
index aa06014..38aae55 100644
--- a/src/System.h
+++ b/src/System.h
@@ -1,155 +1,156 @@
//
// 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"
+#include "components/Upgrades.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 = selfLives = 1;
break;
case BLUE_BLOON:
yield = totalLives = selfLives = 2;
break;
case GREEN_BLOON:
yield = totalLives = selfLives = 3;
break;
case YELLOW_BLOON:
yield = totalLives = selfLives = 4;
break;
case PINK_BLOON:
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 333cc05..02c6206 100644
--- a/src/components/Action.h
+++ b/src/components/Action.h
@@ -1,29 +1,36 @@
//
// 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
+
+enum VisibleObjects {
+ PLAY_FAST_FORWARD,
+ NEXT_STRATEGY,
+ PREVIOUS_STRATEGY,
+ UPGRADE_PATH_1,
+ UPGRADE_PATH_2,
+ UPGRADE_PATH_3,
+ MAP, UPGRADES_BACKGROUND, OTHER
};
+
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) {}
+ Action(ActionType actionType, bool disabled = false) : actionType(actionType), disabled(disabled) {}
};
#endif //SDL_GAME_ACTION_H
diff --git a/src/components/AttackSpeed.h b/src/components/AttackSpeed.h
index 8ffda12..a1c34a9 100644
--- a/src/components/AttackSpeed.h
+++ b/src/components/AttackSpeed.h
@@ -1,22 +1,23 @@
//
// Created by Ido Mozes on 14/07/2019.
//
#ifndef SDL_GAME_ATTACKSPEED_H
#define SDL_GAME_ATTACKSPEED_H
#include <cmath>
#include "../Component.h"
class AttackSpeed : public Component {
- float interval;
- float timeToRecharge = 0;
public:
+ float timeToRecharge = 0;
+ float interval;
static constexpr ComponentType type = ComponentType::ATTACK_SPEED;
explicit AttackSpeed(float amountPerSecond) : interval(60 / amountPerSecond) {}
+
void recharge();
int getAmountReady();
};
#endif //SDL_GAME_ATTACKSPEED_H
diff --git a/src/components/Goo.h b/src/components/Goo.h
index ee99b08..d330d13 100644
--- a/src/components/Goo.h
+++ b/src/components/Goo.h
@@ -1,20 +1,24 @@
//
// 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;
+ int interval;
+ int timetoRecharge;
+ bool soak = false;
+ int damage = 1;
+ float stickness = 0.6;
static constexpr ComponentType type = ComponentType::GOO;
- Goo(int kind, int ttl) : kind(kind), ttl(ttl) {}
+ Goo(int kind, int ttl, int interval=60) : kind(kind), ttl(ttl), timetoRecharge(interval),interval(interval) {}
};
#endif //SDL_GAME_GOO_H
diff --git a/src/components/IntegerComponents.h b/src/components/IntegerComponents.h
index 746d092..44f447c 100644
--- a/src/components/IntegerComponents.h
+++ b/src/components/IntegerComponents.h
@@ -1,74 +1,89 @@
//
// 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{
+enum BloonKinds {
RED_BLOON,
BLUE_BLOON,
GREEN_BLOON,
YELLOW_BLOON,
PINK_BLOON,
PURPLE_BLOON,
WHITE_BLOON,
BLACK_BLOON,
LEAD_BLOON,
ZEBRA_BLOON,
RAINBOW_BLOON,
CERAMIC_BLOON,
MOAB,
BFB,
ZOMG,
DDT,
BAD
};
-enum TowerKinds{
+enum TowerKinds {
DART_MONKEY,
TACK_SHOOTER,
SUPER_MONKEY,
SNIPER_MONKEY,
BOMB_TOWER,
GLUE_GUNNER,
};
-enum ShotKinds{
+enum ShotKinds {
+ SPIKE,
+ JUGGERNAUT,
DART,
RADIAL_DART,
- GUN,
+ BULLET,
+ ENHANCED_BULLET,
BOMB,
EXPLOSION,
GOO_SHOT,
GOO_SPLASH,
GLUE,
GUM,
CORROSIVE,
+ LASER,
+ PLASMA,
+ SUN
};
-INTEGER_COMPONENT(Lives,LIVES);
-INTEGER_COMPONENT(Damage,DAMAGE);
-INTEGER_COMPONENT(Pierce,PIERCE);
-INTEGER_COMPONENT(Type,TYPE);
-INTEGER_COMPONENT(Strategy,STRATEGY);
-INTEGER_COMPONENT(Cost,COST);
-INTEGER_COMPONENT(Kind,KIND);
-INTEGER_COMPONENT(ShotKind,SHOT_KIND);
+INTEGER_COMPONENT(Lives, LIVES);
+
+INTEGER_COMPONENT(Damage, DAMAGE);
+
+INTEGER_COMPONENT(Pierce, PIERCE);
+
+INTEGER_COMPONENT(Type, TYPE);
+
+INTEGER_COMPONENT(Strategy, STRATEGY);
+
+INTEGER_COMPONENT(Cost, COST);
+
+INTEGER_COMPONENT(Kind, KIND);
+
+INTEGER_COMPONENT(ShotKind, SHOT_KIND);
+
+INTEGER_COMPONENT(ShotsAmount, SHOTS_AMOUNT);
#endif //SDL_GAME_INTEGERCOMPONENTS_H
diff --git a/src/components/Upgrades.h b/src/components/Upgrades.h
new file mode 100644
index 0000000..43ef9d9
--- /dev/null
+++ b/src/components/Upgrades.h
@@ -0,0 +1,65 @@
+//
+// Created by Ido Mozes on 20/07/2019.
+//
+
+#ifndef SDL_GAME_UPGRADES_H
+#define SDL_GAME_UPGRADES_H
+
+#include <vector>
+#include <tuple>
+#include <array>
+#include "../Component.h"
+#include "SDL.h"
+
+enum UpgradeTypes {
+ RANGE_UPGRADE,
+ PIERCE_UPGRADE,
+ DISTANCE_UPGRADE,
+ DAMAGE_UPGRADE,
+ ATTACK_SPEED_UPGRADE,
+ CAMO_UPGRADE,
+ GOO_KIND_UPGRADE,
+ GOO_SOAK_UPGRADE,
+ GOO_DURATION_UPGRADE,
+ GOO_STICKNESS_UPGRADE,
+ CORROSIVE_INTERVAL_UPGRADE,
+ CORROSIVE_DAMAGE_UPGRADE,
+ SHOTS_AMOUNT_UPGRADE,
+ SHOT_KIND_UPGRADE
+};
+
+class Upgrade {
+public:
+ std::vector<std::tuple<int, float>> improvements;
+ SDL_Surface *surface;
+ std::string surfaceName;
+ std::string name;
+ int cost;
+ bool locked = false;
+
+ Upgrade(const std::string &name, int cost, const std::string surfaceName, SDL_Surface *surface,
+ std::initializer_list<std::pair<int, float>> improvements) : name(name), cost(cost),
+ surfaceName(surfaceName), surface(surface) {
+ for (auto[kind, value]: improvements)
+ this->improvements.emplace_back(kind, value);
+ }
+};
+
+class UpgradeP : public Component {
+public:
+ Upgrade* value;
+ static constexpr ComponentType type = ComponentType::UPGRADE_P;
+ std::string name;
+ UpgradeP(Upgrade* value) : value(value),name(value->name) {}
+};
+
+class Upgrades : public Component {
+public:
+ std::array<std::vector<Upgrade>,3> paths;
+ int selectedPathUpgrades[3];
+ int chosenPath = -1;
+ static constexpr ComponentType type = ComponentType::UPGRADES;
+
+};
+
+#endif //SDL_GAME_UPGRADES_H
diff --git a/src/components/Visibility.cpp b/src/components/Visibility.cpp
index 8f61c06..dac7a28 100644
--- a/src/components/Visibility.cpp
+++ b/src/components/Visibility.cpp
@@ -1,38 +1,40 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "Visibility.h"
-Visibility::Visibility(SDL_Renderer *renderer, SDL_Surface *newSurface, std::optional<SDL_Rect> dstR, float angle) : dstRect(dstR), angle(angle){
+Visibility::Visibility(SDL_Renderer *renderer, SDL_Surface *newSurface, std::optional<SDL_Rect> dstR, float angle, bool hidden)
+ : dstRect(dstR), angle(angle), hidden(hidden) {
loadTexture(renderer, newSurface);
}
Visibility::~Visibility() {
SDL_DestroyTexture(texture);
}
void Visibility::loadTexture(SDL_Renderer *renderer, SDL_Surface *newSurface) {
surface = newSurface;
reloadTexture(renderer);
if (dstRect) {
if (dstRect->w == 0)
dstRect->w = int((float(surface->w) / surface->h) * dstRect->h);
else if (dstRect->h == 0)
dstRect->h = int((float(surface->h) / surface->w) * dstRect->w);
}
}
void Visibility::reloadTexture(SDL_Renderer *renderer) {
+ SDL_DestroyTexture(texture);
texture = SDL_CreateTextureFromSurface(renderer, surface);
}
void Visibility::setPosition(int x, int y) {
if (dstRect) {
dstRect->x = x;
dstRect->y = y;
}
}
diff --git a/src/components/Visibility.h b/src/components/Visibility.h
index 37ab80c..20cc7ab 100644
--- a/src/components/Visibility.h
+++ b/src/components/Visibility.h
@@ -1,46 +1,47 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#ifndef SDL2_GAME_VISIBILITY_H
#define SDL2_GAME_VISIBILITY_H
#include <fstream>
#include <optional>
#include "SDL.h"
#include "SDL_image.h"
#include "../Component.h"
SDL_Texture *loadTexture(SDL_Renderer *renderer, const char *path);
class Visibility : public Component {
SDL_Surface *surface = nullptr;
SDL_Texture *texture = nullptr;
std::optional<SDL_Rect> dstRect;
public:
float angle = 0;
+ bool hidden = false;
static constexpr ComponentType type = ComponentType::VISIBILITY;
Visibility(SDL_Renderer *renderer, SDL_Surface *newSurface, std::optional<SDL_Rect> dstR = std::nullopt,
- float angle = 0);
+ float angle = 0,bool hidden = false);
~Visibility() override;
SDL_Texture *getTexture() { return texture; }
SDL_Rect *getDstRect() { return dstRect ? &dstRect.value() : nullptr; }
void setDstRect(SDL_Rect newDstRect) {dstRect.value()= newDstRect;}
void setPosition(int x, int y);
void loadTexture(SDL_Renderer *renderer, SDL_Surface *newSurface);
void reloadTexture(SDL_Renderer *renderer);
};
#endif //SDL2_GAME_VISIBILITY_H
diff --git a/src/systems/CashSystem.cpp b/src/systems/CashSystem.cpp
deleted file mode 100644
index a8f56d9..0000000
--- a/src/systems/CashSystem.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// 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/CollisionSystem.cpp b/src/systems/CollisionSystem.cpp
index 49cffab..ad38dc3 100644
--- a/src/systems/CollisionSystem.cpp
+++ b/src/systems/CollisionSystem.cpp
@@ -1,164 +1,184 @@
//
// Created by Ido Mozes on 11/07/2019.
//
#include "CollisionSystem.h"
inline float getLeft(EntityP &entity) {
return entity->getComponent<Position>()->value.X - entity->getComponent<Range>()->value;
}
int binarySearch(Entities &layer, float left, int low, int high) {
if (high <= low)
return left > getLeft(layer[low]) ? (low + 1) : low;
int mid = (low + high) / 2;
if (left == getLeft(layer[mid]))
return mid + 1;
if (left > getLeft(layer[mid]))
return binarySearch(layer, left, mid + 1, high);
return binarySearch(layer, left, low, mid - 1);
}
int
binarySearch(std::vector<std::tuple<EntityP, EntityP, float>> collided, float distance,
int low, int high) {
if (high <= low)
return distance > std::get<2>(collided[low]) ? (low + 1) : low;
int mid = (low + high) / 2;
if (distance == std::get<2>(collided[mid]))
return mid + 1;
if (distance > std::get<2>(collided[mid]))
return binarySearch(collided, distance, mid + 1, high);
return binarySearch(collided, distance, low, mid - 1);
}
void insertionSort(Entities &layer) {
// can be optimized with binary search instead of comparison to every item
for (int i = 1, j = 0; i < layer.size(); j = i++) {
auto &entity = layer[i];
EntityP entityP(entity);
auto &previousEntity = layer[i - 1];
auto[position, range] = entity->getComponents<Position, Range>().value();
float entityLeft = position.value.X - range.value;
if (entityLeft >= getLeft(layer[j]))
continue;
int loc = binarySearch(layer, entityLeft, 0, j);
while (j >= loc) {
layer[j + 1] = layer[j];
--j;
}
if (j != i - 1) {
layer[j + 1] = entityP;
}
}
}
void CollisionSystem::update(Entities *layers, GameData &gameData) {
if (!gameData.levelRunning)
return;
Entities &bloonsLayer = layers[BLOONS_LAYER];
Entities &shotsLayer = layers[SHOTS_LAYER];
insertionSort(bloonsLayer);
insertionSort(shotsLayer);
std::vector<std::tuple<EntityP, EntityP, float>> collided;
int bloonIndex = 0;
for (auto &shot : shotsLayer) {
int collidedIndex = collided.size();
auto[shotPosition, poppedBloons] = shot->getComponents<Position, PoppedBloons>().value();
auto[shotX, shotY] = shotPosition.value;
auto shotRange = shot->getComponent<Range>()->value;
float shotLeft = shotX - shotRange;
float shotRight = shotX + shotRange;
float shotTop = shotY - shotRange;
float shotBottom = shotY + shotRange;
for (int i = bloonIndex; i < bloonsLayer.size(); ++i) {
auto &bloon = bloonsLayer[i];
auto bloonPosition = bloon->getComponent<Position>()->value;
auto[bloonX, bloonY] = bloonPosition;
auto bloonRange = bloon->getComponent<Range>()->value;
float bloonLeft = bloonX - bloonRange;
float bloonRight = bloonX + bloonRange;
float bloonTop = bloonY - bloonRange;
float bloonBottom = bloonY + bloonRange;
if (bloonRight < shotLeft) {
bloonIndex++;
continue;
}
if (shotRight < bloonLeft)
break;
if (shotBottom >= bloonTop and shotTop <= bloonBottom and !poppedBloons.value.count(bloon.get())) {
poppedBloons.value.emplace(bloon.get());
float distance = twoPointsDistance(shotPosition.value, bloonPosition);
if (collidedIndex == collided.size())
collided.emplace_back(shot, bloon, distance);
else {
int index = binarySearch(collided, distance, collidedIndex, collided.size() - 1);
if (index < collided.size())
collided.emplace(collided.begin() + index, shot, bloon, distance);
else
collided.emplace_back(shot, bloon, distance);
}
}
}
}
for (auto[shot, bloon, _] :collided) {
auto[pierce, damage, kind, position] = shot->getComponents<Pierce, Damage, Kind, Position>().value();
+ if (bloon->getComponent<Camo>() and !shot->getComponent<Camo>())
+ continue;
int bloonKind = bloon->getComponent<Kind>()->value;
- if (kind.value == EXPLOSION or kind.value == GOO_SPLASH or pierce.value > 0) {
+ if (kind.value == EXPLOSION or pierce.value > 0) {
switch (kind.value) {
+ case LASER:
+ if (bloonKind != PURPLE_BLOON and bloonKind != LEAD_BLOON)
+ bloon->addComponent<DamageEvent>(damage.value, shot);
+ break;
+ case PLASMA:
+ if (bloonKind != PURPLE_BLOON)
+ bloon->addComponent<DamageEvent>(damage.value, shot);
+ break;
+ case SUN:
+ bloon->addComponent<DamageEvent>(damage.value, shot);
+ break;
case DART:
+ case SPIKE:
if (bloonKind != LEAD_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
+ case JUGGERNAUT:
+ bloon->addComponent<DamageEvent>(bloonKind == CERAMIC_BLOON ? 5 : damage.value, shot);
+ break;
case EXPLOSION:
if (bloonKind != BLACK_BLOON and bloonKind != ZEBRA_BLOON)
bloon->addComponent<DamageEvent>(damage.value, shot);
break;
case GOO_SPLASH: {
bloon->addComponent<DamageEvent>(damage.value, shot);
- if (!bloon->getComponent<Goo>()) {
+ if (bloonKind < MOAB and !bloon->getComponent<Goo>()) {
bloon->addComponent<Goo>(*shot->getComponent<Goo>());
SDL_Surface *surface = gameData.assets[getSurfaceName(bloon)];
auto &visibility = *bloon->getComponent<Visibility>();
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.loadTexture(gameData.renderer, surface);
}
+ shot->addComponent<RemoveEntityEvent>();
break;
}
case BOMB:
case GOO_SHOT:
+ if (shot->getComponent<RemoveEntityEvent>())
+ break;
EntityP explosion(new Entity());
explosion->addComponent<Position>(position.value.X, position.value.Y);
explosion->addComponent<Type>(SHOT_T);
switch (kind.value) {
case BOMB:
explosion->addComponent<Kind>(EXPLOSION);
break;
case GOO_SHOT:
explosion->addComponent<Kind>(GOO_SPLASH);
explosion->addComponent<Goo>(*shot->getComponent<Goo>());
break;
}
- explosion->addComponent<Pierce>(1);
+ explosion->addComponent<Pierce>(pierce);
explosion->addComponent<PoppedBloons>();
explosion->addComponent<Range>(shot->getComponent<Spread>()->value);
explosion->addComponents(damage);
SDL_Surface *surface = gameData.assets[getSurfaceName(explosion)];
explosion->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 2});
layers[SHOTS_LAYER].emplace_back(explosion);
shot->addComponent<RemoveEntityEvent>();
break;
}
- if (kind.value == EXPLOSION or kind.value == GOO_SPLASH or --pierce.value == 0)
+ if (kind.value == EXPLOSION or --pierce.value == 0)
shot->addComponent<RemoveEntityEvent>();
}
}
}
\ No newline at end of file
diff --git a/src/systems/DamageSystem.cpp b/src/systems/DamageSystem.cpp
index 31a2061..82e8725 100644
--- a/src/systems/DamageSystem.cpp
+++ b/src/systems/DamageSystem.cpp
@@ -1,205 +1,217 @@
//
// Created by Ido Mozes on 15/07/2019.
//
#include "DamageSystem.h"
bool didBloonPop(EntityP &bloon, int &lives, int &damage) {
if (bloon->getComponent<Fortified>()) {
if (damage == 1) {
lives -= damage;
if (lives % 2 == 1)
return false;
} else {
lives -= 2;
damage -= 2;
}
} else {
lives -= 1;
damage -= 1;
}
return true;
}
bool didBloonPop(EntityP &bloon, int &lives, int &damage, int minLives) {
if (lives - damage >= minLives) {
lives -= damage;
return false;
}
damage -= lives - minLives + 1;
lives = minLives - 1;
return true;
}
EntityP
spawnBloon(Entities &newBloons, GameData &gameData, int kind, EntityP &shot, int lives, float progress,
- int regrow, bool camo, bool fortified, Goo* gooP) {
+ int regrow, bool camo, bool fortified, Goo *gooP) {
EntityP newBloon(new Entity());
- if (shot)
- shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
+ shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
newBloon->addComponent<Type>(BLOON_T);
newBloon->addComponent<Kind>(kind);
newBloon->addComponent<Lives>(lives);
newBloon->addComponent<Position>(gameData.startingPoint);
newBloon->addComponent<PathIndex>(0, progress);
if (regrow)
newBloon->addComponent<Regrow>(regrow);
if (camo)
newBloon->addComponent<Camo>();
if (fortified)
newBloon->addComponent<Fortified>();
- if (gooP)
+ if (gooP and gooP->soak)
newBloon->addComponent<Goo>(*gooP);
SDL_Surface *surface = gameData.assets[getSurfaceName(newBloon)];
newBloon->addComponent<Range>(std::max(surface->w / 6, surface->h / 6));
newBloon->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 3, 0});
newBloons.emplace_back(newBloon);
return newBloon;
}
void damageBloon(EntityP &bloon, EntityP &shot, int damage, GameData &gameData, Entities &newBloons) {
if (damage == 0)
return;
auto &lives = bloon->getComponent<Lives>()->value;
if (damage >= lives) {
gameData.cash += getBloonProperty<YIELD>(bloon);
bloon->addComponent<RemoveEntityEvent>();
return;
}
auto &kind = bloon->getComponent<Kind>()->value;
+ auto &shotKind = shot->getComponent<Kind>()->value;
float progress = bloon->getComponent<PathIndex>()->progress;
SDL_Surface *surface;
auto &visibility = *bloon->getComponent<Visibility>();
if (kind > PINK_BLOON) {
if (kind >= CERAMIC_BLOON and !didBloonPop(bloon, lives, damage, getBloonProperty<MIN_LIVES>(bloon))) {
surface = gameData.assets[getSurfaceName(bloon)];
visibility.loadTexture(gameData.renderer, surface);
return;
}
if (kind < CERAMIC_BLOON and !didBloonPop(bloon, lives, damage))
return;
}
if (kind > PINK_BLOON) {
gameData.cash += 1;
bloon->addComponent<RemoveEntityEvent>();
}
- auto[regrowP, camoP, fortifiedP, gooP] = bloon->getComponentsP<Regrow, Camo, Fortified, Goo>();
+ auto[regrowP, camoP, fortifiedP, gooP] = bloon->getComponentsP<Regrow, Camo, Fortified, Goo>();
switch (kind) {
case RED_BLOON:
lives -= 1;
break;
case BLUE_BLOON:
case GREEN_BLOON:
case YELLOW_BLOON:
case PINK_BLOON: {
lives -= damage;
if (fortifiedP and damage == 1 and lives % 2 == 1)
break;
+ if (gooP and !gooP->soak)
+ bloon->removeComponent<Goo>();
gameData.cash += kind - ((fortifiedP ? lives / 2 : lives) - 1);
kind = (fortifiedP ? lives / 2 : lives) - 1;
surface = gameData.assets[getSurfaceName(bloon)];
visibility.setDstRect(SDL_Rect{0, 0, surface->w / 3, 0});
visibility.loadTexture(gameData.renderer, surface);
if (regrowP)
regrowP->regrowTime = 60;
bloon->getComponent<Range>()->value = std::max(surface->w / 6, surface->h / 6);
break;
}
case PURPLE_BLOON:
case WHITE_BLOON:
case BLACK_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, PINK_BLOON, shot, lives / 2,
i == 0 ? std::fmaxf(0, progress - 10) : std::fminf(
gameData.path.size() - 1, progress + 10),
regrowP ? regrowP->kind : 0, camoP, false, gooP);
- damageBloon(newBloon, shot, i == 0 ? ceilf(damage / 2.0) : floorf(damage / 2.0), gameData, newBloons);
+ damageBloon(newBloon, shot,
+ shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
+ damage / 2.0)), gameData, newBloons);
}
break;
}
case ZEBRA_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, i == 0 ? BLACK_BLOON : WHITE_BLOON, shot, lives / 2,
- i == 0 ? std::fmaxf(0, progress - 10) : std::fminf(
- 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);
+ i == 0 ? std::fmaxf(0, progress - 12) : std::fminf(
+ gameData.path.size() - 1, progress + 12),
+ regrowP ? regrowP->kind : 0, camoP, false, gooP);
+ damageBloon(newBloon, shot,
+ shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
+ damage / 2.0)), gameData, newBloons);
}
break;
}
case LEAD_BLOON:
case RAINBOW_BLOON:
case CERAMIC_BLOON: {
for (int i = 0; i < 2; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, kind - 1, shot, lives / 2,
- i == 0 ? std::fmaxf(0, progress - 10) : std::fminf(
- 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);
+ i == 0 ? std::fmaxf(0, progress - 14) : std::fminf(
+ gameData.path.size() - 1, progress + 14),
+ regrowP ? regrowP->kind : 0, camoP, false, gooP);
+ damageBloon(newBloon, shot,
+ shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 2.0) : floorf(
+ damage / 2.0)), gameData, newBloons);
}
break;
}
case MOAB:
case BFB:
case ZOMG:
case DDT: {
for (int i = 0; i < 4; ++i) {
EntityP newBloon = spawnBloon(newBloons, gameData, kind == DDT ? CERAMIC_BLOON : kind - 1,
shot, lives / 4,
(i < 2 ? std::fmaxf(0, progress - 20 + 10 * (i % 2)) : std::fminf(
gameData.path.size() - 1, progress + 20 - 10 * (i % 2))),
kind == DDT ? CERAMIC_BLOON : 0, kind == DDT, fortifiedP, nullptr);
- damageBloon(newBloon, shot, i == 0 ? ceilf(damage / 4.0) : damage - ceilf(damage / 4.0) * 3,
+ damageBloon(newBloon, shot,
+ shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 4.0) : damage -
+ ceilf(damage / 4.0) *
+ 3),
gameData, newBloons);
}
break;
}
case BAD: {
for (int i = 0; i < 5; ++i) {
EntityP newBloon(new Entity());
- if (shot)
- shot->getComponent<PoppedBloons>()->value.emplace(newBloon.get());
+ 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);
+ damageBloon(newBloon, shot,
+ shotKind == ENHANCED_BULLET ? damage : (i == 0 ? ceilf(damage / 5.0) : damage -
+ ceilf(damage / 5.0) *
+ 4), gameData,
+ newBloons);
}
break;
}
}
}
void DamageSystem::update(Entities *layers, GameData &gameData) {
- if(!gameData.levelRunning)
+ if (!gameData.levelRunning)
return;
Entities newBloons;
for (auto &bloon: layers[BLOONS_LAYER]) {
if (auto damageEventP = bloon->getComponent<DamageEvent>()) {
auto &damageEvent = *damageEventP;
auto &lives = bloon->getComponent<Lives>()->value;
if (lives < damageEvent.damage) {
gameData.cash += getBloonProperty<YIELD>(bloon);
bloon->addComponent<RemoveEntityEvent>();
} else {
damageBloon(bloon, damageEvent.shot, damageEvent.damage, gameData, newBloons);
bloon->removeComponent<DamageEvent>();
}
}
}
layers[BLOONS_LAYER] += newBloons;
}
diff --git a/src/systems/EventSystem.cpp b/src/systems/EventSystem.cpp
index 2533ca9..64d2bb2 100644
--- a/src/systems/EventSystem.cpp
+++ b/src/systems/EventSystem.cpp
@@ -1,156 +1,273 @@
//
// 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;
+ auto buttonKindP = entity->getComponent<Kind>();
+ if (action.actionType == CLICK and buttonKindP->value >= UPGRADE_PATH_1 and
+ buttonKindP->value <= UPGRADE_PATH_3) {
+ entityX = 4;
+ entityY = 63 + 135 * (buttonKindP->value - UPGRADE_PATH_1);
+ w = 138;
+ h = 81;
+ }
if (auto positionP = entity->getComponent<Position>()) {
auto &position = *positionP;
entityX = int(position.value.X - w / 2.0);
entityY = int(position.value.Y - h / 2.0);
mouseX = originalMouseX - SIDEBAR_WIDTH;
} else
mouseX = originalMouseX;
if (entityX <= mouseX and mouseX <= entityX + w and entityY <= mouseY and
mouseY <= entityY + h) {
+ if (action.disabled or (action.actionType != DROP and gameData.isDragging)) {
+ entityClicked = true;
+ continue;
+ }
switch (action.actionType) {
case DRAG: {
- auto[type, kind, shotKind, range, attackInterval, pierce, damage, distance, cost] =
- entity->getComponents<Type, Kind, ShotKind, Range, AttackSpeed, Pierce, Damage, Distance, Cost>().value();
+ auto[type, kind, shotKind, range, attackInterval, pierce, damage, distance, cost, upgrades] =
+ entity->getComponents<Type, Kind, ShotKind, Range, AttackSpeed, Pierce, Damage, Distance, Cost, Upgrades>().value();
auto draggable = new Entity();
SDL_Surface *surface = gameData.assets[getSurfaceName(entity)];
draggable->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{
originalMouseX - int(surface->w / 3.0), mouseY - int(surface->h / 3.0),
int(surface->w / 1.5), int(surface->h / 1.5)});
draggable->addComponent<Draggable>();
draggable->addComponent<Action>(DROP);
draggable->addComponents(type, kind, shotKind, range, attackInterval,
- pierce, damage, distance, cost);
+ pierce, damage, distance, cost, upgrades);
draggable->addComponent<Strategy>(FIRST);
- if(auto spreadP = entity->getComponent<Spread>())
+ if (auto spreadP = entity->getComponent<Spread>())
draggable->addComponent<Spread>(*spreadP);
- if(auto gooP = entity->getComponent<Goo>())
+ if (auto gooP = entity->getComponent<Goo>())
draggable->addComponent<Goo>(*gooP);
- if(entity->getComponent<Camo>())
+ 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) {
+ if (entity->getComponent<Visibility>()->hidden)
+ break;
+ switch (buttonKindP->value) {
case PLAY_FAST_FORWARD: {
if (gameData.levelRunning)
gameData.FPS = 240 - gameData.FPS;
else
gameData.levelRunning = true;
visibility.loadTexture(gameData.renderer,
gameData.assets[std::string("FastForward") +
(gameData.FPS == 180 ? "Enabled"
: "")]);
+ break;
+ }
+ case NEXT_STRATEGY: {
+ auto &strategy = gameData.selected->getComponent<Strategy>()->value;
+ strategy = (strategy + 1) % 4;
+ break;
+ }
+ case PREVIOUS_STRATEGY: {
+ auto &strategy = gameData.selected->getComponent<Strategy>()->value;
+ strategy -= 1;
+ if (strategy == -1)
+ strategy = 3;
+ break;
+ }
+ case UPGRADE_PATH_1:
+ case UPGRADE_PATH_2:
+ case UPGRADE_PATH_3: {
+ auto &upgradeP = *entity->getComponent<UpgradeP>();
+ auto &upgrades = *gameData.selected->getComponent<Upgrades>();
+ int cost = upgradeP.value->cost;
+ if (cost > gameData.cash or upgradeP.value->locked)
+ break;
+ else {
+ gameData.cash -= cost;
+ gameData.selected->getComponent<Cost>()->value += cost;
+ }
+ int currentPath = buttonKindP->value - UPGRADE_PATH_1;
+ if (upgrades.selectedPathUpgrades[currentPath] ==
+ upgrades.paths[currentPath].size()) {
+ upgrades.chosenPath = currentPath;
+ for (int j = 1; j <= 2; ++j) {
+ int nextPathIndex = (currentPath + j) % 3;
+ auto &nextPath = upgrades.paths[nextPathIndex];
+ if (upgrades.selectedPathUpgrades[nextPathIndex])
+ nextPath[nextPath.size() -
+ upgrades.selectedPathUpgrades[nextPathIndex]].locked = true;
+ }
+ }
+ for (auto[kind, value]:upgradeP.value->improvements) {
+ switch (kind) {
+ case RANGE_UPGRADE:
+ gameData.selected->getComponent<Range>()->value *= value;
+ break;
+ case PIERCE_UPGRADE:
+ gameData.selected->getComponent<Pierce>()->value += value;
+ break;
+ case DAMAGE_UPGRADE:
+ gameData.selected->getComponent<Damage>()->value += value;
+ break;
+ case DISTANCE_UPGRADE:
+ gameData.selected->getComponent<Distance>()->value *= value;
+ break;
+ case ATTACK_SPEED_UPGRADE: {
+ auto &attackSpeed = *gameData.selected->getComponent<AttackSpeed>();
+ attackSpeed.interval /= value;
+ attackSpeed.timeToRecharge = 0;
+ break;
+ }
+ case CAMO_UPGRADE:
+ gameData.selected->addComponent<Camo>();
+ break;
+ case GOO_KIND_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.kind = value;
+ goo.ttl = goo.kind == GLUE ? 60 : 120;
+ break;
+ }
+ case GOO_DURATION_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.ttl = value;
+ break;
+ }
+ case CORROSIVE_INTERVAL_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.interval = value;
+ goo.timetoRecharge = value;
+ break;
+ }
+ case CORROSIVE_DAMAGE_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.damage = value;
+ break;
+ }
+ case GOO_SOAK_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.soak = true;
+ break;
+ }
+ case GOO_STICKNESS_UPGRADE: {
+ auto &goo = *gameData.selected->getComponent<Goo>();
+ goo.stickness = value;
+ break;
+ }
+ case SHOTS_AMOUNT_UPGRADE:
+ gameData.selected->addComponent<ShotsAmount>(value);
+ break;
+ case SHOT_KIND_UPGRADE:
+ gameData.selected->getComponent<ShotKind>()->value = value;
+ break;
+ }
+ }
+ auto &path = gameData.selected->getComponent<Upgrades>()->paths[
+ buttonKindP->value - UPGRADE_PATH_1];
+ path.erase(path.begin());
+ break;
}
}
goto entityClicked;
}
case DROP: {
auto &draggable = *entity->getComponent<Draggable>();
if (draggable.isPlaceable) {
entity->removeComponent<Draggable>();
entity->getComponent<Action>()->actionType = SELECT;
gameData.isDragging = false;
if (mouseX > SIDEBAR_WIDTH + MAP_WIDTH or mouseX < SIDEBAR_WIDTH) {
entity->addComponent<RemoveEntityEvent>();
gameData.selected.reset();
} else {
for (int x = std::max(mouseX - SIDEBAR_WIDTH - 20, 0);
x < std::min(mouseX - SIDEBAR_WIDTH + 21, MAP_WIDTH); ++x) {
for (int y = std::max(mouseY - 20, 0);
y < std::min(mouseY + 21, MAP_HEIGHT); ++y) {
if (gameData.mapData[x][y] == FREE)
gameData.mapData[x][y] = TOWER;
}
}
if (i == MENU_LAYER) {
gameData.cash -= entity->getComponent<Cost>()->value;
entity->addComponent<MoveEntityEvent>(TOWERS_LAYER);
auto &visibility = *entity->getComponent<Visibility>();
SDL_Rect *dstRect = entity->getComponent<Visibility>()->getDstRect();
entity->addComponent<Position>(
dstRect->x - SIDEBAR_WIDTH + dstRect->w / 2,
dstRect->y + dstRect->h / 2);
}
}
}
goto entityClicked;
}
case SELECT: {
if (entity == gameData.selected)
goto entityClicked;
gameData.selected = entity;
auto rangeShadow = new Entity();
rangeShadow->addComponent<RangeShadow>(entity);
newEntities[SHADOW_LAYER].emplace_back(rangeShadow);
goto entityClicked;
}
}
-
}
-
}
}
-
}
- gameData.selected.reset();
+ if (!entityClicked)
+ gameData.selected.reset();
entityClicked:
for (int i = 0; i < N_LAYERS; ++i) {
if (!newEntities[i].empty())
layers[i] += newEntities[i];
}
break;
}
default:
break;
}
}
}
diff --git a/src/systems/LoadLevelSystem.cpp b/src/systems/LoadLevelSystem.cpp
index 0dc6705..feddc53 100644
--- a/src/systems/LoadLevelSystem.cpp
+++ b/src/systems/LoadLevelSystem.cpp
@@ -1,29 +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()) {
+ while (levelFile.peek() != EOF) {
levelFile.read((char *) &sequenceS, 19);
auto s = new Entity();
s->addComponent<Type>(SEQUENCE_T);
s->addComponent<Sequence>(sequenceS.amount, sequenceS.bps, sequenceS.delay);
s->addComponent<Kind>(sequenceS.kind);
if (sequenceS.regrow)
s->addComponent<Regrow>(sequenceS.kind);
if (sequenceS.camo)
s->addComponent<Camo>();
if (sequenceS.fortified)
s->addComponent<Fortified>();
layers[SEQUENCES_LAYER].emplace_back(s);
}
gameData.levelReady = true;
}
diff --git a/src/systems/LoadLevelSystem.h b/src/systems/LoadLevelSystem.h
index 1152096..18b682e 100644
--- a/src/systems/LoadLevelSystem.h
+++ b/src/systems/LoadLevelSystem.h
@@ -1,25 +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;
+ float 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/MenuSystem.cpp b/src/systems/MenuSystem.cpp
new file mode 100644
index 0000000..17c44bc
--- /dev/null
+++ b/src/systems/MenuSystem.cpp
@@ -0,0 +1,65 @@
+//
+// Created by Ido Mozes on 17/07/2019.
+//
+
+#include "MenuSystem.h"
+
+void MenuSystem::update(Entities *layers, GameData &gameData) {
+ for (auto &entity: layers[MENU_LAYER]) {
+ auto[kindP, actionP] = entity->getComponentsP<Kind, Action>();
+ if (kindP and (!actionP or (actionP->actionType != DRAG and actionP->actionType != DROP)))
+ switch (kindP->value) {
+ case UPGRADES_BACKGROUND:
+ case NEXT_STRATEGY:
+ case PREVIOUS_STRATEGY:
+ case UPGRADE_PATH_1:
+ case UPGRADE_PATH_2:
+ case UPGRADE_PATH_3:
+ entity->getComponent<Visibility>()->hidden = bool(!gameData.selected or gameData.isDragging);
+ break;
+ }
+ if (kindP and actionP and actionP->actionType == CLICK and gameData.selected and !gameData.isDragging) {
+ switch (kindP->value) {
+ case UPGRADE_PATH_1:
+ case UPGRADE_PATH_2:
+ case UPGRADE_PATH_3: {
+ auto &upgrades = *gameData.selected->getComponent<Upgrades>();
+ int path = kindP->value - UPGRADE_PATH_1;
+ auto &visibility = *entity->getComponent<Visibility>();
+ auto upgradeP_P = entity->getComponent<UpgradeP>();
+ if (!upgrades.paths[path].empty()) {
+ auto ¤tUpgrade = upgrades.paths[path][0];
+ if (!upgradeP_P or upgradeP_P->value != ¤tUpgrade or
+ upgradeP_P->name != currentUpgrade.name or (!actionP->disabled and currentUpgrade.locked)) {
+ entity->addComponent<UpgradeP>(¤tUpgrade);
+ if (currentUpgrade.locked)
+ currentUpgrade.surface = gameData.assets[currentUpgrade.surfaceName + "Locked"];
+ SDL_Surface *surface = currentUpgrade.surface;
+ visibility.setDstRect(
+ {75 - surface->w / 34, 143 + 135 * path - surface->h / 17, surface->w / 17, 0});
+ visibility.loadTexture(gameData.renderer, surface);
+ }
+ actionP->disabled = currentUpgrade.locked;
+ visibility.hidden = false;
+ } else
+ visibility.hidden = true;
+ break;
+ }
+ }
+ }
+ if (auto components = entity->getComponents<Cost, Action, Visibility>()) {
+ auto[cost, action, visibility] = components.value();
+ switch (action.actionType) {
+ case DRAG:
+ 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/MenuSystem.h
similarity index 53%
rename from src/systems/CashSystem.h
rename to src/systems/MenuSystem.h
index 0ee6c99..73346e5 100644
--- a/src/systems/CashSystem.h
+++ b/src/systems/MenuSystem.h
@@ -1,12 +1,12 @@
//
// Created by Ido Mozes on 17/07/2019.
//
-#ifndef SDL_GAME_CASHSYSTEM_H
-#define SDL_GAME_CASHSYSTEM_H
+#ifndef SDL_GAME_MENUSYSTEM_H
+#define SDL_GAME_MENUSYSTEM_H
#include "../System.h"
-class CashSystem : public System {
+class MenuSystem : public System {
public:
void update(Entities *layers, GameData &gameData) override;
};
-#endif //SDL_GAME_CASHSYSTEM_H
+#endif //SDL_GAME_MENUSYSTEM_H
diff --git a/src/systems/MovementSystem.cpp b/src/systems/MovementSystem.cpp
index 8d45f79..ff04c19 100644
--- a/src/systems/MovementSystem.cpp
+++ b/src/systems/MovementSystem.cpp
@@ -1,167 +1,167 @@
//
// Created by Ido Mozes on 02/07/2019.
//
#include "MovementSystem.h"
enum Directions {
RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP, TOP_RIGHT
};
std::tuple<int, int> getDelta(int direction) {
switch (direction) {
case RIGHT:
return std::make_tuple(1, 0);
case BOTTOM_RIGHT:
return std::make_tuple(1, 1);
case BOTTOM:
return std::make_tuple(0, 1);
case BOTTOM_LEFT:
return std::make_tuple(-1, 1);
case LEFT:
return std::make_tuple(-1, 0);
case TOP_LEFT:
return std::make_tuple(-1, -1);
case TOP:
return std::make_tuple(0, -1);
case TOP_RIGHT:
return std::make_tuple(1, -1);
default:
return std::make_tuple(0, 0);
}
}
void MovementSystem::update(Entities *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
for (auto &entity: layers[i]) {
// Move all entities with Visibility and Position
if (auto components = entity->getComponents<Visibility, Position>()) {
auto[visibility, position]=components.value();
float deltaX = 0, deltaY = 0;
auto pathIndexP = entity->getComponent<PathIndex>();
if (auto velocityP = entity->getComponent<Velocity>()) {
auto &velocity = *velocityP;
if (auto accelerationP = entity->getComponent<Acceleration>()) {
auto &acceleration = *accelerationP;
velocity.changeVelocity(acceleration.value.X, acceleration.value.Y);
}
deltaX = velocity.value.X;
deltaY = velocity.value.Y;
if (auto distanceP = entity->getComponent<Distance>()) {
auto &distance = distanceP->value;
auto[alpha, R] = cartesianToPolar(deltaX, deltaY);
if (distance == 0) {
entity->addComponent<RemoveEntityEvent>();
} else if (distance >= R) {
distance -= R;
} else {
std::tie(deltaX, deltaY) = polarToCartesian(alpha, distance);
distance = 0;
}
}
} else if (pathIndexP) {
if (auto regrowP = entity->getComponent<Regrow>()) {
auto[lives, kind] = entity->getComponents<Lives, Kind>().value();
regrowP->regrowTime -= 1;
if (regrowP->regrowTime == 0 and kind.value < regrowP->kind) {
switch (kind.value) {
case RED_BLOON:
case BLUE_BLOON:
case GREEN_BLOON:
case YELLOW_BLOON:
kind.value += 1;
break;
case PINK_BLOON: {
switch (regrowP->kind) {
case PURPLE_BLOON:
case BLACK_BLOON:
case WHITE_BLOON:
kind.value = regrowP->kind;
break;
case LEAD_BLOON:
case ZEBRA_BLOON:
case RAINBOW_BLOON:
case CERAMIC_BLOON:
kind.value = BLACK_BLOON;
break;
}
break;
}
case BLACK_BLOON:
if (regrowP->kind == LEAD_BLOON)
kind.value = LEAD_BLOON;
else
kind.value = ZEBRA_BLOON;
break;
case WHITE_BLOON:
kind.value = ZEBRA_BLOON;
case ZEBRA_BLOON:
case RAINBOW_BLOON:
kind.value += 1;
break;
}
lives.value = getBloonProperty<TOTAL_LIVES>(entity);
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;
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;
+ entity->addComponent<DamageEvent>(gooP->damage, EntityP(nullptr));
+ gooP->timetoRecharge = gooP->interval;
}
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) {
float angle = 0;
Point tempPosition = position.value;
for (int j = std::max(0, pathIndex.index - 10);
j < std::min(pathIndex.index + 10, int(gameData.path.size() - 1)); ++j) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[j]);
tempPosition.X += tempDeltaX;
tempPosition.Y += tempDeltaY;
}
angle = radToDeg(twoPointsAngle(position.value, tempPosition));
visibility.angle = angle;
}
while (deltaIndex >= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2))) {
auto[tempDeltaX, tempDeltaY] = getDelta(gameData.path[pathIndex.index]);
deltaX += tempDeltaX;
deltaY += tempDeltaY;
deltaIndex -= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2));
if (pathIndex.index < gameData.path.size() - 1)
pathIndex.index++;
}
}
position.changePosition(deltaX, deltaY);
if (position.value.X > MAP_WIDTH or position.value.X < 0 or position.value.Y > MAP_HEIGHT or
position.value.Y < 0) {
if (pathIndexP) {
gameData.lives -= entity->getComponent<Lives>()->value;
if (gameData.lives <= 0) {
gameData.lost = true;
gameData.lives = 0;
}
}
entity->addComponent<RemoveEntityEvent>();
}
}
}
}
}
diff --git a/src/systems/RenderSystem.cpp b/src/systems/RenderSystem.cpp
index 1d38d9f..861095d 100644
--- a/src/systems/RenderSystem.cpp
+++ b/src/systems/RenderSystem.cpp
@@ -1,91 +1,162 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "RenderSystem.h"
-std::string formatCommas(int num){
+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;
+ 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(255,255,255, 255), TTF_STYLE_NORMAL);
+
+ gameData.fonts[WHITE12] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[WHITE12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
+ FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL);
+ gameData.fonts[BLACK12] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[BLACK12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
+ FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
+ gameData.fonts[RED12] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[RED12], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 12 * gameData.mapScale,
+ FC_MakeColor(255, 49, 49, 255), TTF_STYLE_NORMAL);
+ gameData.fonts[WHITE8] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[WHITE8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
+ FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL);
+ gameData.fonts[BLACK8] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[BLACK8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
+ FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
+ gameData.fonts[RED8] = FC_CreateFont();
+ FC_LoadFont(gameData.fonts[RED8], gameData.renderer, "../assets/LuckiestGuy-Regular.ttf", 10 * gameData.mapScale,
+ FC_MakeColor(255, 49, 49, 255), TTF_STYLE_NORMAL);
+
}
void RenderSystem::update(Entities *layers, GameData &gameData) {
-
SDL_RenderClear(gameData.renderer);
for (int i = 0; i < N_LAYERS; ++i) {
if (i == SEQUENCES_LAYER)
continue;
+
for (auto &entity: layers[i]) {
+ if (i == MENU_LAYER) {
+ auto[visibilityP, kindP, actionP] = entity->getComponentsP<Visibility, Kind, Action>();
+ if (visibilityP and visibilityP->hidden)
+ continue;
+ if (kindP and actionP and actionP->actionType == CLICK)
+ switch (kindP->value) {
+ case UPGRADE_PATH_1:
+ case UPGRADE_PATH_2:
+ case UPGRADE_PATH_3: {
+ auto &upgradeP = *entity->getComponent<UpgradeP>();
+ int path = kindP->value - UPGRADE_PATH_1;
+ int cost = upgradeP.value->cost;
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, 24 * gameData.mapScale,
+ (67 + path * 135) * gameData.mapScale, upgradeP.value->name.c_str());
+ int font = WHITE8;
+ if (cost > gameData.cash)
+ font = RED8;
+ FC_Draw(gameData.fonts[font], gameData.renderer, 47 * gameData.mapScale,
+ (80 + path * 135) * gameData.mapScale, "$%s", formatCommas(cost).c_str());
+ break;
+ }
+ }
+ }
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,
+ 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);
+ aacircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range * gameData.mapScale,
+ isRed ? 255 : 0, 0, 0, 150);
}
if (entity == currentEntity) {
SDL_RenderCopyEx(gameData.renderer, visibility.getTexture(), nullptr, &newDstRect, visibility.angle,
nullptr, SDL_FLIP_NONE);
}
}
}
}
- if(gameData.cash > 99999999)
+ if (gameData.cash > 99999999)
gameData.cash = 99999999;
- FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 45) * gameData.mapScale, 14 * gameData.mapScale,
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 45) * gameData.mapScale,
+ 14 * gameData.mapScale,
formatCommas(gameData.cash).c_str());
- FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 45) * gameData.mapScale, 36 * gameData.mapScale,
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 45) * gameData.mapScale,
+ 36 * gameData.mapScale,
std::to_string(gameData.lives).c_str());
- FC_Draw(gameData.font, gameData.renderer, (MAP_WIDTH+SIDEBAR_WIDTH + 160) * gameData.mapScale, 22 * gameData.mapScale,
- "Level: %s",std::to_string(gameData.level).c_str());
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, (MAP_WIDTH + SIDEBAR_WIDTH + 160) * gameData.mapScale,
+ 22 * gameData.mapScale,
+ "Level: %s", std::to_string(gameData.level).c_str());
+ if (gameData.selected and !gameData.isDragging) {
+ std::string strategy;
+ int x;
+ switch (gameData.selected->getComponent<Strategy>()->value) {
+ case CLOSEST:
+ strategy = "Close";
+ x = 56;
+ break;
+ case FIRST:
+ strategy = "First";
+ x = 57;
+ break;
+ case LAST:
+ strategy = "Last";
+ x = 59;
+ break;
+ case STRONGEST:
+ strategy = "Strong";
+ x = 51;
+ break;
+ }
+ FC_Draw(gameData.fonts[WHITE12], gameData.renderer, x * gameData.mapScale, 16 * gameData.mapScale,
+ strategy.c_str());
+
+ }
SDL_RenderPresent(gameData.renderer);
}
diff --git a/src/systems/ShotsSpawnSystem.cpp b/src/systems/ShotsSpawnSystem.cpp
index b7a6241..ee12d03 100644
--- a/src/systems/ShotsSpawnSystem.cpp
+++ b/src/systems/ShotsSpawnSystem.cpp
@@ -1,116 +1,174 @@
//
// 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;
+ float maxLives = -1;
+ Entity *closestBloon = nullptr, *firstBloon = nullptr, *lastBloon = nullptr, *strongestBloon = nullptr;
for (auto &gameEntity: layers[BLOONS_LAYER]) {
if (gameEntity->getComponent<Camo>() and !camoP)
continue;
float distance;
if (gameEntity->getComponent<Type>()->value == BLOON_T) {
- auto[bloonRange, bloonPosition, pathIndex] = gameEntity->getComponents<Range, Position, PathIndex>().value();
+ auto[bloonRange, bloonPosition, pathIndex, lives] = gameEntity->getComponents<Range, Position, PathIndex, Lives>().value();
distance =
twoPointsDistance(bloonPosition.value, towerPosition.value) - bloonRange.value;
if (distance > towerRange.value)
continue;
if (distance < minDistance) {
minDistance = distance;
closestBloon = gameEntity.get();
}
if (pathIndex.progress < minProgress) {
minProgress = pathIndex.progress;
lastBloon = gameEntity.get();
}
+ if (lives.value > maxLives or (lives.value == maxLives and pathIndex.progress > maxProgress)) {
+ maxLives = lives.value;
+ strongestBloon = gameEntity.get();
+ }
if (pathIndex.progress > maxProgress) {
maxProgress = pathIndex.progress;
firstBloon = gameEntity.get();
}
+
}
}
- if (closestBloon or firstBloon or lastBloon) {
+ if (closestBloon or firstBloon or lastBloon or strongestBloon) {
Entity *target;
switch (strategy.value) {
case CLOSEST:
target = closestBloon;
break;
case FIRST:
target = firstBloon;
break;
case LAST:
target = lastBloon;
break;
+ case STRONGEST:
+ target = strongestBloon;
+ break;
}
int amount = attackSpeed.getAmountReady();
float angle = twoPointsAngle(towerPosition.value, target->getComponent<Position>()->value);
for (int i = 0; i < amount; ++i) {
switch (shotKind.value) {
case BOMB:
case GOO_SHOT:
+ case LASER:
+ case PLASMA:
+ case SUN:
+ case SPIKE:
+ case JUGGERNAUT:
case DART: {
- 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);
+ int shotsAmount = 1;
+ auto shotsAmountP = entity->getComponent<ShotsAmount>();
+ float shotAngle = angle;
+ float deltaAngle = 10;
+ if (shotsAmountP) {
+ shotsAmount = shotsAmountP->value;
+ shotAngle -= degToRad((deltaAngle * (shotsAmount - 1)) / 2.0);
+ deltaAngle = degToRad(deltaAngle);
+ }
+
+ for (int j = 0; j < shotsAmount; ++j) {
+ EntityP shot(new Entity());
+ shot->addComponent<Position>(towerPosition.value.X, towerPosition.value.Y);
+ shot->addComponent<Type>(SHOT_T);
+ shot->addComponent<Kind>(shotKind.value);
+ auto[velocityX, velocityY] = polarToCartesian(shotAngle, getSpeed(shot));
+ shot->addComponent<Velocity>(velocityX, velocityY);
+ shot->addComponent<Range>(5);
+ if (shotKind.value == BOMB or shotKind.value == GOO_SHOT) {
+ shot->addComponent<Spread>(*entity->getComponent<Spread>());
+ if (auto gooP = entity->getComponent<Goo>())
+ shot->addComponent<Goo>(*gooP);
+ }
+ shot->addComponents(pierce, damage, distance);
+ if (camoP)
+ shot->addComponent<Camo>();
+ shot->addComponent<PoppedBloons>();
+ SDL_Surface *surface = gameData.assets[getSurfaceName(shot)];
+ int scale = 1;
+ switch (shotKind.value) {
+ case DART:
+ scale = 2;
+ break;
+ case BOMB:
+ scale = 4;
+ break;
+ case SPIKE:
+ case JUGGERNAUT:
+ scale = 4;
+ break;
+ case LASER:
+ scale = 10;
+ break;
+ case PLASMA:
+ case SUN:
+ scale = 6;
+ break;
+ }
+ shot->addComponent<Visibility>(gameData.renderer, surface,
+ SDL_Rect{0, 0, surface->w / scale},
+ radToDeg(shotAngle));
+ layers[SHOTS_LAYER].emplace_back(shot);
+ shotAngle += deltaAngle;
}
- 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 / (shotKind.value == DART ? 2 : 4)},
- radToDeg(angle));
- layers[SHOTS_LAYER].emplace_back(shot);
break;
}
- case GUN: {
- target->addComponent<DamageEvent>(damage.value, EntityP(nullptr));
+
+ case BULLET:
+ case ENHANCED_BULLET: {
+ if (shotKind.value == BULLET and target->getComponent<Kind>()->value == LEAD_BLOON)
+ break;
+ EntityP shot(new Entity());
+ shot->addComponent<PoppedBloons>();
+ shot->addComponent<Kind>(shotKind.value);
+ target->addComponent<DamageEvent>(
+ target->getComponent<Camo>() ? damage.value * 2 : damage.value, shot);
break;
}
case RADIAL_DART: {
SDL_Surface *surface = gameData.assets["Dart"];
for (int j = 0; j < 8; ++j) {
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>();
-
+ if (camoP)
+ shot->addComponent<Camo>();
shot->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{0, 0, surface->w / 2},
radToDeg(angle));
layers[SHOTS_LAYER].emplace_back(shot);
}
break;
}
}
}
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, 3:22 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42899
Default Alt Text
(136 KB)
Attached To
Mode
R74 BloonsTD
Attached
Detach File
Event Timeline
Log In to Comment