Exemple #1
0
        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);
        }
Exemple #2
0
        private static void RegisterPerkHandlers()
        {
            // Use reflection to get all of IPerkHandler implementations.
            var classes = Assembly.GetCallingAssembly().GetTypes()
                          .Where(p => typeof(IPerkHandler).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract).ToArray();

            foreach (var type in classes)
            {
                IPerkHandler instance = Activator.CreateInstance(type) as IPerkHandler;
                if (instance == null)
                {
                    throw new NullReferenceException("Unable to activate instance of type: " + type);
                }
                _perkHandlers.Add(instance.PerkType, instance);
            }
        }
        private static void HandleQueueWeaponSkill(NWCreature activator, Data.Entity.Perk entity, IPerkHandler ability, Feat spellFeatID)
        {
            var    perkFeat           = DataService.PerkFeat.GetByFeatID((int)spellFeatID);
            int?   cooldownCategoryID = ability.CooldownCategoryID(activator, entity.CooldownCategoryID, perkFeat.PerkLevelUnlocked);
            var    cooldownCategory   = DataService.CooldownCategory.GetByID(Convert.ToInt32(cooldownCategoryID));
            string queueUUID          = Guid.NewGuid().ToString();

            activator.SetLocalInt("ACTIVE_WEAPON_SKILL", entity.ID);
            activator.SetLocalString("ACTIVE_WEAPON_SKILL_UUID", queueUUID);
            activator.SetLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID", (int)spellFeatID);
            activator.SendMessage("Weapon skill '" + entity.Name + "' queued for next attack.");
            SendAOEMessage(activator, activator.Name + " readies weapon skill '" + entity.Name + "'.");

            ApplyCooldown(activator, cooldownCategory, ability, perkFeat.PerkLevelUnlocked, 0.0f);

            // Player must attack within 30 seconds after queueing or else it wears off.
            _.DelayCommand(30f, () =>
            {
                if (activator.GetLocalString("ACTIVE_WEAPON_SKILL_UUID") == queueUUID)
                {
                    activator.DeleteLocalInt("ACTIVE_WEAPON_SKILL");
                    activator.DeleteLocalString("ACTIVE_WEAPON_SKILL_UUID");
                    activator.DeleteLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID");
                    activator.SendMessage("Your weapon skill '" + entity.Name + "' is no longer queued.");
                    SendAOEMessage(activator, activator.Name + " no longer has weapon skill '" + entity.Name + "' readied.");
                }
            });
        }
        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;
            }

            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.PCCooldown.GetByPlayerAndCooldownCategoryID(creature.GlobalID, 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);
            }
        }
        private static void ActivateAbility(
            NWCreature activator,
            NWObject target,
            Data.Entity.Perk entity,
            IPerkHandler perkHandler,
            int pcPerkLevel,
            PerkExecutionType executionType,
            int spellTier)
        {
            string uuid = Guid.NewGuid().ToString();
            float  baseActivationTime = perkHandler.CastingTime(activator, (float)entity.BaseCastingTime, spellTier);
            float  activationTime     = baseActivationTime;
            var    vfxID       = VisualEffect.Invalid;
            var    animationID = Animation.Invalid;

            if (baseActivationTime > 0f && activationTime < 1.0f)
            {
                activationTime = 1.0f;
            }

            // Force ability armor penalties
            float armorPenalty = 0.0f;

            if (executionType == PerkExecutionType.ForceAbility ||
                executionType == PerkExecutionType.ConcentrationAbility)
            {
                string penaltyMessage = string.Empty;
                foreach (var item in activator.EquippedItems)
                {
                    if (item.CustomItemType == CustomItemType.HeavyArmor)
                    {
                        armorPenalty   = 2;
                        penaltyMessage = "Heavy armor slows your force cooldown by 100%.";
                        break;
                    }
                    else if (item.CustomItemType == CustomItemType.LightArmor)
                    {
                        armorPenalty   = 1.25f;
                        penaltyMessage = "Light armor slows your force cooldown by 25%.";
                    }
                }

                // If there's an armor penalty, send a message to the player.
                if (armorPenalty > 0.0f)
                {
                    activator.SendMessage(penaltyMessage);
                }
            }

            // If player is in stealth mode, force them out of stealth mode.
            if (_.GetActionMode(activator.Object, ActionMode.Stealth))
            {
                _.SetActionMode(activator.Object, ActionMode.Stealth, false);
            }

            // Make the player face their target.
            _.ClearAllActions();
            BiowarePosition.TurnToFaceObject(target, activator);

            // Force and Concentration Abilities will display a visual effect during the casting process.
            if (executionType == PerkExecutionType.ForceAbility ||
                executionType == PerkExecutionType.ConcentrationAbility)
            {
                vfxID       = VisualEffect.Vfx_Dur_Iounstone_Yellow;
                animationID = Animation.LoopingConjure1;
            }

            if (executionType == PerkExecutionType.ConcentrationAbility)
            {
                activator.SetLocalObject("CONCENTRATION_TARGET", target);
            }

            // If a VFX ID has been specified, play that effect instead of the default one.
            if (vfxID != VisualEffect.Invalid)
            {
                var vfx = _.EffectVisualEffect(vfxID);
                vfx = _.TagEffect(vfx, "ACTIVATION_VFX");
                _.ApplyEffectToObject(DurationType.Temporary, vfx, activator.Object, activationTime + 0.2f);
            }

            // If an animation has been specified, make the player play that animation now.
            // bypassing if perk is throw saber due to couldn't get the animation to work via db table edit
            if (animationID != Animation.Invalid && entity.ID != (int)PerkType.ThrowSaber)
            {
                activator.AssignCommand(() => _.ActionPlayAnimation(animationID, 1.0f, activationTime - 0.1f));
            }

            // Mark player as busy. Busy players can't take other actions (crafting, harvesting, etc.)
            activator.IsBusy = true;

            // Non-players can't be interrupted via movement.
            if (!activator.IsPlayer)
            {
                // Begin the check for spell interruption. If the activator moves, the spell will be canceled.
                CheckForSpellInterruption(activator, uuid, activator.Position);
            }

            activator.SetLocalInt(uuid, (int)SpellStatusType.Started);

            // If there's a casting delay, display a timing bar on-screen.
            if (activationTime > 0)
            {
                NWNXPlayer.StartGuiTimingBar(activator, (int)activationTime, string.Empty);
            }

            // Run the FinishAbilityUse event at the end of the activation time.
            int perkID = entity.ID;

            var @event = new OnFinishAbilityUse(activator, uuid, perkID, target, pcPerkLevel, spellTier, armorPenalty);

            activator.DelayEvent(activationTime + 0.2f, @event);
        }
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);
            }
        }
        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);
            }
        }
Exemple #8
0
        public static void HandleQueueWeaponSkill(NWPlayer pc, Data.Entity.Perk entity, IPerkHandler ability, int spellFeatID)
        {
            int?   cooldownCategoryID = ability.CooldownCategoryID(pc, entity.CooldownCategoryID, spellFeatID);
            var    cooldownCategory   = DataService.Get <CooldownCategory>(cooldownCategoryID);
            string queueUUID          = Guid.NewGuid().ToString();

            pc.SetLocalInt("ACTIVE_WEAPON_SKILL", entity.ID);
            pc.SetLocalString("ACTIVE_WEAPON_SKILL_UUID", queueUUID);
            pc.SetLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID", spellFeatID);
            pc.SendMessage("Weapon skill '" + entity.Name + "' queued for next attack.");

            ApplyCooldown(pc, cooldownCategory, ability, spellFeatID);

            // Player must attack within 30 seconds after queueing or else it wears off.
            _.DelayCommand(30f, () =>
            {
                if (pc.GetLocalString("ACTIVE_WEAPON_SKILL_UUID") == queueUUID)
                {
                    pc.DeleteLocalInt("ACTIVE_WEAPON_SKILL");
                    pc.DeleteLocalString("ACTIVE_WEAPON_SKILL_UUID");
                    pc.DeleteLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID");
                    pc.SendMessage("Your weapon skill '" + entity.Name + "' is no longer queued.");
                }
            });
        }
Exemple #9
0
        private static void ActivateAbility(NWPlayer pc,
                                            NWObject target,
                                            Data.Entity.Perk entity,
                                            IPerkHandler perkHandler,
                                            int pcPerkLevel,
                                            PerkExecutionType executionType,
                                            int spellFeatID)
        {
            string uuid               = Guid.NewGuid().ToString();
            var    effectiveStats     = PlayerStatService.GetPlayerItemEffectiveStats(pc);
            int    itemBonus          = effectiveStats.CastingSpeed;
            float  baseActivationTime = perkHandler.CastingTime(pc, (float)entity.BaseCastingTime, spellFeatID);
            float  activationTime     = baseActivationTime;
            int    vfxID              = -1;
            int    animationID        = -1;

            // Activation Bonus % - Shorten activation time.
            if (itemBonus > 0)
            {
                float activationBonus = Math.Abs(itemBonus) * 0.01f;
                activationTime = activationTime - activationTime * activationBonus;
            }
            // Activation Penalty % - Increase activation time.
            else if (itemBonus < 0)
            {
                float activationPenalty = Math.Abs(itemBonus) * 0.01f;
                activationTime = activationTime + activationTime * activationPenalty;
            }

            if (baseActivationTime > 0f && activationTime < 1.0f)
            {
                activationTime = 1.0f;
            }

            // Force ability armor penalties
            if (executionType == PerkExecutionType.ForceAbility)
            {
                float  armorPenalty   = 0.0f;
                string penaltyMessage = string.Empty;
                foreach (var item in pc.EquippedItems)
                {
                    if (item.CustomItemType == CustomItemType.HeavyArmor)
                    {
                        armorPenalty   = 2;
                        penaltyMessage = "Heavy armor slows your force activation speed by 100%.";
                        break;
                    }
                    else if (item.CustomItemType == CustomItemType.LightArmor)
                    {
                        armorPenalty   = 1.25f;
                        penaltyMessage = "Light armor slows your force activation speed by 25%.";
                    }
                }

                if (armorPenalty > 0.0f)
                {
                    activationTime = baseActivationTime * armorPenalty;
                    pc.SendMessage(penaltyMessage);
                }
            }

            if (_.GetActionMode(pc.Object, ACTION_MODE_STEALTH) == 1)
            {
                _.SetActionMode(pc.Object, ACTION_MODE_STEALTH, 0);
            }

            _.ClearAllActions();
            BiowarePosition.TurnToFaceObject(target, pc);

            if (executionType == PerkExecutionType.ForceAbility)
            {
                vfxID       = VFX_DUR_IOUNSTONE_YELLOW;
                animationID = ANIMATION_LOOPING_CONJURE1;
            }

            if (vfxID > -1)
            {
                var vfx = _.EffectVisualEffect(vfxID);
                vfx = _.TagEffect(vfx, "ACTIVATION_VFX");
                _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, vfx, pc.Object, activationTime + 0.2f);
            }

            if (animationID > -1)
            {
                pc.AssignCommand(() => _.ActionPlayAnimation(animationID, 1.0f, activationTime - 0.1f));
            }

            pc.IsBusy = true;
            CheckForSpellInterruption(pc, uuid, pc.Position);
            pc.SetLocalInt(uuid, (int)SpellStatusType.Started);

            NWNXPlayer.StartGuiTimingBar(pc, (int)activationTime, "");

            int perkID = entity.ID;

            pc.DelayEvent <FinishAbilityUse>(activationTime + 0.2f,
                                             pc,
                                             uuid,
                                             perkID,
                                             target,
                                             pcPerkLevel,
                                             spellFeatID);
        }