From 4fea2ae896e9ffb4eb5d545a25718c3fc245a965 Mon Sep 17 00:00:00 2001 From: Jade Date: Sun, 27 Sep 2020 02:53:03 +0100 Subject: [PATCH] Variable damage to/from mobs * Player weapons and armor ratings are taken into account when damaging/getting damaged by mobs. * Players have a 5% chance to critical strike mobs, this doubles the player's weapon power. * Aside from player and mob stat based damage variance, there is also an inherent 20% variance to any damage. --- src/ItemManager.cpp | 19 +++++++++++++++++++ src/ItemManager.hpp | 3 ++- src/MobManager.cpp | 37 +++++++++++++++++++++++++++++++------ src/MobManager.hpp | 1 + src/Player.hpp | 4 ++++ src/PlayerManager.cpp | 3 +++ src/TableData.cpp | 3 +-- 7 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index 11feb73..2a62035 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -123,6 +123,9 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { // send equip event to other players PlayerManager::sendToViewable(sock, (void*)&equipChange, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE)); + + // set equipment stats serverside + setItemStats(plr.plr); } // send response @@ -859,3 +862,19 @@ void ItemManager::checkItemExpire(CNSocket* sock, Player* player) { player->toRemoveVehicle.eIL = 0; player->toRemoveVehicle.iSlotNum = 0; } + +void ItemManager::setItemStats(Player* plr) { + + plr->pointDamage = 8 + plr->level * 2; + plr->groupDamage = 8 + plr->level * 2; + plr->defense = 16 + plr->level * 4; + + Item* itemStatsDat; + + for (int i = 0; i < 4; i++) { + itemStatsDat = ItemManager::getItemData(plr->Equip[i].iID, plr->Equip[i].iType); + plr->pointDamage += itemStatsDat->pointDamage; + plr->groupDamage += itemStatsDat->groupDamage; + plr->defense += itemStatsDat->defense; + } +} diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index b7221c3..feb96e2 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -5,7 +5,7 @@ struct Item { bool tradeable, sellable; - int buyPrice, sellPrice, stackSize, level, rarity; // TODO: implement more as needed + int buyPrice, sellPrice, stackSize, level, rarity, pointDamage, groupDamage, defense; // TODO: implement more as needed }; struct VendorListing { int sort, type, iID; @@ -49,4 +49,5 @@ namespace ItemManager { int findFreeSlot(Player *plr); Item* getItemData(int32_t id, int32_t type); void checkItemExpire(CNSocket* sock, Player* player); + void setItemStats(Player* plr); } diff --git a/src/MobManager.cpp b/src/MobManager.cpp index ad93cfc..ebabcb6 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -61,12 +61,19 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) { } Mob *mob = Mobs[pktdata[i]]; - int damage = hitMob(sock, mob, 150); + std::pair damage; + + if (pkt->iNPCCnt > 1) + damage = getDamage(plr->groupDamage, (int)mob->data["m_iProtection"], true); + else + damage = getDamage(plr->pointDamage, (int)mob->data["m_iProtection"], true); + + damage.first = hitMob(sock, mob, damage.first); respdata[i].iID = mob->appearanceData.iNPC_ID; - respdata[i].iDamage = damage; + respdata[i].iDamage = damage.first; respdata[i].iHP = mob->appearanceData.iHP; - respdata[i].iHitFlag = 2; // hitscan, not a rocket or a grenade + respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } sock->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_NPCs_SUCC, resplen); @@ -93,15 +100,16 @@ void MobManager::npcAttackPc(Mob *mob) { sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf; sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs)); - plr->HP += (int)mob->data["m_iPower"]; // already negative + auto damage = getDamage(440 + (int)mob->data["m_iPower"], plr->defense, false); + plr->HP -= damage.first; pkt->iNPC_ID = mob->appearanceData.iNPC_ID; pkt->iPCCnt = 1; atk->iID = plr->iID; - atk->iDamage = -(int)mob->data["m_iPower"]; + atk->iDamage = damage.first; atk->iHP = plr->HP; - atk->iHitFlag = 2; + atk->iHitFlag = damage.second; mob->target->sendPacket((void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen); PlayerManager::sendToViewable(mob->target, (void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen); @@ -564,3 +572,20 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { } } } + +std::pair MobManager::getDamage(int attackPower, int defensePower, bool shouldCrit) { + + std::pair ret = {}; + + int damage = attackPower * (rand() % 40 + 80) / 100; // 20% variance + ret.second = 1; + + if (shouldCrit && rand() % 20 == 0) { + damage *= 2; // critical hit + ret.second = 2; + } + + ret.first = std::max(std::min(damage, damage * damage / defensePower / 4), std::min(damage * damage / defensePower / 2, damage - defensePower)); + + return ret; +} \ No newline at end of file diff --git a/src/MobManager.hpp b/src/MobManager.hpp index 5b3e237..4e7f266 100644 --- a/src/MobManager.hpp +++ b/src/MobManager.hpp @@ -104,4 +104,5 @@ namespace MobManager { void killMob(CNSocket *sock, Mob *mob); void giveReward(CNSocket *sock); std::pair lerp(int, int, int, int, int); + std::pair getDamage(int, int, bool); } diff --git a/src/Player.hpp b/src/Player.hpp index 57c24f0..21ffe8b 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -46,6 +46,10 @@ struct Player { bool inCombat; bool dotDamage; + + int pointDamage; + int groupDamage; + int defense; int64_t aQuestFlag[16]; int tasks[ACTIVE_MISSION_COUNT]; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index bffb513..e29a69f 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -316,6 +316,9 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { addPlayer(sock, plr); //check if there is an expiring vehicle ItemManager::checkItemExpire(sock, getPlayer(sock)); + + //set player equip stats + ItemManager::setItemStats(getPlayer(sock)); } void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) { diff --git a/src/TableData.cpp b/src/TableData.cpp index 89ce7f2..2d4e4ba 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -117,8 +117,7 @@ void TableData::init() { auto item = _item.value(); int typeOverride = getItemType(i); // used for special cases where iEquipLoc doesn't indicate item type ItemManager::ItemData[std::pair(item["m_iItemNumber"], typeOverride != -1 ? typeOverride : (int)item["m_iEquipLoc"])] - = { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"], - i > 9 ? 1 : (int)item["m_iRarity"] }; + = { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"], i > 9 ? 1 : (int)item["m_iRarity"], i > 9 ? 0 : (int)item["m_iPointRat"], i > 9 ? 0 : (int)item["m_iGroupRat"], i > 9 ? 0 : (int)item["m_iDefenseRat"] }; } }