public void GetByID_OneItem_ReturnsPerkFeat()
        {
            // Arrange
            PerkFeat entity = new PerkFeat {
                ID = 1
            };

            // Act
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity));

            // Assert
            Assert.AreNotSame(entity, _cache.GetByID(1));
        }
        public void GetByID_TwoItems_ReturnsCorrectObject()
        {
            // Arrange
            PerkFeat entity1 = new PerkFeat {
                ID = 1
            };
            PerkFeat entity2 = new PerkFeat {
                ID = 2
            };

            // Act
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity1));
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity2));

            // Assert
            Assert.AreNotSame(entity1, _cache.GetByID(1));
            Assert.AreNotSame(entity2, _cache.GetByID(2));
        }
        public void GetByID_RemovedItem_ReturnsCorrectObject()
        {
            // Arrange
            PerkFeat entity1 = new PerkFeat {
                ID = 1
            };
            PerkFeat entity2 = new PerkFeat {
                ID = 2
            };

            // Act
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity1));
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity2));
            MessageHub.Instance.Publish(new OnCacheObjectDeleted <PerkFeat>(entity1));

            // Assert
            Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(1); });
            Assert.AreNotSame(entity2, _cache.GetByID(2));
        }
        public void GetByID_NoItems_ThrowsKeyNotFoundException()
        {
            // Arrange
            PerkFeat entity1 = new PerkFeat {
                ID = 1
            };
            PerkFeat entity2 = new PerkFeat {
                ID = 2
            };

            // Act
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity1));
            MessageHub.Instance.Publish(new OnCacheObjectSet <PerkFeat>(entity2));
            MessageHub.Instance.Publish(new OnCacheObjectDeleted <PerkFeat>(entity1));
            MessageHub.Instance.Publish(new OnCacheObjectDeleted <PerkFeat>(entity2));

            // Assert
            Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(1); });
            Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(2); });
        }
        private static void ProcessConcentrationEffects()
        {
            // Loop through each creature. If they have a concentration ability active,
            // process it using that perk's OnConcentrationTick() method.
            for (int index = ConcentratingCreatures.Count - 1; index >= 0; index--)
            {
                var  creature      = ConcentratingCreatures.ElementAt(index);
                var  activeAbility = GetActiveConcentrationEffect(creature);
                int  perkID        = (int)activeAbility.Type;
                int  tier          = activeAbility.Tier;
                bool ended         = false;

                // If we have an invalid creature for any reason, remove it and move to the next one.
                if (!creature.IsValid || creature.CurrentHP <= 0 || activeAbility.Type == PerkType.Unknown)
                {
                    ConcentratingCreatures.RemoveAt(index);
                    continue;
                }

                // Track the current tick.
                int tick = creature.GetLocalInt("ACTIVE_CONCENTRATION_ABILITY_TICK") + 1;
                creature.SetLocalInt("ACTIVE_CONCENTRATION_ABILITY_TICK", tick);

                PerkFeat perkFeat = DataService.PerkFeat.GetByPerkIDAndLevelUnlocked(perkID, tier);

                // Are we ready to continue processing this concentration effect?
                if (tick % perkFeat.ConcentrationTickInterval != 0)
                {
                    continue;
                }

                // Get the perk handler, FP cost, and the target.
                var      handler   = PerkService.GetPerkHandler(perkID);
                int      fpCost    = handler.FPCost(creature, perkFeat.ConcentrationFPCost, tier);
                NWObject target    = creature.GetLocalObject("CONCENTRATION_TARGET");
                int      currentFP = GetCurrentFP(creature);
                int      maxFP     = GetMaxFP(creature);

                // Is the target still valid?
                if (!target.IsValid || target.CurrentHP <= 0)
                {
                    creature.SendMessage("Concentration effect has ended because your target is no longer valid.");
                    EndConcentrationEffect(creature);
                    ended = true;
                }
                // Does player have enough FP to maintain this concentration?
                else if (currentFP < fpCost)
                {
                    creature.SendMessage("Concentration effect has ended because you ran out of FP.");
                    EndConcentrationEffect(creature);
                    ended = true;
                }
                // Is the target still within range and in the same area?
                else if (creature.Area.Object != target.Area.Object ||
                         _.GetDistanceBetween(creature, target) > 50.0f)
                {
                    creature.SendMessage("Concentration effect has ended because your target has gone out of range.");
                    EndConcentrationEffect(creature);
                    ended = true;
                }
                // Otherwise deduct the required FP.
                else
                {
                    currentFP -= fpCost;
                }

                SetCurrentFP(creature, currentFP);

                // Send a FP status message if the effect ended or it's been six seconds since the last one.
                if (ended || tick % 6 == 0)
                {
                    creature.SendMessage(ColorTokenService.Custom("FP: " + currentFP + " / " + maxFP, 32, 223, 219));
                }

                // Run this individual perk's concentration tick method if it didn't end this tick.
                if (!ended && target.IsValid)
                {
                    handler.OnConcentrationTick(creature, target, tier, tick);
                }
            }
        }
Exemple #6
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);
            }
        }