Skip to content

Commit

Permalink
feat: shared_ptr vocation and vocation reload (#2555)
Browse files Browse the repository at this point in the history
  • Loading branch information
beats-dh authored Apr 25, 2024
1 parent 5718a4d commit bf8aa79
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 85 deletions.
1 change: 1 addition & 0 deletions data/scripts/talkactions/god/reload.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ local reloadTypes = {
["scripts"] = RELOAD_TYPE_SCRIPTS,
["stage"] = RELOAD_TYPE_CORE,
["stages"] = RELOAD_TYPE_CORE,
["vocations"] = RELOAD_TYPE_VOCATIONS,
}

local reload = TalkAction("/reload")
Expand Down
6 changes: 3 additions & 3 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Player::~Player() {
}

bool Player::setVocation(uint16_t vocId) {
Vocation* voc = g_vocations().getVocation(vocId);
const auto &voc = g_vocations().getVocation(vocId);
if (!voc) {
return false;
}
Expand Down Expand Up @@ -2395,7 +2395,7 @@ void Player::addExperience(std::shared_ptr<Creature> target, uint64_t exp, bool
++level;
// Player stats gain for vocations level <= 8
if (vocation->getId() != VOCATION_NONE && level <= 8) {
const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE);
const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE);
healthMax += noneVocation->getHPGain();
health += noneVocation->getHPGain();
manaMax += noneVocation->getManaGain();
Expand Down Expand Up @@ -2490,7 +2490,7 @@ void Player::removeExperience(uint64_t exp, bool sendText /* = false*/) {
--level;
// Player stats loss for vocations level <= 8
if (vocation->getId() != VOCATION_NONE && level <= 8) {
const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE);
const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE);
healthMax = std::max<int32_t>(0, healthMax - noneVocation->getHPGain());
manaMax = std::max<int32_t>(0, manaMax - noneVocation->getManaGain());
capacity = std::max<int32_t>(0, capacity - noneVocation->getCapGain());
Expand Down
6 changes: 3 additions & 3 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ class Player final : public Creature, public Cylinder, public Bankable {

bool isBossOnBosstiaryTracker(const std::shared_ptr<MonsterType> &monsterType) const;

Vocation* getVocation() const {
std::shared_ptr<Vocation> getVocation() const {
return vocation;
}

Expand Down Expand Up @@ -2522,7 +2522,7 @@ class Player final : public Creature, public Cylinder, public Bankable {

// Concoction system
void updateConcoction(uint16_t itemId, uint16_t timeLeft) {
if (timeLeft < 0) {
if (timeLeft == 0) {
activeConcoctions.erase(itemId);
} else {
activeConcoctions[itemId] = timeLeft;
Expand Down Expand Up @@ -2773,7 +2773,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
ProtocolGame_ptr client;
std::shared_ptr<Task> walkTask;
std::shared_ptr<Town> town;
Vocation* vocation = nullptr;
std::shared_ptr<Vocation> vocation = nullptr;
std::shared_ptr<RewardChest> rewardChest = nullptr;

uint32_t inventoryWeight = 0;
Expand Down
85 changes: 45 additions & 40 deletions src/creatures/players/vocations/vocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include "utils/pugicast.hpp"
#include "utils/tools.hpp"

bool Vocations::reload() {
vocationsMap.clear();
return loadFromXml();
}

bool Vocations::loadFromXml() {
pugi::xml_document doc;
auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/vocations.xml";
Expand All @@ -32,87 +37,87 @@ bool Vocations::loadFromXml() {

uint16_t id = pugi::cast<uint16_t>(attr.value());

auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id));
Vocation &voc = res.first->second;
auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(std::make_shared<Vocation>(id)));
auto voc = res.first->second;

if ((attr = vocationNode.attribute("name"))) {
voc.name = attr.as_string();
voc->name = attr.as_string();
}

if ((attr = vocationNode.attribute("clientid"))) {
voc.clientId = pugi::cast<uint16_t>(attr.value());
voc->clientId = pugi::cast<uint16_t>(attr.value());
}

if ((attr = vocationNode.attribute("baseid"))) {
voc.baseId = pugi::cast<uint16_t>(attr.value());
voc->baseId = pugi::cast<uint16_t>(attr.value());
}

if ((attr = vocationNode.attribute("description"))) {
voc.description = attr.as_string();
voc->description = attr.as_string();
}

if ((attr = vocationNode.attribute("magicshield"))) {
voc.magicShield = attr.as_bool();
voc->magicShield = attr.as_bool();
}

if ((attr = vocationNode.attribute("gaincap"))) {
voc.gainCap = pugi::cast<uint32_t>(attr.value()) * 100;
voc->gainCap = pugi::cast<uint32_t>(attr.value()) * 100;
}

if ((attr = vocationNode.attribute("gainhp"))) {
voc.gainHP = pugi::cast<uint32_t>(attr.value());
voc->gainHP = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainmana"))) {
voc.gainMana = pugi::cast<uint32_t>(attr.value());
voc->gainMana = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainhpticks"))) {
voc.gainHealthTicks = pugi::cast<uint32_t>(attr.value());
voc->gainHealthTicks = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainhpamount"))) {
voc.gainHealthAmount = pugi::cast<uint32_t>(attr.value());
voc->gainHealthAmount = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainmanaticks"))) {
voc.gainManaTicks = pugi::cast<uint32_t>(attr.value());
voc->gainManaTicks = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainmanaamount"))) {
voc.gainManaAmount = pugi::cast<uint32_t>(attr.value());
voc->gainManaAmount = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("manamultiplier"))) {
voc.manaMultiplier = pugi::cast<float>(attr.value());
voc->manaMultiplier = pugi::cast<float>(attr.value());
}

if ((attr = vocationNode.attribute("attackspeed"))) {
voc.attackSpeed = pugi::cast<uint32_t>(attr.value());
voc->attackSpeed = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("basespeed"))) {
voc.baseSpeed = pugi::cast<uint32_t>(attr.value());
voc->baseSpeed = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("soulmax"))) {
voc.soulMax = pugi::cast<uint16_t>(attr.value());
voc->soulMax = pugi::cast<uint16_t>(attr.value());
}

if ((attr = vocationNode.attribute("gainsoulticks"))) {
voc.gainSoulTicks = pugi::cast<uint32_t>(attr.value());
voc->gainSoulTicks = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("fromvoc"))) {
voc.fromVocation = pugi::cast<uint32_t>(attr.value());
voc->fromVocation = pugi::cast<uint32_t>(attr.value());
}

if ((attr = vocationNode.attribute("canCombat"))) {
voc.combat = attr.as_bool();
voc->combat = attr.as_bool();
}

if ((attr = vocationNode.attribute("avatarlooktype"))) {
voc.avatarLookType = pugi::cast<uint16_t>(attr.value());
voc->avatarLookType = pugi::cast<uint16_t>(attr.value());
}

for (auto childNode : vocationNode.children()) {
Expand All @@ -121,88 +126,88 @@ bool Vocations::loadFromXml() {
if (skillIdAttribute) {
uint16_t skill_id = pugi::cast<uint16_t>(skillIdAttribute.value());
if (skill_id <= SKILL_LAST) {
voc.skillMultipliers[skill_id] = pugi::cast<float>(childNode.attribute("multiplier").value());
voc->skillMultipliers[skill_id] = pugi::cast<float>(childNode.attribute("multiplier").value());
} else {
g_logger().warn("[Vocations::loadFromXml] - "
"No valid skill id: {} for vocation: {}",
skill_id, voc.id);
skill_id, voc->id);
}
} else {
g_logger().warn("[Vocations::loadFromXml] - "
"Missing skill id for vocation: {}",
voc.id);
voc->id);
}
} else if (strcasecmp(childNode.name(), "mitigation") == 0) {
pugi::xml_attribute factorAttribute = childNode.attribute("multiplier");
if (factorAttribute) {
voc.mitigationFactor = pugi::cast<float>(factorAttribute.value());
voc->mitigationFactor = pugi::cast<float>(factorAttribute.value());
}

pugi::xml_attribute primaryShieldAttribute = childNode.attribute("primaryShield");
if (primaryShieldAttribute) {
voc.mitigationPrimaryShield = pugi::cast<float>(primaryShieldAttribute.value());
voc->mitigationPrimaryShield = pugi::cast<float>(primaryShieldAttribute.value());
}

pugi::xml_attribute secondaryShieldAttribute = childNode.attribute("secondaryShield");
if (secondaryShieldAttribute) {
voc.mitigationSecondaryShield = pugi::cast<float>(secondaryShieldAttribute.value());
voc->mitigationSecondaryShield = pugi::cast<float>(secondaryShieldAttribute.value());
}
} else if (strcasecmp(childNode.name(), "formula") == 0) {
pugi::xml_attribute meleeDamageAttribute = childNode.attribute("meleeDamage");
if (meleeDamageAttribute) {
voc.meleeDamageMultiplier = pugi::cast<float>(meleeDamageAttribute.value());
voc->meleeDamageMultiplier = pugi::cast<float>(meleeDamageAttribute.value());
}

pugi::xml_attribute distDamageAttribute = childNode.attribute("distDamage");
if (distDamageAttribute) {
voc.distDamageMultiplier = pugi::cast<float>(distDamageAttribute.value());
voc->distDamageMultiplier = pugi::cast<float>(distDamageAttribute.value());
}

pugi::xml_attribute defenseAttribute = childNode.attribute("defense");
if (defenseAttribute) {
voc.defenseMultiplier = pugi::cast<float>(defenseAttribute.value());
voc->defenseMultiplier = pugi::cast<float>(defenseAttribute.value());
}

pugi::xml_attribute armorAttribute = childNode.attribute("armor");
if (armorAttribute) {
voc.armorMultiplier = pugi::cast<float>(armorAttribute.value());
voc->armorMultiplier = pugi::cast<float>(armorAttribute.value());
}
} else if (strcasecmp(childNode.name(), "pvp") == 0) {
pugi::xml_attribute pvpDamageReceivedMultiplier = childNode.attribute("damageReceivedMultiplier");
if (pvpDamageReceivedMultiplier) {
voc.pvpDamageReceivedMultiplier = pugi::cast<float>(pvpDamageReceivedMultiplier.value());
voc->pvpDamageReceivedMultiplier = pugi::cast<float>(pvpDamageReceivedMultiplier.value());
}

pugi::xml_attribute pvpDamageDealtMultiplier = childNode.attribute("damageDealtMultiplier");
if (pvpDamageDealtMultiplier) {
voc.pvpDamageDealtMultiplier = pugi::cast<float>(pvpDamageDealtMultiplier.value());
voc->pvpDamageDealtMultiplier = pugi::cast<float>(pvpDamageDealtMultiplier.value());
}
} else if (strcasecmp(childNode.name(), "gem") == 0) {
pugi::xml_attribute qualityAttr = childNode.attribute("quality");
pugi::xml_attribute nameAttr = childNode.attribute("name");
auto quality = pugi::cast<uint8_t>(qualityAttr.value());
auto name = nameAttr.as_string();
voc.wheelGems[static_cast<WheelGemQuality_t>(quality)] = name;
voc->wheelGems[static_cast<WheelGemQuality_t>(quality)] = name;
}
}
}
return true;
}

Vocation* Vocations::getVocation(uint16_t id) {
std::shared_ptr<Vocation> Vocations::getVocation(uint16_t id) {
auto it = vocationsMap.find(id);
if (it == vocationsMap.end()) {
g_logger().warn("[Vocations::getVocation] - "
"Vocation {} not found",
id);
return nullptr;
}
return &it->second;
return it->second;
}

uint16_t Vocations::getVocationId(const std::string &name) const {
for (const auto &it : vocationsMap) {
if (strcasecmp(it.second.name.c_str(), name.c_str()) == 0) {
if (strcasecmp(it.second->name.c_str(), name.c_str()) == 0) {
return it.first;
}
}
Expand All @@ -211,7 +216,7 @@ uint16_t Vocations::getVocationId(const std::string &name) const {

uint16_t Vocations::getPromotedVocation(uint16_t vocationId) const {
for (const auto &it : vocationsMap) {
if (it.second.fromVocation == vocationId && it.first != vocationId) {
if (it.second->fromVocation == vocationId && it.first != vocationId) {
return it.first;
}
}
Expand Down Expand Up @@ -292,7 +297,7 @@ std::vector<WheelGemSupremeModifier_t> Vocation::getSupremeGemModifiers() {
auto allModifiers = magic_enum::enum_entries<WheelGemSupremeModifier_t>();
g_logger().debug("Loading supreme gem modifiers for vocation: {}", vocationName);
for (const auto &[value, modifierName] : allModifiers) {
std::string targetVocation(modifierName.substr(0, modifierName.find("_")));
std::string targetVocation(modifierName.substr(0, modifierName.find('_')));
toLowerCaseString(targetVocation);
g_logger().debug("Checking supreme gem modifier: {}, targetVocation: {}", modifierName, targetVocation);
if (targetVocation == "general" || targetVocation.find(vocationName) != std::string::npos) {
Expand Down
7 changes: 4 additions & 3 deletions src/creatures/players/vocations/vocation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,17 @@ class Vocations {
}

bool loadFromXml();
bool reload();

Vocation* getVocation(uint16_t id);
const std::map<uint16_t, Vocation> &getVocations() const {
std::shared_ptr<Vocation> getVocation(uint16_t id);
const std::map<uint16_t, std::shared_ptr<Vocation>> &getVocations() const {
return vocationsMap;
}
uint16_t getVocationId(const std::string &name) const;
uint16_t getPromotedVocation(uint16_t vocationId) const;

private:
std::map<uint16_t, Vocation> vocationsMap;
std::map<uint16_t, std::shared_ptr<Vocation>> vocationsMap;
};

constexpr auto g_vocations = Vocations::getInstance;
8 changes: 8 additions & 0 deletions src/game/functions/game_reload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ bool GameReload::init(Reload_t reloadTypes) const {
return reloadScripts();
case Reload_t::RELOAD_TYPE_GROUPS:
return reloadGroups();
case Reload_t::RELOAD_TYPE_VOCATIONS:
return reloadVocations();
default:
return false;
}
Expand Down Expand Up @@ -201,3 +203,9 @@ bool GameReload::reloadGroups() const {
logReloadStatus("Groups", result);
return result;
}

bool GameReload::reloadVocations() const {
const bool result = g_vocations().reload();
logReloadStatus("Vocations", result);
return result;
}
2 changes: 2 additions & 0 deletions src/game/functions/game_reload.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum class Reload_t : uint8_t {
RELOAD_TYPE_RAIDS,
RELOAD_TYPE_SCRIPTS,
RELOAD_TYPE_GROUPS,
RELOAD_TYPE_VOCATIONS,

// Every is last
RELOAD_TYPE_LAST
Expand Down Expand Up @@ -65,6 +66,7 @@ class GameReload : public Game {
bool reloadRaids() const;
bool reloadScripts() const;
bool reloadGroups() const;
bool reloadVocations() const;
};

constexpr auto g_gameReload = GameReload::getInstance;
6 changes: 3 additions & 3 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8330,12 +8330,12 @@ std::string Game::generateVocationConditionHighscore(uint32_t vocation) {
const auto vocationsMap = g_vocations().getVocations();
for (const auto &it : vocationsMap) {
const auto &voc = it.second;
if (voc.getFromVocation() == vocation) {
if (voc->getFromVocation() == vocation) {
if (firstVocation) {
queryPart << " WHERE `vocation` = " << voc.getId();
queryPart << " WHERE `vocation` = " << voc->getId();
firstVocation = false;
} else {
queryPart << " OR `vocation` = " << voc.getId();
queryPart << " OR `vocation` = " << voc->getId();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/items/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std::
s << " (\"" << it.runeSpellName << "\"). " << (it.stackable && tmpSubType > 1 ? "They" : "It") << " can only be used by ";

const VocSpellMap &vocMap = rune->getVocMap();
std::vector<Vocation*> showVocMap;
std::vector<std::shared_ptr<Vocation>> showVocMap;

// vocations are usually listed with the unpromoted and promoted version, the latter being
// hidden from description, so `total / 2` is most likely the amount of vocations to be shown.
Expand Down
Loading

0 comments on commit bf8aa79

Please sign in to comment.