public ActionChain DoSwingMotion(WorldObject target, out float animLength) { // FIXME: proper swing animation speeds var baseSpeed = GetAnimSpeed(); var animSpeedMod = IsDualWieldAttack ? 1.2f : 1.0f; // dual wield swing animation 20% faster var animSpeed = baseSpeed * animSpeedMod; var swingAnimation = new MotionItem(GetSwingAnimation(), animSpeed); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, swingAnimation.Motion, animSpeed); var motion = new UniversalMotion(CurrentMotionState.Stance, swingAnimation); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.TurnSpeed = 2.25f; motion.HasTarget = true; motion.TargetGuid = target.Guid; CurrentMotionState = motion; var actionChain = new ActionChain(); actionChain.AddAction(this, () => EnqueueBroadcastMotion(motion)); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventCombatCommmenceAttack(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); return(actionChain); }
/// <summary> /// Executes the weapon reload animation for the player /// </summary> public float ReloadMotion() { var reloadAnimation = new MotionItem(GetReloadAnimation(), 1.0f); var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, reloadAnimation); var motion = new UniversalMotion(CurrentMotionState.Stance); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.ForwardCommand = (uint)MotionCommand.Reload; motion.MovementData.TurnSpeed = 2.25f; //motion.HasTarget = true; //motion.TargetGuid = target.Guid; CurrentMotionState = motion; var actionChain = new ActionChain(); actionChain.AddAction(this, () => DoMotion(motion)); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => { motion.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motion); CurrentMotionState = motion; }); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventCombatCommmenceAttack(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); actionChain.EnqueueChain(); var weapon = GetEquippedWeapon(); var reloadTime = weapon.DefaultCombatStyle == CombatStyle.Crossbow ? 3.2f : 1.6f; return(animLength * reloadTime); }
/// <summary> /// Switches a player or creature to magic casting stance /// </summary> public float HandleSwitchToMagicCombatMode() { var wand = GetEquippedWand(); if (wand == null) { return(0.0f); } var animLength = SwitchCombatStyles(); animLength += MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.Magic); var motion = new Motion(MotionStance.Magic); ExecuteMotion(motion); var player = this as Player; if (player != null) { player.HandleActionTradeSwitchToCombatMode(player.Session); player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.Magic)); } //Console.WriteLine("HandleSwitchToMagicCombatMode() - animLength: " + animLength); return(animLength); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public void DoSwingMotion(WorldObject target, MotionCommand motionCommand, out float animLength, out List <float> attackFrames) { if (ForcePos) { SendUpdatePosition(); } //Console.WriteLine($"{maneuver.Style} - {maneuver.Motion} - {maneuver.AttackHeight}"); var baseSpeed = GetAnimSpeed(); var animSpeedMod = IsDualWieldAttack ? 1.2f : 1.0f; // dual wield swing animation 20% faster var animSpeed = baseSpeed * animSpeedMod; animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, motionCommand, animSpeed); attackFrames = MotionTable.GetAttackFrames(MotionTableId, CurrentMotionState.Stance, motionCommand); var motion = new Motion(this, motionCommand, animSpeed); motion.MotionState.TurnSpeed = 2.25f; if (!AiImmobile) { motion.MotionFlags |= MotionFlags.StickToObject; } motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); }
/// <summary> /// Switches a player or creature to melee attack stance /// </summary> public float HandleSwitchToMeleeCombatMode() { // get appropriate combat stance for currently wielded items var combatStance = GetCombatStance(); var animLength = SwitchCombatStyles(); animLength += MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, (MotionCommand)combatStance); var motion = new UniversalMotion(combatStance); motion.MovementData.CurrentStyle = (uint)combatStance; ExecuteMotion(motion); var player = this as Player; if (player != null) { player.HandleActionTradeSwitchToCombatMode(player.Session); player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.Melee)); } //Console.WriteLine("HandleSwitchToMeleeCombatMode() - animLength: " + animLength); return(animLength); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public void DoSwingMotion(WorldObject target, CombatManeuver maneuver, out float animLength, out List <float> attackFrames) { if (ForcePos) { SendUpdatePosition(); } var animSpeed = GetAnimSpeed(); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, maneuver.Motion, animSpeed); attackFrames = MotionTable.GetAttackFrames(MotionTableId, CurrentMotionState.Stance, maneuver.Motion); var motion = new Motion(this, maneuver.Motion, animSpeed); motion.MotionState.TurnSpeed = 2.25f; if (!AiImmobile) { motion.MotionFlags |= MotionFlags.StickToObject; } motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); }
/// <summary> /// Handles switching between combat stances: /// old style -> peace mode -> hand combat (weapon swap) -> peace mode -> new style /// </summary> public float SwitchCombatStyles() { if (CurrentMotionState.Stance == MotionStance.NonCombat || CurrentMotionState.Stance == MotionStance.Invalid || IsMonster) { return(0.0f); } var combatStance = GetCombatStance(); float peace1 = 0.0f, unarmed = 0.0f, peace2 = 0.0f; // this is now handled as a proper 2-step process in HandleActionChangeCombatMode / NextUseTime // FIXME: just call generic method to switch to HandCombat first peace1 = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.NonCombat); /*if (CurrentMotionState.Stance != MotionStance.HandCombat && combatStance != MotionStance.HandCombat) * { * unarmed = MotionTable.GetAnimationLength(MotionTableId, MotionStance.NonCombat, MotionCommand.Ready, MotionCommand.HandCombat); * peace2 = MotionTable.GetAnimationLength(MotionTableId, MotionStance.HandCombat, MotionCommand.Ready, MotionCommand.NonCombat); * }*/ SetStance(MotionStance.NonCombat, false); //Console.WriteLine($"SwitchCombatStyle() - animLength: {animLength}"); //Console.WriteLine($"SwitchCombatStyle() - peace1({peace1}) + unarmed({unarmed}) + peace2({peace2})"); var animLength = peace1 + unarmed + peace2; return(animLength); }
/// <summary> /// Handles switching between combat stances: /// old style -> peace mode -> hand combat (weapon swap) -> peace mode -> new style /// </summary> public float SwitchCombatStyles() { if (CurrentMotionState.Stance == MotionStance.NonCombat || CurrentMotionState.Stance == MotionStance.Invalid) { return(0.0f); } var combatStance = GetCombatStance(); float peace1 = 0.0f, unarmed = 0.0f, peace2 = 0.0f; peace1 = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.NonCombat); if (CurrentMotionState.Stance != MotionStance.HandCombat && combatStance != MotionStance.HandCombat) { unarmed = MotionTable.GetAnimationLength(MotionTableId, MotionStance.NonCombat, MotionCommand.Ready, MotionCommand.HandCombat); peace2 = MotionTable.GetAnimationLength(MotionTableId, MotionStance.HandCombat, MotionCommand.Ready, MotionCommand.NonCombat); } CurrentMotionState = new UniversalMotion(MotionStance.NonCombat); //Console.WriteLine($"SwitchCombatStyle() - animLength: {animLength}"); //Console.WriteLine($"SwitchCombatStyle() - peace1({peace1}) + unarmed({unarmed}) + peace2({peace2})"); var animLength = peace1 + unarmed + peace2; return(animLength); }
/// <summary> /// Handles switching between combat stances: /// old style -> peace mode -> hand combat (weapon swap) -> peace mode -> new style /// </summary> public float SwitchCombatStyles() { if (CurrentMotionState.Stance == MotionStance.NonCombat || CurrentMotionState.Stance == MotionStance.Invalid || IsMonster) { return(0.0f); } var combatStance = GetCombatStance(); float peace1 = 0.0f, unarmed = 0.0f, peace2 = 0.0f; // FIXME: just call generic method to switch to HandCombat first peace1 = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.NonCombat); if (CurrentMotionState.Stance != MotionStance.HandCombat && combatStance != MotionStance.HandCombat) { unarmed = MotionTable.GetAnimationLength(MotionTableId, MotionStance.NonCombat, MotionCommand.Ready, MotionCommand.HandCombat); peace2 = MotionTable.GetAnimationLength(MotionTableId, MotionStance.HandCombat, MotionCommand.Ready, MotionCommand.NonCombat); } SetStance(MotionStance.NonCombat, false); //Console.WriteLine($"SwitchCombatStyle() - animLength: {animLength}"); //Console.WriteLine($"SwitchCombatStyle() - peace1({peace1}) + unarmed({unarmed}) + peace2({peace2})"); var animLength = peace1 + unarmed + peace2; return(animLength); }
/// <summary> /// Executes the weapon reload animation for the player /// </summary> public float ReloadMotion() { var reloadAnimation = new MotionItem(GetReloadAnimation(), 1.0f); var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, reloadAnimation); var motion = new UniversalMotion(CurrentMotionState.Stance); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.ForwardCommand = (uint)MotionCommand.Reload; motion.MovementData.TurnSpeed = 2.25f; //motion.HasTarget = true; //motion.TargetGuid = target.Guid; CurrentMotionState = motion; var actionChain = new ActionChain(); actionChain.AddAction(this, () => DoMotion(motion)); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => { motion.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motion); CurrentMotionState = motion; }); var ammo = GetEquippedAmmo(); if (ammo != null) { actionChain.AddAction(this, () => EnqueueBroadcast( new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat))); } actionChain.AddDelaySeconds(animLength); var player = this as Player; if (player != null) { actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventAttackDone(player.Session))); actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventCombatCommmenceAttack(player.Session))); // TODO: This gets rid of the hourglass but doesn't seem to be sent in retail pcaps... actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventAttackDone(player.Session))); } actionChain.EnqueueChain(); var weapon = GetEquippedWeapon(); var reloadTime = weapon.DefaultCombatStyle == CombatStyle.Crossbow ? 3.2f : 1.6f; return(animLength * reloadTime); }
public float ReloadMissileAmmo(ActionChain actionChain = null) { var weapon = GetEquippedMissileWeapon(); var ammo = GetEquippedAmmo(); if (weapon == null || ammo == null) { return(0.0f); } var newChain = actionChain == null; if (newChain) { actionChain = new ActionChain(); } var animLength = 0.0f; if (weapon.IsAmmoLauncher) { var animSpeed = GetAnimSpeed(); //Console.WriteLine($"AnimSpeed: {animSpeed}"); animLength = EnqueueMotion(actionChain, MotionCommand.Reload, animSpeed); // start pulling out next arrow EnqueueMotion(actionChain, MotionCommand.Ready); // finish reloading } // ensure ammo visibility for players actionChain.AddAction(this, () => { EnqueueActionBroadcast(p => p.TrackEquippedObject(this, ammo)); var delayChain = new ActionChain(); delayChain.AddDelaySeconds(0.001f); // ensuring this message gets sent after player broadcasts above... delayChain.AddAction(this, () => { EnqueueBroadcast(new GameMessageParentEvent(this, ammo, ACE.Entity.Enum.ParentLocation.RightHand, ACE.Entity.Enum.Placement.RightHandCombat)); }); delayChain.EnqueueChain(); }); if (newChain) { actionChain.EnqueueChain(); } var animLength2 = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); //Console.WriteLine($"AnimLength: {animLength} + {animLength2}"); return(animLength + animLength2); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public void DoSwingMotion(WorldObject target, CombatManeuver maneuver, out float animLength) { var animSpeed = GetAnimSpeed(); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, maneuver.Motion, animSpeed); var motion = new Motion(this, maneuver.Motion, animSpeed); motion.MotionState.TurnSpeed = 2.25f; motion.MotionFlags |= MotionFlags.StickToObject; motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public virtual ActionChain DoSwingMotion(WorldObject target, out float animLength) { var swingAnimation = new MotionItem(GetSwingAnimation(), 1.25f); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, swingAnimation); var motion = new UniversalMotion(CurrentMotionState.Stance, swingAnimation); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.TurnSpeed = 2.25f; motion.HasTarget = true; motion.TargetGuid = target.Guid; CurrentMotionState = motion; CurrentLandblock.EnqueueBroadcastMotion(this, motion); return(null); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public void DoSwingMotion(WorldObject target, CombatManeuver maneuver, out float animLength) { var animSpeed = GetAnimSpeed(); var swingAnimation = new MotionItem(maneuver.Motion, animSpeed); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, maneuver.Motion, animSpeed); var motion = new UniversalMotion(CurrentMotionState.Stance, swingAnimation); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.TurnSpeed = 2.25f; motion.HasTarget = true; motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); }
/// <summary> /// Switches a player or creature to non-combat mode /// </summary> public float HandleSwitchToPeaceMode() { var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.NonCombat); var motion = new Motion(MotionStance.NonCombat); ExecuteMotion(motion); var player = this as Player; if (player != null) { player.stance = MotionStance.NonCombat; player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat)); } //Console.WriteLine("HandleSwitchToPeaceMode() - animLength: " + animLength); return(animLength); }
/// <summary> /// Performs the player melee swing animation /// </summary> public float DoSwingMotion(WorldObject target) { // get the proper animation speed for this attack, // based on weapon speed and player quickness var baseSpeed = GetAnimSpeed(); var animSpeedMod = IsDualWieldAttack ? 1.2f : 1.0f; // dual wield swing animation 20% faster var animSpeed = baseSpeed * animSpeedMod; var swingAnimation = GetSwingAnimation(); var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, swingAnimation, animSpeed); // broadcast player swing animation to clients var motion = new Motion(this, swingAnimation, animSpeed); motion.MotionState.TurnSpeed = 2.25f; motion.MotionFlags |= MotionFlags.StickToObject; motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); return(animLength); }
public float ReloadMissileAmmo() { var weapon = GetEquippedMissileWeapon(); var ammo = GetEquippedAmmo(); if (weapon == null || ammo == null) { return(0.0f); } var actionChain = new ActionChain(); var animLength = 0.0f; if (weapon.IsBow) { EnqueueMotion(actionChain, MotionCommand.Reload); // start pulling out next arrow EnqueueMotion(actionChain, MotionCommand.Ready); // finish reloading animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); actionChain.AddDelaySeconds(animLength); } // ensure ammo visibility for players actionChain.AddAction(this, () => { EnqueueBroadcast(new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat)); EnqueueActionBroadcast((Player p) => p.TrackObject(this)); }); actionChain.EnqueueChain(); var animLength2 = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.Reload); //Console.WriteLine($"AnimLength: {animLength} + {animLength2}"); return(animLength + animLength2); }
public override ActionChain DoSwingMotion(WorldObject target, out float animLength) { var swingAnimation = new MotionItem(GetSwingAnimation(), 1.25f); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, swingAnimation); var motion = new UniversalMotion(CurrentMotionState.Stance, swingAnimation); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.TurnSpeed = 2.25f; motion.HasTarget = true; motion.TargetGuid = target.Guid; CurrentMotionState = motion; var actionChain = new ActionChain(); actionChain.AddAction(this, () => DoMotion(motion)); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventCombatCommmenceAttack(Session))); actionChain.AddAction(this, () => Session.Network.EnqueueSend(new GameEventAttackDone(Session))); return(actionChain); }
/// <summary> /// Perform the melee attack swing animation /// </summary> public ActionChain DoSwingMotion(WorldObject target, CombatManeuver maneuver, out float animLength) { var animSpeed = GetAnimSpeed(); var swingAnimation = new MotionItem(maneuver.Motion, animSpeed); animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, maneuver.Motion, null, animSpeed); var motion = new UniversalMotion(CurrentMotionState.Stance, swingAnimation); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.TurnSpeed = 2.25f; motion.HasTarget = true; motion.TargetGuid = target.Guid; CurrentMotionState = motion; EnqueueBroadcastMotion(motion); // play default script? (special attack) //if (MotionTable.HasDefaultScript(MotionTableId, maneuver.Motion, maneuver.Style)) //EnqueueBroadcast(new GameMessageScript(Guid, (PlayScript)DefaultScriptId)); return(null); }
/// <summary> /// Switches a player or creature to a missile combat stance /// </summary> public float HandleSwitchToMissileCombatMode() { // get appropriate combat stance for currently wielded items var weapon = GetEquippedMissileWeapon(); if (weapon == null) { return(0.0f); } var combatStance = GetCombatStance(); var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, (MotionCommand)combatStance, MotionCommand.Ready); var motion = new UniversalMotion(combatStance); motion.MovementData.CurrentStyle = (uint)combatStance; SetMotionState(this, motion); var ammo = GetEquippedAmmo(); if (ammo != null && weapon.IsAmmoLauncher) { animLength += ReloadMissileAmmo(); } var player = this as Player; if (player != null) { player.HandleActionTradeSwitchToCombatMode(player.Session); player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.Missile)); } //Console.WriteLine("HandleSwitchToMissileCombatMode() - animLength: " + animLength); return(animLength); }
private static void HandleCreateItemRecipe(Player player, WorldObject source, WorldObject target, Recipe recipe) { ActionChain craftChain = new ActionChain(); CreatureSkill skill = null; bool skillSuccess = true; // assume success, unless there's a skill check double percentSuccess = 1; UniversalMotion motion = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.ClapHands)); craftChain.AddAction(player, () => player.HandleActionMotion(motion)); float craftAnimationLength = MotionTable.GetAnimationLength((uint)player.MotionTableId, MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); // craftChain.AddDelaySeconds(0.5); craftChain.AddAction(player, () => { if (recipe.SkillId != null && recipe.SkillDifficulty != null) { // there's a skill associated with this Skill skillId = (Skill)recipe.SkillId.Value; // this shouldn't happen, but sanity check for unexpected nulls if (!player.Skills.ContainsKey(skillId)) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } skill = player.Skills[skillId]; percentSuccess = skill.GetPercentSuccess(recipe.SkillDifficulty.Value); } // straight skill check, if applciable if (skill != null) { skillSuccess = _random.NextDouble() < percentSuccess; } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemDestroyed) > 0) { player.DestroyInventoryItem(source); } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemDestroyed) > 0) { player.DestroyInventoryItem(target); } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemUsesDecrement) > 0) { if (source.Structure <= 1) { player.DestroyInventoryItem(source); } else { source.Structure--; source.SendPartialUpdates(player.Session, _updateStructure); } } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemUsesDecrement) > 0) { if (target.Structure <= 1) { player.DestroyInventoryItem(target); } else { target.Structure--; target.SendPartialUpdates(player.Session, _updateStructure); } } if (skillSuccess) { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem1) > 0 && recipe.SuccessItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.SuccessItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem2) > 0 && recipe.SuccessItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.SuccessItem2Wcid.Value); } var text = string.Format(recipe.SuccessMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } else { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem1) > 0 && recipe.FailureItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.FailureItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem2) > 0 && recipe.FailureItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.FailureItem2Wcid.Value); } var text = string.Format(recipe.FailMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } player.SendUseDoneEvent(); }); craftChain.EnqueueChain(); }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void LaunchMissile() { IsTurning = false; var weapon = GetEquippedMissileWeapon(); if (weapon == null || AttackTarget == null) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } // should this be called each launch? AttackHeight = ChooseAttackHeight(); var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, MotionCommand.AimLevel); //Console.WriteLine("LaunchTime: " + launchTime); // launch projectile float targetTime = 0.0f; actionChain.AddAction(this, () => { var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // TODO: monster stamina usage var projectile = LaunchProjectile(ammo, AttackTarget, out targetTime); UpdateAmmoAfterLaunch(ammo); }); // reload animation var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload); //Console.WriteLine("ReloadTime: " + reloadTime); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready, MotionCommand.Reload); //Console.WriteLine("LinkTime: " + linkTime); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.EnqueueChain(); var timeOffset = launchTime + reloadTime + linkTime; if (timeOffset < MissileDelay) { timeOffset = MissileDelay; } NextAttackTime = Timer.CurrentTime + timeOffset; }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void LaunchMissile() { //IsTurning = false; var weapon = GetEquippedMissileWeapon(); if (weapon == null || AttackTarget == null) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } // ensure direct line of sight if (!IsDirectVisible(AttackTarget)) { NextAttackTime = Timers.RunningTime + 1.0f; return; } // should this be called each launch? AttackHeight = ChooseAttackHeight(); var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); if (DebugMove) { Console.WriteLine($"[{Timers.RunningTime}] - {Name} ({Guid}) - LaunchMissile"); } // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, MotionCommand.AimLevel); //Console.WriteLine("LaunchTime: " + launchTime); // launch projectile float targetTime = 0.0f; actionChain.AddAction(this, () => { if (IsDead) { return; } var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // TODO: monster stamina usage if (AttackTarget != null) { var projectile = LaunchProjectile(weapon, ammo, AttackTarget, out targetTime); UpdateAmmoAfterLaunch(ammo); } }); // will ammo be depleted? /*if (ammo.StackSize == null || ammo.StackSize <= 1) * { * // compare monsters: lugianmontokrenegade / sclavusse / zombielichtowerarcher * actionChain.EnqueueChain(); * NextMoveTime = NextAttackTime = Timers.RunningTime + launchTime + MissileDelay; * return; * }*/ // reload animation var animSpeed = GetAnimSpeed(); var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload, animSpeed); //Console.WriteLine("ReloadTime: " + reloadTime); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); if (weapon.IsThrownWeapon) { actionChain.EnqueueChain(); actionChain = new ActionChain(); actionChain.AddDelaySeconds(linkTime); } //Console.WriteLine($"Reload time: launchTime({launchTime}) + reloadTime({reloadTime}) + linkTime({linkTime})"); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, ACE.Entity.Enum.ParentLocation.RightHand, ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.EnqueueChain(); var timeOffset = launchTime + reloadTime + linkTime; var missileDelay = MissileDelay; if (!weapon.IsAmmoLauncher) { missileDelay *= 1.5f; } NextMoveTime = NextAttackTime = Timers.RunningTime + timeOffset + missileDelay; }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void LaunchMissile() { //IsTurning = false; var weapon = GetEquippedMissileWeapon(); if (weapon == null || AttackTarget == null) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } /*if (!IsDirectVisible(AttackTarget)) * { * // ensure direct line of sight * //NextAttackTime = Timers.RunningTime + 1.0f; * SwitchToMeleeAttack(); * return; * }*/ if (SwitchWeaponsPending) { NextAttackTime = Timers.RunningTime + 1.0f; return; } // should this be called each launch? AttackHeight = ChooseAttackHeight(); var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); if (DebugMove) { Console.WriteLine($"[{Timers.RunningTime}] - {Name} ({Guid}) - LaunchMissile"); } var projectileSpeed = GetProjectileSpeed(); // get z-angle for aim motion var aimVelocity = GetAimVelocity(AttackTarget, projectileSpeed); var aimLevel = GetAimLevel(aimVelocity); // calculate projectile spawn pos and velocity var localOrigin = GetProjectileSpawnOrigin(ammo.WeenieClassId, aimLevel); var velocity = CalculateProjectileVelocity(localOrigin, AttackTarget, projectileSpeed, out Vector3 origin, out Quaternion orientation); //Console.WriteLine($"Velocity: {velocity}"); // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, aimLevel); //Console.WriteLine("LaunchTime: " + launchTime); // launch projectile actionChain.AddAction(this, () => { if (IsDead) { return; } var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // TODO: monster stamina usage if (AttackTarget != null) { var projectile = LaunchProjectile(weapon, ammo, AttackTarget, origin, orientation, velocity); UpdateAmmoAfterLaunch(ammo); } }); // will ammo be depleted? /*if (ammo.StackSize == null || ammo.StackSize <= 1) * { * // compare monsters: lugianmontokrenegade / sclavusse / zombielichtowerarcher * actionChain.EnqueueChain(); * NextMoveTime = NextAttackTime = Timers.RunningTime + launchTime + MissileDelay; * return; * }*/ // reload animation var animSpeed = GetAnimSpeed(); var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload, animSpeed); //Console.WriteLine("ReloadTime: " + reloadTime); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkAnim = reloadTime > 0 ? MotionCommand.Reload : aimLevel; var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, linkAnim, MotionCommand.Ready); if (weapon.IsThrownWeapon) { if (reloadTime > 0) { actionChain.EnqueueChain(); actionChain = new ActionChain(); } actionChain.AddDelaySeconds(linkTime); } //log.Info($"{Name}.Reload time: launchTime({launchTime}) + reloadTime({reloadTime}) + linkTime({linkTime})"); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, ACE.Entity.Enum.ParentLocation.RightHand, ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.EnqueueChain(); PrevAttackTime = Timers.RunningTime; var timeOffset = launchTime + reloadTime + linkTime; NextMoveTime = NextAttackTime = PrevAttackTime + timeOffset + MissileDelay; }
/// <summary> /// Launches a missile attack from player to target /// </summary> public void LaunchMissile(WorldObject target) { var weapon = GetEquippedMissileWeapon(); if (weapon == null || CombatMode == CombatMode.NonCombat) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } var creature = target as Creature; if (!IsAlive || MissileTarget == null || !creature.IsAlive) { MissileTarget = null; return; } // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, MotionCommand.AimLevel); // launch projectile actionChain.AddAction(this, () => { var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // stamina usage // TODO: ensure enough stamina for attack // TODO: verify formulas - double/triple cost for bow/xbow? var staminaCost = GetAttackStamina(GetAccuracyRange()); UpdateVitalDelta(Stamina, -staminaCost); float targetTime = 0.0f; var projectile = LaunchProjectile(ammo, target, out targetTime); UpdateAmmoAfterLaunch(ammo); }); // ammo remaining? if (ammo.StackSize == 1) { actionChain.AddAction(this, () => { SetCombatMode(CombatMode.NonCombat); }); actionChain.EnqueueChain(); return; } // reload animation var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); //var cycleTime = MotionTable.GetCycleLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Ready); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.AddDelaySeconds(linkTime); actionChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventAttackDone(Session)); if (creature.IsAlive && GetCharacterOption(CharacterOption.AutoRepeatAttacks)) { Session.Network.EnqueueSend(new GameEventCombatCommenceAttack(Session)); Session.Network.EnqueueSend(new GameEventAttackDone(Session)); var nextAttack = new ActionChain(); nextAttack.AddDelaySeconds(AccuracyLevel + 0.1f); // perform next attack nextAttack.AddAction(this, () => { LaunchMissile(target); }); nextAttack.EnqueueChain(); } else { MissileTarget = null; } }); actionChain.EnqueueChain(); }