forked from Arakne/Araknemu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fight): Handle double effect (Arakne#27)
- Loading branch information
1 parent
d558e0e
commit cb25345
Showing
37 changed files
with
1,780 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/fr/quatrevieux/araknemu/game/fight/FighterSprite.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* 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-2023 Vincent Quatrevieux | ||
*/ | ||
|
||
package fr.quatrevieux.araknemu.game.fight; | ||
|
||
import fr.quatrevieux.araknemu.game.fight.fighter.Fighter; | ||
import fr.quatrevieux.araknemu.game.world.creature.Sprite; | ||
|
||
/** | ||
* Extends the base sprite for fighters, allowing temporary alterations | ||
*/ | ||
public interface FighterSprite extends Sprite { | ||
/** | ||
* Duplicate the sprite with the given fighter | ||
* The returned sprite should be same as current one, but with new fighter cell, orientation, and characteristics. | ||
* | ||
* @param fighter The new fighter | ||
* | ||
* @return The new sprite instance | ||
*/ | ||
public FighterSprite withFighter(Fighter fighter); | ||
} |
73 changes: 73 additions & 0 deletions
73
src/main/java/fr/quatrevieux/araknemu/game/fight/ai/action/BlockNearestEnemy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* 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-2023 Vincent Quatrevieux | ||
*/ | ||
|
||
package fr.quatrevieux.araknemu.game.fight.ai.action; | ||
|
||
import fr.arakne.utils.maps.CoordinateCell; | ||
import fr.quatrevieux.araknemu.game.fight.ai.AI; | ||
import fr.quatrevieux.araknemu.game.fight.fighter.ActiveFighter; | ||
import fr.quatrevieux.araknemu.game.fight.fighter.FighterData; | ||
import fr.quatrevieux.araknemu.game.fight.map.BattlefieldCell; | ||
import fr.quatrevieux.araknemu.game.fight.turn.action.Action; | ||
|
||
import java.util.Comparator; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Try to block the nearest enemy of the invoker | ||
* If the invoker is hidden, this generator will act like {@link MoveNearEnemy} | ||
* | ||
* @param <F> the fighter type | ||
*/ | ||
public final class BlockNearestEnemy<F extends ActiveFighter> implements ActionGenerator<F> { | ||
private final ActionGenerator<F> moveGenerator; | ||
|
||
@SuppressWarnings("methodref.receiver.bound") | ||
public BlockNearestEnemy() { | ||
moveGenerator = new MoveNearFighter<>(this::resolve); | ||
} | ||
|
||
@Override | ||
public void initialize(AI<F> ai) { | ||
moveGenerator.initialize(ai); | ||
} | ||
|
||
@Override | ||
public Optional<Action> generate(AI<F> ai, AiActionFactory actions) { | ||
return moveGenerator.generate(ai, actions); | ||
} | ||
|
||
private Optional<? extends FighterData> resolve(AI<F> ai) { | ||
final FighterData invoker = ai.fighter().invoker(); | ||
|
||
if (invoker == null || invoker.hidden()) { | ||
return ai.enemy(); | ||
} | ||
|
||
final CoordinateCell<BattlefieldCell> currentCell = invoker.cell().coordinate(); | ||
|
||
return ai.helper().enemies().stream() | ||
.filter(fighter -> !fighter.hidden()) | ||
.min(Comparator | ||
.<FighterData>comparingInt(f -> currentCell.distance(f.cell())) | ||
.thenComparingInt(f -> f.life().current()) | ||
) | ||
; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
src/main/java/fr/quatrevieux/araknemu/game/fight/ai/action/MoveNearFighter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* 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-2023 Vincent Quatrevieux | ||
*/ | ||
|
||
package fr.quatrevieux.araknemu.game.fight.ai.action; | ||
|
||
import fr.arakne.utils.maps.path.Pathfinder; | ||
import fr.quatrevieux.araknemu.game.fight.ai.AI; | ||
import fr.quatrevieux.araknemu.game.fight.ai.util.AIHelper; | ||
import fr.quatrevieux.araknemu.game.fight.fighter.ActiveFighter; | ||
import fr.quatrevieux.araknemu.game.fight.fighter.FighterData; | ||
import fr.quatrevieux.araknemu.game.fight.map.BattlefieldCell; | ||
import fr.quatrevieux.araknemu.game.fight.turn.action.Action; | ||
import fr.quatrevieux.araknemu.util.Asserter; | ||
import org.checkerframework.checker.index.qual.Positive; | ||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; | ||
import org.checkerframework.checker.nullness.util.NullnessUtil; | ||
|
||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* Try to move near the selected fighter | ||
*/ | ||
public final class MoveNearFighter<F extends ActiveFighter> implements ActionGenerator<F> { | ||
private @MonotonicNonNull Pathfinder<BattlefieldCell> pathfinder; | ||
private @MonotonicNonNull AIHelper helper; | ||
private final Function<AI<F>, Optional<? extends FighterData>> fighterResolver; | ||
|
||
public MoveNearFighter(Function<AI<F>, Optional<? extends FighterData>> fighterResolver) { | ||
this.fighterResolver = fighterResolver; | ||
} | ||
|
||
@Override | ||
public void initialize(AI<F> ai) { | ||
this.helper = ai.helper(); | ||
this.pathfinder = helper.cells().pathfinder() | ||
.targetDistance(1) | ||
.walkablePredicate(cell -> true) // Fix #94 Ignore inaccessible cell (handled by cell cost) | ||
.cellWeightFunction(this::cellCost) | ||
; | ||
} | ||
|
||
@Override | ||
public Optional<Action> generate(AI<F> ai, AiActionFactory actions) { | ||
if (helper == null || !helper.canMove()) { | ||
return Optional.empty(); | ||
} | ||
|
||
final int movementPoints = helper.movementPoints(); | ||
final BattlefieldCell currentCell = ai.fighter().cell(); | ||
|
||
return fighterResolver.apply(ai) | ||
.map(enemy -> NullnessUtil.castNonNull(pathfinder).findPath(currentCell, enemy.cell()).truncate(movementPoints + 1)) | ||
.map(path -> path.keepWhile(step -> step.cell().equals(currentCell) || step.cell().walkable())) // Truncate path to first unwalkable cell (may occur if the enemy cell is inaccessible or if other fighters block the path) | ||
.filter(path -> path.size() > 1) | ||
.map(path -> actions.move(path)) | ||
; | ||
} | ||
|
||
/** | ||
* Compute the cell cost for optimize the path finding | ||
*/ | ||
private @Positive int cellCost(BattlefieldCell cell) { | ||
// Fix #94 : Some cells are not accessible, but walkable/targetable using teleport. | ||
// In this case the pathfinder will fail, so instead of ignoring unwalkable cells, simply set a very high cost, | ||
// which allows the AI to generate a path to an inaccessible cell without throws a PathException | ||
if (!cell.walkableIgnoreFighter()) { | ||
return 1000; | ||
} | ||
|
||
// A fighter is on the cell : the cell is not walkable | ||
// But the fighter may leave the place at the next turn | ||
// The cost is higher than a simple detour, but permit to resolve a path blocked by a fighter | ||
if (cell.hasFighter()) { | ||
return 15; | ||
} | ||
|
||
// Add a cost of 3 for each enemy around the cell | ||
// This cost corresponds to the detour cost + 1 | ||
return 1 + Asserter.castNonNegative(3 * (int) NullnessUtil.castNonNull(helper).enemies().adjacent(cell).count()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.