private static int EffectiveArmorClass(EffectiveItemStats stats, NWPlayer player) { int baseAC = stats.AC + CustomEffectService.CalculateEffectAC(player); int totalAC = _.GetAC(player) - baseAC; // Shield Oath and Precision Targeting affect a percentage of the TOTAL armor class on a creature. var stance = CustomEffectService.GetCurrentStanceType(player); if (stance == CustomEffectType.ShieldOath) { int bonus = (int)(totalAC * 0.2f); baseAC = baseAC + bonus; } else if (stance == CustomEffectType.PrecisionTargeting) { int penalty = (int)(totalAC * 0.3f); baseAC = baseAC - penalty; } if (baseAC < 0) { baseAC = 0; } return(baseAC); }
private static void HandleStances() { DamageEventData data = NWNXDamage.GetDamageEventData(); NWPlayer damager = data.Damager.Object; NWItem damagerWeapon = _.GetLastWeaponUsed(damager); if (damager.IsPlayer) { CustomEffectType stance = CustomEffectService.GetCurrentStanceType(damager); switch (stance) { case CustomEffectType.ShieldOath: data.AdjustAllByPercent(-0.30f); break; case CustomEffectType.PrecisionTargeting: if (damagerWeapon.CustomItemType == CustomItemType.BlasterPistol || damagerWeapon.CustomItemType == CustomItemType.BlasterRifle) { data.AdjustAllByPercent(0.20f); } break; } } NWNXDamage.SetDamageEventData(data); }
private static int EffectiveArmorClass(NWPlayer player, NWItem ignoreItem, EffectiveItemStats stats) { int baseAC = stats.AC + CustomEffectService.CalculateEffectAC(player); // Calculate AC bonus granted by skill ranks. // Only chest armor is checked for this bonus. if (ignoreItem != player.Chest) { CustomItemType armorType = player.Chest.CustomItemType; int skillRank = 0; switch (armorType) { case CustomItemType.LightArmor: skillRank = SkillService.GetPCSkillRank(player, SkillType.LightArmor); break; case CustomItemType.HeavyArmor: skillRank = SkillService.GetPCSkillRank(player, SkillType.HeavyArmor); break; case CustomItemType.ForceArmor: skillRank = SkillService.GetPCSkillRank(player, SkillType.ForceArmor); break; } // +1 AC per 10 skill ranks, while wearing the appropriate armor. int skillACBonus = skillRank / 10; baseAC += skillACBonus; } int totalAC = _.GetAC(player) - baseAC; // Shield Oath and Precision Targeting affect a percentage of the TOTAL armor class on a creature. var stance = CustomEffectService.GetCurrentStanceType(player); if (stance == CustomEffectType.ShieldOath) { int bonus = (int)(totalAC * 0.2f); baseAC += bonus; } else if (stance == CustomEffectType.PrecisionTargeting) { int penalty = (int)(totalAC * 0.3f); baseAC -= penalty; } if (baseAC < 0) { baseAC = 0; } return(baseAC); }
public static EffectiveItemStats GetPlayerItemEffectiveStats(NWPlayer player, NWItem ignoreItem = null) { using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats")) { var pcSkills = DataService.Where <PCSkill>(x => x.PlayerID == player.GlobalID); int heavyRank = pcSkills.Single(x => x.SkillID == (int)SkillType.HeavyArmor).Rank; int lightRank = pcSkills.Single(x => x.SkillID == (int)SkillType.LightArmor).Rank; int forceRank = pcSkills.Single(x => x.SkillID == (int)SkillType.ForceArmor).Rank; int martialRank = pcSkills.Single(x => x.SkillID == (int)SkillType.MartialArts).Rank; EffectiveItemStats stats = new EffectiveItemStats(); stats.EnmityRate = 1.0f; using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop")) { HashSet <NWItem> processed = new HashSet <NWItem>(); for (int itemSlot = 0; itemSlot < NUM_INVENTORY_SLOTS; itemSlot++) { NWItem item = _.GetItemInSlot(itemSlot, player); if (!item.IsValid || item.Equals(ignoreItem)) { continue; } SkillType skill = ItemService.GetSkillTypeForItem(item); int rank; // Have we already processed this particular item? Skip over it. // NWN likes to include the same weapon in multiple slots for some reasons, so this works around that. // If someone has a better solution to this please feel free to change it. if (processed.Contains(item)) { continue; } processed.Add(item); using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop::GetRank")) { rank = pcSkills.Single(x => x.SkillID == (int)skill).Rank; } using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop::StatAdjustments")) { // Only scale casting speed if it's a bonus. Penalties remain regardless of skill level difference. if (item.CastingSpeed > 0) { stats.CastingSpeed += CalculateAdjustedValue(item.CastingSpeed, item.RecommendedLevel, rank, 1); } else { stats.CastingSpeed += item.CastingSpeed; } stats.EnmityRate += CalculateAdjustedValue(0.01f * item.EnmityRate, item.RecommendedLevel, rank, 0.00f); stats.ForcePotency += CalculateAdjustedValue(item.ForcePotencyBonus, item.RecommendedLevel, rank, 0); stats.ForceDefense += CalculateAdjustedValue(item.ForceDefenseBonus, item.RecommendedLevel, rank, 0); stats.ForceAccuracy += CalculateAdjustedValue(item.ForceAccuracyBonus, item.RecommendedLevel, rank, 0); stats.ElectricalPotency += CalculateAdjustedValue(item.ElectricalPotencyBonus, item.RecommendedLevel, rank, 0); stats.MindPotency += CalculateAdjustedValue(item.MindPotencyBonus, item.RecommendedLevel, rank, 0); stats.LightPotency += CalculateAdjustedValue(item.LightPotencyBonus, item.RecommendedLevel, rank, 0); stats.DarkPotency += CalculateAdjustedValue(item.DarkPotencyBonus, item.RecommendedLevel, rank, 0); stats.ElectricalDefense += CalculateAdjustedValue(item.ElectricalDefenseBonus, item.RecommendedLevel, rank, 0); stats.MindDefense += CalculateAdjustedValue(item.MindDefenseBonus, item.RecommendedLevel, rank, 0); stats.LightDefense += CalculateAdjustedValue(item.LightDefenseBonus, item.RecommendedLevel, rank, 0); stats.DarkDefense += CalculateAdjustedValue(item.DarkDefenseBonus, item.RecommendedLevel, rank, 0); stats.Luck += CalculateAdjustedValue(item.LuckBonus, item.RecommendedLevel, rank, 0); stats.Meditate += CalculateAdjustedValue(item.MeditateBonus, item.RecommendedLevel, rank, 0); stats.Rest += CalculateAdjustedValue(item.RestBonus, item.RecommendedLevel, rank, 0); stats.Medicine += CalculateAdjustedValue(item.MedicineBonus, item.RecommendedLevel, rank, 0); stats.HPRegen += CalculateAdjustedValue(item.HPRegenBonus, item.RecommendedLevel, rank, 0); stats.FPRegen += CalculateAdjustedValue(item.FPRegenBonus, item.RecommendedLevel, rank, 0); stats.Weaponsmith += CalculateAdjustedValue(item.CraftBonusWeaponsmith, item.RecommendedLevel, rank, 0); stats.Cooking += CalculateAdjustedValue(item.CraftBonusCooking, item.RecommendedLevel, rank, 0); stats.Engineering += CalculateAdjustedValue(item.CraftBonusEngineering, item.RecommendedLevel, rank, 0); stats.Fabrication += CalculateAdjustedValue(item.CraftBonusFabrication, item.RecommendedLevel, rank, 0); stats.Armorsmith += CalculateAdjustedValue(item.CraftBonusArmorsmith, item.RecommendedLevel, rank, 0); stats.Harvesting += CalculateAdjustedValue(item.HarvestingBonus, item.RecommendedLevel, rank, 0); stats.Piloting += CalculateAdjustedValue(item.PilotingBonus, item.RecommendedLevel, rank, 0); stats.Scavenging += CalculateAdjustedValue(item.ScavengingBonus, item.RecommendedLevel, rank, 0); stats.SneakAttack += CalculateAdjustedValue(item.SneakAttackBonus, item.RecommendedLevel, rank, 0); stats.Strength += CalculateAdjustedValue(item.StrengthBonus, item.RecommendedLevel, rank, 0); stats.Dexterity += CalculateAdjustedValue(item.DexterityBonus, item.RecommendedLevel, rank, 0); stats.Constitution += CalculateAdjustedValue(item.ConstitutionBonus, item.RecommendedLevel, rank, 0); stats.Wisdom += CalculateAdjustedValue(item.WisdomBonus, item.RecommendedLevel, rank, 0); stats.Intelligence += CalculateAdjustedValue(item.IntelligenceBonus, item.RecommendedLevel, rank, 0); stats.Charisma += CalculateAdjustedValue(item.CharismaBonus, item.RecommendedLevel, rank, 0); stats.HP += CalculateAdjustedValue(item.HPBonus, item.RecommendedLevel, rank, 0); stats.FP += CalculateAdjustedValue(item.FPBonus, item.RecommendedLevel, rank, 0); } using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop::CalcBAB")) { // Calculate base attack bonus int itemLevel = item.RecommendedLevel; int delta = itemLevel - rank; int itemBAB = item.BaseAttackBonus; if (delta >= 1) { itemBAB--; } if (delta > 0) { itemBAB = itemBAB - delta / 5; } if (itemBAB <= 0) { itemBAB = 0; } stats.BAB += itemBAB; } using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop::CalcAC")) { // Calculate AC if (ACBaseItemTypes.Contains(item.BaseItemType)) { int skillRankToUse; if (item.CustomItemType == CustomItemType.HeavyArmor) { skillRankToUse = heavyRank; } else if (item.CustomItemType == CustomItemType.LightArmor) { skillRankToUse = lightRank; } else if (item.CustomItemType == CustomItemType.ForceArmor) { skillRankToUse = forceRank; } else if (item.CustomItemType == CustomItemType.MartialArtWeapon) { skillRankToUse = martialRank; } else { continue; } int itemAC = item.CustomAC; itemAC = CalculateAdjustedValue(itemAC, item.RecommendedLevel, skillRankToUse, 0); stats.AC += itemAC; } } } } using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::FinalAdjustments")) { // Final casting speed adjustments if (stats.CastingSpeed < -99) { stats.CastingSpeed = -99; } else if (stats.CastingSpeed > 99) { stats.CastingSpeed = 99; } // Final enmity adjustments if (stats.EnmityRate < 0.5f) { stats.EnmityRate = 0.5f; } else if (stats.EnmityRate > 1.5f) { stats.EnmityRate = 1.5f; } var stance = CustomEffectService.GetCurrentStanceType(player); if (stance == CustomEffectType.ShieldOath) { stats.EnmityRate = stats.EnmityRate + 0.2f; } return(stats); } } }
public static EffectiveItemStats GetPlayerItemEffectiveStats(NWPlayer player, NWItem ignoreItem = null) { int heavyRank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)SkillType.HeavyArmor).Rank; int lightRank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)SkillType.LightArmor).Rank; int forceRank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)SkillType.ForceArmor).Rank; int martialRank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)SkillType.MartialArts).Rank; EffectiveItemStats stats = new EffectiveItemStats(); stats.EnmityRate = 1.0f; HashSet <NWItem> processed = new HashSet <NWItem>(); for (int itemSlot = 0; itemSlot < NumberOfInventorySlots; itemSlot++) { NWItem item = _.GetItemInSlot((InventorySlot)itemSlot, player); if (!item.IsValid || item.Equals(ignoreItem)) { continue; } // Have we already processed this particular item? Skip over it. // NWN likes to include the same weapon in multiple slots for some reasons, so this works around that. // If someone has a better solution to this please feel free to change it. if (processed.Contains(item)) { continue; } processed.Add(item); SkillType skill = ItemService.GetSkillTypeForItem(item); var rank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)skill).Rank; stats.CooldownRecovery += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_COOLDOWN_RECOVERY"); stats.EnmityRate += item.GetLocalFloat("STAT_EFFECTIVE_LEVEL_ENMITY_RATE"); stats.Luck += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_LUCK_BONUS"); stats.Meditate += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_MEDITATE_BONUS"); stats.Rest += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_REST_BONUS"); stats.Medicine += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_MEDICINE_BONUS"); stats.HPRegen += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_HP_REGEN_BONUS"); stats.FPRegen += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_FP_REGEN_BONUS"); stats.Weaponsmith += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_WEAPONSMITH_BONUS"); stats.Cooking += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_COOKING_BONUS"); stats.Engineering += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_ENGINEERING_BONUS"); stats.Fabrication += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_FABRICATION_BONUS"); stats.Armorsmith += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_ARMORSMITH_BONUS"); stats.Harvesting += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_HARVESTING_BONUS"); stats.Piloting += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_PILOTING_BONUS"); stats.Scavenging += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_SCAVENGING_BONUS"); stats.SneakAttack += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_SNEAK_ATTACK_BONUS"); stats.Strength += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_STRENGTH_BONUS"); stats.Dexterity += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_DEXTERITY_BONUS"); stats.Constitution += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_CONSTITUTION_BONUS"); stats.Wisdom += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_WISDOM_BONUS"); stats.Intelligence += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_INTELLIGENCE_BONUS"); stats.Charisma += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_CHARISMA_BONUS"); stats.HP += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_HP_BONUS"); stats.FP += item.GetLocalInt("STAT_EFFECTIVE_LEVEL_FP_BONUS"); // Calculate base attack bonus if (ItemService.WeaponBaseItemTypes.Contains(item.BaseItemType)) { int itemLevel = item.RecommendedLevel; int delta = itemLevel - rank; int itemBAB = item.BaseAttackBonus; if (delta >= 1) { itemBAB--; } if (delta > 0) { itemBAB = itemBAB - delta / 5; } if (itemBAB <= 0) { itemBAB = 0; } stats.BAB += itemBAB; } // Calculate AC if (ItemService.ArmorBaseItemTypes.Contains(item.BaseItemType)) { int skillRankToUse; int maxAC = 0; if (item.CustomItemType == CustomItemType.HeavyArmor) { skillRankToUse = heavyRank; maxAC = 10; } else if (item.CustomItemType == CustomItemType.LightArmor) { skillRankToUse = lightRank; maxAC = 13; } else if (item.CustomItemType == CustomItemType.ForceArmor) { skillRankToUse = forceRank; maxAC = 11; } else if (item.CustomItemType == CustomItemType.MartialArtWeapon) { skillRankToUse = martialRank; } else { continue; } int itemAC = item.CustomAC; itemAC = CalculateAdjustedValue(itemAC, item.RecommendedLevel, skillRankToUse, 0); if (itemAC > maxAC) { item.CustomAC = maxAC; } stats.AC += itemAC; } } // Final casting speed adjustments if (stats.CooldownRecovery < -99) { stats.CooldownRecovery = -99; } else if (stats.CooldownRecovery > 99) { stats.CooldownRecovery = 99; } // Final enmity adjustments if (stats.EnmityRate < 0.5f) { stats.EnmityRate = 0.5f; } else if (stats.EnmityRate > 1.5f) { stats.EnmityRate = 1.5f; } var stance = CustomEffectService.GetCurrentStanceType(player); if (stance == CustomEffectType.ShieldOath) { stats.EnmityRate = stats.EnmityRate + 0.2f; } return(stats); }
private static void HandleDamageImmunity() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWCreature target = _.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% AC bonuses on the shield + 2% per perk bonus. reduction = (0.1 + (0.01 * shield.CustomAC)) + 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 * 3); if (xp < 5) { xp = 5; } SkillService.GiveSkillXP(target.Object, SkillType.ForceControl, xp); // Play a visual effect signifying the ability was activated. _.ApplyEffectToObject(DurationType.Temporary, EffectVisualEffect(VisualEffect.Dur_Blur), target, 0.5f); } } //Shield Oath Damage Immunity NWPlayer player = _.OBJECT_SELF; if (target.IsPC) { if (CustomEffectService.GetCurrentStanceType(player) == CustomEffectType.ShieldOath) { reduction += 0.2f; } } // No reduction found. Bail out early. if (reduction <= 0.0f) { return; } target.SendMessage("Total Damage: " + data.Total); 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); -- special damage types, such as force rage 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); target.SendMessage("Total Damage: " + data.Total); NWNXDamage.SetDamageEventData(data); }