diff --git a/README.md b/README.md
index 5056f8f..43eb5f3 100644
--- a/README.md
+++ b/README.md
@@ -21,10 +21,10 @@ This game is aimed to be a puzzle game in which the player sets different laws f
- [x] Moving
- [ ] Decorators (Rules)
- [ ] Rules
- - [ ] Creation
- - [ ] Interaction with entities
+ - [x] Creation
+ - [x] Interaction with entities
- [ ] Parsing of rules and creation of subsequent decorators
- - [ ] Raytrace in order to find best goal for pathfinding ?
+ - [x] Find optimal target for pathfinding
- [ ] Graphics
- [x] Scene rendering
- [ ] UI
diff --git a/resources/test_level.xml b/resources/test_level.xml
index 0c7812f..2a7f192 100644
--- a/resources/test_level.xml
+++ b/resources/test_level.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 32421ba..322c065 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,7 +4,7 @@ project(project_maat)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
-add_library(engine Level.cpp Level.h Entity.cpp Entity.h Game.cpp Game.h Utils.h Utils.h Utils.cpp)
+add_library(engine Level.cpp Level.h Entity.cpp Entity.h Game.cpp Game.h Utils.h Utils.h Utils.cpp Rule.cpp Rule.h)
target_link_libraries(engine
sfml-window
diff --git a/src/Entity.cpp b/src/Entity.cpp
index ee3d9f2..81026a4 100644
--- a/src/Entity.cpp
+++ b/src/Entity.cpp
@@ -33,6 +33,9 @@ Entity::Entity(const pugi::xml_node& entityNode, sf::Texture* texture)
entityNode.attribute("w").as_int(1),
entityNode.attribute("h").as_int(1)) {}
+Entity::Entity() : Entity(0,0,EntityType::Citizen,nullptr,0,0)
+{}
+
void Entity::move(Orientation orientation)
{
// TODO : Add speed ?
diff --git a/src/Entity.h b/src/Entity.h
index 275ad11..a0cf0ac 100644
--- a/src/Entity.h
+++ b/src/Entity.h
@@ -45,21 +45,29 @@ public:
Entity(pro_maat::GridUnit x, pro_maat::GridUnit y, EntityType type, sf::Texture* texture, int width = 1,
int height = 1);
explicit Entity(const pugi::xml_node& entityNode, sf::Texture* texture);
+ virtual ~Entity() = default;
- void move(Orientation orientation);
- void update();
+ virtual void move(Orientation orientation);
+ virtual void update();
- State getState() const;
+ virtual State getState() const;
- const sf::RectangleShape& getShape() const;
+ virtual const sf::RectangleShape& getShape() const;
/// Returns the grid coordinates at the center of the entity
- const pro_maat::GridPos getPosition() const;
+ virtual const pro_maat::GridPos getPosition() const;
// Don't like it : iterates over every square every tick
- const std::vector getOccupiedSquares() const;
+ virtual const std::vector getOccupiedSquares() const;
+ // FIXME : Replace with getter
/// Position of the target of the current action on the map
pro_maat::GridPos target;
+
+protected:
+
+ /// Empty constructor for derived class instanciation
+ Entity();
+
private:
static const std::map entityTypeLookup;
@@ -75,6 +83,9 @@ private:
/// Used with rules : last to update has priority
pro_maat::GridPos nextTarget;
+
+ template
+ friend class Rule;
};
diff --git a/src/Game.h b/src/Game.h
index aa149d7..f2ec76a 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -13,8 +13,6 @@
#include "Entity.h"
#include "Utils.h"
-// Used for convenience
-using TextureStore = std::vector>;
class Game {
public:
@@ -37,7 +35,7 @@ private:
/// Store the paths to texture files
const std::vector textureFiles;
/// Stores pointers to textures for future use
- TextureStore textures;
+ pro_maat::TextureStore textures;
void loadTextures();
};
diff --git a/src/Level.cpp b/src/Level.cpp
index 66d2cb5..2653ad5 100644
--- a/src/Level.cpp
+++ b/src/Level.cpp
@@ -5,7 +5,7 @@
#include "Level.h"
-Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore)
+Level::Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore)
: size(xmlDoc.child("Level").attribute("w").as_int(),xmlDoc.child("Level").attribute("h").as_int()),
textures(textureStore)
{
@@ -18,6 +18,10 @@ Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore)
// Initialize the occupied squares vector with the new entity's squares
std::vector entitySquares = entities.rbegin()->getOccupiedSquares();
+
+ // FIXME : For testing purposes
+ Rule newRule(*entities.rbegin(),entities,occupiedSquares,size);
+
std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(occupiedSquares));
}
}
@@ -89,6 +93,8 @@ Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int
std::map paths{};
+ // TODO : Should be OK with raycasting to find goal
+ // FIXME : Just get to goal (Might break)
const std::vector goalNeighbours = pro_maat::getNeighbours(goal,size);
// Save the iterators : vector is const and .begin() and .end() might get called a lot
auto goalNeighboursBeginIterator = goalNeighbours.begin();
diff --git a/src/Level.h b/src/Level.h
index 7f4f8c8..f6219c6 100644
--- a/src/Level.h
+++ b/src/Level.h
@@ -14,12 +14,12 @@
#include "Utils.h"
#include "Entity.h"
+#include "Rule.h"
-using TextureStore = std::vector>;
class Level {
public:
- Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore);
+ Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore);
void render(sf::RenderWindow& renderWindow) const;
void runStep();
@@ -29,7 +29,7 @@ private:
std::vector entities;
- const TextureStore& textures;
+ const pro_maat::TextureStore& textures;
//
// Pathfinding
diff --git a/src/Rule.cpp b/src/Rule.cpp
new file mode 100644
index 0000000..3556db6
--- /dev/null
+++ b/src/Rule.cpp
@@ -0,0 +1,5 @@
+//
+// Created by trotfunky on 09/06/19.
+//
+
+#include "Rule.h"
diff --git a/src/Rule.h b/src/Rule.h
new file mode 100644
index 0000000..3cb0e4e
--- /dev/null
+++ b/src/Rule.h
@@ -0,0 +1,204 @@
+//
+// Created by trotfunky on 09/06/19.
+//
+
+#ifndef PROJECT_MAAT_RULES_H
+#define PROJECT_MAAT_RULES_H
+
+#include
+#include
+
+#include "Entity.h"
+
+
+/// Decorates entities with rules which will modify its behaviour
+template
+class Rule : public Entity
+{
+public:
+ Rule(Entity& entity, const std::vector& entities,
+ const std::vector& occupiedSquares, const pro_maat::GridPos& mapSize);
+
+ void update() override;
+
+ void move(Orientation orientation) override;
+ State getState() const override;
+ const sf::RectangleShape& getShape() const override;
+ const pro_maat::GridPos getPosition() const override;
+ const std::vector getOccupiedSquares() const override;
+
+private:
+ /// Finds the closest free square adjacent to the closest target entity type.
+ /// \return Suitable target square or current position if none was found.
+ pro_maat::GridPos findTarget();
+
+ Entity& entity;
+ const std::vector& entities;
+ const std::vector& occupiedSquares;
+
+ const pro_maat::GridPos& mapSize;
+};
+
+
+template
+Rule::Rule(Entity& entity,
+ const std::vector& entities,
+ const std::vector& occupiedSquares,
+ const pro_maat::GridPos& mapSize)
+ : entity(entity),
+ entities(entities),
+ occupiedSquares(occupiedSquares),
+ mapSize(mapSize)
+{}
+
+template
+void Rule::update()
+{
+ if constexpr (targetState == State::Moving || targetState == State::Fleeing)
+ {
+ entity.nextTarget = findTarget();
+ if(entity.nextTarget == entity.getPosition())
+ {
+ entity.nextState = State::Idle;
+ }
+ else
+ {
+ entity.nextState = targetState;
+ }
+ entity.update();
+ }
+ else if constexpr (targetState == State::Waiting)
+ {
+ entity.nextTarget = entity.getPosition();
+ entity.nextState = State::Waiting;
+ entity.update();
+ }
+ else
+ {
+ entity.nextTarget = entity.getPosition();
+ entity.nextState = State::Idle;
+ entity.update();
+ }
+}
+
+template
+pro_maat::GridPos Rule::findTarget()
+{
+ std::vector sortedEntities{};
+ sortedEntities.insert(sortedEntities.end(),entities.begin(),entities.end());
+
+ // Compares entities via their distance to the current entity
+ auto distanceSortEntities = [this](const Entity& leftHandSide, const Entity& rightHandSide){
+ return (pro_maat::manhattanDistance(entity.getPosition(),leftHandSide.getPosition()) <
+ pro_maat::manhattanDistance(entity.getPosition(),rightHandSide.getPosition()));
+ };
+
+ // Same but with grid coordinates
+ auto distanceSortSquares = [this](const pro_maat::GridPos& leftHandSide, const pro_maat::GridPos& rightHandSide){
+ return (pro_maat::manhattanDistance(entity.getPosition(),leftHandSide) <
+ pro_maat::manhattanDistance(entity.getPosition(),rightHandSide));
+ };
+
+ // Get the smallest, non-occupied, inside the map square
+ auto bestTarget = [this](const pro_maat::GridPos& leftHandSide, const pro_maat::GridPos& rightHandSide){
+ // If the left hand side operand is not in the map or occupied, it is not valid
+ if(!pro_maat::isInMap(leftHandSide,mapSize) ||
+ std::find(occupiedSquares.begin(),occupiedSquares.end(),leftHandSide) != occupiedSquares.end())
+ {
+ return(false);
+ }
+ else if(!pro_maat::isInMap(rightHandSide,mapSize) ||
+ std::find(occupiedSquares.begin(),occupiedSquares.end(),rightHandSide) != occupiedSquares.end())
+ {
+ return(true);
+ }
+ else
+ {
+ return(leftHandSide < rightHandSide);
+ }};
+
+
+ // Sort in order to minimize entities to process
+ std::sort(sortedEntities.begin(),sortedEntities.end(),distanceSortEntities);
+
+ for(const Entity& processingEntity : sortedEntities)
+ {
+ std::vector potentialTargets{};
+
+ pro_maat::GridUnit entityWidth = processingEntity.shape.getSize().x/pro_maat::pixelsPerUnit;
+ pro_maat::GridUnit entityHeight = processingEntity.shape.getSize().y/pro_maat::pixelsPerUnit;
+
+ potentialTargets.reserve((entityWidth+2)*2+entityHeight*2);
+
+ // Computes the top left corner of the entity
+ pro_maat::GridPos topLeftCorner = processingEntity.getPosition();
+ topLeftCorner.first -= entityWidth*0.5 - 1;
+ topLeftCorner.second -= entityHeight*0.5 - 1;
+
+
+ // Get all the top and bottom adjacent squares
+ for(int i = 0;i
+void Rule::move(Orientation orientation)
+{
+ entity.move(orientation);
+}
+
+template
+State Rule::getState() const
+{
+ return entity.getState();
+}
+
+template
+const sf::RectangleShape& Rule::getShape() const
+{
+ return entity.getShape();
+}
+
+template
+const pro_maat::GridPos Rule::getPosition() const
+{
+ return entity.getPosition();
+}
+
+template
+const std::vector Rule::getOccupiedSquares() const
+{
+ return entity.getOccupiedSquares();
+}
+
+
+#endif //PROJECT_MAAT_RULES_H
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 4e820ca..23d78d4 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -37,6 +37,12 @@ namespace pro_maat
}
}
+ bool isInMap(const GridPos& square, const GridPos& gridSize)
+ {
+ return (square.first < 0 || square.second < 0 ||
+ square.first >= gridSize.first || square.second >= gridSize.second);
+ }
+
float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide)
{
// The *0.01 helps with breaking ties and minimizing exploration
diff --git a/src/Utils.h b/src/Utils.h
index 4491e10..af3dfe3 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -12,9 +12,11 @@
namespace pro_maat
{
-// Used as the base of all grid based computations
+/// Used for convenience
+using TextureStore = std::vector>;
+/// Used as the base of all grid based computations
using GridUnit = int16_t;
-// Used when dealing with the grid representation of the map
+/// Used when dealing with the grid representation of the map
using GridPos = std::pair;
static constexpr uint8_t pixelsPerUnit = 30;
@@ -24,6 +26,8 @@ static constexpr char fontFolder[] = "resources/";
void errorWindow(const std::string& error);
+/// Test if the coordinate is inside the map or not
+bool isInMap(const GridPos& square, const GridPos& gridSize);
// Good heuristic on 4-way grids
float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide);
/// Gets the 4 cardinal neighbours on the grid with edge checking