/// <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); }
private static void OnModuleUseFeat() { NWPlayer pc = Object.OBJECT_SELF; NWCreature target = NWNXEvents.OnFeatUsed_GetTarget().Object; int featID = NWNXEvents.OnFeatUsed_GetFeatID(); var perkFeat = DataService.SingleOrDefault <PerkFeat>(x => x.FeatID == featID); if (perkFeat == null) { return; } Data.Entity.Perk perk = DataService.GetAll <Data.Entity.Perk>().SingleOrDefault(x => x.ID == perkFeat.PerkID); if (perk == null) { return; } // Check to see if we are a spaceship. Spaceships can't use abilities... if (pc.GetLocalInt("IS_SHIP") > 0 || pc.GetLocalInt("IS_GUNNER") > 0) { pc.SendMessage("You cannot use that ability while piloting a ship."); return; } var perkAction = PerkService.GetPerkHandler(perkFeat.PerkID); Player playerEntity = DataService.Get <Player>(pc.GlobalID); int pcPerkLevel = PerkService.GetPCPerkLevel(pc, perk.ID); // If player is disabling an existing stance, remove that effect. if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { PCCustomEffect stanceEffect = DataService.SingleOrDefault <PCCustomEffect>(x => x.StancePerkID == perk.ID && x.PlayerID == pc.GlobalID); if (stanceEffect != null) { if (CustomEffectService.RemoveStance(pc)) { return; } } } if (pcPerkLevel <= 0) { pc.SendMessage("You do not meet the prerequisites to use this ability."); return; } if (perkAction.IsHostile() && target.IsPlayer) { if (!PVPSanctuaryService.IsPVPAttackAllowed(pc, target.Object)) { return; } } if (pc.Area.Resref != target.Area.Resref || _.LineOfSightObject(pc.Object, target.Object) == 0) { pc.SendMessage("You cannot see your target."); return; } if (!perkAction.CanCastSpell(pc, target)) { pc.SendMessage(perkAction.CannotCastSpellMessage(pc, target) ?? "That ability cannot be used at this time."); return; } int fpCost = perkAction.FPCost(pc, perkAction.FPCost(pc, perk.BaseFPCost, featID), featID); if (playerEntity.CurrentFP < fpCost) { pc.SendMessage("You do not have enough FP. (Required: " + fpCost + ". You have: " + playerEntity.CurrentFP + ")"); return; } if (pc.IsBusy || pc.CurrentHP <= 0) { pc.SendMessage("You are too busy to activate that ability."); return; } // Check cooldown int? cooldownCategoryID = perkAction.CooldownCategoryID(pc, perk.CooldownCategoryID, featID); PCCooldown pcCooldown = DataService.GetAll <PCCooldown>().SingleOrDefault(x => x.PlayerID == pc.GlobalID && x.CooldownCategoryID == cooldownCategoryID); if (pcCooldown == null) { pcCooldown = new PCCooldown { CooldownCategoryID = Convert.ToInt32(cooldownCategoryID), DateUnlocked = DateTime.UtcNow.AddSeconds(-1), PlayerID = pc.GlobalID }; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Insert); } DateTime unlockDateTime = pcCooldown.DateUnlocked; DateTime now = DateTime.UtcNow; if (unlockDateTime > now) { string timeToWait = TimeService.GetTimeToWaitLongIntervals(now, unlockDateTime, false); pc.SendMessage("That ability can be used in " + timeToWait + "."); return; } // Force Abilities (aka Spells) if (perk.ExecutionTypeID == (int)PerkExecutionType.ForceAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_FORCE); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.ForceAbility, featID); } // Combat Abilities else if (perk.ExecutionTypeID == (int)PerkExecutionType.CombatAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.CombatAbility, featID); } // Queued Weapon Skills else if (perk.ExecutionTypeID == (int)PerkExecutionType.QueuedWeaponSkill) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); HandleQueueWeaponSkill(pc, perk, perkAction, featID); } // Stances else if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_COMBATABILITY); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.Stance, featID); } }