From 07796bea15a2d5f43766f062379b63fc9e9e1b5d Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 1 Nov 2024 22:47:42 +0100 Subject: WIP consumables --- backend/CMakeLists.txt | 3 ++ backend/ConsumableObject.cpp | 8 ++++++ backend/ConsumableObject.h | 14 +++++++++ backend/ElixirConsumableObject.cpp | 8 ++++++ backend/ElixirConsumableObject.h | 11 ++++++++ backend/ExpConsumableObject.cpp | 10 +++++++ backend/ExpConsumableObject.h | 11 ++++++++ backend/ObjectFactory.cpp | 55 +++++++++++++++++++++++------------- backend/ObjectFactory.h | 13 ++------- backend/Player.cpp | 18 ++++++++++++ backend/Player.h | 2 ++ backend/String.cpp | 12 ++++++++ backend/String.h | 6 ++-- backend/TeleportConsumableObject.cpp | 6 ++++ backend/TeleportConsumableObject.h | 11 ++++++++ frontend/GameData.cpp | 20 +++---------- frontend/cmd/go.cpp | 5 +--- frontend/cmd/use.cpp | 24 ++++++++++++++-- 18 files changed, 183 insertions(+), 54 deletions(-) create mode 100644 backend/ElixirConsumableObject.cpp create mode 100644 backend/ElixirConsumableObject.h create mode 100644 backend/ExpConsumableObject.cpp create mode 100644 backend/ExpConsumableObject.h create mode 100644 backend/TeleportConsumableObject.cpp create mode 100644 backend/TeleportConsumableObject.h diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index b7e8996..814d7e9 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -16,5 +16,8 @@ target_sources(main PUBLIC print.cpp Player.cpp Exception.cpp + ElixirConsumableObject.cpp + ExpConsumableObject.cpp + TeleportConsumableObject.cpp ) diff --git a/backend/ConsumableObject.cpp b/backend/ConsumableObject.cpp index cfede41..7a02a3c 100644 --- a/backend/ConsumableObject.cpp +++ b/backend/ConsumableObject.cpp @@ -1,3 +1,11 @@ #include "ConsumableObject.h" +void ConsumableObject::set_potency(const Range & range) { + this->potency_min = range.min; + this->potency_max = range.max; +} + +Range ConsumableObject::get_potency() const { + return { this->potency_min, this->potency_max }; +} diff --git a/backend/ConsumableObject.h b/backend/ConsumableObject.h index 3b4a2ce..0ecd073 100644 --- a/backend/ConsumableObject.h +++ b/backend/ConsumableObject.h @@ -1,9 +1,23 @@ #pragma once #include "Object.h" +#include "Range.h" + +class Player; class ConsumableObject : public Object { using Object::Object; +public: + virtual void consume(Player & player) = 0; + +public: + void set_potency(const Range & range); + Range get_potency() const; + +private: + int potency_min = 0; + int potency_max = 0; + }; diff --git a/backend/ElixirConsumableObject.cpp b/backend/ElixirConsumableObject.cpp new file mode 100644 index 0000000..145e74a --- /dev/null +++ b/backend/ElixirConsumableObject.cpp @@ -0,0 +1,8 @@ +#include "Player.h" +#include "RNG.h" +#include "ElixirConsumableObject.h" + +void ElixirConsumableObject::consume(Player & player) { + player.add_health(RNG::get().rand_int(this->get_potency())); +} + diff --git a/backend/ElixirConsumableObject.h b/backend/ElixirConsumableObject.h new file mode 100644 index 0000000..a2249c6 --- /dev/null +++ b/backend/ElixirConsumableObject.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ConsumableObject.h" + +class ElixirConsumableObject : public ConsumableObject { + using ConsumableObject::ConsumableObject; + +public: + virtual void consume(Player & player); +}; + diff --git a/backend/ExpConsumableObject.cpp b/backend/ExpConsumableObject.cpp new file mode 100644 index 0000000..77aa37e --- /dev/null +++ b/backend/ExpConsumableObject.cpp @@ -0,0 +1,10 @@ +#include "Player.h" +#include "RNG.h" +#include "ExpConsumableObject.h" + +void ExpConsumableObject::consume(Player & player) { + float min = static_cast(this->get_potency().min) / 100; + float max = static_cast(this->get_potency().max) / 100; + player.add_attack(RNG::get().rand_double(min, max)); +} + diff --git a/backend/ExpConsumableObject.h b/backend/ExpConsumableObject.h new file mode 100644 index 0000000..60ffb91 --- /dev/null +++ b/backend/ExpConsumableObject.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ConsumableObject.h" + +class ExpConsumableObject : public ConsumableObject { + using ConsumableObject::ConsumableObject; + +public: + virtual void consume(Player & player); +}; + diff --git a/backend/ObjectFactory.cpp b/backend/ObjectFactory.cpp index 1256f98..8283b06 100644 --- a/backend/ObjectFactory.cpp +++ b/backend/ObjectFactory.cpp @@ -2,34 +2,49 @@ #include "ArmorObject.h" #include "ConsumableObject.h" +#include "TeleportConsumableObject.h" +#include "ExpConsumableObject.h" +#include "ElixirConsumableObject.h" #include "GoldObject.h" #include "WeaponObject.h" #include "RNG.h" Object * ObjectFactory::create_object(const UniversalObject & data) { - switch (data.type) { - case ARMOR: { - ArmorObject * object = new ArmorObject(data.name, data.description); - object->set_protection(data.protection); - return object; - } - case CONSUMABLE: { - ConsumableObject * object = new ConsumableObject(data.name, data.description); - // TODO: read database item explanation - return object; - } - case GOLD: { - GoldObject * object = new GoldObject(data.name, data.description); - object->set_count(RNG::get().rand_int(data.min_value, data.max_value)); - return object; - } - case WEAPON: { - WeaponObject * object = new WeaponObject(data.name, data.description); - object->set_damage({ data.min_value, data.max_value }); + if (data.type == "wapenrusting") { + ArmorObject * object = new ArmorObject(data.name, data.description); + object->set_protection(data.protection); + return object; + } + + { + ConsumableObject * object = nullptr; + + if (data.type == "teleportatiedrank") + object = new TeleportConsumableObject(data.name, data.description); + else if (data.type == "ervaringsdrank") + object = new ExpConsumableObject(data.name, data.description); + else if (data.type == "levenselixer") + object = new ElixirConsumableObject(data.name, data.description); + + if (object != nullptr) { + object->set_potency(data.value); return object; } - default: break; } + + if (data.type == "goudstukken") { + GoldObject * object = new GoldObject(data.name, data.description); + object->set_count(RNG::get().rand_int(data.value)); + return object; + } + + if (data.type == "wapen") { + WeaponObject * object = new WeaponObject(data.name, data.description); + object->set_damage(data.value); + return object; + } + + // fallback return ObjectFactory::create_object(data.name, data.description); } diff --git a/backend/ObjectFactory.h b/backend/ObjectFactory.h index 440d4bd..735ac1f 100644 --- a/backend/ObjectFactory.h +++ b/backend/ObjectFactory.h @@ -1,21 +1,14 @@ #pragma once #include "Object.h" - -enum ObjectType { - ARMOR, - CONSUMABLE, - GOLD, - WEAPON, -}; +#include "Range.h" // database object table row struct UniversalObject { String name; String description; - ObjectType type; - int min_value; - int max_value; + String type; + Range value; int protection; }; diff --git a/backend/Player.cpp b/backend/Player.cpp index 2de5632..5e3e030 100644 --- a/backend/Player.cpp +++ b/backend/Player.cpp @@ -43,6 +43,7 @@ Location & Player::get_location() const { } void Player::set_location(Location & location) { this->location = &location; + lprtf("Je staat nu bij de locatie %s.\n", location.get_name().c_str()); } void Player::equip(WeaponObject * weapon) { @@ -69,3 +70,20 @@ void Player::equip(ArmorObject * armor) { lprtf("doet %s aan.\n", this->armor->get_name().c_str()); } +void Player::add_health(unsigned int bonus) { + if (this->is_dead()) return; + this->health_points += bonus; + lprtf("Je hebt %d levenspunt%s erbij gekregen.\n", bonus, bonus == 1 ? "" : "en"); +} + +void Player::add_attack(float bonus) { + float max_bonus = 0.90f - this->attack_chance; + if (max_bonus < 0.f) { + lprtf("Je aanvalskans is niet verder verhoogd.\n"); + return; + } + bonus = min(max_bonus, bonus); + this->attack_chance += bonus; + lprtf("Je aanvalskans is verhoogd met %.1f%%.\n", bonus * 100); +} + diff --git a/backend/Player.h b/backend/Player.h index 061f13c..bf9815c 100644 --- a/backend/Player.h +++ b/backend/Player.h @@ -31,7 +31,9 @@ public: public: void take_damage(unsigned int dmg); + void add_health(unsigned int bonus); float get_attack() const; + void add_attack(float bonus); unsigned get_health() const; Location & get_location() const; void set_location(Location &); diff --git a/backend/String.cpp b/backend/String.cpp index c856d86..e5512bf 100644 --- a/backend/String.cpp +++ b/backend/String.cpp @@ -26,8 +26,11 @@ String::String(String && other) { } String & String::operator = (String && other) { if (this == &other) return *this; + safe_free(this->_data); this->_data = other._data; this->_data_len = other._data_len; + other._data = nullptr; + other._data_len = 0; return *this; } @@ -90,3 +93,12 @@ bool String::empty() const { return this->_data_len == 0; } + +bool operator == (const String & a, const String & b) { + return strncmp(a._data, b._data, min(a._data_len, b._data_len)) == 0; +} + +bool operator != (const String & a, const String & b) { + return !(a == b); +} + diff --git a/backend/String.h b/backend/String.h index bca8944..4fade62 100644 --- a/backend/String.h +++ b/backend/String.h @@ -10,6 +10,8 @@ public: String(const char * data, size_t size); String(const String &); String(String &&); + String & operator = (const String &); + String & operator = (String &&); ~String(); public: static String va_fmt(va_list args, const char * fmt); @@ -22,8 +24,8 @@ public: bool empty() const; public: - String & operator = (const String &); - String & operator = (String &&); + friend bool operator == (const String &, const String &); + friend bool operator != (const String &, const String &); private: void set(const char * data); diff --git a/backend/TeleportConsumableObject.cpp b/backend/TeleportConsumableObject.cpp new file mode 100644 index 0000000..5fd4459 --- /dev/null +++ b/backend/TeleportConsumableObject.cpp @@ -0,0 +1,6 @@ +#include "TeleportConsumableObject.h" + +void TeleportConsumableObject::consume(Player & player) { + // TODO +} + diff --git a/backend/TeleportConsumableObject.h b/backend/TeleportConsumableObject.h new file mode 100644 index 0000000..7456714 --- /dev/null +++ b/backend/TeleportConsumableObject.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ConsumableObject.h" + +class TeleportConsumableObject : public ConsumableObject { + using ConsumableObject::ConsumableObject; + +public: + virtual void consume(Player & player); +}; + diff --git a/frontend/GameData.cpp b/frontend/GameData.cpp index a716a57..30816c8 100644 --- a/frontend/GameData.cpp +++ b/frontend/GameData.cpp @@ -49,20 +49,11 @@ Enemy * GameData::create_enemy(const string & name) const { } } -static const unordered_map type_map = { - { "teleportatiedrank", ObjectType::CONSUMABLE }, - { "ervaringsdrank", ObjectType::CONSUMABLE }, - { "levenselixer", ObjectType::CONSUMABLE }, - { "wapenrusting", ObjectType::ARMOR }, - { "wapen", ObjectType::WEAPON }, - { "goudstukken", ObjectType::GOLD }, -}; - Object * GameData::create_object(const string & name) const { static DBStatement query = this->db.prepare(R"( select - type, omschrijving, + type, minimumwaarde, maximumwaarde, bescherming @@ -76,14 +67,11 @@ Object * GameData::create_object(const string & name) const { try { auto row = query.row(); - string type = row.col(0); - if (!type_map.contains(type)) throw std::exception(); return ObjectFactory::create_object({ .name = name.c_str(), - .description = row.col(1, nullptr), - .type = type_map.at(type), - .min_value = row.col(2), - .max_value = row.col(3), + .description = row.col(0, nullptr), + .type = row.col(1, nullptr), + .value = { row.col(2), row.col(3) }, .protection = row.col(4), }); } catch (...) { diff --git a/frontend/cmd/go.cpp b/frontend/cmd/go.cpp index 75f6345..b8bb7e2 100644 --- a/frontend/cmd/go.cpp +++ b/frontend/cmd/go.cpp @@ -1,7 +1,6 @@ #include "backend/Location.h" #include "backend/Dungeon.h" #include "backend/Exception.h" -#include "backend/print.h" #include "../GameController.h" #include "../strings.h" @@ -30,9 +29,7 @@ void GameController::cmd_go(string & argv) { this->dungeon->update(); - if (!player.is_dead()) { + if (!player.is_dead()) player.set_location(*next_location); - lprtf("Je staat nu bij de locatie %s\n", player.get_location().get_name().c_str()); - } } diff --git a/frontend/cmd/use.cpp b/frontend/cmd/use.cpp index badab10..36824b5 100644 --- a/frontend/cmd/use.cpp +++ b/frontend/cmd/use.cpp @@ -1,8 +1,28 @@ +#include "backend/ConsumableObject.h" +#include "backend/Exception.h" +#include "backend/print.h" + #include "../GameController.h" +#include "../strings.h" using namespace std; -void GameController::cmd_use(string & argv) { - // TODO +void GameController::cmd_use(string & target_name) { + Player & player = this->dungeon->get_player(); + Location & location = player.get_location(); + for (Object * object : player.inventory) { + if (str_lower(object->get_name().c_str()) != str_lower(target_name)) continue; + + auto consumable = unique_ptr(dynamic_cast(object)); + if (consumable == nullptr) + throw Exception("%s is niet consumeerbaar", object->get_name().c_str()); + + lprtf("Je drinkt %s.\n", object->get_displayname().c_str()); + player.inventory.remove(object); + consumable->consume(player); + return; + } + + throw Exception("object \"%s\" niet gevonden", target_name.c_str()); } -- cgit v1.2.3