diff --git a/Ambermoon.Common/Util.cs b/Ambermoon.Common/Util.cs
index 8e809fec..6ff7052b 100644
--- a/Ambermoon.Common/Util.cs
+++ b/Ambermoon.Common/Util.cs
@@ -30,6 +30,11 @@ public static float Limit(float minValue, float value, float maxValue)
return Math.Max(minValue, Math.Min(value, maxValue));
}
+ public static uint Limit(uint minValue, uint value, uint maxValue)
+ {
+ return Math.Max(minValue, Math.Min(value, maxValue));
+ }
+
public static int Limit(int minValue, int value, int maxValue)
{
return Math.Max(minValue, Math.Min(value, maxValue));
diff --git a/Ambermoon.Core/Battle.cs b/Ambermoon.Core/Battle.cs
index 65b9be78..4e3f2872 100644
--- a/Ambermoon.Core/Battle.cs
+++ b/Ambermoon.Core/Battle.cs
@@ -937,17 +937,20 @@ void AttackAnimationFinished()
var spellInfo = SpellInfos.Entries[spell];
+ if (itemIndex == 0)
+ {
+ battleAction.Character.SpellPoints.CurrentValue = Math.Max(0, battleAction.Character.SpellPoints.CurrentValue - spellInfo.SP);
+
+ if (battleAction.Character is PartyMember partyMember)
+ layout.FillCharacterBars(game.SlotFromPartyMember(partyMember).Value, partyMember);
+ }
+
if (!CheckSpellCast(battleAction.Character, spellInfo))
{
EndCast();
return;
}
- battleAction.Character.SpellPoints.CurrentValue -= spellInfo.SP;
-
- if (battleAction.Character is PartyMember partyMember)
- layout.FillCharacterBars(game.SlotFromPartyMember(partyMember).Value, partyMember);
-
if (spell != Spell.Firebeam &&
spell != Spell.Fireball &&
spell != Spell.Firestorm &&
@@ -960,7 +963,9 @@ void AttackAnimationFinished()
spell != Spell.DestroyUndead &&
spell != Spell.HolyWord &&
spell != Spell.MagicalProjectile &&
- spell != Spell.MagicalArrows) // TODO: REMOVE. For now we only allow some spells for testing.
+ spell != Spell.MagicalArrows &&
+ spell != Spell.LPStealer &&
+ spell != Spell.SPStealer) // TODO: REMOVE. For now we only allow some spells for testing.
{
if (spell < Spell.Lame || spell > Spell.Drug)
break;
@@ -1459,6 +1464,9 @@ void HandleCharacterDeath(Character attacker, Character target, Action finishAct
}
}
+ ///
+ /// The boolean argument of the finish action means: NeedsClickAfterwards
+ ///
void ApplySpellEffect(Character caster, Character target, Spell spell, uint ticks, Action finishAction)
{
switch (spell)
@@ -1548,6 +1556,24 @@ void ApplySpellEffect(Character caster, Character target, Spell spell, uint tick
// Those deal half the caster level as damage.
DealDamage(Math.Max(1, (uint)caster.Level / 2), 0);
return;
+ case Spell.LPStealer:
+ {
+ DealDamage(caster.Level, 0);
+ caster.HitPoints.CurrentValue = Math.Min(caster.HitPoints.MaxValue, caster.HitPoints.CurrentValue +
+ Math.Min(caster.Level, caster.HitPoints.MaxValue - caster.HitPoints.CurrentValue));
+ if (caster is PartyMember castingMember)
+ layout.FillCharacterBars(game.SlotFromPartyMember(castingMember).Value, castingMember);
+ return;
+ }
+ case Spell.SPStealer:
+ // TODO: what happens if a monster wants to cast a spell afterwards but has not enough SP through SP stealer anymore?
+ target.SpellPoints.CurrentValue = (uint)Math.Max(0, (int)target.SpellPoints.CurrentValue - caster.Level);
+ caster.SpellPoints.CurrentValue += Math.Min(caster.Level, caster.SpellPoints.MaxValue - caster.SpellPoints.CurrentValue);
+ if (target is PartyMember targetMember)
+ layout.FillCharacterBars(game.SlotFromPartyMember(targetMember).Value, targetMember);
+ else if (caster is PartyMember castingMember)
+ layout.FillCharacterBars(game.SlotFromPartyMember(castingMember).Value, castingMember);
+ break;
// Winddevil: seen 10-15
// Windhowler: seen 35-46
default:
diff --git a/Ambermoon.Core/Game.cs b/Ambermoon.Core/Game.cs
index 0142c0d5..07692774 100644
--- a/Ambermoon.Core/Game.cs
+++ b/Ambermoon.Core/Game.cs
@@ -3030,6 +3030,8 @@ void ShowBattleWindow(Event nextEvent)
spell != Spell.HolyWord &&
spell != Spell.MagicalProjectile &&
spell != Spell.MagicalArrows &&
+ spell != Spell.LPStealer &&
+ spell != Spell.SPStealer &&
!(spell >= Spell.Lame && spell <= Spell.Drug))
pickedSpell = Spell.Iceball; // TODO
else
diff --git a/Ambermoon.Core/Render/SpellAnimation.cs b/Ambermoon.Core/Render/SpellAnimation.cs
index b31d544b..5f89740e 100644
--- a/Ambermoon.Core/Render/SpellAnimation.cs
+++ b/Ambermoon.Core/Render/SpellAnimation.cs
@@ -366,14 +366,14 @@ public void Play(Action finishAction)
case Spell.Windhowler:
case Spell.MagicalProjectile:
case Spell.MagicalArrows:
+ case Spell.LPStealer:
+ case Spell.SPStealer:
+ case Spell.GhostWeapon:
// Those spells use only the MoveTo method.
this.finishAction?.Invoke();
break;
- case Spell.LPStealer:
- case Spell.SPStealer:
case Spell.MonsterKnowledge:
case Spell.ShowMonsterLP:
- case Spell.GhostWeapon:
case Spell.Blink:
case Spell.Flight:
return; // TODO
@@ -728,11 +728,27 @@ void PlayHolyLight()
case Spell.AntiMagicSphere:
case Spell.Hurry:
case Spell.MassHurry:
- case Spell.LPStealer:
- case Spell.SPStealer:
case Spell.MonsterKnowledge:
case Spell.ShowMonsterLP:
return; // TODO
+ case Spell.LPStealer:
+ case Spell.SPStealer:
+ {
+ // Note: The hurt animation comes first so we immediately call the passed finish action
+ // which will display the hurt animation.
+ finishAction?.Invoke(game.CurrentBattleTicks, true, false); // Play hurt animation but do not finish.
+ this.finishAction = () => finishAction?.Invoke(game.CurrentBattleTicks, false, true); // This is called after the animation to finish.
+
+ float endScale = renderView.GraphicProvider.GetMonsterRowImageScaleFactor((MonsterRow)(tile / 6));
+ game.AddTimedEvent(TimeSpan.FromMilliseconds(500), () =>
+ {
+ byte displayLayer = (byte)(fromMonster ? 255 : ((tile / 6) * 60 + 60));
+ AddAnimation(spell == Spell.LPStealer ? CombatGraphicIndex.BlueBeam : CombatGraphicIndex.GreenBeam, 1,
+ GetTargetPosition(tile), GetSourcePosition(), BattleEffects.GetFlyDuration((uint)tile, (uint)startPosition),
+ fromMonster ? 2.5f: endScale, fromMonster ? endScale : 2.5f, displayLayer);
+ });
+ break;
+ }
case Spell.MagicalProjectile:
case Spell.MagicalArrows:
{
diff --git a/Ambermoon.Data.Common/CharacterValue.cs b/Ambermoon.Data.Common/CharacterValue.cs
index ac1f858e..1f33bf7e 100644
--- a/Ambermoon.Data.Common/CharacterValue.cs
+++ b/Ambermoon.Data.Common/CharacterValue.cs
@@ -11,7 +11,7 @@ public class CharacterValue
public uint MaxValue { get; set; }
public uint BonusValue { get; set; }
public uint Unknown { get; set; }
- public uint TotalCurrentValue => CurrentValue + BonusValue;
+ public uint TotalCurrentValue => Util.Limit(0, CurrentValue + BonusValue, MaxValue);
}
[Serializable]