From 0770da7e76cce7943adbda7be65a5c50694c6027 Mon Sep 17 00:00:00 2001 From: pacien Date: Sun, 4 Feb 2018 12:22:53 +0100 Subject: Refactor updateables and event propagation Signed-off-by: pacien --- .../java/fr/umlv/java/wallj/block/BombBlock.java | 36 ++++++----- .../fr/umlv/java/wallj/block/GarbageBlock.java | 26 ++++---- .../java/fr/umlv/java/wallj/block/RobotBlock.java | 73 ++++++++++++---------- .../java/fr/umlv/java/wallj/block/TrashBlock.java | 16 ++--- .../java/fr/umlv/java/wallj/block/WallBlock.java | 18 +++--- src/main/java/fr/umlv/java/wallj/context/Game.java | 12 +++- .../java/fr/umlv/java/wallj/context/Stage.java | 35 ++++++----- .../fr/umlv/java/wallj/context/Updateable.java | 20 ++---- .../fr/umlv/java/wallj/context/Updateables.java | 40 ++++++++++++ .../java/fr/umlv/java/wallj/viewer/Viewer.java | 3 +- 10 files changed, 166 insertions(+), 113 deletions(-) create mode 100644 src/main/java/fr/umlv/java/wallj/context/Updateables.java diff --git a/src/main/java/fr/umlv/java/wallj/block/BombBlock.java b/src/main/java/fr/umlv/java/wallj/block/BombBlock.java index ac63a87..4f310eb 100644 --- a/src/main/java/fr/umlv/java/wallj/block/BombBlock.java +++ b/src/main/java/fr/umlv/java/wallj/block/BombBlock.java @@ -3,6 +3,7 @@ package fr.umlv.java.wallj.block; import fr.umlv.java.wallj.board.TileVec2; import fr.umlv.java.wallj.context.Context; import fr.umlv.java.wallj.context.GraphicsContext; +import fr.umlv.java.wallj.context.Updateables; import fr.umlv.java.wallj.event.*; import fr.umlv.java.wallj.event.Event; import org.jbox2d.common.Vec2; @@ -10,10 +11,8 @@ import org.jbox2d.dynamics.BodyType; import java.awt.*; import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Objects; +import java.util.stream.Stream; /** * A bomb block. @@ -42,32 +41,35 @@ public class BombBlock extends JBoxBlock { } @Override - public List update(Context context) { - handleBombConfiguration(context.getEvents()); - paint(context.getGraphicsContext()); - return consume(context.getTimeDelta()); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::handleBombConfiguration, + this::consume, + this::paint); } - private void handleBombConfiguration(List events) { - Events.findFirst(events, BombTimerIncrEvent.class) + private Stream handleBombConfiguration(Context context) { + Events.findFirst(context.getEvents(), BombTimerIncrEvent.class) .filter(event -> Objects.equals(event.getPos(), TileVec2.of(getPos()))) .ifPresent(event -> incrementTimer()); + return Stream.empty(); } - private List consume(Duration timeDelta) { - decrementTimer(timeDelta); - - if (timer.isNegative()) - return Arrays.asList(new BombExplosionEvent(TileVec2.of(getPos())), new BlockDestroyEvent(this)); - else - return Collections.emptyList(); + private Stream consume(Context context) { + decrementTimer(context.getTimeDelta()); + return timer.isNegative() ? + Stream.of(new BombExplosionEvent(TileVec2.of(getPos())), new BlockDestroyEvent(this)) : + Stream.empty(); } - private void paint(GraphicsContext graphicsContext) { + private Stream paint(Context context) { + GraphicsContext graphicsContext = context.getGraphicsContext(); graphicsContext.paintCircle(Color.BLACK, getPos(), TileVec2.TILE_DIM); Vec2 textPosition = getPos(); textPosition.x += TileVec2.TILE_DIM / 4.0f; textPosition.y += 3 * TileVec2.TILE_DIM / 4.0f; graphicsContext.paintString(Color.RED, textPosition, Long.toString(timer.getSeconds())); + + return Stream.empty(); } } diff --git a/src/main/java/fr/umlv/java/wallj/block/GarbageBlock.java b/src/main/java/fr/umlv/java/wallj/block/GarbageBlock.java index 9338888..362e680 100644 --- a/src/main/java/fr/umlv/java/wallj/block/GarbageBlock.java +++ b/src/main/java/fr/umlv/java/wallj/block/GarbageBlock.java @@ -2,19 +2,17 @@ package fr.umlv.java.wallj.block; import fr.umlv.java.wallj.board.TileVec2; import fr.umlv.java.wallj.context.Context; -import fr.umlv.java.wallj.context.GraphicsContext; +import fr.umlv.java.wallj.context.Updateables; import fr.umlv.java.wallj.event.BombExplosionEvent; import fr.umlv.java.wallj.event.Event; import fr.umlv.java.wallj.event.Events; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.BodyType; import org.jbox2d.dynamics.Fixture; -import org.jbox2d.dynamics.World; import java.awt.*; -import java.util.Collections; -import java.util.List; import java.util.Objects; +import java.util.stream.Stream; /** * A garbage block. @@ -31,24 +29,26 @@ public class GarbageBlock extends JBoxBlock { } @Override - public List update(Context context) { - handleExplosionBlasts(context.getEvents(), context.getGame().getCurrentStage().getWorld()); - paint(context.getGraphicsContext()); - return Collections.emptyList(); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::handleExplosionBlasts, + this::paint); } - private void handleExplosionBlasts(List events, World world) { - Events.filter(events, BombExplosionEvent.class).forEach(explosion -> { + private Stream handleExplosionBlasts(Context context) { + Events.filter(context.getEvents(), BombExplosionEvent.class).forEach(explosion -> { Vec2 source = explosion.getSource().toVec2(); - world.raycast((fixture, point, normal, fraction) -> { + context.getGame().getCurrentStage().getWorld().raycast((fixture, point, normal, fraction) -> { if (isSelf(fixture)) getBody().applyForceToCenter(computeBlastForce(source)); return STOP_RAYCAST; }, source, getPos()); }); + return Stream.empty(); } - private void paint(GraphicsContext graphicsContext) { - graphicsContext.paintCircle(COLOR, getPos(), TileVec2.TILE_DIM); + private Stream paint(Context context) { + context.getGraphicsContext().paintCircle(COLOR, getPos(), TileVec2.TILE_DIM); + return Stream.empty(); } private boolean isSelf(Fixture fixture) { diff --git a/src/main/java/fr/umlv/java/wallj/block/RobotBlock.java b/src/main/java/fr/umlv/java/wallj/block/RobotBlock.java index f81f423..11cf25f 100644 --- a/src/main/java/fr/umlv/java/wallj/block/RobotBlock.java +++ b/src/main/java/fr/umlv/java/wallj/block/RobotBlock.java @@ -4,17 +4,16 @@ import fr.umlv.java.wallj.board.Board; import fr.umlv.java.wallj.board.PathFinder; import fr.umlv.java.wallj.board.TileVec2; import fr.umlv.java.wallj.context.Context; -import fr.umlv.java.wallj.context.GraphicsContext; import fr.umlv.java.wallj.context.Stage; +import fr.umlv.java.wallj.context.Updateables; import fr.umlv.java.wallj.event.*; import fr.umlv.java.wallj.event.Event; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.World; import java.awt.*; -import java.time.Duration; import java.util.*; -import java.util.List; +import java.util.stream.Stream; /** * A robot block. @@ -50,47 +49,55 @@ public class RobotBlock extends Block { } @Override - public List update(Context context) { - Events.findFirst(context.getEvents(), MoveRobotOrder.class) - .ifPresent(event -> updatePath(context.getGame().getCurrentStage().getBoard(), event.getTarget())); - - move(context.getTimeDelta()); - paint(context.getGraphicsContext()); - return setupBomb(context.getEvents(), context.getGame().getCurrentStage()); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::updatePath, + this::move, + this::paint, + this::setupBomb); } - private List setupBomb(List events, Stage stage) { - return Events.findFirst(events, BombSetupOrder.class) - .map(event -> isOnBomb(stage) ? - Collections.singletonList(new BombTimerIncrEvent(getTile())) : - dropBomb(event)) - .orElse(Collections.emptyList()); + private Stream setupBomb(Context context) { + return Events.findFirst(context.getEvents(), BombSetupOrder.class) + .flatMap(event -> isOnBomb(context.getGame().getCurrentStage()) ? + Optional.of(new BombTimerIncrEvent(getTile())) : + dropBomb()) + .map(Stream::of) // Optional.stream() only available in Java 9 + .orElseGet(Stream::empty); } - private List dropBomb(BombSetupOrder order) { - if (droppedBombCount >= Stage.BOMB_PLACEMENTS) return Collections.emptyList(); - + private Optional dropBomb() { + if (droppedBombCount >= Stage.BOMB_PLACEMENTS) return Optional.empty(); droppedBombCount++; - return Collections.singletonList(new BlockCreateEvent(BlockType.BOMB, getTile())); + return Optional.of(new BlockCreateEvent(BlockType.BOMB, getTile())); } - private void updatePath(Board board, TileVec2 target) { - if (!board.inside(target) || !board.getBlockTypeAt(target).isTraversable()) return; - if (pathFinder == null) pathFinder = new PathFinder(board); - path = new LinkedList<>(pathFinder.findPath(TileVec2.of(pos), target)); + private Stream updatePath(Context context) { + Events.findFirst(context.getEvents(), MoveRobotOrder.class) + .ifPresent(event -> { + Board board = context.getGame().getCurrentStage().getBoard(); + TileVec2 target = event.getTarget(); + if (!board.inside(target) || !board.getBlockTypeAt(target).isTraversable()) return; + if (pathFinder == null) pathFinder = new PathFinder(board); + path = new LinkedList<>(pathFinder.findPath(TileVec2.of(pos), target)); + }); + return Stream.empty(); } - private void move(Duration timeDelta) { - if (path.isEmpty()) return; - Vec2 dest = path.getFirst().toVec2(); - Vec2 dir = dest.sub(pos); - float dist = dir.normalize(); - Vec2 dp = dir.mul(timeDelta.toMillis() * SPEED); - pos = dp.length() < dist ? pos.add(dp) : path.removeFirst().toVec2(); + private Stream move(Context context) { + if (!path.isEmpty()) { + Vec2 dest = path.getFirst().toVec2(); + Vec2 dir = dest.sub(pos); + float dist = dir.normalize(); + Vec2 dp = dir.mul(context.getTimeDelta().toMillis() * SPEED); + pos = dp.length() < dist ? pos.add(dp) : path.removeFirst().toVec2(); + } + return Stream.empty(); } - private void paint(GraphicsContext graphicsContext) { - graphicsContext.paintCircle(Color.BLUE, getPos(), TileVec2.TILE_DIM / 2); + private Stream paint(Context context) { + context.getGraphicsContext().paintCircle(Color.BLUE, getPos(), TileVec2.TILE_DIM / 2); + return Stream.empty(); } /** diff --git a/src/main/java/fr/umlv/java/wallj/block/TrashBlock.java b/src/main/java/fr/umlv/java/wallj/block/TrashBlock.java index 84e993d..33b5a6b 100644 --- a/src/main/java/fr/umlv/java/wallj/block/TrashBlock.java +++ b/src/main/java/fr/umlv/java/wallj/block/TrashBlock.java @@ -2,14 +2,13 @@ package fr.umlv.java.wallj.block; import fr.umlv.java.wallj.board.TileVec2; import fr.umlv.java.wallj.context.Context; -import fr.umlv.java.wallj.context.GraphicsContext; +import fr.umlv.java.wallj.context.Updateables; import fr.umlv.java.wallj.event.Event; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.BodyType; import java.awt.*; -import java.util.Collections; -import java.util.List; +import java.util.stream.Stream; /** * A trash block. @@ -22,12 +21,13 @@ public class TrashBlock extends JBoxBlock { } @Override - public List update(Context context) { - paint(context.getGraphicsContext()); - return Collections.emptyList(); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::paint); } - private void paint(GraphicsContext graphicsContext) { - graphicsContext.paintRectangle(Color.RED, getPos(), TileVec2.TILE_DIM, TileVec2.TILE_DIM); + private Stream paint(Context context) { + context.getGraphicsContext().paintRectangle(Color.RED, getPos(), TileVec2.TILE_DIM, TileVec2.TILE_DIM); + return Stream.empty(); } } diff --git a/src/main/java/fr/umlv/java/wallj/block/WallBlock.java b/src/main/java/fr/umlv/java/wallj/block/WallBlock.java index 584c052..f94ee24 100644 --- a/src/main/java/fr/umlv/java/wallj/block/WallBlock.java +++ b/src/main/java/fr/umlv/java/wallj/block/WallBlock.java @@ -2,14 +2,13 @@ package fr.umlv.java.wallj.block; import fr.umlv.java.wallj.board.TileVec2; import fr.umlv.java.wallj.context.Context; -import fr.umlv.java.wallj.context.GraphicsContext; +import fr.umlv.java.wallj.context.Updateables; import fr.umlv.java.wallj.event.Event; import org.jbox2d.common.Vec2; -import org.jbox2d.dynamics.*; +import org.jbox2d.dynamics.BodyType; import java.awt.*; -import java.util.Collections; -import java.util.List; +import java.util.stream.Stream; /** * A wall block. @@ -22,12 +21,13 @@ public class WallBlock extends JBoxBlock { } @Override - public List update(Context context) { - paint(context.getGraphicsContext()); - return Collections.emptyList(); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::paint); } - private void paint(GraphicsContext graphicsContext) { - graphicsContext.paintRectangle(Color.BLACK, getPos(), TileVec2.TILE_DIM, TileVec2.TILE_DIM); + private Stream paint(Context context) { + context.getGraphicsContext().paintRectangle(Color.BLACK, getPos(), TileVec2.TILE_DIM, TileVec2.TILE_DIM); + return Stream.empty(); } } diff --git a/src/main/java/fr/umlv/java/wallj/context/Game.java b/src/main/java/fr/umlv/java/wallj/context/Game.java index 25629a3..79f00bd 100644 --- a/src/main/java/fr/umlv/java/wallj/context/Game.java +++ b/src/main/java/fr/umlv/java/wallj/context/Game.java @@ -1,9 +1,15 @@ package fr.umlv.java.wallj.context; import fr.umlv.java.wallj.board.Board; -import fr.umlv.java.wallj.event.*; +import fr.umlv.java.wallj.event.ConfirmOrder; +import fr.umlv.java.wallj.event.Event; +import fr.umlv.java.wallj.event.Events; +import fr.umlv.java.wallj.event.QuitGameOrder; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; /** * A game. @@ -100,7 +106,7 @@ public final class Game implements Updateable { * @return a list of new events */ @Override - public List update(Context context) { + public Stream update(Context context) { handleEvents(context); return currentStage.update(context); } diff --git a/src/main/java/fr/umlv/java/wallj/context/Stage.java b/src/main/java/fr/umlv/java/wallj/context/Stage.java index 9737583..44fad7b 100644 --- a/src/main/java/fr/umlv/java/wallj/context/Stage.java +++ b/src/main/java/fr/umlv/java/wallj/context/Stage.java @@ -6,12 +6,15 @@ import fr.umlv.java.wallj.block.BlockType; import fr.umlv.java.wallj.board.Board; import fr.umlv.java.wallj.board.BoardConverter; import fr.umlv.java.wallj.board.TileVec2; -import fr.umlv.java.wallj.event.*; +import fr.umlv.java.wallj.event.BlockCreateEvent; +import fr.umlv.java.wallj.event.BlockDestroyEvent; +import fr.umlv.java.wallj.event.Event; +import fr.umlv.java.wallj.event.Events; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.World; -import java.time.Duration; import java.util.*; +import java.util.stream.Stream; /** * @author Pacien TRAN-GIRARD @@ -76,33 +79,37 @@ public class Stage implements Updateable { /** * @param context the current context - * @return the list of newly generated events + * @return the stream of newly generated events */ @Override - public List update(Context context) { - updatePhysicalWorld(context.getTimeDelta()); - handleBlockDestruction(context.getEvents()); - handleBlockCreation(context.getEvents()); - return Updateable.updateAll(blocks, context); + public Stream update(Context context) { + return Updateables.updateAll(context, + this::updatePhysicalWorld, + this::handleBlockDestruction, + this::handleBlockCreation, + ctx -> Updateables.updateAll(ctx, blocks)); } - private void updatePhysicalWorld(Duration timeDelta) { - int dt = (int) timeDelta.toMillis(); + private Stream updatePhysicalWorld(Context context) { + int dt = (int) context.getTimeDelta().toMillis(); world.step(dt, dt * VELOCITY_TICK_PER_MS, dt * POSITION_TICK_PER_MS); + return Stream.empty(); } - private void handleBlockDestruction(List events) { - Events.filter(events, BlockDestroyEvent.class).forEach(event -> { + private Stream handleBlockDestruction(Context context) { + Events.filter(context.getEvents(), BlockDestroyEvent.class).forEach(event -> { if (blocks.remove(event.getBlock())) event.getBlock().unlink(world); }); + return Stream.empty(); } - private void handleBlockCreation(List events) { - Events.filter(events, BlockCreateEvent.class).forEach(event -> { + private Stream handleBlockCreation(Context context) { + Events.filter(context.getEvents(), BlockCreateEvent.class).forEach(event -> { Block block = BlockFactory.build(event.getBlockType(), event.getPos()); blocks.add(block); block.link(world); }); + return Stream.empty(); } private static TileVec2 findAnyFreeTile(Board board) { diff --git a/src/main/java/fr/umlv/java/wallj/context/Updateable.java b/src/main/java/fr/umlv/java/wallj/context/Updateable.java index 45c217e..9734783 100644 --- a/src/main/java/fr/umlv/java/wallj/context/Updateable.java +++ b/src/main/java/fr/umlv/java/wallj/context/Updateable.java @@ -2,28 +2,18 @@ package fr.umlv.java.wallj.context; import fr.umlv.java.wallj.event.Event; -import java.util.List; -import java.util.stream.Collectors; +import java.util.stream.Stream; /** + * Context-updateable objects and event stream generator interface. + * * @author Pacien TRAN-GIRARD */ +@FunctionalInterface public interface Updateable { /** * @param context an update context * @return a list of generated events */ - List update(Context context); - - /** - * @param updateables list of updateables - * @param context an update context - * @param the updateable type - * @return a list of collected generated events - */ - static List updateAll(List updateables, Context context) { - return updateables.stream() - .flatMap(u -> u.update(context).stream()) - .collect(Collectors.toList()); - } + Stream update(Context context); } diff --git a/src/main/java/fr/umlv/java/wallj/context/Updateables.java b/src/main/java/fr/umlv/java/wallj/context/Updateables.java new file mode 100644 index 0000000..f2aae6a --- /dev/null +++ b/src/main/java/fr/umlv/java/wallj/context/Updateables.java @@ -0,0 +1,40 @@ +package fr.umlv.java.wallj.context; + +import fr.umlv.java.wallj.event.Event; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +/** + * Updateables utilities. + * + * @author Pacien TRAN-GIRARD + */ +public final class Updateables { + /** + * @param the updateable type + * @param context an update context + * @param updateables list of updateables + * @return a stream of generated events + */ + public static Stream updateAll(Context context, List updateables) { + return updateables.stream() + .flatMap(u -> u.update(context)); + } + + /** + * @param the updateable type + * @param context an update context + * @param updateables list of updateables + * @return a stream of generated events + */ + @SafeVarargs + public static Stream updateAll(Context context, T... updateables) { + return updateAll(context, Arrays.asList(updateables)); + } + + private Updateables() { + // static class + } +} diff --git a/src/main/java/fr/umlv/java/wallj/viewer/Viewer.java b/src/main/java/fr/umlv/java/wallj/viewer/Viewer.java index 01a6468..e65389e 100644 --- a/src/main/java/fr/umlv/java/wallj/viewer/Viewer.java +++ b/src/main/java/fr/umlv/java/wallj/viewer/Viewer.java @@ -10,6 +10,7 @@ import java.time.Duration; import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * Link between application and Zen 5 @@ -42,7 +43,7 @@ public final class Viewer { ScreenManager screenManager = new ScreenManager(applicationContext, graphics2D); events.addAll(inputHandler.getEvents()); Context context = new Context(currentGame, events, screenManager.clearScreen(), last); - List newEvents = currentGame.update(context); //return new events created from update(); + List newEvents = currentGame.update(context).collect(Collectors.toList()); //return new events created from update(); events.clear(); events.addAll(newEvents); //add the new events returned by updates }); -- cgit v1.2.3