/// <summary> /// Sends SkillInfo to creature's client. /// </summary> /// <param name="client"></param> /// <param name="creature"></param> /// <param name="skill"></param> public static void SkillInfo(Creature creature, Skill skill) { var packet = new Packet(Op.SkillInfo, creature.EntityId); packet.PutBin(skill.Info); creature.Client.Send(packet); }
public void SetCooldown(Skill skill, DateTime endTime) { if (this._cooldownDictionary.ContainsKey(skill.Info.Id)) this._cooldownDictionary[skill.Info.Id] = endTime; else this._cooldownDictionary.Add(skill.Info.Id, endTime); }
/// <summary> /// Sends SkillRankUp to creature's client. /// </summary> /// <param name="client"></param> /// <param name="creature"></param> /// <param name="skill"></param> public static void SkillRankUp(Creature creature, Skill skill) { var packet = new Packet(Op.SkillRankUp, creature.EntityId); packet.PutByte(1); packet.PutBin(skill.Info); packet.PutFloat(0); creature.Client.Send(packet); }
/// <summary> /// Adds skill silently. Returns false if the skill already exists, /// with a rank that's equal or higher. /// </summary> /// <param name="skill"></param> public bool Add(Skill skill) { if (this.Has(skill.Info.Id, skill.Info.Rank)) return false; lock (_skills) _skills[skill.Info.Id] = skill; this.AddBonuses(skill); return true; }
/// <summary> /// Adds skill silently. Returns false if the skill already exists, /// with a rank that's equal or higher. /// </summary> /// <param name="skill"></param> public bool Add(Skill skill) { // Cancel if skill exists with equal or higher rank. if (this.Has(skill.Info.Id, skill.Info.Rank)) return false; var oldSkill = this.Get(skill.Info.Id); lock (_skills) _skills[skill.Info.Id] = skill; // Remove previous bonuses if skill is replaced if (oldSkill != null) this.RemoveBonuses(oldSkill); // Add new bonuses this.AddBonuses(skill); return true; }
/// <summary> /// Cancels active skill. /// </summary> /// <remarks> /// SkillCancel is sent in any case, even if something goes wrong, /// like the method not being implemented. Unless no skill is active. /// </remarks> public void CancelActiveSkill() { if (this.ActiveSkill == null) { Log.Warning("CancelActiveSkill: Player '{0}' tried to cancel a skill, without one being active.", _creature.Name); return; } var handler = ChannelServer.Instance.SkillManager.GetHandler<ICancelable>(this.ActiveSkill.Info.Id); if (handler == null) { Log.Unimplemented("CancelActiveSkill: Skill handler or interface for '{0}'.", this.ActiveSkill.Info.Id); goto L_Cancel; } try { handler.Cancel(_creature, this.ActiveSkill); } catch (NotImplementedException) { Log.Unimplemented("CancelActiveSkill: Skill cancel method for '{0}'.", this.ActiveSkill.Info.Id); goto L_Cancel; } L_Cancel: this.ActiveSkill.Stacks = 0; Send.SkillCancel(_creature); this.ActiveSkill.State = SkillState.Canceled; this.ActiveSkill = null; _creature.Unlock(Locks.All); }
/// <summary> /// Returns party production bonus for the given skill if creature /// is in a party. /// </summary> /// <remarks> // http://wiki.mabinogiworld.com/view/Party#Production_Bonus /// </remarks> /// <param name="skill"></param> /// <returns></returns> public float GetProductionPartyBonus(Skill skill) { // No bonus when not in party if (!this.IsInParty) return 0; var result = 0f; var members = this.Party.GetMembers(); foreach (var member in members) { // Exclude this creature if (member == this) continue; // Exclude members that don't have Production Master rF+ var productionMastery = member.Skills.Get(SkillId.ProductionMastery); if (productionMastery == null || productionMastery.Info.Rank < SkillRank.RF) continue; // Exclude members that don't have the production skill on rF+ var memberSkill = member.Skills.Get(skill.Info.Id); if (memberSkill == null || memberSkill.Info.Rank < SkillRank.RF) continue; // +1% if member has the skill on a lower rank if (memberSkill.Info.Rank < skill.Info.Rank) result += 1; // +5% if member has the skill on same or higher rank else result += 5; } // Cap at 35 return Math.Min(35, result); }
/// <summary> /// Adds skill at rank, or updates it. /// Sends appropriate packets. /// </summary> /// <param name="id"></param> /// <param name="rank"></param> public void Give(SkillId id, SkillRank rank) { var skill = this.Get(id); if (skill == null) { this.Add(skill = new Skill(_creature, id, rank, _creature.RaceId)); Send.SkillInfo(_creature, skill); if (_creature.Region != Region.Limbo) Send.RankUp(_creature); ChannelServer.Instance.Events.OnSkillRankChanged(_creature, skill); } else { this.RemoveBonuses(skill); skill.ChangeRank(rank); Send.SkillRankUp(_creature, skill); if (_creature.Region != Region.Limbo) Send.RankUp(_creature, skill.Info.Id); this.AddBonuses(skill); } Send.StatUpdate(_creature, StatUpdateType.Private, Stat.Str, Stat.Int, Stat.Dex, Stat.Will, Stat.Luck, Stat.Life, Stat.LifeInjured, Stat.LifeMaxMod, Stat.LifeMax, Stat.Mana, Stat.ManaMaxMod, Stat.ManaMax, Stat.Stamina, Stat.Hunger, Stat.StaminaMaxMod, Stat.StaminaMax ); Send.StatUpdate(_creature, StatUpdateType.Public, Stat.Life, Stat.LifeInjured, Stat.LifeMaxMod, Stat.LifeMax); this.RankChanged.Raise(_creature, skill); }
/// <summary> /// Calculates random base Magic damage for skill, using the given values. /// </summary> /// <remarks> /// http://wiki.mabinogiworld.com/view/Stats#Magic_Damage /// </remarks> /// <param name="skill"></param> /// <param name="baseMin"></param> /// <param name="baseMax"></param> /// <returns></returns> public float GetRndMagicDamage(Skill skill, float baseMin, float baseMax) { var rnd = RandomProvider.Get(); var baseDamage = rnd.Between(baseMin, baseMax); var factor = rnd.Between(skill.RankData.FactorMin, skill.RankData.FactorMax); var wandBonus = 0f; var chargeMultiplier = 0f; if (skill.Info.Id == SkillId.Icebolt && this.RightHand != null && this.RightHand.HasTag("/ice_wand/")) wandBonus = 5; else if (skill.Info.Id == SkillId.Firebolt && this.RightHand != null && this.RightHand.HasTag("/fire_wand/")) wandBonus = 5; else if (skill.Info.Id == SkillId.Lightningbolt && this.RightHand != null && this.RightHand.HasTag("/lightning_wand/")) wandBonus = 3.5f; if (skill.Info.Id == SkillId.Firebolt || skill.Info.Id == SkillId.IceSpear || skill.Info.Id == SkillId.HailStorm) chargeMultiplier = skill.Stacks; // TODO: Enchants var damage = (float)(baseDamage + Math.Floor(wandBonus * (1 + chargeMultiplier)) + (factor * this.MagicAttack)); return (damage * this.GetRndMagicBalance()); }
public bool IsOnCooldown(Skill skill) { if (this._cooldownDictionary.ContainsKey(skill.Info.Id)) return (DateTime.Now < this._cooldownDictionary[skill.Info.Id]); return false; }
/// <summary> /// Raised when one of the inventory's creature's skill's rank changes. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> private void OnCreatureSkillRankChanged(Creature creature, Skill skill) { this.UpdateStatBonuses(); }
/// <summary> /// Sends SkillTrainingUp to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="exp">Exp gained</param> public static void SkillTrainingUp(Creature creature, Skill skill, float exp, string bonus = "") { var packet = new Packet(Op.SkillTrainingUp, creature.EntityId); packet.PutBin(skill.Info); packet.PutFloat(exp); packet.PutByte(1); packet.PutString(bonus); // (Specialized Skill Bonus: x2) creature.Client.Send(packet); }
/// <summary> /// Sends SkillStop to creature's client or broadcasts it if skill is /// of type "BroadcastStartStop". /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="unkByte"></param> public static void SkillStop(Creature creature, Skill skill, byte unkByte) { var packet = new Packet(Op.SkillStop, creature.EntityId); packet.PutUShort((ushort)skill.Info.Id); packet.PutByte(unkByte); if (skill.Data.Type != SkillType.BroadcastStartStop) creature.Client.Send(packet); else creature.Region.Broadcast(packet, creature); }
/// <summary> /// Calculates and sets splash damage reductions and bonuses against splashTarget. /// </summary> /// <param name="splashTarget"></param> /// <param name="damageSplash"></param> /// <param name="skill"></param> /// <param name="critSkill"></param> /// <param name="aAction"></param> /// <param name="tAction"></param> /// <param name="tSplashAction"></param> public void CalculateSplashDamage(Creature splashTarget, ref float damageSplash, Skill skill, Skill critSkill, AttackerAction aAction, TargetAction tAction, TargetAction tSplashAction, Item weapon = null) { //Splash Damage Reduction if (skill.Info.Id == SkillId.Smash) damageSplash *= skill.Info.Rank < SkillRank.R1 ? 0.1f : 0.2f; else damageSplash *= weapon != null ? weapon.Data.SplashDamage : 0f; // Critical Hit if (critSkill != null && tAction.Has(TargetOptions.Critical)) { // Add crit bonus var bonus = critSkill.RankData.Var1 / 100f; damageSplash = damageSplash + (damageSplash * bonus); // Set splashTarget option tSplashAction.Set(TargetOptions.Critical); } var maxDamageSplash = damageSplash; //Damage without Defense and Protection // Subtract splashTarget def/prot SkillHelper.HandleDefenseProtection(splashTarget, ref damageSplash); // Defense Channel.Skills.Combat.Defense.Handle(aAction, tSplashAction, ref damageSplash); // Mana Shield ManaShield.Handle(splashTarget, ref damageSplash, tSplashAction, maxDamageSplash); if (damageSplash <= 0f) damageSplash = 1f; }
/// <summary> /// Updates reach rank objectives. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> private void OnSkillRankChanged(Creature creature, Skill skill) { if (this.CheckPrerequisites(creature)) creature.Quests.SendOwl(this.Id); this.CheckCurrentObjective(creature); }
/// <summary> /// Updates UseSkill objectives. /// </summary> /// <param name="args"></param> private void OnPlayerUsedSkill(Creature creature, Skill skill) { if (creature == null || skill == null) return; var quests = creature.Quests.GetAllIncomplete(this.Id); foreach (var quest in quests) { var progress = quest.CurrentObjectiveOrLast; if (progress == null) return; var objective = this.Objectives[progress.Ident]; if (objective == null || objective.Type != ObjectiveType.UseSkill) return; var useSkillObjective = (objective as QuestObjectiveUseSkill); if (!progress.Done && skill.Info.Id == useSkillObjective.Id) { quest.SetDone(progress.Ident); Send.QuestUpdate(creature, quest); } } }
/// <summary> /// Calculates random base Magic damage for skill, using the given values. /// </summary> /// <remarks> /// http://wiki.mabinogiworld.com/view/Stats#Magic_Damage /// </remarks> /// <param name="skill"></param> /// <param name="baseMin"></param> /// <param name="baseMax"></param> /// <returns></returns> public float GetRndMagicDamage(Skill skill, float baseMin, float baseMax) { var rnd = RandomProvider.Get(); // Base damage float min = baseMin; float max = baseMax; // Bonus var factor = rnd.Between(skill.RankData.FactorMin, skill.RankData.FactorMax); var totalMagicAttack = this.MagicAttack + this.MagicAttackMod; var wandBonus = 0f; var chargeMultiplier = 0f; if (skill.Info.Id == SkillId.Icebolt && this.RightHand != null && this.RightHand.HasTag("/ice_wand/")) wandBonus = 5; else if (skill.Info.Id == SkillId.Firebolt && this.RightHand != null && this.RightHand.HasTag("/fire_wand/")) wandBonus = 5; else if (skill.Info.Id == SkillId.Lightningbolt && this.RightHand != null && this.RightHand.HasTag("/lightning_wand/")) wandBonus = 3.5f; if (skill.Info.Id == SkillId.Firebolt || skill.Info.Id == SkillId.IceSpear || skill.Info.Id == SkillId.HailStorm) chargeMultiplier = skill.Stacks; var bonusDamage = (float)Math.Floor(wandBonus * (1 + chargeMultiplier)) + (factor * totalMagicAttack); min += bonusDamage; max += bonusDamage; // Random balance multiplier var multiplier = this.GetRndMagicBalance() / 100f; if (min > max) min = max; return (min + (max - min) * multiplier); }
/// <summary> /// Calculates and returns general production skill success chance. /// </summary> /// <remarks> /// Unofficial, but seems to work fine in most cases. /// Dex bonus: http://mabination.com/threads/57123-Chaos-Life-Skill-Guide-Refining /// </remarks> /// <returns></returns> public float GetProductionSuccessChance(Skill skill, ProductionCategory category, int baseChance, int rainBonus) { // Base float result = baseChance; if (skill.Info.Id != SkillId.PotionMaking && skill.Info.Id != SkillId.Milling) result += (this.Dex - 60) * (baseChance / 300f); // Production Mastery bonus var pm = this.Skills.Get(SkillId.ProductionMastery); if (pm != null) result += (byte)pm.Info.Rank; // Weather bonus if (ChannelServer.Instance.Weather.GetWeatherType(this.RegionId) == WeatherType.Rain) { if (category == ProductionCategory.Weaving) result += rainBonus * 2; else result *= 1 + (rainBonus / 100f); } // Party bonus result += this.GetProductionPartyBonus(skill); // Monday: Increase in success rate for production skills. // +10%, bonus is unofficial. if (ErinnTime.Now.Month == ErinnMonth.AlbanEiler) result += 5; return Math2.Clamp(0, 99, result); }
/// <summary> /// Updates reach rank objectives. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> private void OnSkillRankChanged(Creature creature, Skill skill) { this.CheckCurrentObjective(creature); }
/// <summary> /// Adds stat bonuses for skill's rank to creature. /// </summary> /// <param name="skill"></param> public void AddBonuses(Skill skill) { _creature.StrBaseSkill += skill.RankData.StrTotal; _creature.IntBaseSkill += skill.RankData.IntTotal; _creature.DexBaseSkill += skill.RankData.DexTotal; _creature.WillBaseSkill += skill.RankData.WillTotal; _creature.LuckBaseSkill += skill.RankData.LuckTotal; _creature.LifeMaxBaseSkill += skill.RankData.LifeTotal; _creature.Life += skill.RankData.LifeTotal; _creature.ManaMaxBaseSkill += skill.RankData.ManaTotal; _creature.Mana += skill.RankData.ManaTotal; _creature.StaminaMaxBaseSkill += skill.RankData.StaminaTotal; _creature.Stamina += skill.RankData.StaminaTotal; if (skill.Info.Id == SkillId.CombatMastery) { _creature.StatMods.Add(Stat.LifeMaxMod, skill.RankData.Var3, StatModSource.SkillRank, (int)skill.Info.Id); _creature.Life += skill.RankData.Var3; } else if (skill.Info.Id == SkillId.MagicMastery) { _creature.StatMods.Add(Stat.ManaMaxMod, skill.RankData.Var1, StatModSource.SkillRank, (int)skill.Info.Id); _creature.Mana += skill.RankData.Var1; } else if (skill.Info.Id == SkillId.Defense) { _creature.StatMods.Add(Stat.DefenseBaseMod, skill.RankData.Var1, StatModSource.SkillRank, (int)skill.Info.Id); } this.UpdateHighestSkills(); }
/// <summary> /// Calculates and returns general production skill success chance. /// </summary> /// <remarks> /// Unofficial, but seems to work fine in most cases. /// Dex bonus: http://mabination.com/threads/57123-Chaos-Life-Skill-Guide-Refining /// </remarks> /// <returns></returns> public float GetProductionSuccessChance(Skill skill, ProductionCategory category, int baseChance, int rainBonus) { // Base float result = baseChance; if (skill.Info.Id != SkillId.PotionMaking && skill.Info.Id != SkillId.Milling) result += (this.Dex - 60) * (baseChance / 300f); // Production Mastery bonus var pm = this.Skills.Get(SkillId.ProductionMastery); if (pm != null) result += (byte)pm.Info.Rank; // Weather bonus if (ChannelServer.Instance.Weather.GetWeatherType(this.RegionId) == WeatherType.Rain) { if (category == ProductionCategory.Weaving) result += rainBonus * 2; else result *= 1 + (rainBonus / 100f); } // Party bonus result += this.GetProductionPartyBonus(skill); return Math2.Clamp(0, 99, result); }
/// <summary> /// Removes stat bonuses for skill's rank from creature. /// (To be run before changing a skills rank.) /// </summary> /// <param name="skill"></param> private void RemoveBonuses(Skill skill) { _creature.StrBaseSkill -= skill.RankData.StrTotal; _creature.IntBaseSkill -= skill.RankData.IntTotal; _creature.DexBaseSkill -= skill.RankData.DexTotal; _creature.WillBaseSkill -= skill.RankData.WillTotal; _creature.LuckBaseSkill -= skill.RankData.LuckTotal; _creature.Life -= skill.RankData.LifeTotal; _creature.LifeMaxBaseSkill -= skill.RankData.LifeTotal; _creature.Mana -= skill.RankData.ManaTotal; _creature.ManaMaxBaseSkill -= skill.RankData.ManaTotal; _creature.Stamina -= skill.RankData.StaminaTotal; _creature.StaminaMaxBaseSkill -= skill.RankData.StaminaTotal; if (skill.Info.Id == SkillId.CombatMastery) { _creature.Life -= skill.RankData.Var3; _creature.StatMods.Remove(Stat.LifeMaxMod, StatModSource.SkillRank, skill.Info.Id); } else if (skill.Info.Id == SkillId.MagicMastery) { _creature.Mana -= skill.RankData.Var1; _creature.StatMods.Remove(Stat.ManaMaxMod, StatModSource.SkillRank, skill.Info.Id); } else if (skill.Info.Id == SkillId.Defense) { _creature.StatMods.Remove(Stat.DefenseBaseMod, StatModSource.SkillRank, skill.Info.Id); } this.UpdateHighestSkills(); }
/// <summary> /// Updates reach rank objectives. /// </summary> /// <param name="creature"></param> /// <param name="killer"></param> private void OnSkillRankChanged(Creature creature, Skill skill) { if (creature == null || skill == null) return; var quest = creature.Quests.Get(this.Id); if (quest == null) return; var progress = quest.CurrentObjective; if (progress == null) return; var objective = this.Objectives[progress.Ident] as QuestObjectiveReachRank; if (objective == null || objective.Type != ObjectiveType.ReachRank) return; if (skill.Info.Id != objective.Id || skill.Info.Rank < objective.Rank) return; quest.SetDone(progress.Ident); Send.QuestUpdate(creature, quest); }