Пример #1
0
        public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData)
        {
            NWArea   area          = user.Area;
            NWPlayer player        = new NWPlayer(user);
            string   structureID   = area.GetLocalString("PC_BASE_STRUCTURE_ID");
            Guid     structureGuid = new Guid(structureID);

            PCBaseStructure pcbs      = DataService.PCBaseStructure.GetByID(structureGuid);
            BaseStructure   structure = DataService.BaseStructure.GetByID(pcbs.BaseStructureID);

            int repair    = SkillService.GetPCSkillRank(player, SkillType.Piloting);
            int maxRepair = (int)structure.Durability - (int)pcbs.Durability;

            if (maxRepair < repair)
            {
                repair = maxRepair;
            }

            // TODO - add perks to make repairing faster/better/shinier/etc.
            // Maybe a perk to allow repairing in space, with ground repairs only otherwise?

            NWCreature ship = area.GetLocalObject("CREATURE");

            if (ship.IsValid)
            {
                ship.SetLocalInt("HP", ship.GetLocalInt("HP") + repair);
                ship.FloatingText("Hull repaired: " + ship.GetLocalInt("HP") + "/" + ship.MaxHP);
            }

            pcbs.Durability += repair;
            DataService.SubmitDataChange(pcbs, DatabaseActionType.Update);

            player.SendMessage("Ship repaired for " + repair + " points. (Hull points: " + pcbs.Durability + "/" + structure.Durability + ")");
        }
Пример #2
0
        /// <summary>
        /// Looks at the creature's feats and if any of them are Perks, stores the highest
        /// level as a local variable on the creature. This variable is later used when the
        /// creature actually uses the feat.
        /// Also registers all of the available PerkFeats (highest tier) on the creature's Data.
        /// This data is also used in the AI to make decisions quicker.
        /// </summary>
        /// <param name="self">The creature whose perks we're registering.</param>
        private static void RegisterCreaturePerks(NWCreature self)
        {
            var perkFeatCache = new Dictionary <int, AIPerkDetails>();
            var featIDs       = new List <int>();

            // Add all feats the creature has to the list.
            int featCount = NWNXCreature.GetFeatCount(self);

            for (int x = 0; x <= featCount - 1; x++)
            {
                var featID = NWNXCreature.GetFeatByIndex(self, x);
                featIDs.Add((int)featID);
            }

            bool hasPerkFeat = false;
            // Retrieve perk feat information for only those feats registered as a perk.
            var perkFeats = DataService.PerkFeat.GetAllByIDs(featIDs);

            // Mark the highest perk level on the creature.
            foreach (var perkFeat in perkFeats)
            {
                int level = self.GetLocalInt("PERK_LEVEL_" + perkFeat.PerkID);
                if (level >= perkFeat.PerkLevelUnlocked)
                {
                    continue;
                }

                var perk = DataService.Perk.GetByID(perkFeat.PerkID);
                self.SetLocalInt("PERK_LEVEL_" + perkFeat.PerkID, perkFeat.PerkLevelUnlocked);
                perkFeatCache[perkFeat.PerkID] = new AIPerkDetails(perkFeat.FeatID, perk.ExecutionTypeID);
                hasPerkFeat = true;
            }

            // If a builder sets a perk feat but forgets to set the FP, do it automatically.
            if (hasPerkFeat && self.GetLocalInt("MAX_FP") <= 0)
            {
                int fp = 50;
                fp += (self.IntelligenceModifier + self.WisdomModifier + self.CharismaModifier) * 5;
                SetMaxFP(self, fp);
                SetCurrentFP(self, fp);
            }

            if (hasPerkFeat)
            {
                // Store a new dictionary containing PerkID and FeatID onto the creature's data.
                // This is later used in the AI processing for decision making.
                self.Data["PERK_FEATS"] = perkFeatCache;
            }
        }
Пример #3
0
        public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData)
        {
            Location effectLocation;
            NWPlayer player = (user.Object);

            // Targeted a location or self. Locate nearest resource.
            if (!target.IsValid || Equals(user, target))
            {
                ScanArea(user, targetLocation);
                _durability.RunItemDecay(player, item, _random.RandomFloat(0.02f, 0.08f));
                effectLocation = targetLocation;
            }
            else if (!string.IsNullOrWhiteSpace(target.GetLocalString("RESOURCE_RESREF")))
            {
                ScanResource(user, target);
                _durability.RunItemDecay(player, item, _random.RandomFloat(0.05f, 0.1f));
                effectLocation = target.Location;
            }
            else
            {
                user.FloatingText("You cannot scan that object with this type of scanner.");
                return;
            }

            _.ApplyEffectAtLocation(DURATION_TYPE_INSTANT, _.EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), effectLocation);

            if (user.IsPlayer && user.GetLocalInt(target.GlobalID.ToString()) == FALSE)
            {
                int scanningBonus = item.ScanningBonus;
                _skill.GiveSkillXP(player, SkillType.Harvesting, 150);
                user.SetLocalInt(target.GlobalID.ToString(), 1 + scanningBonus);
            }
        }
Пример #4
0
        /// <summary>
        /// Sets the max FP for a creature to a specific amount.
        /// </summary>
        /// <param name="creature">The creature whose max FP we're setting.</param>
        /// <param name="amount">The amount of max FP to assign to the creature.</param>
        public static void SetMaxFP(NWCreature creature, int amount)
        {
            if (amount < 0)
            {
                amount = 0;
            }

            if (creature.IsPlayer)
            {
                var player = DataService.Player.GetByID(creature.GlobalID);
                player.MaxFP = amount;
                if (player.CurrentFP > player.MaxFP)
                {
                    player.CurrentFP = player.MaxFP;
                }
                DataService.SubmitDataChange(player, DatabaseActionType.Update);
            }
            else
            {
                if (creature.GetLocalInt("CURRENT_FP") > amount)
                {
                    creature.SetLocalInt("CURRENT_FP", amount);
                }
                creature.SetLocalInt("MAX_FP", amount);
            }
        }
Пример #5
0
        private static void OnModuleApplyDamage()
        {
            DamageEventData data = NWNXDamage.GetDamageEventData();

            NWPlayer   player = data.Damager.Object;
            NWCreature target = NWGameObject.OBJECT_SELF;

            int attackType = target.GetLocalInt(AbilityService.LAST_ATTACK + player.GlobalID);

            LoggingService.Trace(TraceComponent.LastAttack, "Last attack from " + player.GlobalID + " on " + _.GetName(target) + " was type " + attackType);

            if (attackType == AbilityService.ATTACK_PHYSICAL)
            {
                // Only apply bonus damage from physical attacks.
                HandleWeaponStatBonuses();
                HandleEvadeOrDeflectBlasterFire();
                HandleApplySneakAttackDamage();
            }

            HandleDamageImmunity();
            HandleAbsorptionFieldEffect();
            HandleRecoveryBlast();
            HandleTranquilizerEffect();
            HandleStances();
        }
Пример #6
0
        /// <summary>
        /// Sets the current FP amount of a creature to a specific value.
        /// This value must be between 0 and the creature's maximum FP.
        /// </summary>
        /// <param name="creature">The creature whose FP we're setting.</param>
        /// <param name="amount">The amount of FP to set it to.</param>
        public static void SetCurrentFP(NWCreature creature, int amount)
        {
            if (amount < 0)
            {
                amount = 0;
            }

            if (creature.IsPlayer)
            {
                var player = DataService.Get <Player>(creature.GlobalID);
                if (amount > player.MaxFP)
                {
                    amount = player.MaxFP;
                }

                player.CurrentFP = amount;
                DataService.SubmitDataChange(player, DatabaseActionType.Update);
            }
            else
            {
                int maxFP = creature.GetLocalInt("MAX_FP");
                if (amount > maxFP)
                {
                    amount = maxFP;
                }
                creature.SetLocalInt("CURRENT_FP", amount);
            }
        }
Пример #7
0
        private static void CheckForSpellInterruption(NWCreature activator, string spellUUID, Vector position)
        {
            if (activator.GetLocalInt(spellUUID) == (int)SpellStatusType.Completed)
            {
                return;
            }

            Vector currentPosition = activator.Position;

            if (currentPosition.X != position.X ||
                currentPosition.Y != position.Y ||
                currentPosition.Z != position.Z)
            {
                var effect = activator.Effects.SingleOrDefault(x => _.GetEffectTag(x) == "ACTIVATION_VFX");
                if (effect != null)
                {
                    _.RemoveEffect(activator, effect);
                }

                NWNXPlayer.StopGuiTimingBar(activator, "", -1);
                activator.IsBusy = false;
                activator.SetLocalInt(spellUUID, (int)SpellStatusType.Interrupted);
                activator.SendMessage("Your ability has been interrupted.");
                return;
            }

            _.DelayCommand(0.5f, () => { CheckForSpellInterruption(activator, spellUUID, position); });
        }
Пример #8
0
        private void Bar(NWCreature self)
        {
            // barActivity local var on each creature is set 0-3. Leaving room for expansion as we may want people in the bar doing different junk on spawn.
            // barActivity Vars:
            // 0: Do nothing
            // 1: Smoking
            // 2: Drinking
            // 3: Sitting in a chair
            int barActivity = self.GetLocalInt("barActivity");

            /* Random head/clothes check
             *
             *
             */

            switch (barActivity)
            {
            case 1:
                ActionPlayAnimation(Animation.LoopingCustom7, 1.0F, 9999F);
                break;

            case 2:
                ActionPlayAnimation(Animation.LoopingCustom9, 1.0F, 9999F);
                break;

            case 3:
                NWObject chair = GetNearestObjectByTag("chair", self);
                ActionSit(chair);
                break;

            default:
                break;
            }
        }
Пример #9
0
        private static void ProcessLoot()
        {
            NWCreature creature = _.OBJECT_SELF;

            // Single loot table (without an index)
            int singleLootTableID = creature.GetLocalInt("LOOT_TABLE_ID");

            if (singleLootTableID > 0)
            {
                int chance   = creature.GetLocalInt("LOOT_TABLE_CHANCE");
                int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS");

                RunLootAttempt(creature, singleLootTableID, chance, attempts);
            }

            // Multiple loot tables (with an index)
            int lootTableNumber = 1;
            int lootTableID     = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);

            while (lootTableID > 0)
            {
                int chance   = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber);
                int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber);

                RunLootAttempt(creature, lootTableID, chance, attempts);

                lootTableNumber++;
                lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);
            }
        }
Пример #10
0
        public void OnCreatureDeath(NWCreature creature)
        {
            // Single loot table (without an index)
            int singleLootTableID = creature.GetLocalInt("LOOT_TABLE_ID");

            if (singleLootTableID > 0)
            {
                int chance   = creature.GetLocalInt("LOOT_TABLE_CHANCE");
                int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS");

                RunLootAttempt(creature, singleLootTableID, chance, attempts);
            }

            // Multiple loot tables (with an index)
            int lootTableNumber = 1;
            int lootTableID     = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);

            while (lootTableID > 0)
            {
                int chance   = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber);
                int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber);

                RunLootAttempt(creature, lootTableID, chance, attempts);

                lootTableNumber++;
                lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);
            }
        }
Пример #11
0
        public virtual void OnDeath(NWCreature self)
        {
            int vfx = self.GetLocalInt("DEATH_VFX");

            if (vfx > 0)
            {
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(vfx), self);
            }
        }
Пример #12
0
        private static void ProcessPerkFeats(NWCreature self)
        {
            // Bail early if any of the following is true:
            //      - Creature has a weapon skill queued.
            //      - Creature does not have a PerkFeat cache.
            //      - There are no perk feats in the cache.
            //      - Creature has no target.

            if (self.GetLocalInt("ACTIVE_WEAPON_SKILL") > 0)
            {
                return;
            }
            if (!self.Data.ContainsKey("PERK_FEATS"))
            {
                return;
            }

            Dictionary <int, AIPerkDetails> cache = self.Data["PERK_FEATS"];

            if (cache.Count <= 0)
            {
                return;
            }

            NWObject target = _.GetAttackTarget(self);

            if (!target.IsValid)
            {
                return;
            }

            // Pull back whatever concentration effect is currently active, if any.
            var concentration = AbilityService.GetActiveConcentrationEffect(self);

            // Exclude any concentration effects, if necessary, then randomize potential feats to use.
            var randomizedFeatIDs = concentration.Type == PerkType.Unknown
                ? cache.Values                                                                        // No concentration exclusions
                : cache.Values.Where(x => x.ExecutionType != PerkExecutionType.ConcentrationAbility); // Exclude concentration abilities

            randomizedFeatIDs = randomizedFeatIDs.OrderBy(o => RandomService.Random());

            foreach (var perkDetails in randomizedFeatIDs)
            {
                // Move to next feat if this creature cannot use this one.
                if (!AbilityService.CanUsePerkFeat(self, target, perkDetails.FeatID))
                {
                    continue;
                }

                self.AssignCommand(() =>
                {
                    _.ActionUseFeat(perkDetails.FeatID, target);
                });

                break;
            }
        }
Пример #13
0
        public virtual void OnDeath(NWCreature self)
        {
            var vfx = (VisualEffect)self.GetLocalInt("DEATH_VFX");

            if (vfx != VisualEffect.Invalid)
            {
                ApplyEffectToObject(DurationType.Instant, EffectVisualEffect(vfx), self);
            }
        }
Пример #14
0
        public void OnCreatureDeath(NWCreature creature)
        {
            int lootTableNumber = 1;
            int lootTableID     = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);

            while (lootTableID > 0)
            {
                int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber);
                if (chance <= 0 || chance > 100)
                {
                    chance = 100;
                }

                int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber);
                if (attempts <= 0)
                {
                    attempts = 1;
                }

                for (int a = 1; a <= attempts; a++)
                {
                    if (_random.Random(100) + 1 <= chance)
                    {
                        ItemVO model = PickRandomItemFromLootTable(lootTableID);
                        if (model == null)
                        {
                            continue;
                        }

                        int spawnQuantity = model.Quantity > 1 ? _random.Random(1, model.Quantity) : 1;

                        for (int x = 1; x <= spawnQuantity; x++)
                        {
                            _.CreateItemOnObject(model.Resref, creature.Object);
                        }
                    }
                }

                lootTableNumber++;
                lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber);
            }
        }
Пример #15
0
 public static int GetCreaturePerkLevel(NWCreature creature, int perkTypeID)
 {
     if (creature.IsPlayer)
     {
         NWPlayer player = creature.Object;
         return(GetPCEffectivePerkLevel(player, perkTypeID));
     }
     else
     {
         return(creature.GetLocalInt("PERK_LEVEL_" + perkTypeID));
     }
 }
Пример #16
0
 /// <summary>
 /// Retrieves the maximum FP a creature has.
 /// </summary>
 /// <param name="creature">The creature whose max FP we're getting.</param>
 /// <returns>The max FP a creature has.</returns>
 public static int GetMaxFP(NWCreature creature)
 {
     if (creature.IsPlayer)
     {
         var player = DataService.Player.GetByID(creature.GlobalID);
         return(player.MaxFP);
     }
     else
     {
         return(creature.GetLocalInt("MAX_FP"));
     }
 }
Пример #17
0
        /// <summary>
        /// Returns the currently active concentration perk for a given creature.
        /// If no concentration perk is active, PerkType.Unknown will be returned.
        /// </summary>
        /// <param name="creature"></param>
        /// <returns>A ConcentrationEffect containing data about the active concentration effect.</returns>
        public static ConcentrationEffect GetActiveConcentrationEffect(NWCreature creature)
        {
            if (creature.IsPlayer)
            {
                Player dbPlayer = DataService.Player.GetByID(creature.GlobalID);
                if (dbPlayer.ActiveConcentrationPerkID == null)
                {
                    return(new ConcentrationEffect(PerkType.Unknown, 0));
                }

                return(new ConcentrationEffect((PerkType)dbPlayer.ActiveConcentrationPerkID, dbPlayer.ActiveConcentrationTier));
            }
            else
            {
                // Creatures are assumed to always use the highest perk level available.
                int      perkID = creature.GetLocalInt("ACTIVE_CONCENTRATION_PERK_ID");
                int      tier   = creature.GetLocalInt("PERK_LEVEL_" + perkID);
                PerkType type   = perkID <= 0 ? PerkType.Unknown : (PerkType)perkID;
                return(new ConcentrationEffect(type, tier));
            }
        }
Пример #18
0
 /// <summary>
 /// Returns the current FP amount of a creature.
 /// </summary>
 /// <param name="creature">The creature whose FP we're getting.</param>
 /// <returns>The amount of FP the creature currently has.</returns>
 public static int GetCurrentFP(NWCreature creature)
 {
     if (creature.IsPlayer)
     {
         var player = DataService.Player.GetByID(creature.GlobalID);
         return(player.CurrentFP);
     }
     else
     {
         return(creature.GetLocalInt("CURRENT_FP"));
     }
 }
Пример #19
0
        private static void OnModuleApplyDamage()
        {
            var data = NWNXDamage.GetDamageEventData();

            if (data.Base <= 0)
            {
                return;
            }

            NWObject damager = data.Damager;

            if (!damager.IsPlayer)
            {
                return;
            }
            NWCreature target = NWGameObject.OBJECT_SELF;

            // Check that this was a normal attack, and not (say) a damage over time effect.
            if (target.GetLocalInt(AbilityService.LAST_ATTACK + damager.GlobalID) != AbilityService.ATTACK_PHYSICAL)
            {
                return;
            }

            NWItem weapon      = (_.GetLastWeaponUsed(damager.Object));
            int    damageBonus = weapon.DamageBonus;

            NWPlayer  player    = (damager.Object);
            int       itemLevel = weapon.RecommendedLevel;
            SkillType skill     = ItemService.GetSkillTypeForItem(weapon);

            if (skill == SkillType.Unknown)
            {
                return;
            }

            int rank  = SkillService.GetPCSkillRank(player, skill);
            int delta = itemLevel - rank;

            if (delta >= 1)
            {
                damageBonus--;
            }
            damageBonus = damageBonus - delta / 5;

            if (damageBonus <= 0)
            {
                damageBonus = 0;
            }

            data.Base += damageBonus;
            NWNXDamage.SetDamageEventData(data);
        }
Пример #20
0
        public static void AddTemporaryForceDefense(NWCreature target, ForceAbilityType forceAbility, int amount = 5, int length = 5)
        {
            if (amount <= 0)
            {
                amount = 1;
            }
            string   variable        = "TEMP_FORCE_DEFENSE_" + (int)forceAbility;
            int      tempDefense     = target.GetLocalInt(variable) + amount;
            string   tempDateExpires = target.GetLocalString(variable);
            DateTime expirationDate  = DateTime.UtcNow;

            if (!string.IsNullOrWhiteSpace(tempDateExpires))
            {
                expirationDate = DateTime.Parse(tempDateExpires);
            }

            expirationDate = expirationDate.AddSeconds(length);
            target.SetLocalString(variable, expirationDate.ToString(CultureInfo.InvariantCulture));
            target.SetLocalInt(variable, tempDefense);
        }
Пример #21
0
        private void ForceAttackHighestEnmity(NWCreature self)
        {
            if (self.GetLocalInt("CASTING") == 1)
            {
                return;
            }
            var enmityTable = EnmityService.GetEnmityTable(self);
            var target      = enmityTable.Values
                              .OrderByDescending(o => o.TotalAmount)
                              .FirstOrDefault(x => x.TargetObject.IsValid &&
                                              x.TargetObject.Area.Equals(self.Area));

            if (target == null)
            {
                return;
            }
            self.AssignCommand(() =>
            {
                bool bDone = false;

                // See which force feats we have, and pick one to use.
                if (_.GetHasFeat((int)CustomFeatType.ForceLightning, self) == 1)
                {
                    _.ClearAllActions();
                    bDone = UseFeat((int)CustomFeatType.ForceLightning, "ForceLightning", self, target.TargetObject);
                }

                if (!bDone && _.GetHasFeat((int)CustomFeatType.DrainLife, self) == 1)
                {
                    _.ClearAllActions();
                    bDone = UseFeat((int)CustomFeatType.DrainLife, "DrainLife", self, target.TargetObject);
                }

                if (!bDone)
                {
                    // No abilities available right now, run away!
                    _.ActionMoveAwayFromObject(target.TargetObject, 1);
                }
            });
        }
Пример #22
0
        private static void OnCreatureDeath()
        {
            NWCreature creature = Object.OBJECT_SELF;

            int npcGroupID = creature.GetLocalInt("NPC_GROUP");

            if (npcGroupID <= 0)
            {
                return;
            }

            NWObject oKiller = _.GetLastKiller();

            if (!oKiller.IsPlayer)
            {
                return;
            }

            string areaResref = creature.Area.Resref;

            List <KeyValuePair <NWPlayer, int> > playersToAdvance = new List <KeyValuePair <NWPlayer, int> >();
            NWPlayer oPC = _.GetFirstFactionMember(oKiller);

            while (oPC.IsValid)
            {
                if (areaResref != oPC.Area.Resref)
                {
                    oPC = _.GetNextFactionMember(oKiller);
                    continue;
                }

                if (_.GetDistanceBetween(creature, oPC) <= 0.0f || _.GetDistanceBetween(creature, oPC) > 20.0f)
                {
                    oPC = _.GetNextFactionMember(oKiller);
                    continue;
                }

                var playerID    = oPC.GlobalID;
                var killTargets = DataService.Where <PCQuestKillTargetProgress>(x => x.PlayerID == playerID && x.NPCGroupID == npcGroupID).ToList();

                foreach (var kt in killTargets)
                {
                    var questStatus = DataService.Get <PCQuestStatus>(kt.PCQuestStatusID);
                    var quest       = DataService.Get <Quest>(questStatus.QuestID);
                    var npcGroup    = DataService.Get <NPCGroup>(kt.NPCGroupID);

                    kt.RemainingToKill--;
                    string             targetGroupName = npcGroup.Name;
                    string             updateMessage   = "[" + quest.Name + "] " + targetGroupName + " remaining: " + kt.RemainingToKill;
                    DatabaseActionType action          = DatabaseActionType.Update;

                    if (kt.RemainingToKill <= 0)
                    {
                        updateMessage += " " + ColorTokenService.Green(" {COMPLETE}");
                        playersToAdvance.Add(new KeyValuePair <NWPlayer, int>(oPC, quest.ID));
                        action = DatabaseActionType.Delete;
                    }

                    DataService.SubmitDataChange(kt, action);

                    var pc = oPC;
                    _.DelayCommand(1.0f, () =>
                    {
                        pc.SendMessage(updateMessage);
                    });

                    string ruleName = quest.OnKillTargetRule;
                    if (!string.IsNullOrWhiteSpace(ruleName))
                    {
                        var pcCopy = oPC;
                        var rule   = GetQuestRule(ruleName);

                        string[] args = null;
                        if (!string.IsNullOrWhiteSpace(quest.OnKillTargetArgs))
                        {
                            args = quest.OnKillTargetArgs.Split(',');
                        }
                        rule.Run(pcCopy, creature, quest.ID, args);
                    }
                }

                oPC = _.GetNextFactionMember(oKiller);
            }

            foreach (var player in playersToAdvance)
            {
                AdvanceQuestState(player.Key, null, player.Value);
            }
        }
Пример #23
0
        public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData)
        {
            NWPlayer        player     = user.Object;
            ResourceQuality quality    = (ResourceQuality)target.GetLocalInt("RESOURCE_QUALITY");
            int             tier       = target.GetLocalInt("RESOURCE_TIER");
            int             remaining  = target.GetLocalInt("RESOURCE_COUNT") - 1;
            string          itemResref = target.GetLocalString("RESOURCE_RESREF");
            int             gemChance  = ResourceService.CalculateChanceForComponentBonus(player, tier, quality);
            int             roll       = RandomService.Random(1, 100);
            int             rank       = SkillService.GetPCSkillRank(player, SkillType.Harvesting);

            if (item.RecommendedLevel < rank)
            {
                rank = item.RecommendedLevel;
            }

            int difficulty = (tier - 1) * 10 + ResourceService.GetDifficultyAdjustment(quality);
            int delta      = difficulty - rank;

            int baseXP = 0;

            if (delta >= 6)
            {
                baseXP = 400;
            }
            else if (delta == 5)
            {
                baseXP = 350;
            }
            else if (delta == 4)
            {
                baseXP = 325;
            }
            else if (delta == 3)
            {
                baseXP = 300;
            }
            else if (delta == 2)
            {
                baseXP = 250;
            }
            else if (delta == 1)
            {
                baseXP = 225;
            }
            else if (delta == 0)
            {
                baseXP = 200;
            }
            else if (delta == -1)
            {
                baseXP = 150;
            }
            else if (delta == -2)
            {
                baseXP = 100;
            }
            else if (delta == -3)
            {
                baseXP = 50;
            }
            else if (delta == -4)
            {
                baseXP = 25;
            }

            int itemHarvestBonus = item.HarvestingBonus;
            int scanningBonus    = user.GetLocalInt(target.GlobalID.ToString());

            gemChance += itemHarvestBonus * 2 + scanningBonus * 2;

            baseXP = baseXP + scanningBonus * 5;

            // Spawn the normal resource.
            NWItem resource = CreateItemOnObject(itemResref, player);

            user.SendMessage("You harvest " + resource.Name + ".");

            // If player meets the chance to acquire a gem, create one and modify its properties.
            if (quality > ResourceQuality.Low && roll <= gemChance)
            {
                // Gemstone quality is determined by the quality of the vein.
                switch (quality)
                {
                case ResourceQuality.Normal:
                    resource = CreateItemOnObject("flawed_gemstone", player);
                    break;

                case ResourceQuality.High:
                    resource = CreateItemOnObject("gemstone", player);
                    break;

                case ResourceQuality.VeryHigh:
                    resource = CreateItemOnObject("perfect_gemstone", player);
                    break;
                }

                var ip = ResourceService.GetRandomComponentBonusIP(quality);
                BiowareXP2.IPSafeAddItemProperty(resource, ip.Item1, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true);

                switch (ip.Item2)
                {
                case 0:
                    resource.Name = ColorTokenService.Green(resource.Name);
                    break;

                case 1:
                    resource.Name = ColorTokenService.Blue(resource.Name);
                    break;

                case 2:
                    resource.Name = ColorTokenService.Purple(resource.Name);
                    break;

                case 3:
                    resource.Name = ColorTokenService.Orange(resource.Name);
                    break;

                case 4:
                    resource.Name = ColorTokenService.LightPurple(resource.Name);
                    break;

                case 5:
                    resource.Name = ColorTokenService.Yellow(resource.Name);
                    break;

                case 6:
                    resource.Name = ColorTokenService.Red(resource.Name);
                    break;

                case 7:
                    resource.Name = ColorTokenService.Cyan(resource.Name);
                    break;
                }

                user.SendMessage("You harvest " + resource.Name + ".");
            }

            float decayMinimum = 0.03f;
            float decayMaximum = 0.07f;

            if (delta > 0)
            {
                decayMinimum += delta * 0.1f;
                decayMaximum += delta * 0.1f;
            }

            DurabilityService.RunItemDecay(player, item, RandomService.RandomFloat(decayMinimum, decayMaximum));
            int xp = baseXP;

            SkillService.GiveSkillXP(player, SkillType.Harvesting, xp);

            if (remaining <= 0)
            {
                NWPlaceable prop = target.GetLocalObject("RESOURCE_PROP_OBJ");

                if (prop.IsValid)
                {
                    prop.Destroy();
                }

                target.Destroy();
                user.DeleteLocalInt(target.GlobalID.ToString());
            }
            else
            {
                target.SetLocalInt("RESOURCE_COUNT", remaining);
            }

            ApplyEffectAtLocation(DurationType.Instant, EffectVisualEffect(VisualEffect.Vfx_Fnf_Summon_Monster_3), target.Location);
        }
Пример #24
0
        /// <summary>
        /// Updates a player's quest status if the creature is part of an ongoing quest.
        /// Progresses the player to the next state if all requirements are met.
        /// </summary>
        private static void OnCreatureDeath()
        {
            NWCreature creature = _.OBJECT_SELF;

            int npcGroupID = creature.GetLocalInt("NPC_GROUP");

            if (npcGroupID <= 0)
            {
                return;
            }

            NWObject oKiller = GetLastKiller();

            if (!oKiller.IsPlayer)
            {
                return;
            }

            string areaResref = creature.Area.Resref;

            List <KeyValuePair <NWPlayer, int> > playersToAdvance = new List <KeyValuePair <NWPlayer, int> >();
            NWPlayer oPC = GetFirstFactionMember(oKiller);

            while (oPC.IsValid)
            {
                if (areaResref != oPC.Area.Resref)
                {
                    oPC = GetNextFactionMember(oKiller);
                    continue;
                }

                if (GetDistanceBetween(creature, oPC) <= 0.0f || GetDistanceBetween(creature, oPC) > 40.0f)
                {
                    oPC = GetNextFactionMember(oKiller);
                    continue;
                }

                var playerID    = oPC.GlobalID;
                var killTargets = DataService.PCQuestKillTargetProgress.GetAllByPlayerIDAndNPCGroupID(playerID, npcGroupID).ToList();

                foreach (var kt in killTargets)
                {
                    var questStatus = DataService.PCQuestStatus.GetByID(kt.PCQuestStatusID);
                    var quest       = GetQuestByID(questStatus.QuestID);
                    var npcGroup    = DataService.NPCGroup.GetByID(kt.NPCGroupID);

                    kt.RemainingToKill--;
                    string             targetGroupName = npcGroup.Name;
                    string             updateMessage   = "[" + quest.Name + "] " + targetGroupName + " remaining: " + kt.RemainingToKill;
                    DatabaseActionType action          = DatabaseActionType.Update;

                    if (kt.RemainingToKill <= 0)
                    {
                        updateMessage += " " + ColorTokenService.Green(" {COMPLETE}");
                        playersToAdvance.Add(new KeyValuePair <NWPlayer, int>(oPC, quest.QuestID));
                        action = DatabaseActionType.Delete;
                    }

                    DataService.SubmitDataChange(kt, action);

                    var pc = oPC;
                    DelayCommand(1.0f, () =>
                    {
                        pc.SendMessage(updateMessage);
                    });
                }

                oPC = GetNextFactionMember(oKiller);
            }

            foreach (var player in playersToAdvance)
            {
                var quest = GetQuestByID(player.Value);
                quest.Advance(player.Key, creature);
            }
        }
Пример #25
0
        public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData)
        {
            NWPlayer        player        = user.Object;
            ResourceQuality quality       = (ResourceQuality)target.GetLocalInt("RESOURCE_QUALITY");
            int             tier          = target.GetLocalInt("RESOURCE_TIER");
            int             remaining     = target.GetLocalInt("RESOURCE_COUNT") - 1;
            string          itemResref    = target.GetLocalString("RESOURCE_RESREF");
            int             ipBonusChance = _resource.CalculateChanceForComponentBonus(player, tier, quality);
            int             roll          = _random.Random(1, 100);
            int             rank          = _skill.GetPCSkillRank(player, SkillType.Harvesting);

            if (item.RecommendedLevel < rank)
            {
                rank = item.RecommendedLevel;
            }

            int difficulty = (tier - 1) * 10 + _resource.GetDifficultyAdjustment(quality);
            int delta      = difficulty - rank;

            int baseXP = 0;

            if (delta >= 6)
            {
                baseXP = 400;
            }
            else if (delta == 5)
            {
                baseXP = 350;
            }
            else if (delta == 4)
            {
                baseXP = 325;
            }
            else if (delta == 3)
            {
                baseXP = 300;
            }
            else if (delta == 2)
            {
                baseXP = 250;
            }
            else if (delta == 1)
            {
                baseXP = 225;
            }
            else if (delta == 0)
            {
                baseXP = 200;
            }
            else if (delta == -1)
            {
                baseXP = 150;
            }
            else if (delta == -2)
            {
                baseXP = 100;
            }
            else if (delta == -3)
            {
                baseXP = 50;
            }
            else if (delta == -4)
            {
                baseXP = 25;
            }

            int itemHarvestBonus = item.HarvestingBonus;
            int scanningBonus    = user.GetLocalInt(target.GlobalID.ToString());

            ipBonusChance += itemHarvestBonus * 2 + scanningBonus * 2;

            baseXP = baseXP + scanningBonus * 5;

            NWItem resource = _.CreateItemOnObject(itemResref, player.Object);

            if (roll <= ipBonusChance)
            {
                var ip = _resource.GetRandomComponentBonusIP(quality);
                _biowareXP2.IPSafeAddItemProperty(resource, ip.Item1, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true);

                switch (ip.Item2)
                {
                case 0:
                    resource.Name = _color.Green(resource.Name);
                    break;

                case 1:
                    resource.Name = _color.Blue(resource.Name);
                    break;

                case 2:
                    resource.Name = _color.Purple(resource.Name);
                    break;

                case 3:
                    resource.Name = _color.Orange(resource.Name);
                    break;
                }
            }

            float decayMinimum = 0.03f;
            float decayMaximum = 0.07f;

            if (delta > 0)
            {
                decayMinimum += delta * 0.1f;
                decayMaximum += delta * 0.1f;
            }

            user.SendMessage("You harvest " + resource.Name + ".");
            _durability.RunItemDecay(player, item, _random.RandomFloat(decayMinimum, decayMaximum));
            int xp = baseXP;

            _skill.GiveSkillXP(player, SkillType.Harvesting, xp);

            if (remaining <= 0)
            {
                NWPlaceable prop = target.GetLocalObject("RESOURCE_PROP_OBJ");

                if (prop.IsValid)
                {
                    prop.Destroy();
                }

                target.Destroy();
                user.DeleteLocalInt(target.GlobalID.ToString());
            }
            else
            {
                target.SetLocalInt("RESOURCE_COUNT", remaining);
            }

            _.ApplyEffectAtLocation(DURATION_TYPE_INSTANT, _.EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), target.Location);
        }
Пример #26
0
        private static void OnCreatureSpawn()
        {
            NWCreature self = Object.OBJECT_SELF;

            // Don't modify AI behaviour for DM-spawned creatures.
            if (self.GetLocalInt("DM_SPAWNED") == _.TRUE)
            {
                return;
            }

            string script = GetBehaviourScript(Object.OBJECT_SELF);

            if (string.IsNullOrWhiteSpace(script))
            {
                return;
            }
            IAIBehaviour ai = GetAIBehaviour(script);

            if (ai.IgnoreNWNEvents)
            {
                self.SetLocalInt("IGNORE_NWN_EVENTS", 1);
            }
            if (ai.IgnoreOnBlocked)
            {
                self.SetLocalInt("IGNORE_NWN_ON_BLOCKED_EVENT", 1);
            }
            if (ai.IgnoreOnCombatRoundEnd)
            {
                self.SetLocalInt("IGNORE_NWN_ON_COMBAT_ROUND_END_EVENT", 1);
            }
            if (ai.IgnoreOnConversation)
            {
                self.SetLocalInt("IGNORE_NWN_ON_CONVERSATION_EVENT", 1);
            }
            if (ai.IgnoreOnDamaged)
            {
                self.SetLocalInt("IGNORE_NWN_ON_DAMAGED_EVENT", 1);
            }
            if (ai.IgnoreOnDeath)
            {
                self.SetLocalInt("IGNORE_NWN_ON_DEATH_EVENT", 1);
            }
            if (ai.IgnoreOnDisturbed)
            {
                self.SetLocalInt("IGNORE_NWN_ON_DISTURBED_EVENT", 1);
            }
            if (ai.IgnoreOnHeartbeat)
            {
                self.SetLocalInt("IGNORE_NWN_ON_HEARTBEAT_EVENT", 1);
            }
            if (ai.IgnoreOnPerception)
            {
                self.SetLocalInt("IGNORE_NWN_ON_PERCEPTION_EVENT", 1);
            }
            if (ai.IgnoreOnPhysicalAttacked)
            {
                self.SetLocalInt("IGNORE_NWN_ON_PHYSICAL_ATTACKED_EVENT", 1);
            }
            if (ai.IgnoreOnRested)
            {
                self.SetLocalInt("IGNORE_NWN_ON_RESTED_EVENT", 1);
            }
            if (ai.IgnoreOnSpawn)
            {
                self.SetLocalInt("IGNORE_NWN_ON_SPAWN_EVENT", 1);
            }
            if (ai.IgnoreOnSpellCastAt)
            {
                self.SetLocalInt("IGNORE_NWN_ON_SPELL_CAST_AT_EVENT", 1);
            }
            if (ai.IgnoreOnUserDefined)
            {
                self.SetLocalInt("IGNORE_NWN_ON_USER_DEFINED_EVENT", 1);
            }

            _areaAICreatures[self.Area].Add(self);
            ai.OnSpawn(self);
        }
Пример #27
0
 private AIFlags GetAIFlags(NWCreature self)
 {
     return((AIFlags)self.GetLocalInt("AI_FLAGS"));
 }
Пример #28
0
        public void OnCreatureDeath(NWCreature creature)
        {
            int npcGroupID = creature.GetLocalInt("NPC_GROUP");

            if (npcGroupID <= 0)
            {
                return;
            }

            NWObject oKiller = NWObject.Wrap(_.GetLastKiller());

            if (_.GetIsPC(oKiller.Object) == FALSE || _.GetIsDM(oKiller.Object) == TRUE)
            {
                return;
            }

            string areaResref = creature.Area.Resref;

            NWPlayer oPC = NWPlayer.Wrap(_.GetFirstFactionMember(oKiller.Object));

            while (oPC.IsValid)
            {
                if (areaResref != oPC.Area.Resref)
                {
                    continue;
                }
                if (_.GetDistanceBetween(creature.Object, oPC.Object) == 0.0f || _.GetDistanceBetween(creature.Object, oPC.Object) > 20.0f)
                {
                    continue;
                }

                List <PCQuestKillTargetProgress> killTargets = _db.PCQuestKillTargetProgresses.Where(x => x.PlayerID == oPC.GlobalID && x.NPCGroupID == npcGroupID).ToList();

                foreach (PCQuestKillTargetProgress kt in killTargets)
                {
                    kt.RemainingToKill--;
                    string targetGroupName = kt.NPCGroup.Name;
                    string questName       = kt.PcQuestStatus.Quest.Name;
                    string updateMessage   = "[" + questName + "] " + targetGroupName + " remaining: " + kt.RemainingToKill;

                    if (kt.RemainingToKill <= 0)
                    {
                        _db.PCQuestKillTargetProgresses.Remove(kt);
                        updateMessage += " " + _color.Green(" {COMPLETE}");

                        if (kt.PcQuestStatus.PCQuestKillTargetProgresses.Count - 1 <= 0)
                        {
                            AdvanceQuestState(oPC, kt.PcQuestStatus.QuestID);
                        }
                    }

                    _db.SaveChanges();
                    string finalMessage = updateMessage;
                    var    pc           = oPC;
                    oPC.DelayCommand(() =>
                    {
                        pc.SendMessage(finalMessage);
                    }, 1.0f);
                }

                oPC = NWPlayer.Wrap(_.GetNextFactionMember(oKiller.Object));
            }
        }
Пример #29
0
        public bool Run(params object[] args)
        {
            using (new Profiler(nameof(FinishAbilityUse)))
            {
                // These arguments are sent from the AbilityService's ActivateAbility method.
                NWCreature activator    = (NWCreature)args[0];
                string     spellUUID    = Convert.ToString(args[1]);
                int        perkID       = (int)args[2];
                NWObject   target       = (NWObject)args[3];
                int        pcPerkLevel  = (int)args[4];
                int        spellTier    = (int)args[5];
                float      armorPenalty = (float)args[6];

                // Get the relevant perk information from the database.
                Data.Entity.Perk dbPerk = DataService.Single <Data.Entity.Perk>(x => x.ID == perkID);

                // The execution type determines how the perk behaves and the rules surrounding it.
                PerkExecutionType executionType = dbPerk.ExecutionTypeID;

                // Get the class which handles this perk's behaviour.
                IPerkHandler perk = PerkService.GetPerkHandler(perkID);

                // Pull back cooldown information.
                int?cooldownID            = perk.CooldownCategoryID(activator, dbPerk.CooldownCategoryID, spellTier);
                CooldownCategory cooldown = cooldownID == null ? null : DataService.SingleOrDefault <CooldownCategory>(x => x.ID == cooldownID);

                // If the activator interrupted the spell or died, we can bail out early.
                if (activator.GetLocalInt(spellUUID) == (int)SpellStatusType.Interrupted || // Moved during casting
                    activator.CurrentHP < 0 || activator.IsDead)                            // Or is dead/dying
                {
                    activator.DeleteLocalInt(spellUUID);
                    return(false);
                }

                // Remove the temporary UUID which is tracking this spell cast.
                activator.DeleteLocalInt(spellUUID);

                // Force Abilities, Combat Abilities, Stances, and Concentration Abilities
                if (executionType == PerkExecutionType.ForceAbility ||
                    executionType == PerkExecutionType.CombatAbility ||
                    executionType == PerkExecutionType.Stance ||
                    executionType == PerkExecutionType.ConcentrationAbility)
                {
                    // Run the impact script.
                    perk.OnImpact(activator, target, pcPerkLevel, spellTier);

                    // If an animation is specified for this perk, play it now.
                    if (dbPerk.CastAnimationID != null && dbPerk.CastAnimationID > 0)
                    {
                        activator.AssignCommand(() => { _.ActionPlayAnimation((int)dbPerk.CastAnimationID, 1f, 1f); });
                    }

                    // If the target is an NPC, assign enmity towards this creature for that NPC.
                    if (target.IsNPC)
                    {
                        AbilityService.ApplyEnmity(activator, target.Object, dbPerk);
                    }
                }

                // Adjust creature's current FP, if necessary.
                // Adjust FP only if spell cost > 0
                PerkFeat perkFeat = DataService.Single <PerkFeat>(x => x.PerkID == perkID && x.PerkLevelUnlocked == spellTier);
                int      fpCost   = perk.FPCost(activator, perkFeat.BaseFPCost, spellTier);

                if (fpCost > 0)
                {
                    int currentFP = AbilityService.GetCurrentFP(activator);
                    int maxFP     = AbilityService.GetMaxFP(activator);
                    currentFP -= fpCost;
                    AbilityService.SetCurrentFP(activator, currentFP);
                    activator.SendMessage(ColorTokenService.Custom("FP: " + currentFP + " / " + maxFP, 32, 223, 219));
                }

                // Notify activator of concentration ability change and also update it in the DB.
                if (executionType == PerkExecutionType.ConcentrationAbility)
                {
                    AbilityService.StartConcentrationEffect(activator, perkID, spellTier);
                    activator.SendMessage("Concentration ability activated: " + dbPerk.Name);

                    // The Skill Increase effect icon and name has been overwritten. Apply the effect to the player now.
                    // This doesn't do anything - it simply gives a visual cue that the player has an active concentration effect.
                    _.ApplyEffectToObject(_.DURATION_TYPE_PERMANENT, _.EffectSkillIncrease(_.SKILL_USE_MAGIC_DEVICE, 1), activator);
                }

                // Handle applying cooldowns, if necessary.
                if (cooldown != null)
                {
                    AbilityService.ApplyCooldown(activator, cooldown, perk, spellTier, armorPenalty);
                }

                // Mark the creature as no longer busy.
                activator.IsBusy = false;

                // Mark the spell cast as complete.
                activator.SetLocalInt(spellUUID, (int)SpellStatusType.Completed);

                return(true);
            }
        }
Пример #30
0
        /// <summary>
        /// Runs validation checks to ensure activator can use a perk feat.
        /// Activation will fail if any of the following are true:
        ///     - Target is invalid
        ///     - Activator is a ship
        ///     - Feat is not a perk feat
        ///     - Cooldown has not passed
        /// </summary>
        /// <param name="activator">The creature activating a perk feat.</param>
        /// <param name="target">The target of the perk feat.</param>
        /// <param name="featID">The ID number of the feat being used.</param>
        /// <returns>true if able to use perk feat on target, false otherwise.</returns>
        public static bool CanUsePerkFeat(NWCreature activator, NWObject target, Feat featID)
        {
            var perkFeat = DataService.PerkFeat.GetByFeatIDOrDefault((int)featID);

            // There's no matching feat in the DB for this ability. Exit early.
            if (perkFeat == null)
            {
                return(false);
            }

            // Retrieve the perk information.
            Data.Entity.Perk perk = DataService.Perk.GetByIDOrDefault(perkFeat.PerkID);

            // No perk could be found. Exit early.
            if (perk == null)
            {
                return(false);
            }

            // Check to see if we are a spaceship.  Spaceships can't use abilities...
            if (activator.GetLocalInt("IS_SHIP") > 0 || activator.GetLocalInt("IS_GUNNER") > 0)
            {
                activator.SendMessage("You cannot use that ability while piloting a ship.");
                return(false);
            }

            // Retrieve the perk-specific handler logic.
            var handler = PerkService.GetPerkHandler(perkFeat.PerkID);

            // Get the creature's perk level.
            int creaturePerkLevel = PerkService.GetCreaturePerkLevel(activator, perk.ID);

            // If player is disabling an existing stance, remove that effect.
            if (perk.ExecutionTypeID == PerkExecutionType.Stance)
            {
                // Can't process NPC stances at the moment. Need to do some more refactoring before this is possible.
                // todo: handle NPC stances.
                if (!activator.IsPlayer)
                {
                    return(false);
                }

                PCCustomEffect stanceEffect = DataService.PCCustomEffect.GetByStancePerkOrDefault(activator.GlobalID, perk.ID);

                if (stanceEffect != null)
                {
                    if (CustomEffectService.RemoveStance(activator))
                    {
                        return(false);
                    }
                }
            }

            // Check for a valid perk level.
            if (creaturePerkLevel <= 0)
            {
                activator.SendMessage("You do not meet the prerequisites to use this ability.");
                return(false);
            }

            // Verify that this hostile action meets PVP sanctuary restriction rules.
            if (handler.IsHostile() && target.IsPlayer)
            {
                if (!PVPSanctuaryService.IsPVPAttackAllowed(activator.Object, target.Object))
                {
                    return(false);
                }
            }

            // Activator and target must be in the same area and within line of sight.
            if (activator.Area.Resref != target.Area.Resref ||
                _.LineOfSightObject(activator.Object, target.Object) == false)
            {
                activator.SendMessage("You cannot see your target.");
                return(false);
            }

            // Run this perk's specific checks on whether the activator may use this perk on the target.
            string canCast = handler.CanCastSpell(activator, target, perkFeat.PerkLevelUnlocked);

            if (!string.IsNullOrWhiteSpace(canCast))
            {
                activator.SendMessage(canCast);
                return(false);
            }

            // Calculate the FP cost to use this ability. Verify activator has sufficient FP.
            int fpCost    = handler.FPCost(activator, handler.FPCost(activator, perkFeat.BaseFPCost, perkFeat.PerkLevelUnlocked), perkFeat.PerkLevelUnlocked);
            int currentFP = GetCurrentFP(activator);

            if (currentFP < fpCost)
            {
                activator.SendMessage("You do not have enough FP. (Required: " + fpCost + ". You have: " + currentFP + ")");
                return(false);
            }

            // Verify activator isn't busy or dead.
            if (activator.IsBusy || activator.CurrentHP <= 0)
            {
                activator.SendMessage("You are too busy to activate that ability.");
                return(false);
            }

            // verify activator is commandable. https://github.com/zunath/SWLOR_NWN/issues/940#issue-467175951
            if (!activator.IsCommandable)
            {
                activator.SendMessage("You cannot take actions currently.");
                return(false);
            }

            // If we're executing a concentration ability, check and see if the activator currently has this ability
            // active. If it's active, then we immediately remove its effect and bail out.
            // Any other ability (including other concentration abilities) execute as normal.
            if (perk.ExecutionTypeID == PerkExecutionType.ConcentrationAbility)
            {
                // Retrieve the concentration effect for this creature.
                var concentrationEffect = GetActiveConcentrationEffect(activator);
                if ((int)concentrationEffect.Type == perk.ID)
                {
                    // It's active. Time to disable it.
                    EndConcentrationEffect(activator);
                    activator.SendMessage("Concentration ability '" + perk.Name + "' deactivated.");
                    SendAOEMessage(activator, activator.Name + " deactivates concentration ability '" + perk.Name + "'.");
                    return(false);
                }
            }

            // Retrieve the cooldown information and determine the unlock time.
            int?     cooldownCategoryID = handler.CooldownCategoryID(activator, perk.CooldownCategoryID, perkFeat.PerkLevelUnlocked);
            DateTime now            = DateTime.UtcNow;
            DateTime unlockDateTime = cooldownCategoryID == null ? now : GetAbilityCooldownUnlocked(activator, (int)cooldownCategoryID);

            // Check if we've passed the unlock date. Exit early if we have not.
            if (unlockDateTime > now)
            {
                string timeToWait = TimeService.GetTimeToWaitLongIntervals(now, unlockDateTime, false);
                activator.SendMessage("That ability can be used in " + timeToWait + ".");
                return(false);
            }

            // Passed all checks. Return true.
            return(true);
        }