From 896ebbd06081ba3cbdc667b323f591363e46aaf1 Mon Sep 17 00:00:00 2001 From: ThyWolf Date: Wed, 7 Feb 2024 16:26:35 -0800 Subject: [PATCH] fix Corrupting Bolt, Ice Blade, Sanctuary, and Vitality Transfer interaction with twinned --- .../Spells/SpellBuildersLevel01.cs | 114 ++++++++++-------- .../Spells/SpellBuildersLevel03.cs | 75 +++++++----- 2 files changed, 104 insertions(+), 85 deletions(-) diff --git a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs index 0953cdaf39..7431c62c87 100644 --- a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs +++ b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs @@ -755,56 +755,60 @@ public IEnumerator OnMagicEffectFinishedByMe(CharacterActionMagicEffect action, } var caster = actionCastSpell.ActingCharacter; - var target = actionCastSpell.ActionParams.TargetCharacters[0]; var rulesetCaster = caster.RulesetCharacter; var effectLevel = actionCastSpell.ActionParams.activeEffect.EffectLevel; var isCritical = actionCastSpell.AttackRollOutcome == RollOutcome.CriticalSuccess; - foreach (var enemy in Gui.Battle - .GetContenders(target, isOppositeSide: false, excludeSelf: false, withinRange: 1)) + // need to loop over target characters to support twinned metamagic scenarios + foreach (var target in actionCastSpell.ActionParams.TargetCharacters) { - var rulesetEnemy = enemy.RulesetCharacter; - var casterSaveDC = 8 + actionCastSpell.ActiveSpell.MagicAttackBonus; - var modifierTrend = rulesetEnemy.actionModifier.savingThrowModifierTrends; - var advantageTrends = rulesetEnemy.actionModifier.savingThrowAdvantageTrends; - var enemyDexModifier = AttributeDefinitions.ComputeAbilityScoreModifier( - rulesetEnemy.TryGetAttributeValue(AttributeDefinitions.Dexterity)); - - rulesetEnemy.RollSavingThrow( - 0, AttributeDefinitions.Dexterity, baseDefinition, modifierTrend, advantageTrends, enemyDexModifier, - casterSaveDC, - false, out var savingOutcome, out _); - - if (savingOutcome is RollOutcome.Success or RollOutcome.CriticalSuccess) + foreach (var enemy in Gui.Battle + .GetContenders(target, isOppositeSide: false, excludeSelf: false, withinRange: 1)) { - continue; + var rulesetEnemy = enemy.RulesetCharacter; + var casterSaveDC = 8 + actionCastSpell.ActiveSpell.MagicAttackBonus; + var modifierTrend = rulesetEnemy.actionModifier.savingThrowModifierTrends; + var advantageTrends = rulesetEnemy.actionModifier.savingThrowAdvantageTrends; + var enemyDexModifier = AttributeDefinitions.ComputeAbilityScoreModifier( + rulesetEnemy.TryGetAttributeValue(AttributeDefinitions.Dexterity)); + + rulesetEnemy.RollSavingThrow( + 0, AttributeDefinitions.Dexterity, baseDefinition, modifierTrend, advantageTrends, + enemyDexModifier, + casterSaveDC, + false, out var savingOutcome, out _); + + if (savingOutcome is RollOutcome.Success or RollOutcome.CriticalSuccess) + { + continue; + } + + var rolls = new List(); + var damageForm = new DamageForm + { + DamageType = DamageTypeCold, + DieType = DieType.D6, + DiceNumber = 2 + (effectLevel - 1), + BonusDamage = 0 + }; + var damageRoll = + rulesetCaster.RollDamage(damageForm, 0, false, 0, 0, 1, false, false, false, rolls); + + EffectHelpers.StartVisualEffect(caster, target, ConeOfCold); + RulesetActor.InflictDamage( + damageRoll, + damageForm, + damageForm.DamageType, + new RulesetImplementationDefinitions.ApplyFormsParams { targetCharacter = rulesetEnemy }, + rulesetEnemy, + isCritical, + rulesetCaster.Guid, + false, + [], + new RollInfo(damageForm.DieType, rolls, 0), + true, + out _); } - - var rolls = new List(); - var damageForm = new DamageForm - { - DamageType = DamageTypeCold, - DieType = DieType.D6, - DiceNumber = 2 + (effectLevel - 1), - BonusDamage = 0 - }; - var damageRoll = - rulesetCaster.RollDamage(damageForm, 0, false, 0, 0, 1, false, false, false, rolls); - - EffectHelpers.StartVisualEffect(caster, target, ConeOfCold); - RulesetActor.InflictDamage( - damageRoll, - damageForm, - damageForm.DamageType, - new RulesetImplementationDefinitions.ApplyFormsParams { targetCharacter = rulesetEnemy }, - rulesetEnemy, - isCritical, - rulesetCaster.Guid, - false, - [], - new RollInfo(damageForm.DieType, rolls, 0), - true, - out _); } } } @@ -1406,19 +1410,23 @@ public IEnumerator OnMagicEffectFinishedByMe(CharacterActionMagicEffect action, } var rulesetCaster = action.ActingCharacter.RulesetCharacter; - var rulesetTarget = action.ActionParams.TargetCharacters[0].RulesetCharacter; - if (!rulesetTarget.TryGetConditionOfCategoryAndType( - AttributeDefinitions.TagEffect, - conditionSanctuary.Name, - out var activeCondition)) + // need to loop over target characters to support twinned metamagic scenarios + foreach (var rulesetTarget in action.ActionParams.TargetCharacters + .Select(target => target.RulesetCharacter)) { - yield break; - } + if (!rulesetTarget.TryGetConditionOfCategoryAndType( + AttributeDefinitions.TagEffect, + conditionSanctuary.Name, + out var activeCondition)) + { + yield break; + } - rulesetTarget.EnumerateFeaturesToBrowse( - rulesetCaster.FeaturesToBrowse, rulesetCaster.FeaturesOrigin); - activeCondition.Amount = rulesetCaster.ComputeSaveDC(actionCastSpell.activeSpell.SpellRepertoire); + rulesetTarget.EnumerateFeaturesToBrowse( + rulesetCaster.FeaturesToBrowse, rulesetCaster.FeaturesOrigin); + activeCondition.Amount = rulesetCaster.ComputeSaveDC(actionCastSpell.activeSpell.SpellRepertoire); + } } } diff --git a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs index 3994d232ab..6609c8f6e9 100644 --- a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs +++ b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs @@ -1190,28 +1190,33 @@ private sealed class MagicEffectFinishedByMeCorruptingBolt(ConditionDefinition c { public IEnumerator OnMagicEffectFinishedByMe(CharacterActionMagicEffect action, BaseDefinition baseDefinition) { - var rulesetAttacker = action.ActingCharacter.RulesetCharacter; - var rulesetDefender = action.ActionParams.TargetCharacters[0].RulesetCharacter; - - if (rulesetDefender is not { IsDeadOrDyingOrUnconscious: false } - || !action.RolledSaveThrow || action.SaveOutcome == RollOutcome.Success) + if (!action.RolledSaveThrow || action.SaveOutcome == RollOutcome.Success) { yield break; } - rulesetDefender.InflictCondition( - conditionCorruptingBolt.Name, - conditionCorruptingBolt.DurationType, - conditionCorruptingBolt.DurationParameter, - conditionCorruptingBolt.TurnOccurence, - AttributeDefinitions.TagEffect, - rulesetAttacker.guid, - rulesetAttacker.CurrentFaction.Name, - 1, - conditionCorruptingBolt.Name, - 0, - 0, - 0); + var rulesetAttacker = action.ActingCharacter.RulesetCharacter; + + // need to loop over target characters to support twinned metamagic scenarios + foreach (var rulesetDefender in action.ActionParams.TargetCharacters + .Select(target => target.RulesetCharacter) + .Where(rulesetDefender => + rulesetDefender is { IsDeadOrDyingOrUnconscious: false })) + { + rulesetDefender.InflictCondition( + conditionCorruptingBolt.Name, + conditionCorruptingBolt.DurationType, + conditionCorruptingBolt.DurationParameter, + conditionCorruptingBolt.TurnOccurence, + AttributeDefinitions.TagEffect, + rulesetAttacker.guid, + rulesetAttacker.CurrentFaction.Name, + 1, + conditionCorruptingBolt.Name, + 0, + 0, + 0); + } } } @@ -1261,26 +1266,32 @@ public IEnumerator OnMagicEffectFinishedByMe(CharacterActionMagicEffect action, var caster = action.ActingCharacter; var rulesetCaster = caster.RulesetCharacter; - var rulesetTarget = action.ActionParams.TargetCharacters[0].RulesetCharacter; - var rolls = new List(); var diceNumber = 4 + actionCastSpell.activeSpell.EffectLevel - 3; - var damageForm = new DamageForm + + // need to loop over target characters to support twinned metamagic scenarios + foreach (var target in action.ActionParams.TargetCharacters) { - DamageType = DamageTypeNecrotic, DiceNumber = diceNumber, DieType = DieType.D8 - }; - var totalDamage = rulesetCaster.RollDamage(damageForm, 0, false, 0, 0, 1, false, false, false, rolls); - var totalHealing = totalDamage * 2; - var currentHitPoints = rulesetCaster.CurrentHitPoints; + var rulesetTarget = target.RulesetCharacter; + var rolls = new List(); + var damageForm = new DamageForm + { + DamageType = DamageTypeNecrotic, DiceNumber = diceNumber, DieType = DieType.D8 + }; + var totalDamage = rulesetCaster.RollDamage(damageForm, 0, false, 0, 0, 1, false, false, false, rolls); + var totalHealing = totalDamage * 2; + var currentHitPoints = rulesetCaster.CurrentHitPoints; - rulesetCaster.SustainDamage(totalDamage, damageForm.DamageType, false, rulesetCaster.Guid, - new RollInfo(damageForm.DieType, rolls, 0), out _); + rulesetCaster.SustainDamage(totalDamage, damageForm.DamageType, false, rulesetCaster.Guid, + new RollInfo(damageForm.DieType, rolls, 0), out _); - EffectHelpers.StartVisualEffect(caster, caster, PowerSorcererChildRiftOffering); + EffectHelpers.StartVisualEffect(caster, caster, PowerSorcererChildRiftOffering); - rulesetCaster.DamageSustained?.Invoke(rulesetCaster, totalDamage, damageForm.DamageType, true, - currentHitPoints > totalDamage, false); + rulesetCaster.DamageSustained?.Invoke(rulesetCaster, totalDamage, damageForm.DamageType, true, + currentHitPoints > totalDamage, false); + + rulesetTarget.ReceiveHealing(totalHealing, true, rulesetCaster.Guid); + } - rulesetTarget.ReceiveHealing(totalHealing, true, rulesetCaster.Guid); } }