예제 #1
0
        public void PlayerEnterWorld()
        {
            PlayerManager.SwitchPlayerFromOfflineToOnline(this);

            // Save the the LoginTimestamp
            var lastLoginTimestamp = Time.GetUnixTime();

            SetProperty(PropertyInt.LoginTimestamp, (int)lastLoginTimestamp);

            Character.LastLoginTimestamp = lastLoginTimestamp;
            Character.TotalLogins++;
            CharacterChangesDetected = true;

            Sequences.SetSequence(SequenceType.ObjectInstance, new UShortSequence((ushort)Character.TotalLogins));

            if (BarberActive)
            {
                BarberActive = false;
            }

            HandleAugsForwardCompatibility();

            if (AllegianceNode != null)
            {
                AllegianceRank = (int)AllegianceNode.Rank;
            }
            else
            {
                AllegianceRank = null;
            }

            // SendSelf will trigger the entrance into portal space
            SendSelf();

            // Init the client with the chat channel ID's, and then notify the player that they've choined the associated channels.
            UpdateChatChannels();

            var general  = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "General");
            var trade    = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Trade");
            var lfg      = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "LFG");
            var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Roleplay");

            Session.Network.EnqueueSend(general, trade, lfg, roleplay);

            // check if vassals earned XP while offline
            HandleAllegianceOnLogin();
            HandleHouseOnLogin();

            if (PlayerKillerStatus == PlayerKillerStatus.PKLite)
            {
                var actionChain = new ActionChain();
                actionChain.AddDelaySeconds(3.0f);
                actionChain.AddAction(this, () =>
                {
                    UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK);

                    Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNonPKAgain));
                });
                actionChain.EnqueueChain();
            }

            HandleDBUpdates();
        }
예제 #2
0
        /// <summary>
        /// Broadcasts the player death animation, updates vitae, and sends network messages for player death
        /// Queues the action to call TeleportOnDeath and enter portal space soon
        /// </summary>
        protected override void Die(DamageHistoryInfo lastDamager, DamageHistoryInfo topDamager)
        {
            if (topDamager?.Guid == Guid && IsPKType)
            {
                var topDamagerOther = DamageHistory.GetTopDamager(false);

                if (topDamagerOther != null && topDamagerOther.IsPlayer)
                {
                    topDamager = topDamagerOther;
                }
            }

            UpdateVital(Health, 0);
            NumDeaths++;
            suicideInProgress = false;

            // TODO: instead of setting IsBusy here,
            // eventually all of the places that check for states such as IsBusy || Teleporting
            // might want to use a common function, and IsDead should return a separate error
            IsBusy = true;

            // killer = top damager for looting rights
            if (topDamager != null)
            {
                KillerId = topDamager.Guid.Full;
            }

            // broadcast death animation
            var deathAnim = new Motion(MotionStance.NonCombat, MotionCommand.Dead);

            EnqueueBroadcastMotion(deathAnim);

            // create network messages for player death
            var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0);

            // TODO: death sounds? seems to play automatically in client
            // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f);
            var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths);

            // send network messages for player death
            Session.Network.EnqueueSend(msgHealthUpdate, msgNumDeaths);

            if (lastDamager?.Guid == Guid) // suicide
            {
                var msgSelfInflictedDeath = new GameEventWeenieError(Session, WeenieError.YouKilledYourself);
                Session.Network.EnqueueSend(msgSelfInflictedDeath);
            }

            // update vitae
            // players who died in a PKLite fight do not accrue vitae
            if (!IsPKLiteDeath(topDamager))
            {
                InflictVitaePenalty();
            }

            if (/*IsPKDeath(topDamager) || */ AugmentationSpellsRemainPastDeath == 0)
            {
                var msgPurgeEnchantments = new GameEventMagicPurgeEnchantments(Session);
                EnchantmentManager.RemoveAllEnchantments();
                Session.Network.EnqueueSend(msgPurgeEnchantments);
            }

            // wait for the death animation to finish
            var dieChain   = new ActionChain();
            var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead);

            dieChain.AddDelaySeconds(animLength + 1.0f);

            dieChain.AddAction(this, () =>
            {
                CreateCorpse(topDamager);

                ThreadSafeTeleportOnDeath(); // enter portal space

                if (IsPKDeath(topDamager) || IsPKLiteDeath(topDamager))
                {
                    SetMinimumTimeSincePK();
                    GlobalEventManager.SendDeathDetailsViaHTTP(topDamager, lastDamager, this);
                }

                IsBusy = false;
            });

            dieChain.EnqueueChain();
        }
예제 #3
0
        private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas)
        {
            Player player;

            Player.HandleNoLogLandblock(playerBiota);

            var stripAdminProperties  = false;
            var addAdminProperties    = false;
            var addSentinelProperties = false;

            if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions)
            {
                if (session.AccessLevel <= AccessLevel.Advocate)                                                               // check for elevated characters
                {
                    if (playerBiota.WeenieType == (int)WeenieType.Admin || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Downgrade weenie
                    {
                        character.IsPlussed    = false;
                        playerBiota.WeenieType = (int)WeenieType.Creature;
                        stripAdminProperties   = true;
                    }
                }
                else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy)
                {
                    if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Admin) // Up/downgrade weenie
                    {
                        character.IsPlussed    = true;
                        playerBiota.WeenieType = (int)WeenieType.Sentinel;
                        addSentinelProperties  = true;
                    }
                }
                else // Developers and Admins
                {
                    if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Up/downgrade weenie
                    {
                        character.IsPlussed    = true;
                        playerBiota.WeenieType = (int)WeenieType.Admin;
                        addAdminProperties     = true;
                    }
                }
            }

            if (playerBiota.WeenieType == (int)WeenieType.Admin)
            {
                player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session);
            }
            else if (playerBiota.WeenieType == (int)WeenieType.Sentinel)
            {
                player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session);
            }
            else
            {
                player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session);
            }

            session.SetPlayer(player);

            if (stripAdminProperties) // continue stripping properties
            {
                player.CloakStatus = null;
                player.Attackable  = true;
                player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, true);
                player.AdvocateLevel            = null;
                player.ChannelsActive           = null;
                player.ChannelsAllowed          = null;
                player.Invincible               = false;
                player.Cloaked                  = null;
                player.IgnoreHouseBarriers      = false;
                player.IgnorePortalRestrictions = false;
                player.SafeSpellComponents      = false;


                player.ChangesDetected          = true;
                player.CharacterChangesDetected = true;
            }

            if (addSentinelProperties || addAdminProperties) // continue restoring properties to default
            {
                WorldObject weenie;

                if (addAdminProperties)
                {
                    weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full));
                }
                else
                {
                    weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full));
                }

                if (weenie != null)
                {
                    player.CloakStatus = CloakStatus.Off;
                    player.Attackable  = weenie.Attackable;
                    player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, false);
                    player.AdvocateLevel   = weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.AdvocateLevel);
                    player.ChannelsActive  = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsActive);
                    player.ChannelsAllowed = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsAllowed);
                    player.Invincible      = false;
                    player.Cloaked         = false;


                    player.ChangesDetected          = true;
                    player.CharacterChangesDetected = true;
                }
            }

            // If the client is missing a location, we start them off in the starter town they chose
            if (session.Player.Location == null)
            {
                if (session.Player.Instantiation != null)
                {
                    session.Player.Location = new Position(session.Player.Instantiation);
                }
                else
                {
                    session.Player.Location = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback;
                }
            }

            session.Player.PlayerEnterWorld();

            var success = LandblockManager.AddObject(session.Player, true);

            if (!success)
            {
                // send to lifestone, or fallback location
                var fixLoc = session.Player.Sanctuary ?? new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f);

                log.Error($"WorldManager.DoPlayerEnterWorld: failed to spawn {session.Player.Name}, relocating to {fixLoc.ToLOCString()}");

                session.Player.Location = new Position(fixLoc);
                LandblockManager.AddObject(session.Player, true);

                var actionChain = new ActionChain();
                actionChain.AddDelaySeconds(5.0f);
                actionChain.AddAction(session.Player, () =>
                {
                    if (session != null && session.Player != null)
                    {
                        session.Player.Teleport(fixLoc);
                    }
                });
                actionChain.EnqueueChain();
            }

            var popup_header  = PropertyManager.GetString("popup_header").Item;
            var popup_motd    = PropertyManager.GetString("popup_motd").Item;
            var popup_welcome = PropertyManager.GetString("popup_welcome").Item;

            if (character.TotalLogins <= 1)
            {
                session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome)));
            }
            else if (!string.IsNullOrEmpty(popup_motd))
            {
                session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd)));
            }

            var info = "Welcome to Asheron's Call\n  powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n";

            session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast));

            var server_motd = PropertyManager.GetString("server_motd").Item;

            if (!string.IsNullOrEmpty(server_motd))
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast));
            }
        }
예제 #4
0
        public static void UseUnlocker(Player player, WorldObject unlocker, WorldObject target)
        {
            ActionChain chain = new ActionChain();

            chain.AddAction(player, () =>
            {
                if (unlocker.WeenieType == WeenieType.Lockpick &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Trained &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Specialized)
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking));
                    return;
                }
                if (target is Lock @lock)
                {
                    UnlockResults result = UnlockResults.IncorrectKey;
                    if (unlocker.WeenieType == WeenieType.Lockpick)
                    {
                        result = @lock.Unlock(player.Skills[Skill.Lockpick].Current);
                    }
                    else if (unlocker is Key woKey)
                    {
                        if (target is Door woDoor)
                        {
                            if (woDoor.LockCode == "") // the door isn't to be opened with keys
                            {
                                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                                return;
                            }
                        }
                        result = @lock.Unlock(woKey.KeyCode);
                    }

                    switch (result)
                    {
                    case UnlockResults.UnlockSuccess:
                        if (unlocker.WeenieType == WeenieType.Lockpick)
                        {
                            player.HandleActionApplySoundEffect(Sound.Lockpicking);    // Sound.Lockpicking doesn't work via EnqueueBroadcastSound for some reason.
                        }
                        player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You have successfully picked the lock! It is now unlocked.", ChatMessageType.Craft));
                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.Open:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen));
                        break;

                    case UnlockResults.AlreadyUnlocked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked));
                        break;

                    case UnlockResults.PickLockFailed:
                        target.EnqueueBroadcast(new GameMessageSound(target.Guid, Sound.PicklockFail, 1.0f));
                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.CannotBePicked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                        break;

                    case UnlockResults.IncorrectKey:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock));
                        break;
                    }
                }
                else
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                }
            });

            chain.EnqueueChain();
        }
예제 #5
0
파일: Player.cs 프로젝트: Thomasy388/ACE
        public void LogOut_Inner(bool clientSessionTerminatedAbruptly = false)
        {
            if (Fellowship != null)
            {
                FellowshipQuit(false);
            }

            if (IsTrading && TradePartner != null)
            {
                var tradePartner = PlayerManager.GetOnlinePlayer(TradePartner);

                if (tradePartner != null)
                {
                    tradePartner.HandleActionCloseTradeNegotiations();
                }
            }

            if (!clientSessionTerminatedAbruptly)
            {
                // Thie retail server sends a ChatRoomTracker 0x0295 first, then the status message, 0x028B. It does them one at a time for each individual channel.
                // The ChatRoomTracker message doesn't seem to change at all.
                // For the purpose of ACE, we simplify this process.
                var general  = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "General");
                var trade    = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "Trade");
                var lfg      = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "LFG");
                var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "Roleplay");
                Session.Network.EnqueueSend(general, trade, lfg, roleplay);
            }

            if (CurrentActiveCombatPet != null)
            {
                CurrentActiveCombatPet.Destroy();
            }

            if (CurrentLandblock != null)
            {
                var logout = new Motion(MotionStance.NonCombat, MotionCommand.LogOut);
                EnqueueBroadcastMotion(logout);

                EnqueueBroadcastPhysicsState();

                var logoutChain = new ActionChain();

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

                // remove the player from landblock management -- after the animation has run
                logoutChain.AddAction(this, () =>
                {
                    CurrentLandblock?.RemoveWorldObject(Guid, false);
                    SetPropertiesAtLogOut();
                    SavePlayerToDatabase();
                    PlayerManager.SwitchPlayerFromOnlineToOffline(this);
                });

                // close any open landblock containers (chests / corpses)
                if (LastOpenedContainerId != ObjectGuid.Invalid)
                {
                    var container = CurrentLandblock.GetObject(LastOpenedContainerId) as Container;

                    if (container != null)
                    {
                        container.Close(this);
                    }
                }

                logoutChain.EnqueueChain();
            }
            else
            {
                SetPropertiesAtLogOut();
                SavePlayerToDatabase();
                PlayerManager.SwitchPlayerFromOnlineToOffline(this);
            }
        }
예제 #6
0
        /// <summary>
        /// Handles the eviction process for a player house
        /// </summary>
        public static void HandleEviction(House house, uint playerGuid, bool multihouse = false, bool force = false)
        {
            // clear out slumlord inventory
            var slumlord = house.SlumLord;

            slumlord.ClearInventory(true);

            var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline);

            if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse && !force)
            {
                // rent disabled, push forward
                var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0);
                var nextRentTime = house.GetRentDue(purchaseTime);
                player.HouseRentTimestamp = (int)nextRentTime;

                log.Debug($"[HOUSE] HouseManager.HandleRentPaid({player.Name}): house rent disabled via config");

                // re-add item to queue
                AddRentQueue(player, house);
                return;
            }

            // handle eviction
            house.HouseOwner     = null;
            house.MonarchId      = null;
            house.HouseOwnerName = null;

            house.ClearPermissions();

            house.SaveBiotaToDatabase();

            // relink
            house.UpdateLinks();

            if (house.HasDungeon)
            {
                var dungeonHouse = house.GetDungeonHouse();
                if (dungeonHouse != null)
                {
                    dungeonHouse.UpdateLinks();
                }
            }

            // player slumlord 'off' animation
            slumlord.Off();

            // reset slumlord name
            slumlord.SetAndBroadcastName();

            slumlord.SaveBiotaToDatabase();

            // if evicting a multihouse owner's previous house,
            // no update for player properties
            if (player.HouseInstance == house.Guid.Full)
            {
                player.HouseId       = null;
                player.HouseInstance = null;
                //player.HousePurchaseTimestamp = null;
                player.HouseRentTimestamp = null;
            }
            else
            {
                log.Warn($"HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}");
            }

            house.ClearRestrictions();

            log.Debug($"[HOUSE] HouseManager.HandleRentEviction({player.Name})");

            if (multihouse)
            {
                RemoveRentQueue(house.Guid.Full);

                player.SaveBiotaToDatabase();

                return;
            }

            if (!isOnline)
            {
                // inform player of eviction when they log in
                var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid);
                if (offlinePlayer == null)
                {
                    log.Warn($"{player.Name}.HandleEviction(): couldn't find offline player");
                    return;
                }
                offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true);
                offlinePlayer.SaveBiotaToDatabase();
                return;
            }

            var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid);

            onlinePlayer.House = null;

            // send text message
            onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You've been evicted from your house!", ChatMessageType.Broadcast));
            onlinePlayer.RemoveDeed();

            onlinePlayer.SaveBiotaToDatabase();

            // clear house panel for online player
            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(3.0f);  // wait for slumlord inventory biotas above to save
            actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse);
            actionChain.EnqueueChain();
        }
예제 #7
0
        /// <summary>
        /// Handles the eviction process for a player house
        /// </summary>
        public static void HandleEviction(House house, uint playerGuid, bool multihouse = false)
        {
            // clear out slumlord inventory
            var slumlord = house.SlumLord;

            slumlord.ClearInventory(true);

            var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline);

            if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse)
            {
                // rent disabled, push forward
                var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0);
                var nextRentTime = house.GetRentDue(purchaseTime);
                player.HouseRentTimestamp = (int)nextRentTime;

                log.Info($"HouseManager.HandleRentPaid({player.Name}): house rent disabled via config");

                // re-add item to queue
                AddRentQueue(player, house);
                return;
            }

            // handle eviction
            house.HouseOwner     = null;
            house.MonarchId      = null;
            house.HouseOwnerName = null;

            house.ClearPermissions();

            house.SaveBiotaToDatabase();

            // relink
            house.UpdateLinks();

            // player slumlord 'off' animation
            var off = new Motion(MotionStance.Invalid, MotionCommand.Off);

            slumlord.CurrentMotionState = off;
            slumlord.EnqueueBroadcastMotion(off);

            // reset slumlord name
            var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId);
            var wo     = WorldObjectFactory.CreateWorldObject(weenie, ObjectGuid.Invalid);

            slumlord.Name = wo.Name;

            slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name));
            slumlord.SaveBiotaToDatabase();

            // if evicting a multihouse owner's previous house,
            // no update for player properties
            if (player.HouseInstance == house.Guid.Full)
            {
                player.HouseId       = null;
                player.HouseInstance = null;
                //player.HousePurchaseTimestamp = null;
                player.HouseRentTimestamp = null;
            }
            else
            {
                log.Warn($"HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}");
            }

            house.ClearRestrictions();

            log.Info($"HouseManager.HandleRentEviction({player.Name})");

            if (multihouse)
            {
                RemoveRentQueue(house.Guid.Full);

                player.SaveBiotaToDatabase();

                return;
            }

            if (!isOnline)
            {
                // inform player of eviction when they log in
                var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid);
                if (offlinePlayer == null)
                {
                    log.Warn($"{player.Name}.HandleEviction(): couldn't find offline player");
                    return;
                }
                offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true);
                offlinePlayer.SaveBiotaToDatabase();
                return;
            }

            var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid);

            onlinePlayer.House = null;

            // send text message
            onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast));
            onlinePlayer.RemoveDeed();

            onlinePlayer.SaveBiotaToDatabase();

            // clear house panel for online player
            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(3.0f);  // wait for slumlord inventory biotas above to save
            actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse);
            actionChain.EnqueueChain();
        }
예제 #8
0
        /// <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;
                }

                // handle self-procs
                TryProcEquippedItems(this, true);

                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;
        }
예제 #9
0
        /// <summary>
        /// Performs a player melee attack against a target
        /// </summary>
        public void Attack(WorldObject target)
        {
            if (CombatMode != CombatMode.Melee || MeleeTarget == null || !IsAlive)
            {
                return;
            }

            var creature = target as Creature;

            if (creature == null || !creature.IsAlive)
            {
                return;
            }

            var animLength = DoSwingMotion(target);

            if (animLength == 0)
            {
                return;
            }

            var weapon     = GetEquippedMeleeWeapon();
            var attackType = GetWeaponAttackType(weapon);
            var numStrikes = GetNumStrikes(attackType);
            var swingTime  = animLength / numStrikes / 1.5f;

            var actionChain = new ActionChain();

            // stamina usage
            // TODO: ensure enough stamina for attack
            var staminaCost = GetAttackStamina(GetPowerRange());

            UpdateVitalDelta(Stamina, -staminaCost);

            for (var i = 0; i < numStrikes; i++)
            {
                // are there animation hooks for damage frames?
                if (numStrikes > 1 && !TwoHandedCombat)
                {
                    actionChain.AddDelaySeconds(swingTime);
                }

                actionChain.AddAction(this, () =>
                {
                    DamageTarget(creature, weapon);

                    if (weapon != null && weapon.IsCleaving)
                    {
                        var cleave = GetCleaveTarget(creature, weapon);
                        foreach (var cleaveHit in cleave)
                        {
                            DamageTarget(cleaveHit, weapon);
                        }
                    }
                });

                if (numStrikes == 1 || TwoHandedCombat)
                {
                    actionChain.AddDelaySeconds(swingTime);
                }
            }

            actionChain.AddDelaySeconds(animLength - swingTime * numStrikes);

            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));

                    // powerbar refill timing
                    var refillMod = IsDualWieldAttack ? 0.8f : 1.0f;    // dual wield powerbar refills 20% faster
                    actionChain.AddDelaySeconds(PowerLevel * refillMod);
                    actionChain.AddAction(this, () =>
                    {
                        Attack(target);
                    });
                }
                else
                {
                    MeleeTarget = null;
                }
            });

            actionChain.EnqueueChain();
        }
예제 #10
0
        public static void HandleHouseSelect(Session session, bool confirmed, params string[] parameters)
        {
            if (!int.TryParse(parameters[0], out var houseIdx))
            {
                return;
            }

            // ensure current multihouse owner
            if (!session.Player.IsMultiHouseOwner(false))
            {
                log.Warn($"{session.Player.Name} tried to /house-select {houseIdx}, but they are not currently a multi-house owner!");
                return;
            }

            // get house info for this index
            var multihouses = session.Player.GetMultiHouses();

            if (houseIdx < 1 || houseIdx > multihouses.Count)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Please enter a number between 1 and {multihouses.Count}.", ChatMessageType.Broadcast));
                return;
            }

            var keepHouse = multihouses[houseIdx - 1];

            // show confirmation popup
            if (!confirmed)
            {
                var houseType = $"{keepHouse.HouseType}".ToLower();;
                var loc       = HouseManager.GetCoords(keepHouse.SlumLord.Location);

                var msg = $"Are you sure you want to keep the {houseType} at\n{loc}?";
                session.Player.ConfirmationManager.EnqueueSend(new Confirmation_Custom(session.Player.Guid, () => HandleHouseSelect(session, true, parameters)), msg);
                return;
            }

            // house to keep confirmed, abandon the other houses
            var abandonHouses = new List <House>(multihouses);

            abandonHouses.RemoveAt(houseIdx - 1);

            foreach (var abandonHouse in abandonHouses)
            {
                var house = session.Player.GetHouse(abandonHouse.Guid.Full);

                HouseManager.HandleEviction(house, house.HouseOwner ?? 0, true);
            }

            // set player properties for house to keep
            var player = PlayerManager.FindByGuid(keepHouse.HouseOwner ?? 0, out bool isOnline);

            if (player == null)
            {
                log.Error($"{session.Player.Name}.HandleHouseSelect({houseIdx}) - couldn't find HouseOwner {keepHouse.HouseOwner} for {keepHouse.Name} ({keepHouse.Guid})");
                return;
            }

            player.HouseId       = keepHouse.HouseId;
            player.HouseInstance = keepHouse.Guid.Full;

            player.SaveBiotaToDatabase();

            // update house panel for current player
            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(3.0f);  // wait for slumlord inventory biotas above to save
            actionChain.AddAction(session.Player, session.Player.HandleActionQueryHouse);
            actionChain.EnqueueChain();

            Console.WriteLine("OK");
        }
예제 #11
0
        public void SwitchToMeleeAttack()
        {
            if (IsDead)
            {
                return;
            }

            var weapon = GetEquippedMissileWeapon();
            var ammo   = GetEquippedAmmo();

            if (weapon == null && ammo == null)
            {
                return;
            }

            var actionChain = new ActionChain();

            EnqueueMotion_Force(actionChain, MotionStance.NonCombat, MotionCommand.Ready, (MotionCommand)CurrentMotionState.Stance);

            EnqueueMotion_Force(actionChain, MotionStance.HandCombat, MotionCommand.Ready, MotionCommand.NonCombat);

            actionChain.AddAction(this, () =>
            {
                if (IsDead)
                {
                    return;
                }

                // actually destroys the missile weapon + ammo here,
                // to ensure they can't be re-selected from inventory
                if (weapon != null)
                {
                    TryUnwieldObjectWithBroadcasting(weapon.Guid, out _, out _);
                    weapon.Destroy();
                }

                if (ammo != null)
                {
                    TryUnwieldObjectWithBroadcasting(ammo.Guid, out _, out _);
                    ammo.Destroy();
                }

                EquipInventoryItems(true);

                var innerChain = new ActionChain();

                EnqueueMotion_Force(innerChain, MotionStance.NonCombat, MotionCommand.Ready, (MotionCommand)CurrentMotionState.Stance);

                innerChain.AddAction(this, () =>
                {
                    if (IsDead)
                    {
                        return;
                    }

                    //DoAttackStance();

                    // inlined DoAttackStance() / slightly modified -- do not rely on SetCombatMode() for stance swapping time in 1 action,
                    // as it doesn't support that anymore

                    var newStanceTime = SetCombatMode(CombatMode.Melee);

                    NextMoveTime = NextAttackTime = Timers.RunningTime + newStanceTime;

                    PrevAttackTime = NextMoveTime - (AiUseMagicDelay ?? 3.0f);

                    PhysicsObj.StartTimer();

                    // end inline

                    ResetAttack();

                    SwitchWeaponsPending = false;

                    // this is an unfortunate hack to fix the following scenario:

                    // since this function can be called at any point in time now,
                    // including when LaunchMissile -> EnqueueMotion is in the middle of an action queue,
                    // CurrentMotionState.Stance can get reset to the previous combat stance if that happens

                    var newStance = CurrentMotionState.Stance;

                    var swapChain = new ActionChain();
                    swapChain.AddDelaySeconds(2.0f);
                    swapChain.AddAction(this, () => CurrentMotionState.Stance = newStance);
                    swapChain.EnqueueChain();
                });
                innerChain.EnqueueChain();
            });
            actionChain.EnqueueChain();
        }
예제 #12
0
        /// <summary>
        /// This is raised by Player.HandleActionUseItem.<para />
        /// The item does not exist in the players possession.<para />
        /// If the item was outside of range, the player will have been commanded to move using DoMoveTo before ActOnUse is called.<para />
        /// When this is called, it should be assumed that the player is within range.
        /// </summary>
        public override void ActOnUse(WorldObject worldObject)
        {
            var player = worldObject as Player;

            if (player == null)
            {
                return;
            }

            //if (ServerIsPKServer) // Need some form of config switch in configmanager...
            //{
            //    player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UsePkServerError), ChatMessageType.Broadcast));
            //    player.SendUseDoneEvent();
            //    return;
            //}

            if (AllowedActivator != null)
            {
                // do nothing / in use error msg?
                player.SendUseDoneEvent();
                return;
            }

            if (player.IsAdvocate || player.AdvocateQuest || player.AdvocateState)
            {
                // Advocates cannot change their PK status
                if (PkLevelModifier == 1)
                {
                    player.SendUseDoneEvent();
                    return; // maybe send error msg to tell PK to ask another advocate to @remove them (or maybe make the @remove command support self removal)
                }

                // letting it fall through for the NpkSwitch because it will not change status and error properly.
            }

            //if (player.PkLevelModifier == 0) // wrong check but if PkTimestamp(? maybe different timestamp) + MINIMUM_TIME_SINCE_PK_FLOAT < Time.GetUnixTimestamp proceed else fail
            //{
            if (player.PkLevelModifier != PkLevelModifier)
            {
                AllowedActivator = ObjectGuid.Invalid.Full;

                var rotateTime = player.Rotate(this);

                var switchTimer = new ActionChain();
                switchTimer.AddDelaySeconds(rotateTime);

                var useMotion = UseTargetSuccessAnimation != MotionCommand.Invalid ? UseTargetSuccessAnimation : MotionCommand.Twitch1;
                switchTimer.AddAction(player, () => EnqueueBroadcastMotion(new Motion(this, useMotion)));

                var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId);
                switchTimer.AddDelaySeconds(motionTable.GetAnimationLength(useMotion));

                switchTimer.AddAction(player, () =>
                {
                    player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UseMessage), ChatMessageType.Broadcast));
                    player.PkLevelModifier = PkLevelModifier;

                    player.SendUseDoneEvent();

                    if (player.PkLevelModifier == 1)
                    {
                        player.PlayerKillerStatus = PlayerKillerStatus.PK;
                    }
                    else
                    {
                        player.PlayerKillerStatus = PlayerKillerStatus.NPK;
                    }

                    player.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(player, PropertyInt.PlayerKillerStatus, (int)player.PlayerKillerStatus));

                    Reset();
                });
                switchTimer.EnqueueChain();
            }
            else
            {
                if (UseTargetFailureAnimation != MotionCommand.Invalid)
                {
                    //EnqueueBroadcastMotion(new Motion(MotionStance.NonCombat, UseTargetFailureAnimation));
                    EnqueueBroadcastMotion(new Motion(this, UseTargetFailureAnimation));
                }

                player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.ActivationFailure), ChatMessageType.Broadcast));
                player.SendUseDoneEvent();
            }
        }
예제 #13
0
        /// <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 = DateTime.UtcNow.AddSeconds(timeOffset);
        }
예제 #14
0
        public override void HandleActionOnUse(ObjectGuid playerId)
        {
            string serverMessage;

            // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage
            var rangeCheck = 5.0f;

            ActionChain chain = new ActionChain();

            CurrentLandblock.ChainOnObject(chain, playerId, (WorldObject wo) =>
            {
                Player player = wo as Player;
                if (player == null)
                {
                    return;
                }
                // Can check location by guid
                // NOTE: Must use CurrentLandblock.GetPosition() for the portal's position...
                if (CurrentLandblock.GetPosition(Guid).SquaredDistanceTo(player.Location) < rangeCheck)
                {
                    if (Destination != null)
                    {
#if DEBUG
                        serverMessage        = "Checking requirements for " + this.Name;
                        var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System);
                        player.Session.Network.EnqueueSend(usePortalMessage);
#endif
                        // Check player level -- requires remote query to player (ugh)...
                        if ((player.Level >= MinimumLevel) && ((player.Level <= MaximumLevel) || (MaximumLevel == 0)))
                        {
                            Position portalDest = Destination;
                            switch (WeenieClassId)
                            {
                            /// <summary>
                            /// Setup correct racial portal destination for the Central Courtyard in the Training Academy
                            /// </summary>
                            case (ushort)SpecialPortalWCID.CentralCourtyard:
                                {
                                    uint playerLandblockId = player.Location.LandblockId.Raw;
                                    switch (playerLandblockId)
                                    {
                                    case (uint)SpecialPortalLandblockID.ShoushiCCLaunch:            // Shoushi
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiCCLanding);
                                            break;
                                        }

                                    case (uint)SpecialPortalLandblockID.YaraqCCLaunch:            // Yaraq
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqCCLanding);
                                            break;
                                        }

                                    case (uint)SpecialPortalLandblockID.SanamarCCLaunch:            // Sanamar
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarCCLanding);
                                            break;
                                        }

                                    default:                    // Holtburg
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgCCLanding);
                                            break;
                                        }
                                    }

                                    portalDest.PositionX = Destination.PositionX;
                                    portalDest.PositionY = Destination.PositionY;
                                    portalDest.PositionZ = Destination.PositionZ;
                                    portalDest.RotationX = Destination.RotationX;
                                    portalDest.RotationY = Destination.RotationY;
                                    portalDest.RotationZ = Destination.RotationZ;
                                    portalDest.RotationW = Destination.RotationW;
                                    break;
                                }

                            /// <summary>
                            /// Setup correct racial portal destination for the Outer Courtyard in the Training Academy
                            /// </summary>
                            case (ushort)SpecialPortalWCID.OuterCourtyard:
                                {
                                    uint playerLandblockId = player.Location.LandblockId.Raw;
                                    switch (playerLandblockId)
                                    {
                                    case (uint)SpecialPortalLandblockID.ShoushiOCLaunch:            // Shoushi
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiOCLanding);
                                            break;
                                        }

                                    case (uint)SpecialPortalLandblockID.YaraqOCLaunch:            // Yaraq
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqOCLanding);
                                            break;
                                        }

                                    case (uint)SpecialPortalLandblockID.SanamarOCLaunch:            // Sanamar
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarOCLanding);
                                            break;
                                        }

                                    default:                    // Holtburg
                                        {
                                            portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgOCLanding);
                                            break;
                                        }
                                    }

                                    portalDest.PositionX = Destination.PositionX;
                                    portalDest.PositionY = Destination.PositionY;
                                    portalDest.PositionZ = Destination.PositionZ;
                                    portalDest.RotationX = Destination.RotationX;
                                    portalDest.RotationY = Destination.RotationY;
                                    portalDest.RotationZ = Destination.RotationZ;
                                    portalDest.RotationW = Destination.RotationW;
                                    break;
                                }

                            /// <summary>
                            /// All other portals don't need adjustments.
                            /// </summary>
                            default:
                                {
                                    break;
                                }
                            }

#if DEBUG
                            serverMessage    = "Portal sending player to destination";
                            usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System);
                            player.Session.Network.EnqueueSend(usePortalMessage);
#endif
                            player.Session.Player.Teleport(portalDest);
                            // If the portal just used is able to be recalled to,
                            // save the destination coordinates to the LastPortal character position save table
                            if (IsRecallable)
                            {
                                player.SetCharacterPosition(PositionType.LastPortal, portalDest);
                            }
                            // always send useDone event
                            var sendUseDoneEvent = new GameEventUseDone(player.Session);
                            player.Session.Network.EnqueueSend(sendUseDoneEvent);
                        }
                        else if ((player.Level > MaximumLevel) && (MaximumLevel != 0))
                        {
                            // You are too powerful to interact with that portal!
                            var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AC);
                            // always send useDone event
                            var sendUseDoneEvent = new GameEventUseDone(player.Session);
                            player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent);
                        }
                        else
                        {
                            // You are not powerful enough to interact with that portal!
                            var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AB);
                            // always send useDone event
                            var sendUseDoneEvent = new GameEventUseDone(player.Session);
                            player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent);
                        }
                    }
                    else
                    {
                        serverMessage = "Portal destination for portal ID " + this.WeenieClassId + " not yet implemented!";
                        var failedUsePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System);
                        // always send useDone event
                        var sendUseDoneEvent = new GameEventUseDone(player.Session);
                        player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent);
                    }
                }
                else
                {
                    // always send useDone event
                    var sendUseDoneEvent = new GameEventUseDone(player.Session);
                    player.Session.Network.EnqueueSend(sendUseDoneEvent);
                }
            });
            // Run on the player
            chain.EnqueueChain();
        }
예제 #15
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();
        }
예제 #16
0
        /// <summary>
        /// This is raised by Player.HandleActionUseItem.<para />
        /// The item should be in the players possession.
        /// </summary>
        public override void ActOnUse(WorldObject activator)
        {
            // Research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling

            if (!(activator is Player player))
            {
                return;
            }

            if (Spell == null)
            {
                Console.WriteLine($"{Name}.ActOnUse({activator.Name}) - SpellDID not found for {WeenieClassId}");
                return;
            }

            if (player.IsBusy)
            {
                player.SendWeenieError(WeenieError.YoureTooBusy);
                return;
            }

            player.IsBusy = true;

            var actionChain = new ActionChain();

            if (player.CombatMode != CombatMode.NonCombat)
            {
                var stanceTime = player.SetCombatMode(CombatMode.NonCombat);
                actionChain.AddDelaySeconds(stanceTime);

                player.LastUseTime += stanceTime;
            }

            var animTime = player.EnqueueMotion(actionChain, MotionCommand.Reading);

            player.LastUseTime += animTime;

            var readTime = 1.0f;

            actionChain.AddDelaySeconds(readTime);
            player.LastUseTime += readTime;

            actionChain.AddAction(player, () =>
            {
                if (player.SpellIsKnown(Spell.Id))
                {
                    // verify unknown spell
                    player.Session.Network.EnqueueSend(new GameMessageSystemChat("You already know that spell!", ChatMessageType.Broadcast));
                    return;
                }

                var skill       = Spell.GetMagicSkill();
                var playerSkill = player.GetCreatureSkill(skill);

                if (!player.CanReadScroll(this))
                {
                    var msg = "";
                    if (playerSkill.AdvancementClass < SkillAdvancementClass.Trained)
                    {
                        msg = $"You are not trained in {playerSkill.Skill.ToSentence()}!";
                    }
                    else
                    {
                        msg = $"You are not skilled enough in {playerSkill.Skill.ToSentence()} to learn this spell.";
                    }

                    player.Session.Network.EnqueueSend(new GameMessageSystemChat(msg, ChatMessageType.Broadcast));
                    return;
                }

                player.LearnSpellWithNetworking(Spell.Id);

                player.TryConsumeFromInventoryWithNetworking(this);

                player.Session.Network.EnqueueSend(new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Broadcast));
            });


            // FIXME: return stance time
            player.EnqueueMotion(actionChain, MotionCommand.Ready);

            player.LastUseTime += animTime;     // return stance

            actionChain.AddDelaySeconds(animTime);

            actionChain.AddAction(player, () => player.IsBusy = false);

            actionChain.EnqueueChain();
        }
예제 #17
0
        /// <summary>
        /// This is raised when we drop an item. It can be a wielded item, or an item in our inventory.
        /// </summary>
        public void HandleActionDropItem(ObjectGuid itemGuid)
        {
            // Goody Goody -- lets build  drop chain
            // First start drop animation
            new ActionChain(this, () =>
            {
                // check packs of item.
                WorldObject item;

                if (!TryRemoveFromInventory(itemGuid, out item))
                {
                    // check to see if this item is wielded
                    if (TryDequipObject(itemGuid, out item))
                    {
                        Children.Remove(Children.Find(s => s.Guid == item.Guid.Full));

                        Session.Network.EnqueueSend(
                            new GameMessageSound(Guid, Sound.WieldObject, 1.0f),
                            new GameMessageObjDescEvent(this),
                            new GameMessageUpdateInstanceId(Guid, new ObjectGuid(0), PropertyInstanceId.Wielder));
                    }
                }

                if (item == null)
                {
                    log.Error("Player_Inventory HandleActionDropItem item is null");
                    return;
                }

                item.SetPropertiesForWorld(this);

                // It's important that we save an item after it's been removed from inventory.
                // We want to avoid the scenario where the server crashes and a player has too many items.
                item.SaveBiotaToDatabase();

                var motion = new UniversalMotion(MotionStance.Standing);
                motion.MovementData.ForwardCommand = (uint)MotionCommand.Pickup;
                Session.Network.EnqueueSend(new GameMessageUpdateInstanceId(itemGuid, new ObjectGuid(0), PropertyInstanceId.Container));

                // Set drop motion
                CurrentLandblock.EnqueueBroadcastMotion(this, motion);

                // Now wait for Drop Motion to finish -- use ActionChain
                var chain = new ActionChain();

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

                // Play drop sound
                // Put item on landblock
                chain.AddAction(this, () =>
                {
                    motion = new UniversalMotion(MotionStance.Standing);
                    CurrentLandblock.EnqueueBroadcastMotion(this, motion);
                    Session.Network.EnqueueSend(
                        new GameMessageSound(Guid, Sound.DropItem, (float)1.0),
                        new GameMessagePutObjectIn3d(Session, this, itemGuid),
                        new GameMessageUpdateInstanceId(itemGuid, new ObjectGuid(0), PropertyInstanceId.Container),
                        new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.EncumbranceVal, EncumbranceVal ?? 0));

                    // This is the sequence magic - adds back into 3d space seem to be treated like teleport.
                    item.Sequences.GetNextSequence(SequenceType.ObjectTeleport);
                    item.Sequences.GetNextSequence(SequenceType.ObjectVector);

                    CurrentLandblock.AddWorldObject(item);

                    Session.Network.EnqueueSend(new GameMessageUpdateObject(item));
                });

                chain.EnqueueChain();
            }).EnqueueChain();
        }
예제 #18
0
파일: Scroll.cs 프로젝트: klp2/ACE
        /// <summary>
        /// This is raised by Player.HandleActionUseItem.<para />
        /// The item should be in the players possession.
        /// </summary>
        public override void UseItem(Player player)
        {
            bool   success    = true;
            string failReason = "You are unable to read the scroll.";

            switch (Power)
            {
            // research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling
            case spellLevel2:     // Level 2
            case spellLevel3:     // Level 3
            case spellLevel4:     // Level 4
            case spellLevel5:     // Level 5
            case spellLevel6:     // Level 6
                if (!player.CanReadScroll(School, Power))
                {
                    success    = false;
                    failReason = "You are not skilled enough in the inscribed spell's school of magic to understand the writing on this scroll.";
                }
                break;
            }

            if (player.SpellIsKnown(SpellId))
            {
                success    = false;
                failReason = "You already know the spell inscribed upon this scroll.";
            }

            var actionChain = new ActionChain();

            actionChain
            .AddAction(player, () => player.EnqueueBroadcastMotion(motionReading))
            .AddDelaySeconds(2);

            if (success)
            {
                actionChain.AddAction(player, () =>
                {
                    player.LearnSpellWithNetworking(SpellId);
                    player.EnqueueBroadcastMotion(motionReady);

                    if (player.TryConsumeFromInventoryWithNetworking(this))
                    {
                        player.Session.Network.EnqueueSend(new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Magic));
                    }
                });
            }
            else
            {
                actionChain
                .AddDelaySeconds(2)
                .AddAction(player, () =>
                {
                    player.EnqueueBroadcastMotion(motionReady);
                    player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{failReason}", ChatMessageType.Magic));
                });
            }

            actionChain
            .AddAction(player, () => player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session)));

            actionChain.EnqueueChain();
        }
예제 #19
0
        /// <summary>
        /// Called every ~5 secs for equipped mana consuming items
        /// </summary>
        public void ManaConsumersTick()
        {
            if (!EquippedObjectsLoaded)
            {
                return;
            }

            var EquippedManaConsumers = EquippedObjects.Where(k =>
                                                              (k.Value.IsAffecting ?? false) &&
                                                              //k.Value.ManaRate.HasValue &&
                                                              k.Value.ItemMaxMana.HasValue &&
                                                              k.Value.ItemCurMana.HasValue &&
                                                              k.Value.ItemCurMana.Value > 0).ToList();

            foreach (var k in EquippedManaConsumers)
            {
                var item = k.Value;

                // this was a bug in lootgen until 7/11/2019, mostly for clothing/armor/shields
                // tons of existing items on servers are in this bugged state, where they aren't ticking mana.
                // this retroactively fixes them when equipped
                // items such as Impious Staff are excluded from this via IsAffecting

                if (item.ManaRate == null)
                {
                    item.ManaRate = LootGenerationFactory.GetManaRate(item);
                    log.Warn($"{Name}.ManaConsumersTick(): {k.Value.Name} ({k.Value.Guid}) fixed missing ManaRate");
                }

                var rate = item.ManaRate.Value;

                if (LumAugItemManaUsage != 0)
                {
                    rate *= GetNegativeRatingMod(LumAugItemManaUsage);
                }

                if (!item.ItemManaConsumptionTimestamp.HasValue)
                {
                    item.ItemManaConsumptionTimestamp = DateTime.Now;
                }
                DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value;

                var timePerBurn = -1 / rate;

                var secondsSinceLastBurn = (DateTime.Now - mostRecentBurn).TotalSeconds;

                var delta = secondsSinceLastBurn / timePerBurn;

                var deltaChopped = (int)Math.Floor(delta);
                var deltaExtra   = delta - deltaChopped;

                if (deltaChopped <= 0)
                {
                    continue;
                }

                var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn);
                item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd);
                var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped);
                deltaChopped      = Math.Clamp(deltaChopped, 0, 10);
                item.ItemCurMana -= deltaChopped;

                if (item.ItemCurMana < 1 || item.ItemCurMana == null)
                {
                    item.IsAffecting = false;
                    var msg   = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic);
                    var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted);
                    Session.Network.EnqueueSend(msg, sound);
                    if (item.WielderId != null)
                    {
                        if (item.Biota.BiotaPropertiesSpellBook != null)
                        {
                            // unsure if these messages / sounds were ever sent in retail,
                            // or if it just purged the enchantments invisibly
                            // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted'
                            var actionChain = new ActionChain();
                            actionChain.AddDelaySeconds(2.0f);
                            actionChain.AddAction(this, () =>
                            {
                                for (int i = 0; i < item.Biota.BiotaPropertiesSpellBook.Count; i++)
                                {
                                    RemoveItemSpell(item, (uint)item.Biota.BiotaPropertiesSpellBook.ElementAt(i).Spell);
                                }
                            });
                            actionChain.EnqueueChain();
                        }
                    }
                }
                else
                {
                    // get time until empty
                    var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn);
                    if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.Now - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120))
                    {
                        item.ItemManaDepletionMessageTimestamp = DateTime.Now;
                        Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic));
                    }
                }
            }
        }
예제 #20
0
파일: Lock.cs 프로젝트: decaprime/ACE
        public static void UseUnlocker(Player player, WorldObject unlocker, WorldObject target)
        {
            ActionChain chain = new ActionChain();

            chain.AddAction(player, () =>
            {
                if (unlocker.WeenieType == WeenieType.Lockpick &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Trained &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Specialized)
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking));
                    return;
                }
                if (target is Lock @lock)
                {
                    UnlockResults result = UnlockResults.IncorrectKey;
                    var difficulty       = 0;
                    if (unlocker.WeenieType == WeenieType.Lockpick)
                    {
                        var effectiveLockpickSkill = GetEffectiveLockpickSkill(player, unlocker);
                        result = @lock.Unlock(player.Guid.Full, effectiveLockpickSkill, ref difficulty);
                    }
                    else if (unlocker is Key woKey)
                    {
                        if (target is Door woDoor)
                        {
                            if (woDoor.LockCode == "") // the door isn't to be opened with keys
                            {
                                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                                return;
                            }
                        }
                        result = @lock.Unlock(player.Guid.Full, woKey);
                    }

                    switch (result)
                    {
                    case UnlockResults.UnlockSuccess:

                        if (unlocker.WeenieType == WeenieType.Lockpick)
                        {
                            // the source guid for this sound must be the player, else the sound will not play
                            // which differs from PicklockFail and LockSuccess being in the target sound table
                            player.EnqueueBroadcast(new GameMessageSound(player.Guid, Sound.Lockpicking, 1.0f));

                            player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You have successfully picked the lock! It is now unlocked.", ChatMessageType.Broadcast));

                            var lockpickSkill = player.GetCreatureSkill(Skill.Lockpick);
                            Proficiency.OnSuccessUse(player, lockpickSkill, difficulty);
                        }
                        else
                        {
                            player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{target.Name} has been unlocked.", ChatMessageType.Broadcast));
                        }

                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.Open:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen));
                        break;

                    case UnlockResults.AlreadyUnlocked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked));
                        break;

                    case UnlockResults.PickLockFailed:
                        target.EnqueueBroadcast(new GameMessageSound(target.Guid, Sound.PicklockFail, 1.0f));
                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.CannotBePicked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                        break;

                    case UnlockResults.IncorrectKey:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock));
                        break;
                    }
                }
                else
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                }
            });

            chain.EnqueueChain();
        }
예제 #21
0
 public void HandleActionWorldBroadcast(string message, ChatMessageType messageType)
 {
     ActionChain chain = new ActionChain();
     chain.AddAction(this, () => DoWorldBroadcast(message, messageType));
     chain.EnqueueChain();
 }
예제 #22
0
        public void Decay(TimeSpan elapsed)
        {
            // http://asheron.wikia.com/wiki/Item_Decay

            if (decayCompleted)
            {
                return;
            }

            var previousTTR = TimeToRot;

            if (!TimeToRot.HasValue)
            {
                TimeToRot = DefaultTimeToRot.TotalSeconds;

                if (this is Corpse && Level.HasValue)
                {
                    log.Debug($"[CORPSE] {Name} (0x{Guid.ToString()}).Decay: TimeToRot had no value, set to {TimeToRot}");
                }

                return;
            }

            var corpse = this as Corpse;

            if (corpse != null)
            {
                if (!corpse.InventoryLoaded)
                {
                    return;
                }

                if (corpse.Inventory.Count == 0 && TimeToRot.Value > Corpse.EmptyDecayTime)
                {
                    TimeToRot = Corpse.EmptyDecayTime;
                    if (Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
                    {
                        log.Debug($"[CORPSE] {corpse.Name} (0x{corpse.Guid.ToString()}).Decay({elapsed.ToString()}): InventoryLoaded = {corpse.InventoryLoaded} | Inventory.Count = {corpse.Inventory.Count} | previous TimeToRot: {previousTTR} | current TimeToRot: {TimeToRot}");
                    }
                    return;
                }
            }

            if (TimeToRot > 0)
            {
                TimeToRot -= elapsed.TotalSeconds;

                if (this is Corpse && Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
                {
                    log.Debug($"[CORPSE] {corpse.Name} (0x{corpse.Guid.ToString()}).Decay({elapsed.ToString()}): previous TimeToRot: {previousTTR} | current TimeToRot: {TimeToRot}");
                }

                // Is there still time left?
                if (TimeToRot > 0)
                {
                    return;
                }

                TimeToRot = -2; // We force it to -2 to make sure it doesn't end up at 0 or -1. 0 indicates instant rot. -1 indicates no rot. 0 and -1 can be found in weenie defaults

                if (this is Corpse && Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
                {
                    log.Debug($"[CORPSE] {corpse.Name} (0x{corpse.Guid.ToString()}).Decay({elapsed.ToString()}): previous TimeToRot: {previousTTR} | current TimeToRot: {TimeToRot}");
                }
            }

            if (this is Container container && container.IsOpen)
            {
                // If you wanted to add a grace period to the container to give Player B more time to open it after Player A closes it, it would go here.

                return;
            }

            // Time to rot has elapsed, time to disappear...
            decayCompleted = true;

            // If this is a player corpse, puke out the corpses contents onto the landblock
            if (corpse != null && !corpse.IsMonster)
            {
                var inventoryGUIDs = corpse.Inventory.Keys.ToList();

                var pukedItems = "";

                foreach (var guid in inventoryGUIDs)
                {
                    if (corpse.TryRemoveFromInventory(guid, out var item))
                    {
                        item.Location  = new Position(corpse.Location);
                        item.Placement = ACE.Entity.Enum.Placement.Resting; // This is needed to make items lay flat on the ground.
                        CurrentLandblock.AddWorldObject(item);
                        item.SaveBiotaToDatabase();
                        pukedItems += $"{item.Name} (0x{item.Guid.Full.ToString("X8")}), ";
                    }
                }

                if (pukedItems.EndsWith(", "))
                {
                    pukedItems = pukedItems.Substring(0, pukedItems.Length - 2);
                }

                log.Debug($"[CORPSE] {corpse.Name} (0x{corpse.Guid.ToString()}) at {corpse.Location.ToLOCString()} has decayed{((pukedItems == "") ? "" : $" and placed the following items on the landblock: {pukedItems}")}.");
            }

            if (corpse != null)
            {
                EnqueueBroadcast(new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Destroy));

                var actionChain = new ActionChain();
                actionChain.AddDelaySeconds(1.0f);
                actionChain.AddAction(this, () => Destroy());
                actionChain.EnqueueChain();
            }
            else
            {
                Destroy();
            }
        }
예제 #23
0
        /// <summary>
        /// Called on player login
        /// If a player has any skills trained that require updates from ACE-World-16-Patches,
        /// ensure these updates are installed, and if they aren't, send a helpful message to player with instructions for installation
        /// </summary>
        public void HandleDBUpdates()
        {
            // dirty fighting
            var dfSkill = GetCreatureSkill(Skill.DirtyFighting);

            if (dfSkill.AdvancementClass >= SkillAdvancementClass.Trained)
            {
                foreach (var spellID in SpellExtensions.DirtyFightingSpells)
                {
                    var spell = new Server.Entity.Spell(spellID);
                    if (spell.NotFound)
                    {
                        var actionChain = new ActionChain();
                        actionChain.AddDelaySeconds(3.0f);
                        actionChain.AddAction(this, () =>
                        {
                            Session.Network.EnqueueSend(new GameMessageSystemChat("To install Dirty Fighting, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast));
                        });
                        actionChain.EnqueueChain();
                    }
                    break;  // performance improvement: only check first spell
                }
            }

            // void magic
            var voidSkill = GetCreatureSkill(Skill.VoidMagic);

            if (voidSkill.AdvancementClass >= SkillAdvancementClass.Trained)
            {
                foreach (var spellID in SpellExtensions.VoidMagicSpells)
                {
                    var spell = new Server.Entity.Spell(spellID);
                    if (spell.NotFound)
                    {
                        var actionChain = new ActionChain();
                        actionChain.AddDelaySeconds(3.0f);
                        actionChain.AddAction(this, () =>
                        {
                            Session.Network.EnqueueSend(new GameMessageSystemChat("To install Void Magic, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast));
                        });
                        actionChain.EnqueueChain();
                    }
                    break;  // performance improvement: only check first spell (measured 102ms to check 75 uncached void spells)
                }
            }

            // summoning
            var summoning = GetCreatureSkill(Skill.Summoning);

            if (summoning.AdvancementClass >= SkillAdvancementClass.Trained)
            {
                uint essenceWCID = 48878;
                var  weenie      = DatabaseManager.World.GetCachedWeenie(essenceWCID);
                if (weenie == null)
                {
                    var actionChain = new ActionChain();
                    actionChain.AddDelaySeconds(3.0f);
                    actionChain.AddAction(this, () =>
                    {
                        Session.Network.EnqueueSend(new GameMessageSystemChat("To install Summoning, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast));
                    });
                    actionChain.EnqueueChain();
                }
            }
        }
예제 #24
0
        public void HandleActionEnterPkLite()
        {
            // ensure permanent npk
            if (PlayerKillerStatus != PlayerKillerStatus.NPK || MinimumTimeSincePk != null)
            {
                Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.OnlyNonPKsMayEnterPKLite));
                return;
            }

            if (TooBusyToRecall)
            {
                Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YoureTooBusy));
                return;
            }

            EnqueueBroadcast(new GameMessageSystemChat($"{Name} is looking for a fight!", ChatMessageType.Broadcast), LocalBroadcastRange);

            // perform pk lite entry motion / effect

            IsBusy = true;

            var prevStance = CurrentMotionState.Stance;

            var actionChain = new ActionChain();

            var animTime = 0.0f;

            animTime += EnqueueMotion_Force(actionChain, MotionStance.NonCombat, MotionCommand.EnterPKLite);

            actionChain.AddAction(this, () =>
            {
                if (PropertyManager.GetBool("allow_pkl_bump").Item)
                {
                    // check for collisions
                    PlayerKillerStatus = PlayerKillerStatus.PKLite;

                    var colliding = PhysicsObj.ethereal_check_for_collisions();

                    if (colliding)
                    {
                        // try initial placement
                        var result = PhysicsObj.SetPositionSimple(PhysicsObj.Position, true);

                        if (result == SetPositionError.OK)
                        {
                            // handle landblock update?
                            SyncLocation();

                            // force broadcast
                            Sequences.GetNextSequence(SequenceType.ObjectForcePosition);
                            SendUpdatePosition();
                        }
                    }
                }
                UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.PKLite, true);

                Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNowPKLite));
            });

            // return to previous stance, if applicable
            if (prevStance != MotionStance.NonCombat)
            {
                animTime += EnqueueMotion_Force(actionChain, prevStance, MotionCommand.Ready, MotionCommand.NonCombat);
            }

            actionChain.AddAction(this, () => IsBusy = false);

            actionChain.EnqueueChain();
        }
예제 #25
0
파일: Scroll.cs 프로젝트: derandark/ACE
        public override void OnUse(Session session)
        {
            bool   success    = true;
            string failReason = "You are unable to read the scroll.";

            switch (Power)
            {
            // research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling
            case spellLevel2:     // Level 2
            case spellLevel3:     // Level 3
            case spellLevel4:     // Level 4
            case spellLevel5:     // Level 5
            case spellLevel6:     // Level 6
                if (session.Player.CanReadScroll(School, Power))
                {
                    success = true;
                }
                else
                {
                    success    = false;
                    failReason = "You are not skilled enough in the inscribed spell's school of magic to understand the writing on this scroll.";
                }
                break;

            default:     // Level 1 or Level 7+ never fail
                success = true;
                break;
            }

            if (!session.Player.UnknownSpell(SpellId))
            {
                success    = false;
                failReason = "You already know the spell inscribed upon this scroll.";
            }

            ActionChain readScrollChain = new ActionChain();

            readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReading));
            readScrollChain.AddDelaySeconds(2);

            if (success)
            {
                readScrollChain.AddAction(session.Player, () => session.Player.HandleActionLearnSpell(SpellId));
                readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady));
                var removeObjMessage = new GameMessageRemoveObject(this);
                var destroyMessage   = new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Magic);
                readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(destroyMessage, removeObjMessage));
                readScrollChain.AddAction(session.Player, () => session.Player.RemoveFromInventory(session.Player.InventoryObjects, Guid));
            }
            else
            {
                readScrollChain.AddDelaySeconds(2);
                readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady));
                var failMessage = new GameMessageSystemChat($"{failReason}", ChatMessageType.Magic);
                readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(failMessage));
            }
            var sendUseDoneEvent = new GameEventUseDone(session.Player.Session);

            readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(sendUseDoneEvent));
            readScrollChain.EnqueueChain();
        }
예제 #26
0
        public void LogOut_Inner(bool clientSessionTerminatedAbruptly = false)
        {
            IsBusy       = true;
            IsLoggingOut = true;

            if (Fellowship != null)
            {
                FellowshipQuit(false);
            }

            if (IsTrading && TradePartner != ObjectGuid.Invalid)
            {
                var tradePartner = PlayerManager.GetOnlinePlayer(TradePartner);

                if (tradePartner != null)
                {
                    tradePartner.HandleActionCloseTradeNegotiations();
                }
            }

            if (!clientSessionTerminatedAbruptly)
            {
                if (PropertyManager.GetBool("use_turbine_chat").Item)
                {
                    if (GetCharacterOption(CharacterOption.ListenToGeneralChat))
                    {
                        LeaveTurbineChatChannel("General");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToTradeChat))
                    {
                        LeaveTurbineChatChannel("Trade");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToLFGChat))
                    {
                        LeaveTurbineChatChannel("LFG");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToRoleplayChat))
                    {
                        LeaveTurbineChatChannel("Roleplay");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToAllegianceChat) && Allegiance != null)
                    {
                        LeaveTurbineChatChannel("Allegiance");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToSocietyChat) && Society != FactionBits.None)
                    {
                        LeaveTurbineChatChannel("Society");
                    }
                }
            }

            if (CurrentActivePet != null)
            {
                CurrentActivePet.Destroy();
            }

            if (CurrentLandblock != null)
            {
                var logout = new Motion(MotionStance.NonCombat, MotionCommand.LogOut);
                EnqueueBroadcastMotion(logout);

                EnqueueBroadcastPhysicsState();

                var logoutChain = new ActionChain();

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

                // remove the player from landblock management -- after the animation has run
                logoutChain.AddAction(this, () =>
                {
                    if (CurrentLandblock == null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: CurrentLandblock is null, unable to remove from a landblock...");
                        if (Location != null)
                        {
                            log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: Location is not null, Location = {Location.ToLOCString()}");
                        }
                    }

                    CurrentLandblock?.RemoveWorldObject(Guid, false);
                    SetPropertiesAtLogOut();
                    SavePlayerToDatabase();
                    PlayerManager.SwitchPlayerFromOnlineToOffline(this);
                });

                // close any open landblock containers (chests / corpses)
                if (LastOpenedContainerId != ObjectGuid.Invalid)
                {
                    var container = CurrentLandblock.GetObject(LastOpenedContainerId) as Container;

                    if (container != null)
                    {
                        container.Close(this);
                    }
                }

                logoutChain.EnqueueChain();
            }
            else
            {
                log.Debug($"0x{Guid}:{Name}.LogOut_Inner: CurrentLandblock is null");
                if (Location != null)
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is not null, Location = {Location.ToLOCString()}");
                    var validLoadedLandblock = LandblockManager.GetLandblock(Location.LandblockId, false);
                    if (validLoadedLandblock.GetObject(Guid.Full) != null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is still on landblock, removing...");
                        validLoadedLandblock.RemoveWorldObject(Guid, false);
                    }
                    else
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is not found on the landblock Location references.");
                    }
                }
                else
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is null");
                }
                SetPropertiesAtLogOut();
                SavePlayerToDatabase();
                PlayerManager.SwitchPlayerFromOnlineToOffline(this);
            }
        }
예제 #27
0
        /// <summary>
        /// Vendor has validated the transactions and sent a list of items for processing.
        /// </summary>
        public void FinalizeBuyTransaction(Vendor vendor, List <WorldObject> uqlist, List <WorldObject> genlist, uint goldcost, uint altcost)
        {
            // todo research packets more for both buy and sell. ripley thinks buy is update..
            // vendor accepted the transaction

            var valid = ValidateBuyTransaction(vendor, goldcost, altcost);

            if (valid)
            {
                SpendCurrency(goldcost, WeenieType.Coin);

                foreach (WorldObject wo in uqlist)
                {
                    wo.RemoveProperty(PropertyFloat.SoldTimestamp);
                    TryCreateInInventoryWithNetworking(wo);
                }

                foreach (var gen in genlist)
                {
                    var service = gen.GetProperty(PropertyBool.VendorService) ?? false;

                    if (!service)
                    {
                        TryCreateInInventoryWithNetworking(gen);
                    }
                    else
                    {
                        var spell = new Spell(gen.SpellDID ?? 0);
                        if (!spell.NotFound)
                        {
                            var preCastTime = vendor.PreCastMotion(this);

                            var castChain = new ActionChain();
                            castChain.AddDelaySeconds(preCastTime);
                            castChain.AddAction(vendor, () =>
                            {
                                vendor.TryCastSpell(spell, this, vendor);
                                vendor.PostCastMotion();
                            });
                            castChain.EnqueueChain();
                        }
                    }
                }

                if (altcost > 0)
                {
                    var altCurrency = vendor.AlternateCurrency ?? 0;

                    TryConsumeFromInventoryWithNetworking(altCurrency, (int)altcost);
                }

                Session.Network.EnqueueSend(new GameMessageSound(Guid, Sound.PickUpItem));

                if (PropertyManager.GetBool("player_receive_immediate_save").Item)
                {
                    RushNextPlayerSave(5);
                }
            }

            vendor.BuyItems_FinalTransaction(this, uqlist, valid);
        }
예제 #28
0
        /// <summary>
        /// This method processes the Game Action (F7B1) Change Combat Mode (0x0053)
        /// </summary>
        public void HandleGameActionChangeCombatMode(CombatMode newCombatMode)
        {
            var currentCombatStance = GetCombatStance();

            switch (newCombatMode)
            {
            case CombatMode.NonCombat:
            {
                switch (currentCombatStance)
                {
                case MotionStance.BowCombat:
                case MotionStance.CrossbowCombat:
                case MotionStance.AtlatlCombat:
                {
                    var equippedAmmo = GetEquippedAmmo();
                    if (equippedAmmo != null)
                    {
                        ClearChild(equippedAmmo);         // We must clear the placement/parent when going back to peace
                    }
                    break;
                }
                }
                break;
            }

            case CombatMode.Melee:
                // todo expand checks
                break;

            case CombatMode.Missile:
            {
                switch (currentCombatStance)
                {
                case MotionStance.BowCombat:
                case MotionStance.CrossbowCombat:
                case MotionStance.AtlatlCombat:
                {
                    var equippedAmmo = GetEquippedAmmo();
                    if (equippedAmmo == null)
                    {
                        var animTime = SetCombatMode(newCombatMode);
                        Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "You are out of ammunition!"));

                        var actionChain = new ActionChain();
                        actionChain.AddDelaySeconds(animTime);
                        actionChain.AddAction(this, () => SetCombatMode(CombatMode.NonCombat));
                        actionChain.EnqueueChain();
                        return;
                    }
                    else
                    {
                        // We must set the placement/parent when going into combat
                        equippedAmmo.Placement      = ACE.Entity.Enum.Placement.RightHandCombat;
                        equippedAmmo.ParentLocation = ACE.Entity.Enum.ParentLocation.RightHand;
                    }
                    break;
                }
                }
                break;
            }

            case CombatMode.Magic:
                // todo expand checks
                break;
            }

            SetCombatMode(newCombatMode);
        }
예제 #29
0
        public void Teleport(Position newPosition)
        {
            ActionChain chain = GetTeleportChain(newPosition);

            chain.EnqueueChain();
        }
예제 #30
0
        public void PlayerEnterWorld()
        {
            PlayerManager.SwitchPlayerFromOfflineToOnline(this);
            Teleporting = true;

            // Save the the LoginTimestamp
            var lastLoginTimestamp = Time.GetUnixTime();

            SetProperty(PropertyInt.LoginTimestamp, (int)lastLoginTimestamp);

            Character.LastLoginTimestamp = lastLoginTimestamp;
            Character.TotalLogins++;
            CharacterChangesDetected = true;

            Sequences.SetSequence(SequenceType.ObjectInstance, new UShortSequence((ushort)Character.TotalLogins));

            if (BarberActive)
            {
                BarberActive = false;
            }

            if (AllegianceNode != null)
            {
                AllegianceRank = (int)AllegianceNode.Rank;
            }
            else
            {
                AllegianceRank = null;
            }

            // SendSelf will trigger the entrance into portal space
            SendSelf();

            // Update or override certain properties sent to client.
            SendPropertyUpdatesAndOverrides();

            // Init the client with the chat channel ID's, and then notify the player that they've choined the associated channels.
            UpdateChatChannels();

            var general  = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "General");
            var trade    = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Trade");
            var lfg      = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "LFG");
            var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Roleplay");

            Session.Network.EnqueueSend(general, trade, lfg, roleplay);

            // check if vassals earned XP while offline
            HandleAllegianceOnLogin();
            HandleHouseOnLogin();

            // retail appeared to send the squelch list very early,
            // even before the CreatePlayer, but doing it here
            if (SquelchManager.HasSquelches)
            {
                SquelchManager.SendSquelchDB();
            }

            AuditItemSpells();

            if (PlayerKillerStatus == PlayerKillerStatus.PKLite && !PropertyManager.GetBool("pkl_server").Item)
            {
                var actionChain = new ActionChain();
                actionChain.AddDelaySeconds(3.0f);
                actionChain.AddAction(this, () =>
                {
                    UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK, true);

                    Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNonPKAgain));
                });
                actionChain.EnqueueChain();
            }

            HandleDBUpdates();
        }