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:
parent
0dc77aacb4
commit
c12a8ede5a
@ -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>
|
@ -49,8 +49,8 @@ FILE_TYPES = [
|
||||
([".sprite"], "Sprite"),
|
||||
([".config"], "Configuration"),
|
||||
([".scene"], "Scene"),
|
||||
([".back"], "Background"),
|
||||
([".csv"], "BackgroundLayer"),
|
||||
([".back"], "Map"),
|
||||
([".csv"], "MapLayer"),
|
||||
([".item"], "Item"),
|
||||
([".items"], "ItemCollection"),
|
||||
]
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 */
|
@ -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
113
src/components/Map.cpp
Normal 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
73
src/components/Map.h
Normal 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_ */
|
146
src/components/basic/Grid.cpp
Normal file
146
src/components/basic/Grid.cpp
Normal 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 */
|
82
src/components/basic/Grid.h
Normal file
82
src/components/basic/Grid.h
Normal 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_ */
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <GameState.h>
|
||||
#include <components/basic/Transform.h>
|
||||
#include <components/basic/Sprite.h>
|
||||
#include <utils/Assert.h>
|
||||
|
||||
|
@ -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 */
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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_ */
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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";
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -91,6 +91,7 @@ void PlayerInventory::onInitialize()
|
||||
bool PlayerInventory::onEvent(SDL_Event& event)
|
||||
{
|
||||
handleAttackEvents(event);
|
||||
handleCurrentInventoryItemEvent(event);
|
||||
handleInventoryEvents(event);
|
||||
return false;
|
||||
}
|
||||
@ -106,9 +107,18 @@ void PlayerInventory::onPreRender()
|
||||
if (currentItem)
|
||||
{
|
||||
Transform* itemTransf = currentItem->component<Transform>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerInventory::handleAttackEvents(SDL_Event& event)
|
||||
@ -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 */
|
||||
|
@ -42,6 +42,7 @@ namespace player {
|
||||
private:
|
||||
|
||||
void handleAttackEvents(SDL_Event& event);
|
||||
void handleCurrentInventoryItemEvent(SDL_Event& event);
|
||||
void handleInventoryEvents(SDL_Event& event);
|
||||
|
||||
// Inventory
|
||||
|
@ -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<<" ";
|
@ -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_ */
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -42,6 +42,7 @@ namespace input {
|
||||
Inventory8,
|
||||
Inventory9,
|
||||
Inventory10,
|
||||
InventoryDrop,
|
||||
|
||||
// Debug keys
|
||||
Debug_ZoomIn,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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
42
src/model/TileSet.h
Normal 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_ */
|
@ -19,8 +19,8 @@ namespace resources {
|
||||
Sprite,
|
||||
Configuration,
|
||||
Scene,
|
||||
Background,
|
||||
BackgroundLayer,
|
||||
Map,
|
||||
MapLayer,
|
||||
Item,
|
||||
ItemCollection,
|
||||
};
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#define BASE_ICLONEABLE_H_
|
||||
|
||||
namespace farmlands {
|
||||
namespace model {
|
||||
namespace utils {
|
||||
|
||||
template <typename T>
|
||||
class ICloneable
|
@ -9,7 +9,7 @@
|
||||
#define BASE_INONASSIGNABLE_H_
|
||||
|
||||
namespace farmlands {
|
||||
namespace model {
|
||||
namespace utils {
|
||||
|
||||
class INonAssignable
|
||||
{
|
265
src/utils/QTree.h
Normal file
265
src/utils/QTree.h
Normal 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
50
src/utils/Rect.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user