diff --git a/src/main/java/nomadrealms/context/game/GameStateSerializer.java b/src/main/java/nomadrealms/context/game/GameStateSerializer.java index e39a34f2..9a9dd9dc 100644 --- a/src/main/java/nomadrealms/context/game/GameStateSerializer.java +++ b/src/main/java/nomadrealms/context/game/GameStateSerializer.java @@ -29,6 +29,7 @@ import nomadrealms.context.game.card.CardMemory; import nomadrealms.context.game.card.action.Action; import nomadrealms.context.game.card.action.scheduler.CardPlayerActionScheduler; +import nomadrealms.context.game.card.expression.CardExpression; import nomadrealms.context.game.event.DropItemEvent; import nomadrealms.context.game.event.InputEventFrame; import nomadrealms.context.game.event.ProcChain; @@ -158,6 +159,7 @@ private void registerClasses() { Reflections reflections = new Reflections(new ConfigurationBuilder().forPackage("nomadrealms")); List> superclasses = Arrays.asList( Event.class, + CardExpression.class, Actor.class, Structure.class, CardPlayerAI.class, diff --git a/src/main/java/nomadrealms/context/game/actor/cardplayer/CardPlayer.java b/src/main/java/nomadrealms/context/game/actor/cardplayer/CardPlayer.java index 101f369a..2779ef8a 100644 --- a/src/main/java/nomadrealms/context/game/actor/cardplayer/CardPlayer.java +++ b/src/main/java/nomadrealms/context/game/actor/cardplayer/CardPlayer.java @@ -9,6 +9,7 @@ import nomadrealms.context.game.actor.HasSpeech; import nomadrealms.context.game.actor.ai.CardPlayerAI; import nomadrealms.context.game.actor.cardplayer.appendage.Appendage; +import nomadrealms.context.game.event.Target; import nomadrealms.context.game.card.WorldCard; import nomadrealms.context.game.card.action.Action; import nomadrealms.context.game.card.action.scheduler.CardPlayerActionScheduler; @@ -22,7 +23,7 @@ import nomadrealms.render.RenderingEnvironment; import nomadrealms.render.ui.custom.speech.SpeechBubble; -public abstract class CardPlayer implements Actor, HasSpeech { +public abstract class CardPlayer implements Actor, HasSpeech, Target { private final CardPlayerActionScheduler actionScheduler = new CardPlayerActionScheduler(); diff --git a/src/main/java/nomadrealms/context/game/card/GameCard.java b/src/main/java/nomadrealms/context/game/card/GameCard.java index 6a7e3c1b..0d9504f2 100644 --- a/src/main/java/nomadrealms/context/game/card/GameCard.java +++ b/src/main/java/nomadrealms/context/game/card/GameCard.java @@ -9,6 +9,7 @@ import nomadrealms.context.game.card.expression.BuryAnySeedExpression; import nomadrealms.context.game.card.expression.CardExpression; import nomadrealms.context.game.card.expression.CreateStructureExpression; +import nomadrealms.context.game.card.expression.DamageActorsExpression; import nomadrealms.context.game.card.expression.DamageExpression; import nomadrealms.context.game.card.expression.EditTileExpression; import nomadrealms.context.game.card.expression.GatherExpression; @@ -18,9 +19,11 @@ import nomadrealms.context.game.card.expression.SurfaceCardExpression; import nomadrealms.context.game.card.expression.TeleportExpression; import nomadrealms.context.game.card.expression.TeleportNoTargetExpression; +import nomadrealms.context.game.card.query.actor.ActorsOnTilesQuery; import nomadrealms.context.game.card.query.actor.SelfQuery; import nomadrealms.context.game.card.query.card.LastResolvedCardQuery; import nomadrealms.context.game.card.query.tile.PreviousTileQuery; +import nomadrealms.context.game.card.query.tile.TilesInRadiusQuery; import nomadrealms.context.game.card.target.TargetingInfo; import nomadrealms.context.game.world.map.tile.factory.TileType; @@ -57,8 +60,6 @@ public enum GameCard implements Card { new TeleportExpression(10), new TargetingInfo(HEXAGON, 2)), REWIND( - //TODO: Rewind currently attempts to rewind itself since it is the last played card. Need to introduce an - // additional processing phase between playing a card and resolving its effects to avoid this. "Rewind", "teleport", "Teleport to the last hexagon you occupied. Surface the last card you played.", @@ -113,7 +114,13 @@ public enum GameCard implements Card { "overclocked_machinery", "Create a chest on target tile", new CreateStructureExpression(StructureType.CHEST), - new TargetingInfo(HEXAGON, 1)); + new TargetingInfo(HEXAGON, 1)), + FLAME_CIRCLE( + "Flame Circle", + "meteor", + "Deal 4 damage to all enemies within radius 3", + new DamageActorsExpression(new ActorsOnTilesQuery(new TilesInRadiusQuery(3), true), 4), + new TargetingInfo(NONE, 1)); private final String title; private final String artwork; diff --git a/src/main/java/nomadrealms/context/game/card/expression/DamageActorsExpression.java b/src/main/java/nomadrealms/context/game/card/expression/DamageActorsExpression.java new file mode 100644 index 00000000..10ed88c9 --- /dev/null +++ b/src/main/java/nomadrealms/context/game/card/expression/DamageActorsExpression.java @@ -0,0 +1,32 @@ +package nomadrealms.context.game.card.expression; + +import java.util.List; +import java.util.stream.Collectors; + +import nomadrealms.context.game.actor.Actor; +import nomadrealms.context.game.actor.cardplayer.CardPlayer; +import nomadrealms.context.game.card.effect.DamageEffect; +import nomadrealms.context.game.card.effect.Effect; +import nomadrealms.context.game.card.query.Query; +import nomadrealms.context.game.event.Target; +import nomadrealms.context.game.world.World; + +public class DamageActorsExpression implements CardExpression { + + private final Query actors; + private final int amount; + + public DamageActorsExpression(Query actors, int amount) { + this.actors = actors; + this.amount = amount; + } + + @Override + public List effects(World world, Target target, CardPlayer source) { + List result = actors.find(world, source); + return result.stream() + .map(actor -> new DamageEffect(actor, source, amount)) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/nomadrealms/context/game/card/query/actor/ActorsOnTilesQuery.java b/src/main/java/nomadrealms/context/game/card/query/actor/ActorsOnTilesQuery.java new file mode 100644 index 00000000..ddd43264 --- /dev/null +++ b/src/main/java/nomadrealms/context/game/card/query/actor/ActorsOnTilesQuery.java @@ -0,0 +1,42 @@ +package nomadrealms.context.game.card.query.actor; + +import static java.util.stream.Collectors.toList; + +import java.util.List; +import java.util.stream.Stream; + +import nomadrealms.context.game.actor.Actor; +import nomadrealms.context.game.actor.cardplayer.CardPlayer; +import nomadrealms.context.game.card.query.Query; +import nomadrealms.context.game.world.World; +import nomadrealms.context.game.world.map.area.Tile; + +public class ActorsOnTilesQuery implements Query { + + private final Query tileQuery; + private final boolean excludeSource; + + public ActorsOnTilesQuery(Query tileQuery, boolean excludeSource) { + this.tileQuery = tileQuery; + this.excludeSource = excludeSource; + } + + public ActorsOnTilesQuery(Query tileQuery) { + this(tileQuery, false); + } + + @Override + public List find(World world, CardPlayer source) { + List tiles = tileQuery.find(world, source); + // TODO: Tiles need to store the actors on them for efficiency. Once we have that, we can optimize this. + Stream stream = world.actors.stream() + .filter(CardPlayer.class::isInstance) + .map(CardPlayer.class::cast) + .filter(actor -> tiles.contains(actor.tile())); + if (excludeSource) { + stream = stream.filter(actor -> actor != source); + } + return stream.collect(toList()); + } + +} diff --git a/src/main/java/nomadrealms/context/game/card/query/tile/TilesInRadiusQuery.java b/src/main/java/nomadrealms/context/game/card/query/tile/TilesInRadiusQuery.java new file mode 100644 index 00000000..469c4a4d --- /dev/null +++ b/src/main/java/nomadrealms/context/game/card/query/tile/TilesInRadiusQuery.java @@ -0,0 +1,67 @@ +package nomadrealms.context.game.card.query.tile; + +import static java.util.Collections.emptyList; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import nomadrealms.context.game.actor.cardplayer.CardPlayer; +import nomadrealms.context.game.card.query.Query; +import nomadrealms.context.game.world.World; +import nomadrealms.context.game.world.map.area.Tile; + +public class TilesInRadiusQuery implements Query { + + private final int radius; + + public TilesInRadiusQuery(int radius) { + this.radius = radius; + } + + @Override + public List find(World world, CardPlayer source) { + Tile startTile = source.tile(); + if (startTile == null) { + return emptyList(); + } + + List tiles = new ArrayList<>(); + Queue queue = new LinkedList<>(); + Map distance = new HashMap<>(); + + queue.add(startTile); + distance.put(startTile, 0); + + while (!queue.isEmpty()) { + Tile currentTile = queue.poll(); + tiles.add(currentTile); + + int currentDistance = distance.get(currentTile); + if (currentDistance < radius) { + for (Tile neighbor : getNeighbors(world, currentTile)) { + if (neighbor != null && !distance.containsKey(neighbor)) { + distance.put(neighbor, currentDistance + 1); + queue.add(neighbor); + } + } + } + } + return tiles; + } + + private List getNeighbors(World world, Tile tile) { + List neighbors = new ArrayList<>(); + neighbors.add(tile.ul(world)); + neighbors.add(tile.um(world)); + neighbors.add(tile.ur(world)); + neighbors.add(tile.dl(world)); + neighbors.add(tile.dm(world)); + neighbors.add(tile.dr(world)); + return neighbors; + } + +} diff --git a/src/main/java/nomadrealms/context/game/zone/BeginnerDecks.java b/src/main/java/nomadrealms/context/game/zone/BeginnerDecks.java index 8c7afe5d..bde61c1b 100644 --- a/src/main/java/nomadrealms/context/game/zone/BeginnerDecks.java +++ b/src/main/java/nomadrealms/context/game/zone/BeginnerDecks.java @@ -2,6 +2,7 @@ import static nomadrealms.context.game.card.GameCard.ATTACK; import static nomadrealms.context.game.card.GameCard.CREATE_ROCK; +import static nomadrealms.context.game.card.GameCard.FLAME_CIRCLE; import static nomadrealms.context.game.card.GameCard.GATHER; import static nomadrealms.context.game.card.GameCard.HEAL; import static nomadrealms.context.game.card.GameCard.MELEE_ATTACK; @@ -23,7 +24,7 @@ public enum BeginnerDecks { REWIND )), PUNCH_AND_GRAPPLE("Punch & Grapple", new DeckList(HEAL, ATTACK, HEAL, MELEE_ATTACK)), - CYCLE_AND_SEARCH("Cycle & Search ", new DeckList()), + CYCLE_AND_SEARCH("Cycle & Search ", new DeckList(FLAME_CIRCLE)), AGRICULTURE_AND_LABOUR("Agriculture & Labour", new DeckList(GATHER, CREATE_ROCK, WOODEN_CHEST, TILL_SOIL, PLANT_SEED)); private final String name;