diff --git a/CMakeLists.txt b/CMakeLists.txt index ad3e94fe2..dfac8ce5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,7 @@ set(SOURCES src/engine/ui/cmd/do_spells.cpp src/engine/ui/cmd/do_skills.cpp src/gameplay/economics/currencies.cpp + src/gameplay/economics/currency_container.cpp src/gameplay/skills/spell_capable.cpp src/gameplay/skills/lightwalk.cpp src/gameplay/skills/death_rage.cpp @@ -633,6 +634,7 @@ set(HEADERS src/engine/ui/cmd/do_spells.h src/engine/ui/cmd/do_skills.h src/gameplay/economics/currencies.h + src/gameplay/economics/currency_container.h src/gameplay/skills/spell_capable.h src/gameplay/skills/lightwalk.h src/gameplay/skills/death_rage.h diff --git a/lib/cfg/economics/currencies.xml b/lib/cfg/economics/currencies.xml index 1f617bb85..b970f90fd 100644 --- a/lib/cfg/economics/currencies.xml +++ b/lib/cfg/economics/currencies.xml @@ -27,7 +27,7 @@ flags - флаги --> - + @@ -35,7 +35,7 @@ flags - флаги - + @@ -48,10 +48,10 @@ flags - флаги - - + + - + @@ -59,7 +59,7 @@ flags - флаги - + diff --git a/src/administration/accounts.cpp b/src/administration/accounts.cpp index b45627cb3..21907620e 100644 --- a/src/administration/accounts.cpp +++ b/src/administration/accounts.cpp @@ -14,29 +14,72 @@ std::unordered_map> accounts; #define CRYPT(a, b) ((char *) crypt((a),(b))) #endif +DQuest::DQuest() + : id(0) + , count(0) + , time(0) +{ +} + + +DQuest::DQuest(int id, int count, time_t time) + : id(id) + , count(count) + , time(time) +{ +} + +LoginIndex::LoginIndex() + : count(0) + , last_login(time(0)) +{ +} + +LoginIndex::LoginIndex(int count, time_t last_login) + : count(count) + , last_login(last_login) +{ +} + std::shared_ptr Account::get_account(const std::string &email) { if (accounts.contains(email)) { return accounts[email]; } return nullptr; } + +const std::string Account::config_parameter_daily_quest_ = "DaiQ: "; +const std::string Account::config_parameter_password_ = "Pwd: "; +const std::string Account::config_parameter_last_login_ = "ll: "; +const std::string Account::config_parameter_history_login_ = "hl: "; +const std::string Account::config_parameter_alt_currencies_ = "AltCurr: "; + +Account::Account(const std::string &email) + : email_(email) + , last_login_(0) + , currency_container_(true) +{ + read_from_file(); +} + +// TODO логика никак не относится к аккаунту +// убрать процессинг к валютам, когда они будут готовы int Account::zero_hryvn(CharData *ch, int val) { const int zone_lvl = zone_table[world[ch->in_room]->zone_rn].mob_level; - for (auto &plr : this->players_list) { - std::string name = GetNameByUnique(plr); - if (name.empty()) { + const auto &chars_in_account = all_chars_in_account(); + for (auto &plr : chars_in_account) { + if (!plr.name()) { continue; } - const auto &player = player_table[GetPtableByUnique(plr)]; - if (zone_lvl <= 12 && (player.level + player.remorts / 5 >= 20)) { + if (zone_lvl <= 12 && (plr.level + plr.remorts / 5 >= 20)) { if (ch->IsFlagged(EPrf::kTester)) { SendMsgToChar(ch, "У чара %s в расчете %d гривен, тут будет 0, левел %d морты %d обнуляем!!!\r\n", - player.name(), + plr.name(), val, - player.level, - player.remorts); + plr.level, + plr.remorts); } val = 0; } @@ -45,7 +88,7 @@ int Account::zero_hryvn(CharData *ch, int val) { } void Account::complete_quest(int id) { - for (auto &x : this->dquests) { + for (auto &x : dquests_) { if (x.id == id) { x.count += 1; x.time = time(nullptr); @@ -56,27 +99,36 @@ void Account::complete_quest(int id) { dq_tmp.id = id; dq_tmp.count = 1; dq_tmp.time = time(nullptr); - this->dquests.push_back(dq_tmp); + dquests_.push_back(dq_tmp); } -void Account::purge_erased() { - std::string uid; - for (size_t i = 0; i < this->players_list.size(); i++) { - uid = GetNameByUnique(this->players_list[i]); - if (uid.empty()) { - this->players_list.erase(this->players_list.begin() + i); +std::vector Account::all_chars_in_account() const +{ + std::vector result_list; + + const auto &player_table = GlobalObjects::player_table(); + std::copy_if(player_table.begin(), player_table.end(), std::back_inserter(result_list), + [this](const PlayerIndexElement &pie) { + if (!pie.mail) { + return false; } - } - save_to_file(); + + return email_.compare(pie.mail) == 0; + }); + + return result_list; } void Account::show_players(CharData *ch) { int count = 1; std::stringstream ss; - purge_erased(); - ss << "Данные аккаунта: " << this->email << "\r\n"; - for (auto &x : this->players_list) { - std::string name = GetNameByUnique(x); + const auto &chars_in_account = all_chars_in_account(); + ss << "Данные аккаунта: " << email_ << "\r\n"; + for (auto &x : chars_in_account) { + if (!x.name()) { + continue; + } + std::string name(x.name()); name[0] = UPPER(name[0]); ss << count << ") " << name << "\r\n"; count++; @@ -84,14 +136,33 @@ void Account::show_players(CharData *ch) { SendMsgToChar(ss.str(), ch); } +void Account::show_currencies(CharData *ch) { + std::stringstream ss; + ss << "Валюты на аккаунте: " << email_ << "\r\n"; + if (currency_container_.empty()) { + ss << " * Не обнаружено\r\n"; + } else { + int count = 1; + for (const auto &[currency_vnum, currrency_amount] : currency_container_) { + ss << " " << count << ") Vnum: " << currency_vnum << ", количество: " << currrency_amount << "\r\n"; + ++count; + } + } + + SendMsgToChar(ss.str(), ch); +} + void Account::list_players(DescriptorData *d) { int count = 1; std::stringstream ss; - purge_erased(); - ss << "Данные аккаунта: " << this->email << "\r\n"; + const auto &chars_in_account = all_chars_in_account(); + ss << "Данные аккаунта: " << email_ << "\r\n"; iosystem::write_to_output(ss.str().c_str(), d); - for (auto &x : this->players_list) { - std::string name = GetNameByUnique(x); + for (auto &x : chars_in_account) { + if (!x.name()) { + continue; + } + std::string name(x.name()); iosystem::write_to_output((std::to_string(count) + ") ").c_str(), d); name[0] = UPPER(name[0]); iosystem::write_to_output(name.c_str(), d); @@ -103,117 +174,78 @@ void Account::list_players(DescriptorData *d) { void Account::save_to_file() { std::ofstream out; - out.open(LIB_ACCOUNTS + this->email); + out.open(LIB_ACCOUNTS + email_); if (out.is_open()) { - for (const auto &x : this->dquests) { - out << "DaiQ: " << x.id << " " << x.count << " " << x.time << "\n"; + for (const auto &x : dquests_) { + out << config_parameter_daily_quest_ << x.id << " " << x.count << " " << x.time << "\n"; } - for (const auto &x : this->history_logins) { - out << "hl: " << x.first << " " << x.second.count << " " << x.second.last_login << "\n"; + for (const auto &x : history_logins_) { + out << config_parameter_history_login_ << x.first << " " << x.second.count << " " << x.second.last_login << "\n"; } - for (const auto &x : this->players_list) { - out << "p: " << x << "\n"; + if (hash_password_.has_value()) { + // пароля на аккаунте может и не быть, поэтому проверяем перед сохранением + out << config_parameter_password_ << hash_password_.value() << "\n"; + } + out << config_parameter_last_login_ << last_login_ << "\n"; + + const auto serialized_currencies = currency_container_.serialize(); + if (serialized_currencies.has_value()) { + out << config_parameter_alt_currencies_ << serialized_currencies.value(); } - out << "Pwd: " << this->hash_password << "\n"; - out << "ll: " << this->last_login << "\n"; } out.close(); } void Account::read_from_file() { std::string line; - std::ifstream in(LIB_ACCOUNTS + this->email); - std::vector tmp; + std::ifstream in(LIB_ACCOUNTS + email_); if (in.is_open()) { while (getline(in, line)) { - tmp = utils::Split(line); - if (line.starts_with("DaiQ: ")) { - DQuest tmp_quest{}; - tmp_quest.id = atoi(tmp[1].c_str()); - tmp_quest.count = atoi(tmp[2].c_str()); - tmp_quest.time = atoi(tmp[3].c_str()); - this->dquests.push_back(tmp_quest); - } -/* if (line.starts_with("hl: ")) { - utils::Split(tmp, line); - login_index tmp_li; - tmp_li.count = atoi(tmp[2].c_str()); - tmp_li.last_login = atoi(tmp[3].c_str()); - this->history_logins.insert(std::pair(tmp[1].c_str(), tmp_li)); - } - if (line.starts_with("p: ")) { - utils::Split(tmp, line); - this->add_player(atoi(tmp[1].c_str())); - } - if (line.starts_with("Pwd: ")) { - utils::Split(tmp, line); - this->hash_password = tmp[1]; + const std::vector splitted_line = utils::Split(line); + if (line.starts_with(config_parameter_daily_quest_)) { + const auto read_id = atoi(splitted_line[1].c_str()); + const auto read_count = atoi(splitted_line[2].c_str()); + const auto read_time = atoi(splitted_line[3].c_str()); + dquests_.emplace_back(read_id, read_count, read_time); + } else if (line.starts_with(config_parameter_password_)) { + hash_password_ = line.substr(config_parameter_password_.length()); + if (hash_password_->empty()) { + hash_password_ = std::nullopt; + } + } else if (line.starts_with(config_parameter_alt_currencies_)) { + currency_container_.deserealize(line.substr(config_parameter_alt_currencies_.length())); } - if (line.starts_with("ll: ")) { - utils::Split(tmp, line); - this->last_login = atoi(tmp[1].c_str()); - } -*/ } in.close(); } } -std::string Account::get_email() { - return this->email; -} -\ -void Account::add_player(long uid) { - // если уже есть, то не добавляем - for (auto &x : this->players_list) { - if (x == uid) { - return; - } - } - this->players_list.push_back(uid); -} - -void Account::remove_player(long uid) { - for (size_t i = 0; i < this->players_list.size(); i++) { - if (this->players_list[i] == uid) { - this->players_list.erase(this->players_list.begin() + i); - return; - } - } - //mudlog("Функция Account::remove_player, uid %d не был найден", uid); -} - time_t Account::get_last_login() const { - return this->last_login; + return last_login_; } void Account::set_last_login() { - this->last_login = time(nullptr); -} - -Account::Account(const std::string &email) { - this->email = email; - this->read_from_file(); - this->hash_password = ""; - this->last_login = 0; + last_login_ = time(nullptr); } void Account::add_login(const std::string &ip_addr) { - if (this->history_logins.count(ip_addr)) { - this->history_logins[ip_addr].last_login = time(nullptr); - this->history_logins[ip_addr].count += 1; + if (history_logins_.count(ip_addr)) { + history_logins_[ip_addr].last_login = time(nullptr); + history_logins_[ip_addr].count += 1; return; } - login_index tmp{}; - tmp.last_login = time(nullptr); - tmp.count = 1; - this->history_logins.insert(std::pair(ip_addr, tmp)); + + const auto current_last_login = time(nullptr); + const int current_count = 1; + const LoginIndex ll{current_count, current_last_login}; + history_logins_.insert(std::pair(ip_addr, ll)); } + /* Показ хистори логинов */ void Account::show_history_logins(CharData *ch) { std::stringstream ss; - for (auto &x : this->history_logins) { + for (auto &x : history_logins_) { ss << "IP: " << x.first << " count: " << x.second.count << " time: " << rustime(localtime(&x.second.last_login)) @@ -223,15 +255,15 @@ void Account::show_history_logins(CharData *ch) { } void Account::set_password(const std::string &password) { - this->hash_password = Password::generate_md5_hash(password); + hash_password_ = Password::generate_md5_hash(password); } bool Account::compare_password(const std::string &password) { - return CompareParam(this->hash_password, CRYPT(password.c_str(), this->hash_password.c_str()), true); + return hash_password_.has_value() ? (hash_password_.value(), CRYPT(password.c_str(), hash_password_->c_str()), true) : false; } bool Account::quest_is_available(int id) { - for (const auto &x : this->dquests) { + for (const auto &x : dquests_) { if (x.id == id) { if (x.time != 0 && (time(0) - x.time) < 82800) { return false; @@ -240,4 +272,4 @@ bool Account::quest_is_available(int id) { } } return true; -} \ No newline at end of file +} diff --git a/src/administration/accounts.h b/src/administration/accounts.h index 7ab598d22..602cdb1a1 100644 --- a/src/administration/accounts.h +++ b/src/administration/accounts.h @@ -9,6 +9,8 @@ #include "engine/structs/structs.h" #include "engine/entities/char_data.h" #include "engine/network/descriptor_data.h" +#include "engine/db/global_objects.h" +#include "gameplay/economics/currency_container.h" #include #include @@ -16,13 +18,25 @@ #include #include -struct DQuest { +class DQuest { +public: + DQuest(); + DQuest(int id, int count, time_t time); + virtual ~DQuest() = default; + +public: int id; int count; time_t time; }; -struct login_index { +class LoginIndex { +public: + LoginIndex(); + LoginIndex(int count, time_t last_login); + virtual ~LoginIndex() = default; + +public: // количество заходов int count; // дата последнего захода с данного ip @@ -30,44 +44,52 @@ struct login_index { }; class Account { - private: - std::string email; - std::vector characters; - std::vector dquests; - std::string karma; - // список чаров на мыле (только уиды) - std::vector players_list; +private: + const std::string email_; + std::vector dquests_; // пароль (а точнее его хеш) аккаунта - std::string hash_password; + std::optional hash_password_; // дата последнего входа в аккаунт - time_t last_login; + time_t last_login_; // История логинов, ключ - айпи, в структуре количество раз, с которых был произведен заход с данного айпи-адреса + дата, когда последний раз выходили с данного айпишника - std::unordered_map history_logins; + std::unordered_map history_logins_; + // хранилище альтернативных валют на аккаунте + currencies::CurrencyContainer currency_container_; + +public: + static std::shared_ptr get_account(const std::string &email); + Account(const std::string &email); + virtual ~Account() = default; - public: - void purge_erased(); - Account(const std::string &name); +public: void save_to_file(); void read_from_file(); - std::string get_email(); bool quest_is_available(int id); int zero_hryvn(CharData *ch, int val); void complete_quest(int id); - static std::shared_ptr get_account(const std::string &email); void show_players(CharData *ch); + void show_currencies(CharData *ch); void list_players(DescriptorData *d); - void add_player(long uid); - void remove_player(long uid); time_t get_last_login() const; void set_last_login(); void set_password(const std::string &password); bool compare_password(const std::string &password); void show_history_logins(CharData *ch); void add_login(const std::string &ip_addr); + +private: + std::vector all_chars_in_account() const; + +private: + static const std::string config_parameter_daily_quest_; + static const std::string config_parameter_password_; + static const std::string config_parameter_last_login_; + static const std::string config_parameter_history_login_; + static const std::string config_parameter_alt_currencies_; }; extern std::unordered_map> accounts; #endif //__ACCOUNTS_HPP__ -// vim: ts=4 sw=4 tw=0 noet syntax=cpp : \ No newline at end of file +// vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/engine/db/db.cpp b/src/engine/db/db.cpp index 6f5173652..1c7c1a7f8 100644 --- a/src/engine/db/db.cpp +++ b/src/engine/db/db.cpp @@ -3196,11 +3196,6 @@ void ActualizePlayersIndex(char *name) { log("Adding new player %s", element.name()); player_table.Append(element); - } else { - log("Delete %s from account email: %s", - GET_NAME(short_ch), - short_ch->get_account()->get_email().c_str()); - short_ch->get_account()->remove_player(short_ch->get_uid()); } } else { log("SYSERR: Failed to load player %s.", name); diff --git a/src/engine/entities/char_player.cpp b/src/engine/entities/char_player.cpp index ab585ce2d..d8659aa32 100644 --- a/src/engine/entities/char_player.cpp +++ b/src/engine/entities/char_player.cpp @@ -1096,7 +1096,6 @@ int Player::load_char_ascii(const char *name, const int load_flags) { this->account = temp_account; } // log("Add account %s player id %d name %s", GET_EMAIL(this), this->get_uid(), name); - this->account->add_player(this->get_uid()); if (load_flags & ELoadCharFlags::kReboot) { fbclose(fl); diff --git a/src/engine/ui/cmd_god/do_show.cpp b/src/engine/ui/cmd_god/do_show.cpp index beb5e14a1..1ffbbb2ca 100644 --- a/src/engine/ui/cmd_god/do_show.cpp +++ b/src/engine/ui/cmd_god/do_show.cpp @@ -895,6 +895,7 @@ void do_show(CharData *ch, char *argument, int/* cmd*/, int/* subcmd*/) { } chdata->get_account()->show_players(ch); chdata->get_account()->show_history_logins(ch); + chdata->get_account()->show_currencies(ch); break; } case 36: // shops diff --git a/src/engine/ui/interpreter.cpp b/src/engine/ui/interpreter.cpp index 06bb30e80..e1b050dc4 100644 --- a/src/engine/ui/interpreter.cpp +++ b/src/engine/ui/interpreter.cpp @@ -2594,7 +2594,6 @@ void DoAfterEmailConfirm(DescriptorData *d) { } d->character->save_char(); d->character->get_account()->set_last_login(); - d->character->get_account()->add_player(d->character->get_uid()); // добавляем в список ждущих одобрения if (!(int) NAME_FINE(d->character)) { @@ -3592,7 +3591,6 @@ void nanny(DescriptorData *d, char *argument) { iosystem::write_to_output(buffer, d); sprintf(buffer, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GetRealLevel(d->character)); mudlog(buffer, NRM, kLvlGod, SYSLOG, true); - d->character->get_account()->remove_player(d->character->get_uid()); STATE(d) = CON_CLOSE; return; } else { diff --git a/src/gameplay/economics/currencies.cpp b/src/gameplay/economics/currencies.cpp index 0acd8a520..1eab0050c 100644 --- a/src/gameplay/economics/currencies.cpp +++ b/src/gameplay/economics/currencies.cpp @@ -93,6 +93,7 @@ void CurrencyInfo::Print(CharData */*ch*/, std::ostringstream &buffer) const { << " Can be stored in bank: " << kColorGrn << (bankable_ ? "Y" : "N") << kColorNrm << "\r\n" << " Can be transfered: " << kColorGrn << (transferable_ ? "Y" : "N") << kColorNrm << "\r\n" << " Can be transfered to other account: " << kColorGrn << (transferable_to_other_ ? "Y" : "N") << kColorNrm << "\r\n" + << " Account shared: " << kColorGrn << (account_shared_ ? "Y" : "N") << kColorNrm << "\r\n" << " Transfer tax: " << kColorGrn << transfer_tax_ << kColorNrm << "\r\n" << " Drop on death: " << kColorGrn << drop_on_death_ << kColorNrm << "\r\n" << " Max clan tax: " << kColorGrn << max_clan_tax_ << kColorNrm << "\r\n" diff --git a/src/gameplay/economics/currencies.h b/src/gameplay/economics/currencies.h index e739d0b8e..190edf42a 100644 --- a/src/gameplay/economics/currencies.h +++ b/src/gameplay/economics/currencies.h @@ -13,6 +13,9 @@ #include "engine/structs/info_container.h" #include "utils/grammar/cases.h" +#include +#include + // Старый неймспейс со старыми идами валют // Его необходимо удалить после доделывания системы валют namespace currency { diff --git a/src/gameplay/economics/currency_container.cpp b/src/gameplay/economics/currency_container.cpp new file mode 100644 index 000000000..c430a010d --- /dev/null +++ b/src/gameplay/economics/currency_container.cpp @@ -0,0 +1,157 @@ +#include "currency_container.h" + +#include "engine/db/global_objects.h" +#include "gameplay/economics/currencies.h" +#include "utils/logger.h" +#include "utils/utils_string.h" + +#include + +namespace currencies { + +CurrencyContainer::CurrencyContainer(bool account_shared) + : account_shared_(account_shared) +{ +} + +void CurrencyContainer::SetCurrencyAmount(Vnum currency_vnum, int value) +{ + currency_storage_[currency_vnum] = std::clamp(value, 0, currency_amount_cap_); + Validate(); +} + +void CurrencyContainer::ModifyCurrencyAmount(Vnum currency_vnum, int value) +{ + if (!currency_storage_.contains(currency_vnum)) { + currency_storage_[currency_vnum] = value; + } else { + currency_storage_[currency_vnum] += value; + } + currency_storage_[currency_vnum] = std::clamp(currency_storage_[currency_vnum], 0, currency_amount_cap_); + Validate(); +} + +std::optional CurrencyContainer::GetCurrencyAmount(Vnum currency_vnum) const +{ + if (!currency_storage_.contains(currency_vnum)) { + return std::nullopt; + } + + return currency_storage_.at(currency_vnum); +} + +void CurrencyContainer::Validate() +{ + std::erase_if(currency_storage_, [this](const auto &element) { + const bool wrong_amount = element.second <= 0; + const auto currency = FindCurrencyByVnum(element.first); + const bool wrong_account_shared = currency.has_value() ? currency->get().IsAccountShared() != account_shared_ : true; + + const bool result = wrong_amount || wrong_account_shared; + if (result) { + err_log("CurrencyContainer::Validate. Currency text_id: %s, Wrong amount: %d, Wrong account shared: %d", + currency->get().GetTextId().c_str(), wrong_amount, wrong_account_shared); + } + + return result; + }); +} + +std::optional CurrencyContainer::serialize() +{ + Validate(); + + // формат: + // currency_text_id=value currency_text_id_2=value ... + if (currency_storage_.empty()) { + return std::nullopt; + } + + std::ostringstream ss; + + for (const auto & [currency_vnum, currency_amount] : currency_storage_) { + const auto currency = FindCurrencyByVnum(currency_vnum); + if (!currency.has_value() || currency->get().GetId() == info_container::kUndefinedVnum) { + err_log("CurrencyContainer::serialize: No text_id for currency vnum: %d", currency_vnum); + continue; + } + + if (!ss.str().empty()) { + ss << " "; + } + + ss << currency->get().GetTextId() << "=" << currency_amount; + } + + return ss.str(); +} + +void CurrencyContainer::deserealize(const std::string& data) +{ + if (data.empty()) { + return; + } + + for (const auto &data_list : utils::Split(data, ' ')) { + const auto currency_data = utils::Split(data_list, '='); + if (currency_data.size() != 2) { + err_log("CurrencyContainer::deserealize: Wrong input data: %s", data_list.c_str()); + continue; + } + const std::string &in_currency_text_id = currency_data[0]; + const std::string &in_currency_amount = currency_data[1]; + + const auto currency = FindCurrencyByTextId(in_currency_text_id); + if (!currency.has_value() || currency->get().GetId() == info_container::kUndefinedVnum) { + err_log("CurrencyContainer::deserealize: No such currency: %s", data_list.c_str()); + continue; + } + + int currency_amount = 0; + try { + currency_amount = std::stoi(in_currency_amount); + } catch (const std::invalid_argument& e) { + err_log("CurrencyContainer::deserealize: exception: %s", e.what()); + currency_amount = 0; + } catch (const std::out_of_range& e) { + err_log("CurrencyContainer::deserealize: exception: %s", e.what()); + currency_amount = 0; + } + if (currency_amount == 0) { + err_log("CurrencyContainer::deserealize: Wrong currency amount: %s", data_list.c_str()); + continue; + } + + currency_storage_[currency->get().GetId()] = currency_amount; + } + + Validate(); +} + +std::optional> CurrencyContainer::FindCurrencyByVnum(Vnum vnum) +{ + if (vnum == info_container::kUndefinedVnum) { + return std::nullopt; + } + + for (const auto ¤cy : MUD::Currencies()) { + if (currency.GetId() == vnum) { + return std::cref(currency); + } + } + + return std::nullopt; +} + +std::optional> CurrencyContainer::FindCurrencyByTextId(const std::string &text_id) +{ + for (const auto ¤cy : MUD::Currencies()) { + if (currency.GetTextId() == text_id) { + return std::cref(currency); + } + } + + return std::nullopt; +} + +} // namespace currencies diff --git a/src/gameplay/economics/currency_container.h b/src/gameplay/economics/currency_container.h new file mode 100644 index 000000000..a3ca44dbb --- /dev/null +++ b/src/gameplay/economics/currency_container.h @@ -0,0 +1,57 @@ +#pragma once + +#include "engine/structs/structs.h" + +#include +#include +#include + +namespace currencies { + +class CurrencyInfo; + +// класс для хранения альтернативных валют в аккаунте или инвентаре персонажа +class CurrencyContainer +{ +public: + // account_shared==true - контейнер для аккаунта + // account_shared==false - контейнер для персонажа + CurrencyContainer(bool account_shared); + + using StorageType = std::map; + /* + * begin()/end() возвращают константные итераторы + * для исключения возможности модификации хранилища в обход сеттеров + */ + StorageType::const_iterator begin() const {return currency_storage_.cbegin(); } + StorageType::const_iterator end() const { return currency_storage_.cend(); } + bool empty() const { return currency_storage_.empty(); } + +public: + // при установке значения в 0 или меньше - валюта удалится + void SetCurrencyAmount(Vnum currency_vnum, int value); + // value может быть отрицательным, если результат будет 0 или меньше - валюта удалится + void ModifyCurrencyAmount(Vnum currency_vnum, int value); + std::optional GetCurrencyAmount(Vnum currency_vnum) const; + + /* + * Сохранение/чтение в/из строки для записи в файл. + * Для надежности используется text_id валюты вместо внума. + */ + std::optional serialize(); + void deserealize(const std::string &data); + +private: + void Validate(); + static std::optional> FindCurrencyByVnum(Vnum vnum); + static std::optional> FindCurrencyByTextId(const std::string &text_id); + +private: + const bool account_shared_; + // ключ - vnum валюты, значение - количество + StorageType currency_storage_; + // кап потом вынести в конфиг каждой валюты. пока его нет, установить хоть какой-то тут + const int currency_amount_cap_ = 1000000; +}; + +} //namespace currencies