/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.InvalidTarget; // Check range var targetPosition = mainTarget.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(mainTarget))) return CombatSkillResult.OutOfRange; // Stop movement attacker.StopMove(); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet<Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); // Counter if (Counterattack.Handle(targets, attacker)) return CombatSkillResult.Okay; // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); aAction.Stun = StunTime; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Calculate damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { // Stop movement target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) damage *= (skill.RankData.Var4 / 100f); // Critical Hit var critChance = this.GetCritChance(attacker, target, skill); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro if (target == mainTarget) target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); } // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(mainTarget.GetPosition(), attacker.AttackRangeFor(mainTarget))) return CombatSkillResult.OutOfRange; attacker.StopMove(); // Get targets, incl. splash. var targets = new HashSet<Creature>() { mainTarget }; targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); // Counter if (Counterattack.Handle(targets, attacker)) return CombatSkillResult.Okay; var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(attacker.IsDualWielding ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); if (attacker.IsDualWielding) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); cap.Hit = i; cap.Type = (attacker.IsDualWielding ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; var mainDamage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); foreach (var target in targets) { if (target.IsDead) continue; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); cap.Add(tAction); // Base damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) damage *= attacker.GetSplashDamage(weapon); // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander // Can only happen on the first hit var pinged = (cap.Hit == 1 && HeavyStander.Handle(attacker, target, ref damage, tAction)); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Aggro if (target == mainTarget) target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } else if (!attacker.IsDualWielding && !weaponIsKnuckle && target.Is(RaceStands.KnockBackable)) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); if (target == mainTarget) aAction.Set(AttackerOptions.KnockBackHit2); } // Set stun time if not defended, Defense handles the stun // in case the target used it. if (tAction.SkillId != SkillId.Defense) { if (target == mainTarget) aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } if (target == mainTarget) { // Set increased stun if target pinged if (pinged) aAction.Stun = GetAttackerStun(attacker, weapon, true); // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack && !pinged) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, weapon); // Consume stamina for weapon var staminaUsage = (weapon != null ? weapon.Data.StaminaUsage : Creature.BareHandStaminaUsage); if (attacker.Stamina < staminaUsage) Send.Notice(attacker, Localization.Get("Your stamina is too low to fight properly!")); attacker.Stamina -= staminaUsage; // No second hit if defended, pinged, or knocked back if (tAction.IsKnockBack || tAction.SkillId == SkillId.Defense || pinged) { // Set to 1 to prevent second run maxHits = 1; // Remove dual wield option if last hit doesn't come from // the second weapon. If this isn't done, the client shows // the second hit. if (cap.Hit != 2) aAction.Options &= ~AttackerOptions.DualWield; } } } // Handle cap.Handle(); } return CombatSkillResult.Okay; }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // Check range var targetPosition = target.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; // Stop movement attacker.StopMove(); target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Calculate damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Critical Hit var critChance = this.GetCritChance(attacker, target, skill); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback attacker.Stun = aAction.Stun = StunTime; target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; attacker.StopMove(); var targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); cap.Hit = i; cap.Type = (dualWield ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } // Base damage var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.Hit != 2) aAction.Options &= ~AttackerOptions.DualWield; } // Set stun time if (tAction.SkillId != SkillId.Defense) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); cap.Handle(); // No second hit if target was knocked back if (tAction.IsKnockBack) break; } return CombatSkillResult.Okay; }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature creature, Skill skill, Packet packet) { var targetEntityId = packet.GetLong(); // Similar to WM there is a case where these aren't sent. // Apparently this can happen if you activate the skill while // targetting an enemy. var unk1 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0); var unk2 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0); if (_cm == null) _cm = ChannelServer.Instance.SkillManager.GetHandler<CombatMastery>(SkillId.CombatMastery); // TODO: Check duration var attackResult = false; var target = creature.Region.GetCreature(targetEntityId); if (target != null && !creature.IsStunned && creature.CanTarget(target)) { var pos = creature.GetPosition(); var targetPos = target.GetPosition(); var inRange = pos.InRange(targetPos, creature.AttackRangeFor(target)); if (!inRange) { var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100); // Check teleport distance if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100) { Send.Notice(creature, "Out of range"); } else { Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0); creature.SetPosition(telePos.X, telePos.Y); Send.SkillTeleport(creature, telePos.X, telePos.Y); inRange = true; } } if (inRange) attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay); } Send.CombatAttackR(creature, attackResult); Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2); }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None) return CombatSkillResult.Okay; // Check range var attackerPosition = attacker.GetPosition(); var targetPosition = target.GetPosition(); if (!attacker.IgnoreAttackRange && (!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target)))) { return CombatSkillResult.OutOfRange; } if (!attacker.IgnoreAttackRange && (attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position || target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM) { return CombatSkillResult.Okay; } attacker.IgnoreAttackRange = false; // Against Normal Attack Skill combatMastery = target.Skills.Get(SkillId.CombatMastery); if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown)) { target.InterceptingSkillId = SkillId.Smash; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, combatMastery, attacker.EntityId); return CombatSkillResult.Okay; } // Against Windmill //TODO: Change this into the new NPC client system when it comes out if needed. Skill windmill = target.Skills.Get(SkillId.Windmill); if (windmill != null && target.Skills.IsReady(SkillId.Windmill) && !target.IsPlayer && target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.Smash; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<IUseable>(windmill.Info.Id) as Windmill; if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", windmill.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, windmill); return CombatSkillResult.Okay; } // Against Smash Skill smash = target.Skills.Get(SkillId.Smash); if (smash != null && target.Skills.IsReady(SkillId.Smash) && target.IsInBattleStance && target.Target == attacker && !target.IsStunned && attacker.CanAttack(target)) { var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false); var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false); if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime && target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking. || attackerStunTime > targetStunTime && !Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose. 2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%) && !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now))) { if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.Smash; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(smash.Info.Id); if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", smash.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, smash, attacker.EntityId); return CombatSkillResult.Okay; } } else { attacker.InterceptingSkillId = SkillId.Smash; } } // Stop movement attacker.StopMove(); target.StopMove(); target.IgnoreAttackRange = false; // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; var weapon = attacker.RightHand; ICollection<Creature> targets = null; if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0 || weapon == null) { targets = attacker.GetTargetableCreaturesInCone(weapon != null ? (int)weapon.Data.SplashRadius : 200, weapon != null ? (int)weapon.Data.SplashAngle : 20); foreach (var splashTarget in targets) { if (splashTarget != target) { // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; } } } // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); TargetAction tAction; if (attacker.InterceptingSkillId == SkillId.Smash) { aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash); } else { tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); } tAction.Set(TargetOptions.Result | TargetOptions.Smash); attacker.InterceptingSkillId = SkillId.None; var cap = new CombatActionPack(attacker, skill.Info.Id, tAction, aAction); // Calculate damage var damage = this.GetDamage(attacker, skill); var critChance = this.GetCritChance(attacker, target, skill); // Critical Hit CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Apply damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback attacker.Stun = aAction.Stun = StunTime; target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, attacker.RightHand, attacker.LeftHand); var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0) { foreach (var splashTarget in targets) { if (splashTarget != target) { if (splashTarget.IsNotReadyToBeHit) continue; TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id); tSplashAction.Set(TargetOptions.Result | TargetOptions.Smash); // Base damage float damageSplash = this.GetDamage(attacker, skill); attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction); // Deal with it! if (damageSplash > 0) splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker); // Alert splashTarget.Aggro(attacker, true); if (splashTarget.IsDead) tSplashAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); splashTarget.Stun = tSplashAction.Stun = StunTime; splashTarget.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(splashTarget, KnockbackDistance); cap.Add(tSplashAction); } } } // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None) return CombatSkillResult.Okay; var attackerPosition = attacker.GetPosition(); var targetPosition = target.GetPosition(); if (!attacker.IgnoreAttackRange && (!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target)))) { return CombatSkillResult.OutOfRange; } if (!attacker.IgnoreAttackRange && (attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position || target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM) { return CombatSkillResult.Okay; } attacker.IgnoreAttackRange = false; //Against Smash Skill smash = target.Skills.Get(SkillId.Smash); if (smash != null && target.Skills.IsReady(SkillId.Smash) && attacker.CanAttack(target)) attacker.InterceptingSkillId = SkillId.Smash; var rightWeapon = attacker.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0 && (leftWeapon.HasTag("/weapon/edged/") || leftWeapon.HasTag("/weapon/blunt/"))); var staminaUsage = (rightWeapon != null && rightWeapon.Data.StaminaUsage != 0 ? rightWeapon.Data.StaminaUsage : 0.7f) + (dualWield ? leftWeapon.Data.StaminaUsage : 0f); var lowStamina = false; if (attacker.Stamina < staminaUsage) { lowStamina = true; Send.Notice(attacker, Localization.Get("Your stamina is too low to attack properly!")); } attacker.Stamina -= staminaUsage; Send.StatUpdate(attacker, StatUpdateType.Private, Stat.Stamina); // Against Combat Mastery Skill combatMastery = target.Skills.Get(SkillId.CombatMastery); var simultaneousAttackStun = 0; if (attacker.InterceptingSkillId != SkillId.CombatMastery && target.InterceptingSkillId != SkillId.CombatMastery) { if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown) && attacker.CanAttack(target)) { var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false); var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false); if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime && target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking. || attackerStunTime > targetStunTime && !Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose. 2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%) && !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now))) { if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.CombatMastery; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("CombatMastery.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, combatMastery, attacker.EntityId); return CombatSkillResult.Okay; } } else { if (Math2.Probability(((2725 - attackerStunTime) / 2500) * 100)) //Probability in percentage that it will be an interception instead of a double hit. { attacker.InterceptingSkillId = SkillId.CombatMastery; } else { attacker.InterceptingSkillId = SkillId.CombatMastery; if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.CombatMastery; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("CombatMastery.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); } else { skillHandler.Use(target, combatMastery, attacker.EntityId); simultaneousAttackStun = attacker.Stun; attacker.Stun = 0; } } } } } } attacker.StopMove(); targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; ICollection<Creature> targets = null; if (rightWeapon != null && rightWeapon.Data.SplashRadius != 0 && rightWeapon.Data.SplashAngle != 0 || rightWeapon == null) { targets = attacker.GetTargetableCreaturesInCone(rightWeapon != null ? (int)rightWeapon.Data.SplashRadius : 204, rightWeapon != null ? (int)rightWeapon.Data.SplashAngle : 60); foreach (var splashTarget in targets) { if (splashTarget != target) { // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; } } } var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; var defenseStun = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); AttackerAction aAction; TargetAction tAction; if (attacker.InterceptingSkillId == SkillId.Smash) { aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId); aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash); tAction.Options |= TargetOptions.Result; } else if (attacker.InterceptingSkillId == SkillId.CombatMastery) { aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId); aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery); tAction.Options |= TargetOptions.Result; } else { aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery); aAction.Options |= AttackerOptions.Result; tAction.Options |= TargetOptions.Result; } attacker.InterceptingSkillId = SkillId.None; var cap = new CombatActionPack(attacker, skill.Info.Id, tAction, aAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) aAction.Set(AttackerOptions.DualWield); // Base damage var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); if (lowStamina) { damage = attacker.GetRndBareHandDamage(); } // Critical Hit var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0); var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection + critShieldReduction) : attacker.GetLeftCritChance(target.Protection + critShieldReduction)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense var tActionOldType = tAction.Type; Defense.Handle(aAction, tAction, ref damage); if (i == 1 && tAction.Type == CombatActionType.Defended) { defenseStun = tAction.Stun; } // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); if (tAction.Type == CombatActionType.Defended && target.Life <= 0) { tAction.Type = tActionOldType; } // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.Type != CombatActionType.Defended) { if (!target.Skills.IsReady(SkillId.FinalHit)) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); if (target.Life < 0) tAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack && tAction.Type != CombatActionType.Defended) { if (!target.Skills.IsReady(SkillId.FinalHit)) { attacker.Shove(target, KnockBackDistance); } aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) aAction.Options &= ~AttackerOptions.DualWield; } else if (tAction.Type == CombatActionType.Defended) { // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) aAction.Options &= ~AttackerOptions.DualWield; } // Set stun time if (tAction.Type != CombatActionType.Defended) { if (simultaneousAttackStun == 0) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && ((skill.Info.Id != SkillId.FinalHit) && !target.IsDead || AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal"))); } else { aAction.Stun = (short)simultaneousAttackStun; } if (!target.Skills.IsReady(SkillId.FinalHit)) { tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } if (target.IsDead && skill.Info.Id != SkillId.FinalHit) { attacker.AttackDelayTime = DateTime.Now.AddMilliseconds(GetAttackerStun(attacker, weapon, true)); } } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0) { foreach (var splashTarget in targets) { if (splashTarget != target) { if (splashTarget.IsNotReadyToBeHit) continue; TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id); // Base damage float damageSplash; if (lowStamina) { damageSplash = attacker.GetRndBareHandDamage(); } else { damageSplash = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); } attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction, weapon); // Deal with it! if (damageSplash > 0) splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker); // Alert splashTarget.Aggro(attacker, true); // Evaluate caused damage if (!splashTarget.IsDead) { if (tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { splashTarget.Stability -= (this.GetStabilityReduction(attacker, weapon) / maxHits) / 2; //Less stability reduction for splash damage. // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (splashTarget.IsUnstable && splashTarget.Is(RaceStands.KnockBackable)) //tSplashAction.Set(tSplashAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tSplashAction.Set(TargetOptions.KnockDown); if (splashTarget.Life < 0) tSplashAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { splashTarget.Stability = Creature.MinStability; tSplashAction.Set(TargetOptions.KnockDown); } } } } else { tSplashAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tSplashAction.IsKnockBack && tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { attacker.Shove(splashTarget, KnockBackDistance); } } // Set stun time if (tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { if (defenseStun != 0) tSplashAction.Stun = (short)defenseStun; else tSplashAction.Stun = GetTargetStun(attacker, weapon, tSplashAction.IsKnockBack); } } cap.Add(tSplashAction); } } } cap.Handle(); // No second hit if target was knocked back or defended. if (tAction.IsKnockBack || tAction.Type == CombatActionType.Defended) break; } attacker.AttemptingAttack = false; return CombatSkillResult.Okay; }
public void Use(Creature creature, Skill skill, long targetEntityId, int unk1, int unk2) { if (_cm == null) _cm = ChannelServer.Instance.SkillManager.GetHandler<CombatMastery>(SkillId.CombatMastery); var attackResult = false; var target = creature.Region.GetCreature(targetEntityId); if (target != null && !creature.IsStunned && !creature.IsOnAttackDelay && creature.CanTarget(target) && creature.CanAttack(target)) { var pos = creature.GetPosition(); var targetPos = target.GetPosition(); var inRange = (pos.InRange(targetPos, creature.AttackRangeFor(target)) && !creature.Region.Collisions.Any(pos, targetPos) // Check collisions between position && !target.Conditions.Has(ConditionsA.Invisible)); // Check visiblility (GM) if (!inRange && !target.IsNotReadyToBeHit) { var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100); // Check teleport distance if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100) { Send.Notice(creature, "Out of range"); } else { Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0); creature.SetPosition(telePos.X, telePos.Y); Send.SkillTeleport(creature, telePos.X, telePos.Y); inRange = true; } } if (inRange) attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay); } Send.CombatAttackR(creature, attackResult); Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2); }
public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; attacker.StopMove(); var targetPosition = target.StopMove(); var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); cap.Add(aAction, tAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; aAction.Set(AttackerOptions.Result); if (dualWield) aAction.Set(AttackerOptions.DualWield); var damage = attacker.GetRndDamage(weapon); tAction.Damage = damage; target.TakeDamage(tAction.Damage, attacker); if (!target.IsDead) { target.KnockBack += this.GetKnockBack(weapon) / maxHits; if (target.KnockBack >= 100 && target.Is(RaceStands.KnockBackable)) tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); } else { tAction.Set(TargetOptions.FinishingKnockDown); } if (tAction.IsKnockBack) { var newPos = attacker.GetPosition().GetRelative(targetPosition, KnockBackDistance); Position intersection; if (target.Region.Collissions.Find(targetPosition, newPos, out intersection)) newPos = targetPosition.GetRelative(intersection, -50); target.SetPosition(newPos.X, newPos.Y); aAction.Set(AttackerOptions.KnockBackHit2); cap.MaxHits = cap.Hit; } aAction.Stun = this.GetAttackerStun(weapon, tAction.IsKnockBack); tAction.Stun = this.GetTargetStun(weapon, tAction.IsKnockBack); cap.Handle(); if (tAction.IsKnockBack) break; } return CombatSkillResult.Okay; }