private static void RemoveWeaponPenalties(NWItem oItem) { SkillType skillType = ItemService.GetSkillTypeForItem(oItem); if (skillType == SkillType.Unknown || skillType == SkillType.HeavyArmor || skillType == SkillType.LightArmor || skillType == SkillType.ForceArmor || skillType == SkillType.Shields) { return; } foreach (ItemProperty ip in oItem.ItemProperties) { string tag = _.GetItemPropertyTag(ip); if (tag == IPWeaponPenaltyTag) { _.RemoveItemProperty(oItem.Object, ip); } } }
private static void OnModuleNWNXChat() { ChatChannelType channel = (ChatChannelType)NWNXChat.GetChannel(); // So we're going to play with a couple of channels here. // - PlayerTalk, PlayerWhisper, PlayerParty, and PlayerShout are all IC channels. These channels // are subject to emote colouring and language translation. (see below for more info). // - PlayerParty is an IC channel with special behaviour. Those outside of the party but within // range may listen in to the party chat. (see below for more information). // - PlayerShout sends a holocom message server-wide through the DMTell channel. // - PlayerDM echoes back the message received to the sender. bool inCharacterChat = channel == ChatChannelType.PlayerTalk || channel == ChatChannelType.PlayerWhisper || channel == ChatChannelType.PlayerParty || channel == ChatChannelType.PlayerShout; bool messageToDm = channel == ChatChannelType.PlayerDM; if (!inCharacterChat && !messageToDm) { // We don't much care about traffic on the other channels. return; } NWObject sender = NWNXChat.GetSender(); string message = NWNXChat.GetMessage().Trim(); if (string.IsNullOrWhiteSpace(message)) { // We can't handle empty messages, so skip it. return; } if (ChatCommandService.CanHandleChat(sender, message) || BaseService.CanHandleChat(sender) || CraftService.CanHandleChat(sender) || MarketService.CanHandleChat(sender.Object) || MessageBoardService.CanHandleChat(sender) || ItemService.CanHandleChat(sender)) { // This will be handled by other services, so just bail. return; } if (channel == ChatChannelType.PlayerDM) { // Simply echo the message back to the player. NWNXChat.SendMessage((int)ChatChannelType.ServerMessage, "(Sent to DM) " + message, sender, sender); return; } // At this point, every channel left is one we want to manually handle. NWNXChat.SkipMessage(); // If this is a shout message, and the holonet is disabled, we disallow it. if (channel == ChatChannelType.PlayerShout && sender.IsPC && sender.GetLocalInt("DISPLAY_HOLONET") == FALSE) { NWPlayer player = sender.Object; player.SendMessage("You have disabled the holonet and cannot send this message."); return; } List <ChatComponent> chatComponents; // Quick early out - if we start with "//" or "((", this is an OOC message. bool isOOC = false; if (message.Length >= 2 && (message.Substring(0, 2) == "//" || message.Substring(0, 2) == "((")) { ChatComponent component = new ChatComponent { m_Text = message, m_CustomColour = true, m_ColourRed = 64, m_ColourGreen = 64, m_ColourBlue = 64, m_Translatable = false }; chatComponents = new List <ChatComponent> { component }; if (channel == ChatChannelType.PlayerShout) { _.SendMessageToPC(sender, "Out-of-character messages cannot be sent on the Holonet."); return; } isOOC = true; } else { if (EmoteStyleService.GetEmoteStyle(sender) == EmoteStyle.Regular) { chatComponents = SplitMessageIntoComponents_Regular(message); } else { chatComponents = SplitMessageIntoComponents_Novel(message); } // For any components with colour, set the emote colour. foreach (ChatComponent component in chatComponents) { if (component.m_CustomColour) { component.m_ColourRed = 0; component.m_ColourGreen = 255; component.m_ColourBlue = 0; } } } // Now, depending on the chat channel, we need to build a list of recipients. bool needsAreaCheck = false; float distanceCheck = 0.0f; // The sender always wants to see their own message. List <NWObject> recipients = new List <NWObject> { sender }; // This is a server-wide holonet message (that receivers can toggle on or off). if (channel == ChatChannelType.PlayerShout) { recipients.AddRange(NWModule.Get().Players.Where(player => player.GetLocalInt("DISPLAY_HOLONET") == TRUE)); recipients.AddRange(AppCache.ConnectedDMs); } // This is the normal party chat, plus everyone within 20 units of the sender. else if (channel == ChatChannelType.PlayerParty) { // Can an NPC use the playerparty channel? I feel this is safe ... NWPlayer player = sender.Object; recipients.AddRange(player.PartyMembers.Cast <NWObject>().Where(x => x != sender)); recipients.AddRange(AppCache.ConnectedDMs); needsAreaCheck = true; distanceCheck = 20.0f; } // Normal talk - 20 units. else if (channel == ChatChannelType.PlayerTalk) { needsAreaCheck = true; distanceCheck = 20.0f; } // Whisper - 4 units. else if (channel == ChatChannelType.PlayerWhisper) { needsAreaCheck = true; distanceCheck = 4.0f; } if (needsAreaCheck) { recipients.AddRange(sender.Area.Objects.Where(obj => obj.IsPC && _.GetDistanceBetween(sender, obj) <= distanceCheck)); recipients.AddRange(AppCache.ConnectedDMs.Where(dm => dm.Area == sender.Area && _.GetDistanceBetween(sender, dm) <= distanceCheck)); } // Now we have a list of who is going to actually receive a message, we need to modify // the message for each recipient then dispatch them. foreach (NWObject obj in recipients.Distinct()) { // Generate the final message as perceived by obj. StringBuilder finalMessage = new StringBuilder(); if (channel == ChatChannelType.PlayerShout) { finalMessage.Append("[Holonet] "); } else if (channel == ChatChannelType.PlayerParty) { finalMessage.Append("[Comms] "); if (obj.IsDM) { // Convenience for DMs - append the party members. finalMessage.Append("{ "); int count = 0; NWPlayer player = sender.Object; List <NWCreature> partyMembers = player.PartyMembers.ToList(); foreach (NWCreature otherPlayer in partyMembers) { string name = otherPlayer.Name; finalMessage.Append(name.Substring(0, Math.Min(name.Length, 10))); ++count; if (count >= 3) { finalMessage.Append(", ..."); break; } else if (count != partyMembers.Count) { finalMessage.Append(","); } } finalMessage.Append(" } "); } } SkillType language = LanguageService.GetActiveLanguage(sender); // Wookiees cannot speak any other language (but they can understand them). // Swap their language if they attempt to speak in any other language. CustomRaceType race = (CustomRaceType)_.GetRacialType(sender); if (race == CustomRaceType.Wookiee && language != SkillType.Shyriiwook) { LanguageService.SetActiveLanguage(sender, SkillType.Shyriiwook); language = SkillType.Shyriiwook; } int colour = LanguageService.GetColour(language); byte r = (byte)(colour >> 24 & 0xFF); byte g = (byte)(colour >> 16 & 0xFF); byte b = (byte)(colour >> 8 & 0xFF); if (language != SkillType.Basic) { string languageName = LanguageService.GetName(language); finalMessage.Append(ColorTokenService.Custom($"[{languageName}] ", r, g, b)); } foreach (ChatComponent component in chatComponents) { string text = component.m_Text; if (component.m_Translatable && language != SkillType.Basic) { text = LanguageService.TranslateSnippetForListener(sender, obj.Object, language, component.m_Text); if (colour != 0) { text = ColorTokenService.Custom(text, r, g, b); } } if (component.m_CustomColour) { text = ColorTokenService.Custom(text, component.m_ColourRed, component.m_ColourGreen, component.m_ColourBlue); } finalMessage.Append(text); } // Dispatch the final message - method depends on the original chat channel. // - Shout and party is sent as DMTalk. We do this to get around the restriction that // the PC needs to be in the same area for the normal talk channel. // We could use the native channels for these but the [shout] or [party chat] labels look silly. // - Talk and whisper are sent as-is. ChatChannelType finalChannel = channel; if (channel == ChatChannelType.PlayerShout || channel == ChatChannelType.PlayerParty) { finalChannel = ChatChannelType.DMTalk; } // There are a couple of colour overrides we want to use here. // - One for holonet (shout). // - One for comms (party chat). string finalMessageColoured = finalMessage.ToString(); if (channel == ChatChannelType.PlayerShout) { finalMessageColoured = ColorTokenService.Custom(finalMessageColoured, 0, 180, 255); } else if (channel == ChatChannelType.PlayerParty) { finalMessageColoured = ColorTokenService.Orange(finalMessageColoured); } NWNXChat.SendMessage((int)finalChannel, finalMessageColoured, sender, obj); } MessageHub.Instance.Publish(new OnChatProcessed(sender, channel, isOOC)); }
public static void ApplyComponentBonus(NWItem product, ItemProperty sourceIP) { ComponentBonusType bonusType = (ComponentBonusType)_.GetItemPropertySubType(sourceIP); int amount = _.GetItemPropertyCostTableValue(sourceIP); ItemProperty prop = null; string sourceTag = string.Empty; int attackBonus = 0; // A note about the sourceTags: // It's not currently possible to create custom item properties on items. To get around this, // we look in an inaccessible container which holds the custom item properties. Then, we get the // item that has the item property we want. From there we take that item property and copy it to // the crafted item. // This is a really roundabout way to do it, but it's the only option for now. Hopefully a feature to // directly add the item properties comes in to NWNX in the future. // 2019-06-12: Directly modifying item properties is possible now but I'm not going to do a refactor until later. // Anyone interested in working on this let me know and I can point you in the right direction. - Z for (int x = 1; x <= amount; x++) { switch (bonusType) { case ComponentBonusType.ModSocketRed: sourceTag = "rslot_red"; break; case ComponentBonusType.ModSocketBlue: sourceTag = "rslot_blue"; break; case ComponentBonusType.ModSocketGreen: sourceTag = "rslot_green"; break; case ComponentBonusType.ModSocketYellow: sourceTag = "rslot_yellow"; break; case ComponentBonusType.ModSocketPrismatic: sourceTag = "rslot_prismatic"; break; case ComponentBonusType.DurabilityUp: var maxDur = DurabilityService.GetMaxDurability(product) + amount; DurabilityService.SetMaxDurability(product, maxDur); DurabilityService.SetDurability(product, maxDur); break; case ComponentBonusType.ChargesUp: product.Charges += amount; break; case ComponentBonusType.ACUp: product.CustomAC += amount; break; case ComponentBonusType.HarvestingUp: product.HarvestingBonus += amount; break; case ComponentBonusType.CooldownRecoveryUp: product.CooldownRecovery += amount; break; case ComponentBonusType.ArmorsmithUp: product.CraftBonusArmorsmith += amount; break; case ComponentBonusType.WeaponsmithUp: product.CraftBonusWeaponsmith += amount; break; case ComponentBonusType.CookingUp: product.CraftBonusCooking += amount; break; case ComponentBonusType.EngineeringUp: product.CraftBonusEngineering += amount; break; case ComponentBonusType.FabricationUp: product.CraftBonusFabrication += amount; break; case ComponentBonusType.HPUp: product.HPBonus += amount; break; case ComponentBonusType.FPUp: product.FPBonus += amount; break; case ComponentBonusType.EnmityUp: product.EnmityRate += amount; break; case ComponentBonusType.EnmityDown: product.EnmityRate -= amount; break; case ComponentBonusType.LuckUp: product.LuckBonus += amount; break; case ComponentBonusType.MeditateUp: product.MeditateBonus += amount; break; case ComponentBonusType.RestUp: product.RestBonus += amount; break; case ComponentBonusType.MedicineUp: product.MedicineBonus += amount; break; case ComponentBonusType.HPRegenUp: product.HPRegenBonus += amount; break; case ComponentBonusType.FPRegenUp: product.FPRegenBonus += amount; break; case ComponentBonusType.BaseAttackBonusUp: product.BaseAttackBonus += amount; break; case ComponentBonusType.SneakAttackUp: product.SneakAttackBonus += amount; break; case ComponentBonusType.DamageUp: product.DamageBonus += amount; break; case ComponentBonusType.StructureBonusUp: product.StructureBonus += amount; break; case ComponentBonusType.StrengthUp: product.StrengthBonus += amount; break; case ComponentBonusType.DexterityUp: product.DexterityBonus += amount; break; case ComponentBonusType.ConstitutionUp: product.ConstitutionBonus += amount; break; case ComponentBonusType.WisdomUp: product.WisdomBonus += amount; break; case ComponentBonusType.IntelligenceUp: product.IntelligenceBonus += amount; break; case ComponentBonusType.CharismaUp: product.CharismaBonus += amount; break; case ComponentBonusType.AttackBonusUp: attackBonus += amount; break; case ComponentBonusType.DurationUp: product.DurationBonus += amount; break; case ComponentBonusType.ScanningUp: product.ScanningBonus += amount; break; case ComponentBonusType.ScavengingUp: product.ScavengingBonus += amount; break; case ComponentBonusType.PilotingUp: product.PilotingBonus += amount; break; default: throw new ArgumentOutOfRangeException(); } if (!string.IsNullOrWhiteSpace(sourceTag)) { prop = ItemService.GetCustomItemPropertyByItemTag(sourceTag); } if (prop == null) { return; } BiowareXP2.IPSafeAddItemProperty(product, prop, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true); } // Attack bonus is aggregated into one item property, ensuring that the amount doesn't go over 20. if (attackBonus > 0) { // Look for existing properties, get the value and add it. Then remove that item property. foreach (var ip in product.ItemProperties) { if (_.GetItemPropertyType(ip) == _.ITEM_PROPERTY_ATTACK_BONUS) { amount = _.GetItemPropertyCostTableValue(ip); attackBonus += amount; _.RemoveItemProperty(product, ip); } } // Clamp bonus to 20. if (attackBonus > 20) { attackBonus = 20; } // Time to add the new item property. prop = _.ItemPropertyAttackBonus(attackBonus); BiowareXP2.IPSafeAddItemProperty(product, prop, 0.0f, AddItemPropertyPolicy.ReplaceExisting, false, false); } }
public static Tuple <ItemProperty, int> GetRandomComponentBonusIP(ResourceQuality quality) { string[] normalIP = { "compbon_arm1", // Armorsmith 1 "compbon_cspd1", // Cooldown Recovery 1 "compbon_charges1", // Charges 1 //"compbon_cooking1", // Cooking 1 "compbon_dmg1", // Damage 1 "compbon_eng1", // Engineering 1 "compbon_enmdown1", // Enmity Down 1 "compbon_harv1", // Harvesting 1 "compbon_wpn1", // Weaponsmith 1 "compbon_hp2", // Hit Points 2 "compbon_fp2", // Force Points 2 "compbon_enmup1", // Enmity Up 1 "compbon_med1", // Meditate 1 "compbon_faid1", // Medicine 1 "compbon_fab1", // Fabrication 1 "compbon_scanup1", // Scanning 1 "compbon_rest1", // Rest 1 "compbon_ab1" // Attack Bonus 1 }; string[] highIP = { "compbon_arm2", // Armorsmith 2 "compbon_cspd2", // Cooldown Recovery 2 "compbon_charges2", // Charges 2 //"compbon_cooking2", // Cooking 2 "compbon_dmg2", // Damage 2 "compbon_eng2", // Engineering 2 "compbon_enmdown2", // Enmity Down 2 "compbon_harv2", // Harvesting 2 "compbon_wpn2", // Weaponsmith 2 "compbon_hp4", // Hit Points 4 "compbon_fp4", // Force Points 4 "compbon_enmup2", // Enmity Up 2 "compbon_luck1", // Luck 1 "compbon_med2", // Meditate 2 "compbon_faid2", // Medicine 2 "compbon_hpregen1", // HP Regen 1 "compbon_fpregen1", // FP Regen 1 "compbon_snkatk1", // Sneak Attack 1 "compbon_fab2", // Fabrication 2 "compbon_scanup2", // Scanning 2 "compbon_rest2", // Rest 2 "compbon_str1", // Strength 1 "compbon_dex1", // Dexterity 1 "compbon_con1", // Constitution 1 "compbon_wis1", // Wisdom 1 "compbon_int1", // Intelligence 1 "compbon_cha1", // Charisma 1 "compbon_ab2", // Attack Bonus 2 "compbon_scavup1" // Scavenging 1 }; string[] veryHighIP = { "compbon_arm3", // Armorsmith 3 "compbon_cspd3", // Cooldown Recovery 3 //"compbon_cooking3", // Cooking 3 "compbon_charges3", // Charges 3 "compbon_dmg3", // Damage 3 "compbon_eng3", // Engineering 3 "compbon_enmdown3", // Enmity Down 3 "compbon_harv3", // Harvesting 3 "compbon_wpn3", // Weaponsmith 3 "compbon_hp6", // Hit Points 6 "compbon_fp6", // Force Points 6 "compbon_enmup3", // Enmity Up 3 "compbon_luck2", // Luck 2 "compbon_med3", // Meditate 3 "compbon_faid3", // Medicine 3 "compbon_hpregen2", // HP Regen 2 "compbon_fpregen2", // FP Regen 2 "compbon_snkatk2", // Sneak Attack 2 "compbon_fab3", // Fabrication 3 "compbon_scanup3", // Scanning 3 "compbon_rest3", // Rest 3 "compbon_str2", // Strength 2 "compbon_dex2", // Dexterity 2 "compbon_con2", // Constitution 2 "compbon_wis2", // Wisdom 2 "compbon_int2", // Intelligence 2 "compbon_cha2", // Charisma 2 "compbon_ab3", // Attack Bonus 3 "compbon_scavup2" // Scavenging 2 }; string[] setToUse; switch (quality) { case ResourceQuality.Low: case ResourceQuality.Normal: setToUse = normalIP; break; case ResourceQuality.High: setToUse = highIP; break; case ResourceQuality.VeryHigh: setToUse = veryHighIP; break; default: throw new ArgumentOutOfRangeException(nameof(quality), quality, null); } int index = RandomService.Random(0, setToUse.Length - 1); string itemTag = setToUse[index]; return(new Tuple <ItemProperty, int>(ItemService.GetCustomItemPropertyByItemTag(itemTag), Colorize(itemTag))); }
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. }
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); } } }
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.PCSkill.GetByPlayerIDAndSkillID(oPC.GlobalID, 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; switch (weapon.CustomItemType) { case CustomItemType.FinesseVibroblade: receivesBackgroundBonus = background == BackgroundType.Duelist; break; case CustomItemType.Baton: receivesBackgroundBonus = background == BackgroundType.SecurityOfficer; break; case CustomItemType.HeavyVibroblade: receivesBackgroundBonus = background == BackgroundType.Soldier; break; case CustomItemType.TwinBlade: receivesBackgroundBonus = background == BackgroundType.Berserker; break; case CustomItemType.MartialArtWeapon: receivesBackgroundBonus = background == BackgroundType.TerasKasi; break; case CustomItemType.BlasterPistol: receivesBackgroundBonus = background == BackgroundType.Smuggler; break; case CustomItemType.BlasterRifle: receivesBackgroundBonus = background == BackgroundType.Sharpshooter || background == BackgroundType.Mandalorian; break; } 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. }
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); }
public static void CalculateEffectiveStats(NWPlayer player, NWItem item) { if (item == null || !item.IsValid || !player.IsPlayer || player.IsDMPossessed || player.IsDM || !player.IsInitializedAsPlayer) { return; } // Calculating effective stats can be expensive, so we cache it on the item. SkillType skill; if (item.BaseItemType == BaseItem.Amulet || item.BaseItemType == BaseItem.Ring) { var forceArmor = SkillService.GetPCSkill(player, (int)SkillType.ForceArmor); var lightArmor = SkillService.GetPCSkill(player, (int)SkillType.LightArmor); var heavyArmor = SkillService.GetPCSkill(player, (int)SkillType.HeavyArmor); var highest = forceArmor.Rank; skill = SkillType.ForceArmor; if (lightArmor.Rank > highest) { highest = lightArmor.Rank; skill = SkillType.LightArmor; } if (heavyArmor.Rank > highest) { skill = SkillType.HeavyArmor; } } else { skill = ItemService.GetSkillTypeForItem(item); } var rank = DataService.PCSkill.GetByPlayerIDAndSkillID(player.GlobalID, (int)skill).Rank; using (new Profiler("PlayerStatService::ApplyStatChanges::GetPlayerItemEffectiveStats::ItemLoop::CalculateEffectiveStats")) { // Only scale cooldown recovery if it's a bonus. Penalties remain regardless of skill level difference. item.SetLocalInt("STAT_EFFECTIVE_LEVEL_COOLDOWN_RECOVERY", item.CooldownRecovery > 0 ? CalculateAdjustedValue(item.CooldownRecovery, item.RecommendedLevel, rank, 1) : item.CooldownRecovery); item.SetLocalFloat("STAT_EFFECTIVE_LEVEL_ENMITY_RATE", CalculateAdjustedValue(0.01f * item.EnmityRate, item.RecommendedLevel, rank, 0.00f)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_LUCK_BONUS", CalculateAdjustedValue(item.LuckBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_MEDITATE_BONUS", CalculateAdjustedValue(item.MeditateBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_REST_BONUS", CalculateAdjustedValue(item.RestBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_MEDICINE_BONUS", CalculateAdjustedValue(item.MedicineBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_HP_REGEN_BONUS", CalculateAdjustedValue(item.HPRegenBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_FP_REGEN_BONUS", CalculateAdjustedValue(item.FPRegenBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_WEAPONSMITH_BONUS", CalculateAdjustedValue(item.CraftBonusWeaponsmith, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_COOKING_BONUS", CalculateAdjustedValue(item.CraftBonusCooking, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_ENGINEERING_BONUS", CalculateAdjustedValue(item.CraftBonusEngineering, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_FABRICATION_BONUS", CalculateAdjustedValue(item.CraftBonusFabrication, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_ARMORSMITH_BONUS", CalculateAdjustedValue(item.CraftBonusArmorsmith, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_HARVESTING_BONUS", CalculateAdjustedValue(item.HarvestingBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_PILOTING_BONUS", CalculateAdjustedValue(item.PilotingBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_SCAVENGING_BONUS", CalculateAdjustedValue(item.ScavengingBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_SNEAK_ATTACK_BONUS", CalculateAdjustedValue(item.SneakAttackBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_STRENGTH_BONUS", CalculateAdjustedValue(item.StrengthBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_DEXTERITY_BONUS", CalculateAdjustedValue(item.DexterityBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_CONSTITUTION_BONUS", CalculateAdjustedValue(item.ConstitutionBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_WISDOM_BONUS", CalculateAdjustedValue(item.WisdomBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_INTELLIGENCE_BONUS", CalculateAdjustedValue(item.IntelligenceBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_CHARISMA_BONUS", CalculateAdjustedValue(item.CharismaBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_HP_BONUS", CalculateAdjustedValue(item.HPBonus, item.RecommendedLevel, rank, 0)); item.SetLocalInt("STAT_EFFECTIVE_LEVEL_FP_BONUS", CalculateAdjustedValue(item.FPBonus, item.RecommendedLevel, rank, 0)); } }
public static Tuple <ItemProperty, int> GetRandomComponentBonusIP(ResourceQuality quality) { string[] commonIP = { "compbon_ac1", "compbon_arm1", "compbon_cspd1", "compbon_charges1", "compbon_charges2", "compbon_cooking1", "compbon_dmg1", "compbon_dur1", "compbon_dur2", "compbon_eng1", "compbon_enmdown1", "compbon_epup1", "compbon_epdown1", "compbon_edup1", "compbon_eddown1", "compbon_dpdown1", "compbon_dpup1", "compbon_dddown1", "compbon_ddup1", "compbon_lpdown1", "compbon_lpup1", "compbon_lddown1", "compbon_ldup1", "compbon_mpdown1", "compbon_mpup1", "compbon_mddown1", "compbon_mdup1", "compbon_harv1", "compbon_wpn1", "compbon_hp2", "compbon_fp2", "compbon_enmup1", "compbon_med1", "compbon_faid1", "compbon_fab1", "compbon_scanup1", "compbon_rest1", "compbon_ab1" }; string[] uncommonIP = { "compbon_ac2", "compbon_arm2", "compbon_cspd2", "compbon_charges3", "compbon_cooking2", "compbon_dmg2", "compbon_dur3", "compbon_eng2", "compbon_enmdown2", "compbon_epup2", "compbon_epdown2", "compbon_edup2", "compbon_eddown2", "compbon_dpdown2", "compbon_dpup2", "compbon_dddown2", "compbon_ddup2", "compbon_lpdown2", "compbon_lpup2", "compbon_lddown2", "compbon_ldup2", "compbon_mpdown2", "compbon_mpup2", "compbon_mddown2", "compbon_mdup2", "compbon_harv2", "compbon_wpn2", "compbon_hp4", "compbon_fp4", "compbon_enmup2", "compbon_luck1", "compbon_med2", "compbon_faid2", "compbon_hpregen1", "compbon_fpregen1", "compbon_snkatk1", "compbon_fab2", "compbon_scanup2", "compbon_rest2", "compbon_str1", "compbon_dex1", "compbon_con1", "compbon_wis1", "compbon_int1", "compbon_cha1", "compbon_ab2", "compbon_scavup1" }; string[] rareIP = { "compbon_ac3", "compbon_arm3", "compbon_cspd3", "compbon_cooking3", "compbon_dmg3", "compbon_eng3", "compbon_enmdown3", "compbon_epup3", "compbon_epdown3", "compbon_edup3", "compbon_eddown3", "compbon_dpdown3", "compbon_dpup3", "compbon_dddown3", "compbon_ddup3", "compbon_lpdown3", "compbon_lpup3", "compbon_lddown3", "compbon_ldup3", "compbon_mpdown3", "compbon_mpup3", "compbon_mddown3", "compbon_mdup3", "compbon_harv3", "compbon_wpn3", "compbon_hp6", "compbon_fp6", "compbon_enmup3", "compbon_luck2", "compbon_med3", "compbon_faid3", "compbon_hpregen2", "compbon_fpregen2", "compbon_snkatk2", "compbon_fab3", "compbon_scanup3", "compbon_rest3", "compbon_str2", "compbon_dex2", "compbon_con2", "compbon_wis2", "compbon_int2", "compbon_cha2", "compbon_ab3", "compbon_scavup2" }; string[] ultraRareIP = { "compbon_luck3", "compbon_hpregen3", "compbon_fpregen3", "compbon_snkatk3", "compbon_str3", "compbon_dex3", "compbon_con3", "compbon_wis3", "compbon_int3", "compbon_cha3", "compbon_fpup1", "compbon_faup1", "compbon_fdup1", "compbon_scavup3" }; // Order: Common, Uncommon, Rare, Ultra Rare int[] chance; switch (quality) { case ResourceQuality.Low: chance = new[] { 100, 0, 0, 0 }; break; case ResourceQuality.Normal: chance = new[] { 80, 20, 0, 0 }; break; case ResourceQuality.High: chance = new[] { 20, 40, 40, 0 }; break; case ResourceQuality.VeryHigh: chance = new[] { 0, 0, 70, 30 }; break; default: throw new ArgumentOutOfRangeException(nameof(quality)); } string[] setToUse = null; int index = RandomService.GetRandomWeightedIndex(chance); switch (index) { case 0: setToUse = commonIP; break; case 1: setToUse = uncommonIP; break; case 2: setToUse = rareIP; break; case 3: setToUse = ultraRareIP; break; } if (setToUse == null) { throw new NullReferenceException(nameof(setToUse)); } index = RandomService.Random(0, setToUse.Length - 1); string itemTag = setToUse[index]; return(new Tuple <ItemProperty, int>(ItemService.GetCustomItemPropertyByItemTag(itemTag), index)); }
private static void ApplyEquipmentPenalties(NWPlayer oPC, NWItem oItem) { SkillType skill = ItemService.GetSkillTypeForItem(oItem); if (skill == SkillType.Unknown) { return; } int rank = GetPCSkillRank(oPC, skill); int delta = oItem.RecommendedLevel - rank; if (delta <= 0) { return; } int str = 0; int dex = 0; int con = 0; int wis = 0; int @int = 0; int cha = 0; int ab = 0; int eb = 0; foreach (var ip in oItem.ItemProperties) { int type = _.GetItemPropertyType(ip); int value = _.GetItemPropertyCostTableValue(ip); if (type == ITEM_PROPERTY_ABILITY_BONUS) { int abilityType = _.GetItemPropertySubType(ip); switch (abilityType) { case ABILITY_STRENGTH: str += value; break; case ABILITY_CONSTITUTION: con += value; break; case ABILITY_DEXTERITY: dex += value; break; case ABILITY_WISDOM: wis += value; break; case ABILITY_INTELLIGENCE: @int += value; break; case ABILITY_CHARISMA: cha += value; break; } } else if (type == ITEM_PROPERTY_DECREASED_ABILITY_SCORE) { int abilityType = _.GetItemPropertySubType(ip); switch (abilityType) { case ABILITY_STRENGTH: str -= value; break; case ABILITY_CONSTITUTION: con -= value; break; case ABILITY_DEXTERITY: dex -= value; break; case ABILITY_WISDOM: wis -= value; break; case ABILITY_INTELLIGENCE: @int -= value; break; case ABILITY_CHARISMA: cha -= value; break; } } else if (type == ITEM_PROPERTY_ATTACK_BONUS) { ab += value; } else if (type == ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER) { ab -= value; } else if (type == ITEM_PROPERTY_ENHANCEMENT_BONUS) { eb += value; } else if (type == ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER) { eb -= value; } } // Apply penalties only if total value is greater than 0. Penalties don't scale. if (str > 0) { int newStr = 1 + delta / 5; if (newStr > str) { newStr = str; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_STRENGTH, newStr); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (dex > 0) { int newDex = 1 + delta / 5; if (newDex > dex) { newDex = dex; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_DEXTERITY, newDex); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (con > 0) { int newCon = 1 + delta / 5; if (newCon > con) { newCon = con; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_CONSTITUTION, newCon); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (@int > 0) { int newInt = 1 + delta / 5; if (newInt > @int) { newInt = @int; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_INTELLIGENCE, newInt); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (wis > 0) { int newWis = 1 + delta / 5; if (newWis > wis) { newWis = wis; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_WISDOM, newWis); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (cha > 0) { int newCha = 1 + delta / 5; if (newCha > cha) { newCha = cha; } ItemProperty ip = _.ItemPropertyDecreaseAbility(ABILITY_CHARISMA, newCha); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (ab > 0) { int newAB = 1 + delta / 5; if (newAB > ab) { newAB = ab; } ItemProperty ip = _.ItemPropertyAttackPenalty(newAB); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } if (eb > 0) { int newEB = 1 + delta / 5; if (newEB > eb) { newEB = eb; } ItemProperty ip = _.ItemPropertyEnhancementPenalty(newEB); ip = _.TagItemProperty(ip, IPEquipmentPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ip, 0.0f, AddItemPropertyPolicy.IgnoreExisting, false, false); } }
private static void ApplyWeaponPenalties(NWPlayer oPC, NWItem oItem) { SkillType skillType = ItemService.GetSkillTypeForItem(oItem); if (skillType == SkillType.Unknown || skillType == SkillType.HeavyArmor || skillType == SkillType.LightArmor || skillType == SkillType.ForceArmor || skillType == SkillType.Shields) { return; } int skillID = (int)skillType; int rank = GetPCSkillRank(oPC, skillID); int recommendedRank = oItem.RecommendedLevel; if (rank >= recommendedRank) { return; } int delta = rank - recommendedRank; int penalty; if (delta <= -20) { penalty = 99; } else if (delta <= -16) { penalty = 5; } else if (delta <= -12) { penalty = 4; } else if (delta <= -8) { penalty = 3; } else if (delta <= -4) { penalty = 2; } else if (delta <= 0) { penalty = 1; } else { penalty = 99; } // No combat damage penalty if (penalty == 99) { ItemProperty noDamage = _.ItemPropertyNoDamage(); noDamage = _.TagItemProperty(noDamage, IPWeaponPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, noDamage, 0.0f, AddItemPropertyPolicy.ReplaceExisting, false, false); penalty = 5; // Reset to 5 so that the following penalties apply. } // Decreased attack penalty ItemProperty ipPenalty = _.ItemPropertyAttackPenalty(penalty); ipPenalty = _.TagItemProperty(ipPenalty, IPWeaponPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ipPenalty, 0.0f, AddItemPropertyPolicy.ReplaceExisting, false, false); // Decreased damage penalty ipPenalty = _.ItemPropertyDamagePenalty(penalty); ipPenalty = _.TagItemProperty(ipPenalty, IPWeaponPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ipPenalty, 0.0f, AddItemPropertyPolicy.ReplaceExisting, false, false); // Decreased enhancement bonus penalty ipPenalty = _.ItemPropertyEnhancementPenalty(penalty); ipPenalty = _.TagItemProperty(ipPenalty, IPWeaponPenaltyTag); BiowareXP2.IPSafeAddItemProperty(oItem, ipPenalty, 0.0f, AddItemPropertyPolicy.ReplaceExisting, false, false); oPC.SendMessage("A penalty has been applied to your weapon '" + oItem.Name + "' due to your skill being under the recommended level."); }
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); }
public static void ApplyComponentBonus(NWItem product, ItemProperty sourceIP) { ComponentBonusType bonusType = (ComponentBonusType)_.GetItemPropertySubType(sourceIP); int amount = _.GetItemPropertyCostTableValue(sourceIP); ItemProperty prop = null; string sourceTag = string.Empty; // A note about the sourceTags: // It's not currently possible to create custom item properties on items. To get around this, // we look in an inaccessible container which holds the custom item properties. Then, we get the // item that has the item property we want. From there we take that item property and copy it to // the crafted item. // This is a really roundabout way to do it, but it's the only option for now. Hopefully a feature to // directly add the item properties comes in to NWNX in the future. for (int x = 1; x <= amount; x++) { switch (bonusType) { case ComponentBonusType.ModSocketRed: sourceTag = "rslot_red"; break; case ComponentBonusType.ModSocketBlue: sourceTag = "rslot_blue"; break; case ComponentBonusType.ModSocketGreen: sourceTag = "rslot_green"; break; case ComponentBonusType.ModSocketYellow: sourceTag = "rslot_yellow"; break; case ComponentBonusType.ModSocketPrismatic: sourceTag = "rslot_prismatic"; break; case ComponentBonusType.DurabilityUp: var maxDur = DurabilityService.GetMaxDurability(product) + amount; DurabilityService.SetMaxDurability(product, maxDur); DurabilityService.SetDurability(product, maxDur); break; case ComponentBonusType.ChargesUp: product.Charges += amount; break; case ComponentBonusType.ACUp: product.CustomAC += amount; break; case ComponentBonusType.HarvestingUp: product.HarvestingBonus += amount; break; case ComponentBonusType.CastingSpeedUp: product.CastingSpeed += amount; break; case ComponentBonusType.ArmorsmithUp: product.CraftBonusArmorsmith += amount; break; case ComponentBonusType.WeaponsmithUp: product.CraftBonusWeaponsmith += amount; break; case ComponentBonusType.CookingUp: product.CraftBonusCooking += amount; break; case ComponentBonusType.EngineeringUp: product.CraftBonusEngineering += amount; break; case ComponentBonusType.FabricationUp: product.CraftBonusFabrication += amount; break; case ComponentBonusType.HPUp: product.HPBonus += amount; break; case ComponentBonusType.FPUp: product.FPBonus += amount; break; case ComponentBonusType.EnmityUp: product.EnmityRate += amount; break; case ComponentBonusType.EnmityDown: product.EnmityRate -= amount; break; case ComponentBonusType.DarkPotencyUp: product.DarkPotencyBonus += amount; break; case ComponentBonusType.LightPotencyUp: product.LightPotencyBonus += amount; break; case ComponentBonusType.MindPotencyUp: product.MindPotencyBonus += amount; break; case ComponentBonusType.LuckUp: product.LuckBonus += amount; break; case ComponentBonusType.MeditateUp: product.MeditateBonus += amount; break; case ComponentBonusType.RestUp: product.RestBonus += amount; break; case ComponentBonusType.MedicineUp: product.MedicineBonus += amount; break; case ComponentBonusType.HPRegenUp: product.HPRegenBonus += amount; break; case ComponentBonusType.FPRegenUp: product.FPRegenBonus += amount; break; case ComponentBonusType.BaseAttackBonusUp: product.BaseAttackBonus += amount; break; case ComponentBonusType.SneakAttackUp: product.SneakAttackBonus += amount; break; case ComponentBonusType.DamageUp: product.DamageBonus += amount; break; case ComponentBonusType.DarkPotencyDown: product.DarkPotencyBonus -= amount; break; case ComponentBonusType.LightPotencyDown: product.LightPotencyBonus -= amount; break; case ComponentBonusType.StructureBonusUp: product.StructureBonus += amount; break; case ComponentBonusType.StrengthUp: product.StrengthBonus += amount; break; case ComponentBonusType.DexterityUp: product.DexterityBonus += amount; break; case ComponentBonusType.ConstitutionUp: product.ConstitutionBonus += amount; break; case ComponentBonusType.WisdomUp: product.WisdomBonus += amount; break; case ComponentBonusType.IntelligenceUp: product.IntelligenceBonus += amount; break; case ComponentBonusType.CharismaUp: product.CharismaBonus += amount; break; case ComponentBonusType.AttackBonusUp: prop = _.ItemPropertyAttackBonus(amount); break; case ComponentBonusType.DurationUp: product.DurationBonus += amount; break; case ComponentBonusType.ScanningUp: product.ScanningBonus += amount; break; case ComponentBonusType.ScavengingUp: product.ScavengingBonus += amount; break; case ComponentBonusType.MindPotencyDown: product.MindPotencyBonus -= amount; break; case ComponentBonusType.ElectricalPotencyUp: product.ElectricalPotencyBonus += amount; break; case ComponentBonusType.ElectricalPotencyDown: product.ElectricalPotencyBonus -= amount; break; case ComponentBonusType.ForcePotencyUp: product.ForcePotencyBonus += amount; break; case ComponentBonusType.ForcePotencyDown: product.ForcePotencyBonus -= amount; break; case ComponentBonusType.ForceAccuracyUp: product.ForceAccuracyBonus += amount; break; case ComponentBonusType.ForceAccuracyDown: product.ForceAccuracyBonus -= amount; break; case ComponentBonusType.ForceDefenseUp: product.ForceDefenseBonus += amount; break; case ComponentBonusType.ForceDefenseDown: product.ForceDefenseBonus -= amount; break; case ComponentBonusType.ElectricalDefenseUp: product.ElectricalDefenseBonus += amount; break; case ComponentBonusType.ElectricalDefenseDown: product.ElectricalDefenseBonus -= amount; break; case ComponentBonusType.MindDefenseUp: product.MindDefenseBonus += amount; break; case ComponentBonusType.MindDefenseDown: product.MindDefenseBonus -= amount; break; case ComponentBonusType.LightDefenseUp: product.LightDefenseBonus += amount; break; case ComponentBonusType.LightDefenseDown: product.LightDefenseBonus -= amount; break; case ComponentBonusType.DarkDefenseUp: product.DarkDefenseBonus += amount; break; case ComponentBonusType.DarkDefenseDown: product.DarkDefenseBonus -= amount; break; case ComponentBonusType.PilotingUp: product.PilotingBonus += amount; break; default: throw new ArgumentOutOfRangeException(); } if (!string.IsNullOrWhiteSpace(sourceTag)) { prop = ItemService.GetCustomItemPropertyByItemTag(sourceTag); } if (prop == null) { return; } BiowareXP2.IPSafeAddItemProperty(product, prop, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true); } }