#include "stdafx.h" #include "MagicProcess.h" #include "MagicInstance.h" #include "Map.h" #if defined(GAMESERVER) # include "GameServerDlg.h" # include "../shared/DateTime.h" #else # include "../AIServer/ServerDlg.h" # include "../AIServer/User.h" # include "../AIServer/Npc.h" #endif #if defined(GAMESERVER) void CMagicProcess::MagicPacket(Packet & pkt, Unit * pCaster /*= nullptr*/) { if (g_pMain->m_IsMagicTableInUpdateProcess) return; MagicInstance instance; pkt >> instance.bOpcode >> instance.nSkillID; instance.pSkill = g_pMain->m_MagictableArray.GetData(instance.nSkillID); if (instance.pSkill == nullptr) { if (pCaster != nullptr) TRACE("[%s] Used skill %d but it does not exist.\n", pCaster->GetName().c_str(), instance.nSkillID); if (pCaster->isPlayer() && instance.nSkillID < 0) { DateTime time; g_pMain->SendFormattedNotice("%s is currently disconnect for skill hack.",Nation::ALL, pCaster->GetName().c_str()); g_pMain->WriteCheatLogFile(string_format("[ SkillHack - %d:%d:%d ] %s Disconnected for SkillHack.\n", time.GetHour(),time.GetMinute(),time.GetSecond(), pCaster->GetName().c_str())); TO_USER(pCaster)->Disconnect(); } return; } pkt >> instance.sCasterID >> instance.sTargetID >> instance.sData[0] >> instance.sData[1] >> instance.sData[2] >> instance.sData[3] >> instance.sData[4] >> instance.sData[5] >> instance.sData[6]; // Prevent users from faking other players or NPCs. if (pCaster != nullptr // if it's nullptr, it's from AI. && (instance.sCasterID >= NPC_BAND || instance.sCasterID != pCaster->GetID())) return; instance.bIsRecastingSavedMagic = false; instance.Run(); } void CMagicProcess::UpdateAIServer(uint32 nSkillID, AISkillOpcode opcode, Unit * pTarget, Unit * pCaster /*= nullptr*/, bool bIsRecastingSavedMagic /*= false*/) { Packet result(AG_MAGIC_ATTACK_REQ, uint8(opcode)); int16 sCasterID = (pCaster == nullptr ? -1 : pCaster->GetID()), sTargetID = (pTarget == nullptr ? -1 : pTarget->GetID()); result << nSkillID << sCasterID << sTargetID << bIsRecastingSavedMagic; g_pMain->Send_AIServer(&result); } #else void CMagicProcess::MagicPacket(Packet & pkt, Unit * pCaster /*= nullptr*/) { _MAGIC_TABLE * pSkill; _MAGIC_TYPE4 * pType4; Unit * pSkillCaster, * pSkillTarget; uint32 nSkillID; uint16 sCasterID, sTargetID; uint8 bOpcode; bool bIsRecastingSavedMagic; pkt >> bOpcode >> nSkillID >> sCasterID >> sTargetID >> bIsRecastingSavedMagic; pSkill = g_pMain->m_MagictableArray.GetData(nSkillID); if (pSkill == nullptr) return; pSkillCaster = g_pMain->GetUnitPtr(sCasterID); pSkillTarget = g_pMain->GetUnitPtr(sTargetID); if (bOpcode == AISkillOpcodeBuff || bOpcode == AISkillOpcodeRemoveBuff) { pType4 = g_pMain->m_Magictype4Array.GetData(nSkillID); if (pType4 == nullptr) return; } /*if(pSkillCaster != nullptr) printf("Target : %s, pType4: %d\n",pSkillCaster->GetName().c_str(),pType4->bSpeed);*/ switch (bOpcode) { case AISkillOpcodeBuff: if (pSkillCaster == nullptr || pSkillTarget == nullptr) return; GrantType4Buff(pSkill, pType4, pSkillCaster, pSkillTarget, bIsRecastingSavedMagic); break; case AISkillOpcodeRemoveBuff: if (pSkillTarget == nullptr) return; RemoveType4Buff(pType4->bBuffType, pSkillTarget); break; } /*if(pSkillCaster != nullptr) printf("Target : %s, sAmount: %d\n",pSkillCaster->GetName().c_str(),pSkillTarget->m_bSpeedAmount);*/ } #endif // TODO: Clean this up (even using unit code...) bool CMagicProcess::UserRegionCheck(Unit * pSkillCaster, Unit * pSkillTarget, _MAGIC_TABLE * pSkill, int radius, short mousex /*= 0*/, short mousez /*= 0*/) { if (pSkillCaster->isDead() || pSkillTarget == nullptr) return false; switch (pSkill->bMoral) { case MORAL_PARTY_ALL: // Check that it's your party. // NPCs cannot be in parties. if (pSkillCaster->isNPC() || pSkillTarget->isNPC()) return false; if (!TO_USER(pSkillTarget)->isInParty()) return (pSkillTarget == pSkillCaster); if (TO_USER(pSkillTarget)->GetPartyID() == TO_USER(pSkillCaster)->GetPartyID() && pSkill->bType[0] != 8) goto final_test; else if (TO_USER(pSkillTarget)->GetPartyID() == TO_USER(pSkillCaster)->GetPartyID() && pSkill->bType[0] == 8) { if (pSkillTarget->GetMap()->isWarZone() && (UNIXTIME - TO_USER(pSkillTarget)->m_tLastRegeneTime < CLAN_SUMMON_TIME)) return false; goto final_test; } break; // Nation alone cannot dictate whether a unit can attack another. // As such, we must check behaviour specific to these entities. // For example: same nation players attacking each other in an arena. case MORAL_SELF_AREA: case MORAL_AREA_ENEMY: if (pSkillCaster->isHostileTo(pSkillTarget)) goto final_test; break; case MORAL_AREA_ALL: if (pSkillCaster->isNPC() || pSkillTarget->isNPC()) return false; if ((TO_USER(pSkillCaster)->isInArena() && TO_USER(pSkillTarget)->isInArena()) || (TO_USER(pSkillCaster)->isInPartyArena() && (TO_USER(pSkillCaster)->GetPartyID() != TO_USER(pSkillTarget)->GetPartyID() || TO_USER(pSkillCaster)->GetPartyID() == uint16(-1) || TO_USER(pSkillTarget)->GetPartyID() == uint16(-1)) && TO_USER(pSkillTarget)->isInPartyArena()) || (TO_USER(pSkillCaster)->isInPVPZone() && TO_USER(pSkillTarget)->isInPVPZone()) || (TO_USER(pSkillCaster)->isInTempleEventZone() && TO_USER(pSkillTarget)->isInTempleEventZone())) goto final_test; // Players cant attack other players in the safety area. if (TO_USER(pSkillTarget)->isInSafetyArea()) return false; break; case MORAL_AREA_FRIEND: if (!pSkillCaster->isHostileTo(pSkillTarget)) goto final_test; break; case MORAL_CLAN_ALL: // NPCs cannot be in clans. if (pSkillCaster->isNPC() || pSkillTarget->isNPC()) return false; if (!TO_USER(pSkillTarget)->isInClan()) return (pSkillTarget == pSkillCaster); if (TO_USER(pSkillTarget)->GetClanID() == TO_USER(pSkillCaster)->GetClanID() && pSkill->bType[0] != 8) goto final_test; else if (TO_USER(pSkillTarget)->GetClanID() == TO_USER(pSkillCaster)->GetClanID() && pSkill->bType[0] == 8) { if (pSkillTarget->GetMap()->isWarZone() && (UNIXTIME - TO_USER(pSkillTarget)->m_tLastRegeneTime < CLAN_SUMMON_TIME)) return false; goto final_test; } break; } return false; final_test: return (radius == 0 || pSkillTarget->isInRangeSlow(mousex, mousez, (float) radius)); } #if defined(GAMESERVER) void CMagicProcess::CheckExpiredType6Skills(Unit * pTarget) { if (!pTarget->isPlayer() || !TO_USER(pTarget)->isTransformed() || (UNIXTIME - TO_USER(pTarget)->m_tTransformationStartTime) < TO_USER(pTarget)->m_sTransformationDuration) return; MagicInstance instance; instance.pSkillCaster = pTarget; instance.Type6Cancel(); } void CMagicProcess::CheckExpiredType9Skills(Unit * pTarget, bool bForceExpiration /*= false*/) { if (!pTarget->isPlayer()) return; Guard lock(pTarget->_unitlock); Type9BuffMap & buffMap = pTarget->m_type9BuffMap; MagicInstance instance; instance.pSkillCaster = pTarget; for (auto itr = buffMap.begin(); itr != buffMap.end();) { if (bForceExpiration || UNIXTIME >= itr->second.tEndTime) { // Cancel the skill, but don't remove it from the map. We'll do that. instance.nSkillID = itr->second.nSkillID; instance.Type9Cancel(false); itr = buffMap.erase(itr); } else { ++itr; } } } void CMagicProcess::RemoveStealth(Unit * pTarget, InvisibilityType bInvisibilityType) { if (bInvisibilityType != INVIS_DISPEL_ON_MOVE && bInvisibilityType != INVIS_DISPEL_ON_ATTACK) return; Guard lock(pTarget->_unitlock); Type9BuffMap & buffMap = pTarget->m_type9BuffMap; MagicInstance instance; // Buff type is 1 for 'dispel on move' skills and 2 for 'dispel on attack' skills. uint8 bType = (bInvisibilityType == INVIS_DISPEL_ON_MOVE ? 1 : 2); // If there's no such skill active on the user, no reason to remove it. auto itr = buffMap.find(bType); if (itr == buffMap.end()) return; instance.pSkillCaster = pTarget; instance.nSkillID = itr->second.nSkillID; instance.Type9Cancel(); } #endif bool CMagicProcess::GrantType4Buff(_MAGIC_TABLE * pSkill, _MAGIC_TYPE4 *pType, Unit * pCaster, Unit *pTarget, bool bIsRecastingSavedMagic /*= false*/) { // Buff mustn't already be added at this point. Guard lock(pTarget->_unitlock); if (pTarget->m_buffMap.find(pType->bBuffType) != pTarget->m_buffMap.end()) return false; switch (pType->bBuffType) { case BUFF_TYPE_HP_MP: if (pType->sMaxHP == 0 && pType->sMaxHPPct > 0) pTarget->m_sMaxHPAmount = (pType->sMaxHPPct - 100) * (pTarget->GetMaxHealth() - pTarget->m_sMaxHPAmount) / 100; else pTarget->m_sMaxHPAmount = pType->sMaxHP; if (pType->sMaxMP == 0 && pType->sMaxMPPct > 0) pTarget->m_sMaxMPAmount = (pType->sMaxMPPct - 100) * (pTarget->GetMaxMana() - pTarget->m_sMaxMPAmount) / 100; else pTarget->m_sMaxMPAmount = pType->sMaxMP; break; case BUFF_TYPE_AC: if (pType->sAC == 0 && pType->sACPct > 0) pTarget->m_sACPercent += (pType->sACPct - 100); else pTarget->m_sACAmount += pType->sAC; break; case BUFF_TYPE_SIZE: if (pCaster->isPlayer()) { // Unfortunately there's no way to differentiate which does what. // Officially it also resorts to checking the skill ID. uint8 bEffect = ABNORMAL_NORMAL; switch (pSkill->iNum) { case 490034: // Bezoar bEffect = ABNORMAL_GIANT; break; case 490401: // Maximize Scroll bEffect = ABNORMAL_GIANT_TARGET; // NOTE: not sure why, but this is what it uses officially. break; case 490035: // Rice cake case 490100: // unknown, possibly intended to be "Minimize Scroll" bEffect = ABNORMAL_DWARF; break; } if (bEffect != ABNORMAL_NORMAL) pTarget->StateChangeServerDirect(3, bEffect); } break; if (pTarget->isPlayer()) TO_USER(pTarget)->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_ATTACK_SPEED: pTarget->m_sAttackSpeedAmount += (pType->bAttackSpeed - 100); break; case BUFF_TYPE_SPEED: pTarget->m_bSpeedAmount = pType->bSpeed; break; case BUFF_TYPE_BATTLE_CRY: case BUFF_TYPE_STATS: if (pTarget->isPlayer()) { TO_USER(pTarget)->SetStatBuff(STAT_STR, pType->bStr); TO_USER(pTarget)->SetStatBuff(STAT_STA, pType->bSta); TO_USER(pTarget)->SetStatBuff(STAT_DEX, pType->bDex); TO_USER(pTarget)->SetStatBuff(STAT_INT, pType->bIntel); TO_USER(pTarget)->SetStatBuff(STAT_CHA, pType->bCha); } break; case BUFF_TYPE_RESISTANCES: pTarget->m_bAddFireR = pType->bFireR; pTarget->m_bAddColdR = pType->bColdR; pTarget->m_bAddLightningR = pType->bLightningR; pTarget->m_bAddMagicR = pType->bMagicR; pTarget->m_bAddDiseaseR = pType->bDiseaseR; pTarget->m_bAddPoisonR = pType->bPoisonR; break; case BUFF_TYPE_ACCURACY: pTarget->m_bHitRateAmount = pType->bHitRate; pTarget->m_sAvoidRateAmount = pType->sAvoidRate; break; case BUFF_TYPE_MAGIC_POWER: if (pTarget->isPlayer()) pTarget->m_sMagicAttackAmount = (pType->bMagicAttack - 100) * TO_USER(pTarget)->GetStat(STAT_CHA) / 100; break; case BUFF_TYPE_EXPERIENCE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_sExpGainAmount = (uint8) pType->sExpPct; break; case BUFF_TYPE_FISHING: #if defined(GAMESERVER) if (pTarget->isPlayer() && pType->sSpecialAmount == 1) TO_USER(pTarget)->DcFlash(); //TO_USER(pTarget)->m_FlashDcBonus = (uint8) pType->sExpPct; if (pTarget->isPlayer() && pType->sSpecialAmount == 2) TO_USER(pTarget)->ExpFlash(); //TO_USER(pTarget)->m_FlashExpBonus = (uint8) pType->sExpPct; if (pTarget->isPlayer() && pType->sSpecialAmount == 3) TO_USER(pTarget)->WarFlash(); //TO_USER(pTarget)->m_FlashWarBonus = (uint8) pType->sExpPct; #endif break; case BUFF_TYPE_WEIGHT: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bMaxWeightAmount = (uint8) pType->sExpPct; break; case BUFF_TYPE_WEAPON_DAMAGE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bAddWeaponDamage = uint8(pType->bAttack); break; case BUFF_TYPE_WEAPON_AC: if (pTarget->isPlayer()) { if (pType->sAC == 0 && pType->sACPct > 0) TO_USER(pTarget)->m_bPctArmourAc += (pType->sACPct - 100); else TO_USER(pTarget)->m_sAddArmourAc += pType->sAC; } break; case BUFF_TYPE_LOYALTY: if(pTarget->isPlayer()) TO_USER(pTarget)->m_bNPGainAmount = (uint8) pType->sExpPct; break; case BUFF_TYPE_NOAH_BONUS: if(pTarget->isPlayer()) TO_USER(pTarget)->m_bNoahGainAmount = (uint8) pType->sExpPct; break; case BUFF_TYPE_PREMIUM_MERCHANT: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bPremiumMerchant = true; break; case BUFF_TYPE_DAMAGE: if (pTarget->isPlayer()) pTarget->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_ATTACK_SPEED_ARMOR: pTarget->m_sACAmount += pType->sAC; pTarget->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_DAMAGE_DOUBLE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bPlayerAttackAmount = uint8(pType->bAttack); break; case BUFF_TYPE_DISABLE_TARGETING: pTarget->m_bIsBlinded = true; break; case BUFF_TYPE_BLIND: // The only skill that uses this buff type is "Blinding Strafe", which states: // Description: Shoots an arrow that inflicts 400% damage and blinds the enemy. Does not apply to monsters. // As such, we should not blind monsters. if (pTarget->isPlayer()) pTarget->m_bIsBlinded = true; break; case BUFF_TYPE_FREEZE: // Proportional to the target user's current HP. pTarget->m_bBlockMagic = true; //Block Magical Attack Damage (But Effect Work) by Terry TO_USER(pTarget)->BlinkStart(); pTarget->m_bSpeedAmount = pType->bSpeed; break; case BUFF_TYPE_INSTANT_MAGIC: pTarget->m_bInstantCast = true; break; case BUFF_TYPE_DECREASE_RESIST: pTarget->m_bPctFireR = (100 - pType->bFireR); pTarget->m_bPctColdR = (100 - pType->bColdR); pTarget->m_bPctLightningR = (100 - pType->bLightningR); pTarget->m_bPctMagicR = (100 - pType->bMagicR); pTarget->m_bPctDiseaseR = (100 - pType->bDiseaseR); pTarget->m_bPctPoisonR = (100 - pType->bPoisonR); break; case BUFF_TYPE_MAGE_ARMOR: pTarget->m_bReflectArmorType = (pSkill->sSkill % 100); break; case BUFF_TYPE_PROHIBIT_INVIS: pTarget->m_bCanStealth = false; break; case BUFF_TYPE_RESIS_AND_MAGIC_DMG: // Elysian Web pTarget->m_bMagicDamageReduction = (uint8) pType->sExpPct; break; case BUFF_TYPE_TRIPLEAC_HALFSPEED: // Wall of Iron switch (pSkill->iNum) { case 115820: pTarget->m_sACPercent += (pType->sACPct - 100); pTarget->m_bSpeedAmount = pTarget->m_bSpeedAmount / 2; if (pTarget->m_bSpeedAmount == 0) pTarget->m_bSpeedAmount = 1; break; case 215820: pTarget->m_sACPercent += (pType->sACPct - 100); pTarget->m_bSpeedAmount = pTarget->m_bSpeedAmount / 2; if (pTarget->m_bSpeedAmount == 0) pTarget->m_bSpeedAmount = 1; break; default: pTarget->m_sACPercent += 300; // 300%, or 3x pTarget->m_bSpeedAmount = pTarget->m_bSpeedAmount / 2; if (pTarget->m_bSpeedAmount == 0) pTarget->m_bSpeedAmount = 1; } break; case BUFF_TYPE_BLOCK_CURSE: // Counter Curse pTarget->m_bBlockCurses = true; break; case BUFF_TYPE_BLOCK_CURSE_REFLECT: // Curse Refraction pTarget->m_bReflectCurses = true; break; case BUFF_TYPE_MANA_ABSORB: // Outrage / Frenzy / Mana Shield pTarget->m_bManaAbsorb = (uint8) pType->sExpPct; pTarget->AbsorbCount = 4; break; case BUFF_TYPE_VARIOUS_EFFECTS: //... whatever the event item grants. // what is tweaked in the database: AC, Attack, MaxHP, resistances // AC if (pType->sAC == 0 && pType->sACPct > 100) pTarget->m_sACPercent += (pType->sACPct - 100); else if (pType->sAC > 0 && pType->sACPct == 100) pTarget->m_sACAmount += pType->sAC; // Attack if (pType->bAttack > 100) pTarget->m_bAttackAmount += (pType->bAttack - 100); // NP Bonus if (pTarget->isPlayer() && pType->sSpecialAmount > 0) TO_USER(pTarget)->m_bSkillNPBonus += pType->sSpecialAmount; break; case BUFF_TYPE_IGNORE_WEAPON: // Weapon cancellation // Disarms the opponent. (rendering them unable to attack) #if defined(GAMESERVER) if (pTarget->isPlayer()) { CUser * pTUser = TO_USER(pTarget); pTUser->m_bWeaponsDisabled = true; pTUser->UserLookChange(RIGHTHAND, 0, 0); _ITEM_TABLE * pLeftHand = pTUser->GetItemPrototype(LEFTHAND); if (pLeftHand != nullptr && !pLeftHand->isShield()) pTUser->UserLookChange(LEFTHAND, 0, 0); } #endif break; case BUFF_TYPE_PASSION_OF_SOUL: // Passion of the Soul // Increase pet's HP by 120 break; case BUFF_TYPE_FIRM_DETERMINATION: // Firm Determination // Increase pet's AC by 20 break; case BUFF_TYPE_ATTACK_MAGIC_ATTACK: pTarget->m_sMagicAttackAmount += (pType->bMagicAttack - 100); pTarget->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_ATTACK_TIME: pTarget->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_SPEED2: // Cold Wave #if defined(GAMESERVER) // update the target data in the AI server. TO_USER(pTarget)->m_bSpeedAmount = (pTarget->m_bSpeedAmount / 100 * pType->bSpeed); TO_USER(pTarget)->m_bIceSpeedAmount = 100; #elif defined(AI_SERVER) TO_USER(pTarget)->m_bSpeedAmount = (pTarget->m_bSpeedAmount / 100 * pType->bSpeed); TO_USER(pTarget)->m_bIceSpeedAmount = 100; #endif break; case BUFF_TYPE_UNK_EXPERIENCE: // unknown buff type, used for something relating to XP. break; case BUFF_TYPE_ATTACK_RANGE_ARMOR: // Inevitable Murderous pTarget->m_sACAmount += 100; pTarget->m_bRadiusAmount = pType->bRadius; break; case BUFF_TYPE_MIRROR_DAMAGE_PARTY: // Minak's Thorn pTarget->m_bMirrorDamage = true; pTarget->m_byMirrorAmount = (uint8) pType->sSpecialAmount; break; case BUFF_TYPE_DAGGER_BOW_DEFENSE: // Eskrima // Inflicts attacks as well as a bleeding curse on the enemy. Decreases 10% Dagger and Bow Defense of the enemy under the bleeding curse buff. // NOTE: overwrite the percentage for now (nothing else uses it) // Also: the amount is 20 in the database. Could be that it's divided by 2 (i.e. splitting it between dagger/bow), the skill description's inaccurate // or the description roughly reflects the final damage after player damage reduction. For now, we'll just assume it's the latter. pTarget->m_byDaggerRAmount += (uint8) (pType->sSpecialAmount / 2);// 10 pTarget->m_byBowRAmount += (uint8) (pType->sSpecialAmount / 2); break; case BUFF_TYPE_STUN: // Lighting Skill Stun pTarget->m_bSpeedAmount = pType->bSpeed; break; case BUFF_TYPE_DEVIL_TRANSFORM: // Devil Transform pTarget->m_bIsDevil = true; pTarget->AbsorbedAmmount = 0; pTarget->StateChangeServerDirect(12, 1); break; case BUFF_TYPE_LOYALTY_AMOUNT: // Santa's Present (gives an extra +2NP per kill, unlike BUFF_TYPE_LOYALTY which uses an percent). if (pTarget->isPlayer()) TO_USER(pTarget)->m_bSkillNPBonus += 2; break; case BUFF_TYPE_NO_RECALL: // prevents teleportation. pTarget->m_bCanTeleport = false; break; case BUFF_TYPE_REDUCE_TARGET: // "Reduction" (reduces target's stats, but enlarges their character to make them easier to attack) // NOTE: Skill description says "Enlarge enemy, but decrease attack and defense rate by 5%" // There's nothing else set in the client to give those stats, and AC's reduced by 15% according to the data... // Just working with the TBL data for now (i.e. just the 15% AC reduction). if (pTarget->isPlayer()) { pTarget->StateChangeServerDirect(3, ABNORMAL_GIANT_TARGET); pTarget->m_sACPercent += (pType->sACPct - 100); } break; case BUFF_TYPE_SILENCE_TARGET: // Silences the target to prevent them from using any skills (or potions) pTarget->m_bCanUseSkills = false; break; case BUFF_TYPE_NO_POTIONS: // "No Potion" prevents target from using potions. pTarget->m_bCanUsePotions = false; break; case BUFF_TYPE_KAUL_TRANSFORMATION: // Transforms the target into a Kaul (a pig thing), preventing you from /town'ing or attacking, but increases defense. if (pTarget->isPlayer()) { pTarget->m_bIsKaul = true; pTarget->m_sACAmount += 500; pTarget->StateChangeServerDirect(3, pType->iNum); } break; case BUFF_TYPE_UNDEAD: // User becomes undead, increasing defense but preventing the use of potions and converting all health received into damage. pTarget->m_bIsUndead = true; pTarget->m_sACPercent += (pType->sACPct - 100); break; case BUFF_TYPE_UNSIGHT: // Blocks the caster's sight (not the target's). pTarget->m_bIsBlinded = true; break; case BUFF_TYPE_BLOCK_PHYSICAL_DAMAGE: // Blocks all physical damage. pTarget->m_bBlockPhysical = true; break; case BUFF_TYPE_BLOCK_MAGICAL_DAMAGE: // Blocks all magical/skill damage. pTarget->m_bBlockMagic = true; break; case BUFF_TYPE_UNK_POTION: // unknown potion, "Return of the Warrior", "Comeback potion", perhaps some sort of revive? break; case BUFF_TYPE_SLEEP: // Zantman(Sandman), puts enemies to sleep. break; case BUFF_TYPE_INVISIBILITY_POTION: // "Unidentified potion" break; case BUFF_TYPE_GODS_BLESSING: // Increases your defense/max HP break; case BUFF_TYPE_HELP_COMPENSATION: // Compensation for using the help system (to help, ask for help, both?) break; case BUFF_TYPE_INC_CONTRIBUTION: break; case BUFF_TYPE_IMIR_ROAR: break; case BUFF_TYPE_LOGOS_HORNS: break; case BUFF_TYPE_UNKNOW: break; case BUFF_TYPE_DROP_RATE: break; case BUFF_TYPE_MAMA_MAGPIE: pTarget->StateChangeServerDirect(3, pType->iNum); break; case BUFF_TYPE_ATTACK_AMMONUT2: TO_USER(pTarget)->m_bAttackAmount += (pType->bAttack - 100); break; case BUFF_TYPE_ARMORED: //Amored Skill Dark Knight Staff 300AC by Terry pTarget->m_sACAmount += pType->sAC; break; default: return false; } #if defined(GAMESERVER) // update the target data in the AI server. UpdateAIServer(pSkill->iNum, AISkillOpcodeBuff, pCaster, pTarget, bIsRecastingSavedMagic); #elif defined(AI_SERVER) // on the AI server's side, add the buff to the target's buff map. _BUFF_TYPE4_INFO buffInfo; buffInfo.m_nSkillID = pSkill->iNum; pTarget->AddType4Buff(pType->bBuffType, buffInfo); #endif return true; } bool CMagicProcess::RemoveType4Buff(uint8 byBuffType, Unit *pTarget, bool bRemoveSavedMagic /*= true*/, bool bRecastSavedMagic /*= false*/) { // Buff must be added at this point. If it doesn't exist, we can't remove it twice. Guard lock(pTarget->m_buffLock); auto itr = pTarget->m_buffMap.find(byBuffType); if (itr == pTarget->m_buffMap.end()) return false; _MAGIC_TABLE * pSkill = g_pMain->m_MagictableArray.GetData(itr->second.m_nSkillID); if (pSkill == nullptr) return false; //_MAGIC_TYPE4 * pType = g_pMain->m_Magictype4Array.GetData(pSkill->iNum); _MAGIC_TYPE4 * pType = g_pMain->m_Magictype4Array.GetData(itr->second.m_nSkillID); //Missing remove Bufs - Fixed by Terry if (pType == nullptr || pTarget == nullptr) return false; // If this buff persists across logout, it should be removed here too. if (bRemoveSavedMagic && pTarget->isPlayer() && pTarget->HasSavedMagic(pSkill->iNum)) TO_USER(pTarget)->RemoveSavedMagic(pSkill->iNum); if (itr->second.isBuff()) pTarget->m_buffCount--; pTarget->m_buffMap.erase(itr); switch (byBuffType) { case BUFF_TYPE_HP_MP: pTarget->m_sMaxHPAmount = 0; pTarget->m_sMaxMPAmount = 0; break; case BUFF_TYPE_AC: if (pType->sAC == 0 && pType->sACPct > 0) pTarget->m_sACPercent -= (pType->sACPct - 100); else pTarget->m_sACAmount -= pType->sAC; break; case BUFF_TYPE_SIZE: pTarget->StateChangeServerDirect(3, ABNORMAL_NORMAL); break; case BUFF_TYPE_INC_CONTRIBUTION: break; case BUFF_TYPE_IMIR_ROAR: break; case BUFF_TYPE_LOGOS_HORNS: break; case BUFF_TYPE_UNKNOW: break; case BUFF_TYPE_DROP_RATE: break; case BUFF_TYPE_MAMA_MAGPIE: pTarget->StateChangeServerDirect(3, TO_USER(pTarget)->m_nOldAbnormalType); break; case BUFF_TYPE_ARMORED: //Amored Skill, Dark Knight Staff 300-AC by Terry pTarget->m_sACAmount -= pType->sAC; break; case BUFF_TYPE_ATTACK_AMMONUT2: if (pType->bAttack > 100) TO_USER(pTarget)->m_bAttackAmount -= (pType->bAttack - 100); else TO_USER(pTarget)->m_bAttackAmount += (100 - pType->bAttack); break; case BUFF_TYPE_DAMAGE: pTarget->m_bAttackAmount -= (pType->bAttack - 100); break; case BUFF_TYPE_ATTACK_SPEED: pTarget->m_sAttackSpeedAmount -= (pType->bAttackSpeed - 100); break; case BUFF_TYPE_SPEED: pTarget->m_bSpeedAmount = 100; break; case BUFF_TYPE_BATTLE_CRY: case BUFF_TYPE_STATS: if (pTarget->isPlayer()) { TO_USER(pTarget)->RemoveStatBuff(STAT_STR, pType->bStr); TO_USER(pTarget)->RemoveStatBuff(STAT_STA, pType->bSta); TO_USER(pTarget)->RemoveStatBuff(STAT_DEX, pType->bDex); TO_USER(pTarget)->RemoveStatBuff(STAT_INT, pType->bIntel); TO_USER(pTarget)->RemoveStatBuff(STAT_CHA, pType->bCha); } break; case BUFF_TYPE_RESISTANCES: pTarget->m_bAddFireR = 0; pTarget->m_bAddColdR = 0; pTarget->m_bAddLightningR = 0; pTarget->m_bAddMagicR = 0; pTarget->m_bAddDiseaseR = 0; pTarget->m_bAddPoisonR = 0; break; case BUFF_TYPE_ACCURACY: pTarget->m_bHitRateAmount = 100; pTarget->m_sAvoidRateAmount = 100; break; case BUFF_TYPE_MAGIC_POWER: pTarget->m_sMagicAttackAmount = 0; break; case BUFF_TYPE_EXPERIENCE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_sExpGainAmount = 100; break; case BUFF_TYPE_FISHING: break; case BUFF_TYPE_WEIGHT: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bMaxWeightAmount = 100; break; case BUFF_TYPE_WEAPON_DAMAGE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bAddWeaponDamage = 0; break; case BUFF_TYPE_WEAPON_AC: if (pType->sAC == 0 && pType->sACPct > 0) TO_USER(pTarget)->m_bPctArmourAc -= (pType->sACPct - 100); else TO_USER(pTarget)->m_sAddArmourAc -= pType->sAC; break; case BUFF_TYPE_LOYALTY: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bNPGainAmount = 100; break; case BUFF_TYPE_NOAH_BONUS: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bNoahGainAmount = 100; break; case BUFF_TYPE_PREMIUM_MERCHANT: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bPremiumMerchant = false; break; case BUFF_TYPE_ATTACK_SPEED_ARMOR: pTarget->m_sACAmount -= pType->sAC; if (pType->bAttack > 100) pTarget->m_bAttackAmount -= (pType->bAttack - 100); else pTarget->m_bAttackAmount -= pType->bAttack; break; case BUFF_TYPE_DAMAGE_DOUBLE: if (pTarget->isPlayer()) TO_USER(pTarget)->m_bPlayerAttackAmount = 100; break; case BUFF_TYPE_DISABLE_TARGETING: pTarget->m_bIsBlinded = false; break; case BUFF_TYPE_BLIND: // Only players can be blinded (at least by the only skill - "Blinding Strafe" - that uses this type). if (pTarget->isPlayer()) { pTarget->m_bIsBlinded = false; TO_USER(pTarget)->SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE); } break; case BUFF_TYPE_FREEZE: // Proportional to the target user's current HP. pTarget->m_bBlockMagic = false; // Remove Blockes Magical Damage - By Terry pTarget->m_bSpeedAmount = 100; break; case BUFF_TYPE_INSTANT_MAGIC: pTarget->m_bInstantCast = false; break; case BUFF_TYPE_DECREASE_RESIST: pTarget->m_bPctFireR = 100; pTarget->m_bPctColdR = 100; pTarget->m_bPctLightningR = 100; pTarget->m_bPctMagicR = 100; pTarget->m_bPctDiseaseR = 100; pTarget->m_bPctPoisonR = 100; break; case BUFF_TYPE_MAGE_ARMOR: pTarget->m_bReflectArmorType = 0; break; case BUFF_TYPE_PROHIBIT_INVIS: pTarget->m_bCanStealth = true; break; case BUFF_TYPE_RESIS_AND_MAGIC_DMG: // Elysian Web pTarget->m_bMagicDamageReduction = 100; if(pTarget->isPlayer()) TO_USER(pTarget)->SendUserStatusUpdate(USER_STATUS_POISON,USER_STATUS_CURE); break; case BUFF_TYPE_TRIPLEAC_HALFSPEED: // Wall of Iron switch (pSkill->iNum) { case 115820: pTarget->m_sACPercent -= (pType->sACPct - 100); pTarget->m_bSpeedAmount = 100; break; case 215820: pTarget->m_sACPercent -= (pType->sACPct - 100); pTarget->m_bSpeedAmount = 100; break; default: pTarget->m_sACPercent -= 300; // 300%, or 3x pTarget->m_bSpeedAmount = 100; } break; case BUFF_TYPE_BLOCK_CURSE: // Counter Curse pTarget->m_bBlockCurses = false; break; case BUFF_TYPE_BLOCK_CURSE_REFLECT: // Curse Refraction pTarget->m_bReflectCurses = false; break; case BUFF_TYPE_MANA_ABSORB: // Outrage / Frenzy / Mana Shield pTarget->m_bManaAbsorb = 0; pTarget->AbsorbCount = 0; break; case BUFF_TYPE_IGNORE_WEAPON: // Weapon cancellation #if defined(GAMESERVER) if (pTarget->isPlayer()) { CUser * pTUser = TO_USER(pTarget); _ITEM_DATA * pLeftItem, * pRightItem; _ITEM_TABLE * pLeftHand = pTUser->GetItemPrototype(LEFTHAND, pLeftItem), * pRightHand = pTUser->GetItemPrototype(RIGHTHAND, pRightItem); pTUser->m_bWeaponsDisabled = false; if (pLeftHand != nullptr) pTUser->UserLookChange(LEFTHAND, pLeftItem->nNum, pLeftItem->sDuration); if (pRightHand != nullptr) pTUser->UserLookChange(RIGHTHAND, pRightItem->nNum, pRightItem->sDuration); } #endif break; case BUFF_TYPE_VARIOUS_EFFECTS: //... whatever the event item grants. // what is tweaked in the database: AC, Attack, MaxHP, resistances // AC if (pType->sAC == 0 && pType->sACPct > 100) pTarget->m_sACPercent -= (pType->sACPct - 100); else if (pType->sAC > 0 && pType->sACPct == 100) pTarget->m_sACAmount -= pType->sAC; // Attack if (pType->bAttack > 100) pTarget->m_bAttackAmount -= (pType->bAttack - 100); // NP Bonus if (pTarget->isPlayer() && pType->sSpecialAmount > 0) TO_USER(pTarget)->m_bSkillNPBonus -= uint8(pType->sSpecialAmount); break; case BUFF_TYPE_PASSION_OF_SOUL: // Passion of the Soul // Increase pet's HP by 120 break; case BUFF_TYPE_FIRM_DETERMINATION: // Firm Determination // Increase pet's AC by 20 break; case BUFF_TYPE_ATTACK_MAGIC_ATTACK: pTarget->m_sMagicAttackAmount -= (pType->bMagicAttack - 100); pTarget->m_bAttackAmount -= (pType->bAttack - 100); break; case BUFF_TYPE_ATTACK_TIME: pTarget->m_bAttackAmount -= (pType->bAttack - 100); break; case BUFF_TYPE_SPEED2: // Cold Wave #if defined(GAMESERVER) // update the target data in the AI server. TO_USER(pTarget)->m_bSpeedAmount = 100; TO_USER(pTarget)->m_bIceSpeedAmount = 0; #elif defined(AI_SERVER) TO_USER(pTarget)->m_bSpeedAmount = 100; TO_USER(pTarget)->m_bIceSpeedAmount = 0; #endif break; case BUFF_TYPE_UNK_EXPERIENCE: // unknown buff type, used for something relating to XP. break; case BUFF_TYPE_ATTACK_RANGE_ARMOR: // Inevitable Murderous pTarget->m_sACAmount -= 100; pTarget->m_bRadiusAmount = 0; break; case BUFF_TYPE_MIRROR_DAMAGE_PARTY: // Minak's Thorn pTarget->m_bMirrorDamage = false; pTarget->m_byMirrorAmount = 0; break; case BUFF_TYPE_DAGGER_BOW_DEFENSE: // Eskrima // Inflicts attacks as well as a bleeding curse on the enemy. Decreases 10% Dagger and Bow Defense of the enemy under the bleeding curse buff. pTarget->m_byDaggerRAmount -= (uint8) (pType->sSpecialAmount / 2); pTarget->m_byBowRAmount -= (uint8) (pType->sSpecialAmount / 2); // note: overwrite the percentage for now (nothing else uses it) break; case BUFF_TYPE_DEVIL_TRANSFORM: // Devil Transform pTarget->m_bIsDevil = false; pTarget->AbsorbedAmmount = 0; pTarget->StateChangeServerDirect(12, 0); break; case BUFF_TYPE_STUN : // Lighting Skill pTarget->m_bSpeedAmount = 100; break; case BUFF_TYPE_LOYALTY_AMOUNT: // Santa's Present (gives an extra +2NP per kill, unlike BUFF_TYPE_LOYALTY which uses an percent). if (pTarget->isPlayer()) TO_USER(pTarget)->m_bSkillNPBonus -= 2; break; case BUFF_TYPE_NO_RECALL: // prevents teleportation. pTarget->m_bCanTeleport = true; break; case BUFF_TYPE_REDUCE_TARGET: // "Reduction" (reduces target's stats, but enlarges their character to make them easier to attack) // NOTE: Skill description says "Enlarge enemy, but decrease attack and defense rate by 5%" // There's nothing else set in the client to give those stats, and AC's reduced by 15% according to the data... // Just working with the TBL data for now (i.e. just the 15% AC reduction). if (pTarget->isPlayer()) { pTarget->StateChangeServerDirect(3, ABNORMAL_NORMAL); pTarget->m_sACPercent -= (pType->sACPct - 100); } break; case BUFF_TYPE_SILENCE_TARGET: // Silences the target to prevent them from using any skills (or potions) pTarget->m_bCanUseSkills = true; break; case BUFF_TYPE_NO_POTIONS: // "No Potion" prevents target from using potions. pTarget->m_bCanUsePotions = true; break; case BUFF_TYPE_KAUL_TRANSFORMATION: // Transforms the target into a Kaul (a pig thing), preventing you from /town'ing or attacking, but increases defense. if (pTarget->isPlayer()) { pTarget->m_bIsKaul = false; pTarget->m_sACAmount -= 500; pTarget->StateChangeServerDirect(3, TO_USER(pTarget)->m_nOldAbnormalType); } break; case BUFF_TYPE_UNDEAD: // User becomes undead, increasing defense but preventing the use of potions and converting all health received into damage. pTarget->m_bIsUndead = false; pTarget->m_sACPercent -= (pType->sACPct - 100); break; case BUFF_TYPE_UNSIGHT: // Blocks the caster's sight (not the target's). pTarget->m_bIsBlinded = false; break; case BUFF_TYPE_BLOCK_PHYSICAL_DAMAGE: // Blocks all physical damage. pTarget->m_bBlockPhysical = false; break; case BUFF_TYPE_BLOCK_MAGICAL_DAMAGE: // Blocks all magical/skill damage. pTarget->m_bBlockMagic = false; break; case BUFF_TYPE_UNK_POTION: // unknown potion, "Return of the Warrior", "Comeback potion", perhaps some sort of revive? break; case BUFF_TYPE_SLEEP: // Zantman(Sandman), puts enemies to sleep. break; case BUFF_TYPE_INVISIBILITY_POTION: // "Unidentified potion" break; case BUFF_TYPE_GODS_BLESSING: // Increases your defense/max HP break; case BUFF_TYPE_HELP_COMPENSATION: // Compensation for using the help system (to help, ask for help, both?) break; default: return false; } if (pTarget->isPlayer()) { if (pSkill->bMoral >= MORAL_ENEMY) { if (byBuffType == BUFF_TYPE_SPEED || byBuffType == BUFF_TYPE_SPEED2) TO_USER(pTarget)->SendUserStatusUpdate(USER_STATUS_SPEED, USER_STATUS_CURE); } TO_USER(pTarget)->SetUserAbility(); if (bRemoveSavedMagic) { Packet result(WIZ_MAGIC_PROCESS, uint8(MAGIC_DURATION_EXPIRED)); result << byBuffType; TO_USER(pTarget)->Send(&result); } } #if defined(GAMESERVER) // update the target data in the AI server. if (bRecastSavedMagic && TO_USER(pTarget)->isLockableScroll(pType->bBuffType)) { TO_USER(pTarget)->SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE); TO_USER(pTarget)->RecastLockableScrolls(pType->bBuffType); } UpdateAIServer(pType->iNum, AISkillOpcodeRemoveBuff, pTarget); #endif return true; } /** * @brief Test if the specified skill is a buff * or a debuff. * * @param pSkill The specified skill. * * @return true if skill is a buff, false if debuff. */ bool CMagicProcess::IsBuff(_MAGIC_TYPE4 * pType) { switch (pType->bBuffType) { case BUFF_TYPE_NONE: // used by things like firecrackers. return true; case BUFF_TYPE_HP_MP: if (pType->sMaxHP > 0 || pType->sMaxMP > 0) return true; // If either the max HP percent or max MP percent are less than 100% // it is a debuff we are dealing with, not a buff. return (pType->sMaxHPPct >= 100 && pType->sMaxMPPct >= 100); case BUFF_TYPE_AC: if (pType->sAC == 0 && pType->sACPct > 0) return pType->sACPct >= 100; else return pType->sAC >= 0; // Size changes (via Bezoars, Rice cakes, etc) // are buffs. case BUFF_TYPE_SIZE: return true; case BUFF_TYPE_DAMAGE: return pType->bAttack >= 100; case BUFF_TYPE_ATTACK_SPEED: return pType->bAttackSpeed >= 100; case BUFF_TYPE_SPEED: return pType->bSpeed >= 100; // If any of the stats are below 0, it's a debuff. case BUFF_TYPE_BATTLE_CRY: case BUFF_TYPE_STATS: return !(pType->bStr < 0 || pType->bSta < 0 || pType->bDex < 0 || pType->bIntel < 0 || pType->bCha < 0); // There are no skills that negatively affect resistances, so it will always be a buff. case BUFF_TYPE_RESISTANCES: return true; case BUFF_TYPE_ACCURACY: return (pType->bHitRate >= 100 && pType->sAvoidRate >= 100); case BUFF_TYPE_MAGIC_POWER: return pType->bMagicAttack >= 100; case BUFF_TYPE_EXPERIENCE: case BUFF_TYPE_WEIGHT: return true; case BUFF_TYPE_WEAPON_DAMAGE: return pType->bAttack > 0; case BUFF_TYPE_WEAPON_AC: if (pType->sAC == 0 && pType->sACPct > 0) return pType->sACPct >= 100; else return pType->sAC > 0; case BUFF_TYPE_LOYALTY: case BUFF_TYPE_NOAH_BONUS: case BUFF_TYPE_PREMIUM_MERCHANT: case BUFF_TYPE_ATTACK_SPEED_ARMOR: // only used by "Berserk Echo", which is a buff. case BUFF_TYPE_DAMAGE_DOUBLE: return true; case BUFF_TYPE_DISABLE_TARGETING: case BUFF_TYPE_BLIND: case BUFF_TYPE_FREEZE: return false; case BUFF_TYPE_INSTANT_MAGIC: return true; case BUFF_TYPE_DECREASE_RESIST: return false; case BUFF_TYPE_DEVIL_TRANSFORM: case BUFF_TYPE_MAGE_ARMOR: case BUFF_TYPE_PROHIBIT_INVIS: case BUFF_TYPE_RESIS_AND_MAGIC_DMG: // Elysian Web case BUFF_TYPE_TRIPLEAC_HALFSPEED: // Wall of Iron case BUFF_TYPE_BLOCK_CURSE: // Counter Curse case BUFF_TYPE_BLOCK_CURSE_REFLECT: // Curse Refraction case BUFF_TYPE_MANA_ABSORB: // Outrage / Frenzy / Mana Shield case BUFF_TYPE_ARMORED: return true; case BUFF_TYPE_IMIR_ROAR: case BUFF_TYPE_LOGOS_HORNS: case BUFF_TYPE_MAMA_MAGPIE: case BUFF_TYPE_ATTACK_AMMONUT2: return true; case BUFF_TYPE_IGNORE_WEAPON: // Weapon cancellation // Disarms the opponent. (rendering them unable to attack) return false; case BUFF_TYPE_VARIOUS_EFFECTS: //... whatever the event item grants. return true; // everything seems positive, however it might be prudent to have this check. case BUFF_TYPE_PASSION_OF_SOUL: // Passion of the Soul case BUFF_TYPE_FIRM_DETERMINATION: // Firm Determination case BUFF_TYPE_ATTACK_MAGIC_ATTACK: case BUFF_TYPE_ATTACK_TIME: return true; case BUFF_TYPE_SPEED2: // Cold Wave // skill explicitly slows return false; case BUFF_TYPE_UNK_EXPERIENCE: //unknown buff type, used for something relating to XP. return true; case BUFF_TYPE_ATTACK_RANGE_ARMOR: // Inevitable Murderous case BUFF_TYPE_MIRROR_DAMAGE_PARTY: // Minak's Thorn return true; case BUFF_TYPE_DAGGER_BOW_DEFENSE: // Eskrima return false; case BUFF_TYPE_STUN: // Lighting Skill Stun case BUFF_TYPE_DRAKEY: case BUFF_TYPE_SPEED3: case BUFF_TYPE_GM_BUFF: return false; case BUFF_TYPE_UNKNOW: // unknown, assume debuff for now. case BUFF_TYPE_DROP_RATE: case BUFF_TYPE_INC_CONTRIBUTION: return false; case BUFF_TYPE_LOYALTY_AMOUNT: // Santa's Present (gives an extra +2NP per kill). return true; case BUFF_TYPE_NO_RECALL: // prevents teleportation. case BUFF_TYPE_REDUCE_TARGET: // "Reduction" (reduces target's stats, but enlarges their character to make them easier to attack) case BUFF_TYPE_SILENCE_TARGET: // Silences the target to prevent them from using any skills (or potions) case BUFF_TYPE_NO_POTIONS: // "No Potion" prevents target from using potions. case BUFF_TYPE_KAUL_TRANSFORMATION: // Transforms the target into a Kaul (a pig thing), preventing you from /town'ing or attacking, but increases defense. return false; case BUFF_TYPE_UNDEAD: // User becomes undead, increasing defense but preventing the use of potions and converting all health received into damage. case BUFF_TYPE_UNSIGHT: // Blocks the caster's sight (not the target's). return false; case BUFF_TYPE_BLOCK_PHYSICAL_DAMAGE: // Blocks all physical damage. case BUFF_TYPE_BLOCK_MAGICAL_DAMAGE: // Blocks all magical/skill damage. case BUFF_TYPE_UNK_POTION: // unknown potion, "Return of the Warrior", "Comeback potion", perhaps some sort of revive? return true; case BUFF_TYPE_SLEEP: // Zantman(Sandman), puts enemies to sleep. return false; case BUFF_TYPE_INVISIBILITY_POTION: // "Unidentified potion", it debuffs, but hides in the interest of the user. Needs to be a buff. case BUFF_TYPE_GODS_BLESSING: // Increases your defense/max HP case BUFF_TYPE_HELP_COMPENSATION: // Compensation for using the help system (to help, ask for help, both?) return true; // TODO: Identify and name these. case BUFF_TYPE_FISHING: // DC/War/Exp Flash - grants additional NP/XP return true; } printf("WARNING: Unhandled buff type (%d) for skill %d, assuming it's a debuff.\n", pType->bBuffType, pType->iNum); return false; }