From c949def0916645605294c7f111869839506e92c5 Mon Sep 17 00:00:00 2001 From: Vincent Quatrevieux Date: Fri, 12 Jan 2024 18:41:21 +0100 Subject: [PATCH] feat(fight): Order cell of effect area by distance from center --- .../game/spell/effect/area/CircularArea.java | 3 +- .../game/spell/effect/area/CrossArea.java | 3 +- .../game/spell/effect/area/LineArea.java | 4 +- .../effect/area/PerpendicularLineArea.java | 3 +- .../spell/effect/area/SpellEffectArea.java | 1 + .../araknemu/game/spell/effect/area/Util.java | 53 +++++++++++++++++++ .../spell/effect/area/CircleAreaTest.java | 25 ++++++++- .../game/spell/effect/area/CrossAreaTest.java | 29 ++++++++++ .../game/spell/effect/area/LineAreaTest.java | 20 +++++++ .../area/PerpendicularLineAreaTest.java | 19 +++++++ 10 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java index 28af9d3c8..8bc3f3554 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java @@ -25,7 +25,6 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; @@ -43,9 +42,9 @@ public CircularArea(EffectArea area, Predicate distanceChecker) { @Override public Set resolve(C target, C source) { - final Set cells = new HashSet<>(); final DofusMap map = target.map(); final CoordinateCell center = target.coordinate(); + final Set cells = Util.createOrderedSet(target); for (int i = 0; i < map.size(); ++i) { final CoordinateCell cell = map.get(i).coordinate(); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java index 6b04fcf9e..d8b15f462 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java @@ -24,7 +24,6 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.HashSet; import java.util.Set; /** @@ -39,7 +38,7 @@ public CrossArea(EffectArea area) { @Override public Set resolve(C target, C source) { - final Set cells = new HashSet<>(area.size() * 4 + 1); + final Set cells = Util.createOrderedSet(target); cells.add(target); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java index 5daa9c979..2ffb50411 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java @@ -27,7 +27,7 @@ import org.checkerframework.checker.index.qual.NonNegative; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -47,7 +47,7 @@ public Set resolve(C target, C source) { } final Direction direction = source.coordinate().directionTo(target.coordinate()); - final Set cells = new HashSet<>(area.size() + 1); + final Set cells = new LinkedHashSet<>(area.size() + 1); // Cell are added in order, so no need to sort using a TreeSet cells.add(target); addCells(cells, target, direction, area.size()); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java index 7b8b89d10..cbd15a190 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java @@ -24,7 +24,6 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.HashSet; import java.util.Set; /** @@ -39,7 +38,7 @@ public PerpendicularLineArea(EffectArea area) { @Override public Set resolve(C target, C source) { - final Set cells = new HashSet<>(area.size() * 2 + 1); + final Set cells = Util.createOrderedSet(target); final Direction direction = source.coordinate().directionTo(target).orthogonal(); cells.add(target); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java index 15aef5d84..8603a5b88 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java @@ -31,6 +31,7 @@ public interface SpellEffectArea { /** * Resolve the cells from an effect area + * Cells must be sorted by distance from the target cell, nearest cells first * * @param target The target cell * @param source The source cell (caster cell) diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java new file mode 100644 index 000000000..fa422d7ba --- /dev/null +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java @@ -0,0 +1,53 @@ +/* + * 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.spell.effect.area; + +import fr.arakne.utils.maps.CoordinateCell; +import fr.arakne.utils.maps.MapCell; + +import java.util.Set; +import java.util.TreeSet; + +/** + * Utility methods for resolving effect areas + */ +final class Util { + private Util() { + // Disable constructor + } + + /** + * Create a set with cells ordered by distance from target cell + */ + public static Set createOrderedSet(C target) { + final CoordinateCell center = target.coordinate(); + + return new TreeSet<>((o1, o2) -> { + final int firstDistance = center.distance(o1); + final int secondDistance = center.distance(o2); + + if (firstDistance == secondDistance) { + return o1.id() - o2.id(); + } + + return firstDistance - secondDistance; + }); + } +} diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java index ec5906086..42caebd66 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java @@ -22,12 +22,15 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import fr.quatrevieux.araknemu.data.world.repository.environment.MapTemplateRepository; import fr.quatrevieux.araknemu.game.GameBaseCase; +import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.Set; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class CircleAreaTest extends GameBaseCase { @@ -80,4 +83,24 @@ void resolve() { map.get(94) ); } -} \ No newline at end of file + + @Test + void resolveShouldSortByDistanceFromCenter() { + assertCellIds(map.get(152), 0, new int[] {152}); + assertCellIds(map.get(152), 1, new int[] {152, 137, 138, 166, 167}); + assertCellIds(map.get(152), 2, new int[] { + 152, + 137, 138, 166, 167, + 122, 123, 124, 151, 153, 180, 181, 182 + }); + } + + private void assertCellIds(FightCell center, int size, int[] cellIds) { + Set cells = new CircleArea(new EffectArea(EffectArea.Type.CIRCLE, size)).resolve(center, center); + + assertArrayEquals( + cellIds, + cells.stream().mapToInt(FightCell::id).toArray() + ); + } +} diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java index 4f360b726..72b3f9bec 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java @@ -22,12 +22,15 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import fr.quatrevieux.araknemu.data.world.repository.environment.MapTemplateRepository; import fr.quatrevieux.araknemu.game.GameBaseCase; +import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.Set; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class CrossAreaTest extends GameBaseCase { @@ -82,4 +85,30 @@ void resolve() { map.get(78) ); } + + @Test + void resolveShouldSortByDistanceFromCenter() { + assertCellIds(map.get(152), map.get(152), 0, new int[] {152}); + assertCellIds(map.get(152), map.get(152), 1, new int[] {152, 137, 138, 166, 167}); + assertCellIds(map.get(152), map.get(152), 2, new int[] { + 152, + 137, 138, 166, 167, + 122, 124, 180, 182 + }); + assertCellIds(map.get(152), map.get(152), 3, new int[] { + 152, + 137, 138, 166, 167, + 122, 124, 180, 182, + 107, 110, 194, 197, + }); + } + + private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { + Set cells = new CrossArea(new EffectArea(EffectArea.Type.CROSS, size)).resolve(center, from); + + assertArrayEquals( + cellIds, + cells.stream().mapToInt(FightCell::id).toArray() + ); + } } \ No newline at end of file diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java index dcc0362e9..ca06eb656 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java @@ -22,12 +22,15 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import fr.quatrevieux.araknemu.data.world.repository.environment.MapTemplateRepository; import fr.quatrevieux.araknemu.game.GameBaseCase; +import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.Set; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class LineAreaTest extends GameBaseCase { @@ -97,4 +100,21 @@ void resolveWithOutlineCell() { map.get(420) ); } + + @Test + void resolveShouldSortByDistanceFromCenter() { + assertCellIds(map.get(152), map.get(167), 0, new int[] {167}); + assertCellIds(map.get(152), map.get(167), 1, new int[] {167, 182}); + assertCellIds(map.get(152), map.get(167), 2, new int[] {167, 182, 197}); + assertCellIds(map.get(152), map.get(167), 3, new int[] {167, 182, 197, 212}); + } + + private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { + Set cells = new LineArea(new EffectArea(EffectArea.Type.LINE, size)).resolve(center, from); + + assertArrayEquals( + cellIds, + cells.stream().mapToInt(FightCell::id).toArray() + ); + } } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java index 104764bce..5df1c378e 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java @@ -22,12 +22,15 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import fr.quatrevieux.araknemu.data.world.repository.environment.MapTemplateRepository; import fr.quatrevieux.araknemu.game.GameBaseCase; +import fr.quatrevieux.araknemu.game.fight.map.FightCell; import fr.quatrevieux.araknemu.game.fight.map.FightMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.Set; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class PerpendicularLineAreaTest extends GameBaseCase { @@ -74,4 +77,20 @@ void resolve() { map.get(78) ); } + + @Test + void resolveShouldSortByDistanceFromCenter() { + assertCellIds(map.get(152), map.get(167), 0, new int[] {167}); + assertCellIds(map.get(152), map.get(167), 1, new int[] {167, 153, 181}); + assertCellIds(map.get(152), map.get(167), 2, new int[] {167, 153, 181, 139, 195}); + } + + private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { + Set cells = new PerpendicularLineArea(new EffectArea(EffectArea.Type.PERPENDICULAR_LINE, size)).resolve(center, from); + + assertArrayEquals( + cellIds, + cells.stream().mapToInt(FightCell::id).toArray() + ); + } }