Compare commits
No commits in common. "60770b5395243f83a6142379866d42096b55589a" and "c94fc5a0be26d4d5b79a390cd43b223a8fb96115" have entirely different histories.
60770b5395
...
c94fc5a0be
20 changed files with 67 additions and 329 deletions
|
@ -6,7 +6,6 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
|
|||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(game)
|
||||
|
||||
# Detect and add SFML
|
||||
find_package(SFML COMPONENTS system window graphics network audio REQUIRED)
|
||||
|
@ -14,12 +13,6 @@ if(NOT SFML_FOUND)
|
|||
message(FATAL_ERROR "SFML could not be found")
|
||||
endif()
|
||||
|
||||
# Detect and add TGUI
|
||||
find_package(TGUI REQUIRED)
|
||||
if(NOT TGUI_FOUND)
|
||||
message(FATAL_ERROR "SFML could not be found")
|
||||
endif()
|
||||
|
||||
# Detect and add GTest
|
||||
find_package(GTest REQUIRED)
|
||||
if(GTest_FOUND)
|
||||
|
|
23
README.md
23
README.md
|
@ -5,20 +5,10 @@ Project Maat is the codename of my C++ course "mini-project" based on the quote
|
|||
|
||||
This game is aimed to be a puzzle game in which the player sets different laws for the world and the people within it as to control what happens after hitting "play" and achieve the level's goal. I was inspired both by [The Incredible Machine](https://en.wikipedia.org/wiki/The_Incredible_Machine_(video_game)) and its successors and by a much more recent game : [Baba is you](https://en.wikipedia.org/wiki/Baba_Is_You).
|
||||
|
||||
## Dependencies
|
||||
|
||||
The following libraries must be installed on your system or findable by CMake to be able to build:
|
||||
- SFML
|
||||
- TGui
|
||||
- PugiXML
|
||||
- GTest
|
||||
|
||||
## Structure
|
||||
|
||||

|
||||
|
||||
The class diagram omits most constructors and getters.
|
||||
|
||||
## TODO
|
||||
|
||||
- [x] Level
|
||||
|
@ -33,19 +23,16 @@ The class diagram omits most constructors and getters.
|
|||
- [ ] Rules
|
||||
- [x] Creation
|
||||
- [x] Interaction with entities
|
||||
- [x] Parsing of rules and creation of subsequent decorators
|
||||
- [ ] Parsing of rules and creation of subsequent decorators
|
||||
- [x] Find optimal target for pathfinding
|
||||
- [ ] Other rules (Waiting...)
|
||||
- [ ] Graphics
|
||||
- [x] Scene rendering
|
||||
- [x] Some kind of UI
|
||||
- [ ] UI
|
||||
- [ ] Menu ?
|
||||
- [ ] Gameloop
|
||||
- [x] Transition from starting to running state and vice-versa
|
||||
- [x] Entity behaviour evolution
|
||||
- [ ] Transition from starting to running state and vice-versa
|
||||
- [ ] Entity behaviour evolution
|
||||
- [x] Rule application
|
||||
- [ ] Win condition
|
||||
- [ ] ~~Saving state through serialization ?~~
|
||||
- [x] UML
|
||||
- [ ] Saving state through serialization ?
|
||||
- [ ] UML
|
||||
- [ ] Unit tests
|
Binary file not shown.
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 43 KiB |
|
@ -1,12 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
project(project_maat)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
|
||||
|
||||
add_executable(game main.cpp)
|
||||
|
||||
target_include_directories(game PRIVATE ${PROJECT_SOURCE_DIR}/../src)
|
||||
|
||||
target_link_libraries(game
|
||||
engine)
|
|
@ -1,15 +0,0 @@
|
|||
//
|
||||
// Created by trotfunky on 11/06/19.
|
||||
//
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
std::vector<std::string> textures = {"Head_Boy.png","Head_Significant_Boy.png","Building.png"};
|
||||
std::vector<std::string> levels = {"test_level.xml"};
|
||||
|
||||
Game game(levels,textures);
|
||||
game.loadLevel(0);
|
||||
game.runGame();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version = "1.0"?>
|
||||
<Level w="20" h="20" textureId="0">
|
||||
<Entity x="5" y="5" type="Citizen"/>
|
||||
<Entity x="10" y="5" type="Noble" textureId="1"/>
|
||||
</Level>
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version = "1.0"?>
|
||||
<Level w="20" h="20" textureId="0">
|
||||
<Entity x="2" y="2" type="Citizen"/>
|
||||
<Entity x="2" y="3" type="Citizen"/>
|
||||
<Entity x="2" y="4" type="Citizen"/>
|
||||
<Entity x="2" y="5" type="Citizen"/>
|
||||
<Entity x="2" y="6" type="Citizen"/>
|
||||
<Entity x="2" y="7" type="Citizen"/>
|
||||
|
||||
<Entity x="7" y="2" type="Citizen"/>
|
||||
<Entity x="7" y="3" type="Citizen"/>
|
||||
<Entity x="7" y="4" type="Citizen"/>
|
||||
<Entity x="7" y="5" type="Citizen"/>
|
||||
<Entity x="7" y="6" type="Citizen"/>
|
||||
<Entity x="7" y="7" type="Citizen"/>
|
||||
|
||||
<Entity y="2" x="2" type="Citizen"/>
|
||||
<Entity y="2" x="3" type="Citizen"/>
|
||||
<Entity y="2" x="4" type="Citizen"/>
|
||||
<Entity y="2" x="5" type="Citizen"/>
|
||||
<Entity y="2" x="6" type="Citizen"/>
|
||||
<Entity y="2" x="7" type="Citizen"/>
|
||||
|
||||
<Entity y="7" x="2" type="Citizen"/>
|
||||
<Entity y="7" x="3" type="Citizen"/>
|
||||
<Entity y="7" x="4" type="Citizen"/>
|
||||
<Entity y="7" x="5" type="Citizen"/>
|
||||
<Entity y="7" x="6" type="Citizen"/>
|
||||
<Entity y="7" x="7" type="Citizen"/>
|
||||
|
||||
<Entity x="10" y="10" type="Noble" textureId="1"/>
|
||||
<Entity x="3" y="3" w="4" h="4" type="House" textureId="2"/>
|
||||
</Level>
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version = "1.0"?>
|
||||
<Level w="20" h="20" textureId="0">
|
||||
<Entity x="0" y="0" type="Citizen"/>
|
||||
<Entity x="0" y="10" type="Citizen"/>
|
||||
<Entity x="10" y="10" type="Noble" textureId="1"/>
|
||||
<Entity x="2" y="2" w="4" h="4" type="House" textureId="2"/>
|
||||
<Entity x="0" y="1" type="Citizen"/>
|
||||
<Entity x="10" y="10" type="Significant" textureId="1"/>
|
||||
<Entity x="1" y="1" w="4" h="4" type="House" textureId="2"/>
|
||||
</Level>
|
|
@ -10,8 +10,7 @@ target_link_libraries(engine
|
|||
sfml-window
|
||||
sfml-graphics
|
||||
sfml-system
|
||||
pugixml
|
||||
tgui)
|
||||
pugixml)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
target_compile_options(engine PRIVATE -Wall -Wpedantic -Wextra)
|
||||
|
|
|
@ -6,14 +6,10 @@
|
|||
|
||||
const std::map<std::string,EntityType> Entity::entityTypeLookup = {
|
||||
{"Citizen",EntityType::Citizen},
|
||||
{"Noble",EntityType::Noble},
|
||||
{"Significant",EntityType::Significant},
|
||||
{"House",EntityType::House},
|
||||
{"Car",EntityType::Car}};
|
||||
|
||||
const std::map<std::string,State> Entity::stateLookup = {
|
||||
{"Seek",State::Moving},
|
||||
{"Flee",State::Fleeing}};
|
||||
|
||||
Entity::Entity(pro_maat::GridUnit x, pro_maat::GridUnit y, EntityType type, sf::Texture* texture, int width, int height) : type(type)
|
||||
{
|
||||
shape = sf::RectangleShape(sf::Vector2f(width*pro_maat::pixelsPerUnit,height*pro_maat::pixelsPerUnit));
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
enum class EntityType
|
||||
{
|
||||
Citizen,
|
||||
Noble,
|
||||
Significant,
|
||||
House,
|
||||
Car,
|
||||
};
|
||||
|
@ -62,15 +62,14 @@ public:
|
|||
virtual const std::vector<pro_maat::GridPos> getOccupiedSquares() const;
|
||||
|
||||
|
||||
static const std::map<std::string,EntityType> entityTypeLookup;
|
||||
static const std::map<std::string,State> stateLookup;
|
||||
|
||||
protected:
|
||||
/// Empty constructor for derived class instanciation
|
||||
Entity();
|
||||
|
||||
|
||||
private:
|
||||
static const std::map<std::string,EntityType> entityTypeLookup;
|
||||
|
||||
EntityType type;
|
||||
|
||||
// As it contains position, size and orientation, we do not need anything more
|
||||
|
|
142
src/Game.cpp
142
src/Game.cpp
|
@ -8,25 +8,22 @@
|
|||
|
||||
|
||||
Game::Game(std::vector<std::string>& levels, std::vector<std::string>& textures) : levelFiles(std::move(levels)),
|
||||
textureFiles(std::move(textures)), running(false), ruleCount(0)
|
||||
textureFiles(std::move(textures))
|
||||
{
|
||||
loadTextures();
|
||||
}
|
||||
|
||||
void Game::loadLevel(int levelID)
|
||||
void Game::loadLevel(int levelId)
|
||||
{
|
||||
// TODO : Reset rules when reseting level
|
||||
running = false;
|
||||
|
||||
pugi::xml_document document;
|
||||
pugi::xml_parse_result result = document.load_file((pro_maat::levelFolder+levelFiles.at(levelID)).c_str());
|
||||
pugi::xml_parse_result result = document.load_file((pro_maat::levelFolder+levelFiles.at(levelId)).c_str());
|
||||
|
||||
if(!result)
|
||||
{
|
||||
pro_maat::errorWindow(result.description());
|
||||
}
|
||||
|
||||
currentLevel = std::make_unique<Level>(document,textures,levelID);
|
||||
currentLevel = std::make_unique<Level>(document,textures);
|
||||
}
|
||||
|
||||
void Game::loadTextures()
|
||||
|
@ -45,9 +42,6 @@ void Game::runGame()
|
|||
|
||||
sf::Clock clock;
|
||||
|
||||
tgui::Gui gui(window);
|
||||
addWidgets(gui);
|
||||
|
||||
while (window.isOpen())
|
||||
{
|
||||
sf::Event event;
|
||||
|
@ -57,10 +51,8 @@ void Game::runGame()
|
|||
{
|
||||
window.close();
|
||||
}
|
||||
|
||||
gui.handleEvent(event);
|
||||
}
|
||||
if (running && clock.getElapsedTime().asMilliseconds() >= 200)
|
||||
if (clock.getElapsedTime().asMilliseconds() >= 200)
|
||||
{
|
||||
currentLevel->runStep();
|
||||
clock.restart();
|
||||
|
@ -68,135 +60,11 @@ void Game::runGame()
|
|||
window.clear(sf::Color::White);
|
||||
// TODO : Consider drawing in a sf::RenderTexture to allow positioning the level at a fixed position ?
|
||||
currentLevel->render(window);
|
||||
gui.draw();
|
||||
window.display();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::addWidgets(tgui::Gui& gui)
|
||||
{
|
||||
tgui::Button::Ptr button = tgui::Button::create("Start");
|
||||
button->setSize("10%","5%");
|
||||
button->setPosition("89%","94%");
|
||||
button->connect("pressed",&Game::setRunning,this,true,std::ref(gui));
|
||||
gui.add(button,"start");
|
||||
|
||||
// TODO : Use clone/copy ?
|
||||
button = tgui::Button::create("Stop");
|
||||
button->setSize("10%","5%");
|
||||
button->setPosition("79%","94%");
|
||||
button->connect("pressed",&Game::setRunning,this,false,std::ref(gui));
|
||||
gui.add(button,"stop");
|
||||
|
||||
button = tgui::Button::create("Reset");
|
||||
button->setSize("10%","5%");
|
||||
button->setPosition("69%","94%");
|
||||
button->connect("pressed",&Game::loadLevel,this,currentLevel->getLevelID());
|
||||
gui.add(button,"reset");
|
||||
|
||||
button = tgui::Button::create("Add a new rule");
|
||||
button->setSize("10%","5%");
|
||||
button->setPosition("89%","6%");
|
||||
button->connect("pressed",&Game::addRule,this,std::ref(gui));
|
||||
gui.add(button,"add");
|
||||
}
|
||||
|
||||
Game::operator bool() const
|
||||
{
|
||||
return (currentLevel ? true : false);
|
||||
}
|
||||
|
||||
//
|
||||
// Widget callbacks
|
||||
//
|
||||
|
||||
void Game::setRunning(bool newState, tgui::Gui& gui)
|
||||
{
|
||||
running = newState;
|
||||
|
||||
// TODO : Iterate over all rules
|
||||
|
||||
auto affectedTypeCombo = gui.get<tgui::ComboBox>("AffectedType0");
|
||||
if(!affectedTypeCombo) return;
|
||||
auto targetTypeCombo = gui.get<tgui::ComboBox>("TargetType0");
|
||||
if(!targetTypeCombo) return;
|
||||
auto actionCombo = gui.get<tgui::ComboBox>("Action0");
|
||||
if(!actionCombo) return;
|
||||
|
||||
// If the rule is complete only
|
||||
if(!affectedTypeCombo->getSelectedItem().isEmpty() &&
|
||||
!targetTypeCombo->getSelectedItem().isEmpty() &&
|
||||
!actionCombo->getSelectedItem().isEmpty())
|
||||
{
|
||||
currentLevel->addRule(Entity::entityTypeLookup.at(affectedTypeCombo->getSelectedItem()),
|
||||
Entity::stateLookup.at(actionCombo->getSelectedItem()),
|
||||
Entity::entityTypeLookup.at(targetTypeCombo->getSelectedItem()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Game::addRule(tgui::Gui& gui)
|
||||
{
|
||||
if(ruleCount >= pro_maat::maxRules) return;
|
||||
|
||||
// To update names with indexes
|
||||
std::stringstream string;
|
||||
|
||||
|
||||
tgui::ComboBox::Ptr combo = tgui::ComboBox::create();
|
||||
combo->addItem("Citizen");
|
||||
combo->addItem("Noble");
|
||||
combo->setPosition("85%","5%");
|
||||
|
||||
// TODO : Move height according to number of rules
|
||||
// Keep the proportions while moving
|
||||
// sf::Vector2f tempPos = combo->getPosition();
|
||||
// tempPos.y += tempPos.y*2*ruleCount;
|
||||
// combo->setPosition(tempPos);
|
||||
combo->setSize("5%","2%");
|
||||
|
||||
string << "AffectedType" << ruleCount;
|
||||
gui.add(combo,string.str());
|
||||
string.str("");
|
||||
|
||||
// TODO : Use clone/copy ?
|
||||
combo = tgui::ComboBox::create();
|
||||
combo->addItem("Citizen");
|
||||
combo->addItem("Noble");
|
||||
combo->addItem("House");
|
||||
combo->setSize("5%","2%");
|
||||
combo->setPosition("95%","5%");
|
||||
|
||||
// Keep the proportions while moving
|
||||
// tempPos = combo->getPosition();
|
||||
// tempPos.y += tempPos.y*2*ruleCount;
|
||||
// combo->setPosition(tempPos);
|
||||
|
||||
string << "TargetType" << ruleCount;
|
||||
gui.add(combo,string.str());
|
||||
string.str("");
|
||||
|
||||
combo = tgui::ComboBox::create();
|
||||
combo->addItem("Seek");
|
||||
combo->addItem("Flee");
|
||||
combo->setSize("4%","2%");
|
||||
combo->setPosition("90.5%","5%");
|
||||
|
||||
// Keep the proportions while moving
|
||||
// tempPos = combo->getPosition();
|
||||
// tempPos.y += tempPos.y*2*ruleCount;
|
||||
// combo->setPosition(tempPos);
|
||||
|
||||
string << "Action" << ruleCount;
|
||||
gui.add(combo,string.str());
|
||||
string.str("");
|
||||
|
||||
// TODO : Move the button down with each rule then hide it
|
||||
// TODO : Hide button when max rules hit
|
||||
// FIXME : Cannot hide ?
|
||||
gui.get<tgui::Button>("add")->setSize("0%","0%");
|
||||
gui.get<tgui::Button>("add")->setTextSize(0);
|
||||
gui.get<tgui::Button>("add")->setPosition("-10%","-10%");
|
||||
|
||||
ruleCount++;
|
||||
}
|
||||
|
|
16
src/Game.h
16
src/Game.h
|
@ -6,22 +6,21 @@
|
|||
#define SRC_GAME_H
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <TGUI/TGUI.hpp>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "Level.h"
|
||||
#include "Entity.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game(std::vector<std::string>& levels, std::vector<std::string>& textures);
|
||||
|
||||
/// Loads the level of corresponding ID from Game::levelFiles
|
||||
/// Closes the program if there is a fatal error (Missing file, bad file...)
|
||||
void loadLevel(int levelID);
|
||||
void loadLevel(int levelId);
|
||||
|
||||
std::unique_ptr<Level> currentLevel;
|
||||
|
||||
|
@ -38,18 +37,7 @@ private:
|
|||
/// Stores pointers to textures for future use
|
||||
pro_maat::TextureStore textures;
|
||||
|
||||
bool running;
|
||||
unsigned int ruleCount;
|
||||
|
||||
void loadTextures();
|
||||
void addWidgets(tgui::Gui& gui);
|
||||
|
||||
//
|
||||
// Widget callbacks
|
||||
//
|
||||
|
||||
void setRunning(bool newState,tgui::Gui& gui);
|
||||
void addRule(tgui::Gui& gui);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#include "Level.h"
|
||||
|
||||
|
||||
Level::Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore, int id)
|
||||
: levelID(id), size(xmlDoc.child("Level").attribute("w").as_int(),xmlDoc.child("Level").attribute("h").as_int()),
|
||||
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)
|
||||
{
|
||||
pugi::xml_node levelNode = xmlDoc.child("Level");
|
||||
|
@ -14,7 +14,7 @@ Level::Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& tex
|
|||
{
|
||||
if(!strncmp(child.name(),"Entity",6))
|
||||
{
|
||||
entities.emplace_back(std::make_shared<Entity>(child,textures.at(child.attribute("textureId").as_int(0)).get()));
|
||||
entities.emplace_back(std::make_unique<Entity>(child,textures.at(child.attribute("textureId").as_int(0)).get()));
|
||||
|
||||
// Initialize the occupied squares vector with the new entity's squares
|
||||
std::vector<pro_maat::GridPos> entitySquares = entities.rbegin()->get()->getOccupiedSquares();
|
||||
|
@ -23,8 +23,7 @@ Level::Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& tex
|
|||
}
|
||||
|
||||
// FIXME : For testing purposes
|
||||
// addRule(EntityType::Noble,State::Moving,EntityType::House);
|
||||
// addRule(EntityType::Citizen,State::Moving,EntityType::Citizen);
|
||||
addRule(EntityType::Significant,State::Moving,EntityType::Citizen);
|
||||
}
|
||||
|
||||
void Level::addRule(EntityType affectedEntities, const State targetState, EntityType targetEntities)
|
||||
|
@ -33,7 +32,7 @@ void Level::addRule(EntityType affectedEntities, const State targetState, Entity
|
|||
{
|
||||
if(entity->getType() == affectedEntities)
|
||||
{
|
||||
entity = std::make_shared<Rule>(entity,targetState,targetEntities,entities,occupiedSquares,size);
|
||||
entity = std::make_unique<Rule>(entity.release(),targetState,targetEntities,entities,occupiedSquares,size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +127,6 @@ Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int
|
|||
// Expand from the open node with the smallest estimated cost
|
||||
pro_maat::GridPos currentNode = std::min_element(estimatedCosts.begin(),estimatedCosts.end(),compWithOpen)->first;
|
||||
|
||||
// FIXME : Lots of bad cases when fleeing
|
||||
if(currentNode == goal)
|
||||
{
|
||||
if(currentNode == start)
|
||||
|
@ -191,7 +189,7 @@ Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int
|
|||
}
|
||||
|
||||
pathCosts.insert_or_assign(neighbour,newPathCost);
|
||||
estimatedCosts.insert_or_assign(neighbour,newPathCost + pro_maat::manhattanDistance(neighbour,goal)*sign);
|
||||
estimatedCosts.insert_or_assign(neighbour,newPathCost + pro_maat::manhattanDistance(neighbour,goal));
|
||||
paths.insert_or_assign(neighbour,currentNode);
|
||||
}
|
||||
}
|
||||
|
@ -199,8 +197,3 @@ Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int
|
|||
// If we did not find a path, do not move
|
||||
return Orientation::None;
|
||||
}
|
||||
|
||||
int Level::getLevelID()
|
||||
{
|
||||
return levelID;
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
#include <cstring>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "Utils.h"
|
||||
#include "Entity.h"
|
||||
|
@ -20,7 +19,7 @@
|
|||
|
||||
class Level {
|
||||
public:
|
||||
Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore, int id = 0);
|
||||
Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore);
|
||||
|
||||
/// Add a new rule on top of existing ones, thus with a lower priority
|
||||
///
|
||||
|
@ -32,14 +31,10 @@ public:
|
|||
void render(sf::RenderWindow& renderWindow) const;
|
||||
void runStep();
|
||||
|
||||
int getLevelID();
|
||||
|
||||
private:
|
||||
int levelID;
|
||||
|
||||
const pro_maat::GridPos size;
|
||||
|
||||
std::vector<std::shared_ptr<Entity>> entities;
|
||||
std::vector<std::unique_ptr<Entity>> entities;
|
||||
|
||||
const pro_maat::TextureStore& textures;
|
||||
|
||||
|
|
67
src/Rule.cpp
67
src/Rule.cpp
|
@ -5,10 +5,10 @@
|
|||
#include "Rule.h"
|
||||
|
||||
|
||||
Rule::Rule(std::shared_ptr<Entity> entity, State targetState, EntityType targetType,
|
||||
const std::vector<std::shared_ptr<Entity>>& entities,
|
||||
Rule::Rule(Entity* entity, State targetState, EntityType targetType,
|
||||
std::vector<std::unique_ptr<Entity>>& entities,
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares, const pro_maat::GridPos& mapSize)
|
||||
: entity(std::move(entity)),
|
||||
: entity(entity),
|
||||
targetState(targetState),
|
||||
targetType(targetType),
|
||||
entities(entities),
|
||||
|
@ -20,17 +20,15 @@ void Rule::update()
|
|||
{
|
||||
if (targetState == State::Moving || targetState == State::Fleeing)
|
||||
{
|
||||
pro_maat::GridPos target = findTarget();
|
||||
if(target != entity->getPosition())
|
||||
{
|
||||
entity->nextTarget = target;
|
||||
entity->nextState = targetState;
|
||||
}
|
||||
else
|
||||
entity->nextTarget = findTarget();
|
||||
if(entity->nextTarget == entity->getPosition())
|
||||
{
|
||||
entity->nextState = State::Idle;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
entity->nextState = targetState;
|
||||
}
|
||||
entity->update();
|
||||
}
|
||||
else if (targetState == State::Waiting)
|
||||
|
@ -49,11 +47,12 @@ void Rule::update()
|
|||
|
||||
pro_maat::GridPos Rule::findTarget()
|
||||
{
|
||||
std::vector<std::shared_ptr<Entity>> sortedEntities{};
|
||||
sortedEntities.insert(sortedEntities.end(),entities.begin(),entities.end());
|
||||
// TODO : Sorting in place, consider using shared_ptr ?
|
||||
// std::vector<Entity> sortedEntities{};
|
||||
// sortedEntities.insert(sortedEntities.end(),entities.begin(),entities.end());
|
||||
|
||||
// Compares entities via their distance to the current entity
|
||||
auto distanceSortEntities = [this](const std::shared_ptr<Entity>& leftHandSide, const std::shared_ptr<Entity>& rightHandSide){
|
||||
auto distanceSortEntities = [this](const std::unique_ptr<Entity>& leftHandSide, const std::unique_ptr<Entity>& rightHandSide){
|
||||
return (pro_maat::manhattanDistance(entity->getPosition(),leftHandSide->getPosition()) <
|
||||
pro_maat::manhattanDistance(entity->getPosition(),rightHandSide->getPosition()));
|
||||
};
|
||||
|
@ -67,35 +66,28 @@ pro_maat::GridPos Rule::findTarget()
|
|||
// 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))
|
||||
if(!pro_maat::isInMap(leftHandSide,mapSize) ||
|
||||
std::find(occupiedSquares.begin(),occupiedSquares.end(),leftHandSide) != occupiedSquares.end())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
else if(std::find(occupiedSquares.begin(),occupiedSquares.end(),leftHandSide) != occupiedSquares.end())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
else if(!pro_maat::isInMap(rightHandSide,mapSize))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else if(std::find(occupiedSquares.begin(),occupiedSquares.end(),rightHandSide) != occupiedSquares.end())
|
||||
else if(!pro_maat::isInMap(rightHandSide,mapSize) ||
|
||||
std::find(occupiedSquares.begin(),occupiedSquares.end(),rightHandSide) != occupiedSquares.end())
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(pro_maat::manhattanDistance(entity->getPosition(),leftHandSide) <
|
||||
pro_maat::manhattanDistance(entity->getPosition(),rightHandSide));
|
||||
return(leftHandSide < rightHandSide);
|
||||
}};
|
||||
|
||||
|
||||
// Sort in order to minimize entities to process
|
||||
std::sort(sortedEntities.begin(),sortedEntities.end(),distanceSortEntities);
|
||||
std::sort(entities.begin(),entities.end(),distanceSortEntities);
|
||||
|
||||
for(const auto& processingEntity : sortedEntities)
|
||||
for(const auto& processingEntity : entities)
|
||||
{
|
||||
if(processingEntity->getType() != targetType || processingEntity->getPosition() == entity->getPosition()) continue;
|
||||
if(processingEntity->getType() != targetType) continue;
|
||||
|
||||
std::vector<pro_maat::GridPos> potentialTargets{};
|
||||
|
||||
|
@ -104,34 +96,27 @@ pro_maat::GridPos Rule::findTarget()
|
|||
|
||||
potentialTargets.reserve((entityWidth+2)*2+entityHeight*2);
|
||||
|
||||
// Computes the top left corner just outside of the entity
|
||||
// Computes the top left corner of the entity
|
||||
pro_maat::GridPos topLeftCorner = processingEntity->getPosition();
|
||||
topLeftCorner.first -= std::floor(entityWidth*0.5) + 1;
|
||||
topLeftCorner.second -= std::floor(entityHeight*0.5) + 1;
|
||||
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<entityWidth+2;i++)
|
||||
{
|
||||
potentialTargets.emplace_back(topLeftCorner.first+i,topLeftCorner.second);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+i,topLeftCorner.second+entityHeight+1);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+i,topLeftCorner.second+entityHeight+2);
|
||||
}
|
||||
// Get the missing adjacent squares from the sides
|
||||
for(int i = 1;i<=entityHeight;i++)
|
||||
{
|
||||
potentialTargets.emplace_back(topLeftCorner.first,topLeftCorner.second+i);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+entityWidth+1,topLeftCorner.second+i);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+entityWidth+2,topLeftCorner.second+i);
|
||||
}
|
||||
|
||||
|
||||
std::sort(potentialTargets.begin(),potentialTargets.end(),distanceSortSquares);
|
||||
|
||||
// If we are adjacent to a target, we do not need to move
|
||||
if(std::find(potentialTargets.begin(),potentialTargets.end(),entity->getPosition()) != potentialTargets.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto target = std::min_element(potentialTargets.begin(),potentialTargets.end(),bestTarget);
|
||||
|
||||
if(target != potentialTargets.end())
|
||||
|
|
14
src/Rule.h
14
src/Rule.h
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <memory>
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
|
@ -18,8 +16,7 @@ class Rule : public Entity
|
|||
{
|
||||
public:
|
||||
// The Rule object takes ownership of the Entity*
|
||||
Rule(std::shared_ptr<Entity> entity, State targetState, EntityType targetType,
|
||||
const std::vector<std::shared_ptr<Entity>>& entities,
|
||||
Rule(Entity* entity, State targetState, EntityType targetType, std::vector<std::unique_ptr<Entity>>& entities,
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares, const pro_maat::GridPos& mapSize);
|
||||
|
||||
/// Update according to the targetState and targetType
|
||||
|
@ -41,12 +38,13 @@ private:
|
|||
/// \return Suitable target square or current position if none was found.
|
||||
pro_maat::GridPos findTarget();
|
||||
|
||||
std::shared_ptr<Entity> entity;
|
||||
std::unique_ptr<Entity> entity;
|
||||
|
||||
const State targetState;
|
||||
const EntityType targetType;
|
||||
State targetState;
|
||||
EntityType targetType;
|
||||
|
||||
const std::vector<std::shared_ptr<Entity>>& entities;
|
||||
// TOOD : dropped const-qualifier. Consider using shared_ptr ?
|
||||
std::vector<std::unique_ptr<Entity>>& entities;
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares;
|
||||
|
||||
const pro_maat::GridPos& mapSize;
|
||||
|
|
|
@ -39,8 +39,8 @@ 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);
|
||||
return (square.first < 0 || square.second < 0 ||
|
||||
square.first >= gridSize.first || square.second >= gridSize.second);
|
||||
}
|
||||
|
||||
float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide)
|
||||
|
|
|
@ -20,8 +20,6 @@ using GridUnit = int16_t;
|
|||
using GridPos = std::pair<GridUnit,GridUnit>;
|
||||
|
||||
static constexpr uint8_t pixelsPerUnit = 30;
|
||||
static constexpr unsigned int maxRules = 5;
|
||||
|
||||
static constexpr char levelFolder[] = "resources/";
|
||||
static constexpr char textureFolder[] = "resources/";
|
||||
static constexpr char fontFolder[] = "resources/";
|
||||
|
|
|
@ -11,4 +11,8 @@ target_include_directories(gTests PRIVATE ${PROJECT_SOURCE_DIR}/../src)
|
|||
target_link_libraries(gTests
|
||||
engine
|
||||
gtest
|
||||
pthread)
|
||||
pthread
|
||||
sfml-window
|
||||
sfml-graphics
|
||||
sfml-system
|
||||
pugixml)
|
Loading…
Add table
Add a link
Reference in a new issue