public void StopAllActions( bool resetAnim ) { if ( m_RotationTimer != null ) { m_RotationTimer.Stop(); m_RotationTimer = null; } CancelAim( resetAnim ); CancelCharge(); CancelDefensiveFormation( resetAnim ); if ( m_DefenseTimer != null ) { if ( resetAnim ) m_DefenseTimer.Interrupt(); else m_DefenseTimer.Stop(); m_DefenseTimer = null; } if ( m_AttackTimer != null ) { if ( resetAnim ) m_AttackTimer.Interrupt(); else m_AttackTimer.Stop(); m_AttackTimer = null; } if ( resetAnim && m_AnimationTimer != null ) m_AnimationTimer.RefreshAnimation(); }
// I've moved or turned public void OnMoved( Point3D oldLocation ) { m_LastAnimAction = DateTime.Now; Mobile mob = AttachedTo as Mobile; if ( m_RotationTimer != null ) { if ( oldLocation == mob.Location ) return; // we can't possibly stop this awesomeness else // foul play! { m_RotationTimer.Stop(); // stop that shit m_RotationTimer = null; } } CancelQueuedAction(); if ( m_DefensiveFormationTimer != null ) { m_DefensiveFormationTimer.Stop(); m_DefensiveFormationTimer = null; } if ( m_Charging ) { if ( mob.Hidden ) mob.RevealingAction(); if ( ((int)mob.Direction) <= 7 ) // walking in charge cancels it { mob.SendMessage( "You stopped running and thus fumbled your charge." ); CancelCharge(); } } // fumble my attack and defense if ( m_AttackTimer != null ) { m_AttackTimer.Stop(); m_AttackTimer = null; } if ( m_DefenseTimer != null ) { m_DefenseTimer.Stop(); m_DefenseTimer = null; } if ( m_DefensiveFormation ) { if ( mob.Location != oldLocation ) // not just turning CancelDefensiveFormation( false ); // moved while in defensive formation else // this needs a tiny delay, otherwise the animation is sent together with the 'movement' one Timer.DelayCall( TimeSpan.FromMilliseconds( 50 ), new TimerStateCallback( AnimationRefreshCallback ), this ); } if ( m_Aiming ) { if ( mob.Location != oldLocation ) // not just turning CancelAim( false ); // moved while aiming else // this needs a tiny delay, otherwise the animation is sent together with the 'movement' one Timer.DelayCall( TimeSpan.FromMilliseconds( 50 ), new TimerStateCallback( AnimationRefreshCallback ), this ); } // check if i'm showing my back to any of my combatants, if so, check if they're swinging anything, and make it connect List<Mobile> opponents = new List<Mobile>( m_Aggressors ); // necessary due to concurrency foreach ( Mobile opponent in opponents ) { GetCSA( opponent ).CheckForFreeAttack( mob, oldLocation ); GetCSA( opponent ).OnAfterOpponentMoved( mob ); } BaseWeapon weapon = mob.Weapon as BaseWeapon; int myRange = weapon.MaxRange; if ( AttachedTo is BaseCreature && ((BaseCreature)AttachedTo).RangeFight > myRange ) myRange = ((BaseCreature)AttachedTo).RangeFight; if ( m_Charging && m_Opponent != null ) { if ( m_Opponent.InRange( mob, myRange ) ) // we are in range, but charge might not be valid { if ( ValidateCharge( mob, m_Opponent ) ) { //mob.SendMessage( "Charge Okay." ); bool weWin = true; // we're going to strike them now, but let's see if they are, too if ( ValidateCharge( m_Opponent, mob ) ) { // seems they have a valid charge BaseWeapon opponentWeapon = m_Opponent.Weapon as BaseWeapon; int theirRange = opponentWeapon.MaxRange; if ( m_Opponent is BaseCreature && ((BaseCreature)m_Opponent).RangeFight > theirRange ) theirRange = ((BaseCreature)m_Opponent).RangeFight; if ( m_Opponent.InRange( mob, theirRange ) ) { // they can charge-strike us as well! lets make it somewhat randomized double myRidingSkill = mob.Skills[SkillName.Riding].Value + ((IKhaerosMobile)mob).RideBonus; double theirRidingSkill = m_Opponent.Skills[SkillName.Riding].Value + ((IKhaerosMobile)m_Opponent).RideBonus; if ( !mob.Mounted ) myRidingSkill = 0; if ( !m_Opponent.Mounted ) theirRidingSkill = 0; double myWeaponSkill = mob.Skills[weapon.Skill].Base; double theirWeaponSkill = m_Opponent.Skills[opponentWeapon.Skill].Base; double myTacticsSkill = mob.Skills[SkillName.Tactics].Base; double theirTacticsSkill = m_Opponent.Skills[SkillName.Tactics].Base; double ourSum = myRidingSkill + myWeaponSkill + myTacticsSkill; double theirSum = theirRidingSkill + theirWeaponSkill + theirTacticsSkill; double chance = ( ourSum / theirSum ) / 2; // fifty-fifty at same skills and backgrounds if ( Utility.RandomDouble() < chance ) weWin = true; else weWin = false; } } if ( weWin ) { double chargeBonus = CalculateChargeBonus( m_Opponent.Location ); m_ChargeEndTime = DateTime.Now; SetChargeCooldown(mob); ChargeIconRefreshCallback( AttachedTo ); Timer.DelayCall( GetChargeNoRunDelay(), new TimerStateCallback( ChargeIconRefreshCallback ), AttachedTo ); AttackType chargetype = ChargeAttackType( weapon ); m_AttackTimer = new AttackTimer( mob, chargetype, TimeSpan.Zero ); FinishAttack( chargeBonus ); CancelCharge(); } else // they win :( { CombatSystemAttachment opCSA = GetCSA( m_Opponent ); double chargeBonus = opCSA.CalculateChargeBonus( mob.Location ); opCSA.ChargeEndTime = DateTime.Now; SetChargeCooldown(m_Opponent); ChargeIconRefreshCallback( m_Opponent ); Timer.DelayCall( GetChargeNoRunDelay(), new TimerStateCallback( ChargeIconRefreshCallback ), m_Opponent ); AttackType chargetype = ChargeAttackType( m_Opponent.Weapon ); opCSA.AttackTimer = new AttackTimer( m_Opponent, chargetype, TimeSpan.Zero ); opCSA.FinishAttack( chargeBonus ); opCSA.CancelCharge(); } } else // this is a showstopper { if ( m_ErrorMessage != "" ) mob.SendMessage( m_ErrorMessage ); CancelCharge(); } } else if ( m_Opponent.InRange( mob, myRange+2 ) ) { GetCSA( m_Opponent ).ChargeAlert( ChargeAttackType( weapon ) ); // alerts auto combat } } else if ( m_BullRushing ) { IKhaerosMobile km = mob as IKhaerosMobile; if ( (mob.Direction&Direction.Mask) != m_BullRushDirection ) { m_BullRushing = false; mob.SendMessage( "Bull Rush interrupted due to not rushing in a straight line." ); } else { List<Mobile> list = new List<Mobile>(); int moveXOffset = 0; int moveYOffset = 0; switch ( mob.Direction&Direction.Mask ) { case Direction.South: case Direction.North: { moveXOffset = 1; break; } case Direction.West: case Direction.East: { moveYOffset = 1; break; } case Direction.Up: case Direction.Down: { moveXOffset = 1; moveYOffset = -1; break; } case Direction.Left: case Direction.Right: { moveXOffset = 1; moveYOffset = 1; break; } } foreach ( Mobile candidate in mob.GetMobilesInRange( myRange ) ) { if ( candidate != null && mob != candidate ) if ( IsOnCollisionCourse( candidate ) ) list.Add( candidate ); } foreach ( Mobile opponent in list ) { bool randomDir = Utility.RandomBool(); if ( !BaseAI.AreAllies( mob, opponent ) ) { // only attacks non-allies mob.Combatant = opponent; m_AttackTimer = new AttackTimer( mob, AttackType.Swing, TimeSpan.Zero ); FinishAttack(); } Point3D newLoc = new Point3D(); newLoc.X = opponent.Location.X + ( randomDir ? -1 : 1 ) * moveXOffset; newLoc.Y = opponent.Location.Y + ( randomDir ? -1 : 1 ) * moveYOffset; newLoc.Z = opponent.Location.Z; if ( opponent.Map.CanSpawnMobile( newLoc ) ) opponent.SetLocation( newLoc, true ); int additionalPush = 0; if ( km.Feats.GetFeatLevel(FeatList.BullRush) >= 3 ) additionalPush = 2; else if ( km.Feats.GetFeatLevel(FeatList.BullRush) == 2 ) additionalPush = 1; // move x more times if ( additionalPush > 0 ) GetCSA( opponent ).GotBullRushed( ( randomDir ? -1 : 1 ) * moveXOffset, ( randomDir ? -1 : 1 ) * moveYOffset, 2 ); } } m_BullRushSteps++; if ( m_BullRushSteps > km.Feats.GetFeatLevel(FeatList.BullRush) * 2 ) CancelSequences(); } UpdateACBrainExternal(); }
public void FinishAttack( double damageBonus, bool cannotParry, bool spinning, bool opportunity ) { bool flashy = false; AttackType type; bool AOE = AttachedTo != null && AttachedTo is BaseCreature && ( (BaseCreature)AttachedTo ).AOEAttack; if ( !spinning && !AOE ) { if ( m_AttackTimer == null ) return; // we weren't swinging anything else { type = m_AttackTimer.Type; flashy = m_AttackTimer.Flashy; m_AttackTimer.Stop(); } } else type = AttackType.Swing; m_LastMeleeAttackType = type; Mobile attacker = AttachedTo as Mobile; Mobile opponent = m_Opponent; bool throwing = (m_OffHand || type == AttackType.Throw); BaseWeapon weapon = attacker.Weapon as BaseWeapon; if( attacker != null && attacker is IKhaerosMobile ) { SkillName skill = SkillName.Tactics; if( throwing ) skill = SkillName.Throwing; ( (IKhaerosMobile)attacker ).CombatManeuver.CanUseManeuverCheck( attacker, skill ); } double percentageDamage = 0.0; double bestDamage = 0.0; percentageDamage = GetDirectionDamage( type ); if ( type == AttackType.ShieldBash ) percentageDamage = 1.0; bestDamage = GetBestDirectionDamage(); if ( !m_Charging && !spinning && !m_DefensiveFormation && !opportunity ) SpellHelper.Turn( attacker, m_Opponent ); if ( ( spinning || m_Charging || m_DefensiveFormation ) && ((IKhaerosMobile)attacker).OffensiveFeat != FeatList.None ) ((IKhaerosMobile)attacker).DisableManeuver(); // no maneuvers while we're doing these things List<Mobile> defenders = new List<Mobile>(); if ( spinning ) { damageBonus *= ((double)((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.CircularAttack))*0.5; int myRange = ((BaseWeapon)attacker.Weapon).MaxRange; if ( attacker is BaseCreature && ((BaseCreature)attacker).RangeFight > myRange ) myRange = ((BaseCreature)attacker).RangeFight; foreach( Mobile m in attacker.GetMobilesInRange( myRange ) ) { if ( !((IKhaerosMobile)attacker).IsAllyOf( m ) ) defenders.Add( m ); } } else { defenders.Add( opponent ); /*if ( opportunity ) { double damageIgnoreFactor = 0.0; if ( opponent is PlayerMobile ) // reduce damage a bit { PlayerMobile opponentPM = opponent as PlayerMobile; damageIgnoreFactor += opponentPM.HeavyPieces * 0.0625; damageIgnoreFactor += opponentPM.MediumPieces * 0.03125; } damageBonus *= (1.0 - damageIgnoreFactor); }*/ } foreach ( Mobile defender in defenders ) { double attackerDCI, defenderDCI, attackerHCI, defenderHCI; attackerDCI = defenderDCI = attackerHCI = defenderHCI = 0.0; bool defenderDCIRoll, attackerDCIRoll, attackerHCIRoll, defenderHCIRoll; defenderDCIRoll = attackerDCIRoll = attackerHCIRoll = defenderHCIRoll = false; CombatSystemAttachment csa = null; AttackType newAttack = AttackType.Invalid; if ( defender != null ) { csa = GetCSA( defender ); attackerDCI = GetDCI(); // this is used "offensively", but only for unarmed fighting defenderHCI = csa.GetHCI(); // this is used "defensively" but only against unarmed fighting if ( defenderHCI < 0 && attackerDCI < 0 ) // both are negative, we won't favor either of them { double temp = defenderHCI - attackerDCI; attackerDCI -= defenderHCI; defenderHCI = temp; } else if ( defenderHCI < 0 ) // attackerDCI is >=0 attackerDCI -= defenderHCI; // it's negative, so our opponent is adding their negative HCI to our DCI else // attackerDCI < 0 and defenderHCI >= 0 defenderHCI -= attackerDCI; attackerDCIRoll = ( Utility.RandomDouble() < attackerDCI ); defenderHCIRoll = ( Utility.RandomDouble() < defenderHCI ); if ( attackerDCIRoll && defenderHCIRoll ) attackerDCIRoll = defenderHCIRoll = false; // cancel them out defenderDCI = csa.GetDCI(); attackerHCI = GetHCI(); if ( attackerHCI < 0 && defenderDCI < 0 ) // both are negative, we won't favor either of them { double temp = attackerHCI - defenderDCI; defenderDCI -= attackerHCI; attackerHCI = temp; } else if ( attackerHCI < 0 ) // defenderDCI is >=0 defenderDCI -= attackerHCI; // it's negative, so we're adding our negative HCI to our opponent's DCI else // defenderDCI < 0 and attackerHCI >= 0 attackerHCI -= defenderDCI; defenderDCIRoll = ( Utility.RandomDouble() < defenderDCI ); attackerHCIRoll = ( Utility.RandomDouble() < attackerHCI ); if ( defenderDCIRoll && attackerHCIRoll ) // cancel them out defenderDCIRoll = attackerHCIRoll = false; if ( m_Charging || spinning || type == AttackType.ShieldBash || m_DefensiveFormation || type == AttackType.Throw ) { attackerHCIRoll = false; // there's only one possible direction when doing these attacks attackerDCIRoll = false; } if ( type == AttackType.ShieldBash || type == AttackType.Throw ) { defenderDCIRoll = false; // this cannot be parried anyway defenderHCIRoll = false; // they can't change direction to avoid interruption } if ( csa.Charging || csa.RotationTimer != null || csa.DefensiveFormation || (csa.AttackTimer != null && (csa.AttackTimer.Type == AttackType.Throw || csa.AttackTimer.Type == AttackType.ShieldBash) ) ) { defenderHCIRoll = false; // they cannot change direction attackerDCIRoll = false; // we can't interrupt that, no matter the direction } newAttack = AttackType.Invalid; if ( attackerHCIRoll ) { foreach ( AttackType atktype in GetPossibleAttacks() ) { if ( atktype == type ) continue; // skip same attack else if ( newAttack == AttackType.Invalid ) newAttack = atktype; else if ( GetDirectionDamage( atktype ) > GetDirectionDamage( newAttack ) ) newAttack = atktype; } if ( newAttack == AttackType.Invalid ) attackerHCIRoll = false; } } int myRange = ((BaseWeapon)attacker.Weapon).MaxRange; if ( attacker is BaseCreature && ((BaseCreature)attacker).RangeFight > myRange ) myRange = ((BaseCreature)attacker).RangeFight; if ( defender != null && attacker.InRange( defender.Location, myRange ) && defender.Alive && type != AttackType.Throw && attacker.CanSee( defender ) && attacker.InLOS( defender )) { // melee bool parried = false; if ( csa.DefenseTimer != null && !cannotParry ) // defending { DefenseType deftype = csa.DefenseTimer.Type; BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield; BaseWeapon twohander = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon; BaseWeapon onehander = defender.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon; bool weaponCannotBlock = weapon.CannotBlock; if( attacker is PlayerMobile && ( (PlayerMobile)attacker ).Claws != null ) weaponCannotBlock = false; BaseWeapon opponentWeapon = defender.Weapon as BaseWeapon; bool opponentWeaponCannotBlock = opponentWeapon.CannotBlock; if( defender is PlayerMobile && ( (PlayerMobile)defender ).Claws != null ) opponentWeaponCannotBlock = false; if ( ( !opponentWeaponCannotBlock || defender.Body.Type != BodyType.Human ) || weaponCannotBlock || shield != null ) { if ( ( type == AttackType.Swing && deftype == DefenseType.ParrySwing ) || // this handles parries ( type == AttackType.Thrust && deftype == DefenseType.ParryThrust ) || ( type == AttackType.Overhead && deftype == DefenseType.ParryOverhead ) || defenderDCIRoll ) { if ( attackerHCIRoll ) { type = newAttack; percentageDamage = GetDirectionDamage( type ); } else { if ( shield != null ) { int dmg = (int)weapon.GetScaledDamage( attacker, defender, damageBonus ); shield.OnHit( weapon, dmg ); PlayBlockSound( attacker, defender, attacker.Weapon as BaseWeapon, shield ); } else if ( twohander != null ) { twohander.DegradeWeapon(); // this should degrade more. PlayBlockSound( attacker, defender, attacker.Weapon as BaseWeapon, twohander ); } else { if ( onehander != null ) // could be fists onehander.DegradeWeapon(); // this should degrade more. PlayBlockSound( attacker, defender, attacker.Weapon as BaseWeapon, defender.Weapon as BaseWeapon ); } parried = true; if ( !spinning ) { weapon.InitializeManeuver( attacker, null, false ); weapon.EndManeuver( attacker ); } csa.DefenseTimer.Parries++; PlayerMobile pmatk = attacker as PlayerMobile; int blut = weapon.AosElementDamages.Blunt; if( attacker.Weapon is Fists && attacker is IKhaerosMobile && ((IKhaerosMobile)attacker).TechniqueLevel > 0 ) { int slax = 0; int pirc = 0; blut = 100; if( ((IKhaerosMobile)attacker).Technique == "slashing" ) slax = ((IKhaerosMobile)attacker).TechniqueLevel; else if( ((IKhaerosMobile)attacker).Technique == "piercing" ) pirc = ((IKhaerosMobile)attacker).TechniqueLevel; blut -= ((IKhaerosMobile)attacker).TechniqueLevel; if( attacker is PlayerMobile && ( (PlayerMobile)attacker ).Claws != null ) blut = 0; } if ( Utility.RandomDouble() < (((double)(blut))/200.0) ) weapon.OnSplash( attacker, defender, damageBonus*percentageDamage ); // 20% dmg done in OnHit if ( pmatk != null && pmatk.HasSpecializedWeaponSkill() ) { CombatSystemAttachment.FightingStyleOnParry( attacker ); /*PlayerMobile pmdef = defender as PlayerMobile; if ( pmdef != null && pmdef.HasSpecializedWeaponSkill() ) { CombatSystemAttachment.FightingStyleOnHit( defender ); }*/ } PlayerMobile pmdef = defender as PlayerMobile; if ( pmdef != null && pmdef.HasSpecializedWeaponSkill() ) { // This is a positive bonus for the defender, but it's not a hit, it's a parry (confusing :\) CombatSystemAttachment.FightingStyleOnHit( defender ); } if ( ((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.Buildup) > 0 ) CombatSystemAttachment.BuildupOnParry( attacker ); if ( !spinning ) { TimeSpan extraPenalty = TimeSpan.Zero; IKhaerosMobile km = defender as IKhaerosMobile; if ( km != null ) { BaseWeapon greatweapon = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon; if( greatweapon != null && !(greatweapon is BaseRanged) ) { if ( km.Feats.GetFeatLevel(FeatList.GreatweaponFighting) == 1 ) extraPenalty += TimeSpan.FromSeconds( 0.125 ); else if ( km.Feats.GetFeatLevel(FeatList.GreatweaponFighting) == 2 ) extraPenalty += TimeSpan.FromSeconds( 0.25 ); else if ( km.Feats.GetFeatLevel(FeatList.GreatweaponFighting) == 3 ) extraPenalty += TimeSpan.FromSeconds( 0.5 ); } if ( shield != null ) { if ( km.Feats.GetFeatLevel(FeatList.ShieldMastery) == 1 ) extraPenalty += TimeSpan.FromSeconds( 0.125 ); else if ( km.Feats.GetFeatLevel(FeatList.ShieldMastery) == 2 ) extraPenalty += TimeSpan.FromSeconds( 0.25 ); else if ( km.Feats.GetFeatLevel(FeatList.ShieldMastery) == 3 ) extraPenalty += TimeSpan.FromSeconds( 0.5 ); } } if (opportunity) m_NextAttackAction += extraPenalty; else m_NextAttackAction = DateTime.Now + ComputeNextSwingTime() + extraPenalty; // extra penalty since we were parried } if ( m_Charging ) // we're charging and the guy parried, this is the mounted momentum feat { if ( ((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.MountedMomentum) > 0 && !defender.Mounted ) { double distance = CalculateChargeDistance( defender.Location ); // this was csa.CalcCh...? Why? if ( distance - (19-(((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.MountedMomentum)*3)) >= 0 ) { // sufficient distance for feat to activate if( !csa.PerformingSequence ) csa.DoTrip( 1 ); // 2 seconds } } } } } } } if ( !parried ) { if( ((IKhaerosMobile)defender).CanDodge && ( !defender.Mounted || Utility.RandomMinMax(1,100) < BaseWeapon.GetRacialMountAbility(defender, typeof(ForestStrider)) ) && !csa.PerformingSequence ) { if( ((IKhaerosMobile)defender).Dodged() ) { parried = true; if ( !spinning ) { weapon.InitializeManeuver( attacker, null, false ); weapon.EndManeuver( attacker ); ((IKhaerosMobile)attacker).DisableManeuver(); if ( !opportunity ) { if ( type == AttackType.ShieldBash ) m_NextAttackAction = DateTime.Now + TimeSpan.FromSeconds( 1.0 ); else m_NextAttackAction = DateTime.Now + ComputeNextSwingTime(); } } /*if ( csa.RotationTimer == null ) // no more delay for dodging { //csa.NextAction = DateTime.Now + TimeSpan.FromSeconds( 0.5 ); csa.CombatInterrupt( false ); }*/ // Dodge no longer interrupts attacker.PlaySound( 1307 ); defender.Say( "*dodges*" ); /*if ( csa.RotationTimer == null ) { switch ( (int)type ) { case (int)AttackType.Swing: { if ( defender.Body.Type == BodyType.Human ) csa.Animate( 21, 1, 1, false, false, 4 ); break; } case (int)AttackType.Thrust: { if ( defender.Body.Type == BodyType.Human ) csa.Animate( 30, 7, 1, true, false, 0 ); break; } case (int)AttackType.Overhead: { goto case (int)AttackType.Thrust; // same anim } } csa.UpdateACBrainExternal(); }*/ } } if ( !parried ) { bool noInterruption = defender is BaseCreature && ( (BaseCreature)defender ).CantBeInterrupted; bool cantInterrupt = attacker is BaseCreature && ( (BaseCreature)attacker ).CantInterrupt; bool success = true; if ( m_Charging || m_DefensiveFormation ) { double skill = (attacker.Skills[weapon.Skill].Base)/100.0; if ( Utility.RandomDouble() > skill ) success = false; } if ( success ) // this can only miss when charging with below 100.0 skill { if ( type == AttackType.ShieldBash ) attacker.PlaySound( 0x3AC ); else PlayHitSound( attacker, defender ); if ( weapon is Fists && !defender.Mounted && csa.AttackTimer != null ) { if ( csa.AttackTimer.Type == type || attackerDCIRoll || csa.AttackTimer.Type == AttackType.Throw ) { // throw always gets interrupted // this is also an interrupt, as it is an external animation if ( attackerDCIRoll ) // this and the defenderHCIRoll cannot happen both, we made sure of that { type = csa.AttackTimer.Type; percentageDamage = GetDirectionDamage( type ); } bool changedDirection = false; if ( defenderHCIRoll ) // they might change direction { AttackType newType = AttackType.Invalid; foreach ( AttackType atktype in csa.GetPossibleAttacks() ) { if ( atktype == csa.AttackTimer.Type ) continue; // skip same attack else if ( newType == AttackType.Invalid ) newType = atktype; else if ( csa.GetDirectionDamage( atktype ) > csa.GetDirectionDamage( newType ) ) newType = atktype; } if ( newType != AttackType.Invalid ) { csa.AttackTimer.Type = newType; changedDirection = true; } } if ( !csa.PerformingSequence && csa.RotationTimer == null && !changedDirection ) { if ( !opportunity && !noInterruption && !cantInterrupt ) // opportunity attacks do not interrupt { weapon.PlayHurtAnimation( defender ); if ( defender.Mounted ) // there's no hurt animation for it ResetAnimation( defender ); csa.CombatInterrupt( true ); } if ( ((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.Disarm) > 0 && attacker is PlayerMobile ) { if ( ((PlayerMobile)attacker).CanUseMartialPower ) { // Disarm class will check for fists, not movable, etc.. we don't have to if ( Utility.RandomDouble() <= ((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.Disarm)*0.01 ) Server.Misc.Disarm.Effect( attacker, defender ); } } } } } else { if ( !csa.PerformingSequence && csa.RotationTimer == null ) { if( !opportunity && !noInterruption && !cantInterrupt ) { // AoO does not interrupt weapon.PlayHurtAnimation( defender ); if ( defender.Mounted ) // there's no hurt animation for it ResetAnimation( defender ); csa.CombatInterrupt( true ); } } } if ( csa.CruiseControl && csa.ACBrain != null && csa.ACBrain.Enabled ) csa.ACBrain.RegisterFrequency( type, defender ); weapon.OnBeforeSwing( attacker, defender ); weapon.OnSwing( attacker, defender, damageBonus*percentageDamage, false ); PlayerMobile pmatk = attacker as PlayerMobile; if ( type != AttackType.ShieldBash ) { if ( pmatk != null ) { if ( pmatk.HasSpecializedWeaponSkill() ) CombatSystemAttachment.FightingStyleOnHit( attacker ); } if ( attacker.Weapon is Fists && ((IKhaerosMobile)attacker).CanUseMartialStance ) { if ( ((IKhaerosMobile)attacker).Feats.GetFeatLevel(FeatList.Buildup) > 0 ) CombatSystemAttachment.BuildupOnHit( attacker ); } } } else attacker.SendMessage( "Your charge attack misses!" ); if ( !spinning ) { if ( !opportunity ) { if ( type == AttackType.ShieldBash ) m_NextAttackAction = DateTime.Now + TimeSpan.FromSeconds( 1.0 ); else m_NextAttackAction = DateTime.Now + ComputeNextSwingTime(); } } } } if( attacker is BaseCreature ) { bool melee = !( attacker.Weapon is BaseRanged ) && type != AttackType.Throw; ( (BaseCreature)attacker ).OnGaveAttack( melee, parried, defender ); } if( defender is BaseCreature ) { bool melee = !( attacker.Weapon is BaseRanged ) && type != AttackType.Throw; ( (BaseCreature)defender ).OnReceivedAttack( melee, parried, attacker ); } } else if ( type == AttackType.Throw && defender != null && defender.Alive ) // throw attacks, this can't be spinning so who cares { if( weapon is AzhuranBoomerang && !m_OffHand ) // this isn't really "thrown" mechanically { if ( defender.InRange( attacker, myRange ) && attacker.CanSee( defender ) && attacker.InLOS( defender ) ) { attacker.PlaySound( 1329 ); weapon.OnSwing( attacker, defender, weapon.RangedPercentage, false ); if ( !opportunity ) m_NextAttackAction = DateTime.Now + ComputeNextSwingTime(); } } else // actually mechanically thrown weapon { IKhaerosMobile km = attacker as IKhaerosMobile; if ( defender.InRange( attacker, 3 + km.Feats.GetFeatLevel(FeatList.ThrowingMastery) ) && !defender.Deleted && defender.Map == attacker.Map && attacker.CanSee( defender ) && attacker.InLOS( defender )) { bool snatched = false; bool deflected = false; if( defender is PlayerMobile ) { if( ( (PlayerMobile)defender ).Snatched() ) snatched = true; if( ( (PlayerMobile)defender ).DeflectedProjectile() ) deflected = true; } if( m_OffHand ) weapon = ((PlayerMobile)attacker).OffHandWeapon; double damagebonus = 1.0; if( weapon.Throwable ) { switch( km.Feats.GetFeatLevel(FeatList.Finesse) ) { case 0: break; case 1: damagebonus = 1.1; break; case 2: damagebonus = 1.2; break; case 3: damagebonus = 1.3; break; } } /*if ( km.Feats.GetFeatLevel(FeatList.Finesse) > 0 && Utility.RandomDouble() <= (0.6*(km.Feats.GetFeatLevel(FeatList.Finesse))/(weapon.Weight*weapon.Weight) ) ) damagebonus += 1.0;*/ /*if (km.Feats.GetFeatLevel(FeatList.Finesse) > 0) damagebonus += (0.2 * km.Feats.GetFeatLevel(FeatList.Finesse));*/ km.OffensiveFeat = FeatList.ThrowingMastery; Point3D loc = defender.Location; Map map = defender.Map; //attacker.MovingEffect( defender, weapon.ItemID, 9, 1, false, false ); SendCombatAlerts( attacker, defender, weapon.ItemID, 5, 0, false, false, weapon.Hue, 1, 9501, 1, 0, EffectLayer.Waist, 0x100 ); if( snatched ) { attacker.PlaySound( 1329 ); defender.Emote( "*snatches the weapon thrown at {0} by {1}*", defender.Female == true ? "her" : "him", attacker.Name ); } else if( deflected ) { attacker.PlaySound( 1329 ); defender.Emote( "*uses {0} shield to deflect the weapon thrown at {1} by {2}*", defender.Female == true ? "her" : "him", defender.Female == true ? "her" : "him", attacker.Name ); } else { attacker.PlaySound( 1329 ); // 50% damage penalty from off-hand throwing if( m_OffHand ) damagebonus *= 0.5; weapon.OnSwing( attacker, defender, damagebonus, true ); // bestDamage will be multiplied with in BaseWeapon due to "cleave" } if ( !snatched ) weapon.MoveToWorld( loc, map ); else defender.AddToBackpack( weapon ); // N***a stole my ______! // auto-equipping if( ( weapon is Dagger || weapon is TyreanThrowingAxe ) && !m_OffHand && attacker is PlayerMobile && ( (PlayerMobile)attacker ).Feats.GetFeatLevel( FeatList.ThrowingMastery ) > 2 ) { PlayerMobile m = attacker as PlayerMobile; if( m.CraftContainer != null && !m.CraftContainer.Deleted && m.Backpack != null && !m.Backpack.Deleted && m.CraftContainer.IsChildOf(m.Backpack) ) { foreach( Item item in m.CraftContainer.Items ) { if( item is BaseWeapon && weapon.NameType == ((BaseWeapon)item).NameType ) { m.EquipItem( item ); break; } } } } if ( !opportunity ) m_NextAttackAction = DateTime.Now + ComputeNextSwingTime(); } } } else // miss due to range { if ( !spinning ) { weapon.InitializeManeuver( attacker, null, false ); weapon.EndManeuver( attacker ); } PlayMissSound( attacker, defender ); } } if ( spinning ) { m_RotationTimer = null; if ( !opportunity ) m_NextAttackAction = DateTime.Now + ComputeNextSwingTime(); } m_AttackTimer = null; if ( !flashy ) // otherwise another attack is coming right after { UpdateACBrainExternal(); UpdateQueueDelay(); } if (attacker != null && !attacker.Deleted && attacker.Weapon != null && attacker.Weapon is BaseWeapon && !(attacker.Weapon as BaseWeapon).Deleted) { if ((attacker.Weapon as BaseWeapon).MaxHitPoints < 1) { attacker.SendMessage("Your weapon has fallen apart!"); attacker.SendSound(0x3E8, attacker.Location); (attacker.Weapon as BaseWeapon).Delete(); } } }
public void OnAfterOpponentMoved( Mobile opponent ) { Mobile mob = AttachedTo as Mobile; if ( opponent == null || mob.Combatant != opponent || !opponent.Alive ) return; CombatSystemAttachment opponentCSA = GetCSA( opponent ); if ( m_Aiming || m_DefensiveFormation ) { int myRange = ((BaseWeapon)mob.Weapon).MaxRange; if ( mob is BaseCreature && ((BaseCreature)mob).RangeFight > myRange ) myRange = ((BaseCreature)mob).RangeFight; if ( opponent.InRange( mob, myRange ) && m_DefensiveFormation ) { if ( opponentCSA.Charging ) // he charged straight into us { m_AttackTimer = new AttackTimer( mob, AttackType.Thrust, TimeSpan.Zero ); FinishAttack( opponentCSA.CalculateChargeBonus( mob.Location ), true ); // steal his damage bonus CancelDefensiveFormation( true ); } else if ( ((int)opponent.Direction) > 7 ) // running { m_AttackTimer = new AttackTimer( mob, AttackType.Thrust, TimeSpan.Zero ); FinishAttack( 1.0, true ); CancelDefensiveFormation( true ); } } if ( mob.Direction != mob.GetDirectionTo( mob.Combatant ) && mob.CanSee(mob.Combatant) ) { mob.Direction = mob.GetDirectionTo( mob.Combatant ); if ( m_AnimationTimer != null ) m_AnimationTimer.RefreshAnimation(); } } else if ( !m_PerformingSequence && DateTime.Now >= (mob.LastMoveTime + TimeSpan.FromSeconds(0.75)) && mob.InRange( mob.Combatant, 3 ) ) { if ( mob.Direction != mob.GetDirectionTo( mob.Combatant ) && mob.CanSee(mob.Combatant) ) { mob.Direction = mob.GetDirectionTo( mob.Combatant ); } } UpdateACBrainExternal(); }
// melee attacks and throwing only public bool BeginAttack( AttackType attacktype, AttackFlags flags, bool playerInitiated ) { if ( attacktype == AttackType.Invalid ) return false; bool flashyFollowup = ( (flags&AttackFlags.FlashyFollowup) != AttackFlags.None ); bool disregardDelay = ( (flags&AttackFlags.DisregardDelay) != AttackFlags.None ); bool feint = false; bool flashy = false; Mobile attacker = AttachedTo as Mobile; IKhaerosMobile km = attacker as IKhaerosMobile; BaseWeapon weapon = attacker.Weapon as BaseWeapon; BaseShield shield = attacker.FindItemOnLayer( Layer.TwoHanded ) as BaseShield; if( attacker is PlayerMobile ) { PlayerMobile m = attacker as PlayerMobile; if( !m.EnableOffHand ) m.OffHandWeapon = null; else { m.EnableOffHand = false; if( !Commands.OffHandThrow.EnableOffHand( m ) ) return false; } } m_OffHand = ( attacker is PlayerMobile && ( (PlayerMobile)attacker ).OffHandThrowing ) && attacktype == AttackType.Throw; if ( !CanBeginCombatAction( ) || !CanBeginAttack( flags ) ) return false; if ( attacktype == AttackType.Throw ) { if( m_OffHand ) weapon = ((PlayerMobile)attacker).OffHandWeapon; if ( !CanThrow() ) return false; if ( weapon is AzhuranBoomerang ) // requires still time { if ( !((BaseRanged)weapon).IsStill( attacker ) ) { if ( playerInitiated ) { m_ErrorMessage = ""; DisplayQueueResultMessage( QueueRanged() ); } return false; } } } else if ( !CanBeginMeleeAttack() ) return false; else if ( attacktype == AttackType.ShieldBash && shield == null ) { m_ErrorMessage = "A shield is needed in order to perform a shield bash!"; return false; } if ( attacker.Mounted ) { if ( attacktype == AttackType.Swing || attacktype == AttackType.Circular ) { m_ErrorMessage = "Cannot perform swing attacks while mounted."; return false; } else if ( attacktype == AttackType.Thrust && !(CanThrustOnMount()) ) return false; else if ( attacktype == AttackType.ShieldBash ) { m_ErrorMessage = "This attack cannot be performed while mounted."; return false; } } StopAllActions( false ); if ( attacktype == AttackType.Circular ) // it is important that all of these are part of the same if/elseif chain { // as some feats are actually additional attacks, but that does not mean they can be used together with maneuvers. PlayerMobile pm = attacker as PlayerMobile; if ( km.Feats.GetFeatLevel(FeatList.CircularAttack) <= 0 ) { m_ErrorMessage = "You do not have the required feat."; return false; } else if ( pm == null || weapon.Layer != Layer.TwoHanded || weapon is BaseRanged ) { m_ErrorMessage = "You can only perform this attack with two-handed melee weapons."; return false; } else if ( weapon.NameType != pm.WeaponSpecialization && weapon.NameType != pm.SecondSpecialization ) { m_ErrorMessage = "You can only perform this attack with a weapon you have specialized in."; return false; } else if ( !BaseWeapon.CheckStam( attacker, km.Feats.GetFeatLevel(FeatList.CircularAttack), false, false ) ) { m_ErrorMessage = ""; // will be displayed by the checkstam thingy return false; } } else if ( attacktype == AttackType.ShieldBash ) { if ( km.Feats.GetFeatLevel(FeatList.ShieldBash) <= 0 ) { m_ErrorMessage = "You do not have the required feat."; return false; } else if ( !BaseWeapon.CheckStam( attacker, km.Feats.GetFeatLevel(FeatList.ShieldBash), false, false ) ) { m_ErrorMessage = ""; // will be displayed by the checkstam thingy return false; } km.CombatManeuver = new ShieldBash( km.Feats.GetFeatLevel(FeatList.ShieldBash) ); km.OffensiveFeat = FeatList.ShieldBash; } else if ( km.OffensiveFeat == FeatList.Feint ) { if ( BaseWeapon.CheckStam( attacker, km.Feats.GetFeatLevel(FeatList.Feint), false, false ) ) feint = true; weapon.EndManeuver( attacker ); } else if ( km.OffensiveFeat == FeatList.FlashyAttack ) { if ( BaseWeapon.CheckStam( attacker, km.Feats.GetFeatLevel(FeatList.FlashyAttack), false, false ) ) flashy = true; weapon.EndManeuver( attacker ); } // standard delay is 1 second double delay = ComputeAnimationDelay(); if ( attacktype == AttackType.ShieldBash ) delay = 4.0 - (3.0*((attacker.Skills[SkillName.Parry].Base)/100.0)); if ( feint ) delay *= 2.5-(((double)km.Feats.GetFeatLevel(FeatList.Feint))*0.5); // lvl 1 -> 2x slower, lvl 2 -> 1.5x slower, lvl 3 -> same as normal if ( attacktype == AttackType.Throw ) { if ( !(weapon is AzhuranBoomerang) ) // otherwise the duration is already calculated delay = 0.5; } int animspeed = (int)(delay*2); IKhaerosMobile kmob = attacker as IKhaerosMobile; AttackTimer newTimer = null; if ( attacktype != AttackType.Circular ) { newTimer = new AttackTimer( attacker, attacktype, TimeSpan.FromSeconds( delay ) ); newTimer.Flashy = flashy; newTimer.Feint = feint; newTimer.FlashyFollowup = flashyFollowup; } if ( attacktype == AttackType.Throw ) // throws cannot have a rotationtimer anyway { SpellHelper.Turn( attacker, m_Opponent ); m_AttackTimer = newTimer; } if ( attacktype != AttackType.Circular ) // not if we're spinning { m_AttackTimer = newTimer; if ( m_Opponent != null ) { // checking of AoO is necessary so we don't force Turn, which causes jerky movement m_NextAttackAction = DateTime.Now + ComputeNextSwingTime() + TimeSpan.FromSeconds( delay ); if ( !CheckForFreeAttack( m_Opponent, true ) ) // attack of opportunity { m_AttackTimer = null; SpellHelper.Turn( attacker, m_Opponent ); m_AttackTimer = newTimer; } else // we DID get an AoO, so we're done here { if ( BandageContext.GetContext( attacker ) != null ) { BandageContext.GetContext( attacker ).StopHeal(); if ( attacker is IKhaerosMobile ) { if ( ((IKhaerosMobile)attacker).HealingTimer != null ) { ((IKhaerosMobile)attacker).HealingTimer.Stop(); ((IKhaerosMobile)attacker).HealingTimer = null; } } } return true; } } } int zMod = 0; if ( attacker is BaseCreature ) zMod = ((BaseCreature)attacker).Height; if ( !feint ) { if ( attacktype == AttackType.ShieldBash ) m_NextAttackAction = DateTime.Now + TimeSpan.FromSeconds( 1.0 ); else m_NextAttackAction = DateTime.Now + ComputeNextSwingTime() + TimeSpan.FromSeconds( delay ); } switch ( (int)attacktype ) { case (int)AttackType.ShieldBash: { if ( attacker.Body.Type == BodyType.Human ) Animate( 30, 7, 1, true, false, animspeed ); else { int[] anim = BAData.GetAnimation( attacker, AttackType.ShieldBash ); if ( anim != null ) Animate( anim[0], anim[1], 1, true, false, animspeed ); } break; } case (int)AttackType.Throw: { if ( attacker.Body.Type == BodyType.Human ) { if ( !attacker.Mounted ) { if( weapon.Layer == Layer.OneHanded || m_OffHand ) Animate( 11, 7, 1, true, false, animspeed ); else Animate( 12, 7, 1, true, false, animspeed ); } else { if( weapon.Layer == Layer.OneHanded || m_OffHand ) Animate( 26, 8, 1, true, false, animspeed ); else Animate( 29, 7, 1, true, false, animspeed ); } } else { int[] anim = BAData.GetAnimation( attacker, AttackType.Throw ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Thrust ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Overhead ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Swing ); if ( anim != null ) Animate( anim[0], anim[1], 1, true, false, animspeed ); } break; } case (int)AttackType.Circular: goto case (int)AttackType.Swing; case (int)AttackType.Swing: { IEntity from = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + zMod ), attacker.Map ); IEntity to = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + 25 + zMod*2 ), attacker.Map ); SendCombatAlerts( from, to, 9556, 3, 0, true, false, 3, 2, 9501, 1, 0, EffectLayer.Head, 0x100 ); if ( attacker.Body.Type == BodyType.Human ) { if ( weapon is Fists ) { if ( kmob.Stance is VenomousWay ) Animate( 9, 7, 1, true, false, animspeed ); // karate chop else Animate( 31, 7, 1, true, false, animspeed ); // regular wrestling } else { if ( weapon.Layer == Layer.TwoHanded ) Animate( 13, 7, 1, true, false, animspeed ); else Animate( 9, 7, 1, true, false, animspeed ); } } else { int[] anim = BAData.GetAnimation( attacker, AttackType.Swing ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Thrust ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Overhead ); if ( anim != null ) Animate( anim[0], anim[1], 1, true, false, animspeed ); } break; } case (int)AttackType.Thrust: { IEntity from = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + zMod ), attacker.Map ); IEntity to = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + 25 + zMod*2 ), attacker.Map ); SendCombatAlerts( from, to, 9556, 3, 0, true, false, 62, 2, 9501, 1, 0, EffectLayer.Head, 0x100 ); if ( attacker.Body.Type == BodyType.Human ) { if ( attacker.Mounted ) Animate( 28, 7, 1, false, false, animspeed ); else { if ( weapon.Layer == Layer.TwoHanded ) Animate( 14, 7, 1, true, false, animspeed ); else { if ( weapon is Fists && kmob.Stance is SearingBreath ) Animate( 32, 5, 1, false, false, animspeed+2 ); // headbutt! Nah. People could abuse bow to do this. else if ( weapon is Fists && kmob.Stance is TempestuousSea ) // this is fine, though, heartstopper punch! Animate( 16, 7, 1, true, false, animspeed ); else Animate( 10, 7, 1, true, false, animspeed ); } } } else { int[] anim = BAData.GetAnimation( attacker, AttackType.Thrust ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Overhead ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Swing ); if ( anim != null ) Animate( anim[0], anim[1], 1, true, false, animspeed ); } break; } case (int)AttackType.Overhead: { IEntity from = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + zMod ), attacker.Map ); IEntity to = new Entity( Server.Serial.Zero, new Point3D( attacker.X, attacker.Y, attacker.Z + 25 + zMod*2 ), attacker.Map ); SendCombatAlerts( from, to, 9556, 3, 0, true, false, 37, 2, 9501, 1, 0, EffectLayer.Head, 0x100 ); if ( attacker.Body.Type == BodyType.Human ) { if ( attacker.Mounted ) { if ( weapon.Layer == Layer.TwoHanded ) Animate( 29, 7, 1, true, false, animspeed ); else Animate( 26, 7, 1, true, false, animspeed ); } else { if ( weapon.Layer == Layer.TwoHanded ) Animate( 12, 7, 1, true, false, animspeed ); else Animate( 11, 7, 1, true, false, animspeed ); } } else { int[] anim = BAData.GetAnimation( attacker, AttackType.Overhead ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Thrust ); if ( anim == null ) anim = BAData.GetAnimation( attacker, AttackType.Swing ); if ( anim != null ) Animate( anim[0], anim[1], 1, true, false, animspeed ); } break; } } if (attacktype == AttackType.Circular) { m_RotationTimer = new RotationTimer(attacker, TimeSpan.FromSeconds(delay)); m_RotationTimer.Start(); } else { if (m_AttackTimer == null) { attacker.SendMessage("There was an error in the CombatSystemAttachment @ line 2226."); return false; } else m_AttackTimer.Start(); } // this delays as if we were parried, but if we actually won't be, then the delay will shorten // what if we get interrupted, though? it should count as a hit on our part, no extra delay. if ( BandageContext.GetContext( attacker ) != null ) { BandageContext.GetContext( attacker ).StopHeal(); if ( attacker is IKhaerosMobile ) { if ( ((IKhaerosMobile)attacker).HealingTimer != null ) { ((IKhaerosMobile)attacker).HealingTimer.Stop(); ((IKhaerosMobile)attacker).HealingTimer = null; } } } if ( m_Opponent != null ) { GetCSA( m_Opponent ).UpdateACBrainExternal(); } return true; }