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 7e61146..991cbbb 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -7,6 +7,13 @@ #include #include +#ifndef MIN +# define MIN(A,B) ((A)<(B)?(A):(B)) +#endif +#ifndef MAX +# define MAX(A,B) ((A)>(B)?(A):(B)) +#endif + std::map MobManager::Mobs; void MobManager::init() { @@ -61,12 +68,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, (int)mob->data["m_iNpcLevel"]); + else + damage = getDamage(plr->pointDamage, (int)mob->data["m_iProtection"], true, (int)mob->data["m_iNpcLevel"]); + + 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 } resp->iBatteryW = plr->batteryW; @@ -94,15 +108,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, 36 - (int)mob->data["m_iNpcLevel"]); + 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); @@ -275,7 +290,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { // movement logic if (mob->nextMovement != 0 && currTime < mob->nextMovement) return; - mob->nextMovement = currTime + (int)mob->data["m_iDelayTime"] * 100; + mob->nextMovement = currTime + 500; int speed = mob->data["m_iRunSpeed"]; @@ -384,6 +399,11 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { void MobManager::retreatStep(Mob *mob, time_t currTime) { // distance between spawn point and current location + if (mob->nextMovement != 0 && currTime < mob->nextMovement) + return; + + mob->nextMovement = currTime + 500; + int distance = hypot(mob->appearanceData.iX - mob->spawnX, mob->appearanceData.iY - mob->spawnY); if (distance > mob->data["m_iIdleRange"]) { @@ -456,13 +476,21 @@ void MobManager::step(CNServer *serv, time_t currTime) { std::pair MobManager::lerp(int x1, int y1, int x2, int y2, int speed) { std::pair ret = {}; + speed /= 2; int distance = hypot(x1 - x2, y1 - y2); - int lerps = distance / speed; - // interpolate only the first point - float frac = 1.0f / (lerps+1); - ret.first = (x1 * (1.0f - frac)) + (x2 * frac); - ret.second = (y1 * (1.0f - frac)) + (y2 * frac); + if (distance > speed) { + int lerps = distance / speed; + + // interpolate only the first point + float frac = 1.0f / (lerps); + + ret.first = (x1 + (x2 - x1) * frac); + ret.second = (y1 + (y2 - y1) * frac); + } else { + ret.first = x2; + ret.second = y2; + } return ret; } @@ -572,3 +600,19 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { if (currTime - lastHealTime < 4000) lastHealTime = currTime; } + +std::pair MobManager::getDamage(int attackPower, int defensePower, bool shouldCrit, int crutchLevel) { + + std::pair ret = {}; + + int damage = (MAX(40, attackPower - defensePower) * (34 + crutchLevel) + MIN(attackPower, attackPower * attackPower / defensePower) * (36 - crutchLevel)) / 70; + ret.first = damage * (rand() % 40 + 80) / 100; // 20% variance + ret.second = 1; + + if (shouldCrit && rand() % 20 == 0) { + ret.first *= 2; // critical hit + ret.second = 2; + } + + return ret; +} \ No newline at end of file diff --git a/src/MobManager.hpp b/src/MobManager.hpp index 5b3e237..e8db4e1 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, int); } 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 57dec2d..ba08be6 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -322,6 +322,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"] }; } }