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); } }
public static int CalculateChanceForComponentBonus(NWPlayer player, int tier, ResourceQuality quality, bool scavenging = false) { int rank = (scavenging ? SkillService.GetPCSkillRank(player, SkillType.Scavenging) : SkillService.GetPCSkillRank(player, SkillType.Harvesting)); int difficulty = (tier - 1) * 10 + GetDifficultyAdjustment(quality); int delta = difficulty - rank; if (delta >= 7) { return(0); } if (delta <= -7) { return(45); } int chance = 0; switch (delta) { case 6: chance = 1; break; case 5: chance = 2; break; case 4: chance = 3; break; case 3: chance = 6; break; case 2: chance = 9; break; case 1: chance = 12; break; case 0: chance = 15; break; case -1: chance = 18; break; case -2: chance = 20; break; case -3: chance = 21; break; case -4: chance = 23; break; case -5: chance = 25; break; case -6: chance = 27; break; } var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(player); int itemBonus = (scavenging ? effectiveStats.Scavenging : effectiveStats.Harvesting) / 2; if (itemBonus > 30) { itemBonus = 30; } chance += itemBonus; return(chance); }
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); }
private static void OnModuleApplyDamage() { var data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject damager = data.Damager; if (!damager.IsPlayer) { return; } NWCreature target = NWGameObject.OBJECT_SELF; // Check that this was a normal attack, and not (say) a damage over time effect. if (target.GetLocalInt(AbilityService.LAST_ATTACK + damager.GlobalID) != AbilityService.ATTACK_PHYSICAL) { return; } NWItem weapon = (_.GetLastWeaponUsed(damager.Object)); int damageBonus = weapon.DamageBonus; NWPlayer player = (damager.Object); int itemLevel = weapon.RecommendedLevel; SkillType skill = ItemService.GetSkillTypeForItem(weapon); if (skill == SkillType.Unknown) { return; } int rank = SkillService.GetPCSkillRank(player, skill); int delta = itemLevel - rank; if (delta >= 1) { damageBonus--; } damageBonus = damageBonus - delta / 5; if (damageBonus <= 0) { damageBonus = 0; } data.Base += damageBonus; NWNXDamage.SetDamageEventData(data); }
public static string TranslateSnippetForListener(NWObject speaker, NWObject listener, SkillType language, string snippet) { Dictionary <SkillType, Type> map = new Dictionary <SkillType, Type> { { SkillType.Bothese, typeof(TranslatorBothese) }, { SkillType.Catharese, typeof(TranslatorCatharese) }, { SkillType.Cheunh, typeof(TranslatorCheunh) }, { SkillType.Dosh, typeof(TranslatorDosh) }, { SkillType.Droidspeak, typeof(TranslatorDroidspeak) }, { SkillType.Huttese, typeof(TranslatorHuttese) }, { SkillType.Mandoa, typeof(TranslatorMandoa) }, { SkillType.Shyriiwook, typeof(TranslatorShyriiwook) }, { SkillType.Twileki, typeof(TranslatorTwileki) }, { SkillType.Zabraki, typeof(TranslatorZabraki) }, { SkillType.Mirialan, typeof(TranslatorMirialan) }, { SkillType.MonCalamarian, typeof(TranslatorMonCalamarian) }, { SkillType.Ugnaught, typeof(TranslatorUgnaught) } }; Type type = typeof(TranslatorGeneric); map.TryGetValue(language, out type); ITranslator translator = (ITranslator)Activator.CreateInstance(type); if (speaker.IsPC && !speaker.IsDM) { // Get the rank and max rank for the speaker, and garble their English text based on it. NWPlayer speakerAsPlayer = speaker.Object; int speakerSkillRank = SkillService.GetPCSkillRank(speakerAsPlayer, language); int speakerSkillMaxRank = SkillService.GetSkill(language).MaxRank; if (speakerSkillRank != speakerSkillMaxRank) { int garbledChance = 100 - (int)(((float)speakerSkillRank / (float)speakerSkillMaxRank) * 100); string[] split = snippet.Split(' '); for (int i = 0; i < split.Length; ++i) { if (RandomService.Random(100) <= garbledChance) { split[i] = new string(split[i].ToCharArray().OrderBy(s => (RandomService.Random(2) % 2) == 0).ToArray()); } } snippet = split.Aggregate((a, b) => a + " " + b); } } if (!listener.IsPC || listener.IsDM) { // Short circuit for a DM or NPC - they will always understand the text. return(snippet); } // Let's grab the max rank for the listener skill, and then we roll for a successful translate based on that. NWPlayer listenerAsPlayer = listener.Object; int rank = SkillService.GetPCSkillRank(listenerAsPlayer, language); int maxRank = SkillService.GetSkill(language).MaxRank; // Check for the Comprehend Speech concentration ability. Player dbPlayer = DataService.Player.GetByID(listenerAsPlayer.GlobalID); bool grantSenseXP = false; if (dbPlayer.ActiveConcentrationPerkID == (int)PerkType.ComprehendSpeech) { int bonus = 5 * dbPlayer.ActiveConcentrationTier; rank += bonus; grantSenseXP = true; } // Ensure we don't go over the maximum. if (rank > maxRank) { rank = maxRank; } if (rank == maxRank || speaker == listener) { // Guaranteed success - return original. return(snippet); } string textAsForeignLanguage = translator.Translate(snippet); if (rank != 0) { int englishChance = (int)(((float)rank / (float)maxRank) * 100); string[] originalSplit = snippet.Split(' '); string[] foreignSplit = textAsForeignLanguage.Split(' '); StringBuilder endResult = new StringBuilder(); // WARNING: We're making the assumption that originalSplit.Length == foreignSplit.Length. // If this assumption changes, the below logic needs to change too. for (int i = 0; i < originalSplit.Length; ++i) { if (RandomService.Random(100) <= englishChance) { endResult.Append(originalSplit[i]); } else { endResult.Append(foreignSplit[i]); } endResult.Append(" "); } textAsForeignLanguage = endResult.ToString(); } long now = DateTime.Now.Ticks; int lastSkillUpLow = listenerAsPlayer.GetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_LOW"); int lastSkillUpHigh = listenerAsPlayer.GetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_HIGH"); long lastSkillUp = lastSkillUpHigh; lastSkillUp = (lastSkillUp << 32) | (uint)lastSkillUpLow; long differenceInSeconds = (now - lastSkillUp) / 10000000; if (differenceInSeconds / 60 >= 2) { int amount = Math.Max(10, Math.Min(150, snippet.Length) / 3); // Reward exp towards the language - we scale this with character count, maxing at 50 exp for 150 characters. SkillService.GiveSkillXP(listenerAsPlayer, language, amount); // Grant Sense XP if player is concentrating Comprehend Speech. if (grantSenseXP) { SkillService.GiveSkillXP(listenerAsPlayer, SkillType.ForceSense, amount * 10); } listenerAsPlayer.SetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_LOW", (int)(now & 0xFFFFFFFF)); listenerAsPlayer.SetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_HIGH", (int)((now >> 32) & 0xFFFFFFFF)); } return(textAsForeignLanguage); }
private static void OnModuleApplyDamage() { var data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject damager = data.Damager; if (!damager.IsPlayer) { return; } NWCreature target = _.OBJECT_SELF; // Check that this was a normal attack, and not (say) a damage over time effect. if (target.GetLocalInt(AbilityService.LAST_ATTACK + damager.GlobalID) != AbilityService.ATTACK_PHYSICAL) { return; } NWItem weapon = (_.GetLastWeaponUsed(damager.Object)); if (!weapon.IsValid) { // Double weapons don't show up correctly when their offhand makes an attack. // So check for that case here. if (_.GetObjectType(damager) == ObjectType.Creature) { NWCreature attacker = data.Damager.Object; if (attacker.RightHand.BaseItemType == BaseItem.Saberstaff || attacker.RightHand.BaseItemType == BaseItem.TwoBladedSword || attacker.RightHand.BaseItemType == BaseItem.DoubleAxe || attacker.RightHand.BaseItemType == BaseItem.DireMace) { weapon = attacker.RightHand; } } } int damageBonus = weapon.DamageBonus; NWPlayer player = (damager.Object); int itemLevel = weapon.RecommendedLevel; SkillType skill = ItemService.GetSkillTypeForItem(weapon); if (skill == SkillType.Unknown) { return; } int rank = SkillService.GetPCSkillRank(player, skill); int delta = itemLevel - rank; if (delta >= 1) { damageBonus--; } damageBonus = damageBonus - delta / 5; if (damageBonus <= 0) { damageBonus = 0; } data.Base += damageBonus; NWNXDamage.SetDamageEventData(data); }
/// <summary> /// Calculates ability resistance for an ability. /// The attacker and defender's skills, ability modifiers, and balance affinity will be /// used to make this determination. /// </summary> /// <param name="attacker">The creature using the ability.</param> /// <param name="defender">The creature being targeted by the ability.</param> /// <param name="skill">The skill used for this ability.</param> /// <param name="balanceType">The force balance type to use for this ability.</param> /// <param name="sendRollMessage">If true, the roll message will be sent. Otherwise it won't be.</param> /// <returns>Data regarding the ability resistance roll</returns> public static AbilityResistanceResult CalculateAbilityResistance(NWCreature attacker, NWCreature defender, SkillType skill, ForceBalanceType balanceType, bool sendRollMessage = true) { int abilityScoreType; switch (skill) { case SkillType.ForceAlter: abilityScoreType = ABILITY_INTELLIGENCE; break; case SkillType.ForceControl: abilityScoreType = ABILITY_WISDOM; break; case SkillType.ForceSense: abilityScoreType = ABILITY_CHARISMA; break; default: throw new ArgumentException("Invalid skill type called for " + nameof(CalculateAbilityResistance) + ", value '" + skill + "' not supported."); } AbilityResistanceResult result = new AbilityResistanceResult(); int attackerSkill = SkillService.GetPCSkillRank(attacker.Object, skill); int attackerAbility = _.GetAbilityModifier(abilityScoreType, attacker); int defenderSkill = SkillService.GetPCSkillRank(defender.Object, skill); int defenderAbility = _.GetAbilityModifier(abilityScoreType, defender); // If the defender is equipped with a lightsaber, we check their lightsaber skill if (defender.RightHand.CustomItemType == CustomItemType.Lightsaber || defender.LeftHand.CustomItemType == CustomItemType.Lightsaber) { int lightsaberSkill = SkillService.GetPCSkillRank(defender.Object, SkillType.Lightsaber); if (lightsaberSkill > defenderSkill) { defenderSkill = lightsaberSkill; } } // If the defender's martial arts skill is greater than the current skill they're using, we'll use that instead. int defenderMASkill = SkillService.GetPCSkillRank(defender.Object, SkillType.MartialArts); if (defenderMASkill > defenderSkill) { defenderSkill = defenderMASkill; } int attackerAffinity = 0; int defenderAffinity = 0; // Only check affinity if ability has a force balance type. if (balanceType == ForceBalanceType.Dark || balanceType == ForceBalanceType.Light) { attackerAffinity = GetBalanceAffinity(attacker.Object, balanceType); defenderAffinity = GetBalanceAffinity(defender.Object, balanceType); } float attackerCR = attacker.IsPlayer ? 0f : attacker.ChallengeRating * 5f; float defenderCR = defender.IsPlayer ? 0f : defender.ChallengeRating * 5f; float attackerTotal = attackerSkill + attackerAbility + attackerAffinity + attackerCR; float defenderTotal = defenderSkill + defenderAbility + defenderAffinity + defenderCR; float divisor = attackerTotal + defenderTotal + 1; // +1 to prevent division by zero. //Console.WriteLine("attackerCR = " + attackerCR); //Console.WriteLine("defenderCR = " + defenderCR); //Console.WriteLine("attackerSkill = " + attackerSkill); //Console.WriteLine("attackerAbility = " + attackerAbility); //Console.WriteLine("attackerAffinity = " + attackerAffinity); //Console.WriteLine("defenderSkill = " + defenderSkill); //Console.WriteLine("defenderAbility = " + defenderAbility); //Console.WriteLine("defenderAffinity = " + defenderAffinity); //Console.WriteLine("attackerTotal = " + attackerTotal); //Console.WriteLine("defenderTotal = " + defenderTotal); //Console.WriteLine("divisor = " + divisor); result.DC = (int)(attackerTotal / divisor * 100); result.Roll = RandomService.D100(1); if (sendRollMessage) { string resisted = result.IsResisted ? ColorTokenService.Red(" [RESISTED " + Math.Abs(result.Delta) + "%]") : string.Empty; string message = ColorTokenService.SavingThrow("Roll: " + result.Roll + " VS " + result.DC + " DC") + resisted; attacker.SendMessage(message); defender.SendMessage(message); } return(result); }