Skip to content

Commit

Permalink
[FEATURE] Outfit attributies
Browse files Browse the repository at this point in the history
Merge pull request #24 from jprzimba/outfit-attributies
  • Loading branch information
jprzimba authored Jan 20, 2025
2 parents 66495a8 + 7b52448 commit cd45766
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 37 deletions.
20 changes: 20 additions & 0 deletions data/XML/outfits.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<outfits>
<!-- Example of new attributies
<outfit type="1" looktype="128" name="Citizen" premium="no" unlocked="yes" enabled="yes" healthGain="5" healthTicks="5" manaGain="5" manaTicks="5" manaShield="no" speed="220" attackSpeed="500">
<skills>
<fist value="1" />
<club value="2" />
<axe value="3" />
<sword value="4" />
<distance value="5" />
<shielding value="6" />
<fishing value="7" />
</skills>
<stats>
<maxHealth value="5" />
<maxMana value="5" />
<soul value="100" />
<cap value="100" />
<magicLevel value="1" />
</stats>
</outfit>
-->
<!-- Female -->
<outfit type="0" looktype="136" name="Citizen" premium="no" unlocked="yes" enabled="yes" />
<outfit type="0" looktype="137" name="Hunter" premium="no" unlocked="yes" enabled="yes" />
Expand Down
20 changes: 20 additions & 0 deletions markdowns/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
# Changelog for Crystal Server


## Version 4.1.3

### Features

- Added new attributes to outfits. Check `outfits.xml` for examples. ([Tryller](https://github.com/jprzimba))
- Added Vibrancy imbuement. ([pennaor](https://github.com/pennaor))

## Added files

- notting

## Modified files

- data/XML/outfits.xml

### Bug Fixes

- notting


## Version 4.1.2

### Features
Expand Down
8 changes: 4 additions & 4 deletions src/creatures/appearance/mounts/mounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ bool Mounts::loadFromXml() {

std::shared_ptr<Mount> Mounts::getMountByID(uint8_t id) {
auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr<Mount> &mount) {
return mount->id == id; // Note the use of -> operator to access the members of the Mount object
return mount->id == id;
});

return it != mounts.end() ? *it : nullptr; // Returning the shared_ptr to the Mount object
return it != mounts.end() ? *it : nullptr;
}

std::shared_ptr<Mount> Mounts::getMountByName(const std::string &name) {
Expand All @@ -76,8 +76,8 @@ std::shared_ptr<Mount> Mounts::getMountByName(const std::string &name) {

std::shared_ptr<Mount> Mounts::getMountByClientID(uint16_t clientId) {
auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr<Mount> &mount) {
return mount->clientId == clientId; // Note the use of -> operator to access the members of the Mount object
return mount->clientId == clientId;
});

return it != mounts.end() ? *it : nullptr; // Returning the shared_ptr to the Mount object
return it != mounts.end() ? *it : nullptr;
}
229 changes: 223 additions & 6 deletions src/creatures/appearance/outfit/outfit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
////////////////////////////////////////////////////////////////////////

#include "creatures/appearance/outfit/outfit.hpp"

#include "creatures/combat/condition.hpp"
#include "creatures/creatures_definitions.hpp"
#include "config/configmanager.hpp"
#include "creatures/players/player.hpp"
#include "game/game.hpp"
Expand Down Expand Up @@ -76,17 +77,98 @@ bool Outfits::loadFromXml() {
continue;
}

outfits[type].emplace_back(std::make_shared<Outfit>(
auto outfit = std::make_shared<Outfit>(
outfitNode.attribute("name").as_string(),
pugi::cast<uint16_t>(lookTypeAttribute.value()),
outfitNode.attribute("from").as_string(),
outfitNode.attribute("premium").as_bool(),
outfitNode.attribute("unlocked").as_bool(true),
outfitNode.attribute("from").as_string()
));
pugi::cast<uint16_t>(lookTypeAttribute.value())
);

outfit->manaShield = outfitNode.attribute("manaShield").as_bool() || outfitNode.attribute("manashield").as_bool();
outfit->invisible = outfitNode.attribute("invisible").as_bool();
outfit->speed = outfitNode.attribute("speed").as_int();
outfit->attackSpeed = outfitNode.attribute("attackSpeed").as_int() || outfitNode.attribute("attackspeed").as_int();

if (auto healthGainAttr = outfitNode.attribute("healthGain")) {
outfit->healthGain = healthGainAttr.as_int();
outfit->regeneration = true;
}

if (auto healthTicksAttr = outfitNode.attribute("healthTicks")) {
outfit->healthTicks = healthTicksAttr.as_int();
outfit->regeneration = true;
}

if (auto manaGainAttr = outfitNode.attribute("manaGain")) {
outfit->manaGain = manaGainAttr.as_int();
outfit->regeneration = true;
}

if (auto manaTicksAttr = outfitNode.attribute("manaTicks")) {
outfit->manaTicks = manaTicksAttr.as_int();
outfit->regeneration = true;
}

if (auto skillsNode = outfitNode.child("skills")) {
for (auto skillNode : skillsNode.children()) {
std::string skillName = skillNode.name();
int32_t skillValue = skillNode.attribute("value").as_int();

if (skillName == "fist") {
outfit->skills[SKILL_FIST] += skillValue;
} else if (skillName == "club") {
outfit->skills[SKILL_CLUB] += skillValue;
} else if (skillName == "axe") {
outfit->skills[SKILL_AXE] += skillValue;
} else if (skillName == "sword") {
outfit->skills[SKILL_SWORD] += skillValue;
} else if (skillName == "distance" || skillName == "dist") {
outfit->skills[SKILL_DISTANCE] += skillValue;
} else if (skillName == "shielding" || skillName == "shield") {
outfit->skills[SKILL_SHIELD] = skillValue;
} else if (skillName == "fishing" || skillName == "fish") {
outfit->skills[SKILL_FISHING] += skillValue;
} else if (skillName == "melee") {
outfit->skills[SKILL_FIST] += skillValue;
outfit->skills[SKILL_CLUB] += skillValue;
outfit->skills[SKILL_SWORD] += skillValue;
outfit->skills[SKILL_AXE] += skillValue;
} else if (skillName == "weapon" || skillName == "weapons") {
outfit->skills[SKILL_CLUB] += skillValue;
outfit->skills[SKILL_SWORD] += skillValue;
outfit->skills[SKILL_AXE] += skillValue;
outfit->skills[SKILL_DISTANCE] += skillValue;
}
}

if (auto statsNode = outfitNode.child("stats")) {
for (auto statNode : statsNode.children()) {
std::string statName = statNode.name();
int32_t statValue = statNode.attribute("value").as_int();

if (statName == "maxHealth") {
outfit->stats[STAT_MAXHITPOINTS] += statValue;
} else if (statName == "maxMana") {
outfit->stats[STAT_MAXMANAPOINTS] += statValue;
} else if (statName == "soul" || statName == "soulpoints") {
outfit->stats[STAT_SOULPOINTS] += statValue;
} else if (statName == "cap" || statName == "capacity") {
outfit->stats[STAT_CAPACITY] += statValue * 100;
} else if (statName == "magLevel" || statName == "magicLevel" || statName == "ml") {
outfit->stats[STAT_MAGICPOINTS] += statValue;
}
}
}
}

outfits[type].emplace_back(outfit);
}

for (uint8_t sex = PLAYERSEX_FEMALE; sex <= PLAYERSEX_LAST; ++sex) {
outfits[sex].shrink_to_fit();
}

return true;
}

Expand Down Expand Up @@ -120,7 +202,7 @@ const std::vector<std::shared_ptr<Outfit>> &Outfits::getOutfits(PlayerSex_t sex)
return outfits[sex];
}

std::shared_ptr<Outfit> Outfits::getOutfitByName(PlayerSex_t sex, const std::string &name) const {
std::shared_ptr<Outfit> Outfits::getOutfitByName(PlayerSex_t sex, const std::string_view &name) const {
for (const auto &outfit : outfits[sex]) {
if (outfit->name == name) {
return outfit;
Expand All @@ -129,3 +211,138 @@ std::shared_ptr<Outfit> Outfits::getOutfitByName(PlayerSex_t sex, const std::str

return nullptr;
}

uint32_t Outfits::getOutfitId(PlayerSex_t sex, uint16_t lookType) const {
for (const auto &outfit : outfits[sex]) {
if (outfit->lookType == lookType) {
return outfit->lookType;
}
}

return 0;
}

bool Outfits::addAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex, uint16_t addons) {
const auto &player = g_game().getPlayerByID(playerId);
if (!player) {
return false;
}

auto &outfitsList = outfits[sex];
auto it = std::ranges::find_if(outfitsList, [&outfitId](const auto &outfit) {
return outfit->lookType == outfitId;
});

if (it == outfitsList.end()) {
return false;
}

const auto &outfit = *it;

// Apply Conditions
if (outfit->manaShield) {
const auto &condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_MANASHIELD, -1, 0);
player->addCondition(condition);
}

if (outfit->invisible) {
const auto &condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_INVISIBLE, -1, 0);
player->addCondition(condition);
}

if (outfit->speed) {
g_game().changeSpeed(player, outfit->speed);
}

if (outfit->regeneration) {
const auto &condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_REGENERATION, -1, 0);
if (outfit->healthGain) {
condition->setParam(CONDITION_PARAM_HEALTHGAIN, outfit->healthGain);
}

if (outfit->healthTicks) {
condition->setParam(CONDITION_PARAM_HEALTHTICKS, outfit->healthTicks);
}

if (outfit->manaGain) {
condition->setParam(CONDITION_PARAM_MANAGAIN, outfit->manaGain);
}

if (outfit->manaTicks) {
condition->setParam(CONDITION_PARAM_MANATICKS, outfit->manaTicks);
}

player->addCondition(condition);
}

// Apply skills
for (uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
if (outfit->skills[i]) {
player->setVarSkill(static_cast<skills_t>(i), outfit->skills[i]);
}
}

// Apply stats
for (uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) {
if (outfit->stats[s]) {
player->setVarStats(static_cast<stats_t>(s), outfit->stats[s]);
}
}

player->sendStats();
player->sendSkills();
return true;
}

bool Outfits::removeAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex) {
const auto &player = g_game().getPlayerByID(playerId);
if (!player) {
return false;
}

auto &outfitsList = outfits[sex];
auto it = std::ranges::find_if(outfitsList, [&outfitId](const auto &outfit) {
return outfit->lookType == outfitId;
});

if (it == outfitsList.end()) {
return false;
}

const auto &outfit = *it;

// Remove conditions
if (outfit->manaShield) {
player->removeCondition(CONDITION_MANASHIELD, CONDITIONID_OUTFIT);
}

if (outfit->invisible) {
player->removeCondition(CONDITION_INVISIBLE, CONDITIONID_OUTFIT);
}

if (outfit->speed) {
g_game().changeSpeed(player, -outfit->speed);
}

if (outfit->regeneration) {
player->removeCondition(CONDITION_REGENERATION, CONDITIONID_OUTFIT);
}

// Remove skills
for (uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
if (outfit->skills[i]) {
player->setVarSkill(static_cast<skills_t>(i), -outfit->skills[i]);
}
}

// Remove stats
for (uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) {
if (outfit->stats[s]) {
player->setVarStats(static_cast<stats_t>(s), -outfit->stats[s]);
}
}

player->sendStats();
player->sendSkills();
return true;
}
39 changes: 31 additions & 8 deletions src/creatures/appearance/outfit/outfit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
////////////////////////////////////////////////////////////////////////

#pragma once
#include "creatures/creatures_definitions.hpp"

enum PlayerSex_t : uint8_t;
class Player;
Expand All @@ -29,14 +30,32 @@ struct OutfitEntry {
};

struct Outfit {
Outfit(std::string initName, uint16_t initLookType, bool initPremium, bool initUnlocked, std::string initFrom) :
name(std::move(initName)), lookType(initLookType), premium(initPremium), unlocked(initUnlocked), from(std::move(initFrom)) { }
Outfit(std::string initName, std::string initFrom, bool initPremium, bool initUnlocked, uint16_t initLookType) :
name(std::move(initName)), from(std::move(initFrom)), premium(initPremium), unlocked(initUnlocked), lookType(initLookType) {
std::memset(skills, 0, sizeof(skills));
std::memset(stats, 0, sizeof(stats));
}

std::string name;
uint16_t lookType;
bool premium;
bool unlocked;
std::string from;
std::string name = "";
std::string from = "";

bool premium = false;
bool unlocked = false;
bool manaShield = false;
bool invisible = false;
bool regeneration = false;

uint16_t lookType = 0;

int32_t speed = 0;
int32_t attackSpeed = 0;
int32_t healthGain = 0;
int32_t healthTicks = 0;
int32_t manaGain = 0;
int32_t manaTicks = 0;

int32_t skills[SKILL_LAST + 1] = { 0 };
int32_t stats[STAT_LAST + 1] = { 0 };
};

struct ProtocolOutfit {
Expand All @@ -58,5 +77,9 @@ class Outfits {
[[nodiscard]] std::shared_ptr<Outfit> getOutfitByLookType(const std::shared_ptr<const Player> &player, uint16_t lookType, bool isOppositeOutfit = false) const;
[[nodiscard]] const std::vector<std::shared_ptr<Outfit>> &getOutfits(PlayerSex_t sex) const;

std::shared_ptr<Outfit> getOutfitByName(PlayerSex_t sex, const std::string &name) const;
std::shared_ptr<Outfit> getOutfitByName(PlayerSex_t sex, const std::string_view &name) const;
uint32_t getOutfitId(PlayerSex_t sex, uint16_t lookType) const;

bool addAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex, uint16_t addons);
bool removeAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex);
};
Loading

0 comments on commit cd45766

Please sign in to comment.