From 610a683804e542e292de34a51ed8b42faca685f6 Mon Sep 17 00:00:00 2001 From: dongresource Date: Wed, 17 Mar 2021 22:28:24 +0100 Subject: [PATCH] [refactor] E g g s --- Makefile | 2 + src/Abilities.cpp | 3 +- src/Chunking.cpp | 5 +- src/CustomCommands.cpp | 7 +- src/Eggs.cpp | 263 +++++++++++++++++++++++++++++++++++++++++ src/Eggs.hpp | 35 ++++++ src/Items.cpp | 3 +- src/NPCManager.cpp | 254 +-------------------------------------- src/NPCManager.hpp | 26 ---- src/PlayerManager.cpp | 7 +- src/TableData.cpp | 11 +- 11 files changed, 324 insertions(+), 292 deletions(-) create mode 100644 src/Eggs.cpp create mode 100644 src/Eggs.hpp diff --git a/Makefile b/Makefile index 58578a8..f6c9dd4 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,7 @@ CXXSRC=\ src/Chat.cpp\ src/CustomCommands.cpp\ src/Email.cpp\ + src/Eggs.cpp\ src/main.cpp\ src/Missions.cpp\ src/MobAI.cpp\ @@ -92,6 +93,7 @@ CXXHDR=\ src/Chat.hpp\ src/CustomCommands.hpp\ src/Email.hpp\ + src/Eggs.hpp\ src/Missions.hpp\ src/MobAI.hpp\ src/Combat.hpp\ diff --git a/src/Abilities.cpp b/src/Abilities.cpp index bfc796c..cc6d1e2 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -4,6 +4,7 @@ #include "NPCManager.hpp" #include "Nanos.hpp" #include "Groups.hpp" +#include "Eggs.hpp" /* * TODO: This file is in desperate need of deduplication and rewriting. @@ -521,7 +522,7 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in respdata[i].bProtected = 0; std::pair key = std::make_pair(sock, bitFlag); time_t until = getTime() + (time_t)duration * 100; - NPCManager::EggBuffs[key] = until; + Eggs::EggBuffs[key] = until; } respdata[i].iConditionBitFlag = plr->iConditionBitFlag; diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 1fa85ea..c9a5563 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -3,6 +3,7 @@ #include "NPCManager.hpp" #include "settings.hpp" #include "Combat.hpp" +#include "Eggs.hpp" using namespace Chunking; @@ -113,7 +114,7 @@ void Chunking::addPlayerToChunks(std::set chnks, CNSocket* sock) { break; case NPC_EGG: INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); - NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); + Eggs::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER)); break; default: @@ -183,7 +184,7 @@ void Chunking::addNPCToChunks(std::set chnks, int32_t id) { break; case NPC_EGG: INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); - NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); + Eggs::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); for (Chunk* chunk : chnks) { for (CNSocket* sock : chunk->players) { diff --git a/src/CustomCommands.cpp b/src/CustomCommands.cpp index 9b09e1a..dd868d8 100644 --- a/src/CustomCommands.cpp +++ b/src/CustomCommands.cpp @@ -3,6 +3,7 @@ #include "PlayerManager.hpp" #include "TableData.hpp" #include "NPCManager.hpp" +#include "Eggs.hpp" #include "MobAI.hpp" #include "Items.hpp" #include "db/Database.hpp" @@ -485,7 +486,7 @@ static void buffCommand(std::string full, std::vector& args, CNSock if (*tmp) return; - if (NPCManager::eggBuffPlayer(sock, skillId, 0, duration)<0) + if (Eggs::eggBuffPlayer(sock, skillId, 0, duration)<0) Chat::sendServerMessage(sock, "/buff: unknown skill Id"); } @@ -501,7 +502,7 @@ static void eggCommand(std::string full, std::vector& args, CNSocke if (*tmp) return; - if (NPCManager::EggTypes.find(eggType) == NPCManager::EggTypes.end()) { + if (Eggs::EggTypes.find(eggType) == Eggs::EggTypes.end()) { Chat::sendServerMessage(sock, "/egg: Unknown egg type"); return; } @@ -518,7 +519,7 @@ static void eggCommand(std::string full, std::vector& args, CNSocke Egg* egg = new Egg(plr->x + addX, plr->y + addY, plr->z, plr->instanceID, eggType, id, false); // change last arg to true after gruntwork NPCManager::NPCs[id] = egg; - NPCManager::Eggs[id] = egg; + Eggs::Eggs[id] = egg; NPCManager::updateNPCPosition(id, plr->x + addX, plr->y + addY, plr->z, plr->instanceID, plr->angle); // add to template diff --git a/src/Eggs.cpp b/src/Eggs.cpp new file mode 100644 index 0000000..67a9266 --- /dev/null +++ b/src/Eggs.cpp @@ -0,0 +1,263 @@ +#include "core/Core.hpp" +#include "Eggs.hpp" +#include "PlayerManager.hpp" +#include "Items.hpp" +#include "Nanos.hpp" +#include "Abilities.hpp" +#include "Groups.hpp" + +#include + +using namespace Eggs; + +/// sock, CBFlag -> until +std::map, time_t> Eggs::EggBuffs; +std::unordered_map Eggs::EggTypes; +std::unordered_map Eggs::Eggs; + +int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) { + Player* plr = PlayerManager::getPlayer(sock); + Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); + + int bitFlag = Groups::getGroupFlags(otherPlr); + int CBFlag = Nanos::applyBuff(sock, skillId, 1, 3, bitFlag); + + size_t resplen; + + if (skillId == 183) { + resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Damage); + } else if (skillId == 150) { + resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP); + } else { + resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Buff); + } + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + // we know it's only one trailing struct, so we can skip full validation + + uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; + sP_FE2CL_NPC_SKILL_HIT* skillUse = (sP_FE2CL_NPC_SKILL_HIT*)respbuf; + + if (skillId == 183) { // damage egg + sSkillResult_Damage* skill = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); + memset(respbuf, 0, resplen); + skill->eCT = 1; + skill->iID = plr->iID; + skill->iDamage = PC_MAXHEALTH(plr->level) * Nanos::SkillTable[skillId].powerIntensity[0] / 1000; + plr->HP -= skill->iDamage; + if (plr->HP < 0) + plr->HP = 0; + skill->iHP = plr->HP; + } else if (skillId == 150) { // heal egg + sSkillResult_Heal_HP* skill = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); + memset(respbuf, 0, resplen); + skill->eCT = 1; + skill->iID = plr->iID; + skill->iHealHP = PC_MAXHEALTH(plr->level) * Nanos::SkillTable[skillId].powerIntensity[0] / 1000; + plr->HP += skill->iHealHP; + if (plr->HP > PC_MAXHEALTH(plr->level)) + plr->HP = PC_MAXHEALTH(plr->level); + skill->iHP = plr->HP; + } else { // regular buff egg + sSkillResult_Buff* skill = (sSkillResult_Buff*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); + memset(respbuf, 0, resplen); + skill->eCT = 1; + skill->iID = plr->iID; + skill->iConditionBitFlag = plr->iConditionBitFlag; + } + + skillUse->iNPC_ID = eggId; + skillUse->iSkillID = skillId; + skillUse->eST = Nanos::SkillTable[skillId].skillType; + skillUse->iTargetCnt = 1; + + sock->sendPacket((void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen); + PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen); + + if (CBFlag == 0) + return -1; + + std::pair key = std::make_pair(sock, CBFlag); + + // save the buff serverside; + // if you get the same buff again, new duration will override the previous one + time_t until = getTime() + (time_t)duration * 1000; + EggBuffs[key] = until; + + return 0; +} + +static void eggStep(CNServer* serv, time_t currTime) { + // tick buffs + time_t timeStamp = currTime; + auto it = EggBuffs.begin(); + while (it != EggBuffs.end()) { + // check remaining time + if (it->second > timeStamp) + it++; + else { // if time reached 0 + CNSocket* sock = it->first.first; + int32_t CBFlag = it->first.second; + Player* plr = PlayerManager::getPlayer(sock); + Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); + + int groupFlags = Groups::getGroupFlags(otherPlr); + for (auto& pwr : Nanos::NanoPowers) { + if (pwr.bitFlag == CBFlag) { // pick the power with the right flag and unbuff + INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); + resp.eCSTB = pwr.timeBuffID; + resp.eTBU = 2; + resp.eTBT = 3; // for egg buffs + plr->iConditionBitFlag &= ~CBFlag; + resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; + sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); + + INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, resp2); // send a buff timeout to other players + resp2.eCT = 1; + resp2.iID = plr->iID; + resp2.iConditionBitFlag = plr->iConditionBitFlag; + PlayerManager::sendToViewable(sock, (void*)&resp2, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); + } + } + // remove buff from the map + it = EggBuffs.erase(it); + } + } + + // check dead eggs and eggs in inactive chunks + for (auto egg : Eggs::Eggs) { + if (!egg.second->dead || !Chunking::inPopulatedChunks(egg.second->viewableChunks)) + continue; + if (egg.second->deadUntil <= timeStamp) { + // respawn it + egg.second->dead = false; + egg.second->deadUntil = 0; + egg.second->appearanceData.iHP = 400; + + Chunking::addNPCToChunks(Chunking::getViewableChunks(egg.second->chunkPos), egg.first); + } + } + +} + +void Eggs::npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg) { + egg->iX = npc->iX; + egg->iY = npc->iY; + egg->iZ = npc->iZ; + // client doesn't care about egg->iMapNum + egg->iShinyType = npc->iNPCType; + egg->iShiny_ID = npc->iNPC_ID; +} + +static void eggPickup(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_SHINY_PICKUP)) + return; // malformed packet + + sP_CL2FE_REQ_SHINY_PICKUP* pickup = (sP_CL2FE_REQ_SHINY_PICKUP*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + int eggId = pickup->iShinyID; + + if (Eggs::Eggs.find(eggId) == Eggs::Eggs.end()) { + std::cout << "[WARN] Player tried to open non existing egg?!" << std::endl; + return; + } + Egg* egg = Eggs::Eggs[eggId]; + + if (egg->dead) { + std::cout << "[WARN] Player tried to open a dead egg?!" << std::endl; + return; + } + + /* this has some issues with position desync, leaving it out for now + if (abs(egg->appearanceData.iX - plr->x)>500 || abs(egg->appearanceData.iY - plr->y) > 500) { + std::cout << "[WARN] Player tried to open an egg from the other chunk?!" << std::endl; + return; + } + */ + + int typeId = egg->appearanceData.iNPCType; + if (EggTypes.find(typeId) == EggTypes.end()) { + std::cout << "[WARN] Egg Type " << typeId << " not found!" << std::endl; + return; + } + + EggType* type = &EggTypes[typeId]; + + // buff the player + if (type->effectId != 0) + eggBuffPlayer(sock, type->effectId, eggId, type->duration); + + /* + * SHINY_PICKUP_SUCC is only causing a GUI effect in the client + * (buff icon pops up in the bottom of the screen) + * so we don't send it for non-effect + */ + + if (type->effectId != 0) + { + INITSTRUCT(sP_FE2CL_REP_SHINY_PICKUP_SUCC, resp); + resp.iSkillID = type->effectId; + + // in general client finds correct icon on it's own, + // but for damage we have to supply correct CSTB + if (resp.iSkillID == 183) + resp.eCSTB = ECSB_INFECTION; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_SHINY_PICKUP_SUCC, sizeof(sP_FE2CL_REP_SHINY_PICKUP_SUCC)); + } + + // drop + if (type->dropCrateId != 0) { + const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + // we know it's only one trailing struct, so we can skip full validation + + uint8_t respbuf[resplen]; // not a variable length array, don't worry + sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; + sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); + + // don't forget to zero the buffer! + memset(respbuf, 0, resplen); + + // send back player's stats + reward->m_iCandy = plr->money; + reward->m_iFusionMatter = plr->fusionmatter; + reward->m_iBatteryN = plr->batteryN; + reward->m_iBatteryW = plr->batteryW; + reward->iFatigue = 100; // prevents warning message + reward->iFatigue_Level = 1; + reward->iItemCnt = 1; // remember to update resplen if you change this + + int slot = Items::findFreeSlot(plr); + + // no space for drop + if (slot != -1) { + + // item reward + item->sItem.iType = 9; + item->sItem.iOpt = 1; + item->sItem.iID = type->dropCrateId; + item->iSlotNum = slot; + item->eIL = 1; // Inventory Location. 1 means player inventory. + + // update player + plr->Inven[slot] = item->sItem; + sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); + } + } + + if (egg->summoned) + NPCManager::destroyNPC(eggId); + else { + Chunking::removeNPCFromChunks(Chunking::getViewableChunks(egg->chunkPos), eggId); + egg->dead = true; + egg->deadUntil = getTime() + (time_t)type->regen * 1000; + egg->appearanceData.iHP = 0; + } +} + +void Eggs::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_PICKUP, eggPickup); + + REGISTER_SHARD_TIMER(eggStep, 1000); +} diff --git a/src/Eggs.hpp b/src/Eggs.hpp new file mode 100644 index 0000000..9cf58c9 --- /dev/null +++ b/src/Eggs.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "core/Core.hpp" +#include "NPC.hpp" + +struct Egg : public BaseNPC { + bool summoned; + bool dead = false; + time_t deadUntil; + + Egg(int x, int y, int z, uint64_t iID, int type, int32_t id, bool summon) + : BaseNPC(x, y, z, 0, iID, type, id) { + summoned = summon; + npcClass = NPCClass::NPC_EGG; + } +}; + +struct EggType { + int dropCrateId; + int effectId; + int duration; + int regen; +}; + +namespace Eggs { + extern std::unordered_map Eggs; + extern std::map, time_t> EggBuffs; + extern std::unordered_map EggTypes; + + void init(); + + /// returns -1 on fail + int eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration); + void npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg); +} diff --git a/src/Items.cpp b/src/Items.cpp index 751f332..52f53c0 100644 --- a/src/Items.cpp +++ b/src/Items.cpp @@ -6,6 +6,7 @@ #include "Player.hpp" #include "Abilities.hpp" #include "Missions.hpp" +#include "Eggs.hpp" #include // for memset() #include @@ -417,7 +418,7 @@ static void itemUseHandler(CNSocket* sock, CNPacketData* data) { std::pair key = std::make_pair(sock, value1); time_t until = getTime() + (time_t)Nanos::SkillTable[144].durationTime[0] * 100; - NPCManager::EggBuffs[key] = until; + Eggs::EggBuffs[key] = until; } static void itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index e43a9e5..2eb4547 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -10,6 +10,7 @@ #include "Racing.hpp" #include "Vendor.hpp" #include "Abilities.hpp" +#include "Eggs.hpp" #include #include @@ -26,10 +27,6 @@ using namespace NPCManager; std::map NPCManager::NPCs; std::map NPCManager::Warps; std::vector NPCManager::RespawnPoints; -/// sock, CBFlag -> until -std::map, time_t> NPCManager::EggBuffs; -std::unordered_map NPCManager::EggTypes; -std::unordered_map NPCManager::Eggs; nlohmann::json NPCManager::NPCData; /* @@ -65,8 +62,8 @@ void NPCManager::destroyNPC(int32_t id) { MobAI::Mobs.erase(id); // remove from eggs - if (Eggs.find(id) != Eggs.end()) - Eggs.erase(id); + if (Eggs::Eggs.find(id) != Eggs::Eggs.end()) + Eggs::Eggs.erase(id); // finally, remove it from the map and free it delete entity->viewableChunks; @@ -312,247 +309,6 @@ BaseNPC* NPCManager::getNearestNPC(std::set* chunks, int X, int Y, int Z return npc; } -int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) { - Player* plr = PlayerManager::getPlayer(sock); - Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - - int bitFlag = Groups::getGroupFlags(otherPlr); - int CBFlag = Nanos::applyBuff(sock, skillId, 1, 3, bitFlag); - - size_t resplen; - - if (skillId == 183) { - resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Damage); - } else if (skillId == 150) { - resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP); - } else { - resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Buff); - } - assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - // we know it's only one trailing struct, so we can skip full validation - - uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; - sP_FE2CL_NPC_SKILL_HIT* skillUse = (sP_FE2CL_NPC_SKILL_HIT*)respbuf; - - if (skillId == 183) { // damage egg - sSkillResult_Damage* skill = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); - memset(respbuf, 0, resplen); - skill->eCT = 1; - skill->iID = plr->iID; - skill->iDamage = PC_MAXHEALTH(plr->level) * Nanos::SkillTable[skillId].powerIntensity[0] / 1000; - plr->HP -= skill->iDamage; - if (plr->HP < 0) - plr->HP = 0; - skill->iHP = plr->HP; - } else if (skillId == 150) { // heal egg - sSkillResult_Heal_HP* skill = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); - memset(respbuf, 0, resplen); - skill->eCT = 1; - skill->iID = plr->iID; - skill->iHealHP = PC_MAXHEALTH(plr->level) * Nanos::SkillTable[skillId].powerIntensity[0] / 1000; - plr->HP += skill->iHealHP; - if (plr->HP > PC_MAXHEALTH(plr->level)) - plr->HP = PC_MAXHEALTH(plr->level); - skill->iHP = plr->HP; - } else { // regular buff egg - sSkillResult_Buff* skill = (sSkillResult_Buff*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); - memset(respbuf, 0, resplen); - skill->eCT = 1; - skill->iID = plr->iID; - skill->iConditionBitFlag = plr->iConditionBitFlag; - } - - skillUse->iNPC_ID = eggId; - skillUse->iSkillID = skillId; - skillUse->eST = Nanos::SkillTable[skillId].skillType; - skillUse->iTargetCnt = 1; - - sock->sendPacket((void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen); - PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen); - - if (CBFlag == 0) - return -1; - - std::pair key = std::make_pair(sock, CBFlag); - - // save the buff serverside; - // if you get the same buff again, new duration will override the previous one - time_t until = getTime() + (time_t)duration * 1000; - EggBuffs[key] = until; - - return 0; -} - -static void eggStep(CNServer* serv, time_t currTime) { - // tick buffs - time_t timeStamp = currTime; - auto it = EggBuffs.begin(); - while (it != EggBuffs.end()) { - // check remaining time - if (it->second > timeStamp) - it++; - else { // if time reached 0 - CNSocket* sock = it->first.first; - int32_t CBFlag = it->first.second; - Player* plr = PlayerManager::getPlayer(sock); - Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - - int groupFlags = Groups::getGroupFlags(otherPlr); - for (auto& pwr : Nanos::NanoPowers) { - if (pwr.bitFlag == CBFlag) { // pick the power with the right flag and unbuff - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); - resp.eCSTB = pwr.timeBuffID; - resp.eTBU = 2; - resp.eTBT = 3; // for egg buffs - plr->iConditionBitFlag &= ~CBFlag; - resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; - sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - - INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, resp2); // send a buff timeout to other players - resp2.eCT = 1; - resp2.iID = plr->iID; - resp2.iConditionBitFlag = plr->iConditionBitFlag; - PlayerManager::sendToViewable(sock, (void*)&resp2, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); - } - } - // remove buff from the map - it = EggBuffs.erase(it); - } - } - - // check dead eggs and eggs in inactive chunks - for (auto egg : Eggs) { - if (!egg.second->dead || !Chunking::inPopulatedChunks(egg.second->viewableChunks)) - continue; - if (egg.second->deadUntil <= timeStamp) { - // respawn it - egg.second->dead = false; - egg.second->deadUntil = 0; - egg.second->appearanceData.iHP = 400; - - Chunking::addNPCToChunks(Chunking::getViewableChunks(egg.second->chunkPos), egg.first); - } - } - -} - -void NPCManager::npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg) { - egg->iX = npc->iX; - egg->iY = npc->iY; - egg->iZ = npc->iZ; - // client doesn't care about egg->iMapNum - egg->iShinyType = npc->iNPCType; - egg->iShiny_ID = npc->iNPC_ID; -} - -static void eggPickup(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_SHINY_PICKUP)) - return; // malformed packet - - sP_CL2FE_REQ_SHINY_PICKUP* pickup = (sP_CL2FE_REQ_SHINY_PICKUP*)data->buf; - Player* plr = PlayerManager::getPlayer(sock); - - int eggId = pickup->iShinyID; - - if (Eggs.find(eggId) == Eggs.end()) { - std::cout << "[WARN] Player tried to open non existing egg?!" << std::endl; - return; - } - Egg* egg = Eggs[eggId]; - - if (egg->dead) { - std::cout << "[WARN] Player tried to open a dead egg?!" << std::endl; - return; - } - - /* this has some issues with position desync, leaving it out for now - if (abs(egg->appearanceData.iX - plr->x)>500 || abs(egg->appearanceData.iY - plr->y) > 500) { - std::cout << "[WARN] Player tried to open an egg from the other chunk?!" << std::endl; - return; - } - */ - - int typeId = egg->appearanceData.iNPCType; - if (EggTypes.find(typeId) == EggTypes.end()) { - std::cout << "[WARN] Egg Type " << typeId << " not found!" << std::endl; - return; - } - - EggType* type = &EggTypes[typeId]; - - // buff the player - if (type->effectId != 0) - eggBuffPlayer(sock, type->effectId, eggId, type->duration); - - /* - * SHINY_PICKUP_SUCC is only causing a GUI effect in the client - * (buff icon pops up in the bottom of the screen) - * so we don't send it for non-effect - */ - - if (type->effectId != 0) - { - INITSTRUCT(sP_FE2CL_REP_SHINY_PICKUP_SUCC, resp); - resp.iSkillID = type->effectId; - - // in general client finds correct icon on it's own, - // but for damage we have to supply correct CSTB - if (resp.iSkillID == 183) - resp.eCSTB = ECSB_INFECTION; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_SHINY_PICKUP_SUCC, sizeof(sP_FE2CL_REP_SHINY_PICKUP_SUCC)); - } - - // drop - if (type->dropCrateId != 0) { - const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); - assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - // we know it's only one trailing struct, so we can skip full validation - - uint8_t respbuf[resplen]; // not a variable length array, don't worry - sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; - sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); - - // don't forget to zero the buffer! - memset(respbuf, 0, resplen); - - // send back player's stats - reward->m_iCandy = plr->money; - reward->m_iFusionMatter = plr->fusionmatter; - reward->m_iBatteryN = plr->batteryN; - reward->m_iBatteryW = plr->batteryW; - reward->iFatigue = 100; // prevents warning message - reward->iFatigue_Level = 1; - reward->iItemCnt = 1; // remember to update resplen if you change this - - int slot = Items::findFreeSlot(plr); - - // no space for drop - if (slot != -1) { - - // item reward - item->sItem.iType = 9; - item->sItem.iOpt = 1; - item->sItem.iID = type->dropCrateId; - item->iSlotNum = slot; - item->eIL = 1; // Inventory Location. 1 means player inventory. - - // update player - plr->Inven[slot] = item->sItem; - sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); - } - } - - if (egg->summoned) - destroyNPC(eggId); - else { - Chunking::removeNPCFromChunks(Chunking::getViewableChunks(egg->chunkPos), eggId); - egg->dead = true; - egg->deadUntil = getTime() + (time_t)type->regen * 1000; - egg->appearanceData.iHP = 0; - } -} - // TODO: Move this to MobAI, possibly #pragma region NPCEvents @@ -614,8 +370,4 @@ void NPCManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); - - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_PICKUP, eggPickup); - - REGISTER_SHARD_TIMER(eggStep, 1000); } diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index a561765..5c290ab 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -33,33 +33,11 @@ struct WarpLocation { int x, y, z, instanceID, isInstance, limitTaskID, npcID; }; -struct Egg : public BaseNPC { - bool summoned; - bool dead = false; - time_t deadUntil; - - Egg(int x, int y, int z, uint64_t iID, int type, int32_t id, bool summon) - : BaseNPC(x, y, z, 0, iID, type, id) { - summoned = summon; - npcClass = NPCClass::NPC_EGG; - } -}; - -struct EggType { - int dropCrateId; - int effectId; - int duration; - int regen; -}; - namespace NPCManager { extern std::map NPCs; extern std::map Warps; extern std::vector RespawnPoints; extern std::vector NPCEvents; - extern std::unordered_map Eggs; - extern std::map, time_t> EggBuffs; - extern std::unordered_map EggTypes; extern nlohmann::json NPCData; extern int32_t nextId; void init(); @@ -72,8 +50,4 @@ namespace NPCManager { BaseNPC *summonNPC(int x, int y, int z, uint64_t instance, int type, bool respawn=false, bool baseInstance=false); BaseNPC* getNearestNPC(std::set* chunks, int X, int Y, int Z); - - /// returns -1 on fail - int eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration); - void npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg); } diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 9f4b345..fa14825 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -14,6 +14,7 @@ #include "Racing.hpp" #include "BuiltinCommands.hpp" #include "Abilities.hpp" +#include "Eggs.hpp" #include "settings.hpp" @@ -72,10 +73,10 @@ void PlayerManager::removePlayer(CNSocket* key) { Chunking::destroyInstanceIfEmpty(fromInstance); // remove player's buffs from the server - auto it = NPCManager::EggBuffs.begin(); - while (it != NPCManager::EggBuffs.end()) { + auto it = Eggs::EggBuffs.begin(); + while (it != Eggs::EggBuffs.end()) { if (it->first.first == key) { - it = NPCManager::EggBuffs.erase(it); + it = Eggs::EggBuffs.erase(it); } else it++; diff --git a/src/TableData.cpp b/src/TableData.cpp index 0fdbd3b..86dfbab 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -9,6 +9,7 @@ #include "Racing.hpp" #include "Vendor.hpp" #include "Abilities.hpp" +#include "Eggs.hpp" #include "JSON.hpp" @@ -378,7 +379,7 @@ static void loadEggs(int32_t* nextId) { toAdd.effectId = (int)eggType["EffectId"]; toAdd.duration = (int)eggType["Duration"]; toAdd.regen= (int)eggType["Regen"]; - NPCManager::EggTypes[(int)eggType["Id"]] = toAdd; + Eggs::EggTypes[(int)eggType["Id"]] = toAdd; } // Egg instances @@ -390,11 +391,11 @@ static void loadEggs(int32_t* nextId) { Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); NPCManager::NPCs[id] = addEgg; - NPCManager::Eggs[id] = addEgg; + Eggs::Eggs[id] = addEgg; NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); } - std::cout << "[INFO] Loaded " <appearanceData.iX; egg["iY"] = npc->appearanceData.iY;