예제 #1
0
        /// <summary>
        /// Modifies a player's Base Attack Bonus (BAB) by a certain amount.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify.</param>
        /// <param name="player">The player to modify.</param>
        /// <param name="adjustBy">The amount to adjust by</param>
        public static void AdjustBAB(Player entity, uint player, int adjustBy)
        {
            entity.BAB += adjustBy;

            if (entity.BAB < 1)
            {
                entity.BAB = 1;
            }

            Creature.SetBaseAttackBonus(player, entity.BAB);
        }
예제 #2
0
        /// <summary>
        /// Retrieves the maximum STM on a player.
        /// CON modifier will be checked. Each modifier grants +2 to max STM.
        /// </summary>
        /// <param name="player">The player object</param>
        /// <param name="dbPlayer">The player entity. If this is not set, a call to the DB will be made.</param>
        /// <returns>The max amount of STM</returns>
        public static int GetMaxStamina(uint player, Player dbPlayer = null)
        {
            if (dbPlayer == null)
            {
                var playerId = GetObjectUUID(player);
                dbPlayer = DB.Get <Player>(playerId);
            }

            var baseStamina = dbPlayer.MaxStamina;
            var conModifier = GetAbilityModifier(AbilityType.Constitution, player);

            return(baseStamina + (conModifier * 2));
        }
예제 #3
0
        /// <summary>
        /// Reduces an entity's MP by a specified amount.
        /// If player would fall below 0 MP, they will be reduced to 0 instead.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify</param>
        /// <param name="reduceBy">The amount of MP to reduce by.</param>
        public static void ReduceMP(Player entity, int reduceBy)
        {
            if (reduceBy <= 0)
            {
                return;
            }

            entity.MP -= reduceBy;

            if (entity.MP < 0)
            {
                entity.MP = 0;
            }
        }
예제 #4
0
        /// <summary>
        /// Retrieves the maximum MP on a player.
        /// INT and WIS modifiers will be checked. The higher one is used for calculations.
        /// Each modifier grants +2 to max MP.
        /// </summary>
        /// <param name="player">The player object</param>
        /// <param name="dbPlayer">The player entity. If this is not set, a call to the DB will be made.</param>
        /// <returns>The max amount of MP</returns>
        public static int GetMaxMP(uint player, Player dbPlayer = null)
        {
            if (dbPlayer == null)
            {
                var playerId = GetObjectUUID(player);
                dbPlayer = DB.Get <Player>(playerId);
            }
            var baseMP      = dbPlayer.MaxMP;
            var intModifier = GetAbilityModifier(AbilityType.Intelligence, player);
            var wisModifier = GetAbilityModifier(AbilityType.Wisdom, player);
            var modifier    = intModifier > wisModifier ? intModifier : wisModifier;

            return(baseMP + (modifier * 2));
        }
예제 #5
0
        /// <summary>
        /// Reduces an entity's Stamina by a specified amount.
        /// If player would fall below 0 stamina, they will be reduced to 0 instead.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify</param>
        /// <param name="reduceBy">The amount of Stamina to reduce by.</param>
        public static void ReduceStamina(Player entity, int reduceBy)
        {
            if (reduceBy <= 0)
            {
                return;
            }

            entity.Stamina -= reduceBy;

            if (entity.Stamina < 0)
            {
                entity.Stamina = 0;
            }
        }
예제 #6
0
        /// <summary>
        /// Increases or decreases a player's HP by a specified amount.
        /// There is a cap of 255 HP per NWN level. Players are auto-leveled to 5 by default, so this
        /// gives 255 * 5 = 1275 HP maximum. If the player's HP would go over this amount, it will be set to 1275.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify</param>
        /// <param name="player">The player to adjust</param>
        /// <param name="adjustBy">The amount to adjust by.</param>
        public static void AdjustMaxHP(Player entity, uint player, int adjustBy)
        {
            const int MaxHPPerLevel = 255;

            entity.MaxHP += adjustBy;
            var nwnLevelCount = GetLevelByPosition(1, player) +
                                GetLevelByPosition(2, player) +
                                GetLevelByPosition(3, player);

            var hpToApply = entity.MaxHP;

            // All levels must have at least 1 HP, so apply those right now.
            for (var nwnLevel = 1; nwnLevel <= nwnLevelCount; nwnLevel++)
            {
                hpToApply--;
                Creature.SetMaxHitPointsByLevel(player, nwnLevel, 1);
            }

            // It's possible for the MaxHP value to be a negative if builders misuse item properties, etc.
            // Players cannot go under 'nwnLevel' HP, so we apply that first. If our HP to apply is zero, we don't want to
            // do any more logic with HP application.
            if (hpToApply > 0)
            {
                // Apply the remaining HP.
                for (var nwnLevel = 1; nwnLevel <= nwnLevelCount; nwnLevel++)
                {
                    if (hpToApply > MaxHPPerLevel) // Levels can only contain a max of 255 HP
                    {
                        Creature.SetMaxHitPointsByLevel(player, nwnLevel, 255);
                        hpToApply -= 254;
                    }
                    else // Remaining value gets set to the level. (<255 hp)
                    {
                        Creature.SetMaxHitPointsByLevel(player, nwnLevel, hpToApply + 1);
                        break;
                    }
                }
            }

            // If player's current HP is higher than max, deal the difference in damage to bring them back down to their new maximum.
            var currentHP = GetCurrentHitPoints(player);
            var maxHP     = GetMaxHitPoints(player);

            if (currentHP > maxHP)
            {
                var damage = EffectDamage(currentHP - maxHP);
                ApplyEffectToObject(DurationType.Instant, damage, player);
            }
        }
예제 #7
0
        /// <summary>
        /// Restores an entity's MP by a specified amount.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="player">The player to modify.</param>
        /// <param name="entity">The entity to modify.</param>
        /// <param name="amount">The amount of MP to restore.</param>
        public static void RestoreMP(uint player, Player entity, int amount)
        {
            if (amount <= 0)
            {
                return;
            }

            var maxMP = GetMaxMP(player);

            entity.MP += amount;

            if (entity.MP > maxMP)
            {
                entity.MP = maxMP;
            }
        }
예제 #8
0
        /// <summary>
        /// Restores an entity's Stamina by a specified amount.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="player">The player to modify.</param>
        /// <param name="entity">The entity to modify.</param>
        /// <param name="amount">The amount of Stamina to restore.</param>
        public static void RestoreStamina(uint player, Player entity, int amount)
        {
            if (amount <= 0)
            {
                return;
            }

            var maxStamina = GetMaxStamina(player, entity);

            entity.Stamina += amount;

            if (entity.Stamina > maxStamina)
            {
                entity.Stamina = maxStamina;
            }
        }
예제 #9
0
        /// <summary>
        /// Modifies a player's maximum STM by a certain amount.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify</param>
        /// <param name="adjustBy">The amount to adjust by</param>
        public static void AdjustMaxSTM(Player entity, int adjustBy)
        {
            // Note: It's possible for Max STM to drop to a negative number. This is expected to ensure calculations stay in sync.
            // If there are any visual indicators (GUI elements for example) be sure to account for this scenario.
            entity.MaxStamina += adjustBy;

            if (entity.Stamina > entity.MaxStamina)
            {
                entity.Stamina = entity.MaxStamina;
            }

            // Current STM, however, should never drop below zero.
            if (entity.Stamina < 0)
            {
                entity.Stamina = 0;
            }
        }
예제 #10
0
        /// <summary>
        /// Modifies the player's unallocated SP, version, max HP, and other assorted stats.
        /// </summary>
        /// <param name="player">The player object</param>
        /// <param name="dbPlayer">The player entity.</param>
        private static void AdjustStats(uint player, Player dbPlayer)
        {
            dbPlayer.UnallocatedSP = 10;
            dbPlayer.Version       = 1;
            dbPlayer.Name          = GetName(player);
            Stat.AdjustMaxHP(dbPlayer, player, 10);
            Stat.AdjustMaxSTM(dbPlayer, 10);
            Stat.AdjustBAB(dbPlayer, player, 1);
            dbPlayer.MP      = Stat.GetMaxMP(player, dbPlayer);
            dbPlayer.Stamina = Stat.GetMaxStamina(player, dbPlayer);

            dbPlayer.BaseStats[AbilityType.Strength]     = Creature.GetRawAbilityScore(player, AbilityType.Strength);
            dbPlayer.BaseStats[AbilityType.Dexterity]    = Creature.GetRawAbilityScore(player, AbilityType.Dexterity);
            dbPlayer.BaseStats[AbilityType.Constitution] = Creature.GetRawAbilityScore(player, AbilityType.Constitution);
            dbPlayer.BaseStats[AbilityType.Wisdom]       = Creature.GetRawAbilityScore(player, AbilityType.Wisdom);
            dbPlayer.BaseStats[AbilityType.Intelligence] = Creature.GetRawAbilityScore(player, AbilityType.Intelligence);
            dbPlayer.BaseStats[AbilityType.Charisma]     = Creature.GetRawAbilityScore(player, AbilityType.Charisma);
        }
예제 #11
0
        /// <summary>
        /// Modifies a player's attribute by a certain amount.
        /// This method will not persist the changes so be sure you call DB.Set after calling this.
        /// </summary>
        /// <param name="entity">The entity to modify.</param>
        /// <param name="player">The player to modify.</param>
        /// <param name="ability">The ability to modify.</param>
        /// <param name="adjustBy">The amount to adjust by.</param>
        public static void AdjustAttribute(Player entity, uint player, AbilityType ability, float adjustBy)
        {
            if (!GetIsPC(player) || GetIsDM(player))
            {
                return;
            }
            if (ability == AbilityType.Invalid)
            {
                return;
            }
            if (adjustBy == 0f)
            {
                return;
            }

            entity.AdjustedStats[ability] += adjustBy;

            var totalStat = (int)(entity.BaseStats[ability] + entity.AdjustedStats[ability]);

            Creature.SetRawAbilityScore(player, ability, totalStat);
        }
예제 #12
0
        /// <summary>
        /// Retrieves a player's effective perk level.
        /// </summary>
        /// <param name="player">The player object</param>
        /// <param name="dbPlayer">The database entity</param>
        /// <param name="perkType">The type of perk</param>
        /// <returns>The effective level for a given player and perk</returns>
        private static int GetPlayerPerkLevel(uint player, Player dbPlayer, PerkType perkType)
        {
            var playerPerkLevel = dbPlayer.Perks.ContainsKey(perkType) ? dbPlayer.Perks[perkType] : 0;

            // Early exit if player doesn't have the perk at all.
            if (playerPerkLevel <= 0)
            {
                return(0);
            }

            // Retrieve perk levels at or below player's perk level and then order them from highest level to lowest.
            var perk       = GetPerkDetails(perkType);
            var perkLevels = perk.PerkLevels
                             .Where(x => x.Key <= playerPerkLevel)
                             .OrderByDescending(o => o.Key);

            // Iterate over each perk level and check requirements.
            // The first perk level the player passes requirements on is the player's effective level.
            foreach (var(level, detail) in perkLevels)
            {
                // No requirements set for this perk level. Return the level.
                if (detail.Requirements.Count <= 0)
                {
                    return(level);
                }

                foreach (var req in detail.Requirements)
                {
                    if (string.IsNullOrWhiteSpace(req.CheckRequirements(player)))
                    {
                        return(level);
                    }
                }
            }

            // Otherwise none of the perk level requirements passed. Player's effective level is zero.
            return(0);
        }
예제 #13
0
        /// <summary>
        /// This will manually recalculate all stats.
        /// This should be used sparingly because it can be a heavy call.
        /// This method will not persist the changes so be sure your call DB.Set after calling this.
        /// </summary>
        public static void RecalculateAllStats(uint player, Player dbPlayer)
        {
            // Reset all adjusted stat values.
            foreach (var adjustedStat in dbPlayer.AdjustedStats)
            {
                dbPlayer.AdjustedStats[adjustedStat.Key] = 0.0f;
            }

            // Apply adjusted stat increases based on the player's skill ranks.
            // We use a pre-filtered list of skills for this to cut down on the number of iterations.
            var skills = Skill.GetAllSkillsWhichIncreaseStats();

            foreach (var(type, value) in skills)
            {
                var primaryIncrease   = dbPlayer.Skills[type].Rank * Skill.PrimaryStatIncrease;
                var secondaryIncrease = dbPlayer.Skills[type].Rank * Skill.SecondaryStatIncrease;

                if (value.PrimaryStat == AbilityType.Strength)
                {
                    dbPlayer.AdjustedStats[AbilityType.Strength] += primaryIncrease;
                }
                if (value.PrimaryStat == AbilityType.Dexterity)
                {
                    dbPlayer.AdjustedStats[AbilityType.Dexterity] += primaryIncrease;
                }
                if (value.PrimaryStat == AbilityType.Constitution)
                {
                    dbPlayer.AdjustedStats[AbilityType.Constitution] += primaryIncrease;
                }
                if (value.PrimaryStat == AbilityType.Wisdom)
                {
                    dbPlayer.AdjustedStats[AbilityType.Wisdom] += primaryIncrease;
                }
                if (value.PrimaryStat == AbilityType.Intelligence)
                {
                    dbPlayer.AdjustedStats[AbilityType.Intelligence] += primaryIncrease;
                }
                if (value.PrimaryStat == AbilityType.Charisma)
                {
                    dbPlayer.AdjustedStats[AbilityType.Charisma] += primaryIncrease;
                }

                if (value.SecondaryStat == AbilityType.Strength)
                {
                    dbPlayer.AdjustedStats[AbilityType.Strength] += secondaryIncrease;
                }
                if (value.SecondaryStat == AbilityType.Dexterity)
                {
                    dbPlayer.AdjustedStats[AbilityType.Dexterity] += secondaryIncrease;
                }
                if (value.SecondaryStat == AbilityType.Constitution)
                {
                    dbPlayer.AdjustedStats[AbilityType.Constitution] += secondaryIncrease;
                }
                if (value.SecondaryStat == AbilityType.Wisdom)
                {
                    dbPlayer.AdjustedStats[AbilityType.Wisdom] += secondaryIncrease;
                }
                if (value.SecondaryStat == AbilityType.Intelligence)
                {
                    dbPlayer.AdjustedStats[AbilityType.Intelligence] += secondaryIncrease;
                }
                if (value.SecondaryStat == AbilityType.Charisma)
                {
                    dbPlayer.AdjustedStats[AbilityType.Charisma] += secondaryIncrease;
                }
            }

            // We now have all of the correct values. Apply them to the player object.
            foreach (var(ability, amount) in dbPlayer.AdjustedStats)
            {
                var totalStat = (int)(dbPlayer.BaseStats[ability] + amount);
                Creature.SetRawAbilityScore(player, ability, totalStat);
            }
        }
예제 #14
0
        /// <summary>
        /// Handles applying skill XP decay when a player has reached the skill cap.
        /// If decay cannot be applied, false will be returned.
        /// If decay was unnecessary or succeeded, true will be returned.
        /// </summary>
        /// <param name="dbPlayer">The player entity to apply skill decay to</param>
        /// <param name="player">The player object.</param>
        /// <param name="skill">The skill which is receiving XP. This skill will be excluded from decay.</param>
        /// <param name="xp">The amount of XP being applied.</param>
        /// <returns>true if successful or unnecessary, false otherwise</returns>
        private static bool ApplyDecay(Player dbPlayer, uint player, SkillType skill, int xp)
        {
            if (dbPlayer.TotalSPAcquired < SkillCap)
            {
                return(true);
            }

            var playerId = GetObjectUUID(player);
            var skillsPossibleToDecay = dbPlayer.Skills
                                        .Where(x =>
            {
                var detail = GetSkillDetails(x.Key);

                return(!x.Value.IsLocked &&
                       detail.ContributesToSkillCap &&
                       x.Key != skill &&
                       (x.Value.XP > 0 || x.Value.Rank > 0));
            }).ToList();

            // If no skills can be decayed, return false.
            if (!skillsPossibleToDecay.Any())
            {
                return(false);
            }

            // Get the total XP acquired, then add up any remaining XP for a partial level
            int totalAvailableXPToDecay = skillsPossibleToDecay.Sum(x =>
            {
                var totalXP = GetTotalXP(x.Value.Rank);
                xp         += x.Value.XP;

                return(totalXP);
            });

            // There's not enough XP to decay for this gain. Exit early.
            if (totalAvailableXPToDecay < xp)
            {
                return(false);
            }

            while (xp > 0)
            {
                var index        = Random.Next(skillsPossibleToDecay.Count);
                var decaySkill   = skillsPossibleToDecay[index];
                int totalDecayXP = GetTotalXP(decaySkill.Value.Rank) + decaySkill.Value.XP;

                if (totalDecayXP >= xp)
                {
                    totalDecayXP -= xp;
                    xp            = 0;
                }
                else if (totalDecayXP < xp)
                {
                    totalDecayXP = 0;
                    xp          -= totalDecayXP;
                }

                // If skill drops to 0 total XP, remove it from the possible list of skills
                if (totalDecayXP <= 0)
                {
                    skillsPossibleToDecay.Remove(decaySkill);
                    decaySkill.Value.XP   = 0;
                    decaySkill.Value.Rank = 0;
                }
                // Otherwise calculate what rank and XP value the skill should now be.
                else
                {
                    // Get the XP amounts required per level, in ascending order, so we can see how many levels we're now meant to have.
                    var reqs = _skillTotalXP.Where(x => x.Key <= decaySkill.Value.Rank).OrderBy(o => o.Key);

                    // The first entry in the database is for rank 0, and if passed, will raise us to 1.  So start our count at 0.
                    int newDecaySkillRank = 0;
                    foreach (var req in reqs)
                    {
                        if (totalDecayXP >= req.Value)
                        {
                            totalDecayXP -= req.Value;
                            newDecaySkillRank++;
                        }
                        else if (totalDecayXP < req.Value)
                        {
                            break;
                        }
                    }

                    decaySkill.Value.Rank = newDecaySkillRank;
                    decaySkill.Value.XP   = totalDecayXP;
                }

                dbPlayer.Skills[decaySkill.Key].Rank = decaySkill.Value.Rank;
                dbPlayer.Skills[decaySkill.Key].XP   = decaySkill.Value.XP;
            }

            // Perform a full recalc on the player's stats
            Stat.RecalculateAllStats(player, dbPlayer);

            // Save all changes made.
            DB.Set(playerId, dbPlayer);
            return(true);
        }