public override void OnUse(Player player) { string serverMessage = null; // validate within use range, taking into account the radius of the model itself, as well SetupModel csetup = SetupModel.ReadFromDat(this.PhysicsData.CSetup); float radiusSquared = (this.GameData.UseRadius + csetup.Radius) * (this.GameData.UseRadius + csetup.Radius); var motionSanctuary = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player, player.Session, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(this.Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(this.Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.EnqueueMovementEvent(motionSanctuary, player.Guid); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); }
public void BroadcastMovement(MoveToState moveToState) { var state = moveToState.RawMotionState; // update current style if ((state.Flags & RawMotionFlags.CurrentStyle) != 0) { // this lowercase stance field in Player doesn't really seem to be used anywhere stance = state.CurrentStyle; } // update CurrentMotionState here for substates? if ((state.Flags & RawMotionFlags.ForwardCommand) != 0) { if (((uint)state.ForwardCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.ForwardCommand); } } else { CurrentMotionState.SetForwardCommand(MotionCommand.Ready); } if (state.CommandListLength > 0) { if (((uint)state.Commands[0].MotionCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.Commands[0].MotionCommand); } } if (state.HasSoulEmote(false)) { // prevent soul emote spam / bug where client sends multiples var soulEmote = state.Commands[0].MotionCommand; if (soulEmote == LastSoulEmote && DateTime.UtcNow < LastSoulEmoteEndTime) { state.Commands.Clear(); state.CommandListLength = 0; } else { var animLength = Physics.Animation.MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, soulEmote, state.Commands[0].Speed); LastSoulEmote = soulEmote; LastSoulEmoteEndTime = DateTime.UtcNow + TimeSpan.FromSeconds(animLength); } } var movementData = new MovementData(this, moveToState); var movementEvent = new GameMessageUpdateMotion(this, movementData); EnqueueBroadcast(true, movementEvent); // shouldn't need to go to originating player? // TODO: use real motion / animation system from physics //CurrentMotionCommand = movementData.Invalid.State.ForwardCommand; CurrentMovementData = movementData; }
public void SetMotionState(WorldObject obj, UniversalMotion motionState) { CurrentMotionState = motionState; motionState.IsAutonomous = false; GameMessageUpdateMotion updateMotion = new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), obj.Sequences, motionState); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, updateMotion); }
public void SetMotionState(MotionState motionState) { Player p = (Player)this; PhysicsData.CurrentMotionState = motionState; var updateMotion = new GameMessageUpdateMotion(this, p.Session, motionState); p.Session.Network.EnqueueSend(updateMotion); }
public void SetMotionState(MotionState motionState) { Player p = (Player)this; PhysicsData.CurrentMotionState = motionState; var updateMotion = new GameMessageUpdateMotion(p.Guid, p.Sequences.GetCurrentSequence(SequenceType.ObjectInstance), p.Sequences, motionState); p.Session.Network.EnqueueSend(updateMotion); }
//public DateTime LastSoulEmote; //private static TimeSpan SoulEmoteTime = TimeSpan.FromSeconds(2); public void BroadcastMovement(MoveToState moveToState) { var state = moveToState.RawMotionState; // update current style if ((state.Flags & RawMotionFlags.CurrentStyle) != 0) { // this lowercase stance field in Player doesn't really seem to be used anywhere stance = state.CurrentStyle; } // update CurrentMotionState here for substates? if ((state.Flags & RawMotionFlags.ForwardCommand) != 0) { if (((uint)state.ForwardCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.ForwardCommand); } } else { CurrentMotionState.SetForwardCommand(MotionCommand.Ready); } if (state.CommandListLength > 0) { if (((uint)state.Commands[0].MotionCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.Commands[0].MotionCommand); } } /*if (state.HasSoulEmote()) * { * // prevent soul emote spam / bug where client sends multiples * var elapsed = DateTime.UtcNow - LastSoulEmote; * if (elapsed < SoulEmoteTime) return; * * LastSoulEmote = DateTime.UtcNow; * }*/ var movementData = new MovementData(this, moveToState); var movementEvent = new GameMessageUpdateMotion(this, movementData); EnqueueBroadcast(false, movementEvent); // shouldn't need to go to originating player? // TODO: use real motion / animation system from physics //CurrentMotionCommand = movementData.Invalid.State.ForwardCommand; CurrentMovementData = movementData; }
public override void HandleActionOnUse(ObjectGuid playerId) { // All data on a lifestone is constant -- therefore we just run in context of player ActionChain chain = new ActionChain(); CurrentLandblock.ChainOnObject(chain, playerId, (WorldObject wo) => { Player player = wo as Player; string serverMessage = null; // validate within use range, taking into account the radius of the model itself, as well SetupModel csetup = SetupModel.ReadFromDat(SetupTableId.Value); float radiusSquared = (UseRadius.Value + csetup.Radius) * (UseRadius.Value + csetup.Radius); // Run this animation... // Player Enqueue: var motionSanctuary = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player.Guid, player.Sequences.GetCurrentSequence(Network.Sequence.SequenceType.ObjectInstance), player.Sequences, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.HandleActionMotion(motionSanctuary); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); }); chain.EnqueueChain(); }
/// <summary> /// Detects changes in the player's RunRate -- /// If there are changes, re-broadcasts player movement packet /// </summary> public bool HandleRunRateUpdate() { //Console.WriteLine($"{Name}.HandleRunRateUpdates()"); if (CurrentMovementData.MovementType != MovementType.Invalid || CurrentMovementData.Invalid == null) { return(false); } var prevState = CurrentMovementData.Invalid.State; var movementData = new MovementData(this, CurrentMoveToState); var currentState = movementData.Invalid.State; var changed = currentState.ForwardSpeed != prevState.ForwardSpeed || currentState.TurnSpeed != prevState.TurnSpeed || currentState.SidestepSpeed != prevState.SidestepSpeed; if (!changed) { return(false); } //Console.WriteLine($"Old: {prevState.ForwardSpeed}, New: {currentState.ForwardSpeed}"); if (!CurrentMovementData.Invalid.State.HasMovement() || IsJumping) { return(false); } //Console.WriteLine($"{Name}.OnRunRateChanged()"); CurrentMovementData = new MovementData(this, CurrentMoveToState); // verify - forced commands from server should be non-autonomous, but could have been sent as autonomous in retail? // if set to autonomous here, the desired effect doesn't happen CurrentMovementData.IsAutonomous = false; var movementEvent = new GameMessageUpdateMotion(this, CurrentMovementData); EnqueueBroadcast(movementEvent); // broadcast to all players, including self return(true); }
public static void Handle(ClientMessage clientMessage, Session session) { string message = $"{session.Player.Name} is recalling to the marketplace."; var sysChatMessage = new GameMessageSystemChat(message, ChatMessageType.Recall); var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(session, PropertyInt.CombatMode, 1); var motionMarketplaceRecall = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.MarketplaceRecall)); var animationEvent = new GameMessageUpdateMotion(session.Player, session, motionMarketplaceRecall); // TODO: This needs to be changed to broadcast sysChatMessage to only those in local chat hearing range // FIX: Recall text isn't being broadcast yet, need to address session.Network.EnqueueSend(updateCombatMode, sysChatMessage); session.Player.EnqueueMovementEvent(motionMarketplaceRecall, session.Player.Guid); session.Player.SetDelayedTeleport(TimeSpan.FromSeconds(14), marketplaceDrop); }
public void BroadcastMovement(MoveToState moveToState) { var state = moveToState.RawMotionState; // update current style if ((state.Flags & RawMotionFlags.CurrentStyle) != 0) { // this lowercase stance field in Player doesn't really seem to be used anywhere stance = state.CurrentStyle; } var movementData = new MovementData(this, moveToState); var movementEvent = new GameMessageUpdateMotion(this, movementData); EnqueueBroadcast(movementEvent); // shouldn't need to go to originating player? // TODO: use real motion / animation system from physics CurrentMotionCommand = movementData.Invalid.State.ForwardCommand; }
public void BroadcastMovement(MoveToState moveToState) { var state = moveToState.RawMotionState; // update current style if ((state.Flags & RawMotionFlags.CurrentStyle) != 0) { // this lowercase stance field in Player doesn't really seem to be used anywhere stance = state.CurrentStyle; } // update CurrentMotionState here for substates? if ((state.Flags & RawMotionFlags.ForwardCommand) != 0) { if (((uint)state.ForwardCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.ForwardCommand); } } else { CurrentMotionState.SetForwardCommand(MotionCommand.Ready); } if (state.CommandListLength > 0) { if (((uint)state.Commands[0].MotionCommand & (uint)CommandMask.SubState) != 0) { CurrentMotionState.SetForwardCommand(state.Commands[0].MotionCommand); } } var movementData = new MovementData(this, moveToState); var movementEvent = new GameMessageUpdateMotion(this, movementData); EnqueueBroadcast(movementEvent); // shouldn't need to go to originating player? // TODO: use real motion / animation system from physics CurrentMotionCommand = movementData.Invalid.State.ForwardCommand; }
private void HandleGameAction(QueuedGameAction action, Player player) { switch (action.ActionType) { case GameActionType.TalkDirect: { // TODO: remove this hack (using TalkDirect) ASAP var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId)); HandleDeathMessage(obj, d); break; } case GameActionType.TeleToHouse: case GameActionType.TeleToLifestone: case GameActionType.TeleToMansion: case GameActionType.TeleToMarketPlace: case GameActionType.TeleToPkArena: case GameActionType.TeleToPklArena: { player.Teleport(action.ActionLocation); break; } case GameActionType.ApplyVisualEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var particleEffect = (PlayScript)action.SecondaryObjectId; HandleParticleEffectEvent(obj, particleEffect); break; } case GameActionType.ApplySoundEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var soundEffect = (Sound)action.SecondaryObjectId; HandleSoundEvent(obj, soundEffect); break; } case GameActionType.PutItemInContainer: { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId) && worldObjects.ContainsKey(inventoryId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = worldObjects[inventoryId]; } if ((aPlayer != null) && (inventoryItem != null)) { var motion = new GeneralMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(aPlayer), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageSound(aPlayer.Guid, Sound.PickUpItem, (float)1.0)); // Add to the inventory list. aPlayer.AddToInventory(inventoryItem); LandblockManager.RemoveObject(inventoryItem); motion = new GeneralMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(aPlayer.Session, PropertyInt.EncumbVal, aPlayer.GameData.Burden), new GameMessagePutObjectInContainer(aPlayer.Session, aPlayer, inventoryId), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, playerId), new GameMessagePickupEvent(aPlayer.Session, inventoryItem)); aPlayer.TrackObject(inventoryItem); } } break; } case GameActionType.DropItem: { var g = new ObjectGuid(action.ObjectId); // ReSharper disable once InconsistentlySynchronizedField if (worldObjects.ContainsKey(g)) { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = aPlayer.GetInventoryItem(inventoryId); aPlayer.RemoveFromInventory(inventoryId); } if ((aPlayer != null) && (inventoryItem != null)) { var targetContainer = new ObjectGuid(0); aPlayer.Session.Network.EnqueueSend( new GameMessagePrivateUpdatePropertyInt( aPlayer.Session, PropertyInt.EncumbVal, (uint)aPlayer.Session.Player.GameData.Burden)); var motion = new GeneralMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); motion = new GeneralMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessagePutObjectIn3d(aPlayer.Session, aPlayer, inventoryId), new GameMessageSound(aPlayer.Guid, Sound.DropItem, (float)1.0), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); // This is the sequence magic - adds back into 3d space seem to be treated like teleport. inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectTeleport); inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectVector); LandblockManager.AddObject(inventoryItem); aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(inventoryItem)); } } } break; } case GameActionType.MovementEvent: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var motion = action.Motion; HandleMovementEvent(obj, motion); break; } case GameActionType.ObjectCreate: { this.AddWorldObject(action.WorldObject); break; } case GameActionType.ObjectDelete: { this.RemoveWorldObject(action.WorldObject.Guid, false); break; } case GameActionType.QueryHealth: { if (action.ObjectId == 0) { // Deselect the formerly selected Target player.SelectedTarget = 0; break; } object target = null; var targetId = new ObjectGuid(action.ObjectId); // Remember the selected Target player.SelectedTarget = action.ObjectId; // TODO: once items are implemented check if there are items that can trigger // the QueryHealth event. So far I believe it only gets triggered for players and creatures if (targetId.IsPlayer() || targetId.IsCreature()) { if (this.worldObjects.ContainsKey(targetId)) { target = this.worldObjects[targetId]; } if (target == null) { // check adjacent landblocks for the targetId foreach (var block in adjacencies) { if (block.Value != null) { if (block.Value.worldObjects.ContainsKey(targetId)) { target = block.Value.worldObjects[targetId]; } } } } if (target != null) { float healthPercentage = 0; if (targetId.IsPlayer()) { Player tmpTarget = (Player)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } if (targetId.IsCreature()) { Creature tmpTarget = (Creature)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage); player.Session.Network.EnqueueSend(updateHealth); } } break; } case GameActionType.Use: { var g = new ObjectGuid(action.ObjectId); if (worldObjects.ContainsKey(g)) { WorldObject obj = worldObjects[g]; switch (obj.Type) { case Enum.ObjectType.Portal: { // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage float rangeCheck = 5.0f; if (player.Location.SquaredDistanceTo(obj.Location) < rangeCheck) { PortalDestination portalDestination = DatabaseManager.World.GetPortalDestination(obj.WeenieClassid); if (portalDestination != null) { player.Session.Player.Teleport(portalDestination.Position); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } else { string serverMessage = "Portal destination for portal ID " + obj.WeenieClassid + " not yet implemented!"; var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent); } } else { // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } break; } case Enum.ObjectType.LifeStone: { string serverMessage = null; // validate within use range float radiusSquared = obj.GameData.UseRadius * obj.GameData.UseRadius; var motionSanctuary = new GeneralMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player, player.Session, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(obj.Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(obj.Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.EnqueueMovementEvent(motionSanctuary, player.Guid); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); break; } } } break; } } }