Exemplo n.º 1
0
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)
            {
                auto chanceToLearn = 33 + PBlueMage->getMod(Mod::BLUE_LEARN_CHANCE);
                if (dsprand::GetRandomNumber(100) < chanceToLearn) {
					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.
			}
		}

	}
}
Exemplo n.º 2
0
/************************************************************************
*                                                                       *
*  Creates up to many attacks for a particular hand.			        *
*                                                                       *
************************************************************************/
void CAttackRound::CreateAttacks(CItemWeapon* PWeapon, PHYSICAL_ATTACK_DIRECTION direction)
{
    uint8 num = 1;

    bool isPC = m_attacker->objtype == TYPE_PC;

    // Checking the players weapon hit count
    if (PWeapon->getReqLvl() <= m_attacker->GetMLevel())
    {
        num = PWeapon->getHitCount();
    }

    // If the attacker is a mobentity or derived from mobentity, check to see if it has any special mutli-hit capabilties
    if (dynamic_cast<CMobEntity*>(m_attacker))
    {
        auto multiHitMax = (uint8)static_cast<CMobEntity*>(m_attacker)->getMobMod(MOBMOD_MULTI_HIT);

        if (multiHitMax > 0)
            num = 1 + battleutils::getHitCount(multiHitMax);
    }

    AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, num);

    // Checking the players triple, double and quadruple attack
    int16 tripleAttack = m_attacker->getMod(Mod::TRIPLE_ATTACK);
    int16 doubleAttack = m_attacker->getMod(Mod::DOUBLE_ATTACK);
    int16 quadAttack = m_attacker->getMod(Mod::QUAD_ATTACK);

    //check for merit upgrades
    if (isPC)
    {
        CCharEntity* PChar = (CCharEntity*)m_attacker;

        //merit chance only applies if player has the job trait
        if (charutils::hasTrait(PChar, TRAIT_TRIPLE_ATTACK)) tripleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_TRIPLE_ATTACK_RATE, PChar);

        // Ambush Augment adds +1% Triple Attack per merit (need to satisfy conditions for Ambush)
        if (charutils::hasTrait(PChar, TRAIT_AMBUSH) && PChar->getMod(Mod::AUGMENTS_AMBUSH) > 0 && abs(m_defender->loc.p.rotation - m_attacker->loc.p.rotation) < 23)
        {
            tripleAttack += PChar->PMeritPoints->GetMerit(MERIT_AMBUSH)->count;
        }

        if (charutils::hasTrait(PChar, TRAIT_DOUBLE_ATTACK)) doubleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_DOUBLE_ATTACK_RATE, PChar);
        // TODO: Quadruple attack merits when SE release them.
    }

    quadAttack = std::clamp<int16>(quadAttack, 0, 100);
    doubleAttack = std::clamp<int16>(doubleAttack, 0, 100);
    tripleAttack = std::clamp<int16>(tripleAttack, 0, 100);

    // Checking Mikage Effect - Hits Vary With Num of Utsusemi Shadows for Main Weapon
    if (m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_MIKAGE) && m_attacker->m_Weapons[SLOT_MAIN]->getID() == PWeapon->getID())
    {
        auto shadows = (uint8)m_attacker->getMod(Mod::UTSUSEMI);
        //ShowDebug(CL_CYAN"Create Attacks: Mikage Active, Rolling Attack Chance for %d Shadowss...\n" CL_RESET, shadows);
        AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, shadows);
    }
    else if (num == 1 && dsprand::GetRandomNumber(100) < quadAttack)
        AddAttackSwing(PHYSICAL_ATTACK_TYPE::QUAD, direction, 3);

    else if (num == 1 && dsprand::GetRandomNumber(100) < tripleAttack)
        AddAttackSwing(PHYSICAL_ATTACK_TYPE::TRIPLE, direction, 2);

    else if (num == 1 && dsprand::GetRandomNumber(100) < doubleAttack)
        AddAttackSwing(PHYSICAL_ATTACK_TYPE::DOUBLE, direction, 1);

    // Apply Mythic OAT mods (mainhand only)
    if (direction == PHYSICAL_ATTACK_DIRECTION::RIGHTATTACK)
    {
        int16 occAttThriceRate = std::clamp<int16>(m_attacker->getMod(Mod::MYTHIC_OCC_ATT_THRICE), 0, 100);
        int16 occAttTwiceRate = std::clamp<int16>(m_attacker->getMod(Mod::MYTHIC_OCC_ATT_TWICE), 0, 100);
        if (num == 1 && dsprand::GetRandomNumber(100) < occAttThriceRate)
        {
            AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 2);
        }
        else if (num == 1 && dsprand::GetRandomNumber(100) < occAttTwiceRate)
        {
            AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 1);
        }
    }

    // Ammo extra swing - players only
    if (isPC && m_attacker->getMod(Mod::AMMO_SWING) > 0)
    {
        // Check for ammo
        CCharEntity* PChar = (CCharEntity*)m_attacker;
        CItemArmor* PAmmo = PChar->getEquip(SLOT_AMMO);
        CItemArmor* PMain = PChar->getEquip(SLOT_MAIN);
        CItemArmor* PSub = PChar->getEquip(SLOT_SUB);
        uint8 slot = PChar->equip[SLOT_AMMO];
        uint8 loc = PChar->equipLoc[SLOT_AMMO];
        uint8 ammoCount = 0;

        // Handedness check, checking mod of the weapon for the purposes of level scaling
        if (battleutils::GetScaledItemModifier(PChar, PMain, Mod::AMMO_SWING_TYPE) == 2 &&
            dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity())
        {
            AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 1);
            ammoCount += 1;
        }
        else
        {
            if (direction == RIGHTATTACK && battleutils::GetScaledItemModifier(PChar, PMain, Mod::AMMO_SWING_TYPE) == 1 &&
                dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity())
            {
                AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, RIGHTATTACK, 1);
                ammoCount += 1;
            }
            if (direction == LEFTATTACK && PSub != nullptr && battleutils::GetScaledItemModifier(PChar, PSub, Mod::AMMO_SWING_TYPE) == 1 &&
                dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity())
            {
                AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, LEFTATTACK, 1);
                ammoCount += 1;
            }
        }

        if (PAmmo != nullptr)
        {
            if (PAmmo->getQuantity() == ammoCount)
            {
                charutils::UnequipItem(PChar, SLOT_AMMO);
                charutils::SaveCharEquip(PChar);
            }
            charutils::UpdateItem(PChar, loc, slot, -ammoCount);
            PChar->pushPacket(new CInventoryFinishPacket());
        }
    }


    // TODO: Possible Lua function for the nitty gritty stuff below.

    // Iga mod: Extra attack chance whilst dual wield is on.
    if (direction == LEFTATTACK && dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::EXTRA_DUAL_WIELD_ATTACK))
        AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, RIGHTATTACK, 1);

}