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