Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Outfit attributies #24

Merged
merged 25 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
67cec16
* Initial attributies
jprzimba Jan 17, 2025
69fbc15
Code format - (Clang-format)
github-actions[bot] Jan 17, 2025
d2f7a9c
* Modified structure of XML
jprzimba Jan 17, 2025
444432c
Merge branch 'outfit-attributies' of https://github.com/jprzimba/crys…
jprzimba Jan 17, 2025
cf0bcbf
Code format - (Clang-format)
github-actions[bot] Jan 17, 2025
411c7ea
* stats attributies
jprzimba Jan 18, 2025
fc7b440
Merge branch 'outfit-attributies' of https://github.com/jprzimba/crys…
jprzimba Jan 18, 2025
6aaaeed
Code format - (Clang-format)
github-actions[bot] Jan 18, 2025
1bc2975
Update outfit.cpp
jprzimba Jan 18, 2025
0dd0e45
Update outfit.cpp
jprzimba Jan 18, 2025
c95ca03
* More attributies to outfits stats
jprzimba Jan 18, 2025
24092ce
Code format - (Clang-format)
github-actions[bot] Jan 18, 2025
27cfb2c
* More attributies to outfits
jprzimba Jan 18, 2025
c44e998
Update outfit.cpp
jprzimba Jan 18, 2025
a6141ea
* Fixes
jprzimba Jan 18, 2025
739742a
* Accept multiples
jprzimba Jan 18, 2025
e7ce876
* Fix int
jprzimba Jan 18, 2025
0ff6dad
Merge branch 'main' into outfit-attributies
jprzimba Jan 18, 2025
1b9fb0a
* New attributies to outfits
jprzimba Jan 19, 2025
7a981ed
Code format - (Clang-format)
github-actions[bot] Jan 19, 2025
98d1b7e
* Defaults
jprzimba Jan 19, 2025
d68d8e6
Code format - (Clang-format)
github-actions[bot] Jan 19, 2025
4154a90
* Finished outfit attributies
jprzimba Jan 19, 2025
350cdcc
Merge branch 'main' into outfit-attributies
jprzimba Jan 19, 2025
7b52448
Update CHANGELOG.md
jprzimba Jan 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading