diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | backend/CMakeLists.txt | 2 | ||||
-rw-r--r-- | backend/Dungeon.cpp | 4 | ||||
-rw-r--r-- | backend/Dungeon.h | 8 | ||||
-rw-r--r-- | backend/Location.cpp | 38 | ||||
-rw-r--r-- | backend/Location.h | 36 | ||||
-rw-r--r-- | backend/LocationFactory.cpp | 4 | ||||
-rw-r--r-- | backend/LocationFactory.h | 5 | ||||
-rw-r--r-- | backend/Object.cpp | 41 | ||||
-rw-r--r-- | backend/Object.h | 13 | ||||
-rw-r--r-- | backend/util.cpp | 14 | ||||
-rw-r--r-- | backend/util.h | 6 | ||||
-rw-r--r-- | frontend/CMakeLists.txt | 3 | ||||
-rw-r--r-- | frontend/Exception.cpp | 30 | ||||
-rw-r--r-- | frontend/Exception.h | 17 | ||||
-rw-r--r-- | frontend/cmd/view.cpp | 9 | ||||
-rw-r--r-- | frontend/generate_dungeon.cpp | 55 | ||||
-rw-r--r-- | frontend/generate_dungeon.h | 8 | ||||
-rw-r--r-- | frontend/load_dungeon.cpp | 78 | ||||
-rw-r--r-- | frontend/load_dungeon.h | 8 | ||||
-rw-r--r-- | frontend/main.cpp | 32 |
21 files changed, 384 insertions, 28 deletions
@@ -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) { |