Skip to content

Commit

Permalink
feat(fight): Apply attenuation of effect by distance from center of a…
Browse files Browse the repository at this point in the history
…rea effect
  • Loading branch information
vincent4vx committed Jan 27, 2024
1 parent 5b6e936 commit c8dff52
Show file tree
Hide file tree
Showing 23 changed files with 1,025 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

package fr.quatrevieux.araknemu.game.fight.castable;

import fr.arakne.utils.maps.CoordinateCell;
import fr.quatrevieux.araknemu.game.fight.fighter.FighterData;
import fr.quatrevieux.araknemu.game.fight.map.BattlefieldCell;
import fr.quatrevieux.araknemu.game.spell.Spell;
import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.qual.Pure;

Expand Down Expand Up @@ -179,9 +181,9 @@ public final List<EffectScope> effects() {

public final class EffectScope implements CastScope.EffectScope<F, C> {
private final SpellEffect effect;
private final Collection<F> targets;
private final CastTargets<F> targets;

public EffectScope(SpellEffect effect, Collection<F> targets) {
public EffectScope(SpellEffect effect, CastTargets<F> targets) {
this.effect = effect;
this.targets = targets;
}
Expand Down Expand Up @@ -235,5 +237,40 @@ public Collection<F> targets() {
public Set<C> cells() {
return effect.area().resolve(target, from);
}

/**
* Iterate over each target and distance from the center of the area effect
* Like {@link #targets()}, target mapping is resolved, and dead targets are ignored
*
* @param consumer The callback. Takes as first argument the target, and as second argument the distance. Return false to stop iteration
*/
public void forEachTargetAndDistance(TargetDistanceConsumer<F> consumer) {
final CoordinateCell<BattlefieldCell> baseCell = target.coordinate();

targets.forEach((target, cell) -> {
final F actualTarget = resolveTarget(target);

if (actualTarget == null || actualTarget.dead()) {
return true;
}

final int distance = baseCell.distance(cell);

return consumer.accept(actualTarget, distance);
});
}
}

@FunctionalInterface
public interface TargetDistanceConsumer<F extends FighterData> {
/**
* Handle a target
*
* @param target The target
* @param distance The distance from the center of the area effect
*
* @return true to continue the iteration, false to stop
*/
public boolean accept(F target, @NonNegative int distance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
import fr.quatrevieux.araknemu.game.spell.effect.SpellEffect;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
* Perform target resolution for a casted effect
*/
Expand All @@ -37,22 +33,6 @@ public final class CastTargetResolver {
*/
private CastTargetResolver() {}

/**
* Resolve targets of an effect
*
* @param caster The action caster
* @param target The target cell
* @param action Action to perform
* @param effect The effect to resolve
*
* @return List of fighters
*
* @see fr.quatrevieux.araknemu.game.spell.effect.target.EffectTarget
*/
public static <F extends FighterData> Collection<F> resolveFromEffect(F caster, BattlefieldCell target, Castable action, SpellEffect effect) {
return resolveFromEffect(caster, caster.cell(), target, action, effect);
}

/**
* Resolve targets of an effect
*
Expand All @@ -66,13 +46,13 @@ public static <F extends FighterData> Collection<F> resolveFromEffect(F caster,
*
* @see fr.quatrevieux.araknemu.game.spell.effect.target.EffectTarget
*/
public static <F extends FighterData> Collection<F> resolveFromEffect(F caster, BattlefieldCell from, BattlefieldCell target, Castable action, SpellEffect effect) {
public static <F extends FighterData> CastTargets<F> resolveFromEffect(F caster, BattlefieldCell from, BattlefieldCell target, Castable action, SpellEffect effect) {
if (effect.target().onlyCaster()) {
return Collections.singleton(caster);
return CastTargets.one(caster, target);
}

if (action.constraints().freeCell()) {
return Collections.emptyList();
return CastTargets.empty();
}

return resolveFromEffectArea(caster, from, target, effect);
Expand All @@ -82,10 +62,9 @@ public static <F extends FighterData> Collection<F> resolveFromEffect(F caster,
* Perform resolution from effect target and effect area
*/
@SuppressWarnings("cast.unsafe") // @Nullable cast cause a compiler crash on java 8
private static <F extends FighterData> Collection<F> resolveFromEffectArea(F caster, BattlefieldCell from, BattlefieldCell target, SpellEffect effect) {
// Use lazy instantiation and do not use stream API to optimise memory allocations
F firstTarget = null;
Collection<F> targets = null;
private static <F extends FighterData> CastTargets<F> resolveFromEffectArea(F caster, BattlefieldCell from, BattlefieldCell target, SpellEffect effect) {
// Do not use stream API to optimise memory allocations
final CastTargets.Builder<F> builder = new CastTargets.Builder<>();

for (BattlefieldCell cell : effect.area().resolve(target, from)) {
final @Nullable F resolvedTarget = (/*@Nullable*/ F) cell.fighter();
Expand All @@ -94,32 +73,9 @@ private static <F extends FighterData> Collection<F> resolveFromEffectArea(F cas
continue;
}

// Found the first target
if (firstTarget == null) {
firstTarget = resolvedTarget;
continue;
}

// Multiple targets are found : instantiate the collection
if (targets == null) {
targets = new ArrayList<>();
targets.add(firstTarget);
}

targets.add(resolvedTarget);
}

// There is multiple targets
if (targets != null) {
return targets;
}

// There is only one target : create a singleton
if (firstTarget != null) {
return Collections.singleton(firstTarget);
builder.add(resolvedTarget, cell);
}

// No targets are resolved
return Collections.emptyList();
return builder.build();
}
}
Loading

0 comments on commit c8dff52

Please sign in to comment.