Page MenuHomePhabricator (Chris)

No OneTemporary

Size
136 KB
Referenced Files
None
Subscribers
None
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 &currentUpgrade = upgrades.paths[path][0];
+ if (!upgradeP_P or upgradeP_P->value != &currentUpgrade or
+ upgradeP_P->name != currentUpgrade.name or (!actionP->disabled and currentUpgrade.locked)) {
+ entity->addComponent<UpgradeP>(&currentUpgrade);
+ 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 &currentEntity = rangeShadowP ? rangeShadowP->entity : entity;
if (auto visibilityP = currentEntity->getComponent<Visibility>()) {
auto &visibility = *visibilityP;
SDL_Rect *dstRect = visibility.getDstRect();
SDL_Rect newDstRect = {int(dstRect->x * gameData.mapScale), int(dstRect->y * gameData.mapScale),
int(dstRect->w * gameData.mapScale), int(dstRect->h * gameData.mapScale)};
SDL_Point entityCenter;
auto positionP = currentEntity->getComponent<Position>();
if (positionP) {
auto &position = *positionP;
entityCenter.x = (position.value.X + SIDEBAR_WIDTH) * gameData.mapScale;
entityCenter.y = position.value.Y * gameData.mapScale;
newDstRect.x = int((position.value.X + SIDEBAR_WIDTH) * gameData.mapScale - newDstRect.w / 2.0);
newDstRect.y = int(position.value.Y * gameData.mapScale - newDstRect.h / 2.0);
} else {
entityCenter.x = int(dstRect->x * gameData.mapScale + (dstRect->w * gameData.mapScale) / 2.0);
entityCenter.y = int(dstRect->y * gameData.mapScale + (dstRect->h * gameData.mapScale) / 2.0);
}
if (currentEntity != entity) {
auto draggableP = currentEntity->getComponent<Draggable>();
bool isRed = draggableP ? !draggableP->isPlaceable : false;
float range = currentEntity->getComponent<Range>()->value;
- filledCircleRGBA(gameData.renderer, entityCenter.x, entityCenter.y, range* gameData.mapScale, isRed ? 255 : 0, 0, 0,
+ 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

Mime Type
text/x-diff
Expires
Fri, Sep 12, 3:22 PM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42899
Default Alt Text
(136 KB)

Event Timeline