public void Handle()
{
foreach (var action in this.Actions)
{
// Stat update
Send.StatUpdate(action.Creature, StatUpdateType.Private, Stat.Life, Stat.LifeInjured, Stat.Mana, Stat.Stamina);
Send.StatUpdate(action.Creature, StatUpdateType.Public, Stat.Life, Stat.LifeInjured);
// If target action
if (action.Category == CombatActionCategory.Target)
{
var tAction = action as TargetAction;
if (action.Creature.IsPlayer)
{
// Max target stun for players == 2000?
action.Stun = Math.Min((short)2000, action.Stun);
// Break eggs on knock back
if (tAction.IsKnockBack && ChannelServer.Instance.Conf.World.BrokenEggs)
{
// Search for usable (non-broken) eggs, sparing VIP eggs.
var eggs = action.Creature.Inventory.GetItems(a => a.HasTag("/usable/food/cooking/solid/*egg/") && a.Info.Pocket != Pocket.VIPInventory);
foreach (var egg in eggs)
{
// Replace egg
var brokenEgg = new Item(50011);
brokenEgg.Amount = egg.Amount;
// Remove egg and place the broken one at the
// same spot.
if (action.Creature.Inventory.Remove(egg))
action.Creature.Inventory.Add(brokenEgg, egg.Info.Pocket);
}
}
}
// Put target into battle stance if it was hit.
// Fix for #435, Mimics not opening after being got hit
// by Windmill "passively", because they didn't aggro.
if (!action.Creature.IsInBattleStance)
action.Creature.IsInBattleStance = true;
// Reduce stun if pinged
// If the second hit of the second set of dual wield hits
// pings, the monster's stun on the client is longer than
// on the server, which causes a little glitch, where a
// monster might attack while it's still visibly stunned.
// Should stun from combat be *added*, instead of *set*?
if (!tAction.IsKnockBack && tAction.Has(EffectFlags.HeavyStander))
action.Stun = Math.Min((short)1000, action.Stun);
action.Creature.Stun = action.Stun;
// Mana Shield flag
if (tAction.ManaDamage > 0 && tAction.Damage == 0)
tAction.Set(TargetOptions.ManaShield);
// On attack events
ChannelServer.Instance.Events.OnCreatureAttacked(tAction);
if (this.Attacker.IsPlayer)
ChannelServer.Instance.Events.OnCreatureAttackedByPlayer(tAction);
// OnHit AI event
//action.Creature.Aggro(tAction.Attacker);
var npc = action.Creature as NPC;
if (npc != null && npc.AI != null)
{
npc.AI.OnTargetActionHit(tAction);
}
// Cancel target's skill
// Don't cancel Defense, which is still active at this point,
// a Complete for it is sent by the client shortly after.
// That won't happen if Smash is the attacker skill,
// but that's official behavior.
var activeSkill = action.Creature.Skills.ActiveSkill;
if (activeSkill != null && activeSkill.Info.Id != SkillId.Defense)
{
var custom = ChannelServer.Instance.SkillManager.GetHandler(activeSkill.Info.Id) as ICustomHitCanceler;
var stackMax = activeSkill.RankData.StackMax;
var cancel = false;
// Cancel non stackable skills on hit, wait for a
// knock back for stackables
if (stackMax > 1)
cancel = action.IsKnockBack;
else
cancel = true;
if (cancel)
{
if (custom == null)
action.Creature.Skills.CancelActiveSkill();
else
custom.CustomHitCancel(action.Creature, tAction);
}
}
// Cancel rest
if (action.Creature.Has(CreatureStates.SitDown))
{
var restHandler = ChannelServer.Instance.SkillManager.GetHandler<Rest>(SkillId.Rest);
if (restHandler != null)
restHandler.Stop(action.Creature, action.Creature.Skills.Get(SkillId.Rest));
}
// Remember knock back/down
tAction.Creature.WasKnockedBack = tAction.Has(TargetOptions.KnockBack) || tAction.Has(TargetOptions.KnockDown) || tAction.Has(TargetOptions.Smash);
if (tAction.Has(TargetOptions.KnockDown))
tAction.Creature.KnockDownTime = DateTime.Now.AddMilliseconds(tAction.Stun);
// Stability meter
// TODO: Limit it to "targetees"?
var visibleCreatures = tAction.Creature.Region.GetVisibleCreaturesInRange(tAction.Creature);
foreach (var cr in visibleCreatures)
Send.StabilityMeterUpdate(cr, tAction.Creature);
}
// If attacker action
else if (action.Category == CombatActionCategory.Attack)
{
var aAction = action as AttackerAction;
if (action.Creature.IsPlayer && action.SkillId >= SkillId.RangedAttack && action.SkillId <= SkillId.UrgentShot && !AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal"))
{
// Players don't seem to be stunned from ranging
// on old officials. While the proper stun is sent
// to the client, for some reason you didn't need
// to wait. Since we don't know how officials did
// that, we'll just cap the stun time.
action.Stun = 0;
}
action.Creature.Stun = action.Stun;
var npc = action.Creature as NPC;
if (npc != null && npc.AI != null && action.SkillId != SkillId.CombatMastery)
npc.AI.OnUsedSkill(aAction);
ChannelServer.Instance.Events.OnCreatureAttacks(aAction);
}
}
// Send combat action
Send.CombatAction(this);
// Skill used
if (this.SkillId != SkillId.CombatMastery)
Send.CombatUsedSkill(this.Attacker, this.SkillId);
// End combat action
Send.CombatActionEnd(this.Attacker, this.Id);
}
}