From 73fb98e13b6ccfb28471db8cbd3148085bb9d4e7 Mon Sep 17 00:00:00 2001 From: Vincent Quatrevieux Date: Wed, 21 Feb 2024 18:01:32 +0100 Subject: [PATCH 1/2] feat(fight): Handle life erosion --- config.ini.dist | 5 + .../araknemu/game/GameConfiguration.java | 10 ++ .../quatrevieux/araknemu/game/GameModule.java | 4 +- .../effect/handler/damage/Damage.java | 46 +++--- .../effect/handler/damage/DamageApplier.java | 32 ++-- .../damage/FixedCasterDamageHandler.java | 2 +- .../handler/damage/FixedDamageHandler.java | 4 +- .../handler/damage/FixedStealLifeHandler.java | 4 +- .../handler/damage/MultipliableDamage.java | 5 +- .../damage/PercentLifeLostDamageHandler.java | 2 +- .../handler/damage/PunishmentHandler.java | 3 +- .../handler/damage/ReflectedDamage.java | 11 +- .../handler/damage/StealLifeHandler.java | 2 +- .../effect/handler/heal/FixedHealHandler.java | 2 +- .../handler/heal/GivePercentLifeHandler.java | 4 +- .../effect/handler/heal/HealHandler.java | 4 +- .../handler/heal/HealOnAttackHandler.java | 2 +- .../handler/heal/HealOnDamageHandler.java | 2 +- .../handler/shifting/MoveBackApplier.java | 4 +- .../game/fight/fighter/BaseFighterLife.java | 47 ++++-- .../game/fight/fighter/FighterLife.java | 59 ++++++- .../fighter/player/PlayerFighterLife.java | 22 ++- .../module/FighterInitializationModule.java | 57 +++++++ .../fight/turn/SendFightersInformation.java | 12 ++ .../araknemu/game/GameConfigurationTest.java | 1 + .../araknemu/game/fight/FightBaseCase.java | 1 + .../araknemu/game/fight/FunctionalTest.java | 2 + .../game/fight/ai/action/InvokeTest.java | 2 +- .../ai/simulation/CastSimulationTest.java | 12 +- .../effect/FixedHealSimulatorTest.java | 8 +- .../effect/FixedStealLifeSimulatorTest.java | 2 +- .../effect/GivePercentLifeSimulatorTest.java | 4 +- .../simulation/effect/HealSimulatorTest.java | 8 +- .../effect/InvokeDoubleSimulatorTest.java | 2 +- .../effect/InvokeMonsterSimulatorTest.java | 2 +- .../PercentLifeDamageSimulatorTest.java | 4 +- .../PercentLifeLostDamageSimulatorTest.java | 6 +- .../effect/StealLifeSimulatorTest.java | 2 +- .../castable/effect/EffectsHandlerTest.java | 5 +- .../fight/castable/effect/FunctionalTest.java | 139 +++++++++++++---- .../handler/damage/DamageApplierTest.java | 84 +++++++++- .../effect/handler/damage/DamageTest.java | 20 +++ .../damage/FixedStealLifeHandlerTest.java | 2 +- .../IndirectPercentLifeDamageHandlerTest.java | 2 +- .../damage/PercentLifeDamageHandlerTest.java | 2 +- .../PercentLifeLostDamageHandlerTest.java | 4 +- .../handler/damage/PunishmentHandlerTest.java | 4 +- .../handler/damage/StealLifeHandlerTest.java | 6 +- .../handler/heal/FixedHealHandlerTest.java | 4 +- .../heal/GivePercentLifeHandlerTest.java | 4 +- .../effect/handler/heal/HealHandlerTest.java | 4 +- .../handler/heal/HealOnAttackHandlerTest.java | 2 +- .../handler/heal/HealOnDamageHandlerTest.java | 8 +- .../invocations/CreateDoubleHandlerTest.java | 2 +- .../ApplySpellOnStartTurnHandlerTest.java | 2 +- .../effect/handler/misc/KillHandlerTest.java | 2 +- .../misc/RevealInvisibleHandlerTest.java | 2 +- .../reward/drop/SynchronizeLifeTest.java | 2 +- .../fight/fighter/BaseFighterLifeTest.java | 145 +++++++++++++++--- .../fighter/invocation/DoubleFighterTest.java | 8 +- .../invocation/InvocationFighterTest.java | 2 +- .../StaticInvocationFighterTest.java | 3 +- .../fighter/monster/MonsterFighterTest.java | 4 +- .../fighter/player/PlayerFighterLifeTest.java | 59 +++++-- .../fighter/player/PlayerFighterTest.java | 2 +- .../FighterInitializationModuleTest.java | 41 +++++ .../game/fight/state/ActiveStateTest.java | 2 + .../game/fight/state/FinishStateTest.java | 11 +- .../game/fight/team/SimpleTeamTest.java | 2 +- .../game/fight/turn/FightTurnListTest.java | 2 +- .../game/fight/turn/FightTurnTest.java | 3 +- .../handler/fight/EndFighterTurnTest.java | 2 + .../fight/CheckFightTerminatedTest.java | 2 +- .../fight/fighter/RemoveDeadFighterTest.java | 2 +- .../turn/SendFightersInformationTest.java | 7 +- .../action/SendFightActionTerminatedTest.java | 2 +- .../game/listener/player/SendStatsTest.java | 2 +- .../player/spell/SendUpgradedSpellTest.java | 2 +- .../network/game/out/fight/FightEndTest.java | 4 +- .../game/out/fight/turn/TurnMiddleTest.java | 2 +- 80 files changed, 783 insertions(+), 227 deletions(-) create mode 100644 src/main/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModule.java create mode 100644 src/test/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModuleTest.java diff --git a/config.ini.dist b/config.ini.dist index 07a1616be..abfe65844 100644 --- a/config.ini.dist +++ b/config.ini.dist @@ -231,6 +231,11 @@ game.dbname = araknemu ; > nor minimal discernment requirement. ; > Default value : 1.0 ;fight.rate.drop = 1.0 +; > Get the initial erosion rate for fighters +; > This value is a percentage, representing the percent of damage that will be transformed to permanent life loss +; > It must be between 0 and 100, where 0 means no erosion and 100 means all damage are permanent +; > Default value : 10 +;fight.initialErosion = 10 ; Auto save ; --------- diff --git a/src/main/java/fr/quatrevieux/araknemu/game/GameConfiguration.java b/src/main/java/fr/quatrevieux/araknemu/game/GameConfiguration.java index d1c8159d6..755f5a561 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/GameConfiguration.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/GameConfiguration.java @@ -440,5 +440,15 @@ public double xpRate() { public double dropRate() { return pool.decimal("fight.rate.drop", 1.0); } + + /** + * Get the initial erosion rate for fighters + * This value is a percentage, representing the percent of damage that will be transformed to permanent life loss + * It must be between 0 and 100, where 0 means no erosion and 100 means all damage are permanent + * Default value : 10 + */ + public @NonNegative int initialErosion() { + return pool.nonNegativeInteger("fight.initialErosion", 10); + } } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/GameModule.java b/src/main/java/fr/quatrevieux/araknemu/game/GameModule.java index e77a8a1d7..32b842202 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/GameModule.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/GameModule.java @@ -193,6 +193,7 @@ import fr.quatrevieux.araknemu.game.fight.module.AiModule; import fr.quatrevieux.araknemu.game.fight.module.CarryingModule; import fr.quatrevieux.araknemu.game.fight.module.CommonEffectsModule; +import fr.quatrevieux.araknemu.game.fight.module.FighterInitializationModule; import fr.quatrevieux.araknemu.game.fight.module.IndirectSpellApplyEffectsModule; import fr.quatrevieux.araknemu.game.fight.module.LaunchedSpellsModule; import fr.quatrevieux.araknemu.game.fight.module.MonsterInvocationModule; @@ -698,7 +699,8 @@ private void configureServices(ContainerConfigurator configurator) { fight -> new AiModule(container.get(AiFactory.class)), fight -> new MonsterInvocationModule(container.get(MonsterService.class), container.get(FighterFactory.class), fight), SpiritualLeashModule::new, - CarryingModule::new + CarryingModule::new, + fight -> new FighterInitializationModule(container.get(GameConfiguration.class).fight()) ), container.get(FightService.FightFactory.class), container.get(GameConfiguration.class).fight() diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/Damage.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/Damage.java index 7def8c873..2cfd7724e 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/Damage.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/Damage.java @@ -27,20 +27,20 @@ * Compute suffered damage * * Formula : - * (value * percent / 100 - fixed - reduce) * multiply + * ((value * percent / 100) * (0.9 ^ distance) - fixed - reduce) * multiply */ public final class Damage implements MultipliableDamage { - private final int value; + private final @NonNegative int value; private final Element element; private int multiply = 1; private int fixed = 0; - private int percent = 100; - private int reduce = 0; - private int returned = 0; + private @NonNegative int percent = 100; + private @NonNegative int reduce = 0; + private @NonNegative int returned = 0; private @NonNegative int distance = 0; - public Damage(int value, Element element) { + public Damage(@NonNegative int value, Element element) { this.value = value; this.element = element; } @@ -54,24 +54,34 @@ public Element element() { @Override public int value() { - final int base = (value * percent / 100 - fixed - reduce); + final int reducedDamage = (baseDamage() * percent / 100) - fixed - reduce; - if (base <= 0) { + if (reducedDamage <= 0) { return 0; } - return EffectsUtils.applyDistanceAttenuation(base, distance) * multiply; + return reducedDamage * multiply; + } + + /** + * Get the base damage, before applying any reduction or multiplication + * Only the distance attenuation is applied + */ + public @NonNegative int baseDamage() { + final int value = this.value; + + if (value == 0) { + return 0; + } + + return EffectsUtils.applyDistanceAttenuation(value, distance); } /** * Reduce damage in percent */ public Damage percent(int percent) { - if (percent > this.percent) { - this.percent = 0; - } else { - this.percent -= percent; - } + this.percent = Math.max(this.percent - percent, 0); return this; } @@ -95,7 +105,7 @@ public Damage multiply(int factor) { /** * Reduce fixed damage with buff effect */ - public Damage reduce(int value) { + public Damage reduce(@NonNegative int value) { this.reduce += value; return this; @@ -104,7 +114,7 @@ public Damage reduce(int value) { /** * Add reflected damage */ - public Damage reflect(int value) { + public Damage reflect(@NonNegative int value) { this.returned += value; return this; @@ -124,14 +134,14 @@ public Damage distance(@NonNegative int distance) { /** * Get the damage reduction value from armor buff effects */ - public int reducedDamage() { + public @NonNegative int reducedDamage() { return reduce; } /** * How much damage has been reflected by the target ? */ - public int reflectedDamage() { + public @NonNegative int reflectedDamage() { return returned; } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplier.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplier.java index 70fb82655..4a33ffaac 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplier.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplier.java @@ -95,7 +95,7 @@ public int apply(Fighter caster, SpellEffect effect, Fighter target, EffectValue * @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamage(Fighter, Damage) The called buff hook * @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamageApplied(Fighter, int) Buff called when damage are applied */ - public int applyFixed(Fighter caster, int value, Fighter target) { + public int applyFixed(Fighter caster, @NonNegative int value, Fighter target) { final Damage damage = createDamage(caster, target, value); return applyDirectDamage(caster, damage, target); @@ -119,7 +119,7 @@ public int applyFixed(Fighter caster, int value, Fighter target) { * @see DamageApplier#applyFixed(Buff, int) Apply damage with the same way * @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onIndirectDamage(Fighter, Damage) The called buff hook */ - public int applyIndirectFixed(Fighter caster, int value, Fighter target) { + public int applyIndirectFixed(Fighter caster, @NonNegative int value, Fighter target) { final Damage damage = createDamage(caster, target, value); target.buffs().onIndirectDamage(caster, damage); @@ -159,7 +159,7 @@ public int apply(Buff buff) { * * @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onBuffDamage(Buff, Damage) The called buff hook */ - public int applyFixed(Buff buff, int value) { + public int applyFixed(Buff buff, @NonNegative int value) { final Fighter target = buff.target(); final Damage damage = createDamage(buff.caster(), target, value); @@ -208,7 +208,7 @@ private int applyDirectDamage(Fighter caster, Damage damage, Fighter target) { target.buffs().onDirectDamage(caster, damage); if (!caster.equals(target)) { - damage.reflect(target.characteristics().get(Characteristic.COUNTER_DAMAGE)); + damage.reflect(Math.max(target.characteristics().get(Characteristic.COUNTER_DAMAGE), 0)); } final int actualDamage = applyDamage(caster, damage, target); @@ -243,13 +243,20 @@ private int applyDamage(Fighter caster, Damage damage, Fighter target) { fight.send(ActionEffect.reducedDamage(target, damage.reducedDamage())); } - final int lifeChange = target.life().alter(caster, -damage.value()); + final int damageValue = damage.value(); - if (lifeChange < 0 && !target.equals(caster) && damage.reflectedDamage() > 0) { + // Damage has been transformed to heal + if (damageValue < 0) { + return target.life().heal(caster, Asserter.castNonNegative(-damageValue)); + } + + final int actualDamage = target.life().damage(caster, damageValue, damage.baseDamage()); + + if (actualDamage > 0 && !target.equals(caster) && damage.reflectedDamage() > 0) { applyReflectedDamage(target, caster, damage); } - return lifeChange; + return -actualDamage; } /** @@ -268,7 +275,14 @@ private void applyReflectedDamage(Fighter castTarget, Fighter caster, Damage dam if (returnedDamage.baseValue() > 0) { fight.send(ActionEffect.reflectedDamage(castTarget, returnedDamage.baseValue())); - returnedDamage.target().life().alter(castTarget, -returnedDamage.value()); + + final int actualReturnedDamage = returnedDamage.value(); + + if (actualReturnedDamage > 0) { + returnedDamage.target().life().damage(castTarget, actualReturnedDamage); + } else { + returnedDamage.target().life().heal(castTarget, Asserter.castNonNegative(-actualReturnedDamage)); + } } } @@ -281,7 +295,7 @@ private void applyReflectedDamage(Fighter castTarget, Fighter caster, Damage dam * * @return The damage object to apply */ - private Damage createDamage(Fighter caster, Fighter target, int value) { + private Damage createDamage(Fighter caster, Fighter target, @NonNegative int value) { final Damage damage = new Damage(value, element) .percent(target.characteristics().get(element.percentResistance())) .fixed(target.characteristics().get(element.fixedResistance())) diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedCasterDamageHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedCasterDamageHandler.java index 2a6075a31..5739fd65f 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedCasterDamageHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedCasterDamageHandler.java @@ -38,7 +38,7 @@ public void handle(FightCastScope cast, FightCastScope.EffectScope effect) { // This is a fixed effect, without any elements // So it does not call any buff hooks - caster.life().alter(caster, -EffectValue.create(effect.effect(), caster, caster).value()); + caster.life().damage(caster, EffectValue.create(effect.effect(), caster, caster).value()); } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedDamageHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedDamageHandler.java index e48ba89ac..b572a62d8 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedDamageHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedDamageHandler.java @@ -42,7 +42,7 @@ public FixedDamageHandler(Fight fight) { protected void applyOnTarget(FightCastScope cast, SpellEffect effect, Fighter target, EffectValue value) { // This is a fixed effect, without any elements // So it does not call any buff hooks - target.life().alter(cast.caster(), -value.value()); + target.life().damage(cast.caster(), value.value()); } @Override @@ -54,7 +54,7 @@ public void buff(FightCastScope cast, FightCastScope.EffectScope effect) { @Override public boolean onStartTurn(Buff buff) { - buff.target().life().alter(buff.caster(), -EffectValue.create(buff.effect(), buff.caster(), buff.target()).value()); + buff.target().life().damage(buff.caster(), EffectValue.create(buff.effect(), buff.caster(), buff.target()).value()); return !buff.target().dead(); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandler.java index 4e6afca59..654b093e3 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandler.java @@ -46,11 +46,11 @@ protected void applyOnTarget(FightCastScope cast, SpellEffect effect, Fighter ta final FighterLife casterLife = caster.life(); final int damage = value.value(); - final int actualDamage = target.life().alter(caster, -damage); + final int actualDamage = target.life().damage(caster, damage); // This is a fixed effect, without any elements // So it does not call any buff hooks - casterLife.alter(caster, -actualDamage); + casterLife.heal(caster, actualDamage); } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/MultipliableDamage.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/MultipliableDamage.java index 972953928..c916ea0a9 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/MultipliableDamage.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/MultipliableDamage.java @@ -19,7 +19,7 @@ package fr.quatrevieux.araknemu.game.fight.castable.effect.handler.damage; -import fr.quatrevieux.araknemu.game.fight.fighter.FighterData; +import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; /** * Base type for damage object which can be multiplied @@ -31,7 +31,8 @@ public interface MultipliableDamage { * @return The damage value. If negative, the damage is transformed to heal. * If positive the number of life point to remove * - * @see fr.quatrevieux.araknemu.game.fight.fighter.FighterLife#alter(FighterData, int) + * @see fr.quatrevieux.araknemu.game.fight.fighter.FighterLife#damage(Fighter, int) + * @see fr.quatrevieux.araknemu.game.fight.fighter.FighterLife#heal(Fighter, int) */ public int value(); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandler.java index ce921e8b5..75233628b 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandler.java @@ -51,7 +51,7 @@ public void handle(FightCastScope cast, FightCastScope.EffectScope effect) { final SpellEffect spellEffect = effect.effect(); final EffectValue.Context context = EffectValue.preRoll(spellEffect, caster); final FighterLife casterLife = caster.life(); - final int lostLife = casterLife.max() - casterLife.current(); + final int lostLife = Math.max(casterLife.max() - casterLife.current(), 0); // Do not use AbstractPreRollEffectHandler to ensure that current life is not changed // during the loop, so that damage are computed on the same life value diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandler.java index c3e543946..c73ba453c 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandler.java @@ -27,6 +27,7 @@ import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; import fr.quatrevieux.araknemu.game.fight.fighter.FighterLife; import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect; +import fr.quatrevieux.araknemu.util.Asserter; /** * Handle effect of punishment spell @@ -71,7 +72,7 @@ public void handle(FightCastScope cast, FightCastScope.EffectScope effect) { } final double percent = context.forTarget(target).value() / 100d; - final int value = (int) (factor * percent); + final int value = Asserter.castNonNegative((int) (factor * percent)); applier.applyFixed(caster, value, target); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/ReflectedDamage.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/ReflectedDamage.java index 72429eaf8..5c1d53600 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/ReflectedDamage.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/ReflectedDamage.java @@ -21,6 +21,7 @@ import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; import fr.quatrevieux.araknemu.game.world.creature.characteristics.Characteristics; +import org.checkerframework.checker.index.qual.NonNegative; /** * Compute reflected damage of a cast @@ -52,7 +53,13 @@ public Fighter target() { * This value is the effectively reflected value, not the applied one. * A buff can change the real applied damage by using {@link ReflectedDamage#multiply(int)} */ - public int baseValue() { + public @NonNegative int baseValue() { + final int baseCastDamage = castDamage.value(); + + if (baseCastDamage <= 0) { + return 0; + } + final Characteristics characteristics = target.characteristics(); final int percentResistance = characteristics.get(castDamage.element().percentResistance()); final int fixedResistance = characteristics.get(castDamage.element().fixedResistance()); @@ -65,7 +72,7 @@ public int baseValue() { } // Returned damage can be at most half of hit damage - return Math.min(base, castDamage.value() / 2); + return Math.min(base, baseCastDamage / 2); } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandler.java index 9f169cf0a..817222738 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandler.java @@ -73,6 +73,6 @@ private void applyCasterHeal(int damage, Fighter caster) { } // Minimal heal is 1 - caster.life().alter(caster, Math.max(-damage / 2, 1)); + caster.life().heal(caster, Math.max(-damage / 2, 1)); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandler.java index e4e7210c2..00684cda5 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandler.java @@ -53,6 +53,6 @@ public boolean onStartTurn(Buff buff) { } private void apply(Fighter caster, SpellEffect effect, Fighter target) { - target.life().alter(caster, EffectValue.create(effect, caster, target).value()); + target.life().heal(caster, EffectValue.create(effect, caster, target).value()); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandler.java index 62a27aff4..82fff6a7b 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandler.java @@ -33,11 +33,11 @@ public void handle(FightCastScope cast, FightCastScope.EffectScope effect) { final Fighter caster = cast.caster(); final int heal = EffectValue.create(effect.effect(), caster, caster).value() * caster.life().current() / 100; - caster.life().alter(caster, -heal); + caster.life().damage(caster, heal); for (Fighter target : effect.targets()) { if (!target.equals(caster)) { - target.life().alter(caster, heal); + target.life().heal(caster, heal); } } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandler.java index 15f4d93c2..ef4953f9a 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandler.java @@ -51,7 +51,7 @@ protected boolean applyOnTarget(FightCastScope cast, SpellEffect effect, Fighter .fixed(caster.characteristics().get(Characteristic.HEALTH_BOOST)) ; - target.life().alter(caster, EffectsUtils.applyDistanceAttenuation(value.value(), distance)); + target.life().heal(caster, EffectsUtils.applyDistanceAttenuation(value.value(), distance)); return true; } @@ -74,7 +74,7 @@ public boolean onStartTurn(Buff buff) { .fixed(caster.characteristics().get(Characteristic.HEALTH_BOOST)) ; - target.life().alter(caster, value.value()); + target.life().heal(caster, value.value()); return true; } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandler.java index e6e64d2e4..cb458d5e1 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandler.java @@ -52,7 +52,7 @@ public void onDirectDamage(Buff buff, Fighter caster, Damage value) { final int heal = value.value() * percent / 100; if (heal > 0) { - caster.life().alter(buff.target(), heal); + caster.life().heal(buff.target(), heal); } } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandler.java index 1618671d3..b76d0eb63 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandler.java @@ -64,6 +64,6 @@ private void apply(Fighter caster, SpellEffect effect, Fighter target) { .fixed(caster.characteristics().get(Characteristic.HEALTH_BOOST)) ; - target.life().alter(caster, value.value()); + target.life().heal(caster, value.value()); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/shifting/MoveBackApplier.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/shifting/MoveBackApplier.java index c181db1e9..b63f98ac4 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/shifting/MoveBackApplier.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/shifting/MoveBackApplier.java @@ -119,7 +119,7 @@ private void applyBlockingDamageChain(Fighter caster, Fighter target, FightCell return; } - target.life().alter(caster, -damage); + target.life().damage(caster, damage); // Divide damage by 2 on each fighter for (damage /= 2; damage > 0; damage /= 2) { @@ -134,7 +134,7 @@ private void applyBlockingDamageChain(Fighter caster, Fighter target, FightCell } lastCell = nextCell.get(); - NullnessUtil.castNonNull(lastCell.fighter()).life().alter(caster, -damage); + NullnessUtil.castNonNull(lastCell.fighter()).life().damage(caster, damage); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLife.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLife.java index dff8df7bb..a06143390 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLife.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLife.java @@ -22,8 +22,10 @@ import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterDie; import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterLifeChanged; import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterMaxLifeChanged; +import fr.quatrevieux.araknemu.util.Asserter; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.Positive; +import org.checkerframework.common.value.qual.IntRange; /** * Handle life points for fighters @@ -33,6 +35,7 @@ public final class BaseFighterLife implements FighterLife { private @NonNegative int max; private @NonNegative int current; + private @IntRange(from = 0, to = 100) int erosion = 0; private boolean dead = false; public BaseFighterLife(Fighter fighter, @NonNegative int life, @NonNegative int max) { @@ -61,30 +64,47 @@ public boolean dead() { } @Override - @SuppressWarnings("compound.assignment") // bound of value is not resolved - public int alter(Fighter caster, int value) { + public @NonNegative int heal(Fighter caster, @NonNegative int value) { if (dead) { return 0; } - if (value < -current) { - value = -current; - } else if (value > max - current) { - value = max - current; + final int current = this.current; + final int left = Math.max(max - current, 0); + final int actualHeal = Math.min(value, left); + + this.current = current + actualHeal; + + fighter.fight().dispatch(new FighterLifeChanged(fighter, caster, actualHeal)); + fighter.buffs().onLifeAltered(actualHeal); + + return actualHeal; + } + + @Override + public @NonNegative int damage(Fighter caster, @NonNegative int value, @NonNegative int baseDamage) { + if (dead) { + return 0; } - current += value; + final int current = this.current; + final int actualDamage = Math.min(value, current); + final int newLife = Asserter.castNonNegative(current - actualDamage); - fighter.fight().dispatch(new FighterLifeChanged(fighter, caster, value)); + // Apply erosion + max = Math.max(max - (erosion * baseDamage / 100), 1); - if (current == 0) { + this.current = Math.min(newLife, max); + fighter.fight().dispatch(new FighterLifeChanged(fighter, caster, -actualDamage)); + + if (newLife == 0) { dead = true; fighter.fight().dispatch(new FighterDie(fighter, caster)); } else { - fighter.buffs().onLifeAltered(value); + fighter.buffs().onLifeAltered(-actualDamage); } - return value; + return actualDamage; } @Override @@ -124,4 +144,9 @@ public void resuscitate(Fighter caster, @Positive int value) { current = Math.min(value, max); dead = false; } + + @Override + public void alterErosion(int value) { + erosion = Math.max(0, Math.min(100, erosion + value)); + } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/FighterLife.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/FighterLife.java index 6765ce321..30df2e297 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/FighterLife.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/FighterLife.java @@ -20,6 +20,7 @@ package fr.quatrevieux.araknemu.game.fight.fighter; import fr.quatrevieux.araknemu.game.world.creature.Life; +import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.Positive; /** @@ -32,14 +33,64 @@ public interface FighterLife extends Life { public boolean dead(); /** - * Change fighter life + * Heal the fighter (i.e. add life points) + * + * If the fighter is dead, or is full life, this method will do nothing + * If the value is higher that the current life left, the life will be set to the max life * * This method will trigger buffs {@link fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onLifeAltered(int)} * - * @param caster The caster - * @param value The modified value. Positive for heal, negative for damage + * @param caster The caster of the heal effect + * @param value The heal value + * + * @return The applied heal value. Will be clamped to the left life points. + */ + public @NonNegative int heal(Fighter caster, @NonNegative int value); + + /** + * Apply damage to the fighter (i.e. remove life points) + * + * If the fighter is dead, this method will do nothing + * If the value is higher that the current life, the fighter will be considered as dead, and its life will be set to 0 + * + * This method will trigger buffs {@link fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onLifeAltered(int)} + * + * @param caster The caster of the damage effect + * @param value The damage value + * + * @return The actual number of life points removed. If damage are higher than the current life, the returned value will be the remaining life points before the fighter die. + * + * @see #damage(Fighter, int, int) To specify the base damage value. On this method, the base damage is equal to the damage value. + */ + public default @NonNegative int damage(Fighter caster, @NonNegative int value) { + return damage(caster, value, value); + } + + /** + * Apply damage to the fighter (i.e. remove life points) + * + * If the fighter is dead, this method will do nothing + * If the value is higher that the current life, the fighter will be considered as dead, and its life will be set to 0 + * + * This method will trigger buffs {@link fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onLifeAltered(int)} + * + * @param caster The caster of the damage effect + * @param value The damage value + * @param baseDamage The base damage value, before applying any reduction. This value is used to compute erosion. + * + * @return The actual number of life points removed. If damage are higher than the current life, the returned value will be the remaining life points before the fighter die. + * + * @see fr.quatrevieux.araknemu.game.fight.castable.effect.handler.damage.Damage#baseDamage() To get the base damage parameter + */ + public @NonNegative int damage(Fighter caster, @NonNegative int value, @NonNegative int baseDamage); + + /** + * Modify the erosion rate of the fighter + * This value is added to the current erosion rate, and clamped to [0, 100] + * + * @param value The value to add to the current erosion rate. This value is a percentage, so 100 means 100% of the damage will be applied as erosion */ - public int alter(Fighter caster, int value); + public void alterErosion(int value); /** * Change the max life of the current fighter diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLife.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLife.java index 94a387452..95034b35a 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLife.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLife.java @@ -59,12 +59,21 @@ public boolean dead() { } @Override - public int alter(Fighter caster, int value) { + public @NonNegative int heal(Fighter caster, @NonNegative int value) { if (delegate == null) { throw new IllegalStateException("PlayerFighterLife must be initialized"); } - return delegate.alter(caster, value); + return delegate.heal(caster, value); + } + + @Override + public @NonNegative int damage(Fighter caster, @NonNegative int value, @NonNegative int baseDamage) { + if (delegate == null) { + throw new IllegalStateException("PlayerFighterLife must be initialized"); + } + + return delegate.damage(caster, value, baseDamage); } @Override @@ -94,6 +103,15 @@ public void resuscitate(Fighter caster, @Positive int value) { delegate.resuscitate(caster, value); } + @Override + public void alterErosion(int value) { + if (delegate == null) { + throw new IllegalStateException("PlayerFighterLife must be initialized"); + } + + delegate.alterErosion(value); + } + /** * Initialise the fighter life when fight is started */ diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModule.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModule.java new file mode 100644 index 000000000..671801e5d --- /dev/null +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModule.java @@ -0,0 +1,57 @@ +/* + * This file is part of Araknemu. + * + * Araknemu is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Araknemu is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Araknemu. If not, see . + * + * Copyright (c) 2017-2024 Vincent Quatrevieux + */ + +package fr.quatrevieux.araknemu.game.fight.module; + +import fr.quatrevieux.araknemu.core.event.Listener; +import fr.quatrevieux.araknemu.game.GameConfiguration; +import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; +import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterInitialized; + +/** + * Module for perform initialization of fighters depending on configuration + */ +public final class FighterInitializationModule implements FightModule { + private final GameConfiguration.FightConfiguration configuration; + + public FighterInitializationModule(GameConfiguration.FightConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public Listener[] listeners() { + return new Listener[] { + new Listener() { + @Override + public void on(FighterInitialized event) { + initializeFighter(event.fighter()); + } + + @Override + public Class event() { + return FighterInitialized.class; + } + }, + }; + } + + private void initializeFighter(Fighter fighter) { + fighter.life().alterErosion(configuration.initialErosion()); + } +} diff --git a/src/main/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformation.java b/src/main/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformation.java index e1e263027..418611e88 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformation.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformation.java @@ -21,13 +21,17 @@ import fr.quatrevieux.araknemu.core.event.Listener; import fr.quatrevieux.araknemu.game.fight.Fight; +import fr.quatrevieux.araknemu.game.fight.fighter.operation.FighterOperation; +import fr.quatrevieux.araknemu.game.fight.fighter.player.PlayerFighter; import fr.quatrevieux.araknemu.game.fight.turn.event.NextTurnInitiated; +import fr.quatrevieux.araknemu.network.game.out.account.Stats; import fr.quatrevieux.araknemu.network.game.out.fight.turn.TurnMiddle; /** * Send the fighters information between two turns */ public final class SendFightersInformation implements Listener { + private static final SendStats sendStats = new SendStats(); private final Fight fight; public SendFightersInformation(Fight fight) { @@ -37,10 +41,18 @@ public SendFightersInformation(Fight fight) { @Override public void on(NextTurnInitiated event) { fight.send(new TurnMiddle(fight.fighters())); + fight.fighters().forEach(fighter -> fighter.apply(sendStats)); // Life of the current fighter can only be synchronized by this packet } @Override public Class event() { return NextTurnInitiated.class; } + + private static class SendStats implements FighterOperation { + @Override + public void onPlayer(PlayerFighter fighter) { + fighter.send(new Stats(fighter.properties())); + } + } } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/GameConfigurationTest.java b/src/test/java/fr/quatrevieux/araknemu/game/GameConfigurationTest.java index b46eac453..51013fa2b 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/GameConfigurationTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/GameConfigurationTest.java @@ -114,6 +114,7 @@ void fight() { assertEquals(1.0, configuration.fight().xpRate()); assertEquals(1.0, configuration.fight().dropRate()); + assertEquals(10, configuration.fight().initialErosion()); } @Test diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/FightBaseCase.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/FightBaseCase.java index c343b2cc6..aa13dd89d 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/FightBaseCase.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/FightBaseCase.java @@ -40,6 +40,7 @@ import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import fr.quatrevieux.araknemu.game.fight.module.CommonEffectsModule; +import fr.quatrevieux.araknemu.game.fight.module.FighterInitializationModule; import fr.quatrevieux.araknemu.game.fight.module.StatesModule; import fr.quatrevieux.araknemu.game.fight.state.ActiveState; import fr.quatrevieux.araknemu.game.fight.state.FinishState; diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/FunctionalTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/FunctionalTest.java index 0ed2910f9..aab1bd81e 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/FunctionalTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/FunctionalTest.java @@ -55,6 +55,7 @@ import fr.quatrevieux.araknemu.game.fight.turn.action.util.CriticalityStrategy; import fr.quatrevieux.araknemu.game.item.ItemService; import fr.quatrevieux.araknemu.game.player.GamePlayer; +import fr.quatrevieux.araknemu.network.game.out.account.Stats; import fr.quatrevieux.araknemu.network.game.out.fight.BeginFight; import fr.quatrevieux.araknemu.network.game.out.fight.FightEnd; import fr.quatrevieux.araknemu.network.game.out.fight.FighterPositions; @@ -182,6 +183,7 @@ void challengeFight() throws Exception { requestStack.assertAll( new FinishTurn(currentTurn), new TurnMiddle(fight.fighters()), + new Stats(player.fighter().properties()), new StartTurn(currentTurn = fight.turnList().current().get()) ); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/action/InvokeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/action/InvokeTest.java index c4be89579..8f44c2d99 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/action/InvokeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/action/InvokeTest.java @@ -94,7 +94,7 @@ void shouldPrioritizeHealWhenAlliesAreLowLife() { assertCast(39, 108); // Allies are low life : bonus for healing - player.fighter().life().alter(player.fighter(), -95); + player.fighter().life().damage(player.fighter(), 95); action.initialize(ai); // Recompute allies life ratio assertCast(190, 107); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/CastSimulationTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/CastSimulationTest.java index 3c4e354de..213bb03cd 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/CastSimulationTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/CastSimulationTest.java @@ -65,9 +65,9 @@ public void setUp() throws Exception { @Test void heal() { - fighter.life().alter(fighter, -10); - allie.life().alter(fighter, -10); - ennemy.life().alter(fighter, -10); + fighter.life().damage(fighter, 10); + allie.life().damage(fighter, 10); + ennemy.life().damage(fighter, 10); simulation.addHeal(new Interval(5, 5), fighter); simulation.addHeal(new Interval(4, 4), allie); @@ -81,7 +81,7 @@ void heal() { @ParameterizedTest @MethodSource("provideHeal") void healLimitByLostLife(Interval value, double expectedValue) { - fighter.life().alter(fighter, -10); + fighter.life().damage(fighter, 10); simulation.addHeal(value, fighter); @@ -288,7 +288,7 @@ void alterActionPoints() { @Test void addHealBuff() { - fighter.life().alter(fighter, -10); + fighter.life().damage(fighter, 10); simulation.addHealBuff(new Interval(5, 10), 3, fighter); assertEquals(7.5, simulation.selfLife()); @@ -305,7 +305,7 @@ void addHealBuffAlreadyFullLife() { @Test void addHealBuffOnlyOneTurnShouldNotSetAsBuff() { - fighter.life().alter(fighter, -10); + fighter.life().damage(fighter, 10); simulation.addHealBuff(new Interval(5, 10), 1, fighter); assertEquals(7.5, simulation.selfLife()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedHealSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedHealSimulatorTest.java index 0da32a9fd..86e304cf7 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedHealSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedHealSimulatorTest.java @@ -61,8 +61,8 @@ public void setUp() throws Exception { fighter.init(); target.init(); - target.life().alter(target, -40); - fighter.life().alter(fighter, -40); + target.life().damage(target, 40); + fighter.life().damage(fighter, 40); ai = new FighterAI(fighter, fight, new NullGenerator()); } @@ -70,7 +70,7 @@ public void setUp() throws Exception { void simulateSimple() { assertEquals(10, simulate()); - target.life().alter(target, 35); + target.life().heal(target, 35); assertEquals(5, simulate()); } @@ -123,7 +123,7 @@ void simulateBuff() { @Test void simulateWithBuffCappedByCurrentLife() { - fighter.life().alter(fighter, 30); + fighter.life().heal(fighter, 30); FixedHealSimulator simulator = new FixedHealSimulator(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedStealLifeSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedStealLifeSimulatorTest.java index d8b5fc6a0..45d6b0714 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedStealLifeSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/FixedStealLifeSimulatorTest.java @@ -63,7 +63,7 @@ public void setUp() throws Exception { fighter.init(); target.life().alterMax(target, 1000); - fighter.life().alter(fighter, -50); + fighter.life().damage(fighter, 50); ai = new FighterAI(fighter, fight, new NullGenerator()); simulator = new FixedStealLifeSimulator(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/GivePercentLifeSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/GivePercentLifeSimulatorTest.java index 027794d72..aa2c315ab 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/GivePercentLifeSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/GivePercentLifeSimulatorTest.java @@ -26,14 +26,12 @@ import fr.quatrevieux.araknemu.game.fight.ai.action.logic.NullGenerator; import fr.quatrevieux.araknemu.game.fight.ai.simulation.CastSimulation; import fr.quatrevieux.araknemu.game.fight.castable.CastScope; -import fr.quatrevieux.araknemu.game.fight.castable.effect.Element; import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; import fr.quatrevieux.araknemu.game.fight.fighter.player.PlayerFighter; import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.spell.Spell; import fr.quatrevieux.araknemu.game.spell.SpellConstraints; import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect; -import fr.quatrevieux.araknemu.game.spell.effect.area.CellArea; import fr.quatrevieux.araknemu.game.spell.effect.area.CircleArea; import fr.quatrevieux.araknemu.game.spell.effect.target.SpellEffectTarget; import org.junit.jupiter.api.BeforeEach; @@ -60,7 +58,7 @@ public void setUp() throws Exception { fighter.init(); target.init(); - target.life().alter(target, -30); + target.life().damage(target, 30); ai = new FighterAI(fighter, fight, new NullGenerator()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/HealSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/HealSimulatorTest.java index c05b22491..129fc7157 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/HealSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/HealSimulatorTest.java @@ -61,8 +61,8 @@ public void setUp() throws Exception { fighter.init(); target.init(); - target.life().alter(target, -40); - fighter.life().alter(fighter, -40); + target.life().damage(target, 40); + fighter.life().damage(fighter, 40); ai = new FighterAI(fighter, fight, new NullGenerator()); } @@ -70,7 +70,7 @@ public void setUp() throws Exception { void simulateSimple() { assertEquals(25, simulate()); - target.life().alter(target, 30); + target.life().heal(target, 30); assertEquals(10, simulate()); } @@ -123,7 +123,7 @@ void simulateBuff() { @Test void simulateWithBuffCappedByCurrentLife() { - fighter.life().alter(fighter, 30); + fighter.life().heal(fighter, 30); HealSimulator simulator = new HealSimulator(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeDoubleSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeDoubleSimulatorTest.java index d6440b88c..f142e88f9 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeDoubleSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeDoubleSimulatorTest.java @@ -58,7 +58,7 @@ public void setUp() throws Exception { fighter.init(); target.init(); - fighter.life().alter(fighter, -40); + fighter.life().damage(fighter, 40); target.life().alterMax(target, 500); ai = new FighterAI(fighter, fight, new NullGenerator()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeMonsterSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeMonsterSimulatorTest.java index ef8a9fc00..1b7074f85 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeMonsterSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/InvokeMonsterSimulatorTest.java @@ -60,7 +60,7 @@ public void setUp() throws Exception { fighter.init(); target.init(); - fighter.life().alter(fighter, -40); + fighter.life().damage(fighter, 40); target.life().alterMax(target, 500); ai = new FighterAI(fighter, fight, new NullGenerator()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeDamageSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeDamageSimulatorTest.java index 39805e60c..01aa9caf4 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeDamageSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeDamageSimulatorTest.java @@ -70,7 +70,7 @@ void simulateSimple() { assertEquals(-29, simulate().enemiesLife()); assertEquals(0, simulate().selfLife()); - fighter.life().alter(fighter, -100); + fighter.life().damage(fighter, 100); assertEquals(-19, simulate().enemiesLife()); } @@ -82,7 +82,7 @@ void simulateWithResistance() { assertEquals(-16, simulate().enemiesLife()); assertEquals(0, simulate().selfLife()); - fighter.life().alter(fighter, -100); + fighter.life().damage(fighter, 100); assertEquals(-9, simulate().enemiesLife()); simulator = new PercentLifeDamageSimulator(Element.WATER); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeLostDamageSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeLostDamageSimulatorTest.java index f625c22fe..8c22c2648 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeLostDamageSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/PercentLifeLostDamageSimulatorTest.java @@ -61,7 +61,7 @@ public void setUp() throws Exception { target.init(); fighter.init(); target.life().alterMax(target, 1000); - fighter.life().alter(target, -100); + fighter.life().damage(target, 100); ai = new FighterAI(fighter, fight, new NullGenerator()); simulator = new PercentLifeLostDamageSimulator(Element.EARTH); } @@ -71,7 +71,7 @@ void simulateSimple() { assertEquals(-10, simulate().enemiesLife()); assertEquals(0, simulate().selfLife()); - fighter.life().alter(fighter, 50); + fighter.life().heal(fighter, 50); assertEquals(-5, simulate().enemiesLife()); } @@ -83,7 +83,7 @@ void simulateWithResistance() { assertEquals(-2, simulate().enemiesLife()); assertEquals(0, simulate().selfLife()); - fighter.life().alter(fighter, -100); + fighter.life().damage(fighter, 100); assertEquals(-10, simulate().enemiesLife()); simulator = new PercentLifeLostDamageSimulator(Element.WATER); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/StealLifeSimulatorTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/StealLifeSimulatorTest.java index 601bc9000..ad187dc15 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/StealLifeSimulatorTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ai/simulation/effect/StealLifeSimulatorTest.java @@ -60,7 +60,7 @@ public void setUp() throws Exception { target = other.fighter(); fighter.init(); - fighter.life().alter(fighter, -30); + fighter.life().damage(fighter, 30); ai = new FighterAI(fighter, fight, new NullGenerator()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/EffectsHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/EffectsHandlerTest.java index 526dd54f7..9bf1dfaae 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/EffectsHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/EffectsHandlerTest.java @@ -27,7 +27,6 @@ import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.BuffHook; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.EffectHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.damage.DamageHandler; -import fr.quatrevieux.araknemu.game.fight.fighter.PlayableFighter; import fr.quatrevieux.araknemu.game.fight.fighter.player.PlayerFighter; import fr.quatrevieux.araknemu.game.fight.module.CommonEffectsModule; import fr.quatrevieux.araknemu.game.fight.turn.FightTurn; @@ -173,7 +172,7 @@ void applyStealLife() { Mockito.when(spell.constraints()).thenReturn(constraints); Mockito.when(constraints.freeCell()).thenReturn(false); - player.fighter().life().alter(player.fighter(), -20); + player.fighter().life().damage(player.fighter(), 20); requestStack.clear(); handler.apply(makeCastScope(player.fighter(), spell, effect, other.fighter().cell())); @@ -210,7 +209,7 @@ void applyShouldStopWhenFightEnd() throws SQLException { player.fighter().move(fight.map().get(166)); other.fighter().move(fight.map().get(152)); - other.fighter().life().alter(other.fighter(), -45); + other.fighter().life().damage(other.fighter(), 45); requestStack.clear(); handler.apply(FightCastScope.simple( diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java index a07aba1b7..5cbe09eda 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java @@ -20,6 +20,7 @@ package fr.quatrevieux.araknemu.game.fight.castable.effect; import fr.quatrevieux.araknemu.data.constant.Characteristic; +import fr.quatrevieux.araknemu.game.GameConfiguration; import fr.quatrevieux.araknemu.game.fight.Fight; import fr.quatrevieux.araknemu.game.fight.FightBaseCase; import fr.quatrevieux.araknemu.game.fight.ai.FighterAI; @@ -42,6 +43,7 @@ import fr.quatrevieux.araknemu.game.fight.module.AiModule; import fr.quatrevieux.araknemu.game.fight.module.CarryingModule; import fr.quatrevieux.araknemu.game.fight.module.CommonEffectsModule; +import fr.quatrevieux.araknemu.game.fight.module.FighterInitializationModule; import fr.quatrevieux.araknemu.game.fight.module.IndirectSpellApplyEffectsModule; import fr.quatrevieux.araknemu.game.fight.module.MonsterInvocationModule; import fr.quatrevieux.araknemu.game.fight.module.SpiritualLeashModule; @@ -107,6 +109,7 @@ public void setUp() throws Exception { fight.register(new MonsterInvocationModule(container.get(MonsterService.class), container.get(FighterFactory.class), fight)); fight.register(new SpiritualLeashModule(fight)); fight.register(new AiModule(container.get(AiFactory.class))); + fight.register(new FighterInitializationModule(container.get(GameConfiguration.class).fight())); fighter1 = player.fighter(); fighter2 = other.fighter(); @@ -136,9 +139,13 @@ void poisonSpell() { assertEquals(fighter1.life().current(), fighter1.life().max()); assertEquals(fighter2.life().current(), fighter2.life().max()); + int maxBefore = fighter2.life().max(); + int lifeBefore = fighter2.life().current(); + fighter1.turn().stop(); - assertEquals(12, fighter2.life().max() - fighter2.life().current()); + assertEquals(12, lifeBefore - fighter2.life().current()); + assertEquals(1, maxBefore - fighter2.life().max()); requestStack.assertOne(ActionEffect.alterLifePoints(fighter1, fighter2, -12)); assertEquals(4, buff1.get().remainingTurns()); @@ -189,9 +196,10 @@ void skipNextTurnSelfCast() { @Test void probableEffectSpell() { + int lifeBefore = fighter2.life().current(); Spell spell = castNormal(109, fighter2.cell()); // Bluff - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertBetween(1, 50, damage); @@ -206,12 +214,14 @@ void probableEffectSpell() { @Test void returnSpell() { + int lifeBefore = fighter2.life().current(); + castNormal(4, fighter1.cell()); // Return spell fighter1.turn().stop(); castNormal(109, fighter1.cell()); // Bluff - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertBetween(1, 50, damage); @@ -320,7 +330,7 @@ void healOrMultiplyDamage() { int healCount = 0; for (int i = 0; i < 15; ++i) { - fighter1.life().alter(fighter1, fighter1.life().max() - fighter1.life().current() - 15); // Fighter1 has -15 LP + fighter1.life().heal(fighter1, fighter1.life().max() - fighter1.life().current() - 15); // Fighter1 has -15 LP int lifeBefore = fighter1.life().current(); castNormal(103, fighter1.cell()); // Chance d'Ecaflip @@ -388,7 +398,7 @@ void dispelBuffs() { @Test void heal() { - fighter1.life().alter(fighter1, -50); + fighter1.life().damage(fighter1, 50, 0); // ignore erosion castNormal(121, fighter1.cell()); // Mot curatif @@ -400,7 +410,7 @@ void heal() { @Test void healAsBuff() { - fighter1.life().alter(fighter1, -50); + fighter1.life().damage(fighter1, 50, 0); // ignore erosion castNormal(131, fighter1.cell()); // Mot de Régénération @@ -419,7 +429,7 @@ void healAsBuff() { void healOnDamage() { castNormal(1556, fighter1.cell()); // Fourberie - fighter1.life().alter(fighter1, -50); + fighter1.life().damage(fighter1, 50, 0); // ignore erosion int heal = 50 + fighter1.life().current() - fighter1.life().max(); assertEquals(37, heal); @@ -787,9 +797,11 @@ void stealMovementPoints() { @Test void casterFixedDamage() { + int lifeBefore = fighter1.life().current(); + castNormal(135, fighter2.cell()); // Mot de Sacrifice - int damage = fighter1.life().max() - fighter1.life().current(); + int damage = lifeBefore - fighter1.life().current(); assertBetween(31, 40, damage); assertTrue(fighter2.life().isFull()); @@ -822,19 +834,21 @@ void fixedStealLife() { @Test void percentLifeDamage() { + int lifeBefore = fighter2.life().current(); castNormal(951, fighter2.cell()); // Rocaille - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertEquals(44, damage); } @Test void percentLifeLostDamage() { - fighter1.life().alter(fighter1, -100); + fighter1.life().damage(fighter1, 100, 0); // ignore erosion + int lifeBefore = fighter2.life().current(); castNormal(1708, fighter2.cell()); // Correction Bwork - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertEquals(30, damage); } @@ -855,7 +869,7 @@ void punishment() { @Test void motlotov() { - fighter1.life().alter(fighter1, -195); // Set life to 100LP + fighter1.life().damage(fighter1, 195); // Set life to 100LP castNormal(427, fighter1.cell()); // Mot Lotof requestStack.assertOne(ActionEffect.changeAppearance(fighter1, fighter1, 7032, 2)); @@ -895,11 +909,13 @@ void givePercentLife() { @Test void maximizeTargetEffects() { + int lifeBefore = fighter2.life().current(); + castNormal(410, fighter2.cell()); // Brokle passTurns(1); castNormal(109, fighter2.cell()); // Bluff - assertEquals(45, fighter2.life().max() - fighter2.life().current()); + assertEquals(45, lifeBefore - fighter2.life().current()); requestStack.assertOne(ActionEffect.alterLifePoints(fighter1, fighter2, -45)); } @@ -926,13 +942,14 @@ void multiplyDamage() { @Test void damageOnActionPointUse() { + int lifeBefore = fighter2.life().current(); castNormal(200, fighter2.cell()); // Poison Paralysant fighter1.turn().stop(); fighter2.turn().points().useActionPoints(5); fighter2.turn().stop(); - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertEquals(12, damage); requestStack.assertOne(ActionEffect.alterLifePoints(fighter1, fighter2, -12)); } @@ -1004,7 +1021,7 @@ void addVitalityDieOnDebuff() { ); castNormal(155, fighters.get(0).cell()); // Vitality - fighters.get(0).life().alter(fighters.get(0), -110); + fighters.get(0).life().damage(fighters.get(0), 110); fighters.get(0).buffs().removeAll(); @@ -1016,7 +1033,7 @@ void addVitalityDieOnDebuff() { @Test void dieOnBuffRefresh() { castNormal(155, fighter1.cell()); // Vitality - fighter1.life().alter(fighter1, -300); + fighter1.life().damage(fighter1, 300); passTurns(20); fighter1.turn().stop(); @@ -1109,15 +1126,17 @@ void boostSpellDamage() { @Test void healOnAttack() { + int lifeBefore = fighter1.life().current(); + castNormal(1687, fighter1.cell()); // Soin Sylvestre - fighter2.life().alter(fighter2, -20); + fighter2.life().damage(fighter2, 20); fighter1.turn().stop(); int lastLife = fighter2.life().current(); castNormal(183, fighter1.cell()); // Simple attack - int damage = fighter1.life().max() - fighter1.life().current(); + int damage = lifeBefore - fighter1.life().current(); assertEquals(damage, fighter2.life().current() - lastLife); requestStack.assertOne(ActionEffect.alterLifePoints(fighter1, fighter2, damage)); @@ -1125,12 +1144,14 @@ void healOnAttack() { @Test void addCharacteristicOnDamage() { + int lifeBefore = fighter1.life().current(); + castNormal(433, fighter1.cell()); // Châtiment Osé fighter1.turn().stop(); castNormal(183, fighter1.cell()); // Simple attack - int damage = fighter1.life().max() - fighter1.life().current(); + int damage = lifeBefore - fighter1.life().current(); Buff buff = fighter1.buffs().stream().filter(b -> b.effect().effect() == 123).findFirst().get(); assertEquals(damage, fighter1.characteristics().get(Characteristic.LUCK)); @@ -1142,6 +1163,8 @@ void addCharacteristicOnDamage() { @Test void addVitalityOnDamage() { + fighter1.life().alterErosion(-10); // Disable erosion, to make sure it doesn't affect the test + castNormal(441, fighter1.cell()); // Châtiment Vitalesque fighter1.turn().stop(); @@ -1359,11 +1382,15 @@ void trap() { assertEquals(1, fight.map().objects().stream().count()); requestStack.clear(); + int lifeBefore = fighter2.life().current(); + int maxBefore = fighter2.life().max(); + fighter2.move(fight.map().get(126)); // Move on trap - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertBetween(13, 19, damage); + assertEquals(1, maxBefore - fighter2.life().max()); assertFalse(fight.map().objects().stream().findFirst().isPresent()); requestStack.assertOne(new RemoveZone(trap)); requestStack.assertOne(new UpdateCells(UpdateCells.Data.reset(126))); @@ -1388,6 +1415,8 @@ void trapShouldNotBeVisibleByOtherTeam() { @Test void trapChain() { + int lifeBefore = fighter2.life().current(); + fighter1.move(fight.map().get(123)); fighter1.turn().points().addActionPoints(10); @@ -1401,7 +1430,7 @@ void trapChain() { requestStack.assertOne(ActionEffect.trapTriggered(fighter1, fighter2, fight.map().get(125), service.get(73).level(5))); requestStack.assertOne(ActionEffect.trapTriggered(fighter1, fighter2, fight.map().get(80), service.get(65).level(5))); - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertBetween(13, 19, damage); assertEquals(80, fighter2.cell().id()); @@ -1429,6 +1458,11 @@ void trapShouldNotPerformInfiniteRepulsion() { @Test void areaTrapShouldApplyEffectToAllFightersInArea() { + int lifeBefore1 = fighter1.life().current(); + int lifeBefore2 = fighter2.life().current(); + int maxBefore1 = fighter1.life().max(); + int maxBefore2 = fighter2.life().max(); + fighter1.move(fight.map().get(167)); castNormal(79, fight.map().get(197)); // piège de masse @@ -1436,30 +1470,35 @@ void areaTrapShouldApplyEffectToAllFightersInArea() { fighter2.move(fight.map().get(226)); // Move in trap area - int damage1 = fighter1.life().max() - fighter1.life().current(); - int damage2 = fighter2.life().max() - fighter2.life().current(); + int damage1 = lifeBefore1 - fighter1.life().current(); + int damage2 = lifeBefore2 - fighter2.life().current(); assertBetween(13, 25, damage1); assertBetween(13, 25, damage2); + assertBetween(1, 2, maxBefore1 - fighter1.life().max()); + assertBetween(1, 2, maxBefore2 - fighter2.life().max()); requestStack.assertOne(ActionEffect.trapTriggered(fighter1, fighter2, fight.map().get(197), service.get(79).level(5))); } @Test void addPhysicalDamage() { + int lifeBefore = fighter2.life().current(); + fighter1.turn().points().addActionPoints(10); castNormal(16, fighter1.cell()); // Science du bâton castNormal(183, fighter2.cell()); // Ronce - int damage = fighter2.life().max() - fighter2.life().current(); + int damage = lifeBefore - fighter2.life().current(); assertBetween(30, 40, damage); // +15 damage - fighter2.life().alter(fighter2, 100); + fighter2.life().heal(fighter2, 1000); + lifeBefore = fighter2.life().current(); castNormal(3, fighter2.cell()); // Attaque naturelle - damage = fighter2.life().max() - fighter2.life().current(); + damage = lifeBefore - fighter2.life().current(); assertBetween(17, 27, damage); // Boost not applied } @@ -1978,7 +2017,7 @@ void increaseWeaponSkill() throws SQLException { int damage = fighter2.life().max() - fighter2.life().current(); assertBetween(1, 12, damage); - fighter2.life().alter(fighter2, 100); + fighter2.life().heal(fighter2, 100); castCloseCombatCritical(fighter2.cell()); damage = fighter2.life().max() - fighter2.life().current(); @@ -1987,7 +2026,7 @@ void increaseWeaponSkill() throws SQLException { passTurns(5); assertEquals(90, CastableWeapon.class.cast(fighter1.closeCombat().get()).ability()); - fighter2.life().alter(fighter2, 100); + fighter2.life().heal(fighter2, 100); castCloseCombatCritical(fighter2.cell()); damage = fighter2.life().max() - fighter2.life().current(); @@ -1999,7 +2038,7 @@ void shouldNotApplyingEffectWhenFightEnds() { fighter1.move(fight.map().get(166)); fighter2.move(fight.map().get(152)); - fighter2.life().alter(fighter2, 10 - fighter2.life().current()); + fighter2.life().heal(fighter2, 10 - fighter2.life().current()); castNormal(157, fight.map().get(152)); // épée céleste @@ -2057,6 +2096,48 @@ void addDiscernment() { assertEquals(6, fighter1.buffs().stream().filter(b -> b.effect().effect() == 176).findFirst().get().remainingTurns()); } + @Test + void erosionSimpleDamage() { + int lastLife = fighter2.life().current(); + int lastMax = fighter2.life().max(); + + fighter1.characteristics().alter(Characteristic.STRENGTH, 100); + + castNormal(183, fighter2.cell()); // Ronce + assertBetween(25, 42, lastLife - fighter2.life().current()); + assertBetween(2, 4, lastMax - fighter2.life().max()); + } + + @Test + void erosionIgnoreArmor() { + int lastLife = fighter2.life().current(); + int lastMax = fighter2.life().max(); + + fighter1.characteristics().alter(Characteristic.STRENGTH, 100); + fighter2.characteristics().alter(Characteristic.INTELLIGENCE, 1000); + fighter1.turn().stop(); + + castNormal(6, fighter2.cell()); // Armure terrestre + fighter2.turn().stop(); + + castNormal(183, fighter2.cell()); // Ronce + assertBetween(2, 4, lastLife - fighter2.life().max()); + assertBetween(2, 4, lastMax - fighter2.life().max()); + } + + @Test + void erosionCantKill() { + fighter1.characteristics().alter(Characteristic.STRENGTH, 10000); + fighter2.characteristics().alter(Characteristic.INTELLIGENCE, 1000000); + fighter1.turn().stop(); + + castNormal(6, fighter2.cell()); // Armure terrestre + fighter2.turn().stop(); + + castNormal(183, fighter2.cell()); // Ronce + assertEquals(1, fighter2.life().max()); + } + private List configureFight(Consumer configurator) { fight.cancel(true); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplierTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplierTest.java index 3d359e2fa..198e18106 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplierTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageApplierTest.java @@ -99,7 +99,7 @@ void applyWithDistance() { requestStack.assertLast(ActionEffect.alterLifePoints(caster, target, -10)); target.life().alterMax(target, 1000); - target.life().alter(target, 1000); + target.life().heal(target, 1000); assertEquals(-9, applier.apply(caster, effect, target, effectValue, 1)); assertEquals(-8, applier.apply(caster, effect, target, effectValue, 2)); @@ -258,6 +258,52 @@ public void onDamage(Buff buff, Damage value) { requestStack.assertOne(ActionEffect.reducedDamage(target, 7)); } + @Test + void applyWithReduceBuffShouldBeIgnoredForErosion() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + + Mockito.when(effect.min()).thenReturn(100); + + DamageApplier applier = new DamageApplier(Element.AIR, fight); + target.life().alterErosion(10); + + target.buffs().add( + new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), target, target, new BuffHook() { + @Override + public void onDamage(Buff buff, Damage value) { + value.reduce(80); + } + }) + ); + + int previousMaxLife = target.life().max(); + int value = applier.apply(caster, effect, target, EffectValue.create(effect, caster, target), 0); + + assertEquals(-20, value); + assertEquals(10, previousMaxLife - target.life().max()); + + requestStack.assertOne(ActionEffect.alterLifePoints(caster, target, -20)); + requestStack.assertOne(ActionEffect.reducedDamage(target, 80)); + } + + @Test + void applyWithErosion() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + + Mockito.when(effect.min()).thenReturn(30); + + DamageApplier applier = new DamageApplier(Element.AIR, fight); + target.life().alterErosion(10); + + int previousMaxLife = target.life().max(); + int value = applier.apply(caster, effect, target, EffectValue.create(effect, caster, target), 0); + + assertEquals(-30, value); + assertEquals(3, previousMaxLife - target.life().max()); + + requestStack.assertOne(ActionEffect.alterLifePoints(caster, target, -30)); + } + @Test void applyDirectDamageShouldCallBuffHook() { SpellEffect effect = Mockito.mock(SpellEffect.class); @@ -344,6 +390,24 @@ public void onDirectDamageApplied(Buff buff, Fighter caster, @Positive int damag assertFalse(appliedDamageHookCalled.get()); } + @Test + void applyBuffWithErosion() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + + Mockito.when(effect.min()).thenReturn(30); + target.life().alterErosion(10); + + int maxLife = target.life().max(); + + DamageApplier applier = new DamageApplier(Element.AIR, fight); + + Buff toApply = new Buff(effect, Mockito.mock(Spell.class), caster, target, Mockito.mock(BuffHook.class)); + int value = applier.apply(toApply); + + assertEquals(-30, value); + assertEquals(3, maxLife - target.life().max()); + } + @Test void applyWithCounterDamageCharacteristic() { target.characteristics().alter(Characteristic.COUNTER_DAMAGE, 5); @@ -510,7 +574,7 @@ void applyWithCounterDamageNoDamageShouldBeIgnored() { @Test void applyWithCounterDamageAndNegativeMultiplierShouldHealTarget() { target.characteristics().alter(Characteristic.COUNTER_DAMAGE, 5); - caster.life().alter(caster, -10); + caster.life().damage(caster, 10); SpellEffect effect = Mockito.mock(SpellEffect.class); Buff buff = new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), target, target, new BuffHook() { @@ -540,7 +604,7 @@ public void onReflectedDamage(Buff buff, ReflectedDamage damage) { @Test void applyWithNegativeMultiplierShouldHealTargetAndNotCallDirectDamageAppliedHook() { - target.life().alter(target, -20); + target.life().damage(target, 20); SpellEffect effect = Mockito.mock(SpellEffect.class); AtomicBoolean appliedDamageHookCalled = new AtomicBoolean(); @@ -617,6 +681,20 @@ public void onDirectDamageApplied(Buff buff, Fighter caster, @Positive int damag assertEquals(10, appliedDamageValue.get()); } + @Test + void applyFixedWithErosion() { + DamageApplier applier = new DamageApplier(Element.EARTH, fight); + + int maxLife = target.life().max(); + target.life().alterErosion(10); + + requestStack.clear(); + + assertEquals(-30, applier.applyFixed(caster, 30, target)); + assertEquals(30, maxLife - target.life().current()); + assertEquals(3, maxLife - target.life().max()); + } + @Test void applyFixedShouldIgnoreBoost() { caster.characteristics().alter(Characteristic.STRENGTH, 100); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageTest.java index b7b419988..8aeeeed8a 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/DamageTest.java @@ -36,47 +36,56 @@ void setUp() { @Test void defaultValue() { assertEquals(15, damage.value()); + assertEquals(15, damage.baseDamage()); } @Test void fixed() { assertEquals(10, damage.fixed(5).value()); + assertEquals(15, damage.baseDamage()); } @Test void fixedHigherThanValue() { assertEquals(0, damage.fixed(20).value()); + assertEquals(15, damage.baseDamage()); } @Test void percent() { assertEquals(12, damage.percent(20).value()); + assertEquals(15, damage.baseDamage()); } @Test void percentHigherThan100() { assertEquals(0, damage.percent(75).percent(30).value()); + assertEquals(15, damage.baseDamage()); } @Test void fixedAndPercent() { assertEquals(7, damage.percent(20).fixed(5).value()); + assertEquals(15, damage.baseDamage()); } @Test void multiplyPositive() { assertEquals(21, damage.percent(20).fixed(5).multiply(3).value()); + assertEquals(15, damage.baseDamage()); } @Test void multiplyNegative() { assertEquals(-7, damage.percent(20).fixed(5).multiply(-1).value()); + assertEquals(15, damage.baseDamage()); } @Test void reduce() { assertEquals(7, damage.percent(20).reduce(5).value()); assertEquals(5, damage.reducedDamage()); + assertEquals(15, damage.baseDamage()); } @Test @@ -96,5 +105,16 @@ void distance() { assertEquals(1, damage.distance(20).value()); assertEquals(1, damage.distance(25).value()); assertEquals(0, damage.distance(26).value()); + + assertEquals(10, damage.distance(3).baseDamage()); + } + + @Test + void allValues() { + // 15 * 0.81 (distance) = 12 + // 12 * 0.7 (percent) = 8 + // 8 - 4 - 2 (reduce) = 2 + assertEquals(2, damage.fixed(4).percent(30).reduce(2).distance(2).value()); + assertEquals(12, damage.baseDamage()); } } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandlerTest.java index 9d2d93ba1..e288a42f5 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/FixedStealLifeHandlerTest.java @@ -58,7 +58,7 @@ public void setUp() throws Exception { fight.nextState(); caster = player.fighter(); - caster.life().alter(caster, -50); + caster.life().damage(caster, 50); target = other.fighter(); target.move(fight.map().get(123)); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/IndirectPercentLifeDamageHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/IndirectPercentLifeDamageHandlerTest.java index b79b519e2..6533a9df7 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/IndirectPercentLifeDamageHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/IndirectPercentLifeDamageHandlerTest.java @@ -121,7 +121,7 @@ void applyFixedEffect() { @ParameterizedTest @MethodSource("provideLifeAndExpectedDamage") void applyShouldConsiderCurrentLife(int life, int expectedDamage) { - caster.life().alter(caster, life - caster.life().current()); + caster.life().heal(caster, life - caster.life().current()); SpellEffect effect = Mockito.mock(SpellEffect.class); Spell spell = Mockito.mock(Spell.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeDamageHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeDamageHandlerTest.java index 5dcca4270..292c4bf3f 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeDamageHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeDamageHandlerTest.java @@ -120,7 +120,7 @@ void applyFixedEffect() { @ParameterizedTest @MethodSource("provideLifeAndExpectedDamage") void applyShouldConsiderCurrentLife(int life, int expectedDamage) { - caster.life().alter(caster, life - caster.life().current()); + caster.life().heal(caster, life - caster.life().current()); SpellEffect effect = Mockito.mock(SpellEffect.class); Spell spell = Mockito.mock(Spell.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandlerTest.java index b8e5b31fe..475284a84 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PercentLifeLostDamageHandlerTest.java @@ -62,7 +62,7 @@ public void setUp() throws Exception { fight.nextState(); caster = player.fighter(); - caster.life().alter(caster, -200); + caster.life().damage(caster, 200); target = other.fighter(); target.move(fight.map().get(123)); @@ -120,7 +120,7 @@ void applyFixedEffect() { @ParameterizedTest @MethodSource("provideLifeAndExpectedDamage") void applyShouldConsiderCurrentLife(int life, int expectedDamage) { - caster.life().alter(caster, life - caster.life().current()); + caster.life().heal(caster, life - caster.life().current()); SpellEffect effect = Mockito.mock(SpellEffect.class); Spell spell = Mockito.mock(Spell.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandlerTest.java index ce59f7b80..7ff859e4c 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/PunishmentHandlerTest.java @@ -61,7 +61,7 @@ public void setUp() throws Exception { fight.nextState(); caster = player.fighter(); - caster.life().alter(caster, -150); + caster.life().damage(caster, 150); target = other.fighter(); target.move(fight.map().get(123)); @@ -119,7 +119,7 @@ void applyFixedEffect() { @ParameterizedTest @MethodSource("provideLifeAndExpectedDamage") void applyShouldConsiderCurrentLife(int life, int expectedDamage) { - caster.life().alter(caster, life - caster.life().current()); + caster.life().heal(caster, life - caster.life().current()); SpellEffect effect = Mockito.mock(SpellEffect.class); Spell spell = Mockito.mock(Spell.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandlerTest.java index cef06d6ce..e15b8b639 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/damage/StealLifeHandlerTest.java @@ -65,7 +65,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - caster.life().alter(caster, -50); + caster.life().damage(caster, 50); baseLife = caster.life().current(); handler = new StealLifeHandler(Element.AIR, fight); @@ -130,7 +130,7 @@ void applyOnFullLife() { Mockito.when(spell.constraints()).thenReturn(constraints); Mockito.when(constraints.freeCell()).thenReturn(false); - caster.life().alter(caster, 50); + caster.life().heal(caster, 50); requestStack.clear(); FightCastScope scope = makeCastScope(caster, spell, effect, target.cell()); @@ -166,7 +166,7 @@ void applyHealTooHigh() { Spell spell = Mockito.mock(Spell.class); SpellConstraints constraints = Mockito.mock(SpellConstraints.class); - caster.life().alter(caster, 45); + caster.life().heal(caster, 45); requestStack.clear(); Mockito.when(effect.min()).thenReturn(20); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandlerTest.java index c81f6ff65..90f5c7cdb 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/FixedHealHandlerTest.java @@ -61,7 +61,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - target.life().alter(target, -30); + target.life().damage(target, 30); lastTargetLife = target.life().current(); handler = new FixedHealHandler(); @@ -174,7 +174,7 @@ void applyWithAreaMultipleFighters() { Spell spell = Mockito.mock(Spell.class); SpellConstraints constraints = Mockito.mock(SpellConstraints.class); - caster.life().alter(caster, -30); + caster.life().damage(caster, 30); requestStack.clear(); Mockito.when(effect.min()).thenReturn(10); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandlerTest.java index 9cde65372..910d708ce 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/GivePercentLifeHandlerTest.java @@ -57,7 +57,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - target.life().alter(target, -30); + target.life().damage(target, 30); lastTargetLife = target.life().current(); handler = new GivePercentLifeHandler(); @@ -158,7 +158,7 @@ void applyShouldIgnoreSelfTarget() { Spell spell = Mockito.mock(Spell.class); SpellConstraints constraints = Mockito.mock(SpellConstraints.class); - caster.life().alter(caster, -30); + caster.life().damage(caster, 30); requestStack.clear(); Mockito.when(effect.min()).thenReturn(10); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandlerTest.java index 1e351c060..b159882ed 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealHandlerTest.java @@ -61,7 +61,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - target.life().alter(target, -30); + target.life().damage(target, 30); lastTargetLife = target.life().current(); handler = new HealHandler(fight); @@ -176,7 +176,7 @@ void applyWithAreaMultipleFighters() { Spell spell = Mockito.mock(Spell.class); SpellConstraints constraints = Mockito.mock(SpellConstraints.class); - caster.life().alter(caster, -30); + caster.life().damage(caster, 30); requestStack.clear(); Mockito.when(effect.min()).thenReturn(10); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandlerTest.java index b86adc035..9c4e8154c 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnAttackHandlerTest.java @@ -66,7 +66,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - caster.life().alter(caster, -30); + caster.life().damage(caster, 30); lastCasterLife = caster.life().current(); handler = new HealOnAttackHandler(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandlerTest.java index e3a07c792..122030e32 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/heal/HealOnDamageHandlerTest.java @@ -61,7 +61,7 @@ public void setUp() throws Exception { target = other.fighter(); target.move(fight.map().get(123)); - target.life().alter(target, -30); + target.life().damage(target, 30); lastTargetLife = target.life().current(); handler = new HealOnDamageHandler(); @@ -173,7 +173,7 @@ void onAlterLifeWithDamage() { FightCastScope scope = makeCastScope(caster, spell, effect, fight.map().get(122)); handler.buff(scope, scope.effects().get(0)); - target.life().alter(caster, -10); + target.life().damage(caster, 10); int heal = computeHeal() + 10; assertBetween(10, 15, heal); @@ -197,7 +197,7 @@ void onAlterLifeNotDamageShouldDoNothing() { FightCastScope scope = makeCastScope(caster, spell, effect, fight.map().get(122)); handler.buff(scope, scope.effects().get(0)); - target.life().alter(caster, 10); + target.life().heal(caster, 10); int heal = computeHeal() - 10; assertEquals(0, heal); @@ -221,7 +221,7 @@ void onAlterLifeShouldConsiderBoosts() { FightCastScope scope = makeCastScope(caster, spell, effect, fight.map().get(122)); handler.buff(scope, scope.effects().get(0)); - target.life().alter(caster, -10); + target.life().damage(caster, 10); int heal = computeHeal() + 10; assertEquals(17, heal); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/invocations/CreateDoubleHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/invocations/CreateDoubleHandlerTest.java index 46ab6fb90..92ec81946 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/invocations/CreateDoubleHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/invocations/CreateDoubleHandlerTest.java @@ -80,7 +80,7 @@ public void setUp() throws Exception { @Test void handle() { - caster.life().alter(caster, -50); + caster.life().damage(caster, 50); requestStack.clear(); SpellEffect effect = Mockito.mock(SpellEffect.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/ApplySpellOnStartTurnHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/ApplySpellOnStartTurnHandlerTest.java index 7c0996864..b7169dce5 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/ApplySpellOnStartTurnHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/ApplySpellOnStartTurnHandlerTest.java @@ -258,7 +258,7 @@ void stopTurnIfTargetDie() { Mockito.when(constraints.freeCell()).thenReturn(false); FightCastScope scope = makeCastScope(caster, spell, effect, target.cell()); - target.life().alter(target, -49); + target.life().damage(target, 49); handler.buff(scope, scope.effects().get(0)); requestStack.clear(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/KillHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/KillHandlerTest.java index e94dde082..075f3b628 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/KillHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/KillHandlerTest.java @@ -51,7 +51,7 @@ public void setUp() throws Exception { fight.nextState(); caster = player.fighter(); - caster.life().alter(caster, -150); + caster.life().damage(caster, 150); target = other.fighter(); target.move(fight.map().get(123)); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandlerTest.java index cd63f2d4a..18cd14827 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandlerTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandlerTest.java @@ -63,7 +63,7 @@ public void setUp() throws Exception { fight.nextState(); caster = player.fighter(); - caster.life().alter(caster, -150); + caster.life().damage(caster, 150); target = other.fighter(); target.move(fight.map().get(123)); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/ending/reward/drop/SynchronizeLifeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/ending/reward/drop/SynchronizeLifeTest.java index 880392572..fec47925a 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/ending/reward/drop/SynchronizeLifeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/ending/reward/drop/SynchronizeLifeTest.java @@ -48,7 +48,7 @@ public void setUp() throws Exception { @Test void applyOnPlayer() { - player.fighter().life().alter(player.fighter(), -100); + player.fighter().life().damage(player.fighter(), 100); DropReward reward = new DropReward(RewardType.WINNER, player.fighter(), Collections.emptyList()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLifeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLifeTest.java index 8fb10eec5..8570a397f 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLifeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/BaseFighterLifeTest.java @@ -111,13 +111,13 @@ void defaults() { } @Test - void alterOnDamage() { + void damage() { AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(-10, life.alter(caster, -10)); + assertEquals(10, life.damage(caster, 10)); assertEquals(90, life.current()); assertSame(caster, ref.get().caster()); @@ -126,13 +126,99 @@ void alterOnDamage() { } @Test - void alterOnDamageHigherThanCurrentLife() { + void damageWithErosion() { + life.alterErosion(10); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(10, life.damage(caster, 10)); + assertEquals(90, life.current()); + assertEquals(99, life.max()); + } + + @Test + void damageWithErosionShouldTakeBaseDamageInAccount() { + life.alterErosion(10); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(90, life.current()); + assertEquals(90, life.max()); + } + + @Test + void damageWithErosionHigherThanActualDamage() { + life.alterErosion(10); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(5, life.damage(caster, 5, 100)); + assertEquals(90, life.current()); + assertEquals(90, life.max()); + } + + @Test + void damageWithErosionCantKill() { + life.alterErosion(10); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(0, life.damage(caster, 0, 100000)); + assertEquals(1, life.current()); + assertEquals(1, life.max()); + } + + @Test + void erosionCappedTo100() { + life.alterErosion(1000); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(0, life.damage(caster, 0, 50)); + assertEquals(50, life.current()); + assertEquals(50, life.max()); + } + + @Test + void erosionCappedTo0() { + life.alterErosion(-1000); + + Fighter caster = Mockito.mock(Fighter.class); + + assertEquals(0, life.damage(caster, 0, 50)); + assertEquals(100, life.current()); + assertEquals(100, life.max()); + } + + @Test + void alterErosion() { + Fighter caster = Mockito.mock(Fighter.class); + + life.alterErosion(10); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(90, life.max()); + + life.alterErosion(5); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(75, life.max()); + + life.alterErosion(-10); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(70, life.max()); + } + + @Test + void damageHigherThanCurrentLife() { AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(-100, life.alter(caster, -150)); + assertEquals(100, life.damage(caster, 150)); assertEquals(0, life.current()); assertSame(caster, ref.get().caster()); @@ -141,15 +227,15 @@ void alterOnDamageHigherThanCurrentLife() { } @Test - void alterOnHeal() { - life.alter(fighter, -50); + void heal() { + life.damage(fighter, 50); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(10, life.alter(caster, 10)); + assertEquals(10, life.heal(caster, 10)); assertEquals(60, life.current()); assertSame(caster, ref.get().caster()); @@ -158,15 +244,15 @@ void alterOnHeal() { } @Test - void alterOnHealHigherThanMax() { - life.alter(fighter, -50); + void healHigherThanMax() { + life.damage(fighter, 50); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(50, life.alter(caster, 1000)); + assertEquals(50, life.heal(caster, 1000)); assertEquals(life.max(), life.current()); assertSame(caster, ref.get().caster()); @@ -178,15 +264,15 @@ void alterOnHealHigherThanMax() { * #56 : Dot not heal when dead */ @Test - void alterHealIfDead() { - life.alter(fighter, -1000); + void healIfDead() { + life.damage(fighter, 1000); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(0, life.alter(caster, 1000)); + assertEquals(0, life.heal(caster, 1000)); assertEquals(0, life.current()); assertTrue(life.dead()); assertNull(ref.get()); @@ -196,28 +282,28 @@ void alterHealIfDead() { * #56 : Dot not heal when dead */ @Test - void alterDamageIfDead() { - life.alter(fighter, -1000); + void damageIfDead() { + life.damage(fighter, 1000); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(0, life.alter(caster, -1000)); + assertEquals(0, life.damage(caster, 1000)); assertEquals(0, life.current()); assertTrue(life.dead()); assertNull(ref.get()); } @Test - void alterOnDie() { + void damageOnDie() { AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterDie.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - life.alter(caster, -1000); + life.damage(caster, 1000); assertEquals(0, life.current()); assertSame(caster, ref.get().caster()); @@ -226,25 +312,36 @@ void alterOnDie() { } @Test - void alterShouldCallOnLifeAlteredBuffs() { - life.alter(fighter, -50); + void healShouldCallOnLifeAlteredBuffs() { + life.damage(fighter, 50); BuffHook hook = Mockito.mock(BuffHook.class); Buff buff = new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), fighter, fighter, hook); fighter.buffs().add(buff); - life.alter(fighter, 10); + life.heal(fighter, 10); Mockito.verify(hook).onLifeAltered(buff, 10); } @Test - void alterShouldNotCallOnLifeAlteredBuffsWhenDie() { + void damageShouldCallOnLifeAlteredBuffs() { + BuffHook hook = Mockito.mock(BuffHook.class); + Buff buff = new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), fighter, fighter, hook); + fighter.buffs().add(buff); + + life.damage(fighter, 10); + + Mockito.verify(hook).onLifeAltered(buff, -10); + } + + @Test + void damageShouldNotCallOnLifeAlteredBuffsWhenDie() { BuffHook hook = Mockito.mock(BuffHook.class); Buff buff = new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), fighter, fighter, hook); fighter.buffs().add(buff); - life.alter(fighter, -1000); + life.damage(fighter, 1000); Mockito.verify(hook, Mockito.never()).onLifeAltered(Mockito.any(), Mockito.anyInt()); } @@ -266,7 +363,7 @@ void alterMaxPositive() { @Test void alterMaxNotFullLife() { - life.alter(fighter, -50); + life.damage(fighter, 50); Fighter caster = Mockito.mock(Fighter.class); life.alterMax(caster, 100); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/DoubleFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/DoubleFighterTest.java index f14d5ad22..f3b9f7e1c 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/DoubleFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/DoubleFighterTest.java @@ -97,7 +97,7 @@ void shouldNotInheritBuffs() { @Test void shouldCopyLife() { player.fighter().init(); - player.fighter().life().alter(player.fighter(), -50); + player.fighter().life().damage(player.fighter(), 50); assertEquals(245, player.fighter().life().current()); assertEquals(295, player.fighter().life().max()); @@ -106,11 +106,11 @@ void shouldCopyLife() { assertEquals(245, fighter.life().current()); assertEquals(295, fighter.life().max()); - player.fighter().life().alter(player.fighter(), -100); + player.fighter().life().damage(player.fighter(), 100); assertEquals(145, player.fighter().life().current()); assertEquals(245, fighter.life().current()); - fighter.life().alter(fighter, -50); + fighter.life().damage(fighter, 50); assertEquals(145, player.fighter().life().current()); assertEquals(195, fighter.life().current()); } @@ -190,7 +190,7 @@ void dead() throws Exception { fighter.init(); assertFalse(fighter.dead()); - fighter.life().alter(fighter, -10000); + fighter.life().damage(fighter, 10000); assertTrue(fighter.dead()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/InvocationFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/InvocationFighterTest.java index 48e4c6582..8a35a0d0e 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/InvocationFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/InvocationFighterTest.java @@ -155,7 +155,7 @@ void dead() throws Exception { fighter.init(); assertFalse(fighter.dead()); - fighter.life().alter(fighter, -10000); + fighter.life().damage(fighter, 10000); assertTrue(fighter.dead()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/StaticInvocationFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/StaticInvocationFighterTest.java index b4bbcc4d4..171f013ea 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/StaticInvocationFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/invocation/StaticInvocationFighterTest.java @@ -25,7 +25,6 @@ import fr.quatrevieux.araknemu.game.fight.FightBaseCase; import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.BuffList; import fr.quatrevieux.araknemu.game.fight.castable.spell.LaunchedSpells; -import fr.quatrevieux.araknemu.game.fight.exception.FightException; import fr.quatrevieux.araknemu.game.fight.fighter.EmptySpellList; import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; import fr.quatrevieux.araknemu.game.fight.fighter.States; @@ -159,7 +158,7 @@ void dead() throws Exception { fighter.init(); assertFalse(fighter.dead()); - fighter.life().alter(fighter, -10000); + fighter.life().damage(fighter, 10000); assertTrue(fighter.dead()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/monster/MonsterFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/monster/MonsterFighterTest.java index 3486f4b6c..07c21b6c5 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/monster/MonsterFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/monster/MonsterFighterTest.java @@ -39,7 +39,6 @@ import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterMoved; import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterVisible; import fr.quatrevieux.araknemu.game.fight.fighter.operation.FighterOperation; -import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import fr.quatrevieux.araknemu.game.fight.team.MonsterGroupTeam; import fr.quatrevieux.araknemu.game.fight.turn.FightTurn; @@ -50,7 +49,6 @@ import fr.quatrevieux.araknemu.game.monster.environment.RandomCellSelector; import fr.quatrevieux.araknemu.game.monster.group.MonsterGroup; import fr.quatrevieux.araknemu.game.monster.group.MonsterGroupFactory; -import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -190,7 +188,7 @@ void dead() throws Exception { fighter.init(); assertFalse(fighter.dead()); - fighter.life().alter(fighter, -10000); + fighter.life().damage(fighter, 10000); assertTrue(fighter.dead()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLifeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLifeTest.java index 3753f171f..46adff6f8 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLifeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterLifeTest.java @@ -75,10 +75,12 @@ void notInit() throws SQLException, ContainerException { assertEquals(100, life.current()); assertEquals(gamePlayer().properties().life().max(), life.max()); - assertThrows(IllegalStateException.class, () -> life.alter(fighter, -10)); + assertThrows(IllegalStateException.class, () -> life.damage(fighter, 10)); + assertThrows(IllegalStateException.class, () -> life.heal(fighter, 10)); assertThrows(IllegalStateException.class, () -> life.kill(fighter)); assertThrows(IllegalStateException.class, () -> life.alterMax(fighter, 10)); assertThrows(IllegalStateException.class, () -> life.resuscitate(fighter, 10)); + assertThrows(IllegalStateException.class, () -> life.alterErosion(10)); } @Test @@ -96,7 +98,7 @@ void initialized() throws SQLException, ContainerException { } @Test - void alterOnDamage() { + void damage() { player.properties().life().set(100); life.init(); @@ -105,7 +107,7 @@ void alterOnDamage() { Fighter caster = Mockito.mock(Fighter.class); - assertEquals(-10, life.alter(caster, -10)); + assertEquals(10, life.damage(caster, 10)); assertEquals(90, life.current()); assertSame(caster, ref.get().caster()); @@ -114,7 +116,7 @@ void alterOnDamage() { } @Test - void alterOnDamageHigherThanCurrentLife() { + void damageHigherThanCurrentLife() { player.properties().life().set(100); life.init(); @@ -123,7 +125,7 @@ void alterOnDamageHigherThanCurrentLife() { Fighter caster = Mockito.mock(Fighter.class); - assertEquals(-100, life.alter(caster, -150)); + assertEquals(100, life.damage(caster, 150)); assertEquals(0, life.current()); assertSame(caster, ref.get().caster()); @@ -132,7 +134,30 @@ void alterOnDamageHigherThanCurrentLife() { } @Test - void alterOnHeal() { + void alterErosion() { + player.properties().life().set(100); + life.init(); + + Fighter caster = Mockito.mock(Fighter.class); + + life.alterErosion(10); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(285, life.max()); + + life.alterErosion(5); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(270, life.max()); + + life.alterErosion(-10); + + assertEquals(0, life.damage(caster, 0, 100)); + assertEquals(265, life.max()); + } + + @Test + void heal() { player.properties().life().set(100); life.init(); @@ -141,7 +166,7 @@ void alterOnHeal() { Fighter caster = Mockito.mock(Fighter.class); - assertEquals(10, life.alter(caster, 10)); + assertEquals(10, life.heal(caster, 10)); assertEquals(110, life.current()); assertSame(caster, ref.get().caster()); @@ -150,7 +175,7 @@ void alterOnHeal() { } @Test - void alterOnHealHigherThanMax() { + void healHigherThanMax() { player.properties().life().set(100); life.init(); @@ -159,7 +184,7 @@ void alterOnHealHigherThanMax() { Fighter caster = Mockito.mock(Fighter.class); - assertEquals(195, life.alter(caster, 1000)); + assertEquals(195, life.heal(caster, 1000)); assertEquals(life.max(), life.current()); assertSame(caster, ref.get().caster()); @@ -171,16 +196,16 @@ void alterOnHealHigherThanMax() { * #56 : Dot not heal when dead */ @Test - void alterHealIfDead() { + void healIfDead() { life.init(); - life.alter(fighter, -1000); + life.damage(fighter, 1000); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(0, life.alter(caster, 1000)); + assertEquals(0, life.heal(caster, 1000)); assertEquals(0, life.current()); assertTrue(life.dead()); assertNull(ref.get()); @@ -190,23 +215,23 @@ void alterHealIfDead() { * #56 : Dot not heal when dead */ @Test - void alterDamageIfDead() { + void damageIfDead() { life.init(); - life.alter(fighter, -1000); + life.damage(fighter, 1000); AtomicReference ref = new AtomicReference<>(); fight.dispatcher().add(FighterLifeChanged.class, ref::set); Fighter caster = Mockito.mock(Fighter.class); - assertEquals(0, life.alter(caster, -1000)); + assertEquals(0, life.damage(caster, 1000)); assertEquals(0, life.current()); assertTrue(life.dead()); assertNull(ref.get()); } @Test - void alterOnDie() { + void damageOnDie() { player.properties().life().set(100); life.init(); @@ -215,7 +240,7 @@ void alterOnDie() { Fighter caster = Mockito.mock(Fighter.class); - life.alter(caster, -1000); + life.damage(caster, 1000); assertEquals(0, life.current()); assertSame(caster, ref.get().caster()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterTest.java index c2c6c0844..d058ea064 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/fighter/player/PlayerFighterTest.java @@ -265,7 +265,7 @@ void dead() { fighter.init(); assertFalse(fighter.dead()); - fighter.life().alter(fighter, -10000); + fighter.life().damage(fighter, 10000); assertTrue(fighter.dead()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModuleTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModuleTest.java new file mode 100644 index 000000000..7309f721c --- /dev/null +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/module/FighterInitializationModuleTest.java @@ -0,0 +1,41 @@ +/* + * This file is part of Araknemu. + * + * Araknemu is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Araknemu is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Araknemu. If not, see . + * + * Copyright (c) 2017-2024 Vincent Quatrevieux + */ + +package fr.quatrevieux.araknemu.game.fight.module; + +import fr.quatrevieux.araknemu.game.fight.Fight; +import fr.quatrevieux.araknemu.game.fight.FightBaseCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class FighterInitializationModuleTest extends FightBaseCase { + @Test + void shouldInitErosion() throws Exception { + Fight fight = createFight(); + + fight.register(new FighterInitializationModule(configuration.fight())); + fight.nextState(); + + int max = player.fighter().life().max(); + player.fighter().life().damage(player.fighter(), 100, 120); + + assertEquals(max - 12, player.fighter().life().max()); + } +} diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/state/ActiveStateTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/state/ActiveStateTest.java index f8fcc7936..b0eb15699 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/state/ActiveStateTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/state/ActiveStateTest.java @@ -56,6 +56,7 @@ import fr.quatrevieux.araknemu.game.listener.fight.turn.action.SendFightAction; import fr.quatrevieux.araknemu.game.listener.fight.turn.action.SendFightActionTerminated; import fr.quatrevieux.araknemu.game.player.GamePlayer; +import fr.quatrevieux.araknemu.network.game.out.account.Stats; import fr.quatrevieux.araknemu.network.game.out.fight.BeginFight; import fr.quatrevieux.araknemu.network.game.out.fight.action.ActionEffect; import fr.quatrevieux.araknemu.network.game.out.fight.turn.FighterTurnOrder; @@ -168,6 +169,7 @@ void start() throws InterruptedException { new BeginFight(), new FighterTurnOrder(fight.turnList()), new TurnMiddle(fight.fighters()), + new Stats(fighter.properties()), new StartTurn(fight.turnList().current().get()) ); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/state/FinishStateTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/state/FinishStateTest.java index 26c69fc06..fcf649f72 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/state/FinishStateTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/state/FinishStateTest.java @@ -32,12 +32,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -59,7 +56,7 @@ public void setUp() throws Exception { @Test void start() { - other.fighter().life().alter(player.fighter(), -1000); + other.fighter().life().damage(player.fighter(), 1000); Fighter winner = player.fighter(); Fighter looser = other.fighter(); @@ -95,7 +92,7 @@ void startShouldFilterInvocations() { fight.map().get(142) ); - other.fighter().life().alter(player.fighter(), -1000); + other.fighter().life().damage(player.fighter(), 1000); Fighter winner = player.fighter(); Fighter looser = other.fighter(); @@ -150,7 +147,7 @@ void startShouldSavePlayerInDatabase() throws Exception { fight = createPvmFight(); fight.nextState(); - player.fighter().life().alter(player.fighter(), -100); + player.fighter().life().damage(player.fighter(), 100); Collection monsters = fight.team(1).fighters(); @@ -216,7 +213,7 @@ void levelUpShouldRestoreLife() throws Exception { player.properties().experience().max() - player.properties().experience().current() - 1 ); - player.fighter().life().alter(player.fighter(), -100); + player.fighter().life().damage(player.fighter(), 100); Collection monsters = fight.team(1).fighters(); monsters.forEach(fighter -> fighter.life().kill(fighter)); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/team/SimpleTeamTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/team/SimpleTeamTest.java index 9a03999b5..3bed3a495 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/team/SimpleTeamTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/team/SimpleTeamTest.java @@ -96,7 +96,7 @@ void alive() throws Exception { Fight fight = createFight(); fighter.joinFight(fight, fight.map().get(123)); fighter.init(); - fighter.life().alter(fighter, -1000); + fighter.life().damage(fighter, 1000); assertFalse(team.alive()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnListTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnListTest.java index 26968c825..0233bb746 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnListTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnListTest.java @@ -155,7 +155,7 @@ void nextWillSkipDeadFighter() { fight.fighters().forEach(Fighter::init); turnList.start(); - other.fighter().life().alter(other.fighter(), -1000); + other.fighter().life().damage(other.fighter(), 1000); assertTrue(other.fighter().dead()); turnList.next(); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnTest.java index 613588029..3e62c10ef 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/turn/FightTurnTest.java @@ -27,7 +27,6 @@ import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; import fr.quatrevieux.araknemu.game.fight.map.BattlefieldObject; import fr.quatrevieux.araknemu.game.fight.map.FightCell; -import fr.quatrevieux.araknemu.game.fight.turn.action.Action; import fr.quatrevieux.araknemu.game.fight.turn.action.ActionResult; import fr.quatrevieux.araknemu.game.fight.turn.action.FightAction; import fr.quatrevieux.araknemu.game.fight.turn.event.TurnStarted; @@ -175,7 +174,7 @@ void performSuccess() { void performDead() { turn.start(); - turn.fighter().life().alter(turn.fighter(), -1000); + turn.fighter().life().damage(turn.fighter(), 1000); assertTrue(turn.fighter().dead()); FightAction action = Mockito.mock(FightAction.class); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/handler/fight/EndFighterTurnTest.java b/src/test/java/fr/quatrevieux/araknemu/game/handler/fight/EndFighterTurnTest.java index f385f920f..6f9581616 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/handler/fight/EndFighterTurnTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/handler/fight/EndFighterTurnTest.java @@ -25,6 +25,7 @@ import fr.quatrevieux.araknemu.game.fight.state.PlacementState; import fr.quatrevieux.araknemu.game.fight.turn.FightTurn; import fr.quatrevieux.araknemu.network.game.in.fight.TurnEnd; +import fr.quatrevieux.araknemu.network.game.out.account.Stats; import fr.quatrevieux.araknemu.network.game.out.fight.turn.FinishTurn; import fr.quatrevieux.araknemu.network.game.out.fight.turn.StartTurn; import fr.quatrevieux.araknemu.network.game.out.fight.turn.TurnMiddle; @@ -70,6 +71,7 @@ void success() { requestStack.assertAll( new FinishTurn(turn), new TurnMiddle(fight.fighters()), + new Stats(player.fighter().properties()), new StartTurn(other.fighter().turn()) ); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/CheckFightTerminatedTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/CheckFightTerminatedTest.java index 1172bb08a..1cceb64aa 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/CheckFightTerminatedTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/CheckFightTerminatedTest.java @@ -54,7 +54,7 @@ void onFighterDieAlreadyFighting() { @Test void onFighterDieWillTerminateFight() { - player.fighter().life().alter(player.fighter(), -1000); + player.fighter().life().damage(player.fighter(), 1000); listener.on(new FighterDie(player.fighter(), player.fighter())); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/fighter/RemoveDeadFighterTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/fighter/RemoveDeadFighterTest.java index 714d91f49..91f593e12 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/fighter/RemoveDeadFighterTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/fighter/RemoveDeadFighterTest.java @@ -64,7 +64,7 @@ void onFighterDieNotCurrentTurn() { void onFighterDieCurrentTurn() { FightCell cell = player.fighter().cell(); - player.fighter().life().alter(player.fighter(), -1000); + player.fighter().life().damage(player.fighter(), 1000); listener.on(new FighterDie(player.fighter(), player.fighter())); assertFalse(cell.hasFighter()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformationTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformationTest.java index 63dd8fd40..29476ad1b 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformationTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/SendFightersInformationTest.java @@ -22,6 +22,7 @@ import fr.quatrevieux.araknemu.game.fight.Fight; import fr.quatrevieux.araknemu.game.fight.FightBaseCase; import fr.quatrevieux.araknemu.game.fight.turn.event.NextTurnInitiated; +import fr.quatrevieux.araknemu.network.game.out.account.Stats; import fr.quatrevieux.araknemu.network.game.out.fight.turn.TurnMiddle; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,10 +42,12 @@ public void setUp() throws Exception { @Test void onNextTurnInitiated() { + requestStack.clear(); listener.on(new NextTurnInitiated()); - requestStack.assertLast( - new TurnMiddle(fight.fighters()) + requestStack.assertAll( + new TurnMiddle(fight.fighters()), + new Stats(player.fighter().properties()) ); } } \ No newline at end of file diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/action/SendFightActionTerminatedTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/action/SendFightActionTerminatedTest.java index c285f4c41..e5e12227e 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/action/SendFightActionTerminatedTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/fight/turn/action/SendFightActionTerminatedTest.java @@ -72,7 +72,7 @@ void onFightActionTerminated() { @Test void onFightActionTerminatedWithDeadFighter() { - player.fighter().life().alter(player.fighter(), -1000); + player.fighter().life().damage(player.fighter(), 1000); Move move = new Move( player.fighter(), diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/player/SendStatsTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/player/SendStatsTest.java index 0f1f58a69..ec2e28e4b 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/player/SendStatsTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/player/SendStatsTest.java @@ -56,7 +56,7 @@ void onCharacteristicsChangedWithFighter() throws Exception { PlayerFighter fighter = player.fighter(); fighter.init(); - fighter.life().alter(fighter, -100); + fighter.life().heal(fighter, -100); listener.on(new CharacteristicsChanged()); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/listener/player/spell/SendUpgradedSpellTest.java b/src/test/java/fr/quatrevieux/araknemu/game/listener/player/spell/SendUpgradedSpellTest.java index 5851146df..9cefdf4ce 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/listener/player/spell/SendUpgradedSpellTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/listener/player/spell/SendUpgradedSpellTest.java @@ -71,7 +71,7 @@ void onSpellUpgradedDuringFight() throws Exception { createFight(); PlayerFighter fighter = player.fighter(); fighter.init(); - fighter.life().alter(fighter, -100); + fighter.life().heal(fighter, -100); SpellBookEntry entry = new SpellBookEntry( null, diff --git a/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/FightEndTest.java b/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/FightEndTest.java index a75958fea..480aa8632 100644 --- a/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/FightEndTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/FightEndTest.java @@ -39,7 +39,7 @@ void generateWithoutRewards() throws Exception { Fight fight = createFight(); fight.nextState(); - other.fighter().life().alter(player.fighter(), -1000); + other.fighter().life().damage(player.fighter(), 1000); Fighter winner = player.fighter(); Fighter looser = other.fighter(); @@ -68,7 +68,7 @@ void generateWithReward() throws Exception { Fight fight = createFight(); fight.nextState(); - other.fighter().life().alter(player.fighter(), -1000); + other.fighter().life().damage(player.fighter(), 1000); Fighter winner = player.fighter(); Fighter looser = other.fighter(); diff --git a/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/turn/TurnMiddleTest.java b/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/turn/TurnMiddleTest.java index 88e16d20a..c1a95d546 100644 --- a/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/turn/TurnMiddleTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/network/game/out/fight/turn/TurnMiddleTest.java @@ -73,7 +73,7 @@ void generateWithActiveTurnShouldShowTurnPoints() { @Test void generateWithDeadFighter() { player.fighter().init(); - player.fighter().life().alter(player.fighter(), -1000); + player.fighter().life().damage(player.fighter(), 1000); assertEquals( "GTM|1;1|2;0;50;6;3;125;;50", From 85415be7fde0ed851627133e566cd32d6e80da9e Mon Sep 17 00:00:00 2001 From: Vincent Quatrevieux Date: Wed, 21 Feb 2024 18:20:53 +0100 Subject: [PATCH 2/2] feat(fight): Add increase erosion effect --- .../handler/misc/AddErosionHandler.java | 71 ++++++++ .../fight/module/CommonEffectsModule.java | 2 + .../fight/castable/effect/FunctionalTest.java | 15 ++ .../handler/misc/AddErosionHandlerTest.java | 172 ++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandler.java create mode 100644 src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandlerTest.java diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandler.java new file mode 100644 index 000000000..b86ff2db9 --- /dev/null +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandler.java @@ -0,0 +1,71 @@ +/* + * This file is part of Araknemu. + * + * Araknemu is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Araknemu is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Araknemu. If not, see . + * + * Copyright (c) 2017-2024 Vincent Quatrevieux + */ + +package fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc; + +import fr.quatrevieux.araknemu.game.fight.castable.BaseCastScope; +import fr.quatrevieux.araknemu.game.fight.castable.Castable; +import fr.quatrevieux.araknemu.game.fight.castable.FightCastScope; +import fr.quatrevieux.araknemu.game.fight.castable.effect.EffectValue; +import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buff; +import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.BuffEffect; +import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.BuffHook; +import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.EffectHandler; +import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; +import fr.quatrevieux.araknemu.game.fight.map.FightCell; +import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect; + +/** + * Buff effect for increase erosion rate of a target + * + * @see fr.quatrevieux.araknemu.game.fight.fighter.FighterLife#alterErosion(int) + */ +public final class AddErosionHandler implements EffectHandler, BuffHook { + @Override + public void handle(FightCastScope cast, BaseCastScope.EffectScope effect) { + throw new UnsupportedOperationException("Add erosion should be used as buff"); + } + + @Override + public void buff(FightCastScope cast, BaseCastScope.EffectScope effect) { + final SpellEffect spellEffect = effect.effect(); + final Castable action = cast.action(); + final Fighter caster = cast.caster(); + + EffectValue.forEachTargets(spellEffect, caster, cast.targets(), (target, effectValue) -> { + target.buffs().add(new Buff( + BuffEffect.fixed(spellEffect, effectValue.value()), + action, + caster, + target, + this + )); + }); + } + + @Override + public void onBuffStarted(Buff buff) { + buff.target().life().alterErosion(buff.effect().min()); + } + + @Override + public void onBuffTerminated(Buff buff) { + buff.target().life().alterErosion(-buff.effect().min()); + } +} diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/module/CommonEffectsModule.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/module/CommonEffectsModule.java index db703947f..9920f487c 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/module/CommonEffectsModule.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/module/CommonEffectsModule.java @@ -65,6 +65,7 @@ import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.heal.HealOnAttackHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.heal.HealOnDamageHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc.AddDiscernmentHandler; +import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc.AddErosionHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc.ChangeAppearanceHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc.DispelHandler; import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc.InvisibilityHandler; @@ -264,6 +265,7 @@ public void effects(EffectsHandler handler) { .register(126, Characteristic.INTELLIGENCE) .register(138, Characteristic.PERCENT_DAMAGE) ); + handler.register(776, new AddErosionHandler()); handler.register(666, new NoEffectHandler()); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java index 5cbe09eda..21617b1ac 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/FunctionalTest.java @@ -2138,6 +2138,21 @@ void erosionCantKill() { assertEquals(1, fighter2.life().max()); } + @Test + void addErosion() { + castNormal(433, fighter1.cell()); // châtiment +5% érosion + + int max = fighter1.life().max(); + fighter1.life().damage(fighter1, 100); + assertEquals(max - 15, fighter1.life().max()); + + passTurns(6); + + max = fighter1.life().max(); + fighter1.life().damage(fighter1, 100); + assertEquals(max - 10, fighter1.life().max()); + } + private List configureFight(Consumer configurator) { fight.cancel(true); diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandlerTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandlerTest.java new file mode 100644 index 000000000..3115c0182 --- /dev/null +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/AddErosionHandlerTest.java @@ -0,0 +1,172 @@ +/* + * This file is part of Araknemu. + * + * Araknemu is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Araknemu is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Araknemu. If not, see . + * + * Copyright (c) 2017-2024 Vincent Quatrevieux + */ + +package fr.quatrevieux.araknemu.game.fight.castable.effect.handler.misc; + +import fr.quatrevieux.araknemu.data.value.EffectArea; +import fr.quatrevieux.araknemu.game.fight.Fight; +import fr.quatrevieux.araknemu.game.fight.FightBaseCase; +import fr.quatrevieux.araknemu.game.fight.castable.FightCastScope; +import fr.quatrevieux.araknemu.game.fight.castable.effect.EffectValue; +import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buff; +import fr.quatrevieux.araknemu.game.fight.castable.effect.buff.BuffHook; +import fr.quatrevieux.araknemu.game.fight.fighter.player.PlayerFighter; +import fr.quatrevieux.araknemu.game.spell.Spell; +import fr.quatrevieux.araknemu.game.spell.SpellConstraints; +import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect; +import fr.quatrevieux.araknemu.game.spell.effect.area.CellArea; +import fr.quatrevieux.araknemu.game.spell.effect.area.CircleArea; +import fr.quatrevieux.araknemu.game.spell.effect.target.SpellEffectTarget; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +class AddErosionHandlerTest extends FightBaseCase { + private Fight fight; + private PlayerFighter caster; + private PlayerFighter target; + private AddErosionHandler handler; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + fight = createFight(); + fight.nextState(); + fight.turnList().start(); + + caster = player.fighter(); + target = other.fighter(); + + target.move(fight.map().get(123)); + + handler = new AddErosionHandler(); + + requestStack.clear(); + } + + @Test + void handle() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + Spell spell = Mockito.mock(Spell.class); + SpellConstraints constraints = Mockito.mock(SpellConstraints.class); + + Mockito.when(effect.area()).thenReturn(new CellArea()); + Mockito.when(effect.effect()).thenReturn(123); + Mockito.when(effect.min()).thenReturn(50); + Mockito.when(effect.max()).thenReturn(60); + Mockito.when(effect.target()).thenReturn(SpellEffectTarget.DEFAULT); + Mockito.when(spell.constraints()).thenReturn(constraints); + Mockito.when(constraints.freeCell()).thenReturn(false); + + FightCastScope scope = makeCastScope(caster, spell, effect, caster.cell()); + assertThrows(UnsupportedOperationException.class, () -> handler.handle(scope, scope.effects().get(0))); + } + + @Test + void buff() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + Spell spell = Mockito.mock(Spell.class); + SpellConstraints constraints = Mockito.mock(SpellConstraints.class); + + Mockito.when(effect.effect()).thenReturn(123); + Mockito.when(effect.min()).thenReturn(50); + Mockito.when(effect.max()).thenReturn(60); + Mockito.when(effect.duration()).thenReturn(5); + Mockito.when(effect.area()).thenReturn(new CircleArea(new EffectArea(EffectArea.Type.CIRCLE, 10))); + Mockito.when(effect.target()).thenReturn(SpellEffectTarget.DEFAULT); + Mockito.when(spell.constraints()).thenReturn(constraints); + Mockito.when(constraints.freeCell()).thenReturn(false); + + FightCastScope scope = makeCastScope(caster, spell, effect, caster.cell()); + handler.buff(scope, scope.effects().get(0)); + + Optional buff1 = caster.buffs().stream().filter(buff -> buff.effect().effect() == 123).findFirst(); + Optional buff2 = target.buffs().stream().filter(buff -> buff.effect().effect() == 123).findFirst(); + + assertTrue(buff1.isPresent()); + assertTrue(buff2.isPresent()); + + assertBetween(50, 60, buff1.get().effect().min()); + assertEquals(buff1.get().effect().min(), buff2.get().effect().min()); + assertTrue(buff1.get().canBeDispelled()); + assertTrue(buff2.get().canBeDispelled()); + } + + @Test + void buffWithOneTargetMaximized() { + target.buffs().add(new Buff(Mockito.mock(SpellEffect.class), Mockito.mock(Spell.class), target, target, new BuffHook() { + @Override + public void onEffectValueTarget(Buff buff, EffectValue value) { + value.maximize(); + } + })); + + SpellEffect effect = Mockito.mock(SpellEffect.class); + Spell spell = Mockito.mock(Spell.class); + SpellConstraints constraints = Mockito.mock(SpellConstraints.class); + + Mockito.when(effect.effect()).thenReturn(123); + Mockito.when(effect.min()).thenReturn(0); + Mockito.when(effect.max()).thenReturn(10000); + Mockito.when(effect.duration()).thenReturn(5); + Mockito.when(effect.area()).thenReturn(new CircleArea(new EffectArea(EffectArea.Type.CIRCLE, 10))); + Mockito.when(effect.target()).thenReturn(SpellEffectTarget.DEFAULT); + Mockito.when(spell.constraints()).thenReturn(constraints); + Mockito.when(constraints.freeCell()).thenReturn(false); + + FightCastScope scope = makeCastScope(target, spell, effect, caster.cell()); + handler.buff(scope, scope.effects().get(0)); + + Optional buff1 = caster.buffs().stream().filter(buff -> buff.effect().effect() == 123).findFirst(); + Optional buff2 = target.buffs().stream().filter(buff -> buff.effect().effect() == 123).findFirst(); + + assertTrue(buff1.isPresent()); + assertTrue(buff2.isPresent()); + + assertBetween(0, 9999, buff1.get().effect().min()); + assertEquals(10000, buff2.get().effect().min()); + } + + @Test + void onBuffStartedAndTerminated() { + SpellEffect effect = Mockito.mock(SpellEffect.class); + + Mockito.when(effect.effect()).thenReturn(123); + Mockito.when(effect.min()).thenReturn(50); + Mockito.when(effect.duration()).thenReturn(5); + + Buff buff = new Buff(effect, Mockito.mock(Spell.class), caster, target, handler); + + int maxLife = target.life().max(); + + handler.onBuffStarted(buff); + target.life().damage(target, 10); + assertEquals(5, maxLife - target.life().max()); + + handler.onBuffTerminated(buff); + target.life().damage(target, 10); + assertEquals(5, maxLife - target.life().max()); + } +}