public static void Handle(MapleClient c, PacketReader packet) { MapleCharacter chr = c.Account.Character; if (!chr.DisableActions()) { return; } try { int tickCount = packet.ReadInt(); int skillId = packet.ReadInt(); byte amount = packet.ReadByte(); //new v137, multiple sp if (amount <= 0) { amount = 1; } short skillJobId = (short)(skillId / 10000); WzCharacterSkill wzSkill = DataBuffer.GetCharacterSkillById(skillId); if (wzSkill == null) { return; } if (wzSkill.IsGmSkill && !chr.IsStaff) { return; } if (!JobConstants.JobCanLearnSkill(skillId, chr.Job)) { return; } if (wzSkill.HasFixedLevel) { return; } if (chr.Level < wzSkill.RequiredLevel) { return; } if (wzSkill.RequiredSkills != null) { foreach (var kvp in wzSkill.RequiredSkills) { if (!chr.HasSkill(kvp.Key, kvp.Value)) { return; } } } if (wzSkill.IsHyperSkill) { chr.SendPopUpMessage("Hyper skills aren't functional yet."); chr.EnableActions(); return; } if (JobConstants.IsBeginnerJob(skillJobId)) { switch (skillId) { //Three snails: case Explorer.THREE_SNAILS: case Cygnus.THREE_SNAILS: case AranBasics.THREE_SNAILS: case EvanBasics.THREE_SNAILS: case MihileBasics.THREE_SNAILS: //Recovery: case Explorer.RECOVERY: case Cygnus.RECOVERY: case AranBasics.RECOVERY: case EvanBasics.RECOVER: case MihileBasics.RECOVERY: //Nimble Feet: case Explorer.NIMBLE_FEET: case Cygnus.NIMBLE_FEET: case AranBasics.AGILE_BODY: case EvanBasics.NIMBLE_FEET: case MihileBasics.NIMBLE_FEET: //Resistance: case Resistance.POTION_MASTERY: case Resistance.INFILTRATE: case Resistance.CRYSTAL_THROW: if (chr.GetSkillLevel(skillId) + amount > 3) //already maxed { return; } int baseNum = 0; switch (skillId / 100000) { case 300: //resistance { if (!chr.IsResistance) { return; } int usedBeginnerSP = chr.GetSkillLevel(Resistance.CRYSTAL_THROW) + chr.GetSkillLevel(Resistance.INFILTRATE) + chr.GetSkillLevel(Resistance.POTION_MASTERY); if (usedBeginnerSP + amount <= 9 && chr.GetSkillLevel(skillId) + amount <= 3) { chr.IncreaseSkillLevel(skillId, amount); } break; } case 0: if (!chr.IsExplorer) { return; } goto common; case 100: //cygnus if (!chr.IsCygnus) { return; } baseNum = 10000000; goto common; case 200: //hero if (!chr.IsAran && !chr.IsEvan) { return; } baseNum = 20000000; goto common; case 500: //mihile if (!chr.IsMihile) { return; } baseNum = 50000000; common: { int usedBeginnerSP = chr.GetSkillLevel(baseNum + 1000) + chr.GetSkillLevel(baseNum + 1001) + chr.GetSkillLevel(baseNum + 1002); if (usedBeginnerSP + amount <= 6 && chr.GetSkillLevel(skillId) + amount <= 3) { chr.IncreaseSkillLevel(skillId, amount); } break; } default: return; } break; default: return; } } else { int spTableIndex = JobConstants.GetSkillBookForJob(skillJobId); if (chr.SpTable[spTableIndex] >= amount) { Skill skill = chr.GetSkill(skillId); if (skill == null) //Player doesnt have the skill yet { int maxLevel = wzSkill.HasMastery ? wzSkill.DefaultMastery : wzSkill.MaxLevel; if (amount <= maxLevel) { chr.LearnSkill(skillId, amount, wzSkill); chr.SpTable[spTableIndex] -= amount; MapleCharacter.UpdateSingleStat(c, MapleCharacterStat.Sp, chr.SpTable[0], true); } } else { if (skill.Level + amount <= skill.MasterLevel) { chr.IncreaseSkillLevel(skillId, amount); chr.SpTable[spTableIndex] -= amount; MapleCharacter.UpdateSingleStat(c, MapleCharacterStat.Sp, chr.SpTable[0], true); } } } } } finally { chr.EnableActions(false); } }
public static bool CheckAndApplySkillEffect(MapleCharacter chr, int skillId, WzCharacterSkill wzCharacterSkill, int skillLevel = -1, int numTargets = 0, int numAttacks = 0) { if (skillLevel == -1) { skillLevel = chr.GetSkillLevel(skillId); } if (wzCharacterSkill == null) { wzCharacterSkill = DataBuffer.GetCharacterSkillById(skillId); if (wzCharacterSkill == null) { return(false); } } if (wzCharacterSkill.HasFixedLevel && JobConstants.JobCanLearnSkill(skillId, chr.Job)) { skillLevel = 1; } if (chr.IsPhantom) //check stolen skill level { PhantomSystem resource = (PhantomSystem)chr.Resource; int chosenSkillIndex = resource.GetChosenSkillIndex(skillId); if (chosenSkillIndex > -1) { int impeccableMemory = PhantomSystem.GetStealSkill(chosenSkillIndex + 1); skillLevel = Math.Min(chr.GetSkillLevel(impeccableMemory), chr.GetSkillLevel(skillId)); } } if (skillLevel == 0 || (chr.HasSkillOnCooldown(skillId))) { string text = "Player tried using skill " + skillId + " while level 0 or on cooldown."; ServerConsole.Warning(text); FileLogging.Log("./LinkedSkills.txt", text); return(false); } SkillEffect effect = wzCharacterSkill.GetEffect((byte)skillLevel); if (effect == null) { return(false); } bool shadowPartner = false; if (numTargets > 0) { int attackCount = effect.AttackCount; if (chr.IsLuminous || (chr.IsBandit && chr.HasBuff(ChiefBandit.SHADOW_PARTNER)) || (chr.IsAssassin && chr.HasBuff(Hermit.SHADOW_PARTNER)) || (chr.IsNightWalker && chr.HasBuff(NightWalker3.SHADOW_PARTNER))) { attackCount *= 2; shadowPartner = true; } if (effect.MobCount < numTargets || attackCount < numAttacks) { return(false); } } int bulletConsume; if (effect.Info.TryGetValue(CharacterSkillStat.bulletConsume, out bulletConsume)) { if (shadowPartner) { bulletConsume *= 2; } if (!DealDamageHandler.HandleRangedAttackAmmoUsage(chr, bulletConsume)) { ServerConsole.Warning("Character with job: " + chr.Job + " tried using a skill with bulletCount: " + bulletConsume + " but doesn't have the bullets!"); return(false); } } if (chr.Mp < effect.MpCon) { return(false); } else { chr.AddMP(-effect.MpCon); } int hpCon; if (effect.Info.TryGetValue(CharacterSkillStat.hpCon, out hpCon)) { if (chr.Hp < hpCon) { return(false); } chr.AddHP(-hpCon); } #region Manual skill handlers and checks if (chr.IsAran && effect.Info.ContainsKey(CharacterSkillStat.aranComboCon)) { if (!AranSystem.HandleComboUsage(chr, effect.Info[CharacterSkillStat.aranComboCon])) { return(false); } } else if (chr.IsLuminous && chr.Job >= JobConstants.LUMINOUS2 && effect.Info.ContainsKey(CharacterSkillStat.gauge)) { LuminousSystem.HandleGaugeGain(chr, skillId, effect.Info[CharacterSkillStat.gauge]); } switch (skillId) { case Berserker.EVIL_EYE_OF_DOMINATION: { Buff evilEyeBuff = chr.GetBuff(Spearman.EVIL_EYE); MapleSummon evilEye = chr.GetSummon(Spearman.EVIL_EYE); if (evilEyeBuff == null || evilEye == null) { return(false); } uint timeUsed = (uint)((DateTime.UtcNow.Subtract(evilEyeBuff.StartTime)).TotalMilliseconds); uint timeRemainingMS = (uint)evilEyeBuff.Duration - timeUsed; Buff newBuff = new Buff(Spearman.EVIL_EYE, effect, timeRemainingMS, chr); if (evilEyeBuff.Stacks == Berserker.EVIL_EYE_OF_DOMINATION) { newBuff.Stacks = Spearman.EVIL_EYE; evilEye.MovementType = SummonMovementType.Follow; } else { newBuff.Stacks = Berserker.EVIL_EYE_OF_DOMINATION; evilEye.MovementType = SummonMovementType.CircleFollow; } chr.GiveBuff(newBuff); return(true); //no other actions needed } case Berserker.EVIL_EYE_SHOCK: { MapleSummon evilEye = chr.GetSummon(Spearman.EVIL_EYE); if (evilEye == null) { return(false); } List <MapleMonster> mobs = chr.Map.GetMobsInRange(new BoundingBox(evilEye.Position, wzCharacterSkill.TopLeft, wzCharacterSkill.BottomRight)); if (mobs.Count > 0) { int damage = (int)((effect.Info[CharacterSkillStat.damage] / 100.0) * chr.Stats.GetDamage()); int stunProp = effect.Info[CharacterSkillStat.prop]; int stunTime = effect.Info[CharacterSkillStat.time] * 1000; int mobCounter = 0; foreach (MapleMonster mob in mobs) { mob.Damage(chr, damage); if (mob.Alive) { if (Functions.MakeChance(stunProp)) { mob.ApplyStatusEffect(skillId, MonsterBuffStat.STUN, 1, stunTime, chr); } } mobCounter++; if (mobCounter == 10) { break; } } } break; } case DarkKnight.SACRIFICE: if (!chr.RemoveSummon(Spearman.EVIL_EYE)) { return(false); } chr.CancelBuff(Spearman.EVIL_EYE); int healHpR = effect.Info[CharacterSkillStat.y]; int heal = (int)((healHpR / 100.0) * chr.Stats.MaxHp); chr.AddHP(heal); break; case LuminousBasics.SUNFIRE: case LuminousBasics.ECLIPSE: case LuminousBasics.EQUILIBRIUM2: LuminousSystem.HandleChangeDarkLight(chr, skillId); break; } #endregion #region Apply Cooldown bool skipCooldown = skillId == DarkKnight.GUNGNIRS_DESCENT && (chr.HasBuff(DarkKnight.SACRIFICE) || chr.HasBuff(DarkKnight.FINAL_PACT2)); if (!skipCooldown) { int coolTime; if (effect.Info.TryGetValue(CharacterSkillStat.cooltime, out coolTime) && coolTime > 0) { chr.AddCooldown(skillId, (uint)coolTime * 1000); //time in the wz is in seconds } } #endregion effect.ApplyEffect(chr, chr); if (wzCharacterSkill.IsBuff) { effect.ApplyBuffEffect(chr); chr.Map.BroadcastPacket(SkillEffect.Packets.ShowForeignSkillEffect(chr.Id, chr.Level, skillId, effect.Level), chr); } if (wzCharacterSkill.IsPartySkill) { if (chr.Party != null) { List <MapleCharacter> partyMembersOnSameMap = chr.Party.GetCharactersOnMap(chr.Map, chr.Id); if (partyMembersOnSameMap.Count > 0) { List <MapleCharacter> partyMembersInRange = chr.Map.GetCharactersInRange(effect.CalculateBoundingBox(chr.Position, chr.IsFacingLeft), partyMembersOnSameMap); foreach (MapleCharacter partyMember in partyMembersInRange) { effect.ApplyEffect(chr, partyMember); if (wzCharacterSkill.IsBuff) { effect.ApplyBuffEffect(partyMember); } } } } else if (wzCharacterSkill.IsGmSkill && chr.IsStaff) { var targets = chr.Map.GetCharactersInRange(effect.CalculateBoundingBox(chr.Position, chr.IsFacingLeft)); foreach (MapleCharacter target in targets.Where(x => x.Id != chr.Id)) { effect.ApplyEffect(chr, target); if (wzCharacterSkill.IsBuff) { effect.ApplyBuffEffect(target); } } } } return(true); }