diff --git a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs index bc10a8a099..9ec2cfa8e5 100644 --- a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs +++ b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel01.cs @@ -2950,9 +2950,9 @@ internal static SpellDefinition BuildWitchBolt() WitchBoltPower = FeatureDefinitionPowerBuilder .Create($"Power{NAME}") .SetGuiPresentation(NAME, Category.Spell, LightningBolt) - .SetUsesFixed(ActivationTime.Action) + .SetUsesFixed(ActivationTime.BonusAction, RechargeRate.TurnStart) .SetEffectDescription(EffectDescriptionBuilder.Create() - .SetTargetingData(Side.Enemy, RangeType.Distance, 6, TargetType.IndividualsUnique) + .SetTargetingData(Side.Enemy, RangeType.Distance, 12, TargetType.IndividualsUnique, requireVisibility: false) .SetEffectForms(EffectFormBuilder.DamageForm(DamageTypeLightning, 1, DieType.D12)) .SetParticleEffectParameters(ChainLightning) .SetImpactEffectParameters(LightningBolt) @@ -2966,8 +2966,8 @@ internal static SpellDefinition BuildWitchBolt() .SetFeatures(WitchBoltPower) .AddToDB(); - var spell = SpellDefinitionBuilder - .Create(NAME) + var spellPt2 = SpellDefinitionBuilder + .Create(NAME+"ApplyConditions") .SetGuiPresentation(Category.Spell, Sprites.GetSprite(NAME, Resources.WitchBolt, 128)) .SetSchoolOfMagic(SchoolOfMagicDefinitions.SchoolEvocation) .SetSpellLevel(1) @@ -2979,50 +2979,81 @@ internal static SpellDefinition BuildWitchBolt() .SetRequiresConcentration(true) .SetEffectDescription(EffectDescriptionBuilder.Create() .SetDurationData(DurationType.Minute, 1) - .SetTargetingData(Side.Enemy, RangeType.RangeHit, 6, TargetType.IndividualsUnique) + .SetTargetingData(Side.Enemy, RangeType.Distance, 12, TargetType.IndividualsUnique) .SetEffectAdvancement(EffectIncrementMethod.PerAdditionalSlotLevel, additionalDicePerIncrement: 1) .SetEffectForms( - EffectFormBuilder.DamageForm(DamageTypeLightning, 1, DieType.D12), + //EffectFormBuilder.DamageForm(DamageTypeLightning, 2, DieType.D12), EffectFormBuilder.AddConditionForm(conditionWitchBolt), EffectFormBuilder.AddConditionForm(conditionWitchBoltSelf, true)) + .SetAnimationMagicEffect(AnimationDefinitions.AnimationMagicEffect.Count) + .UseQuickAnimations() + .Build()) + .AddToDB(); + + var spell = SpellDefinitionBuilder + .Create(NAME) + .SetGuiPresentation(Category.Spell, Sprites.GetSprite(NAME, Resources.WitchBolt, 128)) + .SetSchoolOfMagic(SchoolOfMagicDefinitions.SchoolEvocation) + .SetSpellLevel(1) + .SetCastingTime(ActivationTime.NoCost) + .SetMaterialComponent(MaterialComponentType.None) + .SetSomaticComponent(false) + .SetVerboseComponent(false) + .SetVocalSpellSameType(VocalSpellSemeType.None) + .SetRequiresConcentration(false) + .SetEffectDescription(EffectDescriptionBuilder.Create() + .SetDurationData(DurationType.Minute, 1) + .SetTargetingData(Side.Enemy, RangeType.RangeHit, 6, TargetType.IndividualsUnique) + .SetEffectAdvancement(EffectIncrementMethod.PerAdditionalSlotLevel, additionalDicePerIncrement: 1) + .SetEffectForms(EffectFormBuilder.DamageForm(DamageTypeLightning, 2, DieType.D12)) .SetParticleEffectParameters(ChainLightning) .SetImpactEffectParameters(LightningBolt) .Build()) .AddToDB(); - var witchBoltDuration = ComputeRoundsDuration(DurationType.Minute, 1); - WitchBoltPower.AddCustomSubFeatures( - new CustomBehaviorWitchBolt(spell, WitchBoltPower, conditionWitchBolt), - new ModifyPowerVisibility((character, power, _) => - { - if (power.activationTime == ActivationTime.Action) { return true; } + spell.AddCustomSubFeatures(new OnPowerOrSpellFinishedByMeWitchBolt(spell, spellPt2)); - if (character.TryGetConditionOfCategoryAndType(AttributeDefinitions.TagEffect, - conditionWitchBoltSelf.Name, out var condition)) - { - return condition.RemainingRounds < witchBoltDuration; - } + var witchBoltDuration = ComputeRoundsDuration(DurationType.Minute, 1); + WitchBoltPower.AddCustomSubFeatures(new CustomBehaviorWitchBolt(WitchBoltPower, conditionWitchBolt)); - return true; - })); conditionWitchBolt.AddCustomSubFeatures( - new ActionFinishedByMeWitchBoltEnemy(spell, conditionWitchBolt)); + new ActionFinishedByMeWitchBoltEnemy(spellPt2, conditionWitchBolt)); conditionWitchBoltSelf.AddCustomSubFeatures( AddUsablePowersFromCondition.Marker, - new ActionFinishedByMeWitchBolt(spell, WitchBoltPower, conditionWitchBolt)); + new ActionFinishedByMeWitchBolt(spellPt2, WitchBoltPower, conditionWitchBolt)); return spell; } + private sealed class OnPowerOrSpellFinishedByMeWitchBolt(SpellDefinition witchBolt, SpellDefinition witchBoltPt2) : IPowerOrSpellFinishedByMe + { + // Trigger the concentration part of the Witch Bolt Spell + // Separate from the attack roll, as it still applies even if you miss the atk roll + public IEnumerator OnPowerOrSpellFinishedByMe(CharacterActionMagicEffect action, BaseDefinition baseDefinition) + { + if (baseDefinition.Name != witchBolt.Name || action.Countered || action.ExecutionFailed) { yield break; } + + var rulesetEffect = action.ActionParams.RulesetEffect; + + var actionParams = new CharacterActionParams(action.ActingCharacter, Id.CastNoCost) + { + ActionModifiers = { new ActionModifier() }, + IntParameter = rulesetEffect.EffectLevel, + StringParameter = witchBoltPt2.Name, + targetCharacters = action.rawTargets + }; + + action.ActingCharacter.MyExecuteActionCastNoCost(witchBoltPt2, rulesetEffect.EffectLevel, actionParams, action.ActionParams.SpellRepertoire); + } + } + private sealed class CustomBehaviorWitchBolt( - // ReSharper disable once SuggestBaseTypeForParameterInConstructor - SpellDefinition spellWitchBolt, // ReSharper disable once SuggestBaseTypeForParameterInConstructor FeatureDefinitionPower powerWitchBolt, // ReSharper disable once SuggestBaseTypeForParameterInConstructor - ConditionDefinition conditionWitchBolt) : IFilterTargetingCharacter, IModifyEffectDescription + ConditionDefinition conditionWitchBolt) : IFilterTargetingCharacter { public bool EnforceFullSelection => false; @@ -3048,22 +3079,6 @@ public bool IsValid(BaseDefinition definition, RulesetCharacter character, Effec { return definition == powerWitchBolt; } - - public EffectDescription GetEffectDescription( - BaseDefinition definition, - EffectDescription effectDescription, - RulesetCharacter character, - RulesetEffect rulesetEffect) - { - if (character.ConcentratedSpell != null && - character.ConcentratedSpell.SpellDefinition == spellWitchBolt) - { - effectDescription.EffectForms[0].DamageForm.DiceNumber = - 1 + (character.ConcentratedSpell.EffectLevel - 1); - } - - return effectDescription; - } } private sealed class ActionFinishedByMeWitchBolt( @@ -3072,7 +3087,7 @@ private sealed class ActionFinishedByMeWitchBolt( // ReSharper disable once SuggestBaseTypeForParameterInConstructor FeatureDefinitionPower powerWitchBolt, // ReSharper disable once SuggestBaseTypeForParameterInConstructor - ConditionDefinition conditionWitchBolt) : IActionFinishedByMe + ConditionDefinition conditionWitchBolt) : IActionFinishedByMe, IOnConditionAddedOrRemoved { public IEnumerator OnActionFinishedByMe(CharacterAction action) { @@ -3090,43 +3105,47 @@ or ActivationTime.OnAttackOrSpellHitAuto case CharacterActionCastSpell actionCastSpell when actionCastSpell.activeSpell.SpellDefinition == spellWitchBolt: action.ActingCharacter.UsedSpecialFeatures.TryAdd(powerWitchBolt.Name, 0); + action.ActingCharacter.RulesetCharacter.UpdateUsageForPower(powerWitchBolt, 1); yield break; } var actingCharacter = action.ActingCharacter; var rulesetCharacter = actingCharacter.RulesetCharacter; - if (action.ActionType - is ActionType.Move - // these although allowed could potentially move both contenders off range - or ActionType.Bonus - or ActionType.Reaction - or ActionType.NoCost) + if (Gui.Battle == null) { - if (Gui.Battle == null) - { - yield break; - } + yield break; + } + + var stillInRange = Gui.Battle + .GetContenders(actingCharacter, withinRange: 6) + .Any(x => + x.RulesetCharacter.TryGetConditionOfCategoryAndType( + AttributeDefinitions.TagEffect, conditionWitchBolt.Name, out var activeCondition) && + rulesetCharacter.Guid == activeCondition.SourceGuid); - var stillInRange = Gui.Battle - .GetContenders(actingCharacter, withinRange: 6) - .Any(x => - x.RulesetCharacter.TryGetConditionOfCategoryAndType( - AttributeDefinitions.TagEffect, conditionWitchBolt.Name, out var activeCondition) && - rulesetCharacter.Guid == activeCondition.SourceGuid); + if (!stillInRange) + { + var rulesetSpell = rulesetCharacter.SpellsCastByMe.FirstOrDefault(x => x.SpellDefinition == spellWitchBolt); - if (stillInRange) + if (rulesetSpell != null) { - yield break; + rulesetCharacter.TerminateSpell(rulesetSpell); } } + } - var rulesetSpell = rulesetCharacter.SpellsCastByMe.FirstOrDefault(x => x.SpellDefinition == spellWitchBolt); + public void OnConditionAdded(RulesetCharacter target, RulesetCondition rulesetCondition) + { + var glc = GameLocationCharacter.GetFromActor(target); + glc.UsedSpecialFeatures.TryAdd(powerWitchBolt.Name, 0); + // can't use the Bonus Action power on the turn the spell was cast + glc.RulesetCharacter.UpdateUsageForPower(powerWitchBolt, 1); + } - if (rulesetSpell != null) - { - rulesetCharacter.TerminateSpell(rulesetSpell); - } + public void OnConditionRemoved(RulesetCharacter target, RulesetCondition rulesetCondition) + { + // Empty } } @@ -3152,14 +3171,6 @@ public IEnumerator OnActionFinishedByMe(CharacterAction action) yield break; } - var stillInRange = Gui.Battle.GetContenders(actingCharacter, withinRange: 6).Any(x => - x.RulesetCharacter.Guid == activeCondition.SourceGuid); - - if (stillInRange) - { - yield break; - } - var rulesetCaster = EffectHelpers.GetCharacterByGuid(activeCondition.SourceGuid); if (rulesetCaster == null) @@ -3167,12 +3178,19 @@ public IEnumerator OnActionFinishedByMe(CharacterAction action) yield break; } - var rulesetSpell = rulesetCharacter.SpellsCastByMe.FirstOrDefault(x => x.SpellDefinition == spellWitchBolt); + var stillInRange = Gui.Battle.GetContenders(actingCharacter, withinRange: 6).Any(x => + x.RulesetCharacter.Guid == activeCondition.SourceGuid); - if (rulesetSpell != null) + if (!stillInRange) { - rulesetCaster.TerminateSpell(rulesetSpell); + var rulesetSpell = rulesetCharacter.SpellsCastByMe.FirstOrDefault(x => x.SpellDefinition == spellWitchBolt); + + if (rulesetSpell != null) + { + rulesetCaster.TerminateSpell(rulesetSpell); + } } + } public void OnConditionAdded(RulesetCharacter target, RulesetCondition rulesetCondition) diff --git a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs index e5f1701a7c..2a435123d2 100644 --- a/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs +++ b/SolastaUnfinishedBusiness/Spells/SpellBuildersLevel03.cs @@ -539,7 +539,7 @@ internal static SpellDefinition BuildAshardalonStride() .SetGuiPresentation(Category.Spell, Sprites.GetSprite(Name, Resources.AshardalonStride, 128)) .SetSchoolOfMagic(SchoolOfMagicDefinitions.SchoolTransmutation) .SetSpellLevel(3) - .SetCastingTime(ActivationTime.Action) + .SetCastingTime(ActivationTime.BonusAction) .SetMaterialComponent(MaterialComponentType.None) .SetVerboseComponent(true) .SetSomaticComponent(true) diff --git a/SolastaUnfinishedBusiness/Translations/en/Spells/Spells01-en.txt b/SolastaUnfinishedBusiness/Translations/en/Spells/Spells01-en.txt index 280d0cc197..0cd0b332cf 100644 --- a/SolastaUnfinishedBusiness/Translations/en/Spells/Spells01-en.txt +++ b/SolastaUnfinishedBusiness/Translations/en/Spells/Spells01-en.txt @@ -129,8 +129,10 @@ Spell/&VileBrewDescription=A stream of acid emanates from you in a line 30 feet Spell/&VileBrewTitle=Tasha's Caustic Brew Spell/&VoidGraspDescription=You invoke the power of malevolent forces. Tendrils of dark energy erupt from you and batter all creatures within 10 feet of you. Each creature in that area must make a Strength saving throw. On a failed save, a target takes 2d6 necrotic damage and can't take reactions until the start of your next turn. On a successful save, the creature takes half damage, but suffers no other effect. When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d6 for each slot level above 1st. Spell/&VoidGraspTitle=Arms of Hadar -Spell/&WitchBoltDescription=A beam of crackling, blue energy lances out toward a creature within range, forming a sustained arc of lightning between you and the target. Make a ranged spell attack against that creature. On a hit, the target takes 1d12 lightning damage, and on each of your turns for the duration, you can use your action to deal 1d12 lightning damage to the target automatically. The spell ends if you use your action to do anything else. The spell also ends if the target is ever outside the spell's range. When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d12 for each slot level above 1st. +Spell/&WitchBoltDescription=A beam of crackling, blue energy lances out toward a creature within range, forming a sustained arc of lightning between you and the target. Make a ranged spell attack against that creature. On a hit, the target takes 2d12 lightning damage, and on each of your turns for the duration, you can use your bonus action to deal 1d12 lightning damage to the target automatically. The spell ends if the target is ever outside the spell's range. When you cast this spell using a spell slot of 2nd level or higher, the initial damage increases by 1d12 for each slot level above 1st. Spell/&WitchBoltTitle=Witch Bolt +Spell/&WitchBoltApplyConditionsDescription=A beam of crackling, blue energy lances out toward a creature within range, forming a sustained arc of lightning between you and the target. Make a ranged spell attack against that creature. On a hit, the target takes 2d12 lightning damage, and on each of your turns for the duration, you can use your bonus action to deal 1d12 lightning damage to the target automatically. The spell ends if the target is ever outside the spell's range. When you cast this spell using a spell slot of 2nd level or higher, the initial damage increases by 1d12 for each slot level above 1st. +Spell/&WitchBoltApplyConditionsTitle=Witch Bolt Spell/&WrathfulSmiteDescription=The target takes an extra 1d6 Necrotic damage from the attack, and it must succeed on a Wisdom saving throw or have the Frightened condition until the spell ends. At the end of each of its turns, the Frightened target repeats the save, ending the spell on itself on a success. The damage increases by 1d6 for each spell slot level above 1. Spell/&WrathfulSmiteTitle=Wrathful Smite Tooltip/&TagDamagePureTitle=Chaotic Damage