diff --git a/src/Abilities.cpp b/src/Abilities.cpp index 9bb14b0..a94f69e 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -35,7 +35,8 @@ static SkillResult handleSkillDamage(SkillData* skill, int power, ICombatant* so static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* source, ICombatant* target) { EntityRef sourceRef = source->getRef(); int heal = skill->values[0][power]; - int healed = target->heal(sourceRef, heal); + double scalingFactor = target->getMaxHP() / 1000.0; + int healed = target->heal(sourceRef, heal * scalingFactor); sSkillResult_Heal_HP result{}; result.eCT = target->getCharType(); @@ -97,6 +98,7 @@ static SkillResult handleSkillMove(SkillData* skill, int power, ICombatant* sour if(source->getCharType() != 1) return SkillResult(); // only Players are valid sources for recall Player* plr = dynamic_cast(source); + PlayerManager::sendPlayerTo(source->getRef().sock, plr->recallX, plr->recallY, plr->recallZ, plr->recallInstance); sSkillResult_Move result{}; result.eCT = target->getCharType(); @@ -154,20 +156,22 @@ static std::vector handleSkill(SkillData* skill, int power, ICombat resultSize = sizeof(sSkillResult_BatteryDrain); skillHandler = handleSkillBatteryDrain; break; - case EST_RECALL: - case EST_RECALL_GROUP: + case EST_RECALL: // still soft lock + case EST_RECALL_GROUP: // works for player who uses it resultSize = sizeof(sSkillResult_Move); skillHandler = handleSkillMove; break; - case EST_PHOENIX_GROUP: + case EST_PHOENIX_GROUP: // broken resultSize = sizeof(sSkillResult_Resurrect); skillHandler = handleSkillResurrect; break; + case EST_RETROROCKET_SELF: + // no-op + return results; default: std::cout << "[WARN] Unhandled skill type " << skill->skillType << std::endl; return results; } - assert(skillHandler != nullptr); for(ICombatant* target : targets) { assert(target != nullptr); @@ -189,15 +193,21 @@ static void attachSkillResults(std::vector results, size_t resultSi } } -void Abilities::useNanoSkill(CNSocket* sock, sNano& nano, std::vector affected) { - if(SkillTable.count(nano.iSkillID) == 0) - return; +void Abilities::useNanoSkill(CNSocket* sock, SkillData* skill, sNano& nano, std::vector affected) { - SkillData* skill = &SkillTable[nano.iSkillID]; Player* plr = PlayerManager::getPlayer(sock); - std::vector results = handleSkill(skill, Nanos::getNanoBoost(plr), plr, affected); - size_t resultSize = results.back().size; // guaranteed to be the same for every item + int boost = 0; + if (Nanos::getNanoBoost(plr)) + boost = 3; + + nano.iStamina -= skill->batteryUse[boost]; + if (nano.iStamina < 0) + nano.iStamina = 0; + + std::vector results = handleSkill(skill, boost, plr, affected); + size_t resultSize = 0; // guaranteed to be the same for every item + if (!results.empty()) resultSize = results.back().size; if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), resultSize)) { std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n"; @@ -221,6 +231,9 @@ void Abilities::useNanoSkill(CNSocket* sock, sNano& nano, std::vectorsendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); + + if (nano.iStamina <= 0) + Nanos::summonNano(sock, -1); } void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector affected) { @@ -257,21 +270,30 @@ void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen); } -std::vector Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) { +std::vector Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) { - std::vector targets; + std::vector targets; for (int i = 0; i < count; i++) { int32_t id = ids[i]; if (skill->targetType == SkillTargetType::MOBS) { - // mob? - if (NPCManager::NPCs.find(id) != NPCManager::NPCs.end()) targets.push_back(id); - else std::cout << "[WARN] skill: id not found\n"; - } else { - // player? - CNSocket* sock = PlayerManager::getSockFromID(id); - if (sock != nullptr) targets.push_back(sock); - else std::cout << "[WARN] skill: sock not found\n"; + // mob + if (NPCManager::NPCs.find(id) != NPCManager::NPCs.end()) { + BaseNPC* npc = NPCManager::NPCs[id]; + if (npc->kind == EntityKind::COMBAT_NPC || npc->kind == EntityKind::MOB) { + targets.push_back(dynamic_cast(npc)); + continue; + } + } + std::cout << "[WARN] skill: invalid mob target (id " << id << ")\n"; + } else if(skill->targetType == SkillTargetType::SELF || skill->targetType == SkillTargetType::GROUP) { + // players (?) + Player* plr = PlayerManager::getPlayerFromID(id); + if (plr != nullptr) { + targets.push_back(dynamic_cast(plr)); + continue; + } + std::cout << "[WARN] skill: invalid player target (id " << id << ")\n"; } } diff --git a/src/Abilities.hpp b/src/Abilities.hpp index 275c2c5..04680b9 100644 --- a/src/Abilities.hpp +++ b/src/Abilities.hpp @@ -60,9 +60,9 @@ struct SkillData { namespace Abilities { extern std::map SkillTable; - void useNanoSkill(CNSocket*, sNano&, std::vector); + void useNanoSkill(CNSocket*, SkillData*, sNano&, std::vector); void useNPCSkill(EntityRef, int skillID, std::vector); - std::vector matchTargets(SkillData*, int, int32_t*); + std::vector matchTargets(SkillData*, int, int32_t*); int getCSTBFromST(int eSkillType); } diff --git a/src/Nanos.cpp b/src/Nanos.cpp index f587b68..03fae57 100644 --- a/src/Nanos.cpp +++ b/src/Nanos.cpp @@ -140,7 +140,7 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) { // passive buff effect resp.eCSTB___Add = 1; std::vector affectedCombatants = applyNanoBuff(skill, plr); - if(!affectedCombatants.empty()) Abilities::useNanoSkill(sock, nano, affectedCombatants); + if(!affectedCombatants.empty()) Abilities::useNanoSkill(sock, skill, nano, affectedCombatants); } if (!silent) // silent nano death but only for the summoning player @@ -309,26 +309,17 @@ static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) { return; } - int16_t skillID = plr->Nanos[plr->activeNano].iSkillID; + sNano& nano = plr->Nanos[plr->activeNano]; + int16_t skillID = nano.iSkillID; SkillData* skillData = &Abilities::SkillTable[skillID]; DEBUGLOG( std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl; ) - int boost = 0; - if (getNanoBoost(plr)) - boost = 1; - - plr->Nanos[plr->activeNano].iStamina -= Abilities::SkillTable[skillID].batteryUse[boost*3]; - if (plr->Nanos[plr->activeNano].iStamina < 0) - plr->Nanos[plr->activeNano].iStamina = 0; - // TODO ABILITIES - std::vector targetData = Abilities::matchTargets(skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1)); - /*for (auto& pwr : Abilities::Powers) - if (pwr.skillType == Abilities::SkillTable[skillID].skillType) - pwr.handle(sock, targetData, nanoID, skillID, Abilities::SkillTable[skillID].durationTime[boost], Abilities::SkillTable[skillID].powerIntensity[boost]);*/ + std::vector targetData = Abilities::matchTargets(skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1)); + Abilities::useNanoSkill(sock, skillData, nano, targetData); if (plr->Nanos[plr->activeNano].iStamina < 0) summonNano(sock, -1);