示例#1
0
        public void Player_Tick(double currentUnixTime)
        {
            actionQueue.RunActions();

            if (nextAgeUpdateTime <= currentUnixTime)
            {
                nextAgeUpdateTime = currentUnixTime + ageUpdateInterval;

                if (initialAgeTime == DateTime.MinValue)
                {
                    initialAge     = Age ?? 1;
                    initialAgeTime = DateTime.UtcNow;
                }

                Age = initialAge + (int)(DateTime.UtcNow - initialAgeTime).TotalSeconds;

                // In retail, this is sent every 7 seconds. If you adjust ageUpdateInterval from 7, you'll need to re-add logic to send this every 7s (if you want to match retail)
                Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.Age, Age ?? 1));
            }

            if (FellowVitalUpdate && Fellowship != null)
            {
                Fellowship.OnVitalUpdate(this);
                FellowVitalUpdate = false;
            }
        }
示例#2
0
        /// <summary>
        /// Directly grants XP to the player, without the XP modifier
        /// </summary>
        /// <param name="amount">The amount of XP to grant to the player</param>
        /// <param name="xpType">The source of the XP being granted</param>
        /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param>
        public void GrantXP(long amount, XpType xpType, ShareType shareType = ShareType.All)
        {
            if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship))
            {
                // this will divy up the XP, and re-call this function
                // with ShareType.Fellowship removed
                Fellowship.SplitXp((ulong)amount, xpType, shareType, this);
                return;
            }

            UpdateXpAndLevel(amount, xpType);

            // for passing XP up the allegiance chain,
            // this function is only called at the very beginning, to start the process.
            if (shareType.HasFlag(ShareType.Allegiance))
            {
                UpdateXpAllegiance(amount);
            }

            // only certain types of XP are granted to items
            if (xpType == XpType.Kill || xpType == XpType.Quest)
            {
                GrantItemXP(amount);
            }
        }
示例#3
0
        /// <summary>
        /// Directly grants XP to the player, without the XP modifier
        /// </summary>
        /// <param name="amount">The amount of XP to grant to the player</param>
        /// <param name="passup">The source of the XP being granted</param>
        /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param>
        public void GrantXP(long amount, XpType xpType, bool shareable = true)
        {
            if (shareable && Fellowship != null && Fellowship.ShareXP && Fellowship.ShareableMembers.ContainsKey(Guid.Full))
            {
                // this will divy up the XP, and re-call this function
                // with shareable = false
                Fellowship.SplitXp((ulong)amount, xpType, this);
                return;
            }

            UpdateXpAndLevel(amount);

            // for passing XP up the allegiance chain,
            // this function is only called at the very beginning, to start the process.
            if (xpType != XpType.Allegiance)
            {
                UpdateXpAllegiance(amount);
            }

            // only certain types of XP are granted to items
            if (xpType == XpType.Kill || xpType == XpType.Quest)
            {
                GrantItemXP(amount);
            }
        }
示例#4
0
        /// <summary>
        /// Directly raises the available XP by a specified amount
        /// For debugging purposes only. Normal XP progression should use EarnXP above.
        /// </summary>
        /// <param name="amount">The amount of XP to grant to the player</param>
        /// <param name="passup">If TRUE, additional XP is passed up the allegiance chain</param>
        public void GrantXP(long amount, bool sharable = true, bool fixedAmount = false, bool message = true)
        {
            if (sharable)
            {
                if (Fellowship != null && Fellowship.ShareXP && Fellowship.SharableMembers.Contains(this))
                {
                    Fellowship.SplitXp((ulong)amount, fixedAmount);
                }
                else
                {
                    UpdateXpAndLevel(amount);
                }

                UpdateXpAllegiance(amount);
            }
            else
            {
                UpdateXpAndLevel(amount);
            }

            if (message)
            {
                Session.Network.EnqueueSend(new GameMessageSystemChat($"{amount:N0} experience granted.", ChatMessageType.Advancement));
            }
        }
示例#5
0
        /// <summary>
        /// Applies damages to a player from a physical damage source
        /// </summary>
        public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false)
        {
            if (Invincible ?? false)
            {
                return;
            }

            // check lifestone protection
            if (UnderLifestoneProtection)
            {
                HandleLifestoneProtection();
                return;
            }

            var amount  = (uint)Math.Round(_amount);
            var percent = (float)amount / Health.MaxValue;

            // update health
            var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount);

            DamageHistory.Add(source, damageType, damageTaken);

            // update stamina
            UpdateVitalDelta(Stamina, -1);

            if (Fellowship != null)
            {
                Fellowship.OnVitalUpdate(this);
            }

            if (Health.Current == 0)
            {
                OnDeath(source, damageType, crit);
                Die();
                return;
            }

            var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart];

            // send network messages
            var creature = source as Creature;
            var hotspot  = source as Hotspot;

            if (creature != null)
            {
                var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None);
                Session.Network.EnqueueSend(text);

                var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f);
                var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this)));
                EnqueueBroadcast(hitSound, splatter);
            }

            if (percent >= 0.1f)
            {
                EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f));
            }
        }
示例#6
0
        /// <summary>
        /// Simplified player take damage function, only called for DoTs currently
        /// </summary>
        public override void TakeDamageOverTime(float _amount, DamageType damageType)
        {
            if (Invincible ?? false || IsDead)
            {
                return;
            }

            // check lifestone protection
            if (UnderLifestoneProtection)
            {
                HandleLifestoneProtection();
                return;
            }

            var amount  = (uint)Math.Round(_amount);
            var percent = (float)amount / Health.MaxValue;

            // update health
            var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount);

            // update stamina
            UpdateVitalDelta(Stamina, -1);

            if (Fellowship != null)
            {
                Fellowship.OnVitalUpdate(this);
            }

            // send damage text message
            if (PropertyManager.GetBool("show_dot_messages").Item)
            {
                var nether = damageType == DamageType.Nether ? "nether " : "";
                var text   = new GameMessageSystemChat($"You receive {amount} points of periodic {nether}damage.", ChatMessageType.Combat);
                Session.Network.EnqueueSend(text);
            }

            // splatter effects
            //var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this)));  // not sent in retail, but great visual indicator?
            var splatter = new GameMessageScript(Guid, damageType == DamageType.Nether ? ACE.Entity.Enum.PlayScript.HealthDownVoid : ACE.Entity.Enum.PlayScript.DirtyFightingDamageOverTime);

            EnqueueBroadcast(splatter);

            if (Health.Current <= 0)
            {
                // since damage over time is possibly combined from multiple sources,
                // sending a message to the last damager here could be tricky..
                OnDeath(null, damageType, false);
                Die();

                return;
            }

            if (percent >= 0.1f)
            {
                EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f));
            }
        }
示例#7
0
        public void FellowshipQuit(bool disband)
        {
            if (Fellowship == null)
            {
                return;
            }

            Fellowship.QuitFellowship(this, disband);

            Fellowship = null;
        }
示例#8
0
 /// <summary>
 /// Directly grants luminance to the player, without any additional luminance modifiers
 /// </summary>
 public void GrantLuminance(long amount, XpType xpType, ShareType shareType = ShareType.All)
 {
     if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship))
     {
         // this will divy up the luminance, and re-call this function
         // with ShareType.Fellowship removed
         Fellowship.SplitLuminance((ulong)amount, xpType, shareType, this);
     }
     else
     {
         AddLuminance(amount, xpType);
     }
 }
示例#9
0
        // todo: Figure out if this is the best place to do this, and whether there are concurrency issues associated with it.
        public void FellowshipCreate(string fellowshipName, bool shareXP)
        {
            // An Olthoi player cannot create a fellowship
            if (IsOlthoiPlayer)
            {
                Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.OlthoiCannotJoinFellowship));
                return;
            }

            Fellowship = new Fellowship(this, fellowshipName, shareXP);
            Session.Network.EnqueueSend(new GameEventFellowshipFullUpdate(Session));
            Session.Network.EnqueueSend(new GameEventFellowshipFellowUpdateDone(Session));
        }
        public GameMessageFellowshipFullUpdate(Session session)
            : base(GameMessageOpcode.GameEvent, GameMessageGroup.Group09)
        {
            // This is a naive, bare-bones implementation of 0x02BE, FullFellowshipUpdate.
            // 0x02BE is fairly complicated, so the following code is at least valuable as an example of a valid server response.

            // todo: The current implementation has race conditions,
            // and there are questions that must be answered before it can be fixed.
            // We need to figure out who "owns" the fellowship data.
            // Does everyone get a turn to read from and modify the fellowship data, and if so, how is this managed?

            // Currently, creating and leaving a fellowship is supported.
            // Any other fellowship function is not yet supported.

            Fellowship fellowship = session.Player.Fellowship;

            Writer.Write(session.Player.Guid.Full);
            Writer.Write(session.GameEventSequence++);
            Writer.Write((uint)GameEvent.GameEventType.FellowshipFullUpdate);

            // the current number of fellowship members
            Writer.Write((UInt16)fellowship.FellowshipMembers.Count);

            // todo: figure out what these two bytes are for
            Writer.Write((byte)0x10);
            Writer.Write((byte)0x00);

            // --- FellowInfo ---

            ActionChain fellowChain = new ActionChain();

            foreach (Player fellow in fellowship.FellowshipMembers)
            {
                // Write data associated with each fellowship member
                WriteFellow(fellow);
            }

            Writer.WriteString16L(fellowship.FellowshipName);
            Writer.Write(fellowship.FellowshipLeaderGuid);
            Writer.Write(Convert.ToUInt32(fellowship.ShareXP));
            Writer.Write(Convert.ToUInt32(fellowship.Open));

            // todo: fellows departed?
            Writer.Write(0u);
            Writer.Write(0u);

            // End of meaningful data?
            Writer.Write((uint)0x00200000);
            Writer.Write((uint)0x00200000);
        }
示例#11
0
 public List <Player> GetFellowshipTargets()
 {
     if (Fellowship != null)
     {
         return(Fellowship.GetFellowshipMembers().Values.ToList());
     }
     else
     {
         return new List <Player>()
                {
                    this
                }
     };
 }
示例#12
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false)
        {
            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            lastDamager.EmoteManager.OnKill(this);

            var playerMsg    = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            var msgYourDeath = new GameEventYourDeath(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg    = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast);

            var excludePlayers = new List <Player>();

            if (lastDamager is Player lastDamagerPlayer)
            {
                excludePlayers.Add(lastDamagerPlayer);
            }

            var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg);

            excludePlayers.AddRange(nearbyPlayers);
            excludePlayers.Add(this);   // exclude self

            if (Fellowship != null)
            {
                Fellowship.OnDeath(this);
            }

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg);
            }

            return(deathMessage);
        }
示例#13
0
        public void Player_Tick(double currentUnixTime)
        {
            actionQueue.RunActions();

            if (nextAgeUpdateTime <= currentUnixTime)
            {
                nextAgeUpdateTime = currentUnixTime + ageUpdateInterval;

                if (initialAgeTime == DateTime.MinValue)
                {
                    initialAge     = Age ?? 1;
                    initialAgeTime = DateTime.UtcNow;
                }

                Age = initialAge + (int)(DateTime.UtcNow - initialAgeTime).TotalSeconds;

                // In retail, this is sent every 7 seconds. If you adjust ageUpdateInterval from 7, you'll need to re-add logic to send this every 7s (if you want to match retail)
                Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.Age, Age ?? 1));
            }

            if (FellowVitalUpdate && Fellowship != null)
            {
                Fellowship.OnVitalUpdate(this);
                FellowVitalUpdate = false;
            }

            if (House != null && PropertyManager.GetBool("house_rent_enabled").Item)
            {
                if (houseRentWarnTimestamp > 0 && currentUnixTime > houseRentWarnTimestamp)
                {
                    HouseManager.GetHouse(House.Guid.Full, (house) =>
                    {
                        if (house != null && house.HouseStatus == HouseStatus.Active && !house.SlumLord.IsRentPaid())
                        {
                            Session.Network.EnqueueSend(new GameMessageSystemChat($"Warning!  You have not paid your maintenance costs for the last {(house.IsApartment ? "90" : "30")} day maintenance period.  Please pay these costs by this deadline or you will lose your house, and all your items within it.", ChatMessageType.Broadcast));
                        }
                    });

                    houseRentWarnTimestamp = Time.GetFutureUnixTime(houseRentWarnInterval);
                }
                else if (houseRentWarnTimestamp == 0)
                {
                    houseRentWarnTimestamp = Time.GetFutureUnixTime(houseRentWarnInterval);
                }
            }
        }
示例#14
0
 public void EarnXP(long amount, bool sharable = true, bool fixedAmount = false)
 {
     if (sharable)
     {
         if (Fellowship != null && Fellowship.ShareXP)
         {
             Fellowship.SplitXp((ulong)amount, fixedAmount);
         }
         else
         {
             UpdateXpAndLevel(amount);
         }
     }
     else
     {
         UpdateXpAndLevel(amount);
     }
 }
示例#15
0
        public static void HandleFellowship(Session session, NetworkObject networkObject)
        {
            FellowshipAction action         = (FellowshipAction)NetworkObjectField.ReadIntField(networkObject.GetField(0));
            FellowshipObject fellowshipInfo = new FellowshipObject();

            fellowshipInfo.FromNetworkObject(networkObject.GetField(1).ReadObject());

            switch (action)
            {
            case FellowshipAction.Join:
            {
                Fellowship fellowship = FellowshipManager.GetFellowship(fellowshipInfo);
                fellowship.AddMember(session);
                break;
            }

            case FellowshipAction.Leave:
            {
                Fellowship fellowship = FellowshipManager.GetFellowship(fellowshipInfo);
                fellowship?.RemoveMember(session);
                break;
            }
            }
        }
示例#16
0
        /// <summary>
        /// Directly grants XP to the player, without the XP modifier
        /// </summary>
        /// <param name="amount">The amount of XP to grant to the player</param>
        /// <param name="xpType">The source of the XP being granted</param>
        /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param>
        public void GrantXP(long amount, XpType xpType, ShareType shareType = ShareType.All)
        {
            if (IsOlthoiPlayer)
            {
                if (HasVitae)
                {
                    UpdateXpVitae(amount);
                }

                return;
            }

            if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship))
            {
                // this will divy up the XP, and re-call this function
                // with ShareType.Fellowship removed
                Fellowship.SplitXp((ulong)amount, xpType, shareType, this);
                return;
            }

            // Make sure UpdateXpAndLevel is done on this players thread
            EnqueueAction(new ActionEventDelegate(() => UpdateXpAndLevel(amount, xpType)));

            // for passing XP up the allegiance chain,
            // this function is only called at the very beginning, to start the process.
            if (shareType.HasFlag(ShareType.Allegiance))
            {
                UpdateXpAllegiance(amount);
            }

            // only certain types of XP are granted to items
            if (xpType == XpType.Kill || xpType == XpType.Quest)
            {
                GrantItemXP(amount);
            }
        }
示例#17
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false)
        {
            if (DamageHistory.TopDamager is Player pkPlayer)
            {
                if (IsPKDeath(pkPlayer))
                {
                    pkPlayer.PkTimestamp = Time.GetUnixTime();
                    pkPlayer.PlayerKillsPk++;

                    var globalPKDe = $"{lastDamager.Name} has defeated {Name}!";

                    if ((Location.Cell & 0xFFFF) < 0x100)
                    {
                        globalPKDe += $" The kill occured at {Location.GetMapCoordStr()}";
                    }

                    globalPKDe += "\n[PKDe]";

                    PlayerManager.BroadcastToAll(new GameMessageSystemChat(globalPKDe, ChatMessageType.Broadcast));
                }
                else if (IsPKLiteDeath(pkPlayer))
                {
                    pkPlayer.PlayerKillsPkl++;
                }
            }

            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            if (lastDamager != null)
            {
                lastDamager.EmoteManager.OnKill(this);
            }

            var playerMsg = "";

            if (lastDamager != null)
            {
                playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            }
            else
            {
                playerMsg = deathMessage.Victim;
            }

            var msgYourDeath = new GameEventYourDeath(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg = "";

            if (lastDamager != null)
            {
                nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            }
            else
            {
                nearbyMsg = deathMessage.Broadcast;
            }

            var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast);

            log.Debug("[CORPSE] " + nearbyMsg);

            var excludePlayers = new List <Player>();

            if (lastDamager is Player lastDamagerPlayer)
            {
                excludePlayers.Add(lastDamagerPlayer);
            }

            var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg);

            excludePlayers.AddRange(nearbyPlayers);
            excludePlayers.Add(this); // exclude self

            if (Fellowship != null)
            {
                Fellowship.OnDeath(this);
            }

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg);
            }

            return(deathMessage);
        }
示例#18
0
 /// <summary>
 /// Constructs a new QuestManager for a Fellowship
 /// </summary>
 public QuestManager(Fellowship fellowship)
 {
     Fellowship = fellowship;
 }
示例#19
0
 public void FellowshipQuit(bool disband)
 {
     Fellowship.QuitFellowship(this, disband);
     Fellowship = null;
 }
示例#20
0
        /// <summary>
        /// Determines if the player has advanced a level
        /// </summary>
        private void CheckForLevelup()
        {
            var xpTable = DatManager.PortalDat.XpTable;

            var maxLevel = GetMaxLevel();

            if (Level >= maxLevel)
            {
                return;
            }

            var  startingLevel = Level;
            bool creditEarned  = false;

            // increases until the correct level is found
            while ((ulong)(TotalExperience ?? 0) >= xpTable.CharacterLevelXPList[(Level ?? 0) + 1])
            {
                Level++;

                // increase the skill credits if the chart allows this level to grant a credit
                if (xpTable.CharacterLevelSkillCreditList[Level ?? 0] > 0)
                {
                    AvailableSkillCredits += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0];
                    TotalSkillCredits     += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0];
                    creditEarned           = true;
                }

                // break if we reach max
                if (Level == maxLevel)
                {
                    PlayParticleEffect(PlayScript.WeddingBliss, Guid);
                    break;
                }
            }

            if (Level > startingLevel)
            {
                var message = (Level == maxLevel) ? $"You have reached the maximum level of {Level}!" : $"You are now level {Level}!";

                message += (AvailableSkillCredits > 0) ? $"\nYou have {AvailableExperience:#,###0} experience points and {AvailableSkillCredits} skill credits available to raise skills and attributes." : $"\nYou have {AvailableExperience:#,###0} experience points available to raise skills and attributes.";

                var levelUp        = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.Level, Level ?? 1);
                var currentCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0);

                if (Level != maxLevel && !creditEarned)
                {
                    var nextLevelWithCredits = 0;

                    for (int i = (Level ?? 0) + 1; i <= maxLevel; i++)
                    {
                        if (xpTable.CharacterLevelSkillCreditList[i] > 0)
                        {
                            nextLevelWithCredits = i;
                            break;
                        }
                    }
                    message += $"\nYou will earn another skill credit at level {nextLevelWithCredits}.";
                }

                if (Fellowship != null)
                {
                    Fellowship.OnFellowLevelUp(this);
                }

                if (AllegianceNode != null)
                {
                    AllegianceNode.OnLevelUp();
                }

                Session.Network.EnqueueSend(levelUp);

                SetMaxVitals();

                // play level up effect
                PlayParticleEffect(PlayScript.LevelUp, Guid);

                Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Advancement), currentCredits);
            }
        }
示例#21
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(DamageHistoryInfo lastDamager, DamageType damageType, bool criticalHit = false)
        {
            var topDamager = DamageHistory.GetTopDamager(false);

            HandlePKDeathBroadcast(lastDamager, topDamager);

            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            var lastDamagerObj = lastDamager?.TryGetAttacker();

            if (lastDamagerObj != null)
            {
                lastDamagerObj.EmoteManager.OnKill(this);
            }

            var playerMsg = "";

            if (lastDamager != null)
            {
                playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            }
            else
            {
                playerMsg = deathMessage.Victim;
            }

            var msgYourDeath = new GameEventVictimNotification(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg = "";

            if (lastDamager != null)
            {
                nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            }
            else
            {
                nearbyMsg = deathMessage.Broadcast;
            }

            var broadcastMsg = new GameMessagePlayerKilled(nearbyMsg, Guid, lastDamager?.Guid ?? ObjectGuid.Invalid);

            log.Debug("[CORPSE] " + nearbyMsg);

            var excludePlayers = new List <Player>();

            var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg);

            excludePlayers.AddRange(nearbyPlayers);

            if (Fellowship != null)
            {
                Fellowship.OnDeath(this);
            }

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (PropertyManager.GetBool("lifestone_broadcast_death").Item&& Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, Sanctuary, LocalBroadcastRangeSq, broadcastMsg);
            }

            return(deathMessage);
        }
示例#22
0
        /// <summary>
        /// Determines if the player has advanced a level
        /// </summary>
        private void CheckForLevelup()
        {
            var xpTable = DatManager.PortalDat.XpTable;

            var maxLevel = GetMaxLevel();

            if (Level >= maxLevel)
            {
                return;
            }

            var  startingLevel = Level;
            bool creditEarned  = false;

            // if level is not under 275(false) do normal stuff, otherwise if over 274(true) do custom function.

            // increases until the correct level is found
            while ((ulong)(TotalExperience ?? 0) >= ((Level < 275) ? xpTable.CharacterLevelXPList[(Level ?? 0) + 1] : (ulong)Catch275(false, startingLevel)))
            {
                Level++;

                // increase the skill credits if the chart allows this level to grant a credit

                // gotta limit this to levels 275 and under otherwise causes crash.
                if (Level <= 275)
                {
                    if (xpTable.CharacterLevelSkillCreditList[Level ?? 0] > 0)
                    {
                        AvailableSkillCredits += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0];
                        TotalSkillCredits     += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0];
                        creditEarned           = true;
                    }
                }

                // if a player is level 300 or higher and level is also divisible by 30 evenly give a skill credit.
                if (Level > 275)
                {
                    if (Level >= 300 && Level % 30 == 0)
                    {
                        AvailableSkillCredits += 1;
                        TotalSkillCredits     += 1;
                        creditEarned           = true;
                    }
                }

                // break if we reach max
                if (Level == maxLevel)
                {
                    PlayParticleEffect(PlayScript.WeddingBliss, Guid);
                    break;
                }
            }

            if (Level > startingLevel)
            {
                var message = (Level == maxLevel) ? $"You have reached the maximum level of {Level}!" : $"You are now level {Level}!";

                message += (AvailableSkillCredits > 0) ? $"\nYou have {AvailableExperience:#,###0} experience points and {AvailableSkillCredits} skill credits available to raise skills and attributes." : $"\nYou have {AvailableExperience:#,###0} experience points available to raise skills and attributes.";

                var levelUp        = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.Level, Level ?? 1);
                var currentCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0);

                // 275 and under code.
                if (Level != maxLevel && !creditEarned && Level <= 275)
                {
                    var nextLevelWithCredits = 0;

                    for (int i = (Level ?? 0) + 1; i <= maxLevel; i++)
                    {
                        if (xpTable.CharacterLevelSkillCreditList[i] > 0)
                        {
                            nextLevelWithCredits = i;
                            break;
                        }
                    }
                    message += $"\nYou will earn another skill credit at level {nextLevelWithCredits}.";
                }

                if (Fellowship != null)
                {
                    Fellowship.OnFellowLevelUp(this);
                }

                if (AllegianceNode != null)
                {
                    AllegianceNode.OnLevelUp();
                }

                Session.Network.EnqueueSend(levelUp);

                SetMaxVitals();

                // play level up effect
                PlayParticleEffect(PlayScript.LevelUp, Guid);

                Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Advancement), currentCredits);
            }
        }
示例#23
0
 // todo: Figure out if this is the best place to do this, and whether there are concurrency issues associated with it.
 public void FellowshipCreate(string fellowshipName, bool shareXP)
 {
     Fellowship = new Fellowship(this, fellowshipName, shareXP);
 }
示例#24
0
        /// <summary>
        /// Method used to perform the animation, sound, and vital update on consumption of food or potions
        /// </summary>
        /// <param name="consumableName">Name of the consumable</param>
        /// <param name="sound">Either Sound.Eat1 or Sound.Drink1</param>
        /// <param name="buffType">ConsumableBuffType.Spell,ConsumableBuffType.Health,ConsumableBuffType.Stamina,ConsumableBuffType.Mana</param>
        /// <param name="boostAmount">Amount the Vital is boosted by; can be null, if buffType = ConsumableBuffType.Spell</param>
        /// <param name="spellDID">Id of the spell cast by the consumable; can be null, if buffType != ConsumableBuffType.Spell</param>
        public void ApplyConsumable(string consumableName, Sound sound, ConsumableBuffType buffType, uint?boostAmount, uint?spellDID)
        {
            MotionCommand motionCommand;

            if (sound == Sound.Eat1)
            {
                motionCommand = MotionCommand.Eat;
            }
            else
            {
                motionCommand = MotionCommand.Drink;
            }

            // start the eat/drink motion
            var motion = new Motion(MotionStance.NonCombat, motionCommand);

            EnqueueBroadcastMotion(motion);

            var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId);
            var animTime    = motionTable.GetAnimationLength(CurrentMotionState.Stance, motionCommand, MotionCommand.Ready);

            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(animTime);

            actionChain.AddAction(this, () =>
            {
                GameMessageSystemChat buffMessage;

                if (buffType == ConsumableBuffType.Spell)
                {
                    bool result = false;

                    uint spellId = spellDID ?? 0;

                    if (spellId != 0)
                    {
                        result = CreateSingleSpell(spellId);
                    }

                    if (result)
                    {
                        var spell   = new Server.Entity.Spell(spellId);
                        buffMessage = new GameMessageSystemChat($"{consumableName} casts {spell.Name} on you.", ChatMessageType.Magic);
                    }
                    else
                    {
                        buffMessage = new GameMessageSystemChat($"Consuming {consumableName} attempted to apply a spell not yet fully implemented.", ChatMessageType.System);
                    }
                }
                else
                {
                    CreatureVital creatureVital;
                    string vitalName;

                    // Null check for safety
                    if (boostAmount == null)
                    {
                        boostAmount = 0;
                    }

                    switch (buffType)
                    {
                    case ConsumableBuffType.Health:
                        creatureVital = Health;
                        vitalName     = "Health";
                        break;

                    case ConsumableBuffType.Mana:
                        creatureVital = Mana;
                        vitalName     = "Mana";
                        break;

                    default:
                        creatureVital = Stamina;
                        vitalName     = "Stamina";
                        break;
                    }

                    var vitalChange = UpdateVitalDelta(creatureVital, (uint)boostAmount);
                    if (vitalName == "Health")
                    {
                        DamageHistory.OnHeal((uint)vitalChange);
                        if (Fellowship != null)
                        {
                            Fellowship.OnVitalUpdate(this);
                        }
                    }

                    buffMessage = new GameMessageSystemChat($"You regain {vitalChange} {vitalName}.", ChatMessageType.Craft);
                }

                var soundEvent = new GameMessageSound(Guid, sound, 1.0f);
                EnqueueBroadcast(soundEvent);

                Session.Network.EnqueueSend(buffMessage);

                // return to original stance
                var returnStance = new Motion(CurrentMotionState.Stance);
                EnqueueBroadcastMotion(returnStance);
            });

            actionChain.EnqueueChain();
        }