/// <summary> /// Executes the style of the given player. Prints /// out messages to the player notifying him of his success or failure. /// </summary> /// <param name="living">The living executing the styles</param> /// <param name="attackData"> /// The AttackData that will be modified to contain the /// new damage and the executed style. /// </param> /// <param name="weapon">The weapon used to execute the style</param> /// <returns>true if a style was performed, false if not</returns> public static bool ExecuteStyle(GameLiving living, AttackData attackData, InventoryItem weapon) { //First thing in processors, lock the objects you modify //This way it makes sure the objects are not modified by //several different threads at the same time! GamePlayer player = living as GamePlayer; lock (living) { //Does the player want to execute a style at all? if (attackData.Style == null) return false; if (weapon != null && weapon.Object_Type == (int)eObjectType.Shield) { attackData.AnimationId = (weapon.Hand != 1) ? attackData.Style.Icon : attackData.Style.TwoHandAnimation; // 2h shield? } int fatCost = 0; if (weapon != null) fatCost = CalculateEnduranceCost(living, attackData.Style, weapon.SPD_ABS); //Reduce endurance if styled attack missed switch (attackData.AttackResult) { case GameLiving.eAttackResult.Blocked: case GameLiving.eAttackResult.Evaded: case GameLiving.eAttackResult.Missed: case GameLiving.eAttackResult.Parried: if (player != null) //No mob endu lost yet living.Endurance -= Math.Max(1, fatCost / 2); return false; } //Ignore all other attack results if (attackData.AttackResult != GameLiving.eAttackResult.HitUnstyled && attackData.AttackResult != GameLiving.eAttackResult.HitStyle) return false; //Did primary and backup style fail? if (!CanUseStyle(living, attackData.Style, weapon)) { if (player != null) { // reduce players endurance, full endurance if failed style player.Endurance -= fatCost; //"You must be hidden to perform this style!" //Print a style-fail message player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client.Account.Language, "StyleProcessor.ExecuteStyle.ExecuteFail", attackData.Style.Name), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); } return false; } else { //Style worked! Print out some nice info and add the damage! :) //Growth * Style Spec * Effective Speed / Unstyled Damage Cap bool staticGrowth = attackData.Style.StealthRequirement; //static growth is not a function of (effective) weapon speed double absorbRatio = attackData.Damage / living.UnstyledDamageCap(weapon); //scaling factor for style damage double effectiveWeaponSpeed = living.AttackSpeed(weapon) * 0.001; double styleGrowth = Math.Max(0,attackData.Style.GrowthOffset + attackData.Style.GrowthRate * living.GetModifiedSpecLevel(attackData.Style.Spec)); double styleDamageBonus = living.GetModified(eProperty.StyleDamage) * 0.01 - 1; if (staticGrowth) { if (living.AttackWeapon.Item_Type == Slot.TWOHAND) { styleGrowth = styleGrowth * 1.25 + living.WeaponDamage(living.AttackWeapon) * Math.Max(0,living.AttackWeapon.SPD_ABS - 21) * 10 / 66d; } attackData.StyleDamage = (int)(absorbRatio * styleGrowth * ServerProperties.Properties.CS_OPENING_EFFECTIVENESS); } else attackData.StyleDamage = (int)(absorbRatio * styleGrowth * effectiveWeaponSpeed); attackData.StyleDamage += (int)(attackData.Damage * styleDamageBonus); //Eden - style absorb bonus int absorb=0; if(attackData.Target is GamePlayer && attackData.Target.GetModified(eProperty.StyleAbsorb) > 0) { absorb=(int)Math.Floor((double)attackData.StyleDamage * ((double)attackData.Target.GetModified(eProperty.StyleAbsorb)/100)); attackData.StyleDamage -= absorb; } //Increase regular damage by styledamage ... like on live servers attackData.Damage += attackData.StyleDamage; if (player != null) { // reduce players endurance player.Endurance -= fatCost; if(absorb > 0) { player.Out.SendMessage("A barrier absorbs " + absorb + " damage!", eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); if(living is GamePlayer) (living as GamePlayer).Out.SendMessage("A barrier absorbs " + absorb + " damage!", eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); } } #region StyleProcs if (attackData.Style.Procs.Count > 0) { ISpellHandler effect; // If ClassID = 0, use the proc for any class, unless there is also a proc with a ClassID // that matches the player's CharacterClass.ID, or for mobs, the style's ClassID - then use // the class-specific proc instead of the ClassID=0 proc if (!attackData.Style.RandomProc) { List<Tuple<Spell, int, int>> procsToExecute = new List<Tuple<Spell, int, int>>(); bool onlyExecuteClassSpecific = false; foreach (Tuple<Spell, int, int> proc in attackData.Style.Procs) { if (player != null && proc.Item2 == player.CharacterClass.ID) { procsToExecute.Add(proc); onlyExecuteClassSpecific = true; } else if (proc.Item2 == attackData.Style.ClassID || proc.Item2 == 0) { procsToExecute.Add(proc); } } foreach (Tuple<Spell, int, int> procToExecute in procsToExecute) { if (onlyExecuteClassSpecific && procToExecute.Item2 == 0) continue; if (Util.Chance(procToExecute.Item3)) { effect = CreateMagicEffect(living, attackData.Target, procToExecute.Item1.ID); //effect could be null if the SpellID is bigger than ushort if (effect != null) { attackData.StyleEffects.Add(effect); if (attackData.Style.OpeningRequirementType == Style.eOpening.Offensive || attackData.Style.OpeningRequirementType == Style.eOpening.Defensive) { effect.UseMinVariance = true; } } } } } else { //Add one proc randomly int random = Util.Random(attackData.Style.Procs.Count - 1); //effect could be null if the SpellID is bigger than ushort effect = CreateMagicEffect(living, attackData.Target, attackData.Style.Procs[random].Item1.ID); if (effect != null) { attackData.StyleEffects.Add(effect); if (attackData.Style.OpeningRequirementType == Style.eOpening.Offensive || attackData.Style.OpeningRequirementType == Style.eOpening.Defensive) { effect.UseMinVariance = true; } } } } #endregion StyleProcs #region Animation if (weapon != null) attackData.AnimationId = (weapon.Hand != 1) ? attackData.Style.Icon : attackData.Style.TwoHandAnimation; // special animation for two-hand else if (living.Inventory != null) attackData.AnimationId = (living.Inventory.GetItem(eInventorySlot.RightHandWeapon) != null) ? attackData.Style.Icon : attackData.Style.TwoHandAnimation; // special animation for two-hand else attackData.AnimationId = attackData.Style.Icon; #endregion Animation return true; } } }