/// <summary> /// Gives GP to a player for a given guild. /// If the baseAmount is less than 1, nothing will happen. /// If the baseAmount is greater than 1000, the baseAmount will be set to 1000. /// If the player ranks up, a message will be sent to him/her and an OnPlayerGuildRankUp event will be published. /// </summary> /// <param name="player">The player to give GP.</param> /// <param name="guild">The guild this GP will apply to.</param> /// <param name="baseAmount">The baseAmount of GP to grant.</param> public static void GiveGuildPoints(NWPlayer player, GuildType guild, int baseAmount) { if (baseAmount <= 0) { return; } // Clamp max GP baseAmount if (baseAmount > 1000) { baseAmount = 1000; } // Grant a bonus based on the player's guild relations perk rank. Always offset by 1 so we don't end up with multiplication by zero. int perkBonus = PerkService.GetCreaturePerkLevel(player, PerkType.GuildRelations) + 1; baseAmount *= perkBonus; var dbGuild = DataService.Get <Guild>((int)guild); var pcGP = DataService.Single <PCGuildPoint>(x => x.GuildID == (int)guild && x.PlayerID == player.GlobalID); pcGP.Points += baseAmount; // Clamp player GP to the highest rank. int maxRank = RankProgression.Keys.Max(); int maxGP = RankProgression[maxRank]; if (pcGP.Points >= maxGP) { pcGP.Points = maxGP - 1; } // Notify player how much GP they earned. player.SendMessage("You earned " + baseAmount + " " + dbGuild.Name + " guild points."); // Are we able to rank up? bool didRankUp = false; if (pcGP.Rank < maxRank) { // Is it time for a rank up? int nextRank = RankProgression[pcGP.Rank]; if (pcGP.Points >= nextRank) { // Let's do a rank up. pcGP.Rank++; player.SendMessage(ColorTokenService.Green("You've reached rank " + pcGP.Rank + " in the " + dbGuild.Name + "!")); didRankUp = true; } } // Submit changes to the DB/cache. DataService.SubmitDataChange(pcGP, DatabaseActionType.Update); // If the player ranked up, publish an event saying so. if (didRankUp) { MessageHub.Instance.Publish(new OnPlayerGuildRankUp(player.GlobalID, pcGP.Rank)); } }
private static void HandleRegenerationTick(NWPlayer oPC, Data.Entity.Player entity) { entity.RegenerationTick = entity.RegenerationTick - 1; int rate = 20; int amount = entity.HPRegenerationAmount; if (entity.RegenerationTick <= 0) { if (oPC.CurrentHP < oPC.MaxHP) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(oPC); // CON bonus int con = oPC.ConstitutionModifier; if (con > 0) { amount += con; } amount += effectiveStats.HPRegen; if (oPC.Chest.CustomItemType == CustomItemType.HeavyArmor) { int sturdinessLevel = PerkService.GetPCPerkLevel(oPC, PerkType.Sturdiness); if (sturdinessLevel > 0) { amount += sturdinessLevel + 1; } } _.ApplyEffectToObject(_.DURATION_TYPE_INSTANT, _.EffectHeal(amount), oPC.Object); } entity.RegenerationTick = rate; } }
private static void HandleFPRegenerationTick(NWPlayer oPC, Player entity) { entity.CurrentFPTick = entity.CurrentFPTick - 1; int rate = 5; int amount = 1; if (entity.CurrentFPTick <= 0) { if (entity.CurrentFP < entity.MaxFP) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(oPC); // CHA bonus int cha = oPC.CharismaModifier; if (cha > 0) { amount += cha; } amount += effectiveStats.FPRegen; if (oPC.Chest.CustomItemType == CustomItemType.ForceArmor) { int clarityLevel = PerkService.GetCreaturePerkLevel(oPC, PerkType.Clarity); if (clarityLevel > 0) { amount += clarityLevel + 1; } } entity = AbilityService.RestorePlayerFP(oPC, amount, entity); } entity.CurrentFPTick = rate; } }
private static void HandleRegenerationTick(NWPlayer oPC, Player entity) { entity.RegenerationTick = entity.RegenerationTick - 1; int rate = 5; int amount = entity.HPRegenerationAmount; if (entity.RegenerationTick <= 0) { if (oPC.CurrentHP < oPC.MaxHP) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(oPC); // CON bonus int con = (oPC.ConstitutionModifier / 2); if (con > 0) { amount += con; } amount += effectiveStats.HPRegen; if (oPC.Chest.CustomItemType == CustomItemType.HeavyArmor) { int sturdinessLevel = PerkService.GetCreaturePerkLevel(oPC, PerkType.Sturdiness); if (sturdinessLevel > 0) { amount += sturdinessLevel + 1; } } ApplyEffectToObject(DurationType.Instant, EffectHeal(amount), oPC.Object); } entity.RegenerationTick = rate; } }
public static int CalculateReassemblyChance(NWPlayer player, int penalty) { const int BaseChance = 70; int harvesting = SkillService.GetPCSkillRank(player, SkillType.Harvesting); var itemBonuses = PlayerStatService.GetPlayerItemEffectiveStats(player); int perkLevel = PerkService.GetCreaturePerkLevel(player, PerkType.MolecularReassemblyProficiency); // Calculate the base chance after factoring in skills, perks, and items. int categoryChance = (int)(BaseChance + (harvesting / 2.5f) + perkLevel * 10 + itemBonuses.Harvesting / 3f); // Reduce the chance by the penalty. This penalty is generally determined by how many properties were already // applied during this batch. categoryChance -= penalty; // Keep bounds between 0 and 100 if (categoryChance < 0) { return(0); } else if (categoryChance > 100) { return(100); } else { return(categoryChance); } }
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 int CalculateGPReward(NWPlayer player, GuildType guild, int baseAmount) { var pcGP = DataService.PCGuildPoint.GetByPlayerIDAndGuildID(player.GlobalID, (int)guild); float rankBonus = 0.25f * pcGP.Rank; // Grant a bonus based on the player's guild relations perk rank. int perkBonus = PerkService.GetCreaturePerkLevel(player, PerkType.GuildRelations); baseAmount = baseAmount + (perkBonus * baseAmount); return(baseAmount + (int)(baseAmount * rankBonus)); }
public static float CalculateCraftingDelay(NWPlayer oPC, int skillID) { int atmosphere = CalculateAreaAtmosphereBonus(oPC.Area); PerkType perkType; float adjustedSpeed = 1.0f; perkType = PerkType.SpeedyCrafting; int perkLevel = PerkService.GetCreaturePerkLevel(oPC, perkType); // Each perk level reduces crafting speed by 10%. switch (perkLevel) { case 1: adjustedSpeed = 0.9f; break; case 2: adjustedSpeed = 0.8f; break; case 3: adjustedSpeed = 0.7f; break; case 4: adjustedSpeed = 0.6f; break; case 5: adjustedSpeed = 0.5f; break; case 6: adjustedSpeed = 0.4f; break; case 7: adjustedSpeed = 0.3f; break; case 8: adjustedSpeed = 0.2f; break; case 9: adjustedSpeed = 0.1f; break; case 10: adjustedSpeed = 0.01f; break; } // Workshops with an atmosphere bonus decrease crafting time. if (atmosphere >= 45) { adjustedSpeed -= 0.2f; } else if (atmosphere >= 5) { adjustedSpeed -= 0.1f; } // Never fall below 1% of overall crafting time. if (adjustedSpeed <= 0.01f) { adjustedSpeed = 0.01f; } return(BaseCraftDelay * adjustedSpeed); }
private static void HandleApplySneakAttackDamage() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWObject damager = data.Damager; int sneakAttackType = damager.GetLocalInt("SNEAK_ATTACK_ACTIVE"); if (damager.IsPlayer && sneakAttackType > 0) { NWPlayer player = damager.Object; NWCreature target = _.OBJECT_SELF; var pcPerk = PerkService.GetPCPerkByID(damager.GlobalID, (int)PerkType.SneakAttack); int perkRank = pcPerk?.PerkLevel ?? 0; int perkBonus = 1; // Rank 4 increases damage bonus by 2x (total: 3x) if (perkRank == 4) { perkBonus = 2; } float perkRate; if (sneakAttackType == 1) // Player is behind target. { perkRate = 1.0f * perkBonus; } else // Player is anywhere else. { perkRate = 0.5f * perkBonus; } var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(player); float damageRate = 1.0f + perkRate + effectiveStats.SneakAttack * 0.05f; data.Base = (int)(data.Base * damageRate); if (target.IsNPC) { EnmityService.AdjustEnmity(target, player, 5 * data.Base); } NWNXDamage.SetDamageEventData(data); } damager.DeleteLocalInt("SNEAK_ATTACK_ACTIVE"); }
private static int EffectiveMaxFP(NWPlayer player, EffectiveItemStats stats) { int fp = 20; fp += (player.IntelligenceModifier + player.WisdomModifier + player.CharismaModifier) * 5; fp += PerkService.GetPCPerkLevel(player, PerkType.FP) * 5; fp += stats.FP; if (fp < 0) { fp = 0; } return(fp); }
private static int EffectiveMaxHitPoints(NWPlayer player, EffectiveItemStats stats) { int hp = 25 + player.ConstitutionModifier * 5; float effectPercentBonus = CustomEffectService.CalculateEffectHPBonusPercent(player); hp += PerkService.GetPCPerkLevel(player, PerkType.Health) * 5; hp += stats.HP; hp = hp + (int)(hp * effectPercentBonus); if (hp > 1275) { hp = 1275; } if (hp < 20) { hp = 20; } return(hp); }
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; } }
private static void HandleGrenadeProficiency(NWPlayer oPC, NWObject target) { NWItem weapon = _.GetSpellCastItem(); if (weapon.BaseItemType != BaseItem.Grenade) { return; } int perkLevel = PerkService.GetCreaturePerkLevel(oPC, PerkType.GrenadeProficiency); int chance = 10 * perkLevel; float duration; switch (perkLevel) { case 1: case 2: case 3: case 4: duration = 6; break; case 5: case 6: case 7: case 8: case 9: duration = 9; break; default: return; } if (RandomService.D100(1) <= chance) { _.ApplyEffectToObject(DurationType.Temporary, AbilityService.EffectKnockdown(target, duration), target, duration); } }
private static void HandleGrenadeProficiency(NWPlayer oPC, NWObject target) { NWItem weapon = _.GetSpellCastItem(); if (weapon.BaseItemType != BASE_ITEM_GRENADE) { return; } int perkLevel = PerkService.GetPCPerkLevel(oPC, PerkType.GrenadeProficiency); int chance = 10 * perkLevel; float duration; switch (perkLevel) { case 1: case 2: case 3: case 4: duration = 6; break; case 5: case 6: case 7: case 8: case 9: duration = 9; break; default: return; } if (RandomService.D100(1) <= chance) { _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, _.EffectKnockdown(), target, duration); } }
public static float CalculateCraftingDelay(NWPlayer oPC, int skillID) { int atmosphere = CalculateAreaAtmosphereBonus(oPC.Area); PerkType perkType; float adjustedSpeed = 1.0f; SkillType skillType = (SkillType)skillID; // Identify which perk to use for this skill. if (skillType == SkillType.Weaponsmith) { perkType = PerkType.SpeedyWeaponsmith; } else if (skillType == SkillType.Armorsmith) { perkType = PerkType.SpeedyArmorsmith; } else if (skillType == SkillType.Cooking) { perkType = PerkType.SpeedyCooking; } else if (skillType == SkillType.Engineering) { perkType = PerkType.SpeedyEngineering; } else if (skillType == SkillType.Fabrication) { perkType = PerkType.SpeedyFabrication; } else if (skillType == SkillType.Medicine) { perkType = PerkType.SpeedyMedicine; } else if (skillType == SkillType.Harvesting) { perkType = PerkType.SpeedyReassembly; } else { return(BaseCraftDelay); } int perkLevel = PerkService.GetPCPerkLevel(oPC, perkType); // Each perk level reduces crafting speed by 10%. switch (perkLevel) { case 1: adjustedSpeed = 0.9f; break; case 2: adjustedSpeed = 0.8f; break; case 3: adjustedSpeed = 0.7f; break; case 4: adjustedSpeed = 0.6f; break; case 5: adjustedSpeed = 0.5f; break; case 6: adjustedSpeed = 0.4f; break; case 7: adjustedSpeed = 0.3f; break; case 8: adjustedSpeed = 0.2f; break; case 9: adjustedSpeed = 0.1f; break; case 10: adjustedSpeed = 0.01f; break; } // Workshops with an atmosphere bonus decrease crafting time. if (atmosphere >= 45) { adjustedSpeed -= 0.2f; } else if (atmosphere >= 5) { adjustedSpeed -= 0.1f; } // Never fall below 1% of overall crafting time. if (adjustedSpeed <= 0.01f) { adjustedSpeed = 0.01f; } return(BaseCraftDelay * adjustedSpeed); }
private static void OnModuleEnter() { NWPlayer player = _.GetEnteringObject(); if (!player.IsPlayer) { return; } var dbPlayer = DataService.Player.GetByID(player.GlobalID); // VERSION 2: Background items are no longer plot because item level no longer dictates your skill XP gain. if (dbPlayer.VersionNumber < 2) { string[] resrefs = { "blaster_s", "rifle_s", "powerglove_t", "baton_s", "doubleaxe_z", "kukri_d", "greatsword_s", "scanner_r_h", "harvest_r_h", "man_armor" }; foreach (var resref in resrefs) { NWItem item = _.GetItemPossessedBy(player, resref); if (item.IsValid) { item.IsPlot = false; } } dbPlayer.VersionNumber = 2; } // VERSION 3: Force feats need to be removed since force powers were reworked. if (dbPlayer.VersionNumber < 3) { // These IDs come from the Feat.2da file. NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceBreach); // Force Breach NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceLightning); // Force Lightning NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceHeal1); // Force Heal NWNXCreature.RemoveFeat(player, Feat.DeprecatedDarkHeal); // Dark Heal NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceSpread); // Force Spread NWNXCreature.RemoveFeat(player, Feat.DeprecatedDarkSpread); // Dark Spread NWNXCreature.RemoveFeat(player, Feat.DeprecatedForcePush); // Force Push NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceAura); // Force Aura NWNXCreature.RemoveFeat(player, Feat.DeprecatedDrainLife); // Drain Life NWNXCreature.RemoveFeat(player, Feat.DeprecatedChainspell); // Chainspell dbPlayer.VersionNumber = 3; } // VERSION 4: Give the Uncanny Dodge 1 feat to all characters. if (dbPlayer.VersionNumber < 4) { NWNXCreature.AddFeatByLevel(player, Feat.UncannyDodge1, 1); dbPlayer.VersionNumber = 4; } // VERSION 5: We're doing another Force rework, so remove any force feats the player may have acquired. if (dbPlayer.VersionNumber < 5) { NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceBreach); // Force Breach NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceLightning); // Force Lightning NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceHeal1); // Force Heal I NWNXCreature.RemoveFeat(player, Feat.DeprecatedAbsorptionField); // Absorption Field NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceSpread); // Force Spread NWNXCreature.RemoveFeat(player, Feat.DeprecatedForcePush); // Force Push NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceAura); // Force Aura NWNXCreature.RemoveFeat(player, Feat.DeprecatedDrainLife); // Drain Life NWNXCreature.RemoveFeat(player, Feat.DeprecatedChainspell); // Chainspell NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceHeal2); // Force Heal II NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceHeal3); // Force Heal III NWNXCreature.RemoveFeat(player, Feat.DeprecatedForceHeal4); // Force Heal IV dbPlayer.VersionNumber = 5; } // VERSION 6: Remove AC from all items the player is carrying. If possible, // grant +1 durability and +1 max durability for every 2 AC the item has. if (dbPlayer.VersionNumber < 6) { ProcessVersion6ItemChanges(player); dbPlayer.VersionNumber = 6; } // VERSION 7: Give 40 language ranks to players, for their distribution. if (dbPlayer.VersionNumber < 7) { var ranks = new PCSkillPool { PlayerID = dbPlayer.ID, SkillCategoryID = 8, // 8 = Languages Levels = 40 }; DataService.SubmitDataChange(ranks, DatabaseActionType.Insert); dbPlayer.VersionNumber = 7; } // VERSION 8: Remove armor restrictions for a number of perks. Ensure player has the feat if they have the perk purchased. if (dbPlayer.VersionNumber < 8) { // Point Blank Shot if (PerkService.GetPCPerkByID(player.GlobalID, (int)PerkType.PointBlankShot) != null) { NWNXCreature.AddFeat(player, Feat.PointBlankShot); } // Rapid Reload if (PerkService.GetPCPerkByID(player.GlobalID, (int)PerkType.RapidReload) != null) { NWNXCreature.AddFeat(player, Feat.RapidReload); } dbPlayer.VersionNumber = 8; } // VERSION 9: Remove Weapon Finesse if player currently has it. if (dbPlayer.VersionNumber < 9) { if (GetHasFeat(Feat.WeaponFinesse, player)) { NWNXCreature.RemoveFeat(player, Feat.WeaponFinesse); } dbPlayer.VersionNumber = 9; } DataService.SubmitDataChange(dbPlayer, DatabaseActionType.Update); }
public static void HandlePlasmaCellPerk(NWPlayer player, NWObject target) { if (!player.IsPlayer) { return; } if (!_.GetHasFeat(Feat.PlasmaCell, player)) { return; // Check if player has the perk } if (player.RightHand.CustomItemType != CustomItemType.BlasterPistol && player.RightHand.CustomItemType != CustomItemType.BlasterRifle) { return; // Check if player has the right weapons } if (GetLocalBool(target, "TRANQUILIZER_EFFECT_FIRST_RUN") == true) { return; // Check if Tranquilizer is on to avoid conflict } if (GetLocalBool(player, "PLASMA_CELL_TOGGLE_OFF") == true) { return; // Check if Plasma Cell toggle is on or off } if (GetLocalBool(target, "TRANQUILIZER_EFFECT_FIRST_RUN") == true) { return; } int perkLevel = PerkService.GetCreaturePerkLevel(player, PerkType.PlasmaCell); int chance; CustomEffectType[] damageTypes; switch (perkLevel) { case 1: chance = 10; damageTypes = new[] { CustomEffectType.FireCell }; break; case 2: chance = 10; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell }; break; case 3: chance = 20; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell }; break; case 4: chance = 20; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell }; break; case 5: chance = 30; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell }; break; case 6: chance = 30; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell }; break; case 7: chance = 40; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell }; break; case 8: chance = 40; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell }; break; case 9: chance = 50; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell }; break; case 10: chance = 50; damageTypes = new[] { CustomEffectType.FireCell, CustomEffectType.ElectricCell, CustomEffectType.SonicCell, CustomEffectType.AcidCell, CustomEffectType.IceCell, CustomEffectType.DivineCell }; break; default: return; } foreach (var effect in damageTypes) { if (RandomService.D100(1) <= chance) { CustomEffectService.ApplyCustomEffect(player, target.Object, effect, RandomService.D6(1), perkLevel, null); } } }
/// <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 HandleEvadeOrDeflectBlasterFire() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWCreature damager = data.Damager.Object; NWCreature target = NWGameObject.OBJECT_SELF; NWItem damagerWeapon = _.GetLastWeaponUsed(damager); NWItem targetWeapon = target.RightHand; int perkLevel; // Attacker isn't using a pistol or rifle. Return. if (damagerWeapon.CustomItemType != CustomItemType.BlasterPistol && damagerWeapon.CustomItemType != CustomItemType.BlasterRifle) { return; } int modifier; string action; // Check target's equipped weapon, armor and perk. if (target.Chest.CustomItemType == CustomItemType.LightArmor && (targetWeapon.CustomItemType == CustomItemType.MartialArtWeapon || !target.RightHand.IsValid && !target.LeftHand.IsValid)) { // Martial Arts (weapon or unarmed) uses the Evade Blaster Fire perk which is primarily DEX based. perkLevel = PerkService.GetCreaturePerkLevel(target.Object, PerkType.EvadeBlasterFire); modifier = target.DexterityModifier; action = "evade"; } else if (target.Chest.CustomItemType == CustomItemType.ForceArmor && (targetWeapon.CustomItemType == CustomItemType.Lightsaber || targetWeapon.CustomItemType == CustomItemType.Saberstaff || targetWeapon.GetLocalInt("LIGHTSABER") == TRUE)) { // Lightsabers (lightsaber or saberstaff) uses the Deflect Blaster Fire perk which is primarily CHA based. perkLevel = PerkService.GetCreaturePerkLevel(target.Object, PerkType.DeflectBlasterFire); modifier = target.CharismaModifier; action = "deflect"; } else { return; } // Don't have the perk. Return. if (perkLevel <= 0) { return; } // Check attacker's DEX against the primary stat of the perk. int delta = modifier - damager.DexterityModifier; if (delta <= 0) { return; } // Has the delay between block/evade attempts past? DateTime cooldown = DateTime.UtcNow; string lastAttemptVar = target.GetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN"); if (!string.IsNullOrWhiteSpace(lastAttemptVar)) { cooldown = DateTime.Parse(lastAttemptVar); } // Cooldown hasn't expired yet. Not ready to attempt a deflect. if (cooldown >= DateTime.UtcNow) { return; } // Ready to attempt a deflect. Adjust chance based on the delta of attacker DEX versus primary stat of defender. int chanceToDeflect = 5 * delta; if (chanceToDeflect > 80) { chanceToDeflect = 80; } int delay; // Seconds delay between deflect attempts. switch (perkLevel) { case 1: delay = 18; break; case 2: delay = 12; break; case 3: delay = 6; break; default: throw new Exception("HandleEvadeOrDeflectBlasterFire -> Perk Level " + perkLevel + " unsupported."); } cooldown = DateTime.UtcNow.AddSeconds(delay); target.SetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN", cooldown.ToString(CultureInfo.InvariantCulture)); int roll = RandomService.D100(1); if (roll <= chanceToDeflect) { target.SendMessage(ColorTokenService.Gray("You " + action + " a blaster shot.")); data.AdjustAllByPercent(-1); NWNXDamage.SetDamageEventData(data); } else { target.SendMessage(ColorTokenService.Gray("You fail to " + action + " a blaster shot. (" + roll + " vs " + chanceToDeflect + ")")); } }
private static void HandleBattlemagePerk() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject target = Object.OBJECT_SELF; if (!data.Damager.IsPlayer || !target.IsNPC) { return; } if (_.GetHasFeat((int)CustomFeatType.Battlemage, data.Damager.Object) == FALSE) { return; } NWPlayer player = data.Damager.Object; NWItem weapon = _.GetLastWeaponUsed(player.Object); if (weapon.CustomItemType != CustomItemType.Baton) { return; } if (player.Chest.CustomItemType != CustomItemType.ForceArmor) { return; } int perkRank = PerkService.GetPCPerkLevel(player, PerkType.Battlemage); int restoreAmount = 0; bool metRoll = RandomService.Random(100) + 1 <= 50; switch (perkRank) { case 1 when metRoll: restoreAmount = 1; break; case 2: restoreAmount = 1; break; case 3: restoreAmount = 1; if (metRoll) { restoreAmount++; } break; case 4: restoreAmount = 2; break; case 5: restoreAmount = 2; if (metRoll) { restoreAmount++; } break; case 6: restoreAmount = 3; break; } if (restoreAmount > 0) { AbilityService.RestoreFP(player, restoreAmount); } }
private static void HandleDamageImmunity() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWCreature target = NWGameObject.OBJECT_SELF; NWItem shield = target.LeftHand; var concentrationEffect = AbilityService.GetActiveConcentrationEffect(target); double reduction = 0.0f; // Shield damage reduction and absorb energy are calculated here. They don't stack, so the one // with the highest reduction will take precedence. // Calculate shield damage reduction. if (ItemService.ShieldBaseItemTypes.Contains(shield.BaseItemType)) { // Apply damage scaling based on shield presence int perkLevel = PerkService.GetCreaturePerkLevel(target.Object, PerkType.ShieldProficiency); float perkBonus = 0.02f * perkLevel; // DI = 10% + 1% / 3 AC bonuses on the shield + 2% per perk bonus. reduction = (0.1 + 0.01 * shield.AC / 3) + perkBonus; } // Calculate Absorb Energy concentration effect reduction. if (concentrationEffect.Type == PerkType.AbsorbEnergy) { double perkReduction = concentrationEffect.Tier * 0.1; if (perkReduction > reduction) { reduction = perkReduction; // Calculate and award force XP based on total damage reduced. int xp = (int)(data.Total * reduction * 3); if (xp < 5) { xp = 5; } SkillService.GiveSkillXP(target.Object, SkillType.ForceControl, xp); // Play a visual effect signifying the ability was activated. _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_BLUR), target, 0.5f); } } // No reduction found. Bail out early. if (reduction <= 0.0f) { return; } target.SendMessage("Damage reduced by " + (int)(reduction * 100) + "%"); reduction = 1.0f - reduction; data.Bludgeoning = (int)(data.Bludgeoning * reduction); data.Pierce = (int)(data.Pierce * reduction); data.Slash = (int)(data.Slash * reduction); data.Magical = (int)(data.Magical * reduction); data.Acid = (int)(data.Acid * reduction); data.Cold = (int)(data.Cold * reduction); data.Divine = (int)(data.Divine * reduction); data.Electrical = (int)(data.Electrical * reduction); data.Fire = (int)(data.Fire * reduction); data.Negative = (int)(data.Negative * reduction); data.Positive = (int)(data.Positive * reduction); data.Sonic = (int)(data.Sonic * reduction); data.Base = (int)(data.Base * reduction); NWNXDamage.SetDamageEventData(data); }
public static void ApplyBackgroundBonuses(NWPlayer oPC) { var dbPlayer = DataService.Single <Player>(x => x.ID == oPC.GlobalID); string pcName = oPC.Name; int classID = oPC.Class1; string item1Resref = ""; int item1Quantity = 1; string item2Resref = ""; int item2Quantity = 1; switch ((BackgroundType)classID) { case BackgroundType.Freelancer: dbPlayer.UnallocatedSP = dbPlayer.UnallocatedSP + 3; DataService.SubmitDataChange(dbPlayer, DatabaseActionType.Update); break; case BackgroundType.Smuggler: item1Resref = "blaster_s"; break; case BackgroundType.Sharpshooter: item1Resref = "rifle_s"; break; case BackgroundType.TerasKasi: item1Resref = "powerglove_t"; break; case BackgroundType.SecurityOfficer: item1Resref = "club_s"; break; case BackgroundType.Berserker: item1Resref = "doubleaxe_z"; break; case BackgroundType.Duelist: item1Resref = "kukri_d"; break; case BackgroundType.Soldier: item1Resref = "greatsword_s"; break; case BackgroundType.Armorsmith: PerkService.DoPerkUpgrade(oPC, PerkType.ArmorBlueprints, true); break; case BackgroundType.Weaponsmith: PerkService.DoPerkUpgrade(oPC, PerkType.WeaponBlueprints, true); break; case BackgroundType.Chef: PerkService.DoPerkUpgrade(oPC, PerkType.FoodRecipes, true); break; case BackgroundType.Engineer: PerkService.DoPerkUpgrade(oPC, PerkType.EngineeringBlueprints, true); break; case BackgroundType.Fabricator: PerkService.DoPerkUpgrade(oPC, PerkType.FabricationBlueprints, true); break; case BackgroundType.Harvester: item1Resref = "scanner_r_h"; item2Resref = "harvest_r_h"; break; case BackgroundType.Scavenger: PerkService.DoPerkUpgrade(oPC, PerkType.ScavengingExpert, true); break; case BackgroundType.Medic: PerkService.DoPerkUpgrade(oPC, PerkType.ImmediateImprovement, true); break; case BackgroundType.Mandalorian: item1Resref = "man_armor"; break; default: throw new ArgumentOutOfRangeException(); } if (!string.IsNullOrWhiteSpace(item1Resref)) { NWItem oItem1 = (_.CreateItemOnObject(item1Resref, oPC.Object, item1Quantity)); oItem1.IsCursed = true; oItem1.Name = pcName + "'s " + oItem1.Name; } if (!string.IsNullOrWhiteSpace(item2Resref)) { NWItem oItem2 = (_.CreateItemOnObject(item2Resref, oPC.Object, item2Quantity, "")); oItem2.IsCursed = true; oItem2.Name = pcName + "'s " + oItem2.Name; } }
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 int CalculateBAB(NWPlayer oPC, NWItem ignoreItem, EffectiveItemStats stats) { NWItem weapon = oPC.RightHand; // The unequip event fires before the item is actually unequipped, so we need // to have additional checks to make sure we're not getting the weapon that's about to be // unequipped. if (weapon.Equals(ignoreItem)) { weapon = null; NWItem offHand = oPC.LeftHand; if (offHand.CustomItemType == CustomItemType.Vibroblade || offHand.CustomItemType == CustomItemType.FinesseVibroblade || offHand.CustomItemType == CustomItemType.Baton || offHand.CustomItemType == CustomItemType.HeavyVibroblade || offHand.CustomItemType == CustomItemType.Saberstaff || offHand.CustomItemType == CustomItemType.Polearm || offHand.CustomItemType == CustomItemType.TwinBlade || offHand.CustomItemType == CustomItemType.MartialArtWeapon || offHand.CustomItemType == CustomItemType.BlasterPistol || offHand.CustomItemType == CustomItemType.BlasterRifle || offHand.CustomItemType == CustomItemType.Throwing) { weapon = offHand; } } if (weapon == null || !weapon.IsValid) { weapon = oPC.Arms; } if (!weapon.IsValid) { return(0); } SkillType itemSkill = ItemService.GetSkillTypeForItem(weapon); if (itemSkill == SkillType.Unknown || itemSkill == SkillType.LightArmor || itemSkill == SkillType.HeavyArmor || itemSkill == SkillType.ForceArmor || itemSkill == SkillType.Shields) { return(0); } int weaponSkillID = (int)itemSkill; PCSkill skill = DataService.Single <PCSkill>(x => x.PlayerID == oPC.GlobalID && x.SkillID == weaponSkillID); if (skill == null) { return(0); } int skillBAB = skill.Rank / 10; int perkBAB = 0; int backgroundBAB = 0; BackgroundType background = (BackgroundType)oPC.Class1; bool receivesBackgroundBonus = false; // Apply increased BAB if player is using a weapon for which they have a proficiency. PerkType proficiencyPerk = PerkType.Unknown; SkillType proficiencySkill = SkillType.Unknown; switch (weapon.CustomItemType) { case CustomItemType.Vibroblade: proficiencyPerk = PerkType.VibrobladeProficiency; proficiencySkill = SkillType.OneHanded; break; case CustomItemType.FinesseVibroblade: proficiencyPerk = PerkType.FinesseVibrobladeProficiency; proficiencySkill = SkillType.OneHanded; receivesBackgroundBonus = background == BackgroundType.Duelist; break; case CustomItemType.Baton: proficiencyPerk = PerkType.BatonProficiency; proficiencySkill = SkillType.OneHanded; receivesBackgroundBonus = background == BackgroundType.SecurityOfficer; break; case CustomItemType.HeavyVibroblade: proficiencyPerk = PerkType.HeavyVibrobladeProficiency; proficiencySkill = SkillType.TwoHanded; receivesBackgroundBonus = background == BackgroundType.Soldier; break; case CustomItemType.Saberstaff: proficiencyPerk = PerkType.SaberstaffProficiency; proficiencySkill = SkillType.Lightsaber; break; case CustomItemType.Polearm: proficiencyPerk = PerkType.PolearmProficiency; proficiencySkill = SkillType.TwoHanded; break; case CustomItemType.TwinBlade: proficiencyPerk = PerkType.TwinVibrobladeProficiency; proficiencySkill = SkillType.TwinBlades; receivesBackgroundBonus = background == BackgroundType.Berserker; break; case CustomItemType.MartialArtWeapon: proficiencyPerk = PerkType.MartialArtsProficiency; proficiencySkill = SkillType.MartialArts; receivesBackgroundBonus = background == BackgroundType.TerasKasi; break; case CustomItemType.BlasterPistol: proficiencyPerk = PerkType.BlasterPistolProficiency; proficiencySkill = SkillType.Firearms; receivesBackgroundBonus = background == BackgroundType.Smuggler; break; case CustomItemType.BlasterRifle: proficiencyPerk = PerkType.BlasterRifleProficiency; proficiencySkill = SkillType.Firearms; receivesBackgroundBonus = background == BackgroundType.Sharpshooter || background == BackgroundType.Mandalorian; break; case CustomItemType.Throwing: proficiencyPerk = PerkType.ThrowingProficiency; proficiencySkill = SkillType.Throwing; break; case CustomItemType.Lightsaber: proficiencyPerk = PerkType.LightsaberProficiency; proficiencySkill = SkillType.Lightsaber; break; } if (weapon.GetLocalInt("LIGHTSABER") == TRUE) { proficiencyPerk = PerkType.LightsaberProficiency; proficiencySkill = SkillType.Lightsaber; } if (proficiencyPerk != PerkType.Unknown && proficiencySkill != SkillType.Unknown) { perkBAB += PerkService.GetPCPerkLevel(oPC, proficiencyPerk); } if (receivesBackgroundBonus) { backgroundBAB = background == BackgroundType.Mandalorian ? 1 : 2; } return(1 + skillBAB + perkBAB + stats.BAB + backgroundBAB); // Note: Always add 1 to BAB. 0 will cause a crash in NWNX. }
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); } }