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).

This commit is contained in:
Tiberiu Chibici 2016-12-06 00:29:43 +02:00
parent 0dc77aacb4
commit c12a8ede5a
42 changed files with 1090 additions and 288 deletions

View File

@ -14,6 +14,10 @@
<BackgroundRenderer />
</GameObject>
<GameObject name="Object Layer">
<Grid w="380" h="250" />
</GameObject>
<!-- Player object -->
<GameObject name="Player">
<Transform x="120" y="100" />
@ -32,4 +36,10 @@
<DebugController />
</GameObject>
<GameObject name="enemyTest">
<Transform x="125" y="110" />
<Sprite src="sprites/Player.sprite" />
<SpriteRenderer />
</GameObject>
</Scene>

View File

@ -49,8 +49,8 @@ FILE_TYPES = [
([".sprite"], "Sprite"),
([".config"], "Configuration"),
([".scene"], "Scene"),
([".back"], "Background"),
([".csv"], "BackgroundLayer"),
([".back"], "Map"),
([".csv"], "MapLayer"),
([".item"], "Item"),
([".items"], "ItemCollection"),
]

View File

@ -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)
{

View File

@ -1,80 +0,0 @@
/*
* Background.cpp
*
* Created on: Dec 1, 2016
* Author: tibi
*/
#include <components/Background.h>
#include <utils/Assert.h>
#include <iostream>
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 */

View File

@ -1,49 +0,0 @@
/*
* Background.h
*
* Created on: Dec 1, 2016
* Author: tibi
*/
#ifndef MODEL_BACKGROUND_H_
#define MODEL_BACKGROUND_H_
#include <model/Component.h>
#include <resources/ResourceManager.h>
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_ */

113
src/components/Map.cpp Normal file
View File

@ -0,0 +1,113 @@
/*
* Background.cpp
*
* Created on: Dec 1, 2016
* Author: tibi
*/
#include <components/Map.h>
#include <utils/Assert.h>
#include <iostream>
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 */

73
src/components/Map.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Map.h
*
* Created on: Dec 1, 2016
* Author: tibi
*/
#ifndef MODEL_MAP_H_
#define MODEL_MAP_H_
#include <model/Component.h>
#include <model/TileSet.h>
#include <resources/ResourceManager.h>
#include <vector>
#include <map>
#include <string>
namespace farmlands {
namespace components {
typedef int16_t Cell;
class MapLayer : utils::ICloneable<MapLayer>
{
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<MapLayer*> m_layers;
std::map<std::string, void*> m_layersStr; // gcc gives error if I use MapLayer*
};
} /* namespace model */
} /* namespace farmlands */
#endif /* MODEL_BACKGROUND_H_ */

View File

@ -0,0 +1,146 @@
/*
* Grid.cpp
*
* Created on: Dec 4, 2016
* Author: tibi
*/
#include <components/basic/Grid.h>
#include <components/basic/Transform.h>
#include <utils/Assert.h>
#include <utils/Exceptions.h>
#include <iostream>
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<Transform>();
if (tr == nullptr)
{
std::cerr << "Grid: ignoring object " << obj->name << ": object has no transform.";
continue;
}
// Compute grid position(s)
Rect<int> 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<int>& 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<int> 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 */

View File

@ -0,0 +1,82 @@
/*
* Grid.h
*
* Created on: Dec 4, 2016
* Author: tibi
*/
#ifndef COMPONENTS_BASIC_GRID_H_
#define COMPONENTS_BASIC_GRID_H_
#include <model/Component.h>
#include <model/GameObject.h>
#include <utils/Rect.h>
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<int>& bounds);
// Getters
utils::Rect<int> bounds() const;
// Operations
model::GameObject* get(int x, int y);
private:
void set(model::GameObject* obj, int x, int y, bool throwOnOverwrite);
utils::Rect<int> m_bounds;
model::GameObject** m_grid;
};
} /* namespace basic */
} /* namespace components */
} /* namespace farmlands */
#endif /* COMPONENTS_BASIC_GRID_H_ */

View File

@ -6,6 +6,7 @@
*/
#include <GameState.h>
#include <components/basic/Transform.h>
#include <components/basic/Sprite.h>
#include <utils/Assert.h>

View File

@ -6,16 +6,18 @@
*/
#include <GameState.h>
#include <components/GuiController.h>
#include <components/gui/GuiController.h>
#include <gui/widgets/TextArea.h>
#include <iostream>
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 */

View File

@ -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 */

View File

@ -8,14 +8,14 @@
#ifndef COMPONENTS_ITEMS_AXE_H_
#define COMPONENTS_ITEMS_AXE_H_
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <model/Component.h>
namespace farmlands {
namespace components {
namespace items {
class Axe: public model::Component, public ITool
class Axe: public model::Component, public IPlayerAction
{
public:
Axe();

View File

@ -8,14 +8,14 @@
#ifndef CONTROLLER_ITEMS_GIFTABLE_H_
#define CONTROLLER_ITEMS_GIFTABLE_H_
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <model/Component.h>
namespace farmlands {
namespace components {
namespace items {
class Giftable: public model::Component, public ITool
class Giftable: public model::Component, public IPlayerAction
{
public:
Giftable();

View File

@ -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<Background>();
auto it = root->findByComponent<Map>();
Assert(it != root->childrenEnd(), "Can't find background game object.");
m_back = (*it)->component<Background>();
m_map = (*it)->component<Map>();
}
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 */

View File

@ -8,8 +8,8 @@
#ifndef COMPONENTS_ITEMS_HOE_H_
#define COMPONENTS_ITEMS_HOE_H_
#include <components/Background.h>
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <components/Map.h>
#include <model/Component.h>
#include <model/Direction.h>
@ -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 */

View File

@ -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 <model/Direction.h>
@ -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_ */

View File

@ -8,14 +8,14 @@
#ifndef COMPONENTS_ITEMS_PICKAXE_H_
#define COMPONENTS_ITEMS_PICKAXE_H_
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <model/Component.h>
namespace farmlands {
namespace components {
namespace items {
class Pickaxe: public model::Component, public ITool
class Pickaxe: public model::Component, public IPlayerAction
{
public:
Pickaxe();

View File

@ -8,14 +8,14 @@
#ifndef COMPONENTS_ITEMS_SCYTHE_H_
#define COMPONENTS_ITEMS_SCYTHE_H_
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <model/Component.h>
namespace farmlands {
namespace components {
namespace items {
class Scythe: public model::Component, public ITool
class Scythe: public model::Component, public IPlayerAction
{
public:
Scythe();

View File

@ -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<Background>();
auto it = root->findByComponent<Map>();
Assert(it != root->childrenEnd(), "Can't find background game object.");
m_back = (*it)->component<Background>();
m_map = (*it)->component<Map>();
}
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";

View File

@ -8,8 +8,8 @@
#ifndef COMPONENTS_ITEMS_WATERINGCAN_H_
#define COMPONENTS_ITEMS_WATERINGCAN_H_
#include <components/Background.h>
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <components/Map.h>
#include <model/Component.h>
#include <model/Direction.h>
@ -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 */

View File

@ -9,7 +9,7 @@
#define CONTROLLER_ITEMS_WEAPON_H_
#include <components/basic/Sprite.h>
#include <components/items/ITool.h>
#include <components/items/IPlayerAction.h>
#include <graphics/SpriteRenderer.h>
#include <model/Component.h>
@ -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();

View File

@ -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<Transform>();
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<ITool*>(it->second);
IPlayerAction* tool = dynamic_cast<IPlayerAction*>(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 */

View File

@ -42,6 +42,7 @@ namespace player {
private:
void handleAttackEvents(SDL_Event& event);
void handleCurrentInventoryItemEvent(SDL_Event& event);
void handleInventoryEvents(SDL_Event& event);
// Inventory

View File

@ -8,7 +8,7 @@
#include <GameState.h>
#include <model/GameObject.h>
#include <graphics/backend/SdlRenderer.h>
#include <graphics/BackgroundRenderer.h>
#include <graphics/MapRenderer.h>
#include <resources/ResourceManager.h>
#include <math/GameMath.h>
#include <utils/Assert.h>
@ -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<Background>();
m_map = gameObject->component<Map>();
}
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<<" ";

View File

@ -5,23 +5,23 @@
* Author: tibi
*/
#ifndef GRAPHICS_BACKGROUNDRENDERER_H_
#define GRAPHICS_BACKGROUNDRENDERER_H_
#ifndef GRAPHICS_MAPRENDERER_H_
#define GRAPHICS_MAPRENDERER_H_
#include <model/Component.h>
#include <components/Background.h>
#include <components/basic/Camera.h>
#include <components/basic/Transform.h>
#include <components/Map.h>
#include <graphics/RenderContext.h>
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_ */

View File

@ -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)
{
}

View File

@ -8,6 +8,7 @@
#include <GameState.h>
#include <graphics/backend/SdlRenderer.h>
#include <algorithm>
#include <iostream>
#include <cassert>
@ -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 */

View File

@ -11,6 +11,7 @@
#include <graphics/RenderContext.h>
#include <string>
#include <vector>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
@ -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<QueueItem> m_queue;
static SdlRenderer s_instance;
};

View File

@ -42,6 +42,7 @@ namespace input {
Inventory8,
Inventory9,
Inventory10,
InventoryDrop,
// Debug keys
Debug_ZoomIn,

View File

@ -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,

View File

@ -8,8 +8,8 @@
#ifndef COMPONENT_H_
#define COMPONENT_H_
#include <model/ICloneable.h>
#include <model/INonAssignable.h>
#include <utils/ICloneable.h>
#include <utils/INonAssignable.h>
#include <SDL2/SDL.h>
@ -18,7 +18,7 @@ namespace model {
class GameObject;
class Component : public INonAssignable, public ICloneable<Component>
class Component : public utils::INonAssignable, public utils::ICloneable<Component>
{
public:
Component();

View File

@ -8,9 +8,9 @@
#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_
#include <model/ICloneable.h>
#include <model/INonAssignable.h>
#include <utils/Assert.h>
#include <utils/ICloneable.h>
#include <utils/INonAssignable.h>
#include <SDL2/SDL.h>
@ -24,7 +24,7 @@ namespace model {
class Component;
class RenderContext;
class GameObject : public INonAssignable, public ICloneable<GameObject>
class GameObject : public utils::INonAssignable, public utils::ICloneable<GameObject>
{
public:
typedef std::unordered_map<std::type_index, Component*> ComponentContainer;

42
src/model/TileSet.h Normal file
View File

@ -0,0 +1,42 @@
/*
* TileMap.h
*
* Created on: Dec 5, 2016
* Author: tibi
*/
#ifndef MODEL_TILESET_H_
#define MODEL_TILESET_H_
#include <resources/ResourceManager.h>
#include <utils/Rect.h>
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<int> getCell(int cellId);
};
inline utils::Rect<int> TileSet::getCell(int cellId)
{
utils::Rect<int> 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_ */

View File

@ -19,8 +19,8 @@ namespace resources {
Sprite,
Configuration,
Scene,
Background,
BackgroundLayer,
Map,
MapLayer,
Item,
ItemCollection,
};

View File

@ -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 },

View File

@ -5,6 +5,7 @@
* Author: tibi
*/
#include <graphics/backend/SdlRenderer.h>
#include <storage/Parsers.h>
#include <resources/Resources.h>
@ -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<components::Background> (boost::property_tree::ptree& root)
components::Map* parse<components::Map> (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<components::Background> (boost::property_tree::ptr
if (!src.empty())
{
ResourceId id = ResourceManager::instance().getId(src);
return parse<components::Background>(id);
return parse<components::Map>(id);
}
// Read sizes
uint32_t layers = root.count("Layer");
uint32_t rows = root.get<uint32_t>("<xmlattr>.rows");
uint32_t cols = root.get<uint32_t>("<xmlattr>.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<size_t>("<xmlattr>.width");
map->height = root.get<size_t>("<xmlattr>.height");
map->cellWidth = root.get<size_t>("<xmlattr>.cellWidth");
map->cellHeight = root.get<size_t>("<xmlattr>.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<std::string>("<xmlattr>.name");
components::MapLayer& layer = map->addLayer(name);
// Read tile set
auto tileSetNode = layerNode.second.get_child("TileSet");
model::TileSet* tileSet = parse<model::TileSet>(tileSetNode);
layer.tileSet = *tileSet;
delete tileSet;
// Read cells
std::string cellsPath = layer.second.get<std::string>("<xmlattr>.cells");
std::string cellsPath = layerNode.second.get<std::string>("<xmlattr>.cells");
resources::ResourceId cellsId = resources::ResourceManager::instance().getId(cellsPath);
parseBackgroundCells(cellsId, back, layerNum);
// Read texture name
std::string texPath = layer.second.get<std::string>("<xmlattr>.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<components::basic::Frame> (boost::property_tree:
return frame;
}
template <>
components::basic::Grid* parse<components::basic::Grid> (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<int> bounds;
bounds.x = root.get<int>("<xmlattr>.x", 0);
bounds.y = root.get<int>("<xmlattr>.y", 0);
bounds.w = root.get<int>("<xmlattr>.w");
bounds.h = root.get<int>("<xmlattr>.h");
grid->setBounds(bounds);
return grid;
}
template <>
components::basic::Sprite* parse<components::basic::Sprite> (boost::property_tree::ptree& root)
{
@ -197,8 +222,8 @@ components::basic::Transform* parse<components::basic::Transform> (boost::proper
components::basic::Transform* transform = new components::basic::Transform();
transform->x = root.get<float>("<xmlattr>.x", 0.0f);
transform->y = root.get<float>("<xmlattr>.y", 0.0f);
transform->w = root.get<float>("<xmlattr>.w", 0.0f);
transform->h = root.get<float>("<xmlattr>.h", 0.0f);
transform->w = root.get<float>("<xmlattr>.w", 1.0f);
transform->h = root.get<float>("<xmlattr>.h", 1.0f);
return transform;
}
@ -347,13 +372,13 @@ components::player::PlayerMovement* parse<components::player::PlayerMovement> (b
/****** Graphics ******/
template <>
graphics::BackgroundRenderer* parse<graphics::BackgroundRenderer> (boost::property_tree::ptree& root)
graphics::MapRenderer* parse<graphics::MapRenderer> (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<model::GameObject> (boost::property_tree::ptree& root)
gameObj->addComponent(parse<components::player::PlayerMovement>(child.second));
// Components
else if (child.first == "Background")
gameObj->addComponent(parse<components::Background>(child.second));
else if (child.first == "Map")
gameObj->addComponent(parse<components::Map>(child.second));
else if (child.first == "DebugController")
gameObj->addComponent(parse<components::DebugController>(child.second));
// Graphics
else if (child.first == "BackgroundRenderer")
gameObj->addComponent(parse<graphics::BackgroundRenderer>(child.second));
else if (child.first == "MapRenderer")
gameObj->addComponent(parse<graphics::MapRenderer>(child.second));
else if (child.first == "SpriteRenderer")
gameObj->addComponent(parse<graphics::SpriteRenderer>(child.second));
@ -487,6 +512,39 @@ model::Scene* parse<model::Scene> (boost::property_tree::ptree& root)
return scene;
}
template<>
model::TileSet* parse<model::TileSet> (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<std::string>("<xmlattr>.src", "");
if (!src.empty())
{
ResourceId id = ResourceManager::instance().getId(src);
return parse<model::TileSet>(id);
}
// Read attributes
model::TileSet* tileSet = new model::TileSet();
std::string texPath = root.get<std::string>("<xmlattr>.texture");
tileSet->texture = resources::ResourceManager::instance().getId(texPath);
tileSet->tileWidth = root.get<size_t>("<xmlattr>.tileWidth");
tileSet->tileHeight = root.get<size_t>("<xmlattr>.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;
}
}
}

View File

@ -8,8 +8,8 @@
#ifndef STORAGE_PARSERS_PARSERS_H_
#define STORAGE_PARSERS_PARSERS_H_
#include <components/Background.h>
#include <components/basic/Camera.h>
#include <components/basic/Grid.h>
#include <components/basic/Sprite.h>
#include <components/basic/Transform.h>
#include <components/DebugController.h>
@ -21,14 +21,15 @@
#include <components/items/Scythe.h>
#include <components/items/WateringCan.h>
#include <components/items/Weapon.h>
#include <components/Map.h>
#include <components/player/PlayerInventory.h>
#include <components/player/PlayerMovement.h>
#include <graphics/BackgroundRenderer.h>
#include <graphics/MapRenderer.h>
#include <graphics/SpriteRenderer.h>
#include <model/GameObject.h>
#include <model/Configuration.h>
#include <model/Scene.h>
#include <model/TileSet.h>
#include <storage/Parse.h>
@ -38,7 +39,7 @@ namespace storage {
/****** Components ******/
template <>
components::Background* parse<components::Background> (boost::property_tree::ptree& root);
components::Map* parse<components::Map> (boost::property_tree::ptree& root);
template <>
components::basic::Camera* parse<components::basic::Camera> (boost::property_tree::ptree& root);
@ -46,6 +47,9 @@ namespace storage {
template <>
components::basic::Frame* parse<components::basic::Frame> (boost::property_tree::ptree& root);
template <>
components::basic::Grid* parse<components::basic::Grid> (boost::property_tree::ptree& root);
template <>
components::basic::Sprite* parse<components::basic::Sprite> (boost::property_tree::ptree& root);
@ -92,7 +96,7 @@ namespace storage {
/****** Graphics ******/
template <>
graphics::BackgroundRenderer* parse<graphics::BackgroundRenderer> (boost::property_tree::ptree& root);
graphics::MapRenderer* parse<graphics::MapRenderer> (boost::property_tree::ptree& root);
template <>
graphics::SpriteRenderer* parse<graphics::SpriteRenderer> (boost::property_tree::ptree& root);
@ -109,6 +113,9 @@ namespace storage {
template <>
model::Scene* parse<model::Scene> (boost::property_tree::ptree& root);
template<>
model::TileSet* parse<model::TileSet> (boost::property_tree::ptree& root);
}
}

View File

@ -9,7 +9,7 @@
#define BASE_ICLONEABLE_H_
namespace farmlands {
namespace model {
namespace utils {
template <typename T>
class ICloneable

View File

@ -9,7 +9,7 @@
#define BASE_INONASSIGNABLE_H_
namespace farmlands {
namespace model {
namespace utils {
class INonAssignable
{

265
src/utils/QTree.h Normal file
View File

@ -0,0 +1,265 @@
/*
* QTree.h
*
* Created on: Dec 4, 2016
* Author: tibi
*/
#ifndef UTILS_QTREE_H_
#define UTILS_QTREE_H_
#include <utils/Assert.h>
#include <utils/INonAssignable.h>
#include <utils/Rect.h>
namespace farmlands {
namespace utils {
#if 0
// Commented because no longer needed.
template <typename T>
struct QTreeItem
{
T data;
float x, y;
};
/**
* Quad tree
*/
template <typename T, size_t Capacity>
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<typename T, size_t Capacity>
QTree<T, Capacity>::QTree(const RectF& bounds)
: m_bounds(bounds),
m_isSplit(false),
m_children(),
m_items(),
m_itemsCount(0)
{
}
template<typename T, size_t Capacity>
QTree<T, Capacity>::~QTree()
{
if (m_isSplit)
{
for (size_t i = 0; i < 4; i++)
delete m_children[i];
}
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::begin()
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::end()
{
}
template<typename T, size_t Capacity>
inline bool QTree<T, Capacity>::empty() const
{
return (m_itemsCount == 0);
}
template<typename T, size_t Capacity>
inline size_t QTree<T, Capacity>::size() const
{
return m_itemsCount;
}
template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::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<T> item =
{
.data = element,
.x = x,
.y = y
};
m_items[m_itemsCount] = item;
}
m_itemsCount++;
}
template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::erase(iterator it)
{
}
template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::clear()
{
if (m_isSplit)
{
for (size_t i = 0; i < 4; i++)
delete m_children[i];
m_isSplit = false;
}
m_itemsCount = 0;
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::find(T element)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::find(float x, float y)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(float x, float y)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(float x, float y, float distance)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(const RectF& area)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(float x, float y)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(float x, float y, float distance)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(const RectF& area)
{
}
template<typename T, size_t Capacity>
void QTree<T, Capacity>::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<T, Capacity>(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<typename T, size_t Capacity>
void QTree<T, Capacity>::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_ */

50
src/utils/Rect.h Normal file
View File

@ -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 <typename T>
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<T> 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<float> RectF;
} /* namespace utils */
} /* namespace farmlands */
#endif /* UTILS_RECT_H_ */