diff --git a/Diagnostics/CommunityExpansionBlueprints/Assets.txt b/Diagnostics/CommunityExpansionBlueprints/Assets.txt index f52831eaef..48891deb28 100644 --- a/Diagnostics/CommunityExpansionBlueprints/Assets.txt +++ b/Diagnostics/CommunityExpansionBlueprints/Assets.txt @@ -898,7 +898,6 @@ MetamagicLearnExtended SolastaCommunityExpansion.Feats.FeatureDefinitionMetamagi MetamagicLearnHeightened SolastaCommunityExpansion.Feats.FeatureDefinitionMetamagicOption FeatureDefinition 8a74dca9-b0a7-4519-aa84-d682a0272e7c MetamagicLearnQuickened SolastaCommunityExpansion.Feats.FeatureDefinitionMetamagicOption FeatureDefinition f1f2a8b9-e290-4ba9-9118-83c2ca19622a MetamagicLearnTwinned SolastaCommunityExpansion.Feats.FeatureDefinitionMetamagicOption FeatureDefinition 84572060-3187-41f7-abad-30ad4a217511 -ModifierTitanFighting FeatureDefinitionAdditionalDamage FeatureDefinition 63afb9fa-958d-5aff-b2dd-062c29be1d25 MoonlitBonusCantrips SolastaCommunityExpansion.CustomDefinitions.FeatureDefinitionFreeBonusCantrips FeatureDefinition eb0e5a7d-1f16-5a77-be8a-16e920c53d56 MoonlitCustomInvisibilityFeature SolastaCommunityExpansion.Classes.Warlock.Subclasses.FeatureDefinitionMoonlitInvisibility FeatureDefinition fe1cc64a-0db4-596f-ae09-e0fc3c668f6a MoonlitDanceoftheNightSky FeatureDefinitionPower FeatureDefinition e3699a79-cda9-5354-b289-84ee80815369 @@ -1096,6 +1095,7 @@ TinkererInfusionReplace SolastaCommunityExpansion.CustomDefinitions.FeatureDefin TinkererMagicalTinkering FeatureDefinitionBonusCantrips FeatureDefinition 2b5b6e4a-00e4-55b9-b453-4ede2fc58ad6 TinkererSoulOfArtificeSavingThrow FeatureDefinitionSavingThrowAffinity FeatureDefinition 0f0f5e39-3044-5ccc-a9c9-45213784b1d0 TinkererSpellStoringItem FeatureDefinitionPower FeatureDefinition a96e75c2-86ab-5062-a8b1-50f07d3884a1 +TitanFightingAttackModifier SolastaCommunityExpansion.CustomDefinitions.FeatureDefinitionOnAttackEffect FeatureDefinition 850acc4e-4212-5a93-9bbf-36fdca74fbd9 ToadKingBasicToxin FeatureDefinitionPower FeatureDefinition 5f61bc99-d0e9-5411-933b-28820be8dd64 ToadKingConditionAffinityPoisonImmunity FeatureDefinitionConditionAffinity FeatureDefinition 0f55f5e6-0f4b-5e9c-ac1b-b898a3d2ad4c ToadKingCroak FeatureDefinitionPower FeatureDefinition 8cbe866a-1e48-5814-85d8-137cc9040671 @@ -1185,7 +1185,6 @@ EWResonatingStrikeDamageBonus FeatureDefinitionAdditionalDamage FeatureDefinitio EWSunlightBladeDamageBonus FeatureDefinitionAdditionalDamage FeatureDefinitionAdditionalDamage a63c2b93-2a11-5742-b23b-791a93e0bfae ForceArtilleryAdditionalDamage FeatureDefinitionAdditionalDamage FeatureDefinitionAdditionalDamage 2e726b2e-052f-482f-a869-721851fcb407 LightningSpearAdditionalDamage FeatureDefinitionAdditionalDamage FeatureDefinitionAdditionalDamage 52d19882-ca63-422d-aece-d80f806859a8 -ModifierTitanFighting FeatureDefinitionAdditionalDamage FeatureDefinitionAdditionalDamage 63afb9fa-958d-5aff-b2dd-062c29be1d25 PathOfTheLightIlluminatingStrike SolastaCommunityExpansion.Subclasses.Barbarian.PathOfTheLight+FeatureDefinitionAdditionalDamageIlluminatingStrike FeatureDefinitionAdditionalDamage 6cf534e4-d124-50f4-803a-7cfd0b277e5f WarlockEldritchSmiteDamage FeatureDefinitionAdditionalDamage FeatureDefinitionAdditionalDamage 7c5af473-ff2c-5bdb-a229-4b2a350322b0 AbilityAffinityInfusionMagicalStrength FeatureDefinitionAbilityCheckAffinity FeatureDefinitionAffinity 9122a4d0-4675-5fd4-964a-0882b06592c0 diff --git a/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionAdditionalDamage/ModifierTitanFighting.json b/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionAdditionalDamage/ModifierTitanFighting.json deleted file mode 100644 index f8f8159c37..0000000000 --- a/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionAdditionalDamage/ModifierTitanFighting.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "$type": "FeatureDefinitionAdditionalDamage, Assembly-CSharp", - "notificationTag": "BracersOfArchery", - "limitedUsage": "None", - "triggerCondition": "AlwaysActive", - "requiredProperty": "MeleeWeapon", - "attackModeOnly": true, - "requiredTargetCondition": "Definition:ConditionMarkedByHunter:dbc2137c1fae2334da286399dea38fd0", - "requiredTargetSenseType": "Darkvision", - "requiredTargetCreatureTag": "", - "requiredCharacterFamily": null, - "damageValueDetermination": "Die", - "damageDieType": "D1", - "damageDiceNumber": 2, - "additionalDamageType": "SameAsBaseDamage", - "specificDamageType": "Radiant", - "damageAdvancement": "None", - "diceByRankTable": [], - "familiesWithAdditionalDice": [], - "familiesDiceNumber": 1, - "ignoreCriticalDoubleDice": false, - "hasSavingThrow": false, - "savingThrowAbility": "Dexterity", - "dcComputation": "FixedValue", - "savingThrowDC": 10, - "damageSaveAffinity": "None", - "conditionOperations": [], - "addLightSource": false, - "lightSourceForm": { - "$type": "LightSourceForm, Assembly-CSharp", - "lightSourceType": "Basic", - "brightRange": 4, - "dimAdditionalRange": 4, - "color": { - "$type": "UnityEngine.Color, UnityEngine.CoreModule", - "r": 1.0, - "g": 1.0, - "b": 1.0, - "a": 1.0 - }, - "graphicsPrefabReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "applyToSelf": false - }, - "impactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "particlesBasedOnAncestryDamageType": false, - "acidImpactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "coldImpactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "fireImpactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "lightningImpactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "poisonImpactParticleReference": { - "$type": "UnityEngine.AddressableAssets.AssetReference, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "computeDescription": false, - "guiPresentation": { - "$type": "GuiPresentation, Assembly-CSharp", - "hidden": false, - "title": "Feature/&NoContentTitle", - "description": "Equipment/&BracersOfArchery_Function_Description", - "spriteReference": { - "$type": "UnityEngine.AddressableAssets.AssetReferenceSprite, Unity.Addressables", - "m_AssetGUID": "", - "m_SubObjectName": "", - "m_SubObjectType": "" - }, - "color": { - "$type": "UnityEngine.Color, UnityEngine.CoreModule", - "r": 1.0, - "g": 1.0, - "b": 1.0, - "a": 1.0 - }, - "symbolChar": "221E", - "sortOrder": 0, - "unusedInSolastaCOTM": false, - "usedInValleyDLC": false - }, - "contentCopyright": "UserContent", - "guid": "63afb9fa-958d-5aff-b2dd-062c29be1d25", - "contentPack": 9999, - "name": "ModifierTitanFighting" -} \ No newline at end of file diff --git a/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionOnAttackEffect/TitanFightingAttackModifier.json b/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionOnAttackEffect/TitanFightingAttackModifier.json new file mode 100644 index 0000000000..6d2c9518ef --- /dev/null +++ b/Diagnostics/CommunityExpansionBlueprints/FeatureDefinitionOnAttackEffect/TitanFightingAttackModifier.json @@ -0,0 +1,30 @@ +{ + "$type": "SolastaCommunityExpansion.CustomDefinitions.FeatureDefinitionOnAttackEffect, SolastaCommunityExpansion", + "guiPresentation": { + "$type": "GuiPresentation, Assembly-CSharp", + "hidden": false, + "title": "Feature/&NoContentTitle", + "description": "Feature/&NoContentTitle", + "spriteReference": { + "$type": "UnityEngine.AddressableAssets.AssetReferenceSprite, Unity.Addressables", + "m_AssetGUID": "", + "m_SubObjectName": null, + "m_SubObjectType": null + }, + "color": { + "$type": "UnityEngine.Color, UnityEngine.CoreModule", + "r": 1.0, + "g": 1.0, + "b": 1.0, + "a": 1.0 + }, + "symbolChar": "221E", + "sortOrder": 0, + "unusedInSolastaCOTM": false, + "usedInValleyDLC": false + }, + "contentCopyright": "UserContent", + "guid": "850acc4e-4212-5a93-9bbf-36fdca74fbd9", + "contentPack": 9999, + "name": "TitanFightingAttackModifier" +} \ No newline at end of file diff --git a/Diagnostics/CommunityExpansionBlueprints/FightingStyleDefinitionCustomizable/TitanFighting.json b/Diagnostics/CommunityExpansionBlueprints/FightingStyleDefinitionCustomizable/TitanFighting.json index fb1c58fa5a..bdb69df617 100644 --- a/Diagnostics/CommunityExpansionBlueprints/FightingStyleDefinitionCustomizable/TitanFighting.json +++ b/Diagnostics/CommunityExpansionBlueprints/FightingStyleDefinitionCustomizable/TitanFighting.json @@ -1,7 +1,7 @@ { "$type": "SolastaCommunityExpansion.CustomDefinitions.FightingStyleDefinitionCustomizable, SolastaCommunityExpansion", "features": [ - "Definition:ModifierTitanFighting:63afb9fa-958d-5aff-b2dd-062c29be1d25" + "Definition:TitanFightingAttackModifier:850acc4e-4212-5a93-9bbf-36fdca74fbd9" ], "condition": "RangedWeaponAttack", "guiPresentation": { diff --git a/Diagnostics/NexusDescription.txt b/Diagnostics/NexusDescription.txt index 76622d30dd..c4ce4a95e1 100644 --- a/Diagnostics/NexusDescription.txt +++ b/Diagnostics/NexusDescription.txt @@ -195,7 +195,7 @@ While you are not wearing any armor, your AC equals 10 + your Dexterity modifier [*][b]Fighting Style: Great Weapon[/b]: When you roll a 1 or 2 on a damage die for an attack you make with a melee weapon that you are wielding with two hands, you can reroll the die. You must use the new roll, even if it is a 1 or a 2. The weapon must have the two-handed or versatile property for you to gain this benefit [*][b]Fighting Style: Protection[/b]: When a creature you can see attacks a target other than you that is within 5 feet of you, you can use your reaction to impose disadvantage on the attack roll. You must be wielding a shield [*][b]Fighting Style: Pugilist[/b]: While you are completely unarmed, your unarmed strikes deal an additional d8 of damage and you can punch with your offhand as a bonus action -[*][b]Fighting Style: Titan Fighting[/b]: You increase your melee attack damage by +2 +[*][b]Fighting Style: Titan Fighting[/b]: You gain a +2 hit against creatures of size large or bigger [*][b]Fighting Style: Two Weapon[/b]: When you engage in two-weapon fighting, you can add your ability modifier to the damage of the second attack [*][b]Fighting Surge (Dexterity)[/b]: Increase your Dexterity by 1, to a maximum of 20. On your turn, you can take one additional action on top of your regular action and a possible bonus action. Once you use this feature, you must finish a long rest before you can use it again @@ -262,7 +262,7 @@ While you are not wearing any armor, your AC equals 10 + your Dexterity modifier [*][b]Blind Fighting[/b]: You have blindsight with a range of 10 feet [*][b]Crippling[/b]: Reduce the speed of your opponent on a melee attack hit to 10 until the end of the next turn [*][b]Pugilist[/b]: While you are completely unarmed, your unarmed strikes deal an additional d8 of damage and you can punch with your offhand as a bonus action -[*][b]Titan Fighting[/b]: You increase your melee attack damage by +2 +[*][b]Titan Fighting[/b]: You gain a +2 hit against creatures of size large or bigger [/list] [size=3][b]Spells[/b][/size] diff --git a/SolastaCommunityExpansion/Api/AdditionalExtensions/CharacterReactionSubitemExtension.cs b/SolastaCommunityExpansion/Api/AdditionalExtensions/CharacterReactionSubitemExtension.cs index 4ccf9acc28..380556dbfe 100644 --- a/SolastaCommunityExpansion/Api/AdditionalExtensions/CharacterReactionSubitemExtension.cs +++ b/SolastaCommunityExpansion/Api/AdditionalExtensions/CharacterReactionSubitemExtension.cs @@ -49,7 +49,7 @@ public static void BindWarcaster(this CharacterReactionSubitem instance, instance.SubitemSelected = subitemSelected; var rectTransform = toggle.GetComponent(); - rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 300); + rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 250); // Hide all slots var slotStatusTable = instance.GetField("slotStatusTable"); diff --git a/SolastaCommunityExpansion/Classes/Warlock/WarlockSpellList.cs b/SolastaCommunityExpansion/Classes/Warlock/WarlockSpellList.cs index 5b1c69b9b0..2a0fe35bd5 100644 --- a/SolastaCommunityExpansion/Classes/Warlock/WarlockSpellList.cs +++ b/SolastaCommunityExpansion/Classes/Warlock/WarlockSpellList.cs @@ -12,7 +12,7 @@ namespace SolastaCommunityExpansion.Classes.Warlock // keep public as CE:MC depends on it public static class WarlockSpells { - public const int PACT_MAGIC_SLOT_TAB_INDEX = 1; + public const int PACT_MAGIC_SLOT_TAB_INDEX = -1; // ideally this would be immutable. Could return a copy so that it can't be accidentally modified. public static List WarlockCastingSlots { get; } = new() diff --git a/SolastaCommunityExpansion/CustomUI/ReactionRequestSpendSpellSlotExtended.cs b/SolastaCommunityExpansion/CustomUI/ReactionRequestSpendSpellSlotExtended.cs index 49e5c7a396..7fcb2f9115 100644 --- a/SolastaCommunityExpansion/CustomUI/ReactionRequestSpendSpellSlotExtended.cs +++ b/SolastaCommunityExpansion/CustomUI/ReactionRequestSpendSpellSlotExtended.cs @@ -1,4 +1,5 @@ -using SolastaCommunityExpansion.Models; +using System.Linq; +using SolastaCommunityExpansion.Models; namespace SolastaCommunityExpansion.CustomUI { @@ -13,8 +14,19 @@ public ReactionRequestSpendSpellSlotExtended(CharacterActionParams actionParams) var hero = actionParams.ActingCharacter.RulesetCharacter as RulesetCharacterHero; var spellRepertoire = ReactionParams.SpellRepertoire; + int selected; + + if (actionParams.StringParameter == "EldritchSmite") + { + var minLevel = SharedSpellsContext.GetWarlockSpellLevel(hero); + + selected = MulticlassGameUiContext.AddAvailableSubLevels(SubOptionsAvailability, hero, spellRepertoire, minLevel, minLevel); + } + else + { + selected = MulticlassGameUiContext.AddAvailableSubLevels(SubOptionsAvailability, hero, spellRepertoire, 1); + } - var selected = MulticlassGameUiContext.AddAvailableSubLevels(SubOptionsAvailability, hero, spellRepertoire, 1); if (selected >= 0) { SelectSubOption(selected); @@ -22,29 +34,29 @@ public ReactionRequestSpendSpellSlotExtended(CharacterActionParams actionParams) _guiCharacter = new GuiCharacter(Character); } - public override int SelectedSubOption => ReactionParams.IntParameter - 1; + public override int SelectedSubOption => SubOptionsAvailability.Keys.ToList().Find(v => v == ReactionParams.IntParameter); public override string SuboptionTag => ReactionParams.StringParameter; - public override void SelectSubOption(int option) => ReactionParams.IntParameter = option + 1; - - public override string FormatTitle() => Gui.Localize(string.Format( - DatabaseRepository.GetDatabase().GetElement(DefinitionName).GuiPresentation.Title, - ReactionParams.StringParameter)); - public override string FormatDescription() => Gui.Format( string.Format( DatabaseRepository.GetDatabase().GetElement(DefinitionName).GuiPresentation .Description, ReactionParams.StringParameter), _guiCharacter.Name); - public override string FormatReactTitle() => Gui.Format( + public override string FormatReactDescription() => Gui.Format( string.Format( - DatabaseRepository.GetDatabase().GetElement(DefinitionName).ReactTitle, + DatabaseRepository.GetDatabase().GetElement(DefinitionName).ReactDescription, ReactionParams.StringParameter), _guiCharacter.Name); - public override string FormatReactDescription() => Gui.Format( + public override string FormatReactTitle() => Gui.Format( string.Format( - DatabaseRepository.GetDatabase().GetElement(DefinitionName).ReactDescription, + DatabaseRepository.GetDatabase().GetElement(DefinitionName).ReactTitle, ReactionParams.StringParameter), _guiCharacter.Name); + + public override string FormatTitle() => Gui.Localize(string.Format( + DatabaseRepository.GetDatabase().GetElement(DefinitionName).GuiPresentation.Title, + ReactionParams.StringParameter)); + + public override void SelectSubOption(int option) => ReactionParams.IntParameter = SubOptionsAvailability.Keys.ToList()[option]; } } diff --git a/SolastaCommunityExpansion/CustomUI/ReactionRequestWarcaster.cs b/SolastaCommunityExpansion/CustomUI/ReactionRequestWarcaster.cs index 6fef56d9a2..71ea40ff54 100644 --- a/SolastaCommunityExpansion/CustomUI/ReactionRequestWarcaster.cs +++ b/SolastaCommunityExpansion/CustomUI/ReactionRequestWarcaster.cs @@ -2,6 +2,7 @@ using System.Linq; using SolastaCommunityExpansion.Builders; using SolastaModApi; +using SolastaModApi.Extensions; using SolastaModApi.Infrastructure; namespace SolastaCommunityExpansion.CustomUI @@ -14,7 +15,7 @@ public class ReactionRequestWarcaster : ReactionRequest public static void Initialize() { ReactWarcasterDefinition = ReactionDefinitionBuilder - .Create(DatabaseHelper.ReactionDefinitions.OpportunityAttack, ReactionRequestWarcaster.Name, + .Create(DatabaseHelper.ReactionDefinitions.OpportunityAttack, Name, DefinitionBuilder.CENamespaceGuid) .SetGuiPresentation(Category.Reaction) .AddToDB(); @@ -24,26 +25,39 @@ public ReactionRequestWarcaster(CharacterActionParams reactionParams) : base(Name, reactionParams) { BuildSuboptions(); - this.ReactionParams.StringParameter2 = "Warcaster"; + ReactionParams.StringParameter2 = "Warcaster"; } void BuildSuboptions() { - this.SubOptionsAvailability.Clear(); - this.SubOptionsAvailability.Add(0, true); + SubOptionsAvailability.Clear(); + SubOptionsAvailability.Add(0, true); var battleManager = ServiceRepository.GetService() as GameLocationBattleManager; if (battleManager == null) { - this.SelectSubOption(0); + SelectSubOption(0); return; } - - var cantrips = new List(); - var reactionParams = this.ReactionParams; + var reactionParams = ReactionParams; var actingCharacter = reactionParams.ActingCharacter; + var rulesetCharacter = actingCharacter.RulesetCharacter; + + // should not trigger if a wildshape form + if (rulesetCharacter is not RulesetCharacterHero) + { + return; + } - actingCharacter.RulesetCharacter.EnumerateReadyAttackCantrips(cantrips); + //TODO: find better way to detect warcaster + var affinities = rulesetCharacter.GetFeaturesByType(); + if (affinities == null || affinities.All(a => a.Name != "MagicAffinityWarCasterFeat")) + { + return; + } + + var cantrips = new List(); + rulesetCharacter.EnumerateReadyAttackCantrips(cantrips); cantrips.RemoveAll(cantrip => { @@ -74,11 +88,11 @@ void BuildSuboptions() foreach (var c in cantrips) { reactionParams.SpellRepertoire.KnownSpells.Add(c); - this.SubOptionsAvailability.Add(i, true); + SubOptionsAvailability.Add(i, true); i++; } - this.SelectSubOption(0); + SelectSubOption(0); } public override int SelectedSubOption @@ -98,8 +112,8 @@ public override int SelectedSubOption public override void SelectSubOption(int option) { - this.ReactionParams.RulesetEffect?.Terminate(false); - var reactionParams = this.ReactionParams; + ReactionParams.RulesetEffect?.Terminate(false); + var reactionParams = ReactionParams; var targetCharacters = reactionParams.TargetCharacters; @@ -150,18 +164,6 @@ public override void SelectSubOption(int option) reactionParams.ActionModifiers.Add(mod); } } - - // for (int i = 0; i < spelltargets; i++) - // { - // var attackParams = new BattleDefinitions.AttackEvaluationParams(); - // var actionModifier = new ActionModifier(); - // attackParams.FillForMagic(actingCharacter, - // actingCharacter.LocationPosition, - // this.ReactionParams.RulesetEffect.EffectDescription, spell.Name, - // reactionParams.TargetCharacters[0], - // reactionParams.TargetCharacters[0].LocationPosition, actionModifier); - // reactionParams.ActionModifiers[i] = actionModifier; - // } } } @@ -172,7 +174,7 @@ public override bool IsStillValid { get { - var targetCharacter = this.ReactionParams.TargetCharacters[0]; + var targetCharacter = ReactionParams.TargetCharacters[0]; return ServiceRepository.GetService().ValidCharacters .Contains(targetCharacter) && !targetCharacter.RulesetCharacter.IsDeadOrDyingOrUnconscious; } @@ -180,9 +182,8 @@ public override bool IsStillValid public override string FormatDescription() { - var caster = new GuiCharacter(this.Character); - var target = new GuiCharacter(this.ReactionParams.TargetCharacters[0]); - return Gui.Format(base.FormatDescription(), caster.Name, target.Name, ""); + var target = new GuiCharacter(ReactionParams.TargetCharacters[0]); + return Gui.Format(base.FormatDescription(), target.Name); } public override string FormatReactDescription() => Gui.Format(base.FormatReactDescription(), ""); @@ -190,7 +191,7 @@ public override string FormatDescription() public override void OnSetInvalid() { base.OnSetInvalid(); - this.ReactionParams.RulesetEffect?.Terminate(false); + ReactionParams.RulesetEffect?.Terminate(false); } } } diff --git a/SolastaCommunityExpansion/DataMiner/PushValue.cs b/SolastaCommunityExpansion/DataMiner/PushValue.cs index f9ce8c5f33..09c10ab372 100644 --- a/SolastaCommunityExpansion/DataMiner/PushValue.cs +++ b/SolastaCommunityExpansion/DataMiner/PushValue.cs @@ -17,7 +17,7 @@ public PushValue(T value, Func getValue, Action setValue) setValue(value); } - #region IDisposable Members +#region IDisposable Members protected override void Dispose(bool disposing) { @@ -25,7 +25,7 @@ protected override void Dispose(bool disposing) setValue = null; } - #endregion +#endregion } } #endif diff --git a/SolastaCommunityExpansion/Displays/GameUiDisplay.cs b/SolastaCommunityExpansion/Displays/GameUiDisplay.cs index b82c382c94..cbc3199f78 100644 --- a/SolastaCommunityExpansion/Displays/GameUiDisplay.cs +++ b/SolastaCommunityExpansion/Displays/GameUiDisplay.cs @@ -104,9 +104,19 @@ internal static void DisplayGameUi() if (UI.Toggle("Enable inventory filtering and sorting", ref toggle, UI.AutoWidth())) { Main.Settings.EnableInventoryFilteringAndSorting = toggle; + Main.Settings.EnableInventoryTaintNonProficientItemsRed = toggle; InventoryManagementContext.RefreshControlsVisibility(); } + if (Main.Settings.EnableInventoryFilteringAndSorting) + { + toggle = Main.Settings.EnableInventoryTaintNonProficientItemsRed; + if (UI.Toggle("+ Taint in red any item the hero isn't proficient with".italic(), ref toggle, UI.AutoWidth())) + { + Main.Settings.EnableInventoryTaintNonProficientItemsRed = toggle; + } + } + toggle = Main.Settings.EnableInvisibleCrownOfTheMagister; if (UI.Toggle("Hide the " + "Crown of the Magister".orange() + " on game UI", ref toggle, UI.AutoWidth())) { diff --git a/SolastaCommunityExpansion/Feats/FeatsValidations.cs b/SolastaCommunityExpansion/Feats/FeatsValidations.cs index c2b72b8036..96f5af0723 100644 --- a/SolastaCommunityExpansion/Feats/FeatsValidations.cs +++ b/SolastaCommunityExpansion/Feats/FeatsValidations.cs @@ -46,7 +46,7 @@ internal static (bool, string) ValidateHasStealthAttack(FeatDefinition _, Rulese { var features = new List(); - hero.EnumerateFeaturesToBrowse(features); + hero.EnumerateFeaturesToBrowse(features); var hasStealthAttack = features.Any(x => x.Name.Contains("SneakAttack")); diff --git a/SolastaCommunityExpansion/FightingStyles/Crippling.cs b/SolastaCommunityExpansion/FightingStyles/Crippling.cs index c70e7befbc..84cf2daa94 100644 --- a/SolastaCommunityExpansion/FightingStyles/Crippling.cs +++ b/SolastaCommunityExpansion/FightingStyles/Crippling.cs @@ -6,6 +6,7 @@ using SolastaModApi; using SolastaModApi.Extensions; using static SolastaModApi.DatabaseHelper.ConditionDefinitions; +using static SolastaModApi.DatabaseHelper.FeatureDefinitionFightingStyleChoices; namespace SolastaCommunityExpansion.FightingStyles { @@ -16,7 +17,10 @@ internal class Crippling : AbstractFightingStyle internal override List GetChoiceLists() { - return new List() { }; + return new List() { + FightingStyleChampionAdditional, + FightingStyleFighter, + FightingStyleRanger,}; } internal override FightingStyleDefinition GetStyle() diff --git a/SolastaCommunityExpansion/FightingStyles/TitanFighting.cs b/SolastaCommunityExpansion/FightingStyles/TitanFighting.cs index 7d4026e9c8..dd994234bf 100644 --- a/SolastaCommunityExpansion/FightingStyles/TitanFighting.cs +++ b/SolastaCommunityExpansion/FightingStyles/TitanFighting.cs @@ -3,45 +3,60 @@ using SolastaCommunityExpansion.Builders; using SolastaCommunityExpansion.Builders.Features; using SolastaCommunityExpansion.CustomDefinitions; -using SolastaModApi; +using static SolastaModApi.DatabaseHelper.CharacterSizeDefinitions; +using static SolastaModApi.DatabaseHelper.CharacterSubclassDefinitions; +using static SolastaModApi.DatabaseHelper.FeatureDefinitionFightingStyleChoices; namespace SolastaCommunityExpansion.FightingStyles { internal class TitanFighting : AbstractFightingStyle { - public readonly Guid TITAN_FIGHTING_BASE_GUID = new("3f7f25de-0ff9-4b63-b38d-8cd7f3a381fc"); + private readonly Guid TITAN_FIGHTING_BASE_GUID = new("3f7f25de-0ff9-4b63-b38d-8cd7f3a381fc"); private FightingStyleDefinitionCustomizable instance; internal override List GetChoiceLists() { - return new List() { }; + return new List() { + FightingStyleChampionAdditional, + FightingStyleFighter, + FightingStylePaladin}; } internal override FightingStyleDefinition GetStyle() { - if (instance == null) + void TitanFightingOnAttackDelegate(GameLocationCharacter attacker, GameLocationCharacter defender, ActionModifier attackModifier, RulesetAttackMode attackerAttackMode) { + // melee attack only + if (attacker == null || defender == null) + { + return; + } + + // grant +2 hit if defender is large or bigger + if (defender.RulesetCharacter.SizeDefinition == Large + || defender.RulesetCharacter.SizeDefinition == Huge + || defender.RulesetCharacter.SizeDefinition == Gargantuan) + { + attackModifier.AttacktoHitTrends.Add( + new RuleDefinitions.TrendInfo(2, RuleDefinitions.FeatureSourceType.FightingStyle, "TitanFighting", this)); + } + } - // This seems to be in HandleCharacterAttackDamage in GameLocationBattleManager.cs - // Perhaps adding a new TriggerConditionAdditionalDamage to check for enemy size (Large or more)? - // This feels like deja vu with doing some patchwork in a very long function - // For now, give a flat +2 melee dmg - var additionalDamage = FeatureDefinitionAdditionalDamageBuilder - .Create(DatabaseHelper.FeatureDefinitionAdditionalDamages.AdditionalDamageBracersOfArchery, "ModifierTitanFighting", TITAN_FIGHTING_BASE_GUID) - // to extend with new condition? something like - // .SetTriggerCondition(RuleDefinitions.AdditionalDamageTriggerCondition.IsSizeLargeOrMore) - .SetTriggerCondition(RuleDefinitions.AdditionalDamageTriggerCondition.AlwaysActive) - .SetRequiredProperty(RuleDefinitions.AdditionalDamageRequiredProperty.MeleeWeapon) - .SetDamageDice(RuleDefinitions.DieType.D1, 2) + if (instance == null) + { + var titanFightingAttackModifier = FeatureDefinitionOnAttackEffectBuilder + .Create("TitanFightingAttackModifier", TITAN_FIGHTING_BASE_GUID) + .SetGuiPresentationNoContent() + .SetOnAttackDelegates(TitanFightingOnAttackDelegate, null) .AddToDB(); instance = CustomizableFightingStyleBuilder .Create("TitanFighting", "edc2a2d1-9f72-4825-b204-d810e911ed12") - .SetGuiPresentation("TitanFighting", Category.FightingStyle, DatabaseHelper.CharacterSubclassDefinitions.PathBerserker.GuiPresentation.SpriteReference) - .SetFeatures(additionalDamage) + .SetGuiPresentation("TitanFighting", Category.FightingStyle, PathBerserker.GuiPresentation.SpriteReference) + .SetFeatures(titanFightingAttackModifier) .AddToDB(); - } + return instance; } } diff --git a/SolastaCommunityExpansion/Info.json b/SolastaCommunityExpansion/Info.json index 9c528e66c9..ef4b3d308f 100644 --- a/SolastaCommunityExpansion/Info.json +++ b/SolastaCommunityExpansion/Info.json @@ -2,8 +2,8 @@ "Id": "SolastaCommunityExpansion", "DisplayName": "Community Expansion", "Author": "SolastaMods", - "Version": "1.3.55.0", - "GameVersion": "1.3.53", + "Version": "1.3.55.1", + "GameVersion": "1.3.55", "ManagerVersion": "0.23.4", "AssemblyName": "SolastaCommunityExpansion.dll", "EntryMethod": "SolastaCommunityExpansion.Main.Load", diff --git a/SolastaCommunityExpansion/Models/CustomFeaturesContext.cs b/SolastaCommunityExpansion/Models/CustomFeaturesContext.cs index 6dbbe30ed3..30bb495008 100644 --- a/SolastaCommunityExpansion/Models/CustomFeaturesContext.cs +++ b/SolastaCommunityExpansion/Models/CustomFeaturesContext.cs @@ -80,7 +80,7 @@ internal static void RecursiveRemoveCustomFeatures(RulesetCharacterHero hero, st .GetElement(prof, false))); } - hero.ClearFeatureModifiers(tag); + hero.UpdateFeatureModifiers(tag); } private static void RemoveFeatureDefinitionPointPool(RulesetCharacterHero hero, RulesetSpellRepertoire heroRepertoire, FeatureDefinitionPointPool featureDefinitionPointPool) diff --git a/SolastaCommunityExpansion/Models/InitialChoicesContext.cs b/SolastaCommunityExpansion/Models/InitialChoicesContext.cs index 4130db3d81..f8911cfe6a 100644 --- a/SolastaCommunityExpansion/Models/InitialChoicesContext.cs +++ b/SolastaCommunityExpansion/Models/InitialChoicesContext.cs @@ -42,14 +42,15 @@ internal static void Load() } LoadVision(); + + SwitchEvenLevelFeats(); + SwitchFirstLevelTotalFeats(); } internal static void LateLoad() { SwitchAsiAndFeat(); SwitchEpicArray(); - SwitchEvenLevelFeats(); - SwitchFirstLevelTotalFeats(); } internal static void LoadVision() diff --git a/SolastaCommunityExpansion/Models/InventoryManagementContext.cs b/SolastaCommunityExpansion/Models/InventoryManagementContext.cs index 1b01ed015e..cf3ac4144f 100644 --- a/SolastaCommunityExpansion/Models/InventoryManagementContext.cs +++ b/SolastaCommunityExpansion/Models/InventoryManagementContext.cs @@ -28,6 +28,8 @@ internal static class InventoryManagementContext private static GuiDropdown SortGuiDropdown { get; set; } + internal static System.Action SelectionChanged { get; set; } + internal static void Load() { var characterInspectionScreen = Gui.GuiService.GetScreen(); @@ -56,7 +58,7 @@ internal static void Load() // on any control change we need to unbind / bind the entire panel to refresh all the additional items gizmos // - void SelectionChanged() + SelectionChanged = () => { var container = containerPanel.Container; var inspectedCharacter = containerPanel.InspectedCharacter; @@ -67,7 +69,8 @@ void SelectionChanged() Flush(container); SortAndFilter(container); containerPanel.Bind(container, inspectedCharacter, dropAreaClicked, visibleSlotsRefreshed); - } + containerPanel.RefreshNow(); + }; // changes the reorder button label and refactor the listener diff --git a/SolastaCommunityExpansion/Models/LevelDownContext.cs b/SolastaCommunityExpansion/Models/LevelDownContext.cs index 9976d8bac8..0b605fd7aa 100644 --- a/SolastaCommunityExpansion/Models/LevelDownContext.cs +++ b/SolastaCommunityExpansion/Models/LevelDownContext.cs @@ -118,10 +118,34 @@ internal static void LevelDown(RulesetCharacterHero hero) RemoveFeaturesByTag(hero, characterClassDefinition, CustomFeaturesContext.CustomizeTag(classTag)); hero.RemoveClassLevel(); - hero.RefreshAll(); + + // supports MC level down scenarios + characterClassDefinition = hero.ClassesHistory.Last(); + hero.ClassesAndSubclasses.TryGetValue(characterClassDefinition, out characterSubclassDefinition); + LevelUpContext.SetSelectedClass(hero, characterClassDefinition); + LevelUpContext.SetSelectedSubclass(hero, characterSubclassDefinition); + + hero.RefreshActiveFightingStyles(); + hero.RefreshActiveItemFeatures(); + hero.RefreshArmorClass(); + hero.RefreshAttackModes(); + hero.RefreshAttributeModifiersFromConditions(); + hero.RefreshAttributeModifiersFromFeats(); + hero.RefreshAttributes(); + hero.RefreshClimbRules(); + hero.RefreshConditionFlags(); + hero.RefreshEncumberance(); + hero.RefreshJumpRules(); + hero.RefreshMoveModes(); + hero.RefreshPersonalityFlags(); + hero.RefreshPowers(); + hero.RefreshProficiencies(); + hero.RefreshSpellRepertoires(); + hero.RefreshTags(); + hero.RefreshUsableDeviceFunctions(); hero.ComputeHitPoints(true); - characterBuildingService.FinalizeCharacter(hero); + characterBuildingService.ReleaseCharacter(hero, true); LevelUpContext.UnregisterHero(hero); @@ -142,6 +166,7 @@ private static void UnlearnSpells(RulesetCharacterHero hero, int indexLevel) return; } + int spellsToRemove; var cantripsToRemove = heroRepertoire.SpellCastingFeature.KnownCantrips[indexLevel] - heroRepertoire.SpellCastingFeature.KnownCantrips[indexLevel - 1]; heroRepertoire.PreparedSpells.Clear(); @@ -176,8 +201,17 @@ private static void UnlearnSpells(RulesetCharacterHero hero, int indexLevel) break; case RuleDefinitions.SpellKnowledge.WholeList: // this is required after patch that adds WholeList to repertoire + var levels = hero.ClassesAndLevels[heroRepertoire.SpellCastingClass]; + + if (levels % 2 > 0) + { + heroRepertoire.KnownSpells.RemoveAll(x => x.SpellLevel == (levels + 1) / 2); + } + + break; + case RuleDefinitions.SpellKnowledge.Selection: - var spellsToRemove = heroRepertoire.SpellCastingFeature.KnownSpells[indexLevel] - heroRepertoire.SpellCastingFeature.KnownSpells[indexLevel - 1]; + spellsToRemove = heroRepertoire.SpellCastingFeature.KnownSpells[indexLevel] - heroRepertoire.SpellCastingFeature.KnownSpells[indexLevel - 1]; while (spellsToRemove-- > 0) { diff --git a/SolastaCommunityExpansion/Models/MulticlassContext.cs b/SolastaCommunityExpansion/Models/MulticlassContext.cs index 94b911b549..1c42c74b62 100644 --- a/SolastaCommunityExpansion/Models/MulticlassContext.cs +++ b/SolastaCommunityExpansion/Models/MulticlassContext.cs @@ -62,11 +62,7 @@ internal static void Load() internal static void LateLoad() { - if (Main.Settings.EnableMulticlass) - { - MulticlassPatchingContext.Load(); // depends on IntegrationContext; - } - + MulticlassPatchingContext.Load(); // depends on IntegrationContext; SharedSpellsContext.Load(); // depends on IntegrationContext } } diff --git a/SolastaCommunityExpansion/Models/MulticlassGameUiContext.cs b/SolastaCommunityExpansion/Models/MulticlassGameUiContext.cs index 71be304fe2..7d08e4e621 100644 --- a/SolastaCommunityExpansion/Models/MulticlassGameUiContext.cs +++ b/SolastaCommunityExpansion/Models/MulticlassGameUiContext.cs @@ -27,25 +27,23 @@ public static void PaintPactSlots( RectTransform rectTransform, bool hasTooltip = false) { - var shortRestSlotsCount = SharedSpellsContext.GetWarlockMaxSlots(heroWithSpellRepertoire); - var longRestSlotsCount = totalSlotsCount - shortRestSlotsCount; - - var shortRestSlotsUsedCount = 0; - var warlockSpellRepertoire = SharedSpellsContext.GetWarlockSpellRepertoire(heroWithSpellRepertoire); var warlockSpellLevel = SharedSpellsContext.GetWarlockSpellLevel(heroWithSpellRepertoire); + var pactSlotsCount = 0; + var pactSlotsRemainingCount = 0; + var pactSlotsUsedCount = 0; + if (warlockSpellRepertoire != null) { - var usedSpellsSlots = warlockSpellRepertoire.GetField>("usedSpellsSlots"); - - usedSpellsSlots.TryGetValue(SharedSpellsContext.MC_PACT_MAGIC_SLOT_TAB_INDEX, out shortRestSlotsUsedCount); + pactSlotsCount = SharedSpellsContext.GetWarlockMaxSlots(heroWithSpellRepertoire); + pactSlotsUsedCount = SharedSpellsContext.GetWarlockUsedSlots(heroWithSpellRepertoire); + pactSlotsRemainingCount = pactSlotsCount - pactSlotsUsedCount; } - var shortRestSlotsRemainingCount = shortRestSlotsCount - shortRestSlotsUsedCount; - var longRestSlotsRemainingCount = totalSlotsRemainingCount - shortRestSlotsRemainingCount; - - var longRestSlotsUsedCount = longRestSlotsCount - longRestSlotsRemainingCount; + var spellSlotsCount = totalSlotsCount - pactSlotsCount; + var spellSlotsRemainingCount = totalSlotsRemainingCount - pactSlotsRemainingCount; + var spellSlotsUsedCount = spellSlotsCount - spellSlotsRemainingCount; for (var index = 0; index < rectTransform.childCount; ++index) { @@ -53,15 +51,15 @@ public static void PaintPactSlots( if (slotLevel <= warlockSpellLevel) { - if (index < longRestSlotsCount) + if (index < spellSlotsCount) { - component.Used.gameObject.SetActive(index >= totalSlotsRemainingCount - shortRestSlotsRemainingCount); - component.Available.gameObject.SetActive(index < totalSlotsRemainingCount - shortRestSlotsRemainingCount); + component.Used.gameObject.SetActive(index >= totalSlotsRemainingCount - pactSlotsRemainingCount); + component.Available.gameObject.SetActive(index < totalSlotsRemainingCount - pactSlotsRemainingCount); } else if (slotLevel == warlockSpellLevel) { - component.Used.gameObject.SetActive(index >= longRestSlotsCount + shortRestSlotsRemainingCount); - component.Available.gameObject.SetActive(index < longRestSlotsCount + shortRestSlotsRemainingCount); + component.Used.gameObject.SetActive(index >= spellSlotsCount + pactSlotsRemainingCount); + component.Available.gameObject.SetActive(index < spellSlotsCount + pactSlotsRemainingCount); } else { @@ -70,7 +68,7 @@ public static void PaintPactSlots( } } - if (index >= longRestSlotsCount && slotLevel <= warlockSpellLevel) + if (index >= spellSlotsCount && slotLevel <= warlockSpellLevel) { component.Available.GetComponent().color = LightGreenSlot; } @@ -95,17 +93,17 @@ public static void PaintPactSlots( { str = "Screen/&SpellSlotsUsedNoneDescription"; } - else if (shortRestSlotsRemainingCount == shortRestSlotsCount) + else if (pactSlotsRemainingCount == pactSlotsCount) { - str = Gui.Format("Screen/&SpellSlotsUsedLongDescription", longRestSlotsUsedCount.ToString()); + str = Gui.Format("Screen/&SpellSlotsUsedLongDescription", spellSlotsUsedCount.ToString()); } - else if (longRestSlotsRemainingCount == longRestSlotsCount) + else if (spellSlotsRemainingCount == spellSlotsCount) { - str = Gui.Format("Screen/&SpellSlotsUsedShortDescription", shortRestSlotsUsedCount.ToString()); + str = Gui.Format("Screen/&SpellSlotsUsedShortDescription", pactSlotsUsedCount.ToString()); } else { - str = Gui.Format("Screen/&SpellSlotsUsedShortLongDescription", shortRestSlotsUsedCount.ToString(), longRestSlotsUsedCount.ToString()); + str = Gui.Format("Screen/&SpellSlotsUsedShortLongDescription", pactSlotsUsedCount.ToString(), spellSlotsUsedCount.ToString()); } rectTransform.GetComponent().Content = str; @@ -124,25 +122,30 @@ public static void PaintSlotsWhite(RectTransform rectTransform) /**Adds available slot level options to optionsAvailability and returns index of pre-picked option, or -1*/ public static int AddAvailableSubLevels(Dictionary optionsAvailability, RulesetCharacterHero hero, - RulesetSpellRepertoire spellRepertoire, int minSpellLebvel = 1) + RulesetSpellRepertoire spellRepertoire, int minSpellLevel = 1, int maxSpellLevel = 0) { var selectedSlot = -1; var warlockSpellLevel = SharedSpellsContext.GetWarlockSpellLevel(hero); - var shortRestSlotsCount = SharedSpellsContext.GetWarlockMaxSlots(hero); + var pactMagicSlotsCount = SharedSpellsContext.GetWarlockMaxSlots(hero); var isMulticaster = SharedSpellsContext.IsMulticaster(hero); var hasPactMagic = warlockSpellLevel > 0; var maxRepertoireLevel = spellRepertoire.MaxSpellLevelOfSpellCastingLevel; - var maxSpellLevel = Math.Max(maxRepertoireLevel, warlockSpellLevel); var selected = false; - for (var level = minSpellLebvel; level <= maxSpellLevel; ++level) + if (maxSpellLevel == 0) + { + maxSpellLevel = Math.Max(maxRepertoireLevel, warlockSpellLevel); + } + + for (var level = minSpellLevel; level <= maxSpellLevel; ++level) { spellRepertoire.GetSlotsNumber(level, out var remaining, out var max); + if (hasPactMagic && level != warlockSpellLevel) { - max -= shortRestSlotsCount; + max -= pactMagicSlotsCount; } if (max > 0 && ( @@ -155,7 +158,7 @@ public static int AddAvailableSubLevels(Dictionary optionsAvailabilit if (!selected && remaining > 0) { selected = true; - selectedSlot = level - minSpellLebvel; + selectedSlot = level - minSpellLevel; } } } diff --git a/SolastaCommunityExpansion/Models/InOutRulesContext.cs b/SolastaCommunityExpansion/Models/MulticlassInOutRulesContext.cs similarity index 93% rename from SolastaCommunityExpansion/Models/InOutRulesContext.cs rename to SolastaCommunityExpansion/Models/MulticlassInOutRulesContext.cs index 1351c98daa..9ed2ee8e8b 100644 --- a/SolastaCommunityExpansion/Models/InOutRulesContext.cs +++ b/SolastaCommunityExpansion/Models/MulticlassInOutRulesContext.cs @@ -5,7 +5,7 @@ namespace SolastaCommunityExpansion.Models { - public static class InOutRulesContext + public static class MulticlassInOutRulesContext { public static void EnumerateHeroAllowedClassDefinitions(RulesetCharacterHero hero, List allowedClasses, ref int selectedClass) { @@ -46,7 +46,19 @@ public static void EnumerateHeroAllowedClassDefinitions(RulesetCharacterHero her } } - allowedClasses.Sort((a, b) => a.FormatTitle().CompareTo(b.FormatTitle())); + allowedClasses.Sort((a, b) => + { + hero.ClassesAndLevels.TryGetValue(a, out var aLevels); + hero.ClassesAndLevels.TryGetValue(b, out var bLevels); + + if (aLevels == bLevels) + { + return a.FormatTitle().CompareTo(b.FormatTitle()); + } + + return bLevels.CompareTo(aLevels); + }); + selectedClass = allowedClasses.IndexOf(hero.ClassesHistory[hero.ClassesHistory.Count - 1]); } diff --git a/SolastaCommunityExpansion/Models/SharedSpellsContext.cs b/SolastaCommunityExpansion/Models/SharedSpellsContext.cs index e2a37611d5..4288a4b6d0 100644 --- a/SolastaCommunityExpansion/Models/SharedSpellsContext.cs +++ b/SolastaCommunityExpansion/Models/SharedSpellsContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using SolastaModApi.Infrastructure; using static FeatureDefinitionCastSpell; using static SolastaCommunityExpansion.Classes.Warlock.WarlockSpells; using static SolastaCommunityExpansion.Level20.SpellsHelper; @@ -200,6 +201,21 @@ public static int GetWarlockMaxSlots(RulesetCharacterHero rulesetCharacterHero) return 0; } + public static int GetWarlockUsedSlots(RulesetCharacterHero rulesetCharacterHero) + { + var repertoire = GetWarlockSpellRepertoire(rulesetCharacterHero); + + if (repertoire != null) + { + repertoire.GetField>("usedSpellsSlots") + .TryGetValue(PACT_MAGIC_SLOT_TAB_INDEX, out var warlockUsedSlots); + + return warlockUsedSlots; + } + + return 0; + } + public static RulesetSpellRepertoire GetWarlockSpellRepertoire(RulesetCharacterHero rulesetCharacterHero) => rulesetCharacterHero.SpellRepertoires.FirstOrDefault(x => IsWarlock(x.SpellCastingClass)); @@ -265,7 +281,5 @@ public static void Load() SubclassCasterType.Add(ConArtistSubclass, CasterType.OneThird); SubclassCasterType.Add(SpellShieldSubclass, CasterType.OneThird); } - - public const int MC_PACT_MAGIC_SLOT_TAB_INDEX = -1; } } diff --git a/SolastaCommunityExpansion/Patches/Bugfix/GameLocationManagerPatcher.cs b/SolastaCommunityExpansion/Patches/Bugfix/GameLocationManagerPatcher.cs index 5d17ed6cb4..797300e980 100644 --- a/SolastaCommunityExpansion/Patches/Bugfix/GameLocationManagerPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Bugfix/GameLocationManagerPatcher.cs @@ -5,7 +5,6 @@ using HarmonyLib; using SolastaCommunityExpansion.Api.AdditionalExtensions; using SolastaCommunityExpansion.CustomDefinitions; -using SolastaModApi; namespace SolastaCommunityExpansion.Patches.Bugfix { @@ -13,6 +12,7 @@ namespace SolastaCommunityExpansion.Patches.Bugfix [HarmonyPatch(typeof(GameLocationManager), "StopCharacterEffectsIfRelevant")] internal static class GameLocationManager_StopCharacterEffectsIfRelevant { +#if false //Disabling force unsummon part since it looks like it is needed internal static void Prefix(GameLocationManager __instance, bool willEnterChainedLocation) { if (willEnterChainedLocation) { return; } @@ -38,6 +38,7 @@ internal static void Prefix(GameLocationManager __instance, bool willEnterChaine } } } +#endif internal static IEnumerable Transpiler(IEnumerable instructions) { diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/CharacterBuildingManagerPatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/CharacterBuildingManagerPatcher.cs index 5ab874f4f8..3e8305eb7b 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/CharacterBuildingManagerPatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/CharacterBuildingManagerPatcher.cs @@ -81,6 +81,13 @@ internal static void Postfix(RulesetCharacterHero hero) return; } + var levels = hero.ClassesAndLevels[spellCastingClass]; + + if (levels % 2 == 0) + { + return; + } + var castingLevel = SharedSpellsContext.GetClassSpellLevel(selectedClassRepertoire); var knownSpells = LevelUpContext.GetAllowedSpells(hero); diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/CharacterReactionItemPatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/CharacterReactionItemPatcher.cs index e6a2fff64b..fa2027b5b8 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/CharacterReactionItemPatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/CharacterReactionItemPatcher.cs @@ -6,6 +6,7 @@ using HarmonyLib; using SolastaCommunityExpansion.Api.AdditionalExtensions; using SolastaCommunityExpansion.CustomUI; +using UnityEngine; namespace SolastaCommunityExpansion.Patches.CustomFeatures.CustomReactions { @@ -35,6 +36,16 @@ internal static IEnumerable Transpiler(IEnumerable() + .SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size); + } + private static void CustomBind(CharacterReactionSubitem instance, RulesetSpellRepertoire spellRepertoire, int slotLevel, @@ -42,7 +53,6 @@ private static void CustomBind(CharacterReactionSubitem instance, bool interactable, CharacterReactionSubitem.SubitemSelectedHandler subitemSelected, ReactionRequest reactionRequest) { - Main.Log($"CustomBind ", true); if (reactionRequest is ReactionRequestWarcaster warcasterRequest) { instance.BindWarcaster(warcasterRequest, slotLevel, interactable, subitemSelected); diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/GameLocationActionManagerPatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/GameLocationActionManagerPatcher.cs index ca26e38347..eb90eb9afe 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/GameLocationActionManagerPatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/CustomReactions/GameLocationActionManagerPatcher.cs @@ -1,7 +1,5 @@ -using System.Linq; -using HarmonyLib; +using HarmonyLib; using SolastaCommunityExpansion.CustomUI; -using SolastaModApi.Extensions; using SolastaModApi.Infrastructure; namespace SolastaCommunityExpansion.Patches.CustomFeatures.CustomReactions @@ -13,16 +11,10 @@ internal static class GameLocationActionManager_ReactForOpportunityAttack { internal static bool Prefix(GameLocationActionManager __instance, CharacterActionParams reactionParams) { - var affinitys = reactionParams?.ActingCharacter?.RulesetCharacter - .GetFeaturesByType(); - if (affinitys != null && affinitys.Any(a => a.Name == "MagicAffinityWarCasterFeat")) - { - __instance.InvokeMethod("AddInterruptRequest", new ReactionRequestWarcaster(reactionParams)); - return false; - } + __instance.InvokeMethod("AddInterruptRequest", new ReactionRequestWarcaster(reactionParams)); - return true; + return false; } } } -} +} \ No newline at end of file diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/OnCharacterAttackEffect/GameLocationBattleManagerPatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/OnCharacterAttackEffect/GameLocationBattleManagerPatcher.cs index 519464721b..8a54944bf2 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/OnCharacterAttackEffect/GameLocationBattleManagerPatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/OnCharacterAttackEffect/GameLocationBattleManagerPatcher.cs @@ -248,6 +248,20 @@ internal static IEnumerator Postfix( for (var spellLevel = 1; spellLevel <= spellRepertoire.MaxSpellLevelOfSpellCastingLevel; spellLevel++) { spellRepertoire.GetSlotsNumber(spellLevel, out var remaining, out var max); + // handle EldritchSmite case that can only consume pact slots + // + // patch here + // + if (featureDefinition is FeatureDefinitionAdditionalDamage featureDefinitionAdditionalDamage + && featureDefinitionAdditionalDamage.NotificationTag == "EldritchSmite") + { + var pactMagicMaxSlots = SharedSpellsContext.GetWarlockMaxSlots(hero); + var pactMagicUsedSlots = SharedSpellsContext.GetWarlockUsedSlots(hero); + + remaining = pactMagicMaxSlots - pactMagicUsedSlots; + } + // end patch + if (remaining > 0) { selectedSpellRepertoire = spellRepertoire; diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/ReactionRequestCastSpellPatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/ReactionRequestCastSpellPatcher.cs index 9cc4384ba2..6b3db2e018 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/ReactionRequestCastSpellPatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/ReactionRequestCastSpellPatcher.cs @@ -14,17 +14,23 @@ internal static class ReactionRequestCastSpell_BuildSlotSubOptions { public static bool Prefix(ReactionRequestCastSpell __instance) { - var hero = __instance.Character.RulesetCharacter as RulesetCharacterHero; - if (hero == null) { return true; } + if (__instance.Character.RulesetCharacter is not RulesetCharacterHero hero) + { + return true; + } - var rulesetEffect = __instance.ReactionParams.RulesetEffect as RulesetEffectSpell; - if (rulesetEffect == null) { return true; } + if (__instance.ReactionParams.RulesetEffect is not RulesetEffectSpell rulesetEffect) + { + return true; + } __instance.SubOptionsAvailability.Clear(); + var spellRepertoire = rulesetEffect.SpellRepertoire; var minSpellLebvel = rulesetEffect.SpellDefinition.SpellLevel; + var selected = MulticlassGameUiContext + .AddAvailableSubLevels(__instance.SubOptionsAvailability, hero, spellRepertoire, minSpellLebvel); - var selected = MulticlassGameUiContext.AddAvailableSubLevels(__instance.SubOptionsAvailability, hero, spellRepertoire, minSpellLebvel); if (selected >= 0) { __instance.SelectSubOption(selected); diff --git a/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/RulesetSpellRepertoirePatcher.cs b/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/RulesetSpellRepertoirePatcher.cs index 5485123d08..7cb16ffdb3 100644 --- a/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/RulesetSpellRepertoirePatcher.cs +++ b/SolastaCommunityExpansion/Patches/CustomFeatures/PactMagic/RulesetSpellRepertoirePatcher.cs @@ -21,10 +21,6 @@ private static bool ShouldNotRun(RulesetSpellRepertoire __instance) || !SharedSpellsContext.IsWarlock(__instance.SpellCastingClass); } - // - // the following 4 patches also exist in CE. The ones in CE get disabled in favor of these - // - // ensures MC Warlocks are treated before SC ones [HarmonyPatch(typeof(RulesetSpellRepertoire), "GetMaxSlotsNumberOfAllLevels")] [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] @@ -41,7 +37,7 @@ internal static bool Prefix( } // handles SC Warlock - ___spellsSlotCapacities.TryGetValue(WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX, out __result); + ___spellsSlotCapacities.TryGetValue(1, out __result); return false; } @@ -104,8 +100,8 @@ internal static bool Prefix( } // handles SC Warlock - ___spellsSlotCapacities.TryGetValue(WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX, out var max); - ___usedSpellsSlots.TryGetValue(WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX, out var used); + ___spellsSlotCapacities.TryGetValue(1, out var max); + ___usedSpellsSlots.TryGetValue(1, out var used); __result = max - used; return false; @@ -136,8 +132,8 @@ internal static bool Prefix( if (spellLevel <= __instance.MaxSpellLevelOfSpellCastingLevel) { - ___spellsSlotCapacities.TryGetValue(WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX, out max); - ___usedSpellsSlots.TryGetValue(WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX, out var used); + ___spellsSlotCapacities.TryGetValue(1, out max); + ___usedSpellsSlots.TryGetValue(1, out var used); remaining = max - used; } @@ -208,8 +204,9 @@ private static void SpendWarlockSlots(RulesetSpellRepertoire rulesetSpellReperto var warlockSpellLevel = SharedSpellsContext.GetWarlockSpellLevel(heroWithSpellRepertoire); var usedSpellsSlots = rulesetSpellRepertoire.GetField>("usedSpellsSlots"); - for (var i = SharedSpellsContext.MC_PACT_MAGIC_SLOT_TAB_INDEX; i <= warlockSpellLevel; i++) + for (var i = WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX; i <= warlockSpellLevel; i++) { + // don't mess with cantrips if (i == 0) { continue; @@ -226,22 +223,23 @@ private static void SpendMulticasterWarlockSlots(RulesetSpellRepertoire __instan { var sharedSpellLevel = SharedSpellsContext.GetSharedSpellLevel(heroWithSpellRepertoire); var warlockSpellLevel = SharedSpellsContext.GetWarlockSpellLevel(heroWithSpellRepertoire); - var warlockMaxSlots = SharedSpellsContext.GetWarlockMaxSlots(heroWithSpellRepertoire); - var usedSpellsSlotsWarlock = warlockSpellRepertoire.GetField>("usedSpellsSlots"); - usedSpellsSlotsWarlock.TryGetValue(SharedSpellsContext.MC_PACT_MAGIC_SLOT_TAB_INDEX, out var warlockUsedSlots); - warlockSpellRepertoire.GetSlotsNumber(slotLevel, out var sharedRemainingSlots, out var sharedMaxSlots); + var pactMaxSlots = SharedSpellsContext.GetWarlockMaxSlots(heroWithSpellRepertoire); + var usedPactSlots = SharedSpellsContext.GetWarlockUsedSlots(heroWithSpellRepertoire); + var pactRemainingSlots = pactMaxSlots - usedPactSlots; - var sharedUsedSlots = sharedMaxSlots - sharedRemainingSlots; + warlockSpellRepertoire.GetSlotsNumber(slotLevel, out var sharedRemainingSlots, out var sharedMaxSlots); - sharedMaxSlots -= warlockMaxSlots; - sharedUsedSlots -= warlockUsedSlots; + sharedMaxSlots -= pactMaxSlots; + sharedRemainingSlots -= pactRemainingSlots; var isShiftPressed = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); - var canConsumePactSlot = warlockUsedSlots < warlockMaxSlots && slotLevel <= warlockSpellLevel; - var canConsumeSpellSlot = sharedUsedSlots < sharedMaxSlots && slotLevel <= sharedSpellLevel; + + var canConsumePactSlot = pactRemainingSlots > 0 && slotLevel <= warlockSpellLevel; + var canConsumeSpellSlot = sharedRemainingSlots > 0 && slotLevel <= sharedSpellLevel; + var forcePactSlot = __instance.SpellCastingClass == IntegrationContext.WarlockClass; - var forceSpellSlot = canConsumeSpellSlot && (isShiftPressed || (!forcePactSlot && sharedSpellLevel < warlockSpellLevel)); + var forceSpellSlot = canConsumeSpellSlot && (isShiftPressed || (!forcePactSlot && sharedSpellLevel <= warlockSpellLevel)); // uses short rest slots across all repertoires if (canConsumePactSlot && !forceSpellSlot) diff --git a/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventoryPanelPatcher.cs b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventoryPanelPatcher.cs index 7531f12712..bb6fb9eaff 100644 --- a/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventoryPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventoryPanelPatcher.cs @@ -16,6 +16,7 @@ internal static void Postfix() } InventoryManagementContext.RefreshControlsVisibility(); + InventoryManagementContext.SelectionChanged(); } } diff --git a/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventorySlotBoxPatcher.cs b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventorySlotBoxPatcher.cs new file mode 100644 index 0000000000..d515f4ee8a --- /dev/null +++ b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/InventorySlotBoxPatcher.cs @@ -0,0 +1,56 @@ +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using SolastaCommunityExpansion.Models; +using UnityEngine.UI; + +namespace SolastaCommunityExpansion.Patches.GameUi.CharacterInspection +{ + [HarmonyPatch(typeof(InventorySlotBox), "RefreshState")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class InventorySlotBox_RefreshState + { + internal static void Postfix(InventorySlotBox __instance, Image ___equipedItemImage) + { + if (Global.IsMultiplayer || Global.InspectedHero == null) + { + return; + } + + if (!Main.Settings.EnableInventoryFilteringAndSorting + || !Main.Settings.EnableInventoryTaintNonProficientItemsRed) + { + return; + } + + if (__instance.InventorySlot == null + || __instance.InventorySlot.EquipedItem == null + || ___equipedItemImage == null) + { + return; + } + + var itemDefinition = __instance.InventorySlot.EquipedItem.ItemDefinition; + + if (!Global.InspectedHero.IsProficientWithItem(itemDefinition)) + { + ___equipedItemImage.color = new UnityEngine.Color(1, 0, 0); + } + } + } + + [HarmonyPatch(typeof(InventorySlotBox), "Unbind")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class InventorySlotBox_Unbind + { + // this should not have any protection to keep the house clean + internal static void Prefix(Image ___equipedItemImage) + { + if (___equipedItemImage == null) + { + return; + } + + ___equipedItemImage.color = new UnityEngine.Color(1, 1, 1); + } + } +} diff --git a/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/ItemMenuModalPatcher.cs b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/ItemMenuModalPatcher.cs index 4acf7bb480..9a8fe5c676 100644 --- a/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/ItemMenuModalPatcher.cs +++ b/SolastaCommunityExpansion/Patches/GameUi/CharacterInspection/ItemMenuModalPatcher.cs @@ -15,10 +15,17 @@ public static bool RequiresDeity(ItemMenuModal itemMenuModal) return itemMenuModal.GuiCharacter.RulesetCharacterHero.ClassesHistory.Exists(x => x.RequiresDeity); } + public static int MaxSpellLevelOfSpellCastingLevel(RulesetSpellRepertoire repertoire) + { + return Models.SharedSpellsContext.GetClassSpellLevel(repertoire); + } + internal static IEnumerable Transpiler(IEnumerable instructions) { var requiresDeityMethod = typeof(CharacterClassDefinition).GetMethod("get_RequiresDeity"); var myRequiresDeityMethod = typeof(ItemMenuModal_SetupFromItem).GetMethod("RequiresDeity"); + var maxSpellLevelOfSpellCastingLevelMethod = typeof(RulesetSpellRepertoire).GetMethod("get_MaxSpellLevelOfSpellCastingLevel"); + var myMaxSpellLevelOfSpellCastingLevelMethod = typeof(ItemMenuModal_SetupFromItem).GetMethod("MaxSpellLevelOfSpellCastingLevel"); foreach (var instruction in instructions) { @@ -28,6 +35,10 @@ internal static IEnumerable Transpiler(IEnumerable ___compatibleFightingStyles, RectTransform ___fightingStylesTable) + { + if (Main.Settings.EnableSortingFightingStyles) + { + ___compatibleFightingStyles + .Sort((a, b) => a.FormatTitle().CompareTo(b.FormatTitle())); + } + + var gridLayoutGroup = ___fightingStylesTable.GetComponent(); + var count = ___compatibleFightingStyles.Count; + + if (OriginalAnchoredPosition == Vector2.zero) + { + OriginalAnchoredPosition = ___fightingStylesTable.anchoredPosition; + } + + if (count > 8) + { + gridLayoutGroup.constraintCount = 3; + ___fightingStylesTable.anchoredPosition = new Vector2(0, +15); + //___fightingStylesTable.localScale = new Vector3(0.8f, 0.8f, 1f); + } + else + { + gridLayoutGroup.constraintCount = 2; + ___fightingStylesTable.anchoredPosition = OriginalAnchoredPosition; + //___fightingStylesTable.localScale = new Vector3(1f, 1f, 1f); + } + } + } +} diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/ArchetypesPreviewModalPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/ArchetypesPreviewModalPatcher.cs new file mode 100644 index 0000000000..c2a3efe9aa --- /dev/null +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/ArchetypesPreviewModalPatcher.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Emit; +using HarmonyLib; +using SolastaCommunityExpansion.Models; + +namespace SolastaCommunityExpansion.Patches.Multiclass.LevelUp +{ + // filter features already taken on subclass display + [HarmonyPatch(typeof(ArchetypesPreviewModal), "Refresh")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class ArchetypesPreviewModal_Refresh + { + public static int Level(FeatureUnlockByLevel featureUnlockByLevel) + { + var hero = Global.ActiveLevelUpHero; + var isLevelingUp = LevelUpContext.IsLevelingUp(hero); + var selectedClass = LevelUpContext.GetSelectedClass(hero); + + if (isLevelingUp && hero.ClassesAndLevels.TryGetValue(selectedClass, out var levels) + && featureUnlockByLevel.Level <= levels + 1) + { + return int.MaxValue; + } + + return featureUnlockByLevel.Level; + } + + internal static IEnumerable Transpiler(IEnumerable instructions) + { + var levelMethod = typeof(FeatureUnlockByLevel).GetMethod("get_Level"); + var myLevelMethod = typeof(ArchetypesPreviewModal_Refresh).GetMethod("Level"); + + foreach (var instruction in instructions) + { + if (instruction.Calls(levelMethod)) + { + yield return new CodeInstruction(OpCodes.Call, myLevelMethod); + } + else + { + yield return instruction; + } + } + } + } + + // only presents the subclass already taken + [HarmonyPatch(typeof(ArchetypesPreviewModal), "Show")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class ArchetypesPreviewModal_Show + { + internal static void Prefix(ref List subclasses) + { + var hero = Models.Global.ActiveLevelUpHero; + var selectedClass = Models.LevelUpContext.GetSelectedClass(hero); + + if (hero.ClassesAndSubclasses.TryGetValue(selectedClass, out var characterSubclassDefinition)) + { + subclasses = new() { characterSubclassDefinition.Name }; + } + } + } +} diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterBuildingManagerPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterBuildingManagerPatcher.cs index fa14ed5466..65c40110b3 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterBuildingManagerPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterBuildingManagerPatcher.cs @@ -16,11 +16,6 @@ internal static bool Prefix(RulesetCharacterHero hero, CharacterClassDefinition { LevelUpContext.SetSelectedClass(hero, classDefinition); - if (!Main.Settings.EnableMulticlass) - { - return true; - } - var isLevelingUp = LevelUpContext.IsLevelingUp(hero); var isClassSelectionStage = LevelUpContext.IsClassSelectionStage(hero); @@ -59,11 +54,6 @@ internal static bool Prefix(RulesetCharacterHero hero) } } - if (!Main.Settings.EnableMulticlass) - { - return true; - } - if (isLevelingUp && !isClassSelectionStage) { LevelUpContext.UngrantItemsIfRequired(hero); @@ -80,11 +70,6 @@ internal static class CharacterBuildingManager_GrantFeatures { internal static bool Prefix(RulesetCharacterHero hero) { - if (!Main.Settings.EnableMulticlass) - { - return true; - } - var isLevelingUp = LevelUpContext.IsLevelingUp(hero); var isClassSelectionStage = LevelUpContext.IsClassSelectionStage(hero); @@ -101,11 +86,6 @@ internal static void Postfix( CharacterHeroBuildingData heroBuildingData, List __result) { - if (!Main.Settings.EnableMulticlass) - { - return; - } - var hero = heroBuildingData.HeroCharacter; var isMulticlass = LevelUpContext.IsMulticlass(hero); @@ -139,11 +119,6 @@ internal static bool Prefix( string tag, ref FeatureDefinitionCastSpell __result) { - if (!Main.Settings.EnableMulticlass) - { - return true; - } - var hero = heroBuildingData.HeroCharacter; var isMulticlass = LevelUpContext.IsMulticlass(hero); @@ -212,11 +187,6 @@ internal static bool Prefix( CharacterBuildingManager __instance, CharacterHeroBuildingData heroBuildingData) { - if (!Main.Settings.EnableMulticlass) - { - return true; - } - var hero = heroBuildingData.HeroCharacter; var isMulticlass = LevelUpContext.IsMulticlass(hero); diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageClassSelectionPanelPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageClassSelectionPanelPatcher.cs index 4ba98f9fc6..a953ab2a82 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageClassSelectionPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageClassSelectionPanelPatcher.cs @@ -22,22 +22,17 @@ internal static void Prefix( ref int ___selectedClass) { // avoids a restart when enabling / disabling classes on the Mod UI panel - var visibleClasses = DatabaseRepository.GetDatabase().Where(x => !x.GuiPresentation.Hidden); - - ___compatibleClasses.SetRange(visibleClasses.OrderBy(x => x.FormatTitle())); - - if (!Main.Settings.EnableMulticlass) - { - return; - } - if (!LevelUpContext.IsLevelingUp(___currentHero)) { + var visibleClasses = DatabaseRepository.GetDatabase() + .Where(x => !x.GuiPresentation.Hidden); + + ___compatibleClasses.SetRange(visibleClasses.OrderBy(x => x.FormatTitle())); return; } LevelUpContext.SetIsClassSelectionStage(___currentHero, true); - InOutRulesContext.EnumerateHeroAllowedClassDefinitions(___currentHero, ___compatibleClasses, ref ___selectedClass); + MulticlassInOutRulesContext.EnumerateHeroAllowedClassDefinitions(___currentHero, ___compatibleClasses, ref ___selectedClass); var commonData = __instance.CommonData; @@ -69,25 +64,57 @@ internal static void Prefix(RulesetCharacterHero ___currentHero) } } - // hide the equipment panel group - [HarmonyPatch(typeof(CharacterStageClassSelectionPanel), "Refresh")] + // hide the features list for already acquired classes + [HarmonyPatch(typeof(CharacterStageClassSelectionPanel), "FillClassFeatures")] [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] - internal static class CharacterStageClassSelectionPanel_Refresh + internal static class CharacterStageClassSelectionPanel_FillClassFeatures { - public static bool SetActive(RulesetCharacterHero currentHero) => !LevelUpContext.IsLevelingUp(currentHero); + public static int Level(FeatureUnlockByLevel featureUnlockByLevel, RulesetCharacterHero hero) + { + var isLevelingUp = LevelUpContext.IsLevelingUp(hero); + var selectedClass = LevelUpContext.GetSelectedClass(hero); + + if (isLevelingUp + && hero.ClassesAndLevels.TryGetValue(selectedClass, out var levels) + && featureUnlockByLevel.Level != (levels + 1)) + { + return int.MaxValue; + } + + return featureUnlockByLevel.Level - 1; + } internal static IEnumerable Transpiler(IEnumerable instructions) { - if (!Main.Settings.EnableMulticlass) + var levelMethod = typeof(FeatureUnlockByLevel).GetMethod("get_Level"); + var myLevelMethod = typeof(CharacterStageClassSelectionPanel_FillClassFeatures).GetMethod("Level"); + var currentHeroField = typeof(CharacterStageClassSelectionPanel).GetField("currentHero", BindingFlags.Instance | BindingFlags.NonPublic); + + foreach (var instruction in instructions) { - foreach (var instruction in instructions) + if (instruction.Calls(levelMethod)) + { + yield return new CodeInstruction(OpCodes.Ldarg_0); + yield return new CodeInstruction(OpCodes.Ldfld, currentHeroField); + yield return new CodeInstruction(OpCodes.Call, myLevelMethod); + } + else { yield return instruction; - }; - - yield break; + } } + } + } + // hide the equipment panel group + [HarmonyPatch(typeof(CharacterStageClassSelectionPanel), "Refresh")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class CharacterStageClassSelectionPanel_Refresh + { + public static bool SetActive(RulesetCharacterHero currentHero) => !LevelUpContext.IsLevelingUp(currentHero); + + internal static IEnumerable Transpiler(IEnumerable instructions) + { var setActiveFound = 0; var setActiveMethod = typeof(GameObject).GetMethod("SetActive"); var mySetActiveMethod = typeof(CharacterStageClassSelectionPanel_Refresh).GetMethod("SetActive"); diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageDeitySelectionPanelPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageDeitySelectionPanelPatcher.cs index 621d4211d6..181f31143b 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageDeitySelectionPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageDeitySelectionPanelPatcher.cs @@ -12,11 +12,6 @@ internal static class CharacterStageDeitySelectionPanel_UpdateRelevance { internal static void Postfix(RulesetCharacterHero ___currentHero, ref bool ___isRelevant) { - if (!Main.Settings.EnableMulticlass) - { - return; - } - if (LevelUpContext.IsLevelingUp(___currentHero)) { ___isRelevant = LevelUpContext.RequiresDeity(___currentHero); diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageLevelGainsPanelPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageLevelGainsPanelPatcher.cs index 8d099c3c43..58080144e3 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageLevelGainsPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageLevelGainsPanelPatcher.cs @@ -36,15 +36,11 @@ public static void GetLastAssignedClassAndLevel(ICharacterBuildingService _, Rul internal static IEnumerable Transpiler(IEnumerable instructions) { var code = instructions.ToList(); + var getLastAssignedClassAndLevelMethod = typeof(ICharacterBuildingService).GetMethod("GetLastAssignedClassAndLevel"); + var customGetLastAssignedClassAndLevelMethod = typeof(CharacterStageLevelGainsPanel_EnterStage).GetMethod("GetLastAssignedClassAndLevel"); + var index = code.FindIndex(x => x.Calls(getLastAssignedClassAndLevelMethod)); - if (Main.Settings.EnableMulticlass) - { - var getLastAssignedClassAndLevelMethod = typeof(ICharacterBuildingService).GetMethod("GetLastAssignedClassAndLevel"); - var customGetLastAssignedClassAndLevelMethod = typeof(CharacterStageLevelGainsPanel_EnterStage).GetMethod("GetLastAssignedClassAndLevel"); - var index = code.FindIndex(x => x.Calls(getLastAssignedClassAndLevelMethod)); - - code[index] = new CodeInstruction(OpCodes.Call, customGetLastAssignedClassAndLevelMethod); - } + code[index] = new CodeInstruction(OpCodes.Call, customGetLastAssignedClassAndLevelMethod); return code; } @@ -70,15 +66,11 @@ public static List SpellRepertoires(RulesetCharacterHero internal static IEnumerable Transpiler(IEnumerable instructions) { var code = instructions.ToList(); + var spellRepertoiresMethod = typeof(RulesetCharacter).GetMethod("get_SpellRepertoires"); + var filteredSpellRepertoiresMethod = typeof(CharacterStageLevelGainsPanel_RefreshSpellcastingFeatures).GetMethod("SpellRepertoires"); + var index = code.FindIndex(x => x.Calls(spellRepertoiresMethod)); - if (Main.Settings.EnableMulticlass) - { - var spellRepertoiresMethod = typeof(RulesetCharacter).GetMethod("get_SpellRepertoires"); - var filteredSpellRepertoiresMethod = typeof(CharacterStageLevelGainsPanel_RefreshSpellcastingFeatures).GetMethod("SpellRepertoires"); - var index = code.FindIndex(x => x.Calls(spellRepertoiresMethod)); - - code[index] = new CodeInstruction(OpCodes.Call, filteredSpellRepertoiresMethod); - } + code[index] = new CodeInstruction(OpCodes.Call, filteredSpellRepertoiresMethod); return code; } diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSpellSelectionPanelPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSpellSelectionPanelPatcher.cs index 8f2e399730..6b13bea632 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSpellSelectionPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSpellSelectionPanelPatcher.cs @@ -13,11 +13,6 @@ internal static class CharacterStageSpellSelectionPanel_EnterStage { public static void Prefix(RulesetCharacterHero ___currentHero) { - if (!Main.Settings.EnableMulticlass) - { - return; - } - LevelUpContext.CacheSpells(___currentHero); } } diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSubclassSelectionPanelPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSubclassSelectionPanelPatcher.cs index eae58f623e..11857fe92d 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSubclassSelectionPanelPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/CharacterStageSubclassSelectionPanelPatcher.cs @@ -10,11 +10,6 @@ internal static class CharacterStageSubclassSelectionPanel_UpdateRelevance { internal static void Postfix(RulesetCharacterHero ___currentHero, ref bool ___isRelevant) { - if (!Main.Settings.EnableMulticlass) - { - return; - } - if (LevelUpContext.IsLevelingUp(___currentHero) && LevelUpContext.RequiresDeity(___currentHero)) { ___isRelevant = false; diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/HigherLevelFeaturesModalPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/HigherLevelFeaturesModalPatcher.cs new file mode 100644 index 0000000000..81e17aa6fd --- /dev/null +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/HigherLevelFeaturesModalPatcher.cs @@ -0,0 +1,22 @@ +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; + +namespace SolastaCommunityExpansion.Patches.Multiclass.LevelUp +{ + // replaces the hard coded experience + [HarmonyPatch(typeof(HigherLevelFeaturesModal), "Bind")] + [SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Patch")] + internal static class HigherLevelFeaturesModal_Bind + { + internal static void Prefix(ref int achievementLevel) + { + var hero = Models.Global.ActiveLevelUpHero; + var selectedClass = Models.LevelUpContext.GetSelectedClass(hero); + + if (hero.ClassesAndLevels.TryGetValue(selectedClass, out var levels)) + { + achievementLevel = levels + 1; + } + } + } +} diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/RulesetCharacterHeroPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/RulesetCharacterHeroPatcher.cs index 293adcfdeb..d32050aa6b 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/RulesetCharacterHeroPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/RulesetCharacterHeroPatcher.cs @@ -12,11 +12,6 @@ internal static class RulesetCharacterHero_AddClassLevel { internal static bool Prefix(RulesetCharacterHero __instance, CharacterClassDefinition classDefinition, List ___hitPointsGainHistory) { - if (!Main.Settings.EnableMulticlass) - { - return true; - } - if (!LevelUpContext.IsLevelingUp(__instance)) { return true; diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellBoxPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellBoxPatcher.cs index 1e5faba0c2..a916751045 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellBoxPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellBoxPatcher.cs @@ -13,8 +13,7 @@ internal static class SpellBox_Refresh { public static void Postfix(SpellBox __instance, SpellBox.BindMode ___bindMode, RectTransform ___autoPreparedGroup, GuiLabel ___autoPreparedTitle, GuiTooltip ___autoPreparedTooltip) { - if (!Main.Settings.EnableMulticlass - || __instance.GuiSpellDefinition == null + if (__instance.GuiSpellDefinition == null || ___bindMode == SpellBox.BindMode.Preparation || ___bindMode == SpellBox.BindMode.Inspection) { diff --git a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellsByLevelGroupPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellsByLevelGroupPatcher.cs index 9569ee7b7c..7f07a31993 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellsByLevelGroupPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/LevelUp/SpellsByLevelGroupPatcher.cs @@ -76,11 +76,6 @@ internal static void Prefix( CollectAllAutoPreparedSpells(__instance, bindMode, hero, allSpells, auToPreparedSpells); - if (!Main.Settings.EnableMulticlass) - { - return; - } - var isMulticlass = LevelUpContext.IsMulticlass(hero); if (!isMulticlass) diff --git a/SolastaCommunityExpansion/Patches/Multiclass/SlotsSpells/RulesetCharacterPatcher.cs b/SolastaCommunityExpansion/Patches/Multiclass/SlotsSpells/RulesetCharacterPatcher.cs index 7bee7f84a3..791c444133 100644 --- a/SolastaCommunityExpansion/Patches/Multiclass/SlotsSpells/RulesetCharacterPatcher.cs +++ b/SolastaCommunityExpansion/Patches/Multiclass/SlotsSpells/RulesetCharacterPatcher.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection.Emit; using HarmonyLib; +using SolastaCommunityExpansion.Classes.Warlock; using SolastaCommunityExpansion.Models; using SolastaModApi.Infrastructure; using static SolastaCommunityExpansion.Level20.SpellsHelper; @@ -45,22 +46,15 @@ public static void RestoreAllSpellSlots(RulesetSpellRepertoire __instance, Rules } var warlockSpellLevel = SharedSpellsContext.GetWarlockSpellLevel(heroWithSpellRepertoire); - - __instance.GetField>("usedSpellsSlots") - .TryGetValue(SharedSpellsContext.MC_PACT_MAGIC_SLOT_TAB_INDEX, out var slotsToRestore); + var slotsToRestore = SharedSpellsContext.GetWarlockUsedSlots(heroWithSpellRepertoire); foreach (var spellRepertoire in heroWithSpellRepertoire.SpellRepertoires .Where(x => x.SpellCastingRace == null)) { var usedSpellsSlots = spellRepertoire.GetField>("usedSpellsSlots"); - for (var i = SharedSpellsContext.MC_PACT_MAGIC_SLOT_TAB_INDEX; i <= warlockSpellLevel; i++) + for (var i = WarlockSpells.PACT_MAGIC_SLOT_TAB_INDEX; i <= warlockSpellLevel; i++) { - if (i == 0) - { - continue; - } - if (usedSpellsSlots.ContainsKey(i)) { usedSpellsSlots[i] -= slotsToRestore; diff --git a/SolastaCommunityExpansion/Repository.json b/SolastaCommunityExpansion/Repository.json index 558b25ffb9..f4a0592b88 100644 --- a/SolastaCommunityExpansion/Repository.json +++ b/SolastaCommunityExpansion/Repository.json @@ -2,8 +2,8 @@ "Releases": [ { "Id": "SolastaCommunityExpansion", - "Version": "1.3.55.0", - "DownloadUrl": "https://github.com/SolastaMods/SolastaCommunityExpansion/releases/download/1.3.55.0/SolastaCommunityExpansion.zip" + "Version": "1.3.55.1", + "DownloadUrl": "https://github.com/SolastaMods/SolastaCommunityExpansion/releases/download/1.3.55.1/SolastaCommunityExpansion.zip" } ] } \ No newline at end of file diff --git a/SolastaCommunityExpansion/Settings.cs b/SolastaCommunityExpansion/Settings.cs index 54a2e7b90f..4ae3915fbe 100644 --- a/SolastaCommunityExpansion/Settings.cs +++ b/SolastaCommunityExpansion/Settings.cs @@ -80,6 +80,7 @@ public class Settings : UnityModManager.ModSettings public bool EnableSortingDeities { get; set; } = true; public bool EnableSortingDungeonMakerAssets { get; set; } = true; public bool EnableSortingFeats { get; set; } = true; + public bool EnableSortingFightingStyles { get; set; } = true; public bool EnableSortingFutureFeatures { get; set; } = true; public bool EnableSortingRaces { get; set; } = true; public bool EnableSortingSubclasses { get; set; } = true; @@ -263,6 +264,7 @@ public class Settings : UnityModManager.ModSettings // Inventory and Items public bool EnableInventoryFilteringAndSorting { get; set; } + public bool EnableInventoryTaintNonProficientItemsRed { get; set; } public bool EnableInvisibleCrownOfTheMagister { get; set; } public string EmpressGarbAppearance { get; set; } = "Normal"; diff --git a/SolastaCommunityExpansion/SolastaCommunityExpansion.csproj b/SolastaCommunityExpansion/SolastaCommunityExpansion.csproj index eef7f83587..a4aceb302c 100644 --- a/SolastaCommunityExpansion/SolastaCommunityExpansion.csproj +++ b/SolastaCommunityExpansion/SolastaCommunityExpansion.csproj @@ -3,8 +3,8 @@ latest net472 - 1.3.55.0 - 1.3.55.0 + 1.3.55.1 + 1.3.55.1 https://github.com/SolastaMods/SolastaCommunityExpansion https://github.com/SolastaMods/SolastaCommunityExpansion diff --git a/SolastaCommunityExpansion/Translations-en.txt b/SolastaCommunityExpansion/Translations-en.txt index 63c1b9b76f..3f7fd5dd9b 100644 --- a/SolastaCommunityExpansion/Translations-en.txt +++ b/SolastaCommunityExpansion/Translations-en.txt @@ -565,7 +565,7 @@ Feat/&FeatTelekineticWisPullTitle Telekinetic Pull Feat/&FeatTelekineticWisPushDescription Telekinetically shove a creature away from you Feat/&FeatTelekineticWisPushTitle Telekinetic Push Feat/&FeatTelekineticWisTitle Telekinetic (Wisdom) -Feat/&FeatTitanFightingDescription You increase your melee attack damage by +2 +Feat/&FeatTitanFightingDescription You gain a +2 hit against creatures of size large or bigger Feat/&FeatTitanFightingTitle Fighting Style: Titan Fighting Feat/&FeatToughDescription Your hit point maximum increases by an amount equal to twice your level when you gain this feat. Whenever you gain a level thereafter, your hit point maximum increases by an additional 2 hit points Feat/&FeatToughTitle Tough @@ -1153,7 +1153,7 @@ FightingStyle/&CripplingDescription Reduce the speed of your opponent on a melee FightingStyle/&CripplingTitle Crippling FightingStyle/&PugilistFightingDescription While you are completely unarmed, your unarmed strikes deal an additional d8 of damage and you can punch with your offhand as a bonus action FightingStyle/&PugilistFightingTitle Pugilist -FightingStyle/&TitanFightingDescription You increase your melee attack damage by +2 +FightingStyle/&TitanFightingDescription You gain a +2 hit against creatures of size large or bigger FightingStyle/&TitanFightingTitle Titan Fighting FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsDescription Arcana, Nature, and Insight FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsTitle Suggested Skills @@ -1331,8 +1331,8 @@ Reaction/&SubitemSelectSlotLevelTitle Slot Level Reaction/&SubitemSelectWarcasterTitle Select action Reaction/&WarcasterAttackDescription Attack target Reaction/&WarcasterAttackTitle Attack -Reaction/&WarcasterReactionDescription {1} is leaving a threatened area. {0} can attack or cast cantrip in responce. -Reaction/&WarcasterReactionTitle Warcaster's Opportunity +Reaction/&WarcasterReactionDescription {0} is leaving an area threatened by one or more characters. +Reaction/&WarcasterReactionTitle Attack of Opportunity Requirement/&FeatureSelectionRequireCharacterLevel Level {0} Requirement/&FeatureSelectionRequireClassLevel Level {0} of {1} Requirement/&WarlockMissingEldritchBlast Eldritch Blast cantrip diff --git a/SolastaCommunityExpansion/Translations-fr.txt b/SolastaCommunityExpansion/Translations-fr.txt index 9b04a03cb3..2027076475 100644 --- a/SolastaCommunityExpansion/Translations-fr.txt +++ b/SolastaCommunityExpansion/Translations-fr.txt @@ -565,7 +565,7 @@ Feat/&FeatTelekineticWisPullTitle Tir télékinésique Feat/&FeatTelekineticWisPushDescription Repousse télékinétiquement une créature loin de vous Feat/&FeatTelekineticWisPushTitle Poussée télékinétique Feat/&FeatTelekineticWisTitle Télékinésie (Sagesse) -Feat/&FeatTitanFightingDescription Vous augmentez vos dégâts d'attaque en mêlée de +2 +Feat/&FeatTitanFightingDescription Vous gagnez +2 en touche contre les créatures de taille grande ou plus Feat/&FeatTitanFightingTitle Style de combat : Combat contre les Titans Feat/&FeatToughDescription Votre maximum de points de vie augmente d'un montant égal à deux fois votre niveau lorsque vous obtenez ce don. Chaque fois que vous gagnez un niveau par la suite, votre maximum de points de vie augmente de 2 points de vie supplémentaires Feat/&FeatToughTitle Difficile @@ -1153,7 +1153,7 @@ FightingStyle/&CripplingDescription Réduisez la vitesse de votre adversaire lor FightingStyle/&CripplingTitle Paralysant FightingStyle/&PugilistFightingDescription Tant que vous êtes complètement désarmé, vos frappes à mains nues infligent un d8 de dégâts supplémentaires et vous pouvez frapper avec votre main secondaire en tant qu'action bonus. FightingStyle/&PugilistFightingTitle Pugiliste -FightingStyle/&TitanFightingDescription Vous augmentez vos dégâts d'attaque en mêlée de +2 +FightingStyle/&TitanFightingDescription Vous gagnez +2 en touche contre les créatures de taille grande ou plus FightingStyle/&TitanFightingTitle Combat de Titans FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsDescription Arcanes, nature et perspicacité FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsTitle Compétences suggérées diff --git a/SolastaCommunityExpansion/Translations-pt-br.txt b/SolastaCommunityExpansion/Translations-pt-br.txt index b4c9446346..03aeb7eab7 100644 --- a/SolastaCommunityExpansion/Translations-pt-br.txt +++ b/SolastaCommunityExpansion/Translations-pt-br.txt @@ -565,7 +565,7 @@ Feat/&FeatTelekineticWisPullTitle Puxada Telecinética Feat/&FeatTelekineticWisPushDescription Empurre telecineticamente uma criatura para longe de você Feat/&FeatTelekineticWisPushTitle Empurrão Telecinético Feat/&FeatTelekineticWisTitle Telecinética (Sabedoria) -Feat/&FeatTitanFightingDescription Você aumenta seu dano de ataque corpo a corpo em +2 +Feat/&FeatTitanFightingDescription Você ganha +2 de acerto contra criaturas de tamanho grande ou maior Feat/&FeatTitanFightingTitle Estilo de luta: Titan Fighting Feat/&FeatToughDescription Seu máximo de pontos de vida aumenta em uma quantidade igual ao dobro do seu nível quando você ganha este talento. Sempre que você ganha um nível depois disso, seu máximo de pontos de vida aumenta em 2 pontos de vida adicionais Feat/&FeatToughTitle Resistente @@ -1153,7 +1153,7 @@ FightingStyle/&CripplingDescription Reduza a velocidade do seu oponente em um at FightingStyle/&CripplingTitle Aleijante FightingStyle/&PugilistFightingDescription Enquanto você está completamente desarmado, seus ataques desarmados causam um d8 adicional de dano e você pode socar com sua mão secundária como uma ação bônus FightingStyle/&PugilistFightingTitle Pugilista -FightingStyle/&TitanFightingDescription Você aumenta seu dano de ataque corpo a corpo em +2 +FightingStyle/&TitanFightingDescription Você ganha +2 de acerto contra criaturas de tamanho grande ou maior FightingStyle/&TitanFightingTitle Luta de Titãs FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsDescription Arcano, Natureza e Insight FlexibleBackgrounds/&AcademicBackgroundSuggestedSkillsTitle Habilidades sugeridas