/// <summary> /// Returns the chance for creature to avoid monster attack /// </summary> public float GetEvadeChance() { // get monster attack skill var target = AttackTarget as Creature; var attackSkill = GetCreatureSkill(GetCurrentAttackSkill()).Current; var offenseMod = GetWeaponOffenseModifier(this); attackSkill = (uint)Math.Round(attackSkill * offenseMod); //if (IsExhausted) //attackSkill = GetExhaustedSkill(attackSkill); // get creature defense skill var defenseSkill = CurrentAttack == CombatType.Missile ? Skill.MissileDefense : Skill.MeleeDefense; var defenseMod = defenseSkill == Skill.MeleeDefense ? GetWeaponMeleeDefenseModifier(AttackTarget as Creature) : 1.0f; var difficulty = (uint)Math.Round(target.GetCreatureSkill(defenseSkill).Current *defenseMod); if (target.IsExhausted) { difficulty = 0; } /*var baseStr = offenseMod != 1.0f ? $" (base: {GetCreatureSkill(GetCurrentAttackSkill()).Current})" : ""; * Console.WriteLine("Attack skill: " + attackSkill + baseStr); * * baseStr = defenseMod != 1.0f ? $" (base: {player.GetCreatureSkill(defenseSkill).Current})" : ""; * Console.WriteLine("Defense skill: " + difficulty + baseStr);*/ var evadeChance = 1.0f - SkillCheck.GetSkillChance((int)attackSkill, (int)difficulty); return((float)evadeChance); }
public void Examine(WorldObject obj) { var success = true; var creature = obj as Creature; Player player = null; if (creature != null) { player = obj as Player; var skill = player != null ? Skill.AssessPerson : Skill.AssessCreature; var currentSkill = (int)GetCreatureSkill(skill).Current; int difficulty = (int)creature.GetCreatureSkill(Skill.Deception).Current; var chance = SkillCheck.GetSkillChance(currentSkill, difficulty); if (difficulty == 0 || player != null && !player.GetCharacterOption(CharacterOption.AttemptToDeceiveOtherPlayers)) { chance = 1.0f; } success = chance >= ThreadSafeRandom.Next(0.0f, 1.0f); } Session.Network.EnqueueSend(new GameEventIdentifyObjectResponse(Session, obj, success)); if (!success && player != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} tried and failed to assess you!", ChatMessageType.Appraisal)); } }
/// <summary> /// Returns the chance for player to avoid monster attack /// </summary> public float GetEvadeChance() { // get monster attack skill var attackSkill = GetCreatureSkill(GetCurrentAttackSkill()).Current; if (IsExhausted) { attackSkill = GetExhaustedSkill(attackSkill); } // get player defense skill var player = AttackTarget as Player; var defenseSkill = CurrentAttack == AttackType.Missile ? Skill.MissileDefense : Skill.MeleeDefense; var difficulty = player.GetCreatureSkill(defenseSkill).Current; if (player.IsExhausted) { difficulty = 0; } //Console.WriteLine("Attack skill: " + attackSkill); //Console.WriteLine("Defense skill: " + difficulty); var evadeChance = 1.0f - SkillCheck.GetSkillChance((int)attackSkill, (int)difficulty); return((float)evadeChance); }
public void Examine(WorldObject obj) { var success = true; var creature = obj as Creature; Player player = null; if (creature != null) { player = obj as Player; var skill = player != null ? Skill.AssessPerson : Skill.AssessCreature; var currentSkill = (int)GetCreatureSkill(skill).Current; int difficulty = (int)creature.GetCreatureSkill(Skill.Deception).Current; if (PropertyManager.GetBool("assess_creature_mod").Item&& skill == Skill.AssessCreature && Skills[Skill.AssessCreature].AdvancementClass < SkillAdvancementClass.Trained) { currentSkill = (int)((Focus.Current + Self.Current) / 2); } var chance = SkillCheck.GetSkillChance(currentSkill, difficulty); if (difficulty == 0 || player == this || player != null && !player.GetCharacterOption(CharacterOption.AttemptToDeceiveOtherPlayers)) { chance = 1.0f; } if ((this is Admin || this is Sentinel) && CloakStatus == CloakStatus.On) { chance = 1.0f; } success = chance >= ThreadSafeRandom.Next(0.0f, 1.0f); } if (creature is Pet || creature is CombatPet) { success = true; } Session.Network.EnqueueSend(new GameEventIdentifyObjectResponse(Session, obj, success)); if (!success && player != null && !player.SquelchManager.Squelches.Contains(this, ChatMessageType.Appraisal)) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} tried and failed to assess you!", ChatMessageType.Appraisal)); } // pooky logic - handle monsters attacking on appraisal if (creature != null && creature.MonsterState == State.Idle) { if (creature.Tolerance.HasFlag(Tolerance.Appraise)) { creature.AttackTarget = this; creature.WakeUp(); } } }
public void Examine(WorldObject obj) { //Console.WriteLine($"{Name}.Examine({obj.Name})"); var success = true; var creature = obj as Creature; Player player = null; if (creature != null) { player = obj as Player; var skill = player != null ? Skill.AssessPerson : Skill.AssessCreature; var currentSkill = (int)GetCreatureSkill(skill).Current; int difficulty = (int)creature.GetCreatureSkill(Skill.Deception).Current; if (PropertyManager.GetBool("assess_creature_mod").Item&& skill == Skill.AssessCreature && Skills[Skill.AssessCreature].AdvancementClass < SkillAdvancementClass.Trained) { currentSkill = (int)((Focus.Current + Self.Current) / 2); } var chance = SkillCheck.GetSkillChance(currentSkill, difficulty); if (difficulty == 0 || player == this || player != null && !player.GetCharacterOption(CharacterOption.AttemptToDeceiveOtherPlayers)) { chance = 1.0f; } if ((this is Admin || this is Sentinel) && CloakStatus == CloakStatus.On) { chance = 1.0f; } success = chance > ThreadSafeRandom.Next(0.0f, 1.0f); } if (obj.ResistItemAppraisal >= 999) { success = false; } if (creature is Pet || creature is CombatPet) { success = true; } if (success) { CurrentAppraisalTarget = obj.Guid.Full; } Session.Network.EnqueueSend(new GameEventIdentifyObjectResponse(Session, obj, success)); OnAppraisal(obj, success); }
public float GetEvadeChance(WorldObject target) { // get player attack skill var attackSkill = GetEffectiveAttackSkill(); // get target defense skill var difficulty = GetTargetEffectiveDefenseSkill(target); var evadeChance = 1.0f - SkillCheck.GetSkillChance((int)attackSkill, (int)difficulty); return((float)evadeChance); }
public float GetEvadeChance(WorldObject target) { // get player attack skill var attackSkill = GetCreatureSkill(GetCurrentWeaponSkill()); // get target defense skill var creature = target as Creature; var defenseSkill = GetAttackType() == AttackType.Melee ? Skill.MeleeDefense : Skill.MissileDefense; var difficulty = creature.GetCreatureSkill(defenseSkill).Current; var evadeChance = 1.0f - SkillCheck.GetSkillChance((int)attackSkill.Current, (int)difficulty); return((float)evadeChance); }
/// <summary> /// Returns the chance for player to avoid monster attack /// </summary> /// <returns></returns> public float GetEvadeChance() { // get monster attack skill var attackSkill = GetCreatureSkill(GetCurrentAttackSkill()); // get player defense skill var player = AttackTarget as Player; var difficulty = player.GetCreatureSkill(Skill.MeleeDefense).Current; //Console.WriteLine("Attack skill: " + attackSkill.Current); //Console.WriteLine("Defense skill: " + difficulty); var evadeChance = 1.0f - SkillCheck.GetSkillChance((int)attackSkill.Current, (int)difficulty); return((float)evadeChance); }
public static uint GetManaCost(uint difficulty, uint manaCost, uint manaConv) { // thanks to GDLE for this function! if (manaConv == 0) { return(manaCost); } // Dropping diff by half as Specced ManaC is only 48 with starter Aug so 50 at level 1 means no bonus // easiest change without having to create two different formulas to try to emulate retail var successChance = SkillCheck.GetSkillChance(manaConv, difficulty / 2); var roll = ThreadSafeRandom.Next(0.0f, 1.0f); // Luck lowers the roll value to give better outcome // e.g. successChance = 0.83 & roll = 0.71 would still provide some savings. // but a luck roll of 0.19 will lower that 0.71 to 0.13 so the caster would // receive a 60% reduction in mana cost. without the luck roll, 12% // so players will always have a level of "luck" in manacost if they make skill checks var luck = ThreadSafeRandom.Next(0.0f, 1.0f); if (roll <= successChance) { manaCost = (uint)Math.Round(manaCost * (1.0f - (successChance - (roll * luck)))); } // above seems to give a good middle of the range // seen in pcaps for mana usage for low level chars // bug still need a way to give a better reduction for the "lucky" // save some calc time if already at 1 mana cost if (manaCost > 1) { successChance = SkillCheck.GetSkillChance(manaConv, difficulty); roll = ThreadSafeRandom.Next(0.0f, 1.0f); if (roll <= successChance) { manaCost = (uint)Math.Round(manaCost * (1.0f - (successChance - (roll * luck)))); } } return(Math.Max(manaCost, 1)); }
public void Examine(WorldObject obj) { var success = true; var creature = obj as Creature; Player player = null; if (creature != null) { player = obj as Player; var skill = player != null ? Skill.AssessPerson : Skill.AssessCreature; var currentSkill = (int)GetCreatureSkill(skill).Current; int difficulty = (int)creature.GetCreatureSkill(Skill.Deception).Current; var chance = SkillCheck.GetSkillChance(currentSkill, difficulty); if (difficulty == 0 || player != null && (!player.GetCharacterOption(CharacterOption.AttemptToDeceiveOtherPlayers) || player == this)) { chance = 1.0f; } success = chance >= ThreadSafeRandom.Next(0.0f, 1.0f); } Session.Network.EnqueueSend(new GameEventIdentifyObjectResponse(Session, obj, success)); if (!success && player != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} tried and failed to assess you!", ChatMessageType.Appraisal)); } // pooky logic - handle monsters attacking on appraisal if (creature != null && creature.MonsterState == State.Idle) { var tolerance = (Tolerance)(creature.GetProperty(PropertyInt.Tolerance) ?? 0); if (tolerance.HasFlag(Tolerance.Appraise)) { creature.AttackTarget = this; creature.WakeUp(); } } }
public CastingPreCheckStatus GetCastingPreCheckStatus(Spell spell, uint magicSkill, bool isWeaponSpell) { var difficulty = spell.Power; var castingPreCheckStatus = CastingPreCheckStatus.CastFailed; if (magicSkill > 0 && magicSkill >= (int)difficulty - 50) { var chance = SkillCheck.GetMagicSkillChance((int)magicSkill, (int)difficulty); var rng = ThreadSafeRandom.Next(0.0f, 1.0f); if (chance > rng) { castingPreCheckStatus = CastingPreCheckStatus.Success; } } // build-in spells never fizzle if (isWeaponSpell) { castingPreCheckStatus = CastingPreCheckStatus.Success; } // limit casting time between war and void if (spell.School == MagicSchool.VoidMagic && LastSuccessCast_School == MagicSchool.WarMagic || spell.School == MagicSchool.WarMagic && LastSuccessCast_School == MagicSchool.VoidMagic) { // roll each time? var timeLimit = ThreadSafeRandom.Next(3.0f, 5.0f); if (Time.GetUnixTime() - LastSuccessCast_Time < timeLimit) { var curType = spell.School == MagicSchool.WarMagic ? "War" : "Void"; var prevType = LastSuccessCast_School == MagicSchool.VoidMagic ? "Nether" : "Elemental"; Session.Network.EnqueueSend(new GameMessageSystemChat($"The {prevType} energies permeating your blood cause this {curType} magic to fail.", ChatMessageType.Magic)); castingPreCheckStatus = CastingPreCheckStatus.CastFailed; } } return(castingPreCheckStatus); }
public static UnlockResults Unlock(WorldObject target, uint playerLockpickSkillLvl, ref int difficulty) { var isPickable = IsPickable(target); if (!isPickable) { return(UnlockResults.CannotBePicked); } int?myResistLockpick = GetResistLockpick(target); difficulty = myResistLockpick.Value; if (target.IsOpen) { return(UnlockResults.Open); } if (!target.IsLocked) { return(UnlockResults.AlreadyUnlocked); } var pickChance = SkillCheck.GetSkillChance((int)playerLockpickSkillLvl, difficulty); #if DEBUG Debug.WriteLine($"{pickChance.FormatChance()} chance of UnlockSuccess"); #endif var dice = ThreadSafeRandom.Next(0.0f, 1.0f); if (dice > pickChance) { return(UnlockResults.PickLockFailed); } target.IsLocked = false; target.EnqueueBroadcast(new GameMessagePublicUpdatePropertyBool(target, PropertyBool.Locked, target.IsLocked)); //target.CurrentLandblock?.EnqueueBroadcastSound(target, Sound.Lockpicking); return(UnlockResults.UnlockSuccess); }
public static UnlockResults Unlock(WorldObject me, uint playerLockpickSkillLvl) { int?myResistLockpick = GetResistLockpick(me); if (!myResistLockpick.HasValue || myResistLockpick < 1) { return(UnlockResults.CannotBePicked); } if (me.IsOpen ?? false) { return(UnlockResults.Open); } if (!me.IsLocked ?? false) { return(UnlockResults.AlreadyUnlocked); } var pickChance = SkillCheck.GetSkillChance((int)playerLockpickSkillLvl, (int)myResistLockpick); #if DEBUG Debug.WriteLine($"{pickChance.FormatChance()} chance of UnlockSuccess"); #endif var dice = Physics.Common.Random.RollDice(0.0f, 1.0f); if (dice > pickChance) { return(UnlockResults.PickLockFailed); } me.IsLocked = false; me.CurrentLandblock.EnqueueBroadcast(me.Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(me, PropertyBool.Locked, me.IsLocked ?? false)); //me.CurrentLandblock.EnqueueBroadcastSound(me, Sound.Lockpicking); return(UnlockResults.UnlockSuccess); }
/// <summary> /// Determines whether a spell will be evaded, based upon the caster's magic skill vs target's magic defense skill /// </summary> /// <param name="casterMagicSkill"></param> /// <param name="targetMagicDefenseSkill"></param> /// <returns></returns> public static bool MagicDefenseCheck(uint casterMagicSkill, uint targetMagicDefenseSkill) { return(Physics.Common.Random.RollDice(0.0f, 1.0f) < (1.0f - SkillCheck.GetSkillChance((int)casterMagicSkill, (int)targetMagicDefenseSkill))); }
/// <summary> /// Method used for handling player targeted spell casts /// </summary> public void CreatePlayerSpell(WorldObject target, TargetCategory targetCategory, uint spellId) { var player = this; var creatureTarget = target as Creature; if (player.IsBusy == true) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YoureTooBusy)); return; } player.IsBusy = true; var spell = new Spell(spellId); if (spell.NotFound) { if (spell._spellBase == null) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, $"SpellId {spellId} Invalid.")); Session.Network.EnqueueSend(new GameEventUseDone(Session, WeenieError.None)); } else { Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); Session.Network.EnqueueSend(new GameEventUseDone(Session, WeenieError.MagicInvalidSpellType)); } player.IsBusy = false; return; } if (IsInvalidTarget(spell, target)) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"{spell.Name} cannot be cast on {target.Name}.")); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.None)); player.IsBusy = false; return; } // if casting implement has spell built in, // use spellcraft from the item, instead of player's magic skill? var caster = GetEquippedWand(); var isWeaponSpell = IsWeaponSpell(spell); // Grab player's skill level in the spell's Magic School var magicSkill = player.GetCreatureSkill(spell.School).Current; if (isWeaponSpell && caster.ItemSpellcraft != null) { magicSkill = (uint)caster.ItemSpellcraft; } if (targetCategory == TargetCategory.WorldObject) { if (target.Guid != Guid) { var targetLoc = target; if (targetLoc.WielderId != null) { targetLoc = CurrentLandblock?.GetObject(targetLoc.WielderId.Value); } float distanceTo = Location.Distance2D(targetLoc.Location); if (distanceTo > spell.BaseRangeConstant + magicSkill * spell.BaseRangeMod) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.None), new GameMessageSystemChat($"Target is out of range!", ChatMessageType.Magic)); player.IsBusy = false; return; } } } var difficulty = spell.Power; // is this needed? should talismans remain the same, regardless of player spell formula? spell.Formula.GetPlayerFormula(player); var castingPreCheckStatus = CastingPreCheckStatus.CastFailed; if (magicSkill > 0 && magicSkill >= (int)difficulty - 50) { var chance = 1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill, (int)difficulty); var rng = ThreadSafeRandom.Next(0.0f, 1.0f); if (chance < rng || isWeaponSpell) { castingPreCheckStatus = CastingPreCheckStatus.Success; } } // limit casting time between war and void if (spell.School == MagicSchool.VoidMagic && LastSuccessCast_School == MagicSchool.WarMagic || spell.School == MagicSchool.WarMagic && LastSuccessCast_School == MagicSchool.VoidMagic) { // roll each time? var timeLimit = ThreadSafeRandom.Next(3.0f, 5.0f); if (Time.GetUnixTime() - LastSuccessCast_Time < timeLimit) { var curType = spell.School == MagicSchool.WarMagic ? "War" : "Void"; var prevType = LastSuccessCast_School == MagicSchool.VoidMagic ? "Nether" : "Elemental"; Session.Network.EnqueueSend(new GameMessageSystemChat($"The {prevType} energies permeating your blood cause this {curType} magic to fail.", ChatMessageType.Magic)); castingPreCheckStatus = CastingPreCheckStatus.CastFailed; } } // Calculate mana usage uint manaUsed = CalculateManaUsage(player, spell, target); var currentMana = player.Mana.Current; if (isWeaponSpell) { currentMana = (uint)(caster.ItemCurMana ?? 0); } if (manaUsed > currentMana) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouDontHaveEnoughManaToCast)); IsBusy = false; // delay? return; } // begin spellcasting Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Skill.ManaConversion), spell.PowerMod); if (!isWeaponSpell) { player.UpdateVitalDelta(player.Mana, -(int)manaUsed); } else { caster.ItemCurMana -= (int)manaUsed; } spell.Formula.GetPlayerFormula(player); string spellWords = spell._spellBase.GetSpellWords(DatManager.PortalDat.SpellComponentsTable); if (spellWords != null && !isWeaponSpell) { EnqueueBroadcast(new GameMessageCreatureMessage(spellWords, Name, Guid.Full, ChatMessageType.Spellcasting)); } var spellChain = new ActionChain(); var castSpeed = 2.0f; // hardcoded for player spell casting? var startPos = new Position(Location); // do wind-up gestures: fastcast has no windup (creature enchantments) if (!spell.Flags.HasFlag(SpellFlags.FastCast) && !isWeaponSpell) { // note that ACE is currently sending the windup motion and the casting gesture // at the same time. the client is automatically queueing these animations to run at the correct time. foreach (var windupGesture in spell.Formula.WindupGestures) { spellChain.AddAction(this, () => { var motionWindUp = new Motion(MotionStance.Magic, windupGesture, castSpeed); EnqueueBroadcastMotion(motionWindUp); }); } } // cast spell spellChain.AddAction(this, () => { var castGesture = spell.Formula.CastGesture; if (isWeaponSpell && caster.UseUserAnimation != 0) { castGesture = caster.UseUserAnimation; } var motionCastSpell = new Motion(MotionStance.Magic, castGesture, castSpeed); EnqueueBroadcastMotion(motionCastSpell); }); var castingDelay = spell.Formula.GetCastTime(MotionTableId, castSpeed, isWeaponSpell); spellChain.AddDelaySeconds(castingDelay); bool movedTooFar = false; spellChain.AddAction(this, () => { if (!isWeaponSpell) { TryBurnComponents(spell); } // check windup move distance cap var endPos = new Position(Location); var dist = startPos.DistanceTo(endPos); if (dist > Windup_MaxMove) { castingPreCheckStatus = CastingPreCheckStatus.CastFailed; movedTooFar = true; } var pk_error = CheckPKStatusVsTarget(player, target, spell); if (pk_error != null) { castingPreCheckStatus = CastingPreCheckStatus.InvalidPKStatus; } var useDone = WeenieError.None; switch (castingPreCheckStatus) { case CastingPreCheckStatus.Success: if ((spell.Flags & SpellFlags.FellowshipSpell) == 0) { CreatePlayerSpell(target, spell); } else { var fellows = GetFellowshipTargets(); foreach (var fellow in fellows) { CreatePlayerSpell(fellow, spell); } } break; case CastingPreCheckStatus.InvalidPKStatus: if (spell.NumProjectiles > 0) { switch (spell.School) { case MagicSchool.WarMagic: WarMagic(target, spell); break; case MagicSchool.VoidMagic: VoidMagic(target, spell); break; case MagicSchool.LifeMagic: LifeMagic(target, spell, out uint damage, out bool critical, out var enchantmentStatus); break; }
/// <summary> /// Method used for handling player untargeted spell casts /// </summary> public void CreatePlayerSpell(uint spellId) { if (IsBusy == true) { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YoureTooBusy)); return; } else { IsBusy = true; } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.MagicInvalidSpellType)); IsBusy = false; return; } SpellBase spell = spellTable.Spells[spellId]; Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.MagicInvalidSpellType)); IsBusy = false; return; } // Grab player's skill level in the spell's Magic School var magicSkill = GetCreatureSkill(spell.School); float scale = SpellAttributes(Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); var formula = SpellTable.GetSpellFormula(spellTable, spellId, Session.Account); bool spellCastSuccess = false || ((Physics.Common.Random.RollDice(0.0f, 1.0f) > (1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill.Current, (int)spell.Power))) && (magicSkill.Current >= (int)spell.Power - 50) && (magicSkill.Current > 0)); // Calculating mana usage #region if (spellCastSuccess == true) { CreatureSkill mc = GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost; uint manaUsed; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { preCost = spell.BaseMana * baseManaPercent; if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } if (spell.MetaSpellType == SpellType.Transfer) { uint vitalChange, casterVitalChange; vitalChange = (uint)(GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } casterVitalChange = (uint)(vitalChange * (1.0f - spellStatMod.LossPercent)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana && (vitalChange + 10 + manaUsed) > Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } if ((vitalChange + 10) > GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source)) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => IsBusy = false); resourceCheckChain.EnqueueChain(); return; } } else if (manaUsed > Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else { UpdateVital(Mana, Mana.Current - manaUsed); } } #endregion ActionChain spellChain = new ActionChain(); uint fastCast = (spell.Bitfield >> 14) & 0x1u; if (fastCast != 1) { spellChain.AddAction(this, () => { var motionWindUp = new UniversalMotion(MotionStance.Spellcasting); motionWindUp.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionWindUp.MovementData.ForwardCommand = (uint)windUpMotion; motionWindUp.MovementData.ForwardSpeed = 2; DoMotion(motionWindUp); }); } spellChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageCreatureMessage(SpellComponentsTable.GetSpellWords(DatManager.PortalDat.SpellComponentsTable, formula), Name, Guid.Full, ChatMessageType.Magic)); }); spellChain.AddAction(this, () => { var motionCastSpell = new UniversalMotion(MotionStance.Spellcasting); motionCastSpell.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionCastSpell.MovementData.ForwardCommand = (uint)spellGesture; motionCastSpell.MovementData.ForwardSpeed = 2; DoMotion(motionCastSpell); }); if (fastCast == 1) { spellChain.AddDelaySeconds(castingDelay * 0.26f); } else { spellChain.AddDelaySeconds(castingDelay); } if (spellCastSuccess == false) { spellChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f))); } else { // TODO - Successful spell casting code goes here for untargeted spells to replace line below Session.Network.EnqueueSend(new GameMessageSystemChat("Targeted SpellID " + spellId + " not yet implemented!", ChatMessageType.System)); } spellChain.AddAction(this, () => { var motionReturnToCastStance = new UniversalMotion(MotionStance.Spellcasting); motionReturnToCastStance.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionReturnToCastStance.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motionReturnToCastStance); }); spellChain.AddDelaySeconds(1.0f); spellChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.None)); IsBusy = false; }); spellChain.EnqueueChain(); return; }
/// <summary> /// Method used for handling player targeted spell casts /// </summary> public void CreatePlayerSpell(ObjectGuid guidTarget, uint spellId) { Player player = CurrentLandblock.GetObject(Guid) as Player; WorldObject target = CurrentLandblock.GetObject(guidTarget); TargetCategory targetCategory = TargetCategory.WorldObject; if (target == null) { target = GetWieldedItem(guidTarget); targetCategory = TargetCategory.Wielded; } if (target == null) { target = GetInventoryItem(guidTarget); targetCategory = TargetCategory.Inventory; } if (target == null) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.TargetNotAcquired)); targetCategory = TargetCategory.UnDef; return; } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } SpellBase spell = spellTable.Spells[spellId]; if (IsInvalidTarget(spell, target)) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"{spell.Name} cannot be cast on {target.Name}.")); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); return; } Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } if (player.IsBusy == true) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YoureTooBusy)); return; } else { player.IsBusy = true; } uint targetEffect = spell.TargetEffect; // Grab player's skill level in the spell's Magic School var magicSkill = player.GetCreatureSkill(spell.School).Current; if (targetCategory == TargetCategory.WorldObject) { if (guidTarget != Guid) { float distanceTo = Location.Distance2D(target.Location); if (distanceTo > spell.BaseRangeConstant + magicSkill * spell.BaseRangeMod) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicTargetOutOfRange), new GameMessageSystemChat($"{target.Name} is out of range!", ChatMessageType.Magic)); player.IsBusy = false; return; } } } // Ensure that a harmful spell isn't being cast on a player target that doesn't have the same PK status if (target.WeenieClassId == 1 && player.PlayerKillerStatus != ACE.Entity.Enum.PlayerKillerStatus.NPK) { bool isSpellHarmful = IsSpellHarmful(spell); if (player.PlayerKillerStatus != target.PlayerKillerStatus && isSpellHarmful) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.InvalidPkStatus)); player.IsBusy = false; return; } } float scale = SpellAttributes(player.Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); var formula = SpellTable.GetSpellFormula(spellTable, spellId, player.Session.Account); bool spellCastSuccess = false || ((Physics.Common.Random.RollDice(0.0f, 1.0f) > (1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill, (int)spell.Power))) && (magicSkill >= (int)spell.Power - 50) && (magicSkill > 0)); // Calculating mana usage #region CreatureSkill mc = player.GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost; uint manaUsed; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { preCost = spell.BaseMana * baseManaPercent; if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } if (spell.MetaSpellType == SpellType.Transfer) { uint vitalChange, casterVitalChange; vitalChange = (uint)(player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } casterVitalChange = (uint)(vitalChange * (1.0f - spellStatMod.LossPercent)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana && (vitalChange + 10 + manaUsed) > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else if ((vitalChange + 10) > player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source)) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => player.IsBusy = false); resourceCheckChain.EnqueueChain(); return; } } else if (manaUsed > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else { player.UpdateVital(player.Mana, player.Mana.Current - manaUsed); } #endregion ActionChain spellChain = new ActionChain(); uint fastCast = (spell.Bitfield >> 14) & 0x1u; if (fastCast != 1) { spellChain.AddAction(this, () => { var motionWindUp = new UniversalMotion(MotionStance.Spellcasting); motionWindUp.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionWindUp.MovementData.ForwardCommand = (uint)windUpMotion; motionWindUp.MovementData.ForwardSpeed = 2; DoMotion(motionWindUp); }); } spellChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageCreatureMessage(SpellComponentsTable.GetSpellWords(DatManager.PortalDat.SpellComponentsTable, formula), Name, Guid.Full, ChatMessageType.Magic)); }); spellChain.AddAction(this, () => { var motionCastSpell = new UniversalMotion(MotionStance.Spellcasting); motionCastSpell.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionCastSpell.MovementData.ForwardCommand = (uint)spellGesture; motionCastSpell.MovementData.ForwardSpeed = 2; DoMotion(motionCastSpell); }); if (fastCast == 1) { spellChain.AddDelaySeconds(castingDelay * 0.26f); } else { spellChain.AddDelaySeconds(castingDelay); } if (spellCastSuccess == false) { spellChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f))); } else { spellChain.AddAction(this, () => { bool targetDeath; string message; switch (spell.School) { case MagicSchool.WarMagic: WarMagic(target, spell, spellStatMod); break; case MagicSchool.VoidMagic: VoidMagic(target, spell, spellStatMod); break; case MagicSchool.CreatureEnchantment: if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); message = CreatureMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; case MagicSchool.LifeMagic: if (spell.MetaSpellType != SpellType.LifeProjectile) { if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); targetDeath = LifeMagic(target, spell, spellStatMod, out message); if (message != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } if (targetDeath == true) { Creature creatureTarget = (Creature)target; creatureTarget.Die(); Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages); player.Session.Network.EnqueueSend(new GameMessageSystemChat(string.Format(messages[0], target.Name), ChatMessageType.Broadcast)); player.EarnXP((long)target.XpOverride); } break; case MagicSchool.ItemEnchantment: if (guidTarget == Guid) { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, (PlayScript)spell.CasterEffect, scale)); } else { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); } message = ItemMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; default: break; } }); } spellChain.AddAction(this, () => { var motionReturnToCastStance = new UniversalMotion(MotionStance.Spellcasting); motionReturnToCastStance.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionReturnToCastStance.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motionReturnToCastStance); }); spellChain.AddDelaySeconds(1.0f); spellChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); player.IsBusy = false; }); spellChain.EnqueueChain(); return; }