private static void OnHitCastSpell() { NWPlayer oPC = Object.OBJECT_SELF; if (!oPC.IsValid) { return; } NWObject oTarget = _.GetSpellTargetObject(); NWItem oItem = _.GetSpellCastItem(); // If this method was triggered by our own armor (from getting hit), return. if (oItem.BaseItemType == BASE_ITEM_ARMOR) { return; } // Flag this attack as physical so that the damage scripts treat it properly. LoggingService.Trace(TraceComponent.LastAttack, "Setting attack type from " + oPC.GlobalID + " against " + _.GetName(oTarget) + " to physical (" + ATTACK_PHYSICAL.ToString() + ")"); oTarget.SetLocalInt(LAST_ATTACK + oPC.GlobalID, ATTACK_PHYSICAL); HandleGrenadeProficiency(oPC, oTarget); HandlePlasmaCellPerk(oPC, oTarget); int activeWeaponSkillID = oPC.GetLocalInt("ACTIVE_WEAPON_SKILL"); if (activeWeaponSkillID <= 0) { return; } int activeWeaponSkillFeatID = oPC.GetLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID"); if (activeWeaponSkillFeatID < 0) { activeWeaponSkillFeatID = -1; } PCPerk entity = DataService.GetAll <PCPerk>().Single(x => x.PlayerID == oPC.GlobalID && x.PerkID == activeWeaponSkillID); var perk = DataService.Get <Data.Entity.Perk>(entity.PerkID); var handler = PerkService.GetPerkHandler(activeWeaponSkillID); if (handler.CanCastSpell(oPC, oTarget)) { handler.OnImpact(oPC, oTarget, entity.PerkLevel, activeWeaponSkillFeatID); if (oTarget.IsNPC) { ApplyEnmity(oPC, oTarget.Object, perk); } } else { oPC.SendMessage(handler.CannotCastSpellMessage(oPC, oTarget) ?? "That ability cannot be used at this time."); } oPC.DeleteLocalString("ACTIVE_WEAPON_SKILL_UUID"); oPC.DeleteLocalInt("ACTIVE_WEAPON_SKILL"); oPC.DeleteLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID"); }
/// <summary> /// Processes all feats which are linked to perks. /// </summary> private static void OnModuleUseFeat() { // Activator is the creature who used the feat. // Target is who the activator selected to use this feat on. NWCreature activator = _.OBJECT_SELF; NWCreature target = NWNXObject.StringToObject(NWNXEvents.GetEventData("TARGET_OBJECT_ID")); var featID = (Feat)Convert.ToInt32(NWNXEvents.GetEventData("FEAT_ID")); // Ensure this perk feat can be activated. if (!CanUsePerkFeat(activator, target, featID)) { return; } // Retrieve information necessary for activation of perk feat. var perkFeat = DataService.PerkFeat.GetByFeatID((int)featID); Data.Entity.Perk perk = DataService.Perk.GetByID(perkFeat.PerkID); int creaturePerkLevel = PerkService.GetCreaturePerkLevel(activator, perk.ID); var handler = PerkService.GetPerkHandler(perkFeat.PerkID); SendAOEMessage(activator, activator.Name + " readies " + perk.Name + "."); // Force Abilities (aka Spells) if (perk.ExecutionTypeID == PerkExecutionType.ForceAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_FORCE); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.ForceAbility, perkFeat.PerkLevelUnlocked); } // Combat Abilities else if (perk.ExecutionTypeID == PerkExecutionType.CombatAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_PHYSICAL); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.CombatAbility, perkFeat.PerkLevelUnlocked); } // Queued Weapon Skills else if (perk.ExecutionTypeID == PerkExecutionType.QueuedWeaponSkill) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_PHYSICAL); HandleQueueWeaponSkill(activator, perk, handler, featID); } // Stances else if (perk.ExecutionTypeID == PerkExecutionType.Stance) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_COMBATABILITY); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.Stance, perkFeat.PerkLevelUnlocked); } // Concentration Abilities else if (perk.ExecutionTypeID == PerkExecutionType.ConcentrationAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_FORCE); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.ConcentrationAbility, perkFeat.PerkLevelUnlocked); } }
public static void ApplyEnmity(NWCreature attacker, NWCreature target, Data.Entity.Perk perk) { switch ((EnmityAdjustmentRuleType)perk.EnmityAdjustmentRuleID) { case EnmityAdjustmentRuleType.AllTaggedTargets: EnmityService.AdjustEnmityOnAllTaggedCreatures(attacker, perk.Enmity); break; case EnmityAdjustmentRuleType.TargetOnly: if (target.IsValid) { EnmityService.AdjustEnmity(target, attacker, perk.Enmity); } break; case EnmityAdjustmentRuleType.Custom: var handler = PerkService.GetPerkHandler(perk.ID); handler.OnCustomEnmityRule(attacker, perk.Enmity); break; } }
/// <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 OnHitCastSpell() { NWPlayer oPC = _.OBJECT_SELF; if (!oPC.IsValid) { return; } NWObject oTarget = _.GetSpellTargetObject(); NWItem oItem = _.GetSpellCastItem(); // If this method was triggered by our own armor (from getting hit), return. if (oItem.BaseItemType == BaseItem.Armor) { return; } // Flag this attack as physical so that the damage scripts treat it properly. LoggingService.Trace(TraceComponent.LastAttack, "Setting attack type from " + oPC.GlobalID + " against " + _.GetName(oTarget) + " to physical (" + ATTACK_PHYSICAL.ToString() + ")"); oTarget.SetLocalInt(LAST_ATTACK + oPC.GlobalID, ATTACK_PHYSICAL); HandleGrenadeProficiency(oPC, oTarget); HandlePlasmaCellPerk(oPC, oTarget); int activeWeaponSkillID = oPC.GetLocalInt("ACTIVE_WEAPON_SKILL"); if (activeWeaponSkillID <= 0) { return; } int activeWeaponSkillFeatID = oPC.GetLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID"); if (activeWeaponSkillFeatID < 0) { activeWeaponSkillFeatID = -1; } PCPerk entity = DataService.PCPerk.GetByPlayerAndPerkID(oPC.GlobalID, activeWeaponSkillID); var perk = DataService.Perk.GetByID(entity.PerkID); var perkFeat = DataService.PerkFeat.GetByFeatID(activeWeaponSkillFeatID); var handler = PerkService.GetPerkHandler(activeWeaponSkillID); string canCast = handler.CanCastSpell(oPC, oTarget, perkFeat.PerkLevelUnlocked); if (string.IsNullOrWhiteSpace(canCast)) { handler.OnImpact(oPC, oTarget, entity.PerkLevel, perkFeat.PerkLevelUnlocked); if (oTarget.IsNPC) { ApplyEnmity(oPC, oTarget.Object, perk); } } else { oPC.SendMessage(canCast); } oPC.DeleteLocalString("ACTIVE_WEAPON_SKILL_UUID"); oPC.DeleteLocalInt("ACTIVE_WEAPON_SKILL"); oPC.DeleteLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID"); }
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); } } }
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); } }