void CMagicState::FinishSpell() { DSP_DEBUG_BREAK_IF(m_PSpell == NULL); DSP_DEBUG_BREAK_IF(m_PEntity->PBattleAI->GetCurrentAction() != ACTION_MAGIC_FINISH); CSpell* PSpellCopy = new CSpell(*m_PSpell); luautils::OnSpellPrecast(m_PEntity, PSpellCopy); SpendCost(PSpellCopy); SetRecast(m_PSpell); // remove effects based on spell cast first int16 effectFlags = EFFECTFLAG_INVISIBLE | EFFECTFLAG_MAGIC_BEGIN; if (PSpellCopy->canTargetEnemy()) { effectFlags |= EFFECTFLAG_DETECTABLE; } m_PEntity->StatusEffectContainer->DelStatusEffectsByFlag(effectFlags); m_PTargetFind->reset(); m_PEntity->m_ActionList.clear(); // setup special targeting flags // can this spell target the dead? uint8 flags = FINDFLAGS_NONE; if (PSpellCopy->getValidTarget() & TARGET_PLAYER_DEAD) { flags |= FINDFLAGS_DEAD; } if (PSpellCopy->getFlag() & SPELLFLAG_HIT_ALL) { flags |= FINDFLAGS_HIT_ALL; } uint8 aoeType = battleutils::GetSpellAoEType(m_PEntity, PSpellCopy); if (aoeType == SPELLAOE_RADIAL) { float distance = spell::GetSpellRadius(PSpellCopy, m_PEntity); m_PTargetFind->findWithinArea(m_PTarget, AOERADIUS_TARGET, distance, flags); } else if (aoeType == SPELLAOE_CONAL) { //TODO: actual radius calculation float radius = spell::GetSpellRadius(PSpellCopy, m_PEntity); m_PTargetFind->findWithinCone(m_PTarget, radius, 45, flags); } else { // only add target m_PTargetFind->findSingleTarget(m_PTarget, flags); } uint16 totalTargets = m_PTargetFind->m_targets.size(); PSpellCopy->setTotalTargets(totalTargets); apAction_t action; action.ActionTarget = m_PTarget; action.reaction = REACTION_NONE; action.speceffect = SPECEFFECT_NONE; action.animation = PSpellCopy->getAnimationID(); action.param = 0; action.messageID = 0; uint16 msg = 0; int16 ce = 0; int16 ve = 0; for (std::vector<CBattleEntity*>::iterator it = m_PTargetFind->m_targets.begin() ; it != m_PTargetFind->m_targets.end(); ++it) { CBattleEntity* PTarget = *it; action.ActionTarget = PTarget; ce = PSpellCopy->getCE(); ve = PSpellCopy->getVE(); // take all shadows if (PSpellCopy->canTargetEnemy() && aoeType > 0) { PTarget->StatusEffectContainer->DelStatusEffect(EFFECT_BLINK); PTarget->StatusEffectContainer->DelStatusEffect(EFFECT_COPY_IMAGE); } // TODO: this is really hacky and should eventually be moved into lua if (PSpellCopy->canHitShadow() && aoeType == SPELLAOE_NONE && battleutils::IsAbsorbByShadow(PTarget)) { // take shadow msg = 31; action.param = 1; ve = 0; ce = 0; } else { action.param = luautils::OnSpellCast(m_PEntity, PTarget, PSpellCopy); // remove effects from damage if (PSpellCopy->canTargetEnemy() && action.param > 0 && m_PSpell->dealsDamage()) { PTarget->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DAMAGE); } if(msg == 0) { msg = PSpellCopy->getMessage(); } else { msg = PSpellCopy->getAoEMessage(); } } action.messageID = msg; if (PSpellCopy->getID() != 305) //I hate to do this, but there really is no other spell like Odin CharOnTarget(&action, ce, ve); m_PEntity->m_ActionList.push_back(action); } CharAfterFinish(); m_PEntity->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_MAGIC_END); DSP_DEBUG_BREAK_IF(m_PEntity->PBattleAI->GetCurrentAction() != ACTION_MAGIC_FINISH); m_PEntity->loc.zone->PushPacket(m_PEntity, CHAR_INRANGE_SELF, new CActionPacket(m_PEntity)); delete PSpellCopy; Clear(); }
void TryLearningSpells(CCharEntity* PChar, CMobEntity* PMob) { if (PMob->m_UsedSkillIds.size() == 0) { // minor optimisation. return; } // prune the learnable blue spells std::vector<CSpell*> PLearnableSpells; for (std::map<uint16, uint16>::iterator i=PMob->m_UsedSkillIds.begin(); i != PMob->m_UsedSkillIds.end(); ++i) { CSpell* PSpell = spell::GetSpellByMonsterSkillId(i->first); if (PSpell != NULL) { PLearnableSpells.push_back(PSpell); } } if (PLearnableSpells.size() == 0) { return; } std::vector<CCharEntity*> PBlueMages; // populate PBlueMages if (PChar->PParty != NULL) { for (uint8 i = 0; i < PChar->PParty->members.size(); i++) { if (PChar->PParty->members[i]->GetMJob() == JOB_BLU && PChar->PParty->members[i]->objtype == TYPE_PC) { PBlueMages.push_back((CCharEntity*)PChar->PParty->members[i]); } } } else if (PChar->GetMJob() == JOB_BLU) { PBlueMages.push_back(PChar); } // loop through the list of BLUs and see if they can learn. for (int i=0; i<PBlueMages.size(); i++) { CCharEntity* PBlueMage = PBlueMages[i]; if (PBlueMage->isDead()) { // too dead to learn continue; } if (distance(PBlueMage->loc.p, PMob->loc.p) > 100) { // too far away to learn continue; } for (int spell=0; spell<PLearnableSpells.size(); spell++) { CSpell* PSpell = PLearnableSpells[spell]; if (charutils::hasSpell(PBlueMage, PSpell->getID())) { continue; } uint8 learnableLevel = PSpell->getJob(JOB_BLU); if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMLevel()+7) { // TODO: Use blue magic skill check rather than level if (rand()%100 < 33) { if (charutils::addSpell(PBlueMage, PSpell->getID())) { PBlueMage->pushPacket(new CMessageBasicPacket(PBlueMage, PBlueMage, PSpell->getID(), 0, MSGBASIC_LEARNS_SPELL)); charutils::SaveSpells(PBlueMage); PBlueMage->pushPacket(new CCharSpellsPacket(PBlueMage)); } } break; // only one attempt at learning a spell, regardless of learn or not. } } } }
void TryLearningSpells(CCharEntity* PChar, CMobEntity* PMob) { if (PMob->m_UsedSkillIds.size() == 0) { // minor optimisation. return; } // prune the learnable blue spells std::vector<CSpell*> PLearnableSpells; for (std::map<uint16, uint16>::iterator i=PMob->m_UsedSkillIds.begin(); i != PMob->m_UsedSkillIds.end(); ++i) { CSpell* PSpell = spell::GetSpellByMonsterSkillId(i->first); if (PSpell != nullptr) { PLearnableSpells.push_back(PSpell); } } if (PLearnableSpells.size() == 0) { return; } std::vector<CCharEntity*> PBlueMages; // populate PBlueMages if (PChar->PParty != nullptr) { for (uint8 i = 0; i < PChar->PParty->members.size(); i++) { if (PChar->PParty->members[i]->GetMJob() == JOB_BLU && PChar->PParty->members[i]->objtype == TYPE_PC) { PBlueMages.push_back((CCharEntity*)PChar->PParty->members[i]); } } } else if (PChar->GetMJob() == JOB_BLU) { PBlueMages.push_back(PChar); } // loop through the list of BLUs and see if they can learn. for (size_t i = 0; i < PBlueMages.size(); i++) { CCharEntity* PBlueMage = PBlueMages[i]; if (PBlueMage->isDead()) { // too dead to learn continue; } if (distance(PBlueMage->loc.p, PMob->loc.p) > 100) { // too far away to learn continue; } for (size_t spell = 0; spell < PLearnableSpells.size(); spell++) { CSpell* PSpell = PLearnableSpells[spell]; if (charutils::hasSpell(PBlueMage, static_cast<uint16>(PSpell->getID()))) { continue; } // get the skill cap for the spell level auto skillLvlForSpell = battleutils::GetMaxSkill(SKILL_BLUE_MAGIC, JOB_BLU, PSpell->getJob(JOB_BLU)); // get player skill level with bonus from gear auto playerSkillLvl = PBlueMage->GetSkill(SKILL_BLUE_MAGIC); // make sure the difference between spell skill and player is at most 31 points if (playerSkillLvl >= skillLvlForSpell - 31) { // TODO: check for blue learning bonus and adjust base percent if (dsprand::GetRandomNumber(100) < 33) { if (charutils::addSpell(PBlueMage, static_cast<uint16>(PSpell->getID()))) { PBlueMage->pushPacket(new CMessageBasicPacket(PBlueMage, PBlueMage, static_cast<uint16>(PSpell->getID()), 0, MSGBASIC_LEARNS_SPELL)); charutils::SaveSpell(PBlueMage, static_cast<uint16>(PSpell->getID())); PBlueMage->pushPacket(new CCharSpellsPacket(PBlueMage)); } } break; // only one attempt at learning a spell, regardless of learn or not. } } } }