public bool Process() { if (Timer == 0) { Unit.VState = UnitVisualState.Attacking; Unit.AttackFrame = 0; Unit.AttackTime = 0; Unit.DoUpdateView = true; Speed = 0.5f; // disable invisibility on attack, but only if target is not self if (TargetUnit != Unit) { List <SpellEffects.Invisibility> invis = Unit.GetSpellEffects <SpellEffects.Invisibility>(); foreach (SpellEffects.Invisibility inv in invis) { Unit.RemoveSpellEffect(inv); } } } if (Unit.Class.AttackPhases > 1) { if (Speed * Unit.AttackTime >= Unit.Class.AttackFrames[Unit.AttackFrame].Time) { //Unit.AttackFrame = ++Unit.AttackFrame % Unit.Class.AttackPhases; Unit.AttackFrame++; if (Unit.AttackFrame >= Unit.Class.AttackPhases) { Unit.AttackFrame = Unit.Class.AttackPhases - 1; } Unit.AttackTime = -1; Unit.DoUpdateView = true; } Unit.AttackTime++; } else { Unit.AttackFrame = 0; Unit.AttackTime = 0; } if (Speed * Timer >= Unit.Charge && !NetworkManager.IsClient && !DamageDone) { // do damage here! // check if we need to fire a projectile (range) AllodsProjectile proj = AllodsProjectile.None; // default :D // check for castspell in weapon. // check for weapon. List <Spell> castspells = new List <Spell>(); bool procoverride = false; if (Unit.GetObjectType() == MapObjectType.Human) { Item weapon = ((MapHuman)Unit).GetItemFromBody(MapUnit.BodySlot.Weapon); // if weapon is a staff, then castspell is called immediately. // otherwise castspell is only applied when the projectile hits. if (weapon != null && weapon.IsValid) { foreach (ItemEffect eff in weapon.Effects) { if (eff.Type1 == ItemEffect.Effects.CastSpell) { Spell sp = new Spell(eff.Value1); sp.Skill = eff.Value2; sp.User = Unit; sp.Item = weapon; castspells.Add(sp); } } if (weapon.Class.Option.Name == "Staff" || weapon.Class.Option.Name == "Shaman Staff") { procoverride = true; } } } if (Unit.Interaction.GetAttackRange() > 1) { // send this projectile to clients too // make projectile specified in the unit class. proj = (AllodsProjectile)Unit.Class.Projectile; } if (!procoverride && Spell == null) { if (proj != AllodsProjectile.None) { // following offsets are based on unit's width, height and center float cX = Unit.X + Unit.Width * 0.5f + Unit.FracX; float cY = Unit.Y + Unit.Height * 0.5f + Unit.FracY; float tX = TargetUnit.X + TargetUnit.Width * 0.5f + TargetUnit.FracX; float tY = TargetUnit.Y + TargetUnit.Height * 0.5f + TargetUnit.FracY; Vector2 dir = new Vector2(tX - cX, tY - cY).normalized *((Unit.Width + Unit.Height) / 2) / 1.5f; cX += dir.x; cY += dir.y; Server.SpawnProjectileDirectional(proj, Unit, cX, cY, 0, tX, tY, 0, 10, (MapProjectile fproj) => { if (fproj.ProjectileX >= TargetUnit.X + TargetUnit.FracX && fproj.ProjectileY >= TargetUnit.Y + TargetUnit.FracY && fproj.ProjectileX < TargetUnit.X + TargetUnit.FracX + TargetUnit.Width && fproj.ProjectileY < TargetUnit.Y + TargetUnit.FracY + TargetUnit.Height) { //Debug.LogFormat("projectile hit!"); // done, make damage TargetUnit.TakeDamage(DamageFlags, Unit, Damage); // and cast spell foreach (Spell spell in castspells) { Spells.SpellProc sp = spell.Cast(TargetUnit.X + TargetUnit.Width / 2, TargetUnit.Y + TargetUnit.Height / 2, TargetUnit); if (sp != null) { Unit.AddSpellProcessors(sp); } } } fproj.Dispose(); }); } else { TargetUnit.TakeDamage(DamageFlags, Unit, Damage); } } else { // cast spells directly // just do something atm. // todo: check spells that can be only casted on self (distance 0). if (Spell != null) { Spells.SpellProc sp; if (TargetUnit != null) { sp = Spell.Cast(TargetUnit.X + TargetUnit.Width / 2, TargetUnit.Y + TargetUnit.Height / 2, TargetUnit); } else { sp = Spell.Cast(TargetX, TargetY, null); } if (sp != null) { Unit.AddSpellProcessors(sp); } castspells.Clear(); } } foreach (Spell spell in castspells) { Spells.SpellProc sp = spell.Cast(TargetUnit.X + TargetUnit.Width / 2, TargetUnit.Y + TargetUnit.Height / 2, TargetUnit); if (sp != null) { Unit.AddSpellProcessors(sp); } } DamageDone = true; } if (Speed * Timer >= Unit.Charge + Unit.Relax) { return(false); // end of attack } Timer++; return(true); }