#include #include "backend/Enemy.h" #include "backend/EnemyFactory.h" #include "backend/Exception.h" #include "backend/LocationFactory.h" #include "backend/Object.h" #include "backend/ObjectFactory.h" #include "backend/RNG.h" #include "backend/Range.h" #include "backend/print.h" #include "GameData.h" using namespace std; GameData & GameData::get_instance() { static GameData instance; return instance; } Enemy * GameData::create_enemy(const string & name) const { static DBStatement query = this->db.prepare(R"( select naam, omschrijving, minimumobjecten, maximumobjecten, levenspunten, aanvalskans, minimumschade, maximumschade from Vijanden where lower(naam) = lower(?) limit 1 )"); query.reset() .bind(name) ; try { auto row = query.row(); auto enemy = unique_ptr{ EnemyFactory::create_enemy(row.col(0), row.col(1)) }; int object_count = RNG::get().rand_int(Range { row.col(2), row.col(3) }); for (const string & name : this->random_objects(object_count)) enemy->add_hidden_object(this->create_object(name)); enemy->set_health(row.col(4)); enemy->set_attack(static_cast(row.col(5)) / 100); enemy->set_damage({ row.col(6), row.col(7) }); return enemy.release(); } catch (Exception & e) { printf("Fout bij aanmaken van vijand: %s\n", e.what()); return EnemyFactory::create_enemy(name.c_str()); } } Object * GameData::create_object(const string & name) const { static DBStatement query = this->db.prepare(R"( select omschrijving, type, minimumwaarde, maximumwaarde, bescherming from Objecten where lower(naam) = lower(?) limit 1 )"); query.reset() .bind(name) ; try { auto row = query.row(); return ObjectFactory::create_object({ .name = name.c_str(), .description = row.col(0, nullptr), .type = row.col(1, nullptr), .value = { row.col(2), row.col(3) }, .protection = row.col(4), }); } catch (...) { return ObjectFactory::create_object(name.c_str()); } } Location * GameData::create_location(const string & name) const{ static DBStatement query = this->db.prepare(R"( select naam, beschrijving from Locaties where lower(naam) = lower(?) limit 1 )"); query.reset() .bind(name) ; try { auto row = query.row(); return LocationFactory::create_location(row.col(0), row.col(1)); } catch (...) { return LocationFactory::create_location(name.c_str()); } } void GameData::leaderbord_add(const string & name, unsigned int gold) { static DBStatement stmt = this->db.prepare(R"( insert into Leaderboard (naam, goudstukken) values (?, ?) )"); stmt.reset() .bind(name) .bind(gold) ; stmt.execute(); } void GameData::leaderbord_print() const { static DBStatement query = this->db.prepare(R"( select naam, goudstukken from Leaderboard order by goudstukken desc limit 10 )"); query.reset(); printf("\033[1;4m"); lprtf("%3s %-20s %4s", "#", "Naam", "Goud"); printf("\033[0m"); lprtf("\n"); unsigned int i = 1; for (DBQueryRow & row : query.rows()) { lprtf("%3d %-20s %4d\n", i++, row.col(0), row.col(1)); } } vector GameData::random_names(const string & table, unsigned count) const { if (count == 0) return {}; try { // NOTE: Parameter placeholders cannot be used for database identifiers // (i.e. the table name in this case), which makes this function vulnerable // to SQL injection if the table argument contains user-controllable data. String query_str = String::fmt("select naam from %s order by random() limit ?", table.c_str()); static DBStatement query = this->db.prepare(query_str.c_str()); query.reset() .bind(count) ; vector names = {}; for (DBQueryRow & row : query.rows()) { names.push_back(row.col(0)); } return names; } catch (Exception & e) { throw Exception("genereren van %d willekeurige namen uit tabel %s: %s", count, table.c_str(), e.what()); } } vector GameData::random_locations(unsigned count) const { return this->random_names("Locaties", count); } vector GameData::random_objects(unsigned count) const { return this->random_names("Objecten", count); } vector GameData::random_enemies(unsigned count) const { return this->random_names("Vijanden", count); }