private void ApplyCooldown(NWPlayer pc, CooldownCategory cooldown, IPerk ability) { float finalCooldown = ability.CooldownTime(pc, (float)cooldown.BaseCooldownTime); int cooldownSeconds = (int)finalCooldown; int cooldownMillis = (int)((finalCooldown - cooldownSeconds) * 100); PCCooldown pcCooldown = _db.PCCooldowns.Single(x => x.PlayerID == pc.GlobalID && x.CooldownCategoryID == cooldown.CooldownCategoryID); pcCooldown.DateUnlocked = DateTime.UtcNow.AddSeconds(cooldownSeconds).AddMilliseconds(cooldownMillis); _db.SaveChanges(); }
public static void ApplyCooldown(NWPlayer pc, CooldownCategory cooldown, IPerkHandler ability, int spellFeatID) { float finalCooldown = ability.CooldownTime(pc, (float)cooldown.BaseCooldownTime, spellFeatID); int cooldownSeconds = (int)finalCooldown; int cooldownMillis = (int)((finalCooldown - cooldownSeconds) * 100); PCCooldown pcCooldown = DataService.GetAll <PCCooldown>().Single(x => x.PlayerID == pc.GlobalID && x.CooldownCategoryID == cooldown.ID); pcCooldown.DateUnlocked = DateTime.UtcNow.AddSeconds(cooldownSeconds).AddMilliseconds(cooldownMillis); DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Update); }
public void GetByID_OneItem_ReturnsCooldownCategory() { // Arrange CooldownCategory entity = new CooldownCategory { ID = 1 }; // Act MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity)); // Assert Assert.AreNotSame(entity, _cache.GetByID(1)); }
public void GetByID_TwoItems_ReturnsCorrectObject() { // Arrange CooldownCategory entity1 = new CooldownCategory { ID = 1 }; CooldownCategory entity2 = new CooldownCategory { ID = 2 }; // Act MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity1)); MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity2)); // Assert Assert.AreNotSame(entity1, _cache.GetByID(1)); Assert.AreNotSame(entity2, _cache.GetByID(2)); }
public static void ApplyCooldown(NWCreature creature, CooldownCategory cooldown, IPerkHandler handler, int spellTier, float armorPenalty) { if (armorPenalty <= 0.0f) { armorPenalty = 1.0f; } // If player has a a cooldown recovery bonus on their equipment, apply that change now. if (creature.IsPlayer) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(creature.Object); if (effectiveStats.CooldownRecovery > 0) { armorPenalty -= (effectiveStats.CooldownRecovery * 0.01f); } } // There's a cap of 50% cooldown reduction from equipment. if (armorPenalty < 0.5f) { armorPenalty = 0.5f; } Console.WriteLine("armorPenalty final = " + armorPenalty); float finalCooldown = handler.CooldownTime(creature, (float)cooldown.BaseCooldownTime, spellTier) * armorPenalty; int cooldownSeconds = (int)finalCooldown; int cooldownMillis = (int)((finalCooldown - cooldownSeconds) * 100); DateTime unlockDate = DateTime.UtcNow.AddSeconds(cooldownSeconds).AddMilliseconds(cooldownMillis); if (creature.IsPlayer) { PCCooldown pcCooldown = DataService.Single <PCCooldown>(x => x.PlayerID == creature.GlobalID && x.CooldownCategoryID == cooldown.ID); pcCooldown.DateUnlocked = unlockDate; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Update); } else { string unlockDateString = unlockDate.ToString("yyyy-MM-dd hh:mm:ss"); creature.SetLocalString("ABILITY_COOLDOWN_ID_" + (int)handler.PerkType, unlockDateString); } }
public void GetByID_RemovedItem_ReturnsCorrectObject() { // Arrange CooldownCategory entity1 = new CooldownCategory { ID = 1 }; CooldownCategory entity2 = new CooldownCategory { ID = 2 }; // Act MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity1)); MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity2)); MessageHub.Instance.Publish(new OnCacheObjectDeleted <CooldownCategory>(entity1)); // Assert Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(1); }); Assert.AreNotSame(entity2, _cache.GetByID(2)); }
public void GetByID_NoItems_ThrowsKeyNotFoundException() { // Arrange CooldownCategory entity1 = new CooldownCategory { ID = 1 }; CooldownCategory entity2 = new CooldownCategory { ID = 2 }; // Act MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity1)); MessageHub.Instance.Publish(new OnCacheObjectSet <CooldownCategory>(entity2)); MessageHub.Instance.Publish(new OnCacheObjectDeleted <CooldownCategory>(entity1)); MessageHub.Instance.Publish(new OnCacheObjectDeleted <CooldownCategory>(entity2)); // Assert Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(1); }); Assert.Throws <KeyNotFoundException>(() => { _cache.GetByID(2); }); }
public bool Run(params object[] args) { NWPlayer pc = (NWPlayer)args[0]; string spellUUID = Convert.ToString(args[1]); int perkID = (int)args[2]; NWObject target = (NWObject)args[3]; int pcPerkLevel = (int)args[4]; Data.Entity.Perk entity = _data.Single <Data.Entity.Perk>(x => x.ID == perkID); CooldownCategory cooldown = _data.SingleOrDefault <CooldownCategory>(x => x.ID == entity.CooldownCategoryID); PerkExecutionType executionType = (PerkExecutionType)entity.ExecutionTypeID; return(App.ResolveByInterface <IPerk, bool>("Perk." + entity.ScriptName, perk => { if (pc.GetLocalInt(spellUUID) == (int)SpellStatusType.Interrupted || // Moved during casting pc.CurrentHP < 0 || pc.IsDead) // Or is dead/dying { pc.DeleteLocalInt(spellUUID); return false; } pc.DeleteLocalInt(spellUUID); if (executionType == PerkExecutionType.ForceAbility || executionType == PerkExecutionType.CombatAbility || executionType == PerkExecutionType.Stance) { perk.OnImpact(pc, target, pcPerkLevel); if (entity.CastAnimationID != null && entity.CastAnimationID > 0) { pc.AssignCommand(() => { _.ActionPlayAnimation((int)entity.CastAnimationID, 1f, 1f); }); } if (target.IsNPC) { _ability.ApplyEnmity(pc, (target.Object), entity); } } else if (executionType == PerkExecutionType.QueuedWeaponSkill) { _ability.HandleQueueWeaponSkill(pc, entity, perk); } // Adjust FP only if spell cost > 0 Data.Entity.Player pcEntity = _data.Single <Data.Entity.Player>(x => x.ID == pc.GlobalID); if (perk.FPCost(pc, entity.BaseFPCost) > 0) { pcEntity.CurrentFP = pcEntity.CurrentFP - perk.FPCost(pc, entity.BaseFPCost); _data.SubmitDataChange(pcEntity, DatabaseActionType.Update); pc.SendMessage(_color.Custom("FP: " + pcEntity.CurrentFP + " / " + pcEntity.MaxFP, 32, 223, 219)); } bool hasChainspell = _customEffect.DoesPCHaveCustomEffect(pc, CustomEffectType.Chainspell) && executionType == PerkExecutionType.ForceAbility; if (!hasChainspell) { // Mark cooldown on category _ability.ApplyCooldown(pc, cooldown, perk); } pc.IsBusy = false; pc.SetLocalInt(spellUUID, (int)SpellStatusType.Completed); return true; })); }
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); } }
public bool Run(params object[] args) { using (new Profiler(nameof(FinishAbilityUse))) { NWPlayer pc = (NWPlayer)args[0]; string spellUUID = Convert.ToString(args[1]); int perkID = (int)args[2]; NWObject target = (NWObject)args[3]; int pcPerkLevel = (int)args[4]; int featID = (int)args[5]; Data.Entity.Perk entity = DataService.Single <Data.Entity.Perk>(x => x.ID == perkID); PerkExecutionType executionType = (PerkExecutionType)entity.ExecutionTypeID; IPerkHandler perk = PerkService.GetPerkHandler(perkID); int?cooldownID = perk.CooldownCategoryID(pc, entity.CooldownCategoryID, featID); CooldownCategory cooldown = cooldownID == null ? null : DataService.SingleOrDefault <CooldownCategory>(x => x.ID == cooldownID); if (pc.GetLocalInt(spellUUID) == (int)SpellStatusType.Interrupted || // Moved during casting pc.CurrentHP < 0 || pc.IsDead) // Or is dead/dying { pc.DeleteLocalInt(spellUUID); return(false); } pc.DeleteLocalInt(spellUUID); if (executionType == PerkExecutionType.ForceAbility || executionType == PerkExecutionType.CombatAbility || executionType == PerkExecutionType.Stance) { perk.OnImpact(pc, target, pcPerkLevel, featID); if (entity.CastAnimationID != null && entity.CastAnimationID > 0) { pc.AssignCommand(() => { _.ActionPlayAnimation((int)entity.CastAnimationID, 1f, 1f); }); } if (target.IsNPC) { AbilityService.ApplyEnmity(pc, (target.Object), entity); } } else if (executionType == PerkExecutionType.QueuedWeaponSkill) { AbilityService.HandleQueueWeaponSkill(pc, entity, perk, featID); } // Adjust FP only if spell cost > 0 Data.Entity.Player pcEntity = DataService.Single <Data.Entity.Player>(x => x.ID == pc.GlobalID); int fpCost = perk.FPCost(pc, entity.BaseFPCost, featID); if (fpCost > 0) { pcEntity.CurrentFP = pcEntity.CurrentFP - fpCost; DataService.SubmitDataChange(pcEntity, DatabaseActionType.Update); pc.SendMessage(ColorTokenService.Custom("FP: " + pcEntity.CurrentFP + " / " + pcEntity.MaxFP, 32, 223, 219)); } bool hasChainspell = CustomEffectService.DoesPCHaveCustomEffect(pc, CustomEffectType.Chainspell) && executionType == PerkExecutionType.ForceAbility; if (!hasChainspell && cooldown != null) { // Mark cooldown on category AbilityService.ApplyCooldown(pc, cooldown, perk, featID); } pc.IsBusy = false; pc.SetLocalInt(spellUUID, (int)SpellStatusType.Completed); return(true); } }
private void CastSpell(NWPlayer pc, NWObject target, Data.Entities.Perk entity, IPerk perk, CooldownCategory cooldown) { string spellUUID = Guid.NewGuid().ToString(); int itemBonus = pc.CastingSpeed; float baseCastingTime = perk.CastingTime(pc, (float)entity.BaseCastingTime); float castingTime = baseCastingTime; // Casting Bonus % - Shorten casting time. if (itemBonus < 0) { float castingPercentageBonus = Math.Abs(itemBonus) * 0.01f; castingTime = castingTime - (castingTime * castingPercentageBonus); } // Casting Penalty % - Increase casting time. else if (itemBonus > 0) { float castingPercentageBonus = Math.Abs(itemBonus) * 0.01f; castingTime = castingTime + (castingTime * castingPercentageBonus); } if (castingTime < 0.5f) { castingTime = 0.5f; } // Heavy armor increases casting time by 2x the base casting time if (pc.Chest.CustomItemType == CustomItemType.HeavyArmor) { castingTime = baseCastingTime * 2; } if (_.GetActionMode(pc.Object, ACTION_MODE_STEALTH) == 1) { _.SetActionMode(pc.Object, ACTION_MODE_STEALTH, 0); } _.ClearAllActions(); _biowarePosition.TurnToFaceObject(target, pc); _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, _.EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD), pc.Object, castingTime + 0.2f); float animationTime = castingTime; pc.AssignCommand(() => _.ActionPlayAnimation(ANIMATION_LOOPING_CONJURE1, 1.0f, animationTime - 0.1f)); pc.IsBusy = true; CheckForSpellInterruption(pc, spellUUID, pc.Position); pc.SetLocalInt(spellUUID, SPELL_STATUS_STARTED); _nwnxPlayer.StartGuiTimingBar(pc, (int)castingTime, ""); pc.DelayCommand(() => { if (pc.GetLocalInt(spellUUID) == SPELL_STATUS_INTERRUPTED || // Moved during casting pc.CurrentHP < 0 || pc.IsDead) // Or is dead/dying { pc.DeleteLocalInt(spellUUID); pc.SendMessage("Your spell has been interrupted."); return; } pc.DeleteLocalInt(spellUUID); if ((PerkExecutionType)entity.ExecutionTypeID == PerkExecutionType.Spell || (PerkExecutionType)entity.ExecutionTypeID == PerkExecutionType.CombatAbility) { perk.OnImpact(pc, target); } else { HandleQueueWeaponSkill(pc, entity, perk); } // Adjust mana only if spell cost > 0 PlayerCharacter pcEntity = _db.PlayerCharacters.Single(x => x.PlayerID == pc.GlobalID); if (perk.ManaCost(pc, entity.BaseManaCost) > 0) { pcEntity.CurrentMana = pcEntity.CurrentMana - perk.ManaCost(pc, entity.BaseManaCost); _db.SaveChanges(); pc.SendMessage(_color.Custom("Mana: " + pcEntity.CurrentMana + " / " + pcEntity.MaxMana, 32, 223, 219)); } if (_random.Random(100) + 1 <= 3) { _food.DecreaseHungerLevel(pc, 1); } // Mark cooldown on category ApplyCooldown(pc, cooldown, perk); pc.IsBusy = false; }, castingTime + 0.5f); }