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

fix: display imbuement damage reductions in cyclopedia #819

Merged
Merged
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions data/items/items.xml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
<items>
<!-- Liquids -->
<!-- Fluid type 0 is same as water (id="1") -->
<item id="1" name="water" />
<item id="1" name="water" />
<item id="2" name="wine" />
<item id="3" name="beer" />
<item id="4" name="mud" />
@@ -20752,7 +20752,7 @@
</item>
<item id="11258" article="a" name="unknown item">
<attribute key="duration" value="10"/>
<attribute key="decayTo" value="11257"/>
<attribute key="decayTo" value="11257"/>
</item>
<item fromid="11259" toid="11262" article="a" name="banner"/>
<item fromid="11263" toid="11264" article="a" name="tapestry"/>
@@ -35287,7 +35287,7 @@
<attribute key="healthgain" value="3"/>
<attribute key="speed" value="30"/>
<attribute key="duration" value="3600"/>
<attribute key="transformdeequipto" value="23477"/>
<attribute key="transformdeequipto" value="23477"/>
<attribute key="decayTo" value="0"/>
<attribute key="armor" value="2"/>
<attribute key="weight" value="1500"/>
@@ -52606,7 +52606,7 @@
<attribute key="description" value="Juding by the pinching of his beak, this one seems to be born to joust"/>
<attribute key="wrapableto" value="23398"/>
</item>
<!-- Unmoveable decorative items -->
<!-- Unmoveable decorative items -->
<item id="37213" article="a" name="blue balloon"/>
<item id="37214" article="a" name="green balloon"/>
<item id="37215" article="an" name="orange balloon"/>
@@ -52664,7 +52664,7 @@
<item id="37297" article="a" name="blue Tibia carpet"/>
<item id="37298" article="a" name="purple Tibia carpet"/>
<item id="37299" article="a" name="pink Tibia carpet"/>
<!-- End of Unmoveable Decorative items-->
<!-- End of Unmoveable Decorative items-->
<item id="37303" article="a" name="half empty picture album">
<attribute key="description" value="Eight pictures are missing. Under the first missing one you read: &quot;Banner of the Falcon Order&quot;" />
<attribute key="weight" value="500"/>
81 changes: 76 additions & 5 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
@@ -486,6 +486,7 @@ void Player::updateInventoryImbuement()
for (auto item : getAllInventoryItems())
{
// Iterate through all imbuement slots on the item

for (uint8_t slotid = 0; slotid < item->getImbuementSlot(); slotid++)
{
ImbuementInfo imbuementInfo;
@@ -1375,7 +1376,7 @@ void Player::sendMarketEnter(uint32_t depotId)
if (!client || this->getLastDepotId() == -1 || !depotId) {
return;
}

client->sendMarketEnter(depotId);
}

@@ -2501,7 +2502,7 @@ void Player::death(Creature* lastHitCreature)
}
}
bool pvpDeath = false;
if(playerDmg > 0 || othersDmg > 0){
if (playerDmg > 0 || othersDmg > 0){
pvpDeath = (Player::lastHitIsPlayer(lastHitCreature) || playerDmg / (playerDmg + static_cast<double>(othersDmg)) >= 0.05);
}
if (pvpDeath && sumLevels > level) {
@@ -3575,7 +3576,7 @@ void Player::stashContainer(StashContainerList itemDict)
}

for (auto it : stashItems) {
if(!stashItemDict[it.first]) {
if (!stashItemDict[it.first]) {
stashItemDict[it.first] = it.second;
} else {
stashItemDict[it.first] += it.second;
@@ -3731,6 +3732,76 @@ std::vector<Item*> Player::getInventoryItemsFromId(uint16_t itemId, bool ignore
return itemVector;
}

std::array<double_t, COMBAT_COUNT> Player::getFinalDamageReduction() const
{
std::array<double_t, COMBAT_COUNT> combatReductionArray;
combatReductionArray.fill(0);
calculateDamageReductionFromEquipedItems(combatReductionArray);
for (int combatTypeIndex = 0; combatTypeIndex < COMBAT_COUNT; combatTypeIndex++) {
combatReductionArray[combatTypeIndex] = std::clamp<double_t>(
std::floor(combatReductionArray[combatTypeIndex]),
-100.,
100.
);
}
return combatReductionArray;
}

void Player::calculateDamageReductionFromEquipedItems(std::array<double_t, COMBAT_COUNT> &combatReductionArray) const
{
for (uint8_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
Item *item = inventory[slot];
if (item) {
calculateDamageReductionFromItem(combatReductionArray, item);
}
}
}

void Player::calculateDamageReductionFromItem(std::array<double_t, COMBAT_COUNT> &combatReductionArray, Item *item) const
{
for (uint16_t combatTypeIndex = 0; combatTypeIndex < COMBAT_COUNT; combatTypeIndex++) {
updateDamageReductionFromItemImbuement(combatReductionArray, item, combatTypeIndex);
updateDamageReductionFromItemAbility(combatReductionArray, item, combatTypeIndex);
}
}

void Player::updateDamageReductionFromItemImbuement(
std::array<double_t, COMBAT_COUNT> &combatReductionArray, Item *item, uint16_t combatTypeIndex
) const
{
for (uint8_t imbueSlotId = 0; imbueSlotId < item->getImbuementSlot(); imbueSlotId++) {
ImbuementInfo imbuementInfo;
if (item->getImbuementInfo(imbueSlotId, &imbuementInfo) && imbuementInfo.imbuement) {
int16_t imbuementAbsorption = imbuementInfo.imbuement->absorbPercent[combatTypeIndex];
if (imbuementAbsorption != 0) {
combatReductionArray[combatTypeIndex] = calculateDamageReduction(combatReductionArray[combatTypeIndex], imbuementAbsorption);
}
}
}
}

void Player::updateDamageReductionFromItemAbility(
std::array<double_t, COMBAT_COUNT> &combatReductionArray, const Item *item, uint16_t combatTypeIndex
) const
{
if (!item) {
return;
}

const ItemType &itemType = Item::items[item->getID()];
if (itemType.abilities) {
int16_t elementReduction = itemType.abilities->absorbPercent[combatTypeIndex];
if (elementReduction != 0) {
combatReductionArray[combatTypeIndex] = calculateDamageReduction(combatReductionArray[combatTypeIndex], elementReduction);
}
}
}

double_t Player::calculateDamageReduction(double_t currentTotal, int16_t resistance) const
{
return (100 - currentTotal) / 100.0 * resistance + currentTotal;
}

std::vector<Item*> Player::getAllInventoryItems(bool ignoreEquiped /*= false*/) const
{
std::vector<Item*> itemVector;
@@ -4068,7 +4139,7 @@ void Player::doAttacking(uint32_t)
} else {
result = weapon->useWeapon(this, tool, attackedCreature);
}
} else if(hasWeaponDistanceEquipped()) {
} else if (hasWeaponDistanceEquipped()) {
return;
} else {
result = Weapon::useFist(this, attackedCreature);
@@ -5987,7 +6058,7 @@ std::string Player::getBlessingsName() const
std::ostringstream os;
for (uint8_t i = 1; i <= 8; i++) {
if (hasBlessing(i)) {
if (auto blessName = BlessingNames.find(static_cast<Blessings_t>(i));
if (auto blessName = BlessingNames.find(static_cast<Blessings_t>(i));
blessName != BlessingNames.end()) {
os << (*blessName).second;
} else {
16 changes: 13 additions & 3 deletions src/creatures/players/player.h
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ struct ForgeHistory {
bool tierLoss = false;
bool successCore = false;
bool tierCore = false;

std::string description;
std::string firstItemName;
std::string secondItemName;
@@ -382,7 +382,7 @@ class Player final : public Creature, public Cylinder
uint8_t getBlessingCount(uint8_t index) const {
return blessings[index - 1];
}
std::string getBlessingsName() const;
std::string getBlessingsName() const;

bool isOffline() const {
return (getID() == 0);
@@ -2085,7 +2085,7 @@ class Player final : public Creature, public Cylinder
void forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId);
void forgeResourceConversion(uint8_t action);
void forgeHistory(uint8_t page) const;

void sendOpenForge() const
{
if (client)
@@ -2181,6 +2181,8 @@ class Player final : public Creature, public Cylinder

void registerForgeHistoryDescription(ForgeHistory history);

std::map<uint16_t, Item*> getEquippedItemsWithEnabledAbilitiesBySlot() const;

private:
std::forward_list<Condition*> getMuteConditions() const;

@@ -2528,6 +2530,14 @@ class Player final : public Creature, public Cylinder
bool hasWeaponDistanceEquipped() const;

Item* getQuiverAmmoOfType(const ItemType &it) const;

std::array<double_t, COMBAT_COUNT> getFinalDamageReduction() const;
void calculateDamageReductionFromEquipedItems(std::array<double_t, COMBAT_COUNT> &combatReductionMap) const;
void calculateDamageReductionFromItem(std::array<double_t, COMBAT_COUNT> &combatReductionMap, Item *item) const;
void updateDamageReductionFromItemImbuement(std::array<double_t, COMBAT_COUNT> &combatReductionMap, Item *item, uint16_t combatTypeIndex) const;
void updateDamageReductionFromItemAbility(std::array<double_t, COMBAT_COUNT> &combatReductionMap, const Item *item, uint16_t combatTypeIndex) const;
double_t calculateDamageReduction(double_t currentTotal, int16_t resistance) const;
};


#endif // SRC_CREATURES_PLAYERS_PLAYER_H_
68 changes: 12 additions & 56 deletions src/server/network/protocol/protocolgame.cpp
Original file line number Diff line number Diff line change
@@ -158,7 +158,7 @@ void ProtocolGame::AddItem(NetworkMessage &msg, const Item *item)
}
else if (it.isSplash() || it.isFluidContainer())
{
msg.addByte(static_cast<uint8_t>(item->getFluidType()));
msg.addByte(static_cast<uint8_t>(item->getFluidType()));
}
else if (it.isContainer())
{
@@ -3185,58 +3185,14 @@ void ProtocolGame::sendCyclopediaCharacterCombatStats()
auto startCombats = msg.getBufferPosition();
msg.skipBytes(1);

alignas(16) int16_t absorbs[COMBAT_COUNT] = {};
for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot)
{
if (!player->isItemAbilityEnabled(static_cast<Slots_t>(slot)))
{
continue;
}

Item *item = player->getInventoryItem(static_cast<Slots_t>(slot));
if (!item)
{
continue;
}

const ItemType &it = Item::items[item->getID()];
if (!it.abilities)
{
continue;
}

if (COMBAT_COUNT == 12)
{
absorbs[0] += it.abilities->absorbPercent[0];
absorbs[1] += it.abilities->absorbPercent[1];
absorbs[2] += it.abilities->absorbPercent[2];
absorbs[3] += it.abilities->absorbPercent[3];
absorbs[4] += it.abilities->absorbPercent[4];
absorbs[5] += it.abilities->absorbPercent[5];
absorbs[6] += it.abilities->absorbPercent[6];
absorbs[7] += it.abilities->absorbPercent[7];
absorbs[8] += it.abilities->absorbPercent[8];
absorbs[9] += it.abilities->absorbPercent[9];
absorbs[10] += it.abilities->absorbPercent[10];
absorbs[11] += it.abilities->absorbPercent[11];
}
else
{
for (size_t i = 0; i < COMBAT_COUNT; ++i)
{
absorbs[i] += it.abilities->absorbPercent[i];
}
}
}

const std::array<double_t, COMBAT_COUNT> &damageReduction = player->getFinalDamageReduction();
static const Cipbia_Elementals_t cipbiaCombats[] = {CIPBIA_ELEMENTAL_PHYSICAL, CIPBIA_ELEMENTAL_ENERGY, CIPBIA_ELEMENTAL_EARTH, CIPBIA_ELEMENTAL_FIRE, CIPBIA_ELEMENTAL_UNDEFINED,
CIPBIA_ELEMENTAL_LIFEDRAIN, CIPBIA_ELEMENTAL_UNDEFINED, CIPBIA_ELEMENTAL_HEALING, CIPBIA_ELEMENTAL_DROWN, CIPBIA_ELEMENTAL_ICE, CIPBIA_ELEMENTAL_HOLY, CIPBIA_ELEMENTAL_DEATH};
for (size_t i = 0; i < COMBAT_COUNT; ++i)
{
if (absorbs[i] != 0)
{
for (size_t i = 0; i < COMBAT_COUNT; ++i) {
auto finalDamage = std::clamp<uint8_t>(static_cast<uint8_t>(damageReduction[i]), 0, 255);
if (finalDamage != 0) {
msg.addByte(cipbiaCombats[i]);
msg.addByte(std::max<int16_t>(-100, std::min<int16_t>(100, absorbs[i])));
msg.addByte(finalDamage);
++combats;
}
}
@@ -4574,7 +4530,7 @@ void ProtocolGame::sendForgeHistory(uint8_t page)
msg.addByte((history.bonus >= 1 && history.bonus < 8) ? 0x01 : 0x00);
}
}

writeToOutputBuffer(msg);
}

@@ -6225,7 +6181,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot)
msg.addByte(player->isPremium() ? 0x01 : 0x00);
} else if (slot->state == PreyDataState_Inactive) {
// Empty
} else if (slot->state == PreyDataState_Active) {
} else if (slot->state == PreyDataState_Active) {
if (const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(slot->selectedRaceId)) {
msg.addString(mtype->name);
const Outfit_t outfit = mtype->info.outfit;
@@ -7329,7 +7285,7 @@ void ProtocolGame::parseDepotSearchItemRequest(NetworkMessage &msg)
if (Item::items[itemId].upgradeClassification > 0) {
itemTier = msg.getByte();
}

addGameTask(&Game::playerRequestDepotSearchItem, player->getID(), itemId, itemTier);
}

@@ -7341,7 +7297,7 @@ void ProtocolGame::parseRetrieveDepotSearch(NetworkMessage &msg)
itemTier = msg.getByte();
}
uint8_t type = msg.getByte();

addGameTask(&Game::playerRequestDepotSearchRetrieve, player->getID(), itemId, itemTier, type);
}

@@ -7355,7 +7311,7 @@ void ProtocolGame::parseOpenParentContainer(NetworkMessage &msg)
void ProtocolGame::sendUpdateCreature(const Creature* creature)
{
if (!creature || !player) {
return;
return;
}

if (!canSee(creature))
@@ -7366,7 +7322,7 @@ void ProtocolGame::sendUpdateCreature(const Creature* creature)
return;
}

NetworkMessage msg;
NetworkMessage msg;
msg.addByte(0x6B);
msg.addPosition(creature->getPosition());
msg.addByte(static_cast<uint8_t>(stackPos));