aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-10-29 20:01:27 +0100
committerLoek Le Blansch <loek@pipeframe.xyz>2024-10-29 20:01:27 +0100
commit9283e1eb66d6ff96b02f317e28cb6ff060953cdf (patch)
treec03d853ef620216f1c2299936004f56c6c3cee04
parent7285f9f2c2622acff734e31314f92df9b25cae16 (diff)
WIP load XML
-rw-r--r--.gitignore1
-rw-r--r--backend/CMakeLists.txt2
-rw-r--r--backend/Dungeon.cpp4
-rw-r--r--backend/Dungeon.h8
-rw-r--r--backend/Location.cpp38
-rw-r--r--backend/Location.h36
-rw-r--r--backend/LocationFactory.cpp4
-rw-r--r--backend/LocationFactory.h5
-rw-r--r--backend/Object.cpp41
-rw-r--r--backend/Object.h13
-rw-r--r--backend/util.cpp14
-rw-r--r--backend/util.h6
-rw-r--r--frontend/CMakeLists.txt3
-rw-r--r--frontend/Exception.cpp30
-rw-r--r--frontend/Exception.h17
-rw-r--r--frontend/cmd/view.cpp9
-rw-r--r--frontend/generate_dungeon.cpp55
-rw-r--r--frontend/generate_dungeon.h8
-rw-r--r--frontend/load_dungeon.cpp78
-rw-r--r--frontend/load_dungeon.h8
-rw-r--r--frontend/main.cpp32
21 files changed, 384 insertions, 28 deletions
diff --git a/.gitignore b/.gitignore
index 842e98e..2aecb0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
build
.cache
*.log
+*.db
diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt
index 6d94517..9b1e251 100644
--- a/backend/CMakeLists.txt
+++ b/backend/CMakeLists.txt
@@ -4,5 +4,7 @@ target_sources(main PUBLIC
Object.cpp
ObjectFactory.cpp
Dungeon.cpp
+ Location.cpp
+ util.cpp
)
diff --git a/backend/Dungeon.cpp b/backend/Dungeon.cpp
index 45158a6..55dccec 100644
--- a/backend/Dungeon.cpp
+++ b/backend/Dungeon.cpp
@@ -4,3 +4,7 @@ void Dungeon::update() {
}
+void Dungeon::add_location(Location * location) {
+ this->locations.push_back(location);
+}
+
diff --git a/backend/Dungeon.h b/backend/Dungeon.h
index 60faa09..88328c8 100644
--- a/backend/Dungeon.h
+++ b/backend/Dungeon.h
@@ -1,8 +1,16 @@
#pragma once
+#include "List.hpp"
+
+class Location;
+
class Dungeon {
public:
void update();
+ void add_location(Location *);
+
+private:
+ List<Location *> locations;
};
diff --git a/backend/Location.cpp b/backend/Location.cpp
new file mode 100644
index 0000000..8a79af5
--- /dev/null
+++ b/backend/Location.cpp
@@ -0,0 +1,38 @@
+#include <string.h>
+
+#include "Location.h"
+#include "util.h"
+
+Location::Location(const char * name, const char * description) {
+ this->set_name(name);
+ this->set_description(description);
+}
+
+Location::~Location() {
+ safe_free(this->name);
+ safe_free(this->description);
+}
+
+void Location::set_name(const char * name) {
+ safe_free(this->name);
+ this->name = strdup(name);
+}
+const char * Location::get_name() {
+ return this->name;
+}
+
+void Location::set_description(const char * description) {
+ safe_free(this->description);
+ this->description = strdup(description);
+}
+const char * Location::get_description() {
+ return this->description;
+}
+
+void Location::set_exit(Direction dir, Location * location) {
+ this->edges[dir] = location;
+}
+Location * Location::get_exit(Direction dir) {
+ return this->edges[dir];
+}
+
diff --git a/backend/Location.h b/backend/Location.h
index 1b23c78..6068a99 100644
--- a/backend/Location.h
+++ b/backend/Location.h
@@ -1,9 +1,41 @@
#pragma once
+#include "List.hpp"
+
+class Enemy;
+class Object;
+
+enum Direction {
+ NORTH = 0,
+ EAST = 1,
+ SOUTH = 2,
+ WEST = 3,
+};
+
class Location {
+public:
+ void set_name(const char * name);
+ const char * get_name();
+ void set_description(const char * description);
+ const char * get_description();
+ void set_exit(Direction dir, Location * location = nullptr);
+ Location * get_exit(Direction dir);
+
protected:
- Location() = default;
- virtual ~Location() = default;
+ Location(const char * name = "", const char * description = "");
+ virtual ~Location();
friend class LocationFactory;
+private:
+ const char * name = nullptr;
+ const char * description = nullptr;
+ List<Enemy*> enemies = {};
+ List<Object*> objects = {};
+
+ Location * edges[4] = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ };
};
diff --git a/backend/LocationFactory.cpp b/backend/LocationFactory.cpp
index 98a0cd8..6e0b6b8 100644
--- a/backend/LocationFactory.cpp
+++ b/backend/LocationFactory.cpp
@@ -1,6 +1,6 @@
#include "LocationFactory.h"
-Location * LocationFactory::create_location() {
- return new Location();
+Location * LocationFactory::create_location(const char * name, const char * description) {
+ return new Location(name, description);
}
diff --git a/backend/LocationFactory.h b/backend/LocationFactory.h
index e9b1bc1..a12bb0e 100644
--- a/backend/LocationFactory.h
+++ b/backend/LocationFactory.h
@@ -4,9 +4,6 @@
class LocationFactory {
public:
- static Location * create_location();
-
-private:
- LocationFactory() = delete;
+ Location * create_location(const char * name, const char * description);
};
diff --git a/backend/Object.cpp b/backend/Object.cpp
index 300e6ac..e14c780 100644
--- a/backend/Object.cpp
+++ b/backend/Object.cpp
@@ -1,15 +1,40 @@
+#include <string.h>
#include <stdlib.h>
#include "Object.h"
+#include "util.h"
+
+Object::Object(const char * name, const char * description) {
+ this->set_name(name);
+ this->set_description(description);
+}
+
Object::~Object() {
- if (this->name != nullptr) {
- free(const_cast<char *>(this->name));
- this->name = nullptr;
- }
- if (this->description != nullptr) {
- free(const_cast<char *>(this->description));
- this->description = nullptr;
- }
+ safe_free(this->name);
+ safe_free(this->description);
+}
+
+void Object::set_name(const char * name) {
+ safe_free(this->name);
+ this->name = strdup(name);
+}
+const char * Object::get_name() {
+ return this->name;
+}
+
+void Object::set_description(const char * description) {
+ safe_free(this->description);
+ this->description = strdup(description);
+}
+const char * Object::get_description() {
+ return this->description;
+}
+
+void Object::set_hidden(bool hidden) {
+ this->hidden = hidden;
+}
+bool Object::get_hidden() {
+ return this->hidden;
}
diff --git a/backend/Object.h b/backend/Object.h
index 789de25..92652c4 100644
--- a/backend/Object.h
+++ b/backend/Object.h
@@ -5,10 +5,21 @@ private:
const char * name = nullptr;
const char * description = nullptr;
+public:
+ void set_name(const char * name);
+ const char * get_name();
+ void set_description(const char * description);
+ const char * get_description();
+ void set_hidden(bool hidden);
+ bool get_hidden();
+
protected:
- Object() = default;
+ Object(const char * name = "", const char * description = "");
virtual ~Object();
friend class ObjectFactory;
+protected:
+ bool hidden = false;
+
};
diff --git a/backend/util.cpp b/backend/util.cpp
new file mode 100644
index 0000000..4a55f63
--- /dev/null
+++ b/backend/util.cpp
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+#include "util.h"
+
+void safe_free(void * & ptr) {
+ if (ptr == nullptr) return;
+ free(ptr);
+ ptr = nullptr;
+}
+void safe_free(const char * & ptr) {
+ auto x = static_cast<void *>(const_cast<char *>(ptr));
+ safe_free(x);
+}
+
diff --git a/backend/util.h b/backend/util.h
new file mode 100644
index 0000000..1889698
--- /dev/null
+++ b/backend/util.h
@@ -0,0 +1,6 @@
+#pragma once
+
+void safe_free(void * & ptr);
+void safe_free(const char * & ptr);
+
+
diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt
index ef64ee2..60b830c 100644
--- a/frontend/CMakeLists.txt
+++ b/frontend/CMakeLists.txt
@@ -4,6 +4,9 @@ target_sources(main PUBLIC
strings.cpp
print.cpp
Player.cpp
+ load_dungeon.cpp
+ generate_dungeon.cpp
+ Exception.cpp
)
add_subdirectory(cmd)
diff --git a/frontend/Exception.cpp b/frontend/Exception.cpp
new file mode 100644
index 0000000..423f4e9
--- /dev/null
+++ b/frontend/Exception.cpp
@@ -0,0 +1,30 @@
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#include "Exception.h"
+
+using namespace std;
+
+const char * Exception::what() {
+ return error.get();
+}
+
+void Exception::va_format(va_list args, const char * fmt) {
+ va_list args_copy;
+ va_copy(args_copy, args);
+ size_t sz = vsnprintf(NULL, 0, fmt, args_copy) + 1;
+ va_end(args_copy);
+
+ this->error = unique_ptr<char>(static_cast<char *>(malloc(sz)));
+
+ vsnprintf(this->error.get(), sz, fmt, args);
+}
+
+Exception::Exception(const char * fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ va_format(args, fmt);
+ va_end(args);
+}
+
diff --git a/frontend/Exception.h b/frontend/Exception.h
new file mode 100644
index 0000000..633cb4f
--- /dev/null
+++ b/frontend/Exception.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <cstdarg>
+#include <exception>
+#include <memory>
+
+class Exception : public std::exception {
+public:
+ Exception(const char * fmt, ...);
+ const char * what();
+
+protected:
+ Exception() = default;
+ void va_format(va_list args, const char * fmt);
+ std::unique_ptr<char> error = NULL;
+};
+
diff --git a/frontend/cmd/view.cpp b/frontend/cmd/view.cpp
index 8b08a2c..7998a4a 100644
--- a/frontend/cmd/view.cpp
+++ b/frontend/cmd/view.cpp
@@ -7,6 +7,15 @@ FollowupAction Player::cmd_view(Argv argv) {
return FollowupAction::NONE;
}
+ if (argv[0] == "Zelf") {
+ lprtf("Je hebt %d levenspunten.\n", this->health_points);
+ lprtf("Je hebt een aanvalskans van %.0f%%.\n", this->attack_chance * 100);
+ // TODO: weapon
+ // TODO: armor
+ // TODO: gold
+ // TODO: inventory
+ }
+
return FollowupAction::NONE;
}
diff --git a/frontend/generate_dungeon.cpp b/frontend/generate_dungeon.cpp
new file mode 100644
index 0000000..c6ddcbf
--- /dev/null
+++ b/frontend/generate_dungeon.cpp
@@ -0,0 +1,55 @@
+#include <memory>
+#include <functional>
+#include <sqlite3.h>
+
+#include "backend/Dungeon.h"
+
+#include "generate_dungeon.h"
+#include "Exception.h"
+
+using namespace std;
+
+class DB {
+ typedef unique_ptr<sqlite3, function<void(sqlite3*)>> unique_sqlite3;
+ typedef unique_ptr<sqlite3_stmt, function<void(sqlite3_stmt*)>> unique_sqlite3_stmt;
+
+public:
+ DB(const string & path) {
+ sqlite3 * db = NULL;
+ int ret = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READONLY, NULL);
+ this->db = {
+ db,
+ [] (sqlite3 * db) {
+ sqlite3_close_v2(db);
+ },
+ };
+ if (ret != SQLITE_OK)
+ throw Exception("sqlite3_open_v2");
+ }
+
+ unique_sqlite3_stmt prepare(const string & query) {
+ sqlite3_stmt * stmt = NULL;
+ int ret = sqlite3_prepare_v2(this->db.get(), query.c_str(), query.size(), &stmt, NULL);
+ unique_sqlite3_stmt uniq_stmt = {
+ stmt,
+ [] (sqlite3_stmt * stmt) {
+ sqlite3_finalize(stmt);
+ },
+ };
+ if (ret != SQLITE_OK)
+ throw Exception("sqlite3_prepare_v2");
+ return uniq_stmt;
+ }
+
+private:
+ unique_sqlite3 db = NULL;
+};
+
+unique_ptr<Dungeon> generate_dungeon() {
+ unique_ptr<Dungeon> dungeon = make_unique<Dungeon>();
+
+ DB db { "kerkersendraken.db" };
+
+ return dungeon;
+}
+
diff --git a/frontend/generate_dungeon.h b/frontend/generate_dungeon.h
new file mode 100644
index 0000000..6252c83
--- /dev/null
+++ b/frontend/generate_dungeon.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <memory>
+
+class Dungeon;
+
+std::unique_ptr<Dungeon> generate_dungeon();
+
diff --git a/frontend/load_dungeon.cpp b/frontend/load_dungeon.cpp
new file mode 100644
index 0000000..ef8cb54
--- /dev/null
+++ b/frontend/load_dungeon.cpp
@@ -0,0 +1,78 @@
+#include <memory>
+#include <filesystem>
+#include <pugixml.hpp>
+#include <map>
+
+#include "backend/Location.h"
+#include "backend/LocationFactory.h"
+#include "backend/Dungeon.h"
+
+#include "load_dungeon.h"
+#include "Exception.h"
+#include "frontend/strings.h"
+
+using namespace std;
+using namespace pugi;
+
+unique_ptr<Dungeon> load_dungeon(const string & filename) {
+ unique_ptr<Dungeon> dungeon = make_unique<Dungeon>();
+
+ xml_document doc;
+
+ string canonical = filename;
+ if (canonical.starts_with("~/"))
+ canonical = getenv("HOME") + canonical.substr(1);
+ try {
+ canonical = filesystem::canonical(canonical);
+ } catch (...) {
+ throw Exception("Kon bestand niet vinden");
+ }
+
+ xml_parse_result result = doc.load_file(canonical.c_str());
+ if (!result)
+ throw Exception("Kon XML-bestand niet lezen");
+
+ xml_node locations = doc.child("locaties");
+ if (!locations)
+ throw Exception("XML-bestand mist een <locaties> tag");
+
+ LocationFactory factory;
+ struct TempData {
+ Location * location;
+ unsigned edges[4];
+ };
+ map<unsigned, TempData> temp_map;
+ for (xml_node & tag : locations) {
+ const char * name = tag.text().as_string();
+ const char * description = tag.child("beschrijving").text().as_string();
+
+ // vector<string> objects_hidden = split_string(tag.attribute("objectenverborgen").as_string(), ";");
+ // vector<string> objects_visible = split_string(tag.attribute("objectenzichtbaar").as_string(), ";");
+ // vector<string> enemies = split_string(tag.attribute("vijand").as_string(), ";");
+
+ Location * location = factory.create_location(name, description);
+ temp_map[tag.attribute("id").as_uint()] = {
+ .location = location,
+ .edges = {
+ tag.attribute("noord").as_uint(0),
+ tag.attribute("oost").as_uint(0),
+ tag.attribute("zuid").as_uint(0),
+ tag.attribute("west").as_uint(0),
+ },
+ };
+ dungeon->add_location(location);
+ }
+
+ // connect edges after creating all locations
+ for (auto & [here, temp] : temp_map) {
+ for (Direction direction : { NORTH, EAST, SOUTH, WEST }) {
+ unsigned there = temp.edges[direction];
+ if (temp.edges[direction] == 0) continue;
+ if (!temp_map.contains(there)) continue;
+ temp.location->set_exit(direction, temp_map[there].location);
+ }
+ }
+
+ return dungeon;
+}
+
diff --git a/frontend/load_dungeon.h b/frontend/load_dungeon.h
new file mode 100644
index 0000000..4eff4cf
--- /dev/null
+++ b/frontend/load_dungeon.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <memory>
+
+class Dungeon;
+
+std::unique_ptr<Dungeon> load_dungeon(const std::string & filename);
+
diff --git a/frontend/main.cpp b/frontend/main.cpp
index b9322ee..c421d9e 100644
--- a/frontend/main.cpp
+++ b/frontend/main.cpp
@@ -3,25 +3,35 @@
#include <memory>
#include "backend/Dungeon.h"
-#include "Player.h"
+#include "Player.h"
+#include "Exception.h"
+#include "frontend/print.h"
+#include "load_dungeon.h"
+#include "generate_dungeon.h"
#include "rl.h"
#include "strings.h"
-#include "print.h"
using namespace std;
-FollowupAction game_main() {
- auto dungeon = make_unique<Dungeon>();
-
- print_string(strings::INTRO);
- string filename = rl();
- if (filename.size() == 0) {
- lprtf("TODO: generate dungeon\n");
- } else {
- lprtf("TODO: load %s\n", filename.c_str());
+static unique_ptr<Dungeon> make_dungeon() noexcept {
+ while (1) {
+ print_string(strings::INTRO);
+ string filename = rl();
+ try {
+ if (filename.size() == 0) {
+ return generate_dungeon();
+ } else {
+ return load_dungeon(filename);
+ }
+ } catch (Exception & e) {
+ lprtf("FOUT: %s\n", e.what());
+ }
}
+}
+FollowupAction game_main() {
+ unique_ptr<Dungeon> dungeon = make_dungeon();
Player player { *dungeon };
while (1) {