Skip to content

Commit

Permalink
Arakne#27 Add effect handler for punishment
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent4vx committed May 20, 2022
1 parent 901f29d commit addf2f3
Show file tree
Hide file tree
Showing 14 changed files with 1,140 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ public final class BuffEffect implements SpellEffect {

private final int effect;
private final @NonNegative int value;
private final @GTENegativeOne int duration;

private BuffEffect(SpellEffect baseEffect, int effect, @NonNegative int value) {
private BuffEffect(SpellEffect baseEffect, int effect, @NonNegative int value, @GTENegativeOne int duration) {
this.baseEffect = baseEffect;
this.effect = effect;
this.value = value;
this.duration = duration;
}

@Override
Expand All @@ -62,7 +64,7 @@ public int special() {

@Override
public @GTENegativeOne int duration() {
return baseEffect.duration();
return duration;
}

@Override
Expand Down Expand Up @@ -92,7 +94,7 @@ public EffectTarget target() {
* @param value The applied value
*/
public static BuffEffect fixed(SpellEffect baseEffect, @NonNegative int value) {
return new BuffEffect(baseEffect, baseEffect.effect(), value);
return new BuffEffect(baseEffect, baseEffect.effect(), value, baseEffect.duration());
}

/**
Expand All @@ -103,6 +105,17 @@ public static BuffEffect fixed(SpellEffect baseEffect, @NonNegative int value) {
* @param value The applied value
*/
public static BuffEffect withCustomEffect(SpellEffect baseEffect, int effect, @NonNegative int value) {
return new BuffEffect(baseEffect, effect, value);
return new BuffEffect(baseEffect, effect, value, baseEffect.duration());
}

/**
* Define an effect with custom effect id and duration, with a fixed value
*
* @param baseEffect The spell effect
* @param effect The overridden effect id
* @param value The overridden effect duration
*/
public static BuffEffect withCustomEffectAndDuration(SpellEffect baseEffect, int effect, @NonNegative int value, @GTENegativeOne int duration) {
return new BuffEffect(baseEffect, effect, value, duration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import fr.quatrevieux.araknemu.game.fight.fighter.ActiveFighter;
import fr.quatrevieux.araknemu.game.fight.fighter.PassiveFighter;
import fr.quatrevieux.araknemu.game.fight.turn.Turn;
import org.checkerframework.checker.index.qual.Positive;

/**
* Hook action for apply buff effects
Expand Down Expand Up @@ -123,6 +124,21 @@ public default void onDirectDamage(Buff buff, ActiveFighter caster, Damage value
onDamage(buff, value);
}

/**
* Direct damage has been applied to the current fighter
* This hook is called after {@link BuffHook#onDirectDamage(Buff, ActiveFighter, Damage)}
*
* Unlike {@link BuffHook#onLifeAltered(Buff, int)} this hook is only called by direct damage attack, so it ignores
* poison, push damage and some special effects.
*
* Note: this hook is not called if the attack has killed the fighter
*
* @param buff Active buff
* @param caster Attack caster
* @param damage Applied damage. Always positive. If an attack is inefficient, this hook will not be called.
*/
public default void onDirectDamageApplied(Buff buff, ActiveFighter caster, @Positive int damage) {}

/**
* The fighter will take damages indirectly (like poison)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import fr.quatrevieux.araknemu.game.fight.fighter.PassiveFighter;
import fr.quatrevieux.araknemu.game.fight.turn.Turn;
import fr.quatrevieux.araknemu.network.game.out.fight.AddBuff;
import fr.quatrevieux.araknemu.util.SafeLinkedList;
import org.checkerframework.checker.index.qual.Positive;

import java.util.Collection;
import java.util.Iterator;
Expand All @@ -41,7 +43,7 @@
*/
public final class BuffList implements Iterable<Buff>, Buffs {
private final Fighter fighter;
private final Collection<Buff> buffs = new LinkedList<>();
private final SafeLinkedList<Buff> buffs = new SafeLinkedList<>();

public BuffList(Fighter fighter) {
this.fighter = fighter;
Expand Down Expand Up @@ -126,6 +128,13 @@ public void onBuffDamage(Buff poison, Damage value) {
}
}

@Override
public void onDirectDamageApplied(ActiveFighter caster, @Positive int value) {
for (Buff buff : buffs) {
buff.hook().onDirectDamageApplied(buff, caster, value);
}
}

@Override
public void onLifeAltered(int value) {
for (Buff buff : buffs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import fr.quatrevieux.araknemu.game.fight.fighter.ActiveFighter;
import fr.quatrevieux.araknemu.game.fight.fighter.PassiveFighter;
import fr.quatrevieux.araknemu.game.fight.turn.Turn;
import org.checkerframework.checker.index.qual.Positive;

/**
* Handle and store buff list for a fighter
Expand Down Expand Up @@ -63,6 +64,11 @@ public interface Buffs extends Iterable<Buff> {
*/
public void onBuffDamage(Buff poison, Damage value);

/**
* @see BuffHook#onDirectDamageApplied(Buff, ActiveFighter, int)
*/
public void onDirectDamageApplied(ActiveFighter caster, @Positive int value);

/**
* @see BuffHook#onLifeAltered(Buff, int)
*/
Expand All @@ -79,7 +85,7 @@ public interface Buffs extends Iterable<Buff> {
public boolean onStartTurn();

/**
* @see BuffHook#onEndTurn(Buff)
* @see BuffHook#onEndTurn(Buff, Turn)
*/
public void onEndTurn(Turn turn);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (c) 2017-2022 Vincent Quatrevieux
*/

package fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic;

import fr.quatrevieux.araknemu.data.constant.Characteristic;
import fr.quatrevieux.araknemu.game.fight.Fight;
import fr.quatrevieux.araknemu.game.fight.castable.CastScope;
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.ActiveFighter;
import fr.quatrevieux.araknemu.game.fight.fighter.PassiveFighter;
import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.Positive;

import java.util.HashMap;
import java.util.Map;

/**
* Effect handler for punishment
*
* Add a characteristic boost when the fight suffer from direct damage
* Characteristics mapping must be added using {@link AddCharacteristicOnDamageHandler#register(int, Characteristic)} method
*
* Parameters:
* - first (i.e. min) is the effect used as characteristic boost
* - second (i.e. max) is the maximal boost value per turn
* - third (i.e. special) is the boost duration
*
* Note: this buff cannot be dispelled, but the boosted characteristics can
*/
public final class AddCharacteristicOnDamageHandler implements EffectHandler, BuffHook {
private final Fight fight;

/**
* Map first effect parameter to related characteristic hook
*/
private final Map<Integer, AlterCharacteristicHook> hooksMapping = new HashMap<>();

public AddCharacteristicOnDamageHandler(Fight fight) {
this.fight = fight;
}

@Override
public void handle(CastScope cast, CastScope.EffectScope effect) {
throw new UnsupportedOperationException("Alter characteristic effect must be used as a buff");
}

@Override
public void buff(CastScope cast, CastScope.EffectScope effect) {
final SpellEffect spellEffect = effect.effect();
final ActiveFighter caster = cast.caster();

for (PassiveFighter target : cast.targets()) {
target.buffs().add(new Buff(
spellEffect,
cast.action(),
caster,
target,
this,
false
));
}
}

/**
* Register an handled characteristic
*
* @param effectId Handled effect id. This is the first parameter of the spell effect
* @param hook Alter characteristic hook to use
*
* @return This instance
*/
public AddCharacteristicOnDamageHandler register(int effectId, AlterCharacteristicHook hook) {
hooksMapping.put(effectId, hook);

return this;
}

/**
* Register an handled characteristic
*
* @param effectId Handled effect id. This is the first parameter of the spell effect
* @param characteristic The mapped characteristic
*
* @return This instance
*/
public AddCharacteristicOnDamageHandler register(int effectId, Characteristic characteristic) {
return register(effectId, AlterCharacteristicHook.add(fight, characteristic));
}

@Override
public void onDirectDamageApplied(Buff buff, ActiveFighter caster, @Positive int damage) {
final PassiveFighter target = buff.target();
final int boostEffectId = buff.effect().min();
final AlterCharacteristicHook hook = hooksMapping.get(boostEffectId);

if (hook == null) {
throw new IllegalArgumentException("Unsupported effect " + boostEffectId + " for punishment effect");
}

final int maximalValue = buff.effect().max() - currentBoostValue(buff, caster);

if (maximalValue <= 0) {
return;
}

target.buffs().add(new Buff(
BuffEffect.withCustomEffectAndDuration(buff.effect(), boostEffectId, Math.min(maximalValue, damage), buff.effect().special()),
buff.action(),
caster,
target,
hook
));
}

private @NonNegative int currentBoostValue(Buff buff, ActiveFighter caster) {
// Add 1 to duration in case of self damage
final int expectedEffectDuration = buff.effect().special() + (caster.equals(buff.target()) ? 1 : 0);

int boost = 0;

for (Buff activeBuff : buff.target().buffs()) {
if (activeBuff.effect().effect() == buff.effect().min()
&& activeBuff.remainingTurns() == expectedEffectDuration
&& activeBuff.action().equals(buff.action())
) {
boost += activeBuff.effect().min();
}
}

return boost;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public DamageApplier(Element element, Fight fight) {
*
* @see DamageApplier#apply(Buff) For apply a buff damage (i.e. poison)
* @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamage(ActiveFighter, Damage) The called buff hook
* @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamageApplied(ActiveFighter, int) Buff called when damage are applied
*/
public int apply(ActiveFighter caster, SpellEffect effect, PassiveFighter target) {
final Damage damage = computeDamage(caster, effect, target);
Expand All @@ -82,6 +83,7 @@ public int apply(ActiveFighter caster, SpellEffect effect, PassiveFighter target
*
* @see DamageApplier#applyFixed(Buff, int) For apply a precomputed damage buff
* @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamage(ActiveFighter, Damage) The called buff hook
* @see fr.quatrevieux.araknemu.game.fight.castable.effect.buff.Buffs#onDirectDamageApplied(ActiveFighter, int) Buff called when damage are applied
*/
public int applyFixed(ActiveFighter caster, int value, PassiveFighter target) {
final Damage damage = createDamage(caster, target, value);
Expand Down Expand Up @@ -170,7 +172,7 @@ private Damage computeDamage(ActiveFighter caster, SpellEffect effect, PassiveFi
/**
* Apply damage to the target for direct damage
*
* This method will call direct damage buff and apply returned damage
* This method will call direct damage buffs and apply returned damage
*
* @return The life change value. Negative for damage, positive for heal.
*/
Expand All @@ -181,7 +183,13 @@ private int applyDirectDamage(ActiveFighter caster, Damage damage, PassiveFighte
damage.reflect(target.characteristics().get(Characteristic.COUNTER_DAMAGE));
}

return applyDamage(caster, damage, target);
final int actualDamage = applyDamage(caster, damage, target);

if (actualDamage < 0 && !target.dead()) {
target.buffs().onDirectDamageApplied(caster, -actualDamage);
}

return actualDamage;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.armor.SpellReturnHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddActionPointsHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddCharacteristicHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddCharacteristicOnDamageHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddMovementPointsHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddNotDispellableCharacteristicHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddVitalityHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AddVitalityNotDispellableHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.AlterVitalityHook;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.BoostCasterSightHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.DecreaseCasterSightHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.characteristic.RemoveActionPointsHandler;
Expand Down Expand Up @@ -241,6 +243,15 @@ public void effects(EffectsHandler handler) {
handler.register(290, new BoostSpellHandler(SpellsBoosts.Modifier.LAUNCH_PER_TURN));
handler.register(293, new BoostSpellHandler(SpellsBoosts.Modifier.BASE_DAMAGE));

handler.register(788, new AddCharacteristicOnDamageHandler(fight)
.register(108, AlterVitalityHook.add(fight))
.register(118, Characteristic.STRENGTH)
.register(119, Characteristic.AGILITY)
.register(123, Characteristic.LUCK)
.register(126, Characteristic.INTELLIGENCE)
.register(138, Characteristic.PERCENT_DAMAGE)
);

handler.register(666, new NoEffectHandler());
}

Expand Down
Loading

0 comments on commit addf2f3

Please sign in to comment.