Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F134549
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
46 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 728d9e7..6de4187 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)
-add_executable(SDL_Game src/main.cpp src/Game.cpp src/Game.h src/Entity.cpp src/Entity.h src/Component.cpp src/Component.h src/System.cpp 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.cpp src/components/Position.h src/components/PathIndex.cpp src/components/PathIndex.h src/GameData.h src/components/Speed.h src/components/Sequence.cpp src/components/Sequence.h src/components/Kind.h src/systems/SpawnSystem.cpp src/systems/SpawnSystem.h src/GameData.cpp)
+link_libraries(mingw32 SDL2main SDL2 SDL2_image SDL2_gfx)
+add_executable(SDL_Game src/main.cpp src/Game.cpp src/Game.h src/Entity.cpp src/Entity.h src/Component.cpp src/Component.h src/System.cpp 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.cpp src/components/Position.h src/components/PathIndex.cpp src/components/PathIndex.h src/GameData.h src/components/Speed.h src/components/Sequence.cpp src/components/Sequence.h src/components/Kind.h src/systems/SpawnSystem.cpp src/systems/SpawnSystem.h src/GameData.cpp src/components/Action.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/components/Type.h)
target_link_libraries(SDL_Game ${Boost_LIBRARIES})
\ No newline at end of file
diff --git a/scripts/convert_maps.py b/scripts/convert_maps.py
index 1a14afc..4ddd8c7 100644
--- a/scripts/convert_maps.py
+++ b/scripts/convert_maps.py
@@ -1,84 +1,83 @@
from PIL import Image
from sys import argv
from struct import pack
arrows = ['→', '↘', '↓', '↙', '←', '↖', '↑', '↗']
# obstacles
image = Image.open(argv[1] + "_obstacles.bmp")
width, height = image.size
obstacles = b''
byte = 0
-i = 7
-for i, pixel in enumerate(image.getdata()):
- byte += 2 ** (i % 8) * (pixel == (0, 0, 0))
- if i % 8 == 7:
- obstacles += bytes([byte])
- byte = 0
+i = 0
+for y in range(height):
+ for x in range(width):
+ pixel = image.getpixel((x, y))
+ byte |= (2 ** (i % 8)) * (pixel == (0, 0, 0))
+ print(i,pixel,byte)
+ _=input()
+ if i % 8 == 7:
+ obstacles += bytes([byte])
+ byte = 0
+ i += 1
if i % 8 != 7:
obstacles += bytes([byte])
with open(argv[1] + "_obstacles.data", 'wb') as file:
- file.write(pack('<H', width))
- file.write(pack('<H', height))
file.write(obstacles)
# path
image = Image.open(argv[1] + "_path.bmp")
width, height = image.size
instructions = b''
i = 0
for i, pixel in enumerate(image.getdata()):
if pixel == (255, 0, 0):
break
start_loc_x = x = i % width
start_loc_y = y = i // width
-# print(x, y)
prev_x = prev_y = next_x = next_y = 0
pixels = image.load()
pixel = pixels[x, y]
length = 0
prev_locations = [(x, y)]
directions = ((1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1))
while pixel != (255, 255, 0):
length += 1
possible_locations = [(pixels[x + delta_x, y + delta_y], x + delta_x, y + delta_y, mask) for
mask, (delta_x, delta_y) in
enumerate(directions) if (0 <= x + delta_x < width) and (0 <= y + delta_y < height) and not (
x + delta_x == prev_x and y + delta_y == prev_y) and pixels[x + delta_x, y + delta_y] != (
255, 255, 255)]
if len(possible_locations) == 1:
pixel, next_x, next_y, mask = possible_locations[0]
else:
special_locations = sorted([loc for loc in possible_locations if loc[0][2] == 255])
if len(special_locations) > 0:
pixel, next_x, next_y, mask = special_locations[0]
ttl = pixel[1] - 1
pixels[next_x, next_y] = (pixel[0] if ttl else 0, ttl, 255 if ttl else 0)
else:
new_locations = [loc for loc in possible_locations if (loc[1], loc[2]) not in prev_locations]
if len(new_locations) > 0:
pixel, next_x, next_y, mask = new_locations[0]
else:
old_locations = sorted((prev_locations.index((loc[1], loc[2])), loc) for loc in possible_locations if
(loc[1], loc[2]) in prev_locations)[::-1]
pixel, next_x, next_y, mask = old_locations[0][1]
if (next_x, next_y) in prev_locations:
prev_locations.remove((next_x, next_y))
prev_locations.append((next_x, next_y))
- # print(next_x, next_y)
- # print(arrows[mask])
- # _ = input()
instructions += bytes([mask])
prev_x = x
prev_y = y
x = next_x
y = next_y
with open(argv[1] + "_path.data", 'wb') as file:
file.write(pack('<H', start_loc_x))
file.write(pack('<H', start_loc_y))
file.write(pack('<I', length))
file.write(instructions)
file.write(pack('<H', x))
file.write(pack('<H', y))
diff --git a/src/Component.h b/src/Component.h
index deb40a7..c7dd6e9 100644
--- a/src/Component.h
+++ b/src/Component.h
@@ -1,31 +1,30 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#ifndef SDL2_GAME_COMPONENT_H
#define SDL2_GAME_COMPONENT_H
struct Point {
short X;
short Y;
};
class Entity;
enum ComponentType {
- VISIBILITY, POSITION, VELOCITY,SPEED, ACCELERATION, PATH_INDEX, HEALTH, KIND, TYPE, DAMAGE, PIERCE, SPREAD,SEQUENCE,
+ VISIBILITY, POSITION, VELOCITY,SPEED, ACCELERATION, PATH_INDEX, HEALTH, KIND, TYPE, DAMAGE, PIERCE, SPREAD,SEQUENCE,ACTION,DRAGGABLE,
LENGTH
};
class Component {
Entity *entity;
public:
explicit Component(Entity *entity) : entity(entity) {};
- ~Component() = default;
Entity *getEntity() { return entity; }
};
#endif //SDL2_GAME_COMPONENT_H
diff --git a/src/Entity.cpp b/src/Entity.cpp
index 5358b18..2fc8206 100644
--- a/src/Entity.cpp
+++ b/src/Entity.cpp
@@ -1,10 +1,6 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#include "Entity.h"
-Entity::Entity():componentsMask() ,components(){
- id = getNewId();
-}
-
diff --git a/src/Entity.h b/src/Entity.h
index 961818c..f0afe01 100644
--- a/src/Entity.h
+++ b/src/Entity.h
@@ -1,47 +1,66 @@
//
// Created by Ido Mozes on 20/06/2019.
//
#ifndef SDL2_GAME_ENTITY_H
#define SDL2_GAME_ENTITY_H
#include <bitset>
#include <vector>
+#include <cmath>
#include <memory>
+#include <optional>
#include "Component.h"
-constexpr int maxComponents = 64;
-typedef std::size_t EntityId;
-
+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 {
- static EntityId getNewId() {
- static EntityId id_counter = 0;
- return id_counter++;
- }
- EntityId id;
std::bitset<ComponentType::LENGTH> componentsMask;
- std::array<Component *, ComponentType::LENGTH> components;
+ std::unique_ptr<Component> components[ComponentType::LENGTH];
public:
- Entity();
- ~Entity() = default;
+// bool hasComponents(uint64_t mask) { return (componentsMask.to_ullong() & mask) == mask; }
- EntityId getId() { return id; }
+// template<class T>
+// T &getComponent() { return *(T *) (components[T::getComponentType()].get()); }
- bool hasComponents(uint64_t mask) { return (componentsMask.to_ullong() & mask) == mask; }
+ template<class ... T>
+ std::optional<std::tuple<T &...>> getComponents() {
+ uint64_t mask = createMask({T::getComponentType()...});
+ if ((componentsMask.to_ullong() & mask) == mask) {
+ return std::tuple<T &...>((*(T *) components[T::getComponentType()].get())...);
+ }
+ return std::nullopt;
+ }
template<class T>
- T &getComponent() { return *(T *) (components[T::getComponentType()]); }
+ T * getComponent() {
+ if (auto &c =components[T::getComponentType()])
+ return (T*)c.get();
+ return nullptr;
+ }
template<typename T, typename ... Targs>
- Component &addComponent(Targs &&... args){
+ Component &addComponent(Targs &&... args) {
T *c = new T(this, std::forward<Targs>(args)...);
- components[T::getComponentType()] = c;
+ components[T::getComponentType()] = std::move(std::unique_ptr<Component>(c));
componentsMask[T::getComponentType()] = true;
return *c;
}
+
+ template<typename T>
+ void removeComponent() {
+ components[T::getComponentType()].release();
+ componentsMask[T::getComponentType()] = false;
+ }
+
};
#endif //SDL2_GAME_ENTITY_H
diff --git a/src/Game.cpp b/src/Game.cpp
index 27848eb..2575aec 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,89 +1,120 @@
//
// 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());
}
gameData.assets["map"] = IMG_Load("../assets/map0.jpg");
gameData.assets["upgrade_bar"] = IMG_Load("../assets/upgrade_bar.jpg");
+ gameData.assets["upgrade_bar2"] = IMG_Load("../assets/upgrade_bar2.jpg");
gameData.assets["menu"] = IMG_Load("../assets/menu.jpg");
- renderSystem.init(gameData);
+ gameData.assets["Super_Monkey"] = IMG_Load("../assets/Super_Monkey.png");
+ gameData.assets["Sniper_Monkey"] = IMG_Load("../assets/Sniper_Monkey.png");
+ renderSystem = new RenderSystem();
+ renderSystem->init(gameData);
loadMap();
- auto mapEntity = new Entity();
- mapEntity->addComponent<Visibility>(gameData.renderer, gameData.assets["map"],
- SDL_Rect{150, 0, gameData.assets["map"]->w, gameData.assets["map"]->h});
- layers[0].emplace_back(mapEntity);
- auto upgrade_bar = new Entity();
- upgrade_bar->addComponent<Visibility>(gameData.renderer, gameData.assets["upgrade_bar"],
- SDL_Rect{0, 0, gameData.assets["upgrade_bar"]->w, gameData.assets["upgrade_bar"]->h});
- layers[1].emplace_back(upgrade_bar);
- auto menu = new Entity();
- menu->addComponent<Visibility>(gameData.renderer, gameData.assets["menu"],
- SDL_Rect{685+150, 0, gameData.assets["menu"]->w, gameData.assets["menu"]->h});
- layers[1].emplace_back(menu);
+ std::initializer_list<std::pair<std::string, Point>> sprites[]{
+ {{"map", {SIDEBAR_WIDTH, 0}}},
+ {},
+ {{"upgrade_bar", {0, 0}}, {"menu", {MAP_WIDTH + SIDEBAR_WIDTH, 0}}},
+ {}
+ };
+ for (int i = 0; i < N_LAYERS; i++) {
+ for (auto &sprite :sprites[i]) {
+ auto spriteEntity = new Entity();
+ spriteEntity->addComponent<Visibility>(gameData.renderer, gameData.assets[sprite.first],
+ SDL_Rect{sprite.second.X, sprite.second.Y,
+ gameData.assets[sprite.first]->w,
+ gameData.assets[sprite.first]->h});
+ layers[i].emplace_back(spriteEntity);
+ }
+ }
+ auto button = new Entity();
+ button->addComponent<Kind>("Super_Monkey");
+ button->addComponent<Visibility>(gameData.renderer, gameData.assets["Super_Monkey"],
+ SDL_Rect{SIDEBAR_WIDTH + MAP_WIDTH + 10, 10, gameData.assets["Super_Monkey"]->w,
+ gameData.assets["Super_Monkey"]->h});
+ button->addComponent<Action>(DRAG);
+ layers[3].emplace_back(button);
+ button = new Entity();
+ button->addComponent<Kind>("Sniper_Monkey");
+ button->addComponent<Visibility>(gameData.renderer, gameData.assets["Sniper_Monkey"],
+ SDL_Rect{SIDEBAR_WIDTH + MAP_WIDTH + 100, 10, gameData.assets["Sniper_Monkey"]->w,
+ gameData.assets["Sniper_Monkey"]->h});
+ button->addComponent<Action>(DRAG);
+ layers[3].emplace_back(button);
auto s = new Entity();
s->addComponent<Sequence>(100, 10, 0);
s->addComponent<Kind>(std::string("Ceramic"));
s->addComponent<Speed>(3.5);
- layers[0].emplace_back(s);
+ layers[1].emplace_back(s);
s = new Entity();
s->addComponent<Sequence>(100, 10, 60 * 5);
s->addComponent<Kind>(std::string("Blue"));
s->addComponent<Speed>(1.5);
- layers[0].emplace_back(s);
+ layers[1].emplace_back(s);
+ systems.emplace_back(new EventSystem);
systems.emplace_back(new SpawnSystem);
+ systems.emplace_back(new DraggingSystem);
systems.emplace_back(new MovementSystem);
- systems.emplace_back(&renderSystem);
+ systems.emplace_back(renderSystem);
}
Game::~Game() {
SDL_Quit();
}
-void Game::handleEvents() {
- SDL_Event event;
- SDL_PollEvent(&event);
- switch (event.type) {
- case SDL_QUIT:
- gameData.isRunning = false;
- break;
- default:
- break;
- }
-}
void Game::update() {
for (auto &system : systems) {
system->update(layers, gameData);
}
}
void Game::loadMap() {
- gameData.path.clear();
std::string fileName = "../assets/map" + std::to_string(gameData.map);
- std::ifstream file(fileName + "_path.data", std::ios::binary);
+ 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 & int(pow(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;
- file.read((char *) &gameData.startingPoint, 4);
- file.read((char *) &length, 4);
+ pathFile.read((char *) &gameData.startingPoint, 4);
+ pathFile.read((char *) &length, 4);
gameData.path.resize(length);
- file.read(&gameData.path[0], length);
- file.read((char *) &gameData.finishPoint, 4);
+ pathFile.read(&gameData.path[0], length);
+ pathFile.read((char *) &gameData.finishPoint, 4);
}
diff --git a/src/Game.h b/src/Game.h
index 31c70cd..bcd7f48 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,49 +1,46 @@
//
// 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/SpawnSystem.h"
-#include "components/Visibility.h"
-#include "components/PathIndex.h"
-#include "components/Speed.h"
+#include "systems/DraggingSystem.h"
#include "GameData.h"
#include "boost/filesystem.hpp"
#include <iostream>
class Game {
std::vector<std::unique_ptr<System>> systems;
std::vector<std::shared_ptr<Entity>> layers[N_LAYERS];
- RenderSystem renderSystem;
+ RenderSystem * renderSystem;
GameData gameData;
public:
explicit Game(bool fullscreen, float mapScale=1.5);
~Game();
- void handleEvents();
-
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 da0953b..dff7a83 100644
--- a/src/GameData.cpp
+++ b/src/GameData.cpp
@@ -1,9 +1,12 @@
//
// 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);
}
diff --git a/src/GameData.h b/src/GameData.h
index d7e699a..075d864 100644
--- a/src/GameData.h
+++ b/src/GameData.h
@@ -1,31 +1,33 @@
//
// Created by Ido Mozes on 03/07/2019.
//
#ifndef SDL_GAME_GAMEDATA_H
#define SDL_GAME_GAMEDATA_H
-constexpr int N_LAYERS = 2;
+
#include <vector>
#include <unordered_map>
#include "Component.h"
+#include "Settings.h"
#include "SDL.h"
class GameData {
public:
bool isRunning = true;
+ bool isDragging = false;
int level = 0;
int map = 0;
float mapScale = 1.5;
bool fullscreen;
std::vector<char> path;
-// char **mapData;
+ char mapData[MAP_WIDTH][MAP_HEIGHT];
Point startingPoint;
Point finishPoint;
- std::unordered_map<std::string,SDL_Surface *> assets;
+ std::unordered_map<std::string, SDL_Surface *> assets;
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
~GameData();
};
#endif //SDL_GAME_GAMEDATA_H
diff --git a/src/Settings.h b/src/Settings.h
new file mode 100644
index 0000000..99f2478
--- /dev/null
+++ b/src/Settings.h
@@ -0,0 +1,12 @@
+//
+// Created by Ido Mozes on 09/07/2019.
+//
+
+#ifndef SDL_GAME_SETTINGS_H
+#define SDL_GAME_SETTINGS_H
+constexpr int SIDEBAR_WIDTH = 150;
+constexpr int MENU_WIDTH = 250;
+constexpr int MAP_WIDTH = 686;
+constexpr int MAP_HEIGHT = 511;
+constexpr int N_LAYERS = 4;
+#endif //SDL_GAME_SETTINGS_H
diff --git a/src/System.h b/src/System.h
index 332f21b..a565f76 100644
--- a/src/System.h
+++ b/src/System.h
@@ -1,24 +1,27 @@
//
// 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/Sequence.h"
+#include "components/Speed.h"
+#include "components/Kind.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"
+
-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 System{
public:
virtual void update(std::vector<std::shared_ptr<Entity>> *entities, GameData & gameData)=0;
};
#endif //SDL2_GAME_SYSTEM_H
diff --git a/src/components/Action.cpp b/src/components/Action.cpp
new file mode 100644
index 0000000..9286f63
--- /dev/null
+++ b/src/components/Action.cpp
@@ -0,0 +1,5 @@
+//
+// Created by Ido Mozes on 08/07/2019.
+//
+
+#include "Action.h"
diff --git a/src/components/Action.h b/src/components/Action.h
new file mode 100644
index 0000000..ee83bf4
--- /dev/null
+++ b/src/components/Action.h
@@ -0,0 +1,26 @@
+//
+// 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
+};
+
+class Action : public Component {
+
+public:
+ bool disabled;
+ ActionType type;
+
+ static ComponentType getComponentType() { return ComponentType::ACTION; }
+
+ Action(Entity *entity, ActionType type, bool disabled = false) : Component(entity), type(type),
+ disabled(disabled) {}
+};
+
+#endif //SDL_GAME_ACTION_H
diff --git a/src/components/ComponentHeader.h b/src/components/ComponentHeader.h
new file mode 100644
index 0000000..e8232db
--- /dev/null
+++ b/src/components/ComponentHeader.h
@@ -0,0 +1,7 @@
+#include "../Component.h"
+
+class __: public Component{
+public:
+ static ComponentType getComponentType() { return ComponentType::__; }
+ __(Entity *entity):Component(entity){}
+};
\ No newline at end of file
diff --git a/src/components/Draggable.h b/src/components/Draggable.h
new file mode 100644
index 0000000..6bffb89
--- /dev/null
+++ b/src/components/Draggable.h
@@ -0,0 +1,15 @@
+//
+// Created by Ido Mozes on 09/07/2019.
+//
+
+#ifndef SDL_GAME_DRAGGABLE_H
+#define SDL_GAME_DRAGGABLE_H
+#include "../Component.h"
+
+class Draggable: public Component{
+public:
+ bool isPlaceable = false;
+ static ComponentType getComponentType() { return ComponentType::DRAGGABLE; }
+ Draggable(Entity *entity):Component(entity){}
+};
+#endif //SDL_GAME_DRAGGABLE_H
diff --git a/src/components/Kind.h b/src/components/Kind.h
index f6000e0..e5452d4 100644
--- a/src/components/Kind.h
+++ b/src/components/Kind.h
@@ -1,21 +1,22 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#ifndef SDL_GAME_KIND_H
#define SDL_GAME_KIND_H
#include "../Component.h"
#include <string>
class Kind : public Component {
public:
std::string kind;
static ComponentType getComponentType() { return ComponentType::KIND; }
Kind(Entity *entity, std::string kind) : Component(entity), kind(std::move(kind)) {}
+ Kind(Entity *entity, Kind& kind) : Component(entity), kind(kind.kind) {}
};
#endif //SDL_GAME_KIND_H
diff --git a/src/components/Type.h b/src/components/Type.h
new file mode 100644
index 0000000..4882227
--- /dev/null
+++ b/src/components/Type.h
@@ -0,0 +1,16 @@
+//
+// Created by Ido Mozes on 09/07/2019.
+//
+
+#ifndef SDL_GAME_TYPE_H
+#define SDL_GAME_TYPE_H
+#include "../Component.h"
+enum Types{
+ OBSTACLE, TOWER, BLOON, RADIOS
+};
+class Type: public Component{
+public:
+ static ComponentType getComponentType() { return ComponentType::TYPE; }
+ Type(Entity *entity):Component(entity){}
+};
+#endif //SDL_GAME_TYPE_H
diff --git a/src/components/Visibility.cpp b/src/components/Visibility.cpp
index dfe13c2..e801e94 100644
--- a/src/components/Visibility.cpp
+++ b/src/components/Visibility.cpp
@@ -1,40 +1,39 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "Visibility.h"
-Visibility::Visibility(Entity *entity, SDL_Renderer *renderer, SDL_Surface *newSurface, std::optional<SDL_Rect> dstR)
- : Component(entity), dstRect(dstR) {
+Visibility::Visibility(Entity *entity, SDL_Renderer *renderer, SDL_Surface *newSurface, std::optional<SDL_Rect> dstR,
+ std::optional<float> radios) : Component(entity), dstRect(dstR), radios(radios) {
loadTexture(renderer, newSurface);
}
Visibility::~Visibility() {
SDL_DestroyTexture(texture);
- SDL_FreeSurface(surface);
}
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) {
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 89fc46a..3401552 100644
--- a/src/components/Visibility.h
+++ b/src/components/Visibility.h
@@ -1,42 +1,48 @@
//
// 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"
-
+constexpr float NO_RADIOS = -1;
SDL_Texture *loadTexture(SDL_Renderer *renderer, const char *path);
class Visibility : public Component {
- SDL_Surface *surface;
- SDL_Texture *texture;
+ SDL_Surface *surface = nullptr;
+ SDL_Texture *texture = nullptr;
std::optional<SDL_Rect> dstRect;
-
+ std::optional<float> radios;
public:
- static ComponentType getComponentType() { return ComponentType::VISIBILITY; }
+ static constexpr inline ComponentType getComponentType() { return ComponentType::VISIBILITY; }
+
+ Visibility(Entity *entity, SDL_Renderer *renderer, SDL_Surface *newSurface,
+ std::optional<SDL_Rect> dstR = std::nullopt, std::optional<float> radios = std::nullopt);
- Visibility(Entity *entity, SDL_Renderer *renderer, SDL_Surface * newSurface,std::optional<SDL_Rect> dstR = std::nullopt);
~Visibility();
SDL_Texture *getTexture() { return texture; }
SDL_Rect *getDstRect() { return dstRect ? &dstRect.value() : nullptr; }
+ float getRadios() { return radios ? radios.value() : NO_RADIOS; }
+
+ void setRadios(std::optional<float> newRadios) { radios = newRadios; }
+
void setPosition(int x, int y);
- void loadTexture(SDL_Renderer *renderer, SDL_Surface * newSurface);
+ void loadTexture(SDL_Renderer *renderer, SDL_Surface *newSurface);
void reloadTexture(SDL_Renderer *renderer);
};
#endif //SDL2_GAME_VISIBILITY_H
diff --git a/src/main.cpp b/src/main.cpp
index 6faa8be..e754cb5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,21 +1,21 @@
//#include <iostream>
#include "SDL.h"
#include "Game.h"
int main(int argc, char *argv[]) {
const int FPS = 60, frameDelay = 1000 / FPS;
Uint32 frameStart;
int frameTime;
Game game(false, 1.5);
while (game.running()) {
frameStart = SDL_GetTicks();
- game.handleEvents();
+
game.update();
frameTime = SDL_GetTicks() - frameStart;
if (frameDelay > frameTime) {
SDL_Delay(frameDelay - frameTime);
}
}
return 0;
}
\ No newline at end of file
diff --git a/src/systems/DraggingSystem.cpp b/src/systems/DraggingSystem.cpp
new file mode 100644
index 0000000..badac80
--- /dev/null
+++ b/src/systems/DraggingSystem.cpp
@@ -0,0 +1,40 @@
+//
+// Created by Ido Mozes on 09/07/2019.
+//
+
+#include "DraggingSystem.h"
+
+
+
+void DraggingSystem::update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) {
+
+ int mouseX, mouseY;
+ SDL_GetMouseState(&mouseX, &mouseY);
+ mouseX = int(mouseX / gameData.mapScale);
+ mouseY = int(mouseY / gameData.mapScale);
+ for (int i = 0; i < N_LAYERS; ++i) {
+ for (auto &entity: layers[i]) {
+ if (auto components = entity->getComponents<Draggable, Visibility>()) {
+ auto[draggable, visibility] = components.value();
+ visibility.setPosition(mouseX - int(visibility.getDstRect()->w / 2.0),
+ mouseY - int(visibility.getDstRect()->h / 2.0));
+ bool freePosition = true;
+ if (mouseX > SIDEBAR_WIDTH + MAP_WIDTH or mouseX < SIDEBAR_WIDTH)
+ freePosition = false;
+
+ else
+ for (int x = mouseX - SIDEBAR_WIDTH - 4; x < mouseX - SIDEBAR_WIDTH + 5; ++x) {
+ for (int y = mouseY - 4; y < mouseY + 5; ++y) {
+ if (gameData.mapData[x][y]) {
+ freePosition = false;
+ goto setIsPlaceable;
+ }
+ }
+ }
+ setIsPlaceable:
+ draggable.isPlaceable = freePosition;
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/systems/DraggingSystem.h b/src/systems/DraggingSystem.h
new file mode 100644
index 0000000..e4c9d78
--- /dev/null
+++ b/src/systems/DraggingSystem.h
@@ -0,0 +1,13 @@
+//
+// Created by Ido Mozes on 09/07/2019.
+//
+
+#ifndef SDL_GAME_DRAGGINGSYSTEM_H
+#define SDL_GAME_DRAGGINGSYSTEM_H
+#include <iostream>
+#include "../System.h"
+class DraggingSystem : public System {
+public:
+ void update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) override;
+};
+#endif //SDL_GAME_DRAGGINGSYSTEM_H
diff --git a/src/systems/EventSystem.cpp b/src/systems/EventSystem.cpp
new file mode 100644
index 0000000..f61506b
--- /dev/null
+++ b/src/systems/EventSystem.cpp
@@ -0,0 +1,90 @@
+//
+// Created by Ido Mozes on 08/07/2019.
+//
+
+#include "EventSystem.h"
+
+
+void EventSystem::update(std::vector<std::shared_ptr<Entity>> *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: {
+ std::vector<std::shared_ptr<Entity>> newEntities;
+ std::vector<std::shared_ptr<Entity>> entitiesToRemove;
+ int mouseX, mouseY, originalMouseX;
+ SDL_GetMouseState(&mouseX, &mouseY);
+ mouseX = originalMouseX = int(mouseX / gameData.mapScale);
+ mouseY = int(mouseY / gameData.mapScale);
+ 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.type == DRAG and gameData.isDragging))
+ continue;
+ int entityX, entityY, w, h;
+ entityX = visibility.getDstRect()->x;
+ entityY = visibility.getDstRect()->y;
+ w = visibility.getDstRect()->w;
+ h = visibility.getDstRect()->h;
+
+ if (auto positionP=entity->getComponent<Position>()) {
+ auto &position = *positionP;
+ entityX = int(position.getX() - w / 2.0);
+ entityY = int(position.getY() - h / 2.0);
+ mouseX = originalMouseX - SIDEBAR_WIDTH;
+ } else
+ mouseX = originalMouseX;
+
+ if (entityX <= mouseX and mouseX <= entityX + w and entityY <= mouseY and
+ mouseY <= entityY + h) {
+ switch (action.type) {
+ case DRAG: {
+ auto &kind = *entity->getComponent<Kind>();
+ auto draggable = new Entity();
+ SDL_Surface *surface = gameData.assets[kind.kind];
+ draggable->addComponent<Visibility>(gameData.renderer, surface, SDL_Rect{
+ originalMouseX - int(surface->w / 2.0), mouseY - int(surface->h / 2.0),
+ surface->w, surface->h}, 100);
+ draggable->addComponent<Draggable>();
+ draggable->addComponent<Action>(DROP);
+ draggable->addComponent<Kind>(kind);
+ newEntities.emplace_back(draggable);
+ gameData.isDragging = true;
+ goto entityClicked;
+ }
+
+ case CLICK: {
+ goto entityClicked;
+ }
+ case DROP: {
+ auto &draggable = *entity->getComponent<Draggable>();
+ if (draggable.isPlaceable) {
+ entity->removeComponent<Draggable>();
+ gameData.isDragging = false;
+ }
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+ entityClicked:
+ layers[3].insert(layers[3].end(), std::make_move_iterator(newEntities.begin()),
+ std::make_move_iterator(newEntities.end()));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+}
diff --git a/src/systems/EventSystem.h b/src/systems/EventSystem.h
new file mode 100644
index 0000000..a57cb3b
--- /dev/null
+++ b/src/systems/EventSystem.h
@@ -0,0 +1,19 @@
+//
+// Created by Ido Mozes on 08/07/2019.
+//
+
+#ifndef SDL_GAME_EVENTSYSTEM_H
+#define SDL_GAME_EVENTSYSTEM_H
+
+#include <iostream>
+#include "../System.h"
+#include "../systems/RenderSystem.h"
+
+
+
+class EventSystem : public System {
+public:
+ void update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) override;
+};
+
+#endif //SDL_GAME_EVENTSYSTEM_H
diff --git a/src/systems/MovementSystem.cpp b/src/systems/MovementSystem.cpp
index d822eb3..d5a60cc 100644
--- a/src/systems/MovementSystem.cpp
+++ b/src/systems/MovementSystem.cpp
@@ -1,70 +1,68 @@
//
// Created by Ido Mozes on 02/07/2019.
//
#include "MovementSystem.h"
void MovementSystem::update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
for (auto &entity: layers[i]) {
// Move all entities with Visibility and Position
- if (entity->hasComponents(createMask({ComponentType::VISIBILITY, ComponentType::POSITION}))) {
- auto &visibility = entity->getComponent<Visibility>();
- auto &position = entity->getComponent<Position>();
+ if (auto components = entity->getComponents<Visibility, Position>()) {
+ auto[visibility, position]=components.value();
float deltaX = 0, deltaY = 0;
- if (entity->hasComponents(createMask({ComponentType::VELOCITY}))) {
- auto &velocity = entity->getComponent<Velocity>();
- if (entity->hasComponents(createMask({ACCELERATION}))) {
- auto &acceleration = entity->getComponent<Acceleration>();
+ if (auto velocityP = entity->getComponent<Velocity>()) {
+ auto &velocity = *velocityP;
+ if (auto accelerationP = entity->getComponent<Acceleration>()) {
+ auto &acceleration = *accelerationP;
velocity.changeVelocity(acceleration.getX(), acceleration.getY());
}
deltaX = velocity.getX();
deltaY = velocity.getY();
- } else if (entity->hasComponents(createMask({ComponentType::PATH_INDEX, ComponentType::SPEED}))) {
- auto &pathIndex = entity->getComponent<PathIndex>();
- auto &speed = entity->getComponent<Speed>();
+ } else if (auto components2 = entity->getComponents<PathIndex, Speed>()) {
+ auto[pathIndex, speed] =components2.value();
pathIndex.progress += speed.speed;
float deltaIndex = pathIndex.progress - pathIndex.index;
while (deltaIndex >= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2))) {
switch (gameData.path[pathIndex.index]) {
case 0:
deltaX += 1;
break;
case 1:
deltaX += 1;
deltaY += 1;
break;
case 2:
deltaY += 1;
break;
case 3:
deltaX += -1;
deltaY += 1;
break;
case 4:
deltaX += -1;
break;
case 5:
deltaX += -1;
deltaY += -1;
break;
case 6:
deltaY += -1;
break;
case 7:
deltaX += 1;
deltaY += -1;
break;
}
deltaIndex -= (gameData.path[pathIndex.index] % 2 == 0 ? 1 : sqrt(2));
if (pathIndex.index < gameData.path.size() - 1)
pathIndex.index++;
}
}
position.changePosition(deltaX, deltaY);
}
}
}
}
diff --git a/src/systems/MovementSystem.h b/src/systems/MovementSystem.h
index bb2ede3..089e90a 100644
--- a/src/systems/MovementSystem.h
+++ b/src/systems/MovementSystem.h
@@ -1,23 +1,18 @@
//
// Created by Ido Mozes on 02/07/2019.
//
#ifndef SDL_GAME_MOVEMENTSYSTEM_H
#define SDL_GAME_MOVEMENTSYSTEM_H
#include <iostream>
-#include "../components/Visibility.h"
-#include "../components/Velocity.h"
-#include "../components/Position.h"
-#include "../components/PathIndex.h"
-#include "../components/Speed.h"
#include "../GameData.h"
#include "../System.h"
class MovementSystem : public System{
public:
MovementSystem()= default;
~MovementSystem()= default;
void update(std::vector<std::shared_ptr<Entity>> *layers, GameData & gameData) override;
};
#endif //SDL_GAME_MOVEMENTSYSTEM_H
diff --git a/src/systems/RenderSystem.cpp b/src/systems/RenderSystem.cpp
index 9665c52..a0318d1 100644
--- a/src/systems/RenderSystem.cpp
+++ b/src/systems/RenderSystem.cpp
@@ -1,52 +1,64 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#include "RenderSystem.h"
#include "../components/Visibility.h"
#include "../components/Position.h"
-uint64_t RenderSystem::mask = createMask({ComponentType::VISIBILITY});
-
-
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,
- (685 + 400) * gameData.mapScale,
- 511 * gameData.mapScale, gameData.fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
+ (MAP_WIDTH + SIDEBAR_WIDTH + MENU_WIDTH) * gameData.mapScale,
+ 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);
}
void RenderSystem::update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) {
+
SDL_RenderClear(gameData.renderer);
for (int i = 0; i < N_LAYERS; ++i) {
for (auto &entity: layers[i]) {
- if (entity->hasComponents(mask)) {
- auto &visibility = entity->getComponent<Visibility>();
+ if (auto visibilityP = entity->getComponent<Visibility>()) {
+ auto &visibility = *visibilityP;
+ auto positionP = entity->getComponent<Position>();
SDL_Rect r = {int(visibility.getDstRect()->x * gameData.mapScale),
int(visibility.getDstRect()->y * gameData.mapScale),
int(visibility.getDstRect()->w * gameData.mapScale),
int(visibility.getDstRect()->h * gameData.mapScale)};
- if (entity->hasComponents(createMask({ComponentType::POSITION}))) {
- if (entity->hasComponents(createMask({ComponentType::POSITION}))) {
- auto &position = entity->getComponent<Position>();
- r.x = int(position.getX() * gameData.mapScale);
- r.y = int(position.getY() * gameData.mapScale);
- }
- r.x -= int(r.w / 2.0);
- r.y -= int(r.h / 2.0);
- r.x += int(150 * gameData.mapScale);
+ if (positionP) {
+ auto &position = *positionP;
+ r.x = int((position.getX() + SIDEBAR_WIDTH) * gameData.mapScale -r.w / 2.0);
+ r.y = int(position.getY() * gameData.mapScale -r.h / 2.0);
}
SDL_RenderCopy(gameData.renderer, visibility.getTexture(), nullptr, &r);
+ if (visibility.getRadios() != NO_RADIOS) {
+ int entityX, entityY;
+ if (positionP) {
+ entityX = r.x;
+ entityY = r.y;
+ } else {
+ entityX = r.x + int((visibility.getDstRect()->w / 2.0) * gameData.mapScale);
+ entityY = r.y + int((visibility.getDstRect()->h / 2.0) * gameData.mapScale);
+ }
+ auto draggableP = entity->getComponent<Draggable>();
+ bool isRed = draggableP ? !draggableP->isPlaceable : false;
+ filledCircleRGBA(gameData.renderer, entityX, entityY, visibility.getRadios(), isRed ? 255 : 0, 0, 0,
+ 100);
+ aacircleRGBA(gameData.renderer, entityX, entityY, visibility.getRadios(), isRed ? 255 : 0, 0, 0,
+ 150);
+
+ }
}
}
}
+
SDL_RenderPresent(gameData.renderer);
}
diff --git a/src/systems/RenderSystem.h b/src/systems/RenderSystem.h
index c3d9ff2..f1fd4a3 100644
--- a/src/systems/RenderSystem.h
+++ b/src/systems/RenderSystem.h
@@ -1,25 +1,26 @@
//
// Created by Ido Mozes on 23/06/2019.
//
#ifndef SDL2_GAME_RENDERSYSTEM_H
#define SDL2_GAME_RENDERSYSTEM_H
#include <iostream>
-#include "../components/Visibility.h"
#include "../System.h"
#include "../GameData.h"
+#include "SDL2_gfxPrimitives.h"
+
+
class RenderSystem : public System {
- static uint64_t mask;
public:
RenderSystem() { SDL_Init(SDL_INIT_EVERYTHING); }
void init(GameData &gameData);
void update(std::vector<std::shared_ptr<Entity>> *layers , GameData & gameData) override;
};
#endif //SDL2_GAME_RENDERSYSTEM_H
diff --git a/src/systems/SpawnSystem.cpp b/src/systems/SpawnSystem.cpp
index 67a351d..31639c2 100644
--- a/src/systems/SpawnSystem.cpp
+++ b/src/systems/SpawnSystem.cpp
@@ -1,32 +1,32 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#include "SpawnSystem.h"
-uint64_t SpawnSystem::mask = createMask({ComponentType::SEQUENCE, ComponentType::KIND});
void SpawnSystem::update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) {
for (int i = 0; i < N_LAYERS; ++i) {
std::vector<std::shared_ptr<Entity>> newEntities;
for (auto &entity: layers[i]) {
- if (entity->hasComponents(mask)) {
- int amount = entity->getComponent<Sequence>().getAmountReady();
- for (int i = 0; i < amount; ++i) {
+ if (auto components = entity->getComponents<Sequence, Kind, Speed>()) {
+ auto[sequence, kind, speed] = components.value();
+ int amount =sequence.getAmountReady();
+ for (int j = 0; j < amount; ++j) {
auto *newEntity = new Entity();
- newEntity->addComponent<Kind>(entity->getComponent<Kind>().kind);
+ newEntity->addComponent<Kind>(kind);
newEntity->addComponent<Position>(gameData.startingPoint.X, gameData.startingPoint.Y);
newEntity->addComponent<PathIndex>(0);
- newEntity->addComponent<Speed>(entity->getComponent<Speed>().speed);
- SDL_Surface *surface = gameData.assets[entity->getComponent<Kind>().kind];
+ newEntity->addComponent<Speed>(speed.speed);
+ SDL_Surface *surface = gameData.assets[kind.kind];
newEntity->addComponent<Visibility>(gameData.renderer, surface,
SDL_Rect{gameData.startingPoint.X, gameData.startingPoint.Y,
surface->w / 3, surface->h / 3});
newEntities.emplace_back(newEntity);
}
}
}
layers[i].insert(layers[i].end(), std::make_move_iterator(newEntities.begin()),
std::make_move_iterator(newEntities.end()));
}
}
diff --git a/src/systems/SpawnSystem.h b/src/systems/SpawnSystem.h
index ce93985..fdc00b5 100644
--- a/src/systems/SpawnSystem.h
+++ b/src/systems/SpawnSystem.h
@@ -1,24 +1,17 @@
//
// Created by Ido Mozes on 07/07/2019.
//
#ifndef SDL_GAME_SPAWNSYSTEM_H
#define SDL_GAME_SPAWNSYSTEM_H
#include <vector>
#include <iterator>
#include "../System.h"
-#include "../components/Sequence.h"
-#include "../components/Speed.h"
-#include "../components/Kind.h"
-#include "../components/Position.h"
-#include "../components/Visibility.h"
-#include "../components/PathIndex.h"
class SpawnSystem : public System {
- static uint64_t mask;
public:
void update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) override;
};
#endif //SDL_GAME_SPAWNSYSTEM_H
diff --git a/src/systems/SystemHeader.h b/src/systems/SystemHeader.h
new file mode 100644
index 0000000..b85af64
--- /dev/null
+++ b/src/systems/SystemHeader.h
@@ -0,0 +1,5 @@
+#include "../System.h"
+class __ : public System {
+public:
+ void update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) override;
+};
\ No newline at end of file
diff --git a/src/systems/SystemSource.cpp b/src/systems/SystemSource.cpp
new file mode 100644
index 0000000..025038c
--- /dev/null
+++ b/src/systems/SystemSource.cpp
@@ -0,0 +1,9 @@
+void __::update(std::vector<std::shared_ptr<Entity>> *layers, GameData &gameData) {
+ for (int i = 0; i < N_LAYERS; ++i) {
+ for (auto &entity: layers[i]) {
+
+ }
+ }
+}
+
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:36 PM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72898
Default Alt Text
(46 KB)
Attached To
Mode
R74 BloonsTD
Attached
Detach File
Event Timeline