Almost working save sytem but the data I store is totally wrong. I need to also save the entity IDs being used and map them to the components.
This commit is contained in:
parent
babc190525
commit
d113dba42f
15 changed files with 213 additions and 64 deletions
18
combat.cpp
18
combat.cpp
|
@ -1,14 +1,16 @@
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
#include "rand.hpp"
|
#include "rand.hpp"
|
||||||
|
|
||||||
int Combat::attack(Combat &target) {
|
namespace components {
|
||||||
int attack = Random::uniform<int>(0,1);
|
int Combat::attack(Combat &target) {
|
||||||
int my_dmg = 0;
|
int attack = Random::uniform<int>(0,1);
|
||||||
|
int my_dmg = 0;
|
||||||
|
|
||||||
if(attack) {
|
if(attack) {
|
||||||
my_dmg = Random::uniform<int>(1, damage);
|
my_dmg = Random::uniform<int>(1, damage);
|
||||||
target.hp -= my_dmg;
|
target.hp -= my_dmg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return my_dmg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return my_dmg;
|
|
||||||
}
|
}
|
||||||
|
|
16
combat.hpp
16
combat.hpp
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "components.hpp"
|
namespace components {
|
||||||
|
struct Combat {
|
||||||
|
int hp;
|
||||||
|
int damage;
|
||||||
|
bool dead;
|
||||||
|
|
||||||
struct Combat {
|
int attack(Combat &target);
|
||||||
int hp;
|
};
|
||||||
int damage;
|
}
|
||||||
bool dead;
|
|
||||||
|
|
||||||
int attack(Combat &target);
|
|
||||||
};
|
|
||||||
|
|
|
@ -3,27 +3,33 @@
|
||||||
#include "map.hpp"
|
#include "map.hpp"
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include "tser.hpp"
|
||||||
|
|
||||||
namespace Components {
|
namespace components {
|
||||||
struct Player {
|
struct Player {
|
||||||
DinkyECS::Entity entity;
|
DinkyECS::Entity entity;
|
||||||
|
DEFINE_SERIALIZABLE(Player, entity);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Position {
|
struct Position {
|
||||||
Point location;
|
Point location;
|
||||||
|
DEFINE_SERIALIZABLE(Position, location);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Motion {
|
struct Motion {
|
||||||
int dx;
|
int dx;
|
||||||
int dy;
|
int dy;
|
||||||
|
DEFINE_SERIALIZABLE(Motion, dx, dy);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Treasure {
|
struct Treasure {
|
||||||
int amount;
|
int amount;
|
||||||
|
DEFINE_SERIALIZABLE(Treasure, amount);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Tile {
|
struct Tile {
|
||||||
std::string chr = "!";
|
std::string chr = "!";
|
||||||
|
DEFINE_SERIALIZABLE(Tile, chr);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MapConfig {
|
struct MapConfig {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include "tser.hpp"
|
||||||
|
|
||||||
namespace DinkyECS {
|
namespace DinkyECS {
|
||||||
|
|
||||||
|
@ -116,4 +117,6 @@ namespace DinkyECS {
|
||||||
return !queue.empty();
|
return !queue.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
11
gui.cpp
11
gui.cpp
|
@ -24,7 +24,7 @@ using std::string;
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace ftxui;
|
using namespace ftxui;
|
||||||
using namespace Components;
|
using namespace components;
|
||||||
|
|
||||||
|
|
||||||
GUI::GUI(DinkyECS::World &world, Map& game_map) :
|
GUI::GUI(DinkyECS::World &world, Map& game_map) :
|
||||||
|
@ -56,6 +56,13 @@ void GUI::resize_map(int new_size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI::save_world() {
|
||||||
|
tser::BinaryArchive archive;
|
||||||
|
archive.save($world);
|
||||||
|
std::string_view archive_view = archive.get_buffer();
|
||||||
|
$log.log(format("Game saved! {} bytes.", archive_view.size()));
|
||||||
|
}
|
||||||
|
|
||||||
void GUI::create_renderer() {
|
void GUI::create_renderer() {
|
||||||
Terminal::SetColorSupport(Terminal::Color::TrueColor);
|
Terminal::SetColorSupport(Terminal::Color::TrueColor);
|
||||||
auto player = $world.get_the<Player>();
|
auto player = $world.get_the<Player>();
|
||||||
|
@ -151,6 +158,8 @@ bool GUI::handle_ui_events() {
|
||||||
resize_map(map_font_size + 10);
|
resize_map(map_font_size + 10);
|
||||||
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) {
|
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) {
|
||||||
resize_map(map_font_size - 10);
|
resize_map(map_font_size - 10);
|
||||||
|
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
|
||||||
|
save_world();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
gui.hpp
1
gui.hpp
|
@ -58,6 +58,7 @@ public:
|
||||||
void handle_world_events();
|
void handle_world_events();
|
||||||
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
|
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
|
||||||
void run_systems();
|
void run_systems();
|
||||||
|
void save_world();
|
||||||
|
|
||||||
int main();
|
int main();
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ runtests = executable('runtests', [
|
||||||
'collider.cpp',
|
'collider.cpp',
|
||||||
'ansi_parser.cpp',
|
'ansi_parser.cpp',
|
||||||
'config.cpp',
|
'config.cpp',
|
||||||
|
'save.cpp',
|
||||||
'tests/fsm.cpp',
|
'tests/fsm.cpp',
|
||||||
'tests/dbc.cpp',
|
'tests/dbc.cpp',
|
||||||
'tests/map.cpp',
|
'tests/map.cpp',
|
||||||
|
@ -29,6 +30,7 @@ runtests = executable('runtests', [
|
||||||
'tests/dinkyecs.cpp',
|
'tests/dinkyecs.cpp',
|
||||||
'tests/ansi_parser.cpp',
|
'tests/ansi_parser.cpp',
|
||||||
'tests/config.cpp',
|
'tests/config.cpp',
|
||||||
|
'tests/save.cpp',
|
||||||
],
|
],
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
||||||
|
@ -45,6 +47,7 @@ roguish = executable('roguish', [
|
||||||
'ansi_parser.cpp',
|
'ansi_parser.cpp',
|
||||||
'render.cpp',
|
'render.cpp',
|
||||||
'config.cpp',
|
'config.cpp',
|
||||||
|
'save.cpp',
|
||||||
],
|
],
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "tser.hpp"
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
size_t x = 0;
|
size_t x = 0;
|
||||||
|
@ -7,6 +8,8 @@ struct Point {
|
||||||
bool operator==(const Point& other) const {
|
bool operator==(const Point& other) const {
|
||||||
return other.x == x && other.y == y;
|
return other.x == x && other.y == y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(Point, x, y);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Point> PointList;
|
typedef std::vector<Point> PointList;
|
||||||
|
|
63
save.cpp
Normal file
63
save.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#include "save.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include "dbc.hpp"
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
using namespace components;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename CompT>
|
||||||
|
inline void extract(DinkyECS::World &world, std::vector<CompT> &into) {
|
||||||
|
auto from_world = world.entity_map_for<CompT>();
|
||||||
|
for(auto [entity, value] : from_world) {
|
||||||
|
into.push_back(std::any_cast<CompT>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void save::to_file(std::string path, DinkyECS::World &world) {
|
||||||
|
SaveData save_data;
|
||||||
|
tser::BinaryArchive archive;
|
||||||
|
|
||||||
|
save_data.player = world.get_the<Player>();
|
||||||
|
extract<Position>(world, save_data.position);
|
||||||
|
extract<Combat>(world, save_data.combat);
|
||||||
|
extract<Motion>(world, save_data.motion);
|
||||||
|
|
||||||
|
archive.save(save_data);
|
||||||
|
std::string_view archive_view = archive.get_buffer();
|
||||||
|
|
||||||
|
std::ofstream out(path, std::ios::binary);
|
||||||
|
out << archive_view;
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void save::from_file(std::string path, DinkyECS::World &world_out) {
|
||||||
|
tser::BinaryArchive archive(0);
|
||||||
|
|
||||||
|
if(std::ifstream in_file{path, std::ios::binary | std::ios::ate}) {
|
||||||
|
auto size = in_file.tellg();
|
||||||
|
std::string in_data(size, '\0');
|
||||||
|
in_file.seekg(0);
|
||||||
|
|
||||||
|
if(in_file.read(&in_data[0], size)) {
|
||||||
|
std::string_view in_view(in_data);
|
||||||
|
archive.initialize(in_view);
|
||||||
|
} else {
|
||||||
|
dbc::sentinel(fmt::format("wrong size or error reading {}", path));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dbc::sentinel(fmt::format("failed to load file {}", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto save_data = archive.load<SaveData>();
|
||||||
|
|
||||||
|
// BUG: need the entities!
|
||||||
|
world_out.set_the<Player>(save_data.player);
|
||||||
|
|
||||||
|
for(auto position : save_data.position) {
|
||||||
|
auto entity = world_out.entity();
|
||||||
|
// BUG: actually do need the entities
|
||||||
|
world_out.set<Position>(entity, position);
|
||||||
|
}
|
||||||
|
}
|
21
save.hpp
Normal file
21
save.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "components.hpp"
|
||||||
|
#include "dinkyecs.hpp"
|
||||||
|
#include "tser.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace save {
|
||||||
|
struct SaveData {
|
||||||
|
components::Player player;
|
||||||
|
std::vector<components::Position> position;
|
||||||
|
std::vector<components::Motion> motion;
|
||||||
|
std::vector<components::Combat> combat;
|
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(SaveData, player, position, motion, combat);
|
||||||
|
};
|
||||||
|
|
||||||
|
void to_file(std::string path, DinkyECS::World &world);
|
||||||
|
void from_file(std::string path, DinkyECS::World &world_out);
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using namespace Components;
|
using namespace components;
|
||||||
using ftxui::Color;
|
using ftxui::Color;
|
||||||
|
|
||||||
void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) {
|
void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
#include <ftxui/dom/canvas.hpp>
|
#include <ftxui/dom/canvas.hpp>
|
||||||
|
|
||||||
using namespace Components;
|
using namespace components;
|
||||||
|
|
||||||
namespace System {
|
namespace System {
|
||||||
void motion(DinkyECS::World &world, Map &game_map);
|
void motion(DinkyECS::World &world, Map &game_map);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "dinkyecs.hpp"
|
#include "dinkyecs.hpp"
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
|
|
||||||
|
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
@ -61,47 +62,3 @@ TEST_CASE("can go into a world", "[config]") {
|
||||||
Config &cfg = world.get_the<Config>();
|
Config &cfg = world.get_the<Config>();
|
||||||
REQUIRE(cfg["types"]["NUMBER"] == 1234);
|
REQUIRE(cfg["types"]["NUMBER"] == 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <iostream>
|
|
||||||
#include "tser.hpp"
|
|
||||||
|
|
||||||
enum class Item : char {
|
|
||||||
RADAR = 'R',
|
|
||||||
TRAP = 'T',
|
|
||||||
ORE = 'O'
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Pixel {
|
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
|
|
||||||
DEFINE_SERIALIZABLE(Pixel, x, y);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Robot {
|
|
||||||
Pixel point;
|
|
||||||
std::wstring name;
|
|
||||||
std::optional<Item> item;
|
|
||||||
|
|
||||||
DEFINE_SERIALIZABLE(Robot, point, name, item);
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("test using tser for serialization", "[config]") {
|
|
||||||
auto robot = Robot{ Pixel{3,4}, L"BIG NAME", Item::RADAR};
|
|
||||||
std::cout << robot << '\n';
|
|
||||||
|
|
||||||
tser::BinaryArchive archive;
|
|
||||||
archive.save(robot);
|
|
||||||
std::string_view archive_view = archive.get_buffer();
|
|
||||||
|
|
||||||
tser::BinaryArchive archive2(0);
|
|
||||||
archive2.initialize(archive_view);
|
|
||||||
auto loadedRobot = archive2.load<Robot>();
|
|
||||||
|
|
||||||
REQUIRE(loadedRobot.point.x == robot.point.x);
|
|
||||||
REQUIRE(loadedRobot.point.y == robot.point.y);
|
|
||||||
REQUIRE(loadedRobot.name == robot.name);
|
|
||||||
REQUIRE(loadedRobot.item == robot.item);
|
|
||||||
}
|
|
||||||
|
|
80
tests/save.cpp
Normal file
80
tests/save.cpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <string>
|
||||||
|
#include "dinkyecs.hpp"
|
||||||
|
#include "components.hpp"
|
||||||
|
#include "save.hpp"
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
#include "tser.hpp"
|
||||||
|
|
||||||
|
using namespace fmt;
|
||||||
|
using std::string;
|
||||||
|
using namespace components;
|
||||||
|
|
||||||
|
enum class Item : char {
|
||||||
|
RADAR = 'R',
|
||||||
|
TRAP = 'T',
|
||||||
|
ORE = 'O'
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pixel {
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(Pixel, x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Robot {
|
||||||
|
Pixel point;
|
||||||
|
std::wstring name;
|
||||||
|
std::optional<Item> item;
|
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(Robot, point, name, item);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("test using tser for serialization", "[config]") {
|
||||||
|
auto robot = Robot{ Pixel{3,4}, L"BIG NAME", Item::RADAR};
|
||||||
|
std::cout << robot << '\n';
|
||||||
|
|
||||||
|
tser::BinaryArchive archive;
|
||||||
|
archive.save(robot);
|
||||||
|
std::string_view archive_view = archive.get_buffer();
|
||||||
|
|
||||||
|
tser::BinaryArchive archive2(0);
|
||||||
|
archive2.initialize(archive_view);
|
||||||
|
auto loadedRobot = archive2.load<Robot>();
|
||||||
|
|
||||||
|
REQUIRE(loadedRobot.point.x == robot.point.x);
|
||||||
|
REQUIRE(loadedRobot.point.y == robot.point.y);
|
||||||
|
REQUIRE(loadedRobot.name == robot.name);
|
||||||
|
REQUIRE(loadedRobot.item == robot.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("basic save a world", "[save]") {
|
||||||
|
DinkyECS::World world;
|
||||||
|
|
||||||
|
// configure a player as a fact of the world
|
||||||
|
Player player{world.entity()};
|
||||||
|
world.set_the<Player>(player);
|
||||||
|
|
||||||
|
world.set<Position>(player.entity, {10,10});
|
||||||
|
world.set<Motion>(player.entity, {0, 0});
|
||||||
|
world.set<Combat>(player.entity, {100, 10});
|
||||||
|
|
||||||
|
save::to_file("./savetest.world", world);
|
||||||
|
|
||||||
|
DinkyECS::World in_world;
|
||||||
|
save::from_file("./savetest.world", in_world);
|
||||||
|
|
||||||
|
Position &position1 = world.get<Position>(player.entity);
|
||||||
|
Position &position2 = in_world.get<Position>(player.entity);
|
||||||
|
|
||||||
|
// BUGGGGGGGG! This doesn't actually work, it's all fake
|
||||||
|
// The world uses an internal id to increment entities so
|
||||||
|
// by default player gets the first one, but all data after
|
||||||
|
// that is wrong.
|
||||||
|
REQUIRE(position1.location.x == position2.location.x);
|
||||||
|
REQUIRE(position1.location.y == position2.location.y);
|
||||||
|
}
|
1
tser.hpp
1
tser.hpp
|
@ -8,6 +8,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
|
|
||||||
namespace tser{
|
namespace tser{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue