From c12a8ede5a365835ba1e36d15c91c62ea96fac59 Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Tue, 6 Dec 2016 00:29:43 +0200 Subject: [PATCH] Implemented grid. Modified map renderer so that it can render tiles that have a different size than the map tiles. (Note: this change is not tested yet). --- assets/scenes/Game.scene | 10 + build/prepareAssets.py | 4 +- src/GameState.cpp | 5 +- src/components/Background.cpp | 80 ------ src/components/Background.h | 49 ---- src/components/Map.cpp | 113 ++++++++ src/components/Map.h | 73 +++++ src/components/basic/Grid.cpp | 146 ++++++++++ src/components/basic/Grid.h | 82 ++++++ src/components/basic/Sprite.cpp | 1 + src/components/{ => gui}/GuiController.cpp | 15 +- src/components/{ => gui}/GuiController.h | 4 +- src/components/items/Axe.h | 4 +- src/components/items/Giftable.h | 4 +- src/components/items/Hoe.cpp | 14 +- src/components/items/Hoe.h | 8 +- .../items/{ITool.h => IPlayerAction.h} | 12 +- src/components/items/Pickaxe.h | 4 +- src/components/items/Scythe.h | 4 +- src/components/items/WateringCan.cpp | 14 +- src/components/items/WateringCan.h | 8 +- src/components/items/Weapon.h | 4 +- src/components/player/PlayerInventory.cpp | 35 ++- src/components/player/PlayerInventory.h | 1 + ...BackgroundRenderer.cpp => MapRenderer.cpp} | 69 ++--- .../{BackgroundRenderer.h => MapRenderer.h} | 17 +- src/graphics/SpriteRenderer.cpp | 4 +- src/graphics/backend/SdlRenderer.cpp | 35 ++- src/graphics/backend/SdlRenderer.h | 27 +- src/input/GameKey.h | 1 + src/input/GameKeyConfiguration.cpp | 1 + src/model/Component.h | 6 +- src/model/GameObject.h | 6 +- src/model/TileSet.h | 42 +++ src/resources/ResourceInfo.h | 4 +- src/resources/Resources.g.h | 6 +- src/storage/Parsers.cpp | 130 ++++++--- src/storage/Parsers.h | 17 +- src/{model => utils}/ICloneable.h | 2 +- src/{model => utils}/INonAssignable.h | 2 +- src/utils/QTree.h | 265 ++++++++++++++++++ src/utils/Rect.h | 50 ++++ 42 files changed, 1090 insertions(+), 288 deletions(-) delete mode 100644 src/components/Background.cpp delete mode 100644 src/components/Background.h create mode 100644 src/components/Map.cpp create mode 100644 src/components/Map.h create mode 100644 src/components/basic/Grid.cpp create mode 100644 src/components/basic/Grid.h rename src/components/{ => gui}/GuiController.cpp (80%) rename src/components/{ => gui}/GuiController.h (93%) rename src/components/items/{ITool.h => IPlayerAction.h} (62%) rename src/graphics/{BackgroundRenderer.cpp => MapRenderer.cpp} (53%) rename src/graphics/{BackgroundRenderer.h => MapRenderer.h} (61%) create mode 100644 src/model/TileSet.h rename src/{model => utils}/ICloneable.h (94%) rename src/{model => utils}/INonAssignable.h (95%) create mode 100644 src/utils/QTree.h create mode 100644 src/utils/Rect.h diff --git a/assets/scenes/Game.scene b/assets/scenes/Game.scene index 1188e7f..6aa92f4 100644 --- a/assets/scenes/Game.scene +++ b/assets/scenes/Game.scene @@ -14,6 +14,10 @@ + + + + @@ -32,4 +36,10 @@ + + + + + + \ No newline at end of file diff --git a/build/prepareAssets.py b/build/prepareAssets.py index dab3d4f..2bc6011 100755 --- a/build/prepareAssets.py +++ b/build/prepareAssets.py @@ -49,8 +49,8 @@ FILE_TYPES = [ ([".sprite"], "Sprite"), ([".config"], "Configuration"), ([".scene"], "Scene"), - ([".back"], "Background"), - ([".csv"], "BackgroundLayer"), + ([".back"], "Map"), + ([".csv"], "MapLayer"), ([".item"], "Item"), ([".items"], "ItemCollection"), ] diff --git a/src/GameState.cpp b/src/GameState.cpp index 396e567..02112b2 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -11,9 +11,10 @@ namespace farmlands { GameState GameState::s_current; GameState::GameState() - : renderContext(), + : config(nullptr), + renderContext(), scene(nullptr), - config(nullptr), + itemPrefabs(), elapsedTime(0), gameInitialized(false) { diff --git a/src/components/Background.cpp b/src/components/Background.cpp deleted file mode 100644 index a62ee30..0000000 --- a/src/components/Background.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Background.cpp - * - * Created on: Dec 1, 2016 - * Author: tibi - */ - -#include -#include - -#include - -namespace farmlands { -namespace components { - - -Background::Background(size_t layerCount, size_t rowCount, size_t columnCount) - : m_cells(new Cell[layerCount * rowCount * columnCount]), - m_textures(new resources::ResourceId[layerCount]), - m_layers(layerCount), - m_rows(rowCount), - m_columns(columnCount) -{ -} - -Background::~Background() -{ - delete[] m_cells; - delete[] m_textures; -} - -model::Component* Background::clone() -{ - Background* clone = new Background(m_layers, m_rows, m_columns); - memcpy(clone->m_cells, m_cells, sizeof(Cell) * m_layers * m_rows * m_columns); - memcpy(clone->m_textures, m_textures, sizeof(resources::ResourceId) * m_layers); - - return clone; -} - -Cell Background::cell(size_t layer, size_t row, size_t col) const -{ - Assert(layer < m_layers, "Layer out of bounds."); - Assert(row < m_rows, "Row out of bounds."); - Assert(col < m_columns, "Column out of bounds."); - - return m_cells[layer * m_rows * m_columns + row * m_columns + col]; -} - -void Background::setCell(size_t layer, size_t row, size_t col, Cell value) -{ - Assert(layer < m_layers, "Layer out of bounds."); - Assert(row < m_rows, "Row out of bounds."); - Assert(col < m_columns, "Column out of bounds."); - - m_cells[layer * m_rows * m_columns + row * m_columns + col] = value; -} - -resources::ResourceId Background::texture(size_t layer) const -{ - Assert(layer < m_layers, "Layer out of bounds."); - return m_textures[layer]; -} - -void Background::setTexture(size_t layer, resources::ResourceId textureId) const -{ - Assert(layer < m_layers, "Layer out of bounds."); - m_textures[layer] = textureId; -} - -void Background::dump(unsigned level) -{ - for (unsigned i = 0; i < level; i++) - std::cout<<" "; - - std::cout << " .Component: Background\n"; -} - -} /* namespace model */ -} /* namespace farmlands */ diff --git a/src/components/Background.h b/src/components/Background.h deleted file mode 100644 index fae8795..0000000 --- a/src/components/Background.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Background.h - * - * Created on: Dec 1, 2016 - * Author: tibi - */ - -#ifndef MODEL_BACKGROUND_H_ -#define MODEL_BACKGROUND_H_ - -#include -#include - -namespace farmlands { -namespace components { - - typedef int16_t Cell; - - class Background: public model::Component - { - public: - Background(size_t layerCount, size_t rowCount, size_t columnCount); - virtual ~Background(); - - virtual model::Component* clone() override; - virtual void dump(unsigned level) override; - - inline size_t layerCount() const { return m_layers; } - inline size_t rowCount() const { return m_rows; } - inline size_t columnCount() const { return m_columns; } - - Cell cell(size_t layer, size_t row, size_t col) const; - void setCell(size_t layer, size_t row, size_t col, Cell value); - - resources::ResourceId texture(size_t layer) const; - void setTexture(size_t layer, resources::ResourceId textureId) const; - - private: - Cell* m_cells; - resources::ResourceId* m_textures; - size_t m_layers; - size_t m_rows; - size_t m_columns; - }; - -} /* namespace model */ -} /* namespace farmlands */ - -#endif /* MODEL_BACKGROUND_H_ */ diff --git a/src/components/Map.cpp b/src/components/Map.cpp new file mode 100644 index 0000000..2947032 --- /dev/null +++ b/src/components/Map.cpp @@ -0,0 +1,113 @@ +/* + * Background.cpp + * + * Created on: Dec 1, 2016 + * Author: tibi + */ + +#include +#include + +#include + +namespace farmlands { +namespace components { + +/****** MapLayer implementation ******/ + +MapLayer::MapLayer(size_t w, size_t h) + : name(), + tileSet(), + m_cells(new Cell[w * h]), + m_w(w), m_h(h) +{ +} + +MapLayer* MapLayer::clone() +{ + auto clone = new MapLayer(m_w, m_h); + clone->name = name; + clone->tileSet = tileSet; + memcpy(clone->m_cells, m_cells, sizeof(Cell) * m_w * m_h); + + return clone; +} + +Cell MapLayer::get(size_t row, size_t col) const +{ + Assert(row < m_w && col < m_h, "Index out of bounds."); + + return m_cells[row * m_w + col]; +} + +Cell* MapLayer::operator [](size_t row) +{ + Assert(row < m_w, "Index out of bounds."); + + return m_cells + (row * m_w); +} + +void MapLayer::set(size_t row, size_t col, Cell value) +{ + m_cells[row * m_w + col] = value; +} + + +/****** Map implementation ******/ + +Map::~Map() +{ + for (MapLayer* layer : m_layers) + delete layer; +} + +model::Component* Map::clone() +{ + Map* clone = new Map(); + clone->width = width; + clone->height = height; + + for (MapLayer* layer : m_layers) + { + MapLayer* layerClone = layer->clone(); + clone->m_layers.push_back(layerClone); + clone->m_layersStr.emplace(layerClone->name, clone); + } + + return clone; +} + +void Map::dump(unsigned level) +{ + for (unsigned i = 0; i < level; i++) + std::cout<<" "; + + std::cout << " .Component: Background\n"; +} + + +MapLayer& Map::addLayer(std::string name) +{ + Assert(m_layersStr.count(name) == 0, "Layer with same name already exists!"); + + MapLayer* layer = new MapLayer(width, height); + layer->name = name; + m_layers.push_back(layer); + m_layersStr.emplace(name, layer); + + return *layer; +} + +MapLayer& Map::layer(size_t index) +{ + return *m_layers.at(index); +} + +MapLayer& Map::layer(std::string name) +{ + return *(MapLayer*)m_layersStr.at(name); +} + +} /* namespace model */ +} /* namespace farmlands */ + diff --git a/src/components/Map.h b/src/components/Map.h new file mode 100644 index 0000000..2bdd9da --- /dev/null +++ b/src/components/Map.h @@ -0,0 +1,73 @@ +/* + * Map.h + * + * Created on: Dec 1, 2016 + * Author: tibi + */ + +#ifndef MODEL_MAP_H_ +#define MODEL_MAP_H_ + +#include +#include +#include + +#include +#include +#include + +namespace farmlands { +namespace components { + + typedef int16_t Cell; + + class MapLayer : utils::ICloneable + { + public: + MapLayer(size_t w, size_t h); + virtual MapLayer* clone() override; + + // Getters + Cell get(size_t row, size_t col) const; + Cell* operator[] (size_t row); + + // Setters + void set(size_t row, size_t col, Cell value); + + // Public properties + std::string name; + model::TileSet tileSet; + + private: + Cell* m_cells; + size_t m_w, m_h; + }; + + class Map: public model::Component + { + public: + virtual ~Map(); + + virtual model::Component* clone() override; + virtual void dump(unsigned level) override; + + // Layer operations + MapLayer& addLayer(std::string name); + MapLayer& layer(size_t index); + MapLayer& layer(std::string name); + + size_t layersCount() const { return m_layers.size(); } + + // Size counted in cells + size_t width, height; + size_t cellWidth, cellHeight; + + private: + std::vector m_layers; + std::map m_layersStr; // gcc gives error if I use MapLayer* + }; + +} /* namespace model */ +} /* namespace farmlands */ + +#endif /* MODEL_BACKGROUND_H_ */ diff --git a/src/components/basic/Grid.cpp b/src/components/basic/Grid.cpp new file mode 100644 index 0000000..157403c --- /dev/null +++ b/src/components/basic/Grid.cpp @@ -0,0 +1,146 @@ +/* + * Grid.cpp + * + * Created on: Dec 4, 2016 + * Author: tibi + */ + +#include +#include +#include +#include + +#include + +using namespace farmlands::model; +using namespace farmlands::utils; + +namespace farmlands { +namespace components { +namespace basic { + +Grid::Grid() + : m_bounds(0, 0, 1, 1), + m_grid(nullptr) +{ +} + +Grid::~Grid() +{ + if (m_grid != nullptr) + delete[] m_grid; +} + +model::Component* Grid::clone() +{ + Grid* clone = new Grid(); + clone->m_bounds = m_bounds; + + return clone; +} + +void Grid::dump(unsigned level) +{ + for (unsigned i = 0; i < level; i++) + std::cout<<" "; + + std::cout << " .Component: Grid bounds=["; + std::cout << m_bounds.x << "," << m_bounds.y << ","; + std::cout << m_bounds.w << "," << m_bounds.h << "]\n"; +} + +void Grid::onInitialize() +{ + // Allocate memory + m_grid = new model::GameObject*[m_bounds.w * m_bounds.h]; + memset(m_grid, 0, sizeof(model::GameObject*) * m_bounds.w * m_bounds.h); + + // Add objects + for (auto it = gameObject->childrenBegin(); it != gameObject->childrenEnd(); it++) + { + GameObject* obj = *it; + + // Get transform + Transform* tr = obj->component(); + if (tr == nullptr) + { + std::cerr << "Grid: ignoring object " << obj->name << ": object has no transform."; + continue; + } + + // Compute grid position(s) + Rect bounds(tr->x, tr->y, roundf(tr->w), roundf(tr->h)); + if (!bounds.intersects(m_bounds)) + { + std::cerr << "Grid: ignoring object " << obj->name << ": object outside allowed bounds."; + continue; + } + + // Set + for (int y = bounds.y; y < bounds.y + bounds.h; y++) + for (int x = bounds.x; x < bounds.x + bounds.w; x++) + set(obj, x, y, false); + } +} + +void Grid::setBounds(const utils::Rect& bounds) +{ + if (m_grid == nullptr) + m_bounds = bounds; + + else + { + // Get rid of old grid + delete[] m_grid; + m_grid = nullptr; + + m_bounds = bounds; + + // Reinitialize + onInitialize(); + } +} + +utils::Rect Grid::bounds() const +{ + return m_bounds; +} + +model::GameObject* Grid::get(int x, int y) +{ + Assert(m_grid != nullptr, "Grid not initialized!!!"); + + if (m_bounds.contains(x, y)) + { + int gx = x - m_bounds.x; + int gy = y - m_bounds.y; + + return m_grid[gy * m_bounds.w + gx]; + } + + return nullptr; +} + +void Grid::set(model::GameObject* obj, int x, int y, bool throwOnOverwrite) +{ + int gx = x - m_bounds.x; + int gy = y - m_bounds.y; + int index = gy * m_bounds.w + gx; + + if (m_grid[index] != nullptr) + { + if (throwOnOverwrite) + THROW(InvalidArgumentException, "Position already occupied!"); + else + { + std::cerr << "Grid: cannot set " << obj->name << " to position " << x << ", " << y; + std::cerr << ": cell already occupied"; + } + } + + m_grid[index] = obj; +} + +} /* namespace basic */ +} /* namespace components */ +} /* namespace farmlands */ diff --git a/src/components/basic/Grid.h b/src/components/basic/Grid.h new file mode 100644 index 0000000..6e97965 --- /dev/null +++ b/src/components/basic/Grid.h @@ -0,0 +1,82 @@ +/* + * Grid.h + * + * Created on: Dec 4, 2016 + * Author: tibi + */ + +#ifndef COMPONENTS_BASIC_GRID_H_ +#define COMPONENTS_BASIC_GRID_H_ + +#include +#include +#include + +namespace farmlands { +namespace components { +namespace basic { + + /** + * Manages all the children components so that they are in a grid. + * Uses the x and y positions of the game objects to find the grid position. + * + * Positions are read in the initialize method. + * If there are multiple objects in the same cell position, only the first one is considered. + * If there are objects outside the given bounds, they are ignored. + * + * + * Operation: + * After initialization, use get to obtain the element in a specific grid position. + * When adding a game object to the parent, make sure to call "update on the grid". + * + * The update/move methods check if a non-empty cell will be written over, and they throw + * an exception. + */ + class Grid: public model::Component + { + public: + Grid(); + virtual ~Grid(); + + virtual model::Component* clone() override; + virtual void dump(unsigned level) override; + + /** + * Initializes the grid component. + * + * When this method gets called, the grid puts all the children game objects in corresponding + * cells. The object transform is used to calculate what cells the objects occupy (x, y, w, h). + * + * If there are multiple objects in the same cell position, only the first one is considered. + * (a warning is printed to stderr). + * Also, if there are objects outside the given bounds, they are ignored. + */ + virtual void onInitialize() override; + + // Setters + + /** + * Sets the bounds of the grid. + * + * If the bounds are changed after initialization, the positions will be updated. + */ + void setBounds(const utils::Rect& bounds); + + // Getters + utils::Rect bounds() const; + + // Operations + model::GameObject* get(int x, int y); + + private: + void set(model::GameObject* obj, int x, int y, bool throwOnOverwrite); + + utils::Rect m_bounds; + model::GameObject** m_grid; + }; + +} /* namespace basic */ +} /* namespace components */ +} /* namespace farmlands */ + +#endif /* COMPONENTS_BASIC_GRID_H_ */ diff --git a/src/components/basic/Sprite.cpp b/src/components/basic/Sprite.cpp index faa4a22..a79029f 100644 --- a/src/components/basic/Sprite.cpp +++ b/src/components/basic/Sprite.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include diff --git a/src/components/GuiController.cpp b/src/components/gui/GuiController.cpp similarity index 80% rename from src/components/GuiController.cpp rename to src/components/gui/GuiController.cpp index b2077d0..4adbf3b 100644 --- a/src/components/GuiController.cpp +++ b/src/components/gui/GuiController.cpp @@ -6,16 +6,18 @@ */ #include -#include +#include #include #include namespace farmlands { namespace components { +namespace gui { GuiController::GuiController() - : m_canvas() + : m_canvas(), + m_context(nullptr) { } @@ -36,16 +38,16 @@ void GuiController::onInitialize() m_canvas.setSize(m_context->viewport.width, m_context->viewport.height); // Add a text element - auto text = new gui::widgets::TextArea(); + auto text = new farmlands::gui::widgets::TextArea(); text->setText("Hello world!"); text->setSize(50, 5); text->setPosition(100, 10); text->setColor(0, 1, 0); text->setBackColor(0.5f, 0, 0, 0.5f); text->setTextSize(11); - text->setHorizontalWrap(gui::widgets::TextHorizontalWrapping::Ellipsis); - text->setVerticalWrap(gui::widgets::TextVerticalWrapping::Trim); - text->setAlignment(gui::widgets::TextAlign::BottomRight); + text->setHorizontalWrap(farmlands::gui::widgets::TextHorizontalWrapping::Ellipsis); + text->setVerticalWrap(farmlands::gui::widgets::TextVerticalWrapping::Trim); + text->setAlignment(farmlands::gui::widgets::TextAlign::BottomRight); m_canvas.addChild(text); } @@ -82,5 +84,6 @@ void GuiController::dump(unsigned level) std::cout << " .Component: DebugController\n"; } +} } /* namespace controller */ } /* namespace farmlands */ diff --git a/src/components/GuiController.h b/src/components/gui/GuiController.h similarity index 93% rename from src/components/GuiController.h rename to src/components/gui/GuiController.h index 03e9c3f..96f5e81 100644 --- a/src/components/GuiController.h +++ b/src/components/gui/GuiController.h @@ -16,6 +16,7 @@ namespace farmlands { namespace components { +namespace gui { class GuiController : public model::Component { @@ -34,10 +35,11 @@ namespace components { virtual void onRender() override; private: - gui::layout::Canvas m_canvas; + farmlands::gui::layout::Canvas m_canvas; graphics::RenderContext* m_context; }; +} } /* namespace controller */ } /* namespace farmlands */ diff --git a/src/components/items/Axe.h b/src/components/items/Axe.h index a8c0f9b..7f6f4cc 100644 --- a/src/components/items/Axe.h +++ b/src/components/items/Axe.h @@ -8,14 +8,14 @@ #ifndef COMPONENTS_ITEMS_AXE_H_ #define COMPONENTS_ITEMS_AXE_H_ -#include +#include #include namespace farmlands { namespace components { namespace items { - class Axe: public model::Component, public ITool + class Axe: public model::Component, public IPlayerAction { public: Axe(); diff --git a/src/components/items/Giftable.h b/src/components/items/Giftable.h index ae101b7..efec8c2 100644 --- a/src/components/items/Giftable.h +++ b/src/components/items/Giftable.h @@ -8,14 +8,14 @@ #ifndef CONTROLLER_ITEMS_GIFTABLE_H_ #define CONTROLLER_ITEMS_GIFTABLE_H_ -#include +#include #include namespace farmlands { namespace components { namespace items { - class Giftable: public model::Component, public ITool + class Giftable: public model::Component, public IPlayerAction { public: Giftable(); diff --git a/src/components/items/Hoe.cpp b/src/components/items/Hoe.cpp index e76b699..1b86c34 100644 --- a/src/components/items/Hoe.cpp +++ b/src/components/items/Hoe.cpp @@ -21,7 +21,7 @@ namespace components { namespace items { Hoe::Hoe() - : m_back(nullptr) + : m_map(nullptr) { } @@ -47,15 +47,15 @@ void Hoe::onInitialize() model::GameObject* root = &GameState::current().scene->root; // Find background object - auto it = root->findByComponent(); + auto it = root->findByComponent(); Assert(it != root->childrenEnd(), "Can't find background game object."); - m_back = (*it)->component(); + m_map = (*it)->component(); } void Hoe::performAction(float x, float y, model::Direction d) { - Assert(m_back, "No background object!!!"); + Assert(m_map, "No background object!!!"); // Compute watering position float digX, digY; @@ -65,11 +65,11 @@ void Hoe::performAction(float x, float y, model::Direction d) size_t row = floorf(digY); // See what the cell contains - Cell backCell = m_back->cell(0, row, col); - Cell soilCell = m_back->cell(1, row, col); + Cell backCell = m_map->layer(0).get(row, col); + Cell soilCell = m_map->layer(1).get(row, col); if (groundIsDirt(backCell) && soilCell == Ground::None) - m_back->setCell(1, row, col, Ground::SoilDry); + m_map->layer(1)[row][col] = Ground::SoilDry; } } /* namespace items */ diff --git a/src/components/items/Hoe.h b/src/components/items/Hoe.h index 0054766..ccfcf0e 100644 --- a/src/components/items/Hoe.h +++ b/src/components/items/Hoe.h @@ -8,8 +8,8 @@ #ifndef COMPONENTS_ITEMS_HOE_H_ #define COMPONENTS_ITEMS_HOE_H_ -#include -#include +#include +#include #include #include @@ -17,7 +17,7 @@ namespace farmlands { namespace components { namespace items { - class Hoe: public model::Component, public ITool + class Hoe: public model::Component, public IPlayerAction { public: Hoe(); @@ -31,7 +31,7 @@ namespace items { virtual void performAction(float x, float y, model::Direction d) override; private: - Background* m_back; + Map* m_map; }; } /* namespace items */ diff --git a/src/components/items/ITool.h b/src/components/items/IPlayerAction.h similarity index 62% rename from src/components/items/ITool.h rename to src/components/items/IPlayerAction.h index 1510963..14c8810 100644 --- a/src/components/items/ITool.h +++ b/src/components/items/IPlayerAction.h @@ -5,8 +5,8 @@ * Author: tibi */ -#ifndef COMPONENTS_ITEMS_ITOOL_H_ -#define COMPONENTS_ITEMS_ITOOL_H_ +#ifndef COMPONENTS_ITEMS_IPLAYERACTION_H_ +#define COMPONENTS_ITEMS_IPLAYERACTION_H_ #include @@ -14,15 +14,17 @@ namespace farmlands { namespace components { namespace items { - class ITool + class IPlayerAction { public: - virtual ~ITool() { } + virtual ~IPlayerAction() { } virtual void performAction(float playerX, float playerY, model::Direction) = 0; + + float actionCost; }; } /* namespace items */ } /* namespace components */ } /* namespace farmlands */ -#endif /* COMPONENTS_ITEMS_ITOOL_H_ */ +#endif /* COMPONENTS_ITEMS_IPLAYERACTION_H_ */ diff --git a/src/components/items/Pickaxe.h b/src/components/items/Pickaxe.h index aead1fa..1edeb99 100644 --- a/src/components/items/Pickaxe.h +++ b/src/components/items/Pickaxe.h @@ -8,14 +8,14 @@ #ifndef COMPONENTS_ITEMS_PICKAXE_H_ #define COMPONENTS_ITEMS_PICKAXE_H_ -#include +#include #include namespace farmlands { namespace components { namespace items { - class Pickaxe: public model::Component, public ITool + class Pickaxe: public model::Component, public IPlayerAction { public: Pickaxe(); diff --git a/src/components/items/Scythe.h b/src/components/items/Scythe.h index c2b1a55..583626e 100644 --- a/src/components/items/Scythe.h +++ b/src/components/items/Scythe.h @@ -8,14 +8,14 @@ #ifndef COMPONENTS_ITEMS_SCYTHE_H_ #define COMPONENTS_ITEMS_SCYTHE_H_ -#include +#include #include namespace farmlands { namespace components { namespace items { - class Scythe: public model::Component, public ITool + class Scythe: public model::Component, public IPlayerAction { public: Scythe(); diff --git a/src/components/items/WateringCan.cpp b/src/components/items/WateringCan.cpp index 8067974..9f394ff 100644 --- a/src/components/items/WateringCan.cpp +++ b/src/components/items/WateringCan.cpp @@ -22,7 +22,7 @@ namespace items { WateringCan::WateringCan() : capacity(10), amountLeft(10), - m_back(nullptr) + m_map(nullptr) { } @@ -52,15 +52,15 @@ void WateringCan::onInitialize() model::GameObject* root = &GameState::current().scene->root; // Find background object - auto it = root->findByComponent(); + auto it = root->findByComponent(); Assert(it != root->childrenEnd(), "Can't find background game object."); - m_back = (*it)->component(); + m_map = (*it)->component(); } void WateringCan::performAction(float x, float y, model::Direction d) { - Assert(m_back, "No background object!!!"); + Assert(m_map, "No background object!!!"); // Compute watering position float digX, digY; @@ -70,8 +70,8 @@ void WateringCan::performAction(float x, float y, model::Direction d) size_t row = floorf(digY); // See what the cell contains - Cell backCell = m_back->cell(0, row, col); - Cell soilCell = m_back->cell(1, row, col); + Cell backCell = m_map->layer(0).get(row, col); + Cell soilCell = m_map->layer(1).get(row, col); // If there is water, fill can if (backCell == Ground::Water) @@ -83,7 +83,7 @@ void WateringCan::performAction(float x, float y, model::Direction d) // If there is dry soil, wet it if (groundIsDrySoil(soilCell) && amountLeft > 0) { - m_back->setCell(1, row, col, Ground::SoilWet); + m_map->layer(1)[row][col] = Ground::SoilWet; --amountLeft; std::cout << "Watering can: " << amountLeft << "\n"; diff --git a/src/components/items/WateringCan.h b/src/components/items/WateringCan.h index 5c920da..99cfed7 100644 --- a/src/components/items/WateringCan.h +++ b/src/components/items/WateringCan.h @@ -8,8 +8,8 @@ #ifndef COMPONENTS_ITEMS_WATERINGCAN_H_ #define COMPONENTS_ITEMS_WATERINGCAN_H_ -#include -#include +#include +#include #include #include @@ -17,7 +17,7 @@ namespace farmlands { namespace components { namespace items { - class WateringCan: public model::Component, public ITool + class WateringCan: public model::Component, public IPlayerAction { public: WateringCan(); @@ -35,7 +35,7 @@ namespace items { uint32_t amountLeft; private: - Background* m_back; + Map* m_map; }; } /* namespace items */ diff --git a/src/components/items/Weapon.h b/src/components/items/Weapon.h index 5208966..b3e46a8 100644 --- a/src/components/items/Weapon.h +++ b/src/components/items/Weapon.h @@ -9,7 +9,7 @@ #define CONTROLLER_ITEMS_WEAPON_H_ #include -#include +#include #include #include @@ -17,7 +17,7 @@ namespace farmlands { namespace components { namespace items { - class Weapon: public model::Component, public ITool + class Weapon: public model::Component, public IPlayerAction { public: Weapon(); diff --git a/src/components/player/PlayerInventory.cpp b/src/components/player/PlayerInventory.cpp index d7833eb..9ba92bd 100644 --- a/src/components/player/PlayerInventory.cpp +++ b/src/components/player/PlayerInventory.cpp @@ -91,6 +91,7 @@ void PlayerInventory::onInitialize() bool PlayerInventory::onEvent(SDL_Event& event) { handleAttackEvents(event); + handleCurrentInventoryItemEvent(event); handleInventoryEvents(event); return false; } @@ -106,8 +107,17 @@ void PlayerInventory::onPreRender() if (currentItem) { Transform* itemTransf = currentItem->component(); - itemTransf->x = 0.2f; - itemTransf->y = -0.8f; + + if (m_playerMovement->facingDirection & Direction::East) + { + itemTransf->x = 0.2f; + itemTransf->y = -0.8f; + } + else if (m_playerMovement->facingDirection & Direction::West) + { + itemTransf->x = -0.8f; + itemTransf->y = -0.8f; + } } } @@ -123,14 +133,14 @@ void PlayerInventory::handleAttackEvents(SDL_Event& event) // Call components which implement ITool for (auto it = currentItem->componentsBegin(); it != currentItem->componentsEnd(); it++) { - ITool* tool = dynamic_cast(it->second); + IPlayerAction* tool = dynamic_cast(it->second); if (tool != nullptr) tool->performAction(x, y, d); } } } -void PlayerInventory::handleInventoryEvents(SDL_Event& event) +void PlayerInventory::handleCurrentInventoryItemEvent(SDL_Event& event) { // See what key was pressed int slot = -1; @@ -159,8 +169,6 @@ void PlayerInventory::handleInventoryEvents(SDL_Event& event) // Enable new object currentItem->setEnabled(true); - - gameObject->dumpTree(0); } else if (0 <= slot) { @@ -168,6 +176,21 @@ void PlayerInventory::handleInventoryEvents(SDL_Event& event) } } +void PlayerInventory::handleInventoryEvents(SDL_Event& event) +{ + bool inventoryDrop = Input::instance().down(GameKey::InventoryDrop, event); + + if (inventoryDrop && currentItem != nullptr) + { + // For now we delete the object. We may want to save its state later on + m_inventory->destroyChild(currentItem); + + currentItemIndex = -1; + currentItem = nullptr; + currentWeapon = nullptr; + } +} + } /* namespace player */ } /* namespace components */ } /* namespace farmlands */ diff --git a/src/components/player/PlayerInventory.h b/src/components/player/PlayerInventory.h index 1ce412a..4eba949 100644 --- a/src/components/player/PlayerInventory.h +++ b/src/components/player/PlayerInventory.h @@ -42,6 +42,7 @@ namespace player { private: void handleAttackEvents(SDL_Event& event); + void handleCurrentInventoryItemEvent(SDL_Event& event); void handleInventoryEvents(SDL_Event& event); // Inventory diff --git a/src/graphics/BackgroundRenderer.cpp b/src/graphics/MapRenderer.cpp similarity index 53% rename from src/graphics/BackgroundRenderer.cpp rename to src/graphics/MapRenderer.cpp index 1ef45ce..aefd166 100644 --- a/src/graphics/BackgroundRenderer.cpp +++ b/src/graphics/MapRenderer.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,33 +23,36 @@ using namespace farmlands::resources; namespace farmlands { namespace graphics { -BackgroundRenderer::BackgroundRenderer() +MapRenderer::MapRenderer() : m_context(nullptr), - m_back(nullptr) + m_map(nullptr) { } -BackgroundRenderer::~BackgroundRenderer() +MapRenderer::~MapRenderer() { } -model::Component* BackgroundRenderer::clone() +model::Component* MapRenderer::clone() { - return new BackgroundRenderer(); + return new MapRenderer(); } -void BackgroundRenderer::onInitialize() +void MapRenderer::onInitialize() { Assert(gameObject != nullptr, "Component not properly initialized!"); m_context = &GameState::current().renderContext; - m_back = gameObject->component(); + m_map = gameObject->component(); } -void BackgroundRenderer::onRender() +void MapRenderer::onRender() { - float cellW = m_context->viewport.pixelsPerUnitX * m_context->camera()->scale; - float cellH = m_context->viewport.pixelsPerUnitY * m_context->camera()->scale; + Assert(m_map != nullptr, "Can't find map component."); + + float scale = m_context->camera()->scale; + float cellW = m_map->cellWidth * scale; + float cellH = m_map->cellHeight * scale; // Compute how many cells fit on the screen float cellsOnScreenX = m_context->viewport.width / cellW; @@ -61,37 +64,40 @@ void BackgroundRenderer::onRender() int maxCellY = ceilf(m_context->cameraTransform()->y + cellsOnScreenY / 2); // Clamp cell positions - minCellX = clamp(minCellX, 0, (int)m_back->columnCount() - 1); - maxCellX = clamp(maxCellX, 0, (int)m_back->columnCount() - 1); - minCellY = clamp(minCellY, 0, (int)m_back->rowCount() - 1); - maxCellY = clamp(maxCellY, 0, (int)m_back->rowCount() - 1); + minCellX = clamp(minCellX, 0, (int)m_map->width - 1); + maxCellX = clamp(maxCellX, 0, (int)m_map->width - 1); + minCellY = clamp(minCellY, 0, (int)m_map->height - 1); + maxCellY = clamp(maxCellY, 0, (int)m_map->height - 1); // Draw each layer - for (size_t i = 0; i < m_back->layerCount(); i++) + for (size_t i = 0; i < m_map->layersCount(); i++) { - resources::ResourceId textureId = m_back->texture(i); + MapLayer& layer = m_map->layer(i); + resources::ResourceId textureId = layer.tileSet.texture; // Render only visible tiles for (int y = minCellY; y <= maxCellY; y++) for (int x = minCellX; x <= maxCellX; x++) { - int cellId = m_back->cell(i, y, x); + Cell cellId = layer[y][x]; // Obtain texture SDL_Texture* texture = ResourceManager::instance().texture(textureId); // Calculate source rect SDL_Rect src; - getCell(texture, cellId, &src.x, &src.y); - src.w = m_context->viewport.pixelsPerUnitX; - src.h = m_context->viewport.pixelsPerUnitY; + auto r = layer.tileSet.getCell(cellId); + src.x = r.x; + src.y = r.y; + src.w = r.w; + src.h = r.h; // Compute destination rect SDL_Rect dest; dest.x = floorf(m_context->xToScreen(x)); - dest.y = floorf(m_context->yToScreen(y)); - dest.w = ceilf(cellW); - dest.h = ceilf(cellH); + dest.y = floorf(m_context->yToScreen(y + 1) - src.h * scale); // Anchor should be bottom right corner + dest.w = ceilf(src.w * scale); + dest.h = ceilf(src.h * scale); // Blit SdlRenderer::instance().renderTexture(texture, &src, &dest); @@ -99,20 +105,7 @@ void BackgroundRenderer::onRender() } } -void BackgroundRenderer::getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY) -{ - int texWidth, texHeight; - SdlRenderer::instance().getTextureSize(texture, &texWidth, &texHeight); - - int ppuX = m_context->viewport.pixelsPerUnitX; - int ppuY = m_context->viewport.pixelsPerUnitY; - - // Compute texture coordinates - *outX = (cell * ppuX) % texWidth; - *outY = ((cell * ppuX) / texWidth) * ppuY; -} - -void BackgroundRenderer::dump(unsigned level) +void MapRenderer::dump(unsigned level) { for (unsigned i = 0; i < level; i++) std::cout<<" "; diff --git a/src/graphics/BackgroundRenderer.h b/src/graphics/MapRenderer.h similarity index 61% rename from src/graphics/BackgroundRenderer.h rename to src/graphics/MapRenderer.h index 0fb124f..27dd9e0 100644 --- a/src/graphics/BackgroundRenderer.h +++ b/src/graphics/MapRenderer.h @@ -5,23 +5,23 @@ * Author: tibi */ -#ifndef GRAPHICS_BACKGROUNDRENDERER_H_ -#define GRAPHICS_BACKGROUNDRENDERER_H_ +#ifndef GRAPHICS_MAPRENDERER_H_ +#define GRAPHICS_MAPRENDERER_H_ #include -#include #include #include +#include #include namespace farmlands { namespace graphics { - class BackgroundRenderer: public model::Component + class MapRenderer: public model::Component { public: - BackgroundRenderer(); - virtual ~BackgroundRenderer(); + MapRenderer(); + virtual ~MapRenderer(); virtual model::Component* clone() override; virtual void dump(unsigned level) override; @@ -30,14 +30,13 @@ namespace graphics { virtual void onRender() override; private: - void getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY); // Private fields graphics::RenderContext* m_context; - components::Background* m_back; + components::Map* m_map; }; } /* namespace graphics */ } /* namespace farmlands */ -#endif /* GRAPHICS_BACKGROUNDRENDERER_H_ */ +#endif /* GRAPHICS_MAPRENDERER_H_ */ diff --git a/src/graphics/SpriteRenderer.cpp b/src/graphics/SpriteRenderer.cpp index 01a3341..0ede68c 100644 --- a/src/graphics/SpriteRenderer.cpp +++ b/src/graphics/SpriteRenderer.cpp @@ -22,8 +22,8 @@ namespace farmlands { namespace graphics { SpriteRenderer::SpriteRenderer() - : m_transform(nullptr), - m_context(nullptr), + : m_context(nullptr), + m_transform(nullptr), m_sprite(nullptr) { } diff --git a/src/graphics/backend/SdlRenderer.cpp b/src/graphics/backend/SdlRenderer.cpp index 6e75fef..862c491 100644 --- a/src/graphics/backend/SdlRenderer.cpp +++ b/src/graphics/backend/SdlRenderer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -29,7 +30,8 @@ SdlRenderer& SdlRenderer::instance() SdlRenderer::SdlRenderer() : m_sdlWindow(nullptr), - m_sdlRenderer(nullptr) + m_sdlRenderer(nullptr), + m_queue() { } @@ -85,10 +87,20 @@ void SdlRenderer::renderBegin() { SDL_SetRenderDrawColor(m_sdlRenderer, 0, 0, 0, 255); SDL_RenderClear(m_sdlRenderer); + + m_queue.clear(); } void SdlRenderer::renderEnd() { + // Sort by z index, but keep relative order + std::stable_sort(m_queue.begin(), m_queue.end(), compareQueueItems); + + // Render everything to screen + for (auto item : m_queue) + SDL_RenderCopy(m_sdlRenderer, item.texture, &item.src, &item.dest); + + // Swap buffers SDL_RenderPresent(m_sdlRenderer); } @@ -97,6 +109,17 @@ void SdlRenderer::renderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* d SDL_RenderCopy(m_sdlRenderer, texture, src, dest); } +void SdlRenderer::queueRenderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* dest, float z) +{ + QueueItem q; + q.texture = texture; + q.src = *src; + q.dest = *dest; + q.z = z; + + m_queue.push_back(q); +} + SDL_Texture* SdlRenderer::renderText(const std::string& text, TTF_Font* font, SDL_Color color) { assert(font != nullptr); @@ -116,6 +139,16 @@ void SdlRenderer::getTextureSize(SDL_Texture* texture, int* width, int* height) SDL_QueryTexture(texture, NULL, NULL, width, height); } +bool SdlRenderer::compareQueueItems(const QueueItem& a, const QueueItem& b) +{ + // First criteria is the z index + if (a.z != b.z) + return (a.z < b.z); + + // Second criteria is the Y position + return (a.dest.y < b.dest.y); +} + } } /* namespace graphics */ } /* namespace farmlands */ diff --git a/src/graphics/backend/SdlRenderer.h b/src/graphics/backend/SdlRenderer.h index 7d247a4..f401c43 100644 --- a/src/graphics/backend/SdlRenderer.h +++ b/src/graphics/backend/SdlRenderer.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -49,10 +50,17 @@ namespace backend { SDL_Texture* renderText(const std::string& text, TTF_Font* font, SDL_Color color); /** - * Renders a textue + * Renders a texture immediately */ void renderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* dest); + /** + * Queues a texture for rendering later. + * + * When using the queue, you can specify a z index. + */ + void queueRenderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* dest, float z = 0.0f); + /** * Returns the size of a texture */ @@ -68,8 +76,25 @@ namespace backend { SdlRenderer(); + /** + * Render queue item + */ + struct QueueItem + { + SDL_Texture* texture; + SDL_Rect src; + SDL_Rect dest; + float z; + }; + + /** + * Compares queue items. + */ + static bool compareQueueItems(const QueueItem& a, const QueueItem& b); + SDL_Window* m_sdlWindow; SDL_Renderer* m_sdlRenderer; + std::vector m_queue; static SdlRenderer s_instance; }; diff --git a/src/input/GameKey.h b/src/input/GameKey.h index f126888..17a58a3 100644 --- a/src/input/GameKey.h +++ b/src/input/GameKey.h @@ -42,6 +42,7 @@ namespace input { Inventory8, Inventory9, Inventory10, + InventoryDrop, // Debug keys Debug_ZoomIn, diff --git a/src/input/GameKeyConfiguration.cpp b/src/input/GameKeyConfiguration.cpp index 1c1e1c0..0975c99 100644 --- a/src/input/GameKeyConfiguration.cpp +++ b/src/input/GameKeyConfiguration.cpp @@ -46,6 +46,7 @@ namespace input { SDL_SCANCODE_8, // Inventory8 SDL_SCANCODE_9, // Inventory9 SDL_SCANCODE_0, // Inventory10 + SDL_SCANCODE_G, // InventoryDrop // Debug SDL_SCANCODE_KP_PLUS, // Debug_ZoomIn, diff --git a/src/model/Component.h b/src/model/Component.h index 1825a12..9696727 100644 --- a/src/model/Component.h +++ b/src/model/Component.h @@ -8,8 +8,8 @@ #ifndef COMPONENT_H_ #define COMPONENT_H_ -#include -#include +#include +#include #include @@ -18,7 +18,7 @@ namespace model { class GameObject; - class Component : public INonAssignable, public ICloneable + class Component : public utils::INonAssignable, public utils::ICloneable { public: Component(); diff --git a/src/model/GameObject.h b/src/model/GameObject.h index fed3830..450ed17 100644 --- a/src/model/GameObject.h +++ b/src/model/GameObject.h @@ -8,9 +8,9 @@ #ifndef GAMEOBJECT_H_ #define GAMEOBJECT_H_ -#include -#include #include +#include +#include #include @@ -24,7 +24,7 @@ namespace model { class Component; class RenderContext; - class GameObject : public INonAssignable, public ICloneable + class GameObject : public utils::INonAssignable, public utils::ICloneable { public: typedef std::unordered_map ComponentContainer; diff --git a/src/model/TileSet.h b/src/model/TileSet.h new file mode 100644 index 0000000..9e0c7bd --- /dev/null +++ b/src/model/TileSet.h @@ -0,0 +1,42 @@ +/* + * TileMap.h + * + * Created on: Dec 5, 2016 + * Author: tibi + */ + +#ifndef MODEL_TILESET_H_ +#define MODEL_TILESET_H_ + +#include +#include + +namespace farmlands { +namespace model { + + struct TileSet + { + resources::ResourceId texture; + size_t width, height; + size_t tileWidth, tileHeight; + + // Gets the boundary of a cell based on its id + utils::Rect getCell(int cellId); + }; + + inline utils::Rect TileSet::getCell(int cellId) + { + utils::Rect r; + size_t tilesRow = width / tileWidth; + + r.x = tileWidth * (cellId % tilesRow); + r.y = tileHeight * (cellId / tilesRow); + r.w = tileWidth; + r.h = tileHeight; + return r; + } + +} /* namespace model */ +} /* namespace farmlands */ + +#endif /* MODEL_TILESET_H_ */ diff --git a/src/resources/ResourceInfo.h b/src/resources/ResourceInfo.h index f65eb06..23f0f66 100644 --- a/src/resources/ResourceInfo.h +++ b/src/resources/ResourceInfo.h @@ -19,8 +19,8 @@ namespace resources { Sprite, Configuration, Scene, - Background, - BackgroundLayer, + Map, + MapLayer, Item, ItemCollection, }; diff --git a/src/resources/Resources.g.h b/src/resources/Resources.g.h index 037a333..cccdd1f 100644 --- a/src/resources/Resources.g.h +++ b/src/resources/Resources.g.h @@ -87,9 +87,9 @@ namespace resources { { "tilesets/Ground.png", ResourceType::Texture }, { "ui/mini_inventory.png", ResourceType::Texture }, { "ui/cursor.png", ResourceType::Texture }, - { "levels/Farm_Background.csv", ResourceType::BackgroundLayer }, - { "levels/Farm.back", ResourceType::Background }, - { "levels/Farm_Soil.csv", ResourceType::BackgroundLayer }, + { "levels/Farm_Background.csv", ResourceType::MapLayer }, + { "levels/Farm.back", ResourceType::Map }, + { "levels/Farm_Soil.csv", ResourceType::MapLayer }, { "config/Default.config", ResourceType::Configuration }, { "items/Tools.items", ResourceType::ItemCollection }, { "items/Weapons.items", ResourceType::ItemCollection }, diff --git a/src/storage/Parsers.cpp b/src/storage/Parsers.cpp index 2b1ba7a..a61bc73 100644 --- a/src/storage/Parsers.cpp +++ b/src/storage/Parsers.cpp @@ -5,6 +5,7 @@ * Author: tibi */ +#include #include #include @@ -15,9 +16,9 @@ namespace storage { /****** Components ******/ -void parseBackgroundCells(resources::ResourceId cellsResource, components::Background* back, size_t layer) +void parseMapCells(resources::ResourceId cellsResource, components::Map* map, components::MapLayer& layer) { - Assert(RInfo[cellsResource].type == ResourceType::BackgroundLayer, "Resource must be a level layer."); + Assert(RInfo[cellsResource].type == ResourceType::MapLayer, "Resource must be a level layer."); char buffer[1024 * 10]; @@ -28,7 +29,7 @@ void parseBackgroundCells(resources::ResourceId cellsResource, components::Backg THROW(utils::ResourceLoadException, "Could not load level layer " + pathIn); // Read CSV file line by line - for (size_t row = 0; row < back->rowCount(); row++) + for (size_t row = 0; row < map->height; row++) { in.getline(buffer, sizeof(buffer)); @@ -37,10 +38,10 @@ void parseBackgroundCells(resources::ResourceId cellsResource, components::Backg // Separated by comma (or maybe semicolon) char* nextNum = strtok(buffer, ",;"); - for (size_t col = 0; col < back->columnCount() && nextNum != NULL; col++) + for (size_t col = 0; col < map->width && nextNum != NULL; col++) { components::Cell cell = (components::Cell) strtol(nextNum, NULL, 10); - back->setCell(layer, row, col, cell); + layer[row][col] = cell; nextNum = strtok(NULL, ",;"); } @@ -50,10 +51,10 @@ void parseBackgroundCells(resources::ResourceId cellsResource, components::Backg } template <> -components::Background* parse (boost::property_tree::ptree& root) +components::Map* parse (boost::property_tree::ptree& root) { // Ensure we are on the model::Scene node (property tree seems to add root of its own) - if (root.front().first == "Background") + if (root.front().first == "Map") root = root.front().second; // This object can be declared in another file @@ -61,39 +62,42 @@ components::Background* parse (boost::property_tree::ptr if (!src.empty()) { ResourceId id = ResourceManager::instance().getId(src); - return parse(id); + return parse(id); } - // Read sizes - uint32_t layers = root.count("Layer"); - uint32_t rows = root.get(".rows"); - uint32_t cols = root.get(".columns"); + // Read map attributes + components::Map* map = new components::Map(); - // Create components::Background object - components::Background* back = new components::Background(layers, rows, cols); + map->width = root.get(".width"); + map->height = root.get(".height"); + map->cellWidth = root.get(".cellWidth"); + map->cellHeight = root.get(".cellHeight"); // Read layers - size_t layerNum = 0; - for (auto layer : root) + for (auto layerNode : root) { - if (layer.first == "Layer") + if (layerNode.first == "Layer") { + // Read layer name + std::string name = layerNode.second.get(".name"); + components::MapLayer& layer = map->addLayer(name); + + // Read tile set + auto tileSetNode = layerNode.second.get_child("TileSet"); + + model::TileSet* tileSet = parse(tileSetNode); + layer.tileSet = *tileSet; + delete tileSet; + // Read cells - std::string cellsPath = layer.second.get(".cells"); + std::string cellsPath = layerNode.second.get(".cells"); resources::ResourceId cellsId = resources::ResourceManager::instance().getId(cellsPath); - parseBackgroundCells(cellsId, back, layerNum); - - // Read texture name - std::string texPath = layer.second.get(".texture"); - resources::ResourceId tex = resources::ResourceManager::instance().getId(texPath); - back->setTexture(layerNum, tex); - - ++layerNum; + parseMapCells(cellsId, map, layer); } } - return back; + return map; } template <> @@ -130,6 +134,27 @@ components::basic::Frame* parse (boost::property_tree: return frame; } + +template <> +components::basic::Grid* parse (boost::property_tree::ptree& root) +{ + // Ensure we are on the model::Scene node (property tree seems to add root of its own) + if (root.front().first == "Grid") + root = root.front().second; + + components::basic::Grid* grid = new components::basic::Grid(); + + // Set properties + utils::Rect bounds; + bounds.x = root.get(".x", 0); + bounds.y = root.get(".y", 0); + bounds.w = root.get(".w"); + bounds.h = root.get(".h"); + + grid->setBounds(bounds); + return grid; +} + template <> components::basic::Sprite* parse (boost::property_tree::ptree& root) { @@ -197,8 +222,8 @@ components::basic::Transform* parse (boost::proper components::basic::Transform* transform = new components::basic::Transform(); transform->x = root.get(".x", 0.0f); transform->y = root.get(".y", 0.0f); - transform->w = root.get(".w", 0.0f); - transform->h = root.get(".h", 0.0f); + transform->w = root.get(".w", 1.0f); + transform->h = root.get(".h", 1.0f); return transform; } @@ -347,13 +372,13 @@ components::player::PlayerMovement* parse (b /****** Graphics ******/ template <> -graphics::BackgroundRenderer* parse (boost::property_tree::ptree& root) +graphics::MapRenderer* parse (boost::property_tree::ptree& root) { // Ensure we are on the model::Scene node (property tree seems to add root of its own) - if (root.front().first == "BackgroundRenderer") + if (root.front().first == "MapRenderer") root = root.front().second; - graphics::BackgroundRenderer* renderer = new graphics::BackgroundRenderer(); + graphics::MapRenderer* renderer = new graphics::MapRenderer(); return renderer; } @@ -432,15 +457,15 @@ model::GameObject* parse (boost::property_tree::ptree& root) gameObj->addComponent(parse(child.second)); // Components - else if (child.first == "Background") - gameObj->addComponent(parse(child.second)); + else if (child.first == "Map") + gameObj->addComponent(parse(child.second)); else if (child.first == "DebugController") gameObj->addComponent(parse(child.second)); // Graphics - else if (child.first == "BackgroundRenderer") - gameObj->addComponent(parse(child.second)); + else if (child.first == "MapRenderer") + gameObj->addComponent(parse(child.second)); else if (child.first == "SpriteRenderer") gameObj->addComponent(parse(child.second)); @@ -487,6 +512,39 @@ model::Scene* parse (boost::property_tree::ptree& root) return scene; } +template<> +model::TileSet* parse (boost::property_tree::ptree& root) +{ + // Ensure we are on the model::Scene node (property tree seems to add root of its own) + if (root.front().first == "TileSet") + root = root.front().second; + + // This object can be declared in another file + std::string src = root.get(".src", ""); + if (!src.empty()) + { + ResourceId id = ResourceManager::instance().getId(src); + return parse(id); + } + + // Read attributes + model::TileSet* tileSet = new model::TileSet(); + + std::string texPath = root.get(".texture"); + tileSet->texture = resources::ResourceManager::instance().getId(texPath); + tileSet->tileWidth = root.get(".tileWidth"); + tileSet->tileHeight = root.get(".tileHeight"); + + // Get image size + int w, h; + SDL_Texture* sdlTex = ResourceManager::instance().texture(tileSet->texture); + graphics::backend::SdlRenderer::instance().getTextureSize(sdlTex, &w, &h); + tileSet->width = w; + tileSet->height = h; + + return tileSet; +} + } } diff --git a/src/storage/Parsers.h b/src/storage/Parsers.h index 5a97bd6..367aadd 100644 --- a/src/storage/Parsers.h +++ b/src/storage/Parsers.h @@ -8,8 +8,8 @@ #ifndef STORAGE_PARSERS_PARSERS_H_ #define STORAGE_PARSERS_PARSERS_H_ -#include #include +#include #include #include #include @@ -21,14 +21,15 @@ #include #include #include +#include #include #include -#include +#include #include - #include #include #include +#include #include @@ -38,7 +39,7 @@ namespace storage { /****** Components ******/ template <> - components::Background* parse (boost::property_tree::ptree& root); + components::Map* parse (boost::property_tree::ptree& root); template <> components::basic::Camera* parse (boost::property_tree::ptree& root); @@ -46,6 +47,9 @@ namespace storage { template <> components::basic::Frame* parse (boost::property_tree::ptree& root); + template <> + components::basic::Grid* parse (boost::property_tree::ptree& root); + template <> components::basic::Sprite* parse (boost::property_tree::ptree& root); @@ -92,7 +96,7 @@ namespace storage { /****** Graphics ******/ template <> - graphics::BackgroundRenderer* parse (boost::property_tree::ptree& root); + graphics::MapRenderer* parse (boost::property_tree::ptree& root); template <> graphics::SpriteRenderer* parse (boost::property_tree::ptree& root); @@ -109,6 +113,9 @@ namespace storage { template <> model::Scene* parse (boost::property_tree::ptree& root); + template<> + model::TileSet* parse (boost::property_tree::ptree& root); + } } diff --git a/src/model/ICloneable.h b/src/utils/ICloneable.h similarity index 94% rename from src/model/ICloneable.h rename to src/utils/ICloneable.h index b7cbcb4..dbd513a 100644 --- a/src/model/ICloneable.h +++ b/src/utils/ICloneable.h @@ -9,7 +9,7 @@ #define BASE_ICLONEABLE_H_ namespace farmlands { -namespace model { +namespace utils { template class ICloneable diff --git a/src/model/INonAssignable.h b/src/utils/INonAssignable.h similarity index 95% rename from src/model/INonAssignable.h rename to src/utils/INonAssignable.h index 9fd696b..d8f6818 100644 --- a/src/model/INonAssignable.h +++ b/src/utils/INonAssignable.h @@ -9,7 +9,7 @@ #define BASE_INONASSIGNABLE_H_ namespace farmlands { -namespace model { +namespace utils { class INonAssignable { diff --git a/src/utils/QTree.h b/src/utils/QTree.h new file mode 100644 index 0000000..c170657 --- /dev/null +++ b/src/utils/QTree.h @@ -0,0 +1,265 @@ +/* + * QTree.h + * + * Created on: Dec 4, 2016 + * Author: tibi + */ + +#ifndef UTILS_QTREE_H_ +#define UTILS_QTREE_H_ + +#include +#include +#include + +namespace farmlands { +namespace utils { + +#if 0 + // Commented because no longer needed. + + template + struct QTreeItem + { + T data; + float x, y; + }; + + /** + * Quad tree + */ + template + class QTree : public INonAssignable + { + public: + typedef int iterator; + + QTree(const RectF& bounds); + virtual ~QTree(); + + // Container operations + iterator begin(); + iterator end(); + + bool empty() const; + size_t size() const; + + void insert(T element, float x, float y); + void erase(iterator it); + void clear(); + + iterator find(T element); + iterator find(float x, float y); + + iterator lower_bound(float x, float y); + iterator lower_bound(float x, float y, float distance); + iterator lower_bound(const RectF& area); + + iterator upper_bound(float x, float y); + iterator upper_bound(float x, float y, float distance); + iterator upper_bound(const RectF& area); + + private: + void subdivide(); + void merge(); + + RectF m_bounds; + bool m_isSplit; + QTree* m_children[4]; + QTreeItem m_items[Capacity]; + size_t m_itemsCount; + }; + + template + QTree::QTree(const RectF& bounds) + : m_bounds(bounds), + m_isSplit(false), + m_children(), + m_items(), + m_itemsCount(0) + { + } + + template + QTree::~QTree() + { + if (m_isSplit) + { + for (size_t i = 0; i < 4; i++) + delete m_children[i]; + } + } + + template + inline QTree::iterator QTree::begin() + { + } + + template + inline QTree::iterator QTree::end() + { + } + + template + inline bool QTree::empty() const + { + return (m_itemsCount == 0); + } + + template + inline size_t QTree::size() const + { + return m_itemsCount; + } + + template + inline void QTree::insert(T element, float x, float y) + { + Assert(m_bounds.contains(x, y), "Can't add element outside bounds."); + + // Not split case + if (!m_isSplit && m_itemsCount >= Capacity) + subdivide(); + + // If split, add element in one of the subtrees + if (m_isSplit) + { + for (size_t i = 0; i < 4; i++) + if (m_children[i]->m_bounds.contains(x, y)) + { + m_children[i]->insert(element, x, y); + return; + } + } + else + { + QTreeItem item = + { + .data = element, + .x = x, + .y = y + }; + + m_items[m_itemsCount] = item; + } + + m_itemsCount++; + } + + template + inline void QTree::erase(iterator it) + { + } + + template + inline void QTree::clear() + { + if (m_isSplit) + { + for (size_t i = 0; i < 4; i++) + delete m_children[i]; + m_isSplit = false; + } + + m_itemsCount = 0; + } + + template + inline QTree::iterator QTree::find(T element) + { + } + + template + inline QTree::iterator QTree::find(float x, float y) + { + } + + template + inline QTree::iterator QTree::lower_bound(float x, float y) + { + } + + template + inline QTree::iterator QTree::lower_bound(float x, float y, float distance) + { + } + + template + inline QTree::iterator QTree::lower_bound(const RectF& area) + { + } + + template + inline QTree::iterator QTree::upper_bound(float x, float y) + { + } + + template + inline QTree::iterator QTree::upper_bound(float x, float y, float distance) + { + } + + template + inline QTree::iterator QTree::upper_bound(const RectF& area) + { + } + + template + void QTree::subdivide() + { + Assert(!m_isSplit, "Can't subdivide if already subdivided!"); + + float halfW = m_bounds.w / 2; + float halfH = m_bounds.h / 2; + + RectF rects[4] = + { + { m_bounds.x, m_bounds.y, halfW, halfH }, + { m_bounds.x + halfW, m_bounds.y, halfW, halfH }, + { m_bounds.x, m_bounds.y + halfH, halfW, halfH }, + { m_bounds.x + halfW, m_bounds.y + halfH, halfW, halfH }, + }; + + // Allocate subtrees + for (size_t i = 0; i < 4; i++) + m_children[i] = new QTree(rects[i]); + + // Set split flag + m_isSplit = true; + + // Re-insert all the children + size_t itemsCount = m_itemsCount; + m_itemsCount = 0; + + for (size_t i = 0; i < itemsCount; i++) + insert(m_items[i].data, m_items[i].x, m_items[i].y); + } + + template + void QTree::merge() + { + Assert(m_isSplit, "Can't merge if not subdivided!"); + + // Unset split flag + m_isSplit = false; + + // Put all the children in their places + m_itemsCount = 0; + + for (size_t i = 0; i < 4; i++) + { + Assert(!m_children[i].m_isSplit, "Cannot merge if children are split!!!"); + + for (size_t j = 0; j < m_children[i].m_itemsCount; j++) + insert(m_children[i].m_items[j].data, m_children[i].m_items[j].x, m_children[i].m_items[j].y); + + delete m_children[i]; + } + } + +#endif + +} /* namespace utils */ +} /* namespace farmlands */ + +#endif /* UTILS_QTREE_H_ */ diff --git a/src/utils/Rect.h b/src/utils/Rect.h new file mode 100644 index 0000000..bcc254c --- /dev/null +++ b/src/utils/Rect.h @@ -0,0 +1,50 @@ +/* + * Rect.h + * + * Created on: Dec 4, 2016 + * Author: tibi + */ + +#ifndef UTILS_RECT_H_ +#define UTILS_RECT_H_ + +namespace farmlands { +namespace utils { + + template + class Rect + { + public: + // Constructors + Rect() + : x(), y(), w(), h() { } + + Rect(T x, T y, T w, T h) + : x(x), y(y), w(w), h(h) { } + + bool contains(T px, T py) + { + bool containsX = (x <= px && px <= x + w); + bool containsY = (y <= px && px <= x + w); + + return containsX && containsY; + } + + bool intersects(const Rect other) + { + bool intersectsX = (x <= other.x && other.x <= x + w) || (other.x <= x && other.x + other.w >= x); + bool intersectsY = (y <= other.y && other.y <= y + h) || (other.y <= y && other.y + other.h >= y); + + return intersectsX && intersectsY; + } + + // Values + T x, y, w, h; + }; + + typedef Rect RectF; + +} /* namespace utils */ +} /* namespace farmlands */ + +#endif /* UTILS_RECT_H_ */