Пример #1
0
        public void LogOffPlayer()
        {
            // First save, then logout
            ActionChain logoutChain = new ActionChain();

            logoutChain.AddChain(Player.GetSaveChain());
            logoutChain.AddChain(Player.GetLogoutChain());
            logoutChain.EnqueueChain();

            logOffRequestTime = DateTime.UtcNow;
        }
Пример #2
0
        public void TeleToMarketplace()
        {
            string message = $"{Name} is recalling to the marketplace.";

            var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(Session.Player.Sequences, PropertyInt.CombatMode, (int)CombatMode.NonCombat);

            var motionMarketplaceRecall = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.MarketplaceRecall));

            var animationEvent = new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, 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
            CurrentLandblock.EnqueueBroadcastSystemChat(this, message, ChatMessageType.Recall);
            Session.Network.EnqueueSend(updateCombatMode);
            DoMotion(motionMarketplaceRecall);

            // TODO: (OptimShi): Actual animation length is longer than in retail. 18.4s
            // float mpAnimationLength = MotionTable.GetAnimationLength((uint)MotionTableId, MotionCommand.MarketplaceRecall);
            // mpChain.AddDelaySeconds(mpAnimationLength);
            ActionChain mpChain = new ActionChain();

            mpChain.AddDelaySeconds(14);

            // Then do teleport
            mpChain.AddChain(GetTeleportChain(MarketplaceDrop));

            // Set the chain to run
            mpChain.EnqueueChain();
        }
Пример #3
0
        /// <summary>
        /// Handles teleporting a player to the lifestone (/ls or /lifestone command)
        /// </summary>
        public void TeleToLifestone()
        {
            if (Positions.ContainsKey(PositionType.Sanctuary))
            {
                // FIXME(ddevec): I should probably make a better interface for this
                UpdateVitalInternal(Mana, Mana.Current / 2);

                if (CombatMode != CombatMode.NonCombat)
                {
                    var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(Session.Player.Sequences, PropertyInt.CombatMode, (int)CombatMode.NonCombat);
                    Session.Network.EnqueueSend(updateCombatMode);
                }

                var motionLifestoneRecall = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.LifestoneRecall));
                // TODO: This needs to be changed to broadcast sysChatMessage to only those in local chat hearing range
                CurrentLandblock.EnqueueBroadcastSystemChat(this, $"{Name} is recalling to the lifestone.", ChatMessageType.Recall);
                // FIX: Recall text isn't being broadcast yet, need to address
                DoMotion(motionLifestoneRecall);

                // Wait for animation
                ActionChain lifestoneChain           = new ActionChain();
                var         motionTable              = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId);
                float       lifestoneAnimationLength = motionTable.GetAnimationLength(MotionCommand.LifestoneRecall);

                // Then do teleport
                lifestoneChain.AddDelaySeconds(lifestoneAnimationLength);
                lifestoneChain.AddChain(GetTeleportChain(Positions[PositionType.Sanctuary]));

                lifestoneChain.EnqueueChain();
            }
            else
            {
                ChatPacket.SendServerMessage(Session, "Your spirit has not been attuned to a sanctuary location.", ChatMessageType.Broadcast);
            }
        }
Пример #4
0
        /// <summary>
        /// Handles teleporting a player to the lifestone (/ls or /lifestone command)
        /// </summary>
        public void HandleActionTeleToLifestone()
        {
            if (Sanctuary != null)
            {
                // FIXME(ddevec): I should probably make a better interface for this
                UpdateVital(Mana, Mana.Current / 2);

                if (CombatMode != CombatMode.NonCombat)
                {
                    // this should be handled by a different thing, probably a function that forces player into peacemode
                    var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat);
                    Session.Network.EnqueueSend(updateCombatMode);
                }

                CurrentLandblock.EnqueueBroadcastSystemChat(this, $"{Name} is recalling to the lifestone.", ChatMessageType.Recall);
                CurrentLandblock.EnqueueBroadcastMotion(this, motionLifestoneRecall);

                // Wait for animation
                ActionChain lifestoneChain = new ActionChain();

                // Then do teleport
                lifestoneChain.AddDelaySeconds(DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.LifestoneRecall));
                lifestoneChain.AddChain(GetTeleportChain(Sanctuary));

                lifestoneChain.EnqueueChain();
            }
            else
            {
                ChatPacket.SendServerMessage(Session, "Your spirit has not been attuned to a sanctuary location.", ChatMessageType.Broadcast);
            }
        }
Пример #5
0
        public void HandleActionUseItem(ObjectGuid usedItemId)
        {
            StopExistingMoveToChains();

            // Search our inventory first
            var item = GetInventoryItem(usedItemId);

            if (item != null)
            {
                item.UseItem(this);
            }
            else
            {
                // Search the world second
                item = CurrentLandblock?.GetObject(usedItemId);

                if (item == null)
                {
                    Session.Network.EnqueueSend(new GameEventUseDone(Session)); // todo add an argument that indicates the item was not found
                    return;
                }

                if (item is Container)
                {
                    lastUsedContainerId = usedItemId;
                }

                var actionChain = new ActionChain();

                if (!IsWithinUseRadiusOf(item))
                {
                    var moveToChain = CreateMoveToChain(item, out var thisMoveToChainNumber);

                    actionChain.AddChain(moveToChain);
                    actionChain.AddDelaySeconds(0.50);

                    // Make sure that after we've executed our MoveToChain, and waited our delay, we're still within use radius.
                    actionChain.AddAction(this, () =>
                    {
                        if (IsWithinUseRadiusOf(item) && thisMoveToChainNumber == moveToChainCounter)
                        {
                            actionChain.AddAction(item, () => item.ActOnUse(this));
                        }
                        else
                        {
                            // Action is cancelled
                            Session.Network.EnqueueSend(new GameEventUseDone(Session));
                        }
                    });
                }
                else
                {
                    actionChain.AddAction(item, () => item.ActOnUse(this));
                }

                actionChain.EnqueueChain();
            }
        }
Пример #6
0
        protected ActionChain OnKillInternal(Session killerSession)
        {
            // Will start death animation
            OnKill(killerSession);

            // Wait, then run kill animation
            ActionChain onKillChain = new ActionChain();

            onKillChain.AddDelaySeconds(2);
            onKillChain.AddChain(GetCreateCorpseChain());

            return(onKillChain);
        }
Пример #7
0
        public virtual ActionChain GetOnKillChain(Session killerSession)
        {
            ActionChain onKillChain = new ActionChain();

            /*
             * // Wait to respawn
             * onKillChain.AddDelayTicks(10);
             */

            // Will start death animation
            onKillChain.AddAction(this, () => OnKill(killerSession));
            // Wait for kill animation
            onKillChain.AddDelaySeconds(2);
            onKillChain.AddChain(GetCreateCorpseChain());

            return(onKillChain);
        }
Пример #8
0
        public ActionChain GetLogoutChain(bool clientSessionTerminatedAbruptly = false)
        {
            ActionChain logoutChain = new ActionChain(this, () => LogoutInternal(clientSessionTerminatedAbruptly));

            var   motionTable           = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId);
            float logoutAnimationLength = motionTable.GetAnimationLength(MotionCommand.LogOut);

            logoutChain.AddDelaySeconds(logoutAnimationLength);

            if (CurrentLandblock != null)
            {
                // remove the player from landblock management -- after the animation has run
                logoutChain.AddChain(CurrentLandblock.GetRemoveWorldObjectChain(Guid, false));
            }

            return(logoutChain);
        }
Пример #9
0
        public void HandleActionTeleToMarketPlace()
        {
            var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat);

            CurrentLandblock.EnqueueBroadcastSystemChat(this, $"{Name} is recalling to the marketplace.", ChatMessageType.Recall);
            Session.Network.EnqueueSend(updateCombatMode); // this should be handled by a different thing, probably a function that forces player into peacemode
            CurrentLandblock.EnqueueBroadcastMotion(this, motionMarketplaceRecall);

            // TODO: (OptimShi): Actual animation length is longer than in retail. 18.4s
            // float mpAnimationLength = MotionTable.GetAnimationLength((uint)MotionTableId, MotionCommand.MarketplaceRecall);
            // mpChain.AddDelaySeconds(mpAnimationLength);
            ActionChain mpChain = new ActionChain();

            mpChain.AddDelaySeconds(14);

            // Then do teleport
            mpChain.AddChain(GetTeleportChain(MarketplaceDrop));

            // Set the chain to run
            mpChain.EnqueueChain();
        }
Пример #10
0
        public void HandleActionUseItem(ObjectGuid usedItemId)
        {
            StopExistingMoveToChains();

            // Search our inventory first
            var item = GetInventoryItem(usedItemId);

            if (item != null)
            {
                item.UseItem(this);
            }
            else
            {
                // Search the world second
                item = CurrentLandblock?.GetObject(usedItemId);

                if (item == null)
                {
                    Session.Network.EnqueueSend(new GameEventUseDone(Session)); // todo add an argument that indicates the item was not found
                    return;
                }

                var moveTo    = true;
                var container = item as Container;
                if (container != null)
                {
                    lastUsedContainerId = usedItemId;

                    // if the container is already open by this player,
                    // this packet indicates to close the container.
                    if (container.IsOpen && container.Viewer == Guid.Full)
                    {
                        // closing the container does not require moving towards it
                        moveTo = false;
                    }
                }

                // already there?
                if (!moveTo || IsWithinUseRadiusOf(item))
                {
                    item.ActOnUse(this);
                    return;
                }

                // if required, move to
                var moveToChain = CreateMoveToChain(item, out var thisMoveToChainNumber);

                var actionChain = new ActionChain();
                actionChain.AddChain(moveToChain);
                actionChain.AddAction(item, () =>
                {
                    if (thisMoveToChainNumber == moveToChainCounter)
                    {
                        item.ActOnUse(this);
                    }
                    else
                    {
                        // Action is cancelled
                        // this needs to happen much earlier,
                        // when a player event happens that actually cancels an existing moveto chain
                        // as it currently stands, the player will get a perpetual hourglass
                        // if they legitimately cancel a moveto event in progress...

                        Session.Network.EnqueueSend(new GameEventUseDone(Session));
                    }
                });
                actionChain.EnqueueChain();
            }
        }
Пример #11
0
        /// <summary>
        /// This method is used to pick items off the world - out of 3D space and into our inventory or to a wielded slot.
        /// It checks the use case needed, sends the appropriate response messages.   In addition, it will move to objects
        /// that are out of range in the attemp to pick them up.   It will call update apperiance if needed and you have
        /// wielded an item from the ground. Og II
        /// </summary>
        /// <param name="container"></param>
        /// <param name="itemGuid"></param>
        /// <param name="placement"></param>
        /// <param name="iidPropertyId"></param>
        private void PickupItem(Container container, ObjectGuid itemGuid, int placement, PropertyInstanceId iidPropertyId)
        {
            // Logical operations:
            // !! FIXME: How to handle repeat on condition?
            // while (!objectInRange)
            //   try Move to object
            // !! FIXME: How to handle conditional
            // Try acquire from landblock
            // if acquire successful:
            //   add to container
            ActionChain pickUpItemChain = new ActionChain();

            // Move to the object
            pickUpItemChain.AddChain(CreateMoveToChain(itemGuid, PickUpDistance));

            // Pick up the object
            // Start pickup animation
            pickUpItemChain.AddAction(this, () =>
            {
                var motion = new UniversalMotion(MotionStance.Standing);
                motion.MovementData.ForwardCommand = (uint)MotionCommand.Pickup;
                CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange,
                                                  new GameMessageUpdatePosition(this),
                                                  new GameMessageUpdateMotion(Guid,
                                                                              Sequences.GetCurrentSequence(SequenceType.ObjectInstance),
                                                                              Sequences, motion));
            });
            // Wait for animation to progress
            var motionTable           = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId);
            var pickupAnimationLength = motionTable.GetAnimationLength(MotionCommand.Pickup);

            pickUpItemChain.AddDelaySeconds(pickupAnimationLength);

            // Ask landblock to transfer item
            // pickUpItemChain.AddAction(CurrentLandblock, () => CurrentLandblock.TransferItem(itemGuid, containerGuid));
            if (container.Guid.IsPlayer())
            {
                CurrentLandblock.QueueItemTransfer(pickUpItemChain, itemGuid, container.Guid);
            }
            else
            {
                CurrentLandblock.ScheduleItemTransferInContainer(pickUpItemChain, itemGuid, (Container)GetInventoryItem(container.Guid));
            }

            // Finish pickup animation
            pickUpItemChain.AddAction(this, () =>
            {
                // If success, the item is in our inventory:
                WorldObject item = GetInventoryItem(itemGuid);

                if (item.ContainerId != Guid.Full)
                {
                    //Burden += item.Burden ?? 0;

                    if (item.WeenieType == WeenieType.Coin)
                    {
                        UpdateCurrencyClientCalculations(WeenieType.Coin);
                    }
                }

                if (item is Container itemAsContainer)
                {
                    Session.Network.EnqueueSend(new GameEventViewContents(Session, itemAsContainer));

                    foreach (var packItem in itemAsContainer.Inventory)
                    {
                        Session.Network.EnqueueSend(new GameMessageCreateObject(packItem.Value));
                        UpdateCurrencyClientCalculations(WeenieType.Coin);
                    }
                }

                // Update all our stuff if we succeeded
                if (item != null)
                {
                    item.SetPropertiesForContainer(placement);
                    // FIXME(ddevec): I'm not 100% sure which of these need to be broadcasts, and which are local sends...
                    var motion = new UniversalMotion(MotionStance.Standing);
                    if (iidPropertyId == PropertyInstanceId.Container)
                    {
                        Session.Network.EnqueueSend(
                            ////new GameMessagePrivateUpdatePropertyInt(Session.Player.Sequences, PropertyInt.EncumbranceVal, UpdateBurden()),
                            new GameMessageSound(Guid, Sound.PickUpItem, 1.0f),
                            new GameMessageUpdateInstanceId(itemGuid, container.Guid, iidPropertyId),
                            new GameMessagePutObjectInContainer(Session, container.Guid, item, placement));
                    }
                    else
                    {
                        AddToWieldedObjects(item, container, (EquipMask)placement);
                        Session.Network.EnqueueSend(new GameMessageSound(Guid, Sound.WieldObject, (float)1.0),
                                                    new GameMessageObjDescEvent(this),
                                                    new GameMessageUpdateInstanceId(container.Guid, itemGuid, PropertyInstanceId.Wielder),
                                                    new GameEventWieldItem(Session, itemGuid.Full, placement));
                    }

                    CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange,
                                                      new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion),
                                                      new GameMessagePickupEvent(item));

                    if (iidPropertyId == PropertyInstanceId.Wielder)
                    {
                        CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageObjDescEvent(this));
                    }

                    // TODO: Og II - check this later to see if it is still required.
                    Session.Network.EnqueueSend(new GameMessageUpdateObject(item));
                }
                // If we didn't succeed, just stand up and be ashamed of ourself
                else
                {
                    var motion = new UniversalMotion(MotionStance.Standing);

                    CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange,
                                                      new GameMessageUpdateMotion(Guid,
                                                                                  Sequences.GetCurrentSequence(SequenceType.ObjectInstance),
                                                                                  Sequences, motion));
                    // CurrentLandblock.EnqueueBroadcast(self shame);
                }
            });
            // Set chain to run
            pickUpItemChain.EnqueueChain();
        }
Пример #12
0
        // =====================================
        // Helper Functions - Inventory Movement
        // =====================================
        // Used by HandleActionPutItemInContainer, HandleActionDropItem

        /// <summary>
        /// This method is used to pick items off the world - out of 3D space and into our inventory or to a wielded slot.
        /// It checks the use case needed, sends the appropriate response messages.
        /// In addition, it will move to objects that are out of range in the attemp to pick them up.
        /// It will call update apperiance if needed and you have wielded an item from the ground. Og II
        /// </summary>
        private void PickupItemWithNetworking(Container container, ObjectGuid itemGuid, int placementPosition, PropertyInstanceId iidPropertyId)
        {
            ActionChain pickUpItemChain = new ActionChain();

            // Move to the object
            pickUpItemChain.AddChain(CreateMoveToChain(itemGuid, PickUpDistance));

            // Pick up the object
            // Start pickup animation
            pickUpItemChain.AddAction(this, () =>
            {
                var motion = new UniversalMotion(MotionStance.Standing);
                motion.MovementData.ForwardCommand = (uint)MotionCommand.Pickup;
                CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange,
                                                  new GameMessageUpdatePosition(this),
                                                  new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion));
            });

            // Wait for animation to progress
            var motionTable           = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId);
            var pickupAnimationLength = motionTable.GetAnimationLength(MotionCommand.Pickup);

            pickUpItemChain.AddDelaySeconds(pickupAnimationLength);

            // Grab a reference to the item before its removed from the CurrentLandblock
            var item = CurrentLandblock.GetObject(itemGuid);

            // Queue up an action that wait for the landblock to remove the item. The action that gets queued, when fired, will be run on the landblocks ActionChain, not this players.
            CurrentLandblock.QueueItemRemove(pickUpItemChain, itemGuid);

            // Finish pickup animation
            pickUpItemChain.AddAction(this, () =>
            {
                // If the item still has a location, CurrentLandblock failed to remove it
                if (item.Location != null)
                {
                    log.Error("Player_Inventory PickupItemWithNetworking item.Location != null");
                    return;
                }

                // If the item has a ContainerId, it was probably picked up by someone else before us
                if (item.ContainerId != null && item.ContainerId != 0)
                {
                    log.Error("Player_Inventory PickupItemWithNetworking item.ContainerId != 0");
                    return;
                }

                item.SetPropertiesForContainer();

                // FIXME(ddevec): I'm not 100% sure which of these need to be broadcasts, and which are local sends...

                if (iidPropertyId == PropertyInstanceId.Container)
                {
                    if (!container.TryAddToInventory(item, placementPosition, true))
                    {
                        log.Error("Player_Inventory PickupItemWithNetworking TryAddToInventory failed");
                        return;
                    }

                    // If we've put the item to a side pack, we must increment our main EncumbranceValue and Value
                    if (container != this)
                    {
                        EncumbranceVal += item.EncumbranceVal;
                        Value          += item.Value;
                        // todo CoinValue
                    }

                    if (item is Container itemAsContainer)
                    {
                        Session.Network.EnqueueSend(new GameEventViewContents(Session, itemAsContainer));

                        foreach (var packItem in itemAsContainer.Inventory)
                        {
                            Session.Network.EnqueueSend(new GameMessageCreateObject(packItem.Value));
                        }
                    }

                    Session.Network.EnqueueSend(
                        new GameMessageSound(Guid, Sound.PickUpItem, 1.0f),
                        new GameMessageUpdateInstanceId(itemGuid, container.Guid, iidPropertyId),
                        new GameMessagePutObjectInContainer(Session, container.Guid, item, placementPosition));
                }
                else if (iidPropertyId == PropertyInstanceId.Wielder)
                {
                    if (!TryEquipObject(item, placementPosition))
                    {
                        log.Error("Player_Inventory PickupItemWithNetworking TryEquipObject failed");
                        return;
                    }

                    if (((EquipMask)placementPosition & EquipMask.Selectable) != 0)
                    {
                        SetChild(item, placementPosition, out _, out _);
                    }

                    // todo I think we need to recalc our SetupModel here. see CalculateObjDesc()

                    Session.Network.EnqueueSend(
                        new GameMessageSound(Guid, Sound.WieldObject, (float)1.0),
                        new GameMessageObjDescEvent(this),
                        new GameMessageUpdateInstanceId(container.Guid, itemGuid, PropertyInstanceId.Wielder),
                        new GameEventWieldItem(Session, itemGuid.Full, placementPosition));
                }

                Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(Session.Player.Sequences, PropertyInt.EncumbranceVal, EncumbranceVal ?? 0));

                var motion = new UniversalMotion(MotionStance.Standing);

                CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange,
                                                  new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion),
                                                  new GameMessagePickupEvent(item));

                if (iidPropertyId == PropertyInstanceId.Wielder)
                {
                    CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageObjDescEvent(this));
                }

                // TODO: Og II - check this later to see if it is still required.
                Session.Network.EnqueueSend(new GameMessageUpdateObject(item));

                // Was Item controlled by a generator?
                // TODO: Should this be happening this way? Should the landblock notify the object of pickup or the generator...

                /*if (item.GeneratorId > 0)
                 * {
                 *  WorldObject generator = GetObject(new ObjectGuid((uint)item.GeneratorId));
                 *
                 *  item.GeneratorId = null;
                 *
                 *  generator.NotifyGeneratorOfPickup(item.Guid.Full);
                 * }*/

                item.SaveBiotaToDatabase();
            });
            // Set chain to run
            pickUpItemChain.EnqueueChain();
        }