From 4dd6e13ff056387debb08c770a61cf74580e16a8 Mon Sep 17 00:00:00 2001 From: filipsasala Date: Thu, 12 May 2022 19:17:58 +0200 Subject: [PATCH 01/12] GameInputManager NullSafe update --- .idea/workspace.xml | 8 ++------ .../rt/game/input/GameInputManager.java | 17 ++++++----------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 54d1af7..349fcd9 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -11,11 +11,7 @@ - - - - - + diff --git a/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java b/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java index 8d8aede..184275e 100644 --- a/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java +++ b/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java @@ -2,6 +2,7 @@ import sk.bytecode.bludisko.rt.game.math.Vector2; import sk.bytecode.bludisko.rt.game.util.Config; +import sk.bytecode.bludisko.rt.game.util.NullSafe; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -52,14 +53,7 @@ private Vector2 toVector(int direction) { } private void toggleSprint(boolean toggle) { - withDelegate(d -> d.didUpdateSprintingStatus(toggle)); - } - - private void withDelegate(Consumer action) { - GameInputManagerDelegate delegate; - if((this.delegate != null) && (delegate = this.delegate.get()) != null) { - action.accept(delegate); - } + NullSafe.acceptWeak(delegate, d -> d.didUpdateSprintingStatus(toggle)); } // MARK: - KeyListener @@ -79,7 +73,7 @@ public void keyPressed(KeyEvent e) { toggleSprint(true); } - withDelegate(d -> d.didUpdateMovementDirection(toVector(this.direction))); + NullSafe.acceptWeak(delegate, d -> d.didUpdateMovementDirection(toVector(this.direction))); } @Override @@ -90,7 +84,7 @@ public void keyReleased(KeyEvent e) { toggleSprint(false); } - withDelegate(d -> d.didUpdateMovementDirection(toVector(this.direction))); + NullSafe.acceptWeak(delegate, d -> d.didUpdateMovementDirection(toVector(this.direction))); } // MARK: - MouseListener @@ -123,7 +117,8 @@ public void mouseMoved(MouseEvent e) { newPosition.translate(-this.windowDimensions.x, this.windowDimensions.y); newPosition.translate(-this.windowDimensions.width / 2, -this.windowDimensions.height / 2); - withDelegate(d -> d.didUpdateRotation(new Vector2(-newPosition.x, -newPosition.y))); + var rotationVector = new Vector2(newPosition.x, newPosition.y).scl(-1); + NullSafe.acceptWeak(delegate, d -> d.didUpdateRotation(rotationVector)); } } From 281e38cac37267a5b61f98107b3777ae99663ea5 Mon Sep 17 00:00:00 2001 From: filipsasala Date: Fri, 13 May 2022 20:56:07 +0200 Subject: [PATCH 02/12] Item use action --- .idea/workspace.xml | 10 +++++- .../bludisko/rt/game/blocks/game/Portal.java | 2 +- .../bludisko/rt/game/entities/Player.java | 32 ++++++++++++++----- .../rt/game/input/GameInputManager.java | 12 ++++--- .../game/input/GameInputManagerDelegate.java | 12 +++++-- .../bytecode/bludisko/rt/game/items/Item.java | 23 +++++++++++++ .../bludisko/rt/game/items/PortalGun.java | 14 ++++++++ .../bludisko/rt/game/util/Config.java | 4 +++ 8 files changed, 93 insertions(+), 16 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 349fcd9..9a9f52f 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -11,7 +11,14 @@ + + + + + + + diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java index fb3a8e4..2b7a3ae 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java @@ -56,7 +56,7 @@ public RayAction hitAction(Ray ray) { var rayPosition = ray.getPosition(); var hitSide = getSide(rayPosition); if(hitSide == side && otherPortal != null) { - var thisExit = this.getExitRotation(); + var thisExit = getExitRotation(); var otherExit = otherPortal.getExitRotation(); var exitRotation = thisExit.angleRad() - otherExit.angleRad(); diff --git a/src/sk/bytecode/bludisko/rt/game/entities/Player.java b/src/sk/bytecode/bludisko/rt/game/entities/Player.java index f630566..76a4e8e 100644 --- a/src/sk/bytecode/bludisko/rt/game/entities/Player.java +++ b/src/sk/bytecode/bludisko/rt/game/entities/Player.java @@ -20,6 +20,9 @@ */ public class Player extends Entity implements GameInputManagerDelegate { + private static final float WALKING_SPEED = 1.75f; + private static final float RUNNING_SPEED = 2.5f; + private Map worldWallMap; private WeakReference camera; private Rectangle screenSize; @@ -27,7 +30,7 @@ public class Player extends Entity implements GameInputManagerDelegate { private Vector2 movementVector; private Item heldItem; - private float walkingSpeed = 1.75f; + private float movementSpeed = 1.75f; // MARK: - Constructor @@ -45,6 +48,7 @@ public Player(World world) { 0f ); + this.screenSize = new Rectangle(0, 0); this.movementVector = new Vector2(0f, 0f); this.worldWallMap = world.getMap().walls(); } @@ -83,6 +87,16 @@ public Item getHeldItem() { return heldItem; } + /** + * Updates current screen size information. This is used to draw + * currently equipped item overlay to screen. + * @see Player#drawItemOverlay(Graphics) + * @param bounds New screen size + */ + public void setItemOverlayScreenSizeInformation(@NotNull Rectangle bounds) { + screenSize = bounds; + } + // MARK: - Game loop @Override @@ -92,7 +106,8 @@ public void tick(float dt) { } /** - * Draws player overlay - currently held item to current graphics content. + * Draws currently held item overlay to current graphics context. + * Uses screen size information from {@link Player#setItemOverlayScreenSizeInformation(Rectangle)} * @param graphics Graphics content to draw on */ public void drawItemOverlay(@NotNull Graphics graphics) { @@ -108,10 +123,6 @@ public void drawItemOverlay(@NotNull Graphics graphics) { }); } - public void setItemOverlayScreenSizeInformation(@NotNull Rectangle bounds) { - screenSize = bounds; - } - // MARK: - Input @Override @@ -126,14 +137,19 @@ public void didUpdateRotation(Vector2 rotation) { @Override public void didUpdateSprintingStatus(boolean isSprinting) { - this.walkingSpeed = isSprinting ? 2.5f : 1.75f; + this.movementSpeed = isSprinting ? Player.RUNNING_SPEED : Player.WALKING_SPEED; + } + + @Override + public void didToggleMouseButton(boolean rmb) { + NullSafe.accept(heldItem, rmb ? Item::useSecondary : Item::use); } // MARK: - Private private void move(float dt) { var movementVector = this.movementVector.cpy() - .scl(walkingSpeed) + .scl(movementSpeed) .scl(dt) .rotateRad(direction.angleRad()); diff --git a/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java b/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java index 184275e..4634b79 100644 --- a/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java +++ b/src/sk/bytecode/bludisko/rt/game/input/GameInputManager.java @@ -6,13 +6,12 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.util.function.Consumer; /** * Custom implementation of Input Manager for * a Game Screen. */ -public class GameInputManager extends InputManager { +public final class GameInputManager extends InputManager { private int direction = 0b0000; @@ -94,7 +93,10 @@ public void mouseClicked(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) { - + switch(e.getButton()) { + case Config.Keybinds.USE_PRIMARY -> NullSafe.acceptWeak(delegate, d -> d.didToggleMouseButton(false)); + case Config.Keybinds.USE_SECONDARY -> NullSafe.acceptWeak(delegate, d -> d.didToggleMouseButton(true)); + } } @Override @@ -109,7 +111,9 @@ public void mouseExited(MouseEvent e) {} // MARK: - MouseMotionListener @Override - public void mouseDragged(MouseEvent e) {} + public void mouseDragged(MouseEvent e) { + mouseMoved(e); + } @Override public void mouseMoved(MouseEvent e) { diff --git a/src/sk/bytecode/bludisko/rt/game/input/GameInputManagerDelegate.java b/src/sk/bytecode/bludisko/rt/game/input/GameInputManagerDelegate.java index 5abc026..1e32566 100644 --- a/src/sk/bytecode/bludisko/rt/game/input/GameInputManagerDelegate.java +++ b/src/sk/bytecode/bludisko/rt/game/input/GameInputManagerDelegate.java @@ -26,10 +26,18 @@ public interface GameInputManagerDelegate { void didUpdateRotation(Vector2 rotation); /** - * Called when the player starts sprinting. - * Check Config to set the key. + * Called when player presses the sprinting key. + * Check {@link sk.bytecode.bludisko.rt.game.util.Config} to set the key. * @param isSprinting Whether the player is sprinting */ void didUpdateSprintingStatus(boolean isSprinting); + /** + * Called when player pushes one of the use buttons. + * Check {@link sk.bytecode.bludisko.rt.game.util.Config} to set the + * mouse buttons. + * @param rmb If the button pushed was a secondary action + */ + void didToggleMouseButton(boolean rmb); + } diff --git a/src/sk/bytecode/bludisko/rt/game/items/Item.java b/src/sk/bytecode/bludisko/rt/game/items/Item.java index 003b5bd..e0b97b7 100644 --- a/src/sk/bytecode/bludisko/rt/game/items/Item.java +++ b/src/sk/bytecode/bludisko/rt/game/items/Item.java @@ -2,6 +2,10 @@ import sk.bytecode.bludisko.rt.game.graphics.Texture; +/** + * Abstract class representing a common and default implementation + * for all other in-game items. + */ public abstract class Item { // MARK: - Attributes @@ -10,6 +14,25 @@ public abstract class Item { // MARK: - Public + /** + * Default item use action. + * Called after a user input, usually LMB. + * @see sk.bytecode.bludisko.rt.game.entities.Player#didToggleMouseButton(boolean) + * @see sk.bytecode.bludisko.rt.game.input.GameInputManagerDelegate + */ + public void use() {} + + /** + * Secondary item use action. + * Called after a user input, usually RMB. + * @see sk.bytecode.bludisko.rt.game.entities.Player#didToggleMouseButton(boolean) + * @see sk.bytecode.bludisko.rt.game.input.GameInputManagerDelegate + */ + public void useSecondary() {} + + /** + * @return Texture of the item to be rendered as an overlay + */ public Texture getOverlay() { return overlay; } diff --git a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java index ff6e8fe..e1961bf 100644 --- a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java +++ b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java @@ -2,10 +2,24 @@ import sk.bytecode.bludisko.rt.game.graphics.TextureManager; +import java.lang.ref.WeakReference; + public class PortalGun extends Item { + // private WeakReference world; + public PortalGun() { this.overlay = TextureManager.getTexture(13); } + @Override + public void use() { + System.out.println("LMB"); + } + + @Override + public void useSecondary() { + System.out.println("RMB"); + } + } diff --git a/src/sk/bytecode/bludisko/rt/game/util/Config.java b/src/sk/bytecode/bludisko/rt/game/util/Config.java index 13f0f6d..a643673 100644 --- a/src/sk/bytecode/bludisko/rt/game/util/Config.java +++ b/src/sk/bytecode/bludisko/rt/game/util/Config.java @@ -1,6 +1,7 @@ package sk.bytecode.bludisko.rt.game.util; import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; public class Config { @@ -11,6 +12,9 @@ public static class Keybinds { public static final int DOWN = KeyEvent.VK_S; public static final int RIGHT = KeyEvent.VK_D; + public static final int USE_PRIMARY = MouseEvent.BUTTON1; + public static final int USE_SECONDARY = MouseEvent.BUTTON3; + public static final int LOCK_MOUSE = KeyEvent.VK_ESCAPE; public static final int SPRINT = KeyEvent.VK_CONTROL; From 55e2f78fa8c00aacd59c62c6f2353a98f6ebcd07 Mon Sep 17 00:00:00 2001 From: filipsasala Date: Sat, 14 May 2022 17:33:27 +0200 Subject: [PATCH 03/12] Dynamic portals PortalManager --- .idea/workspace.xml | 35 +++--- res/maps/chamber1.map | Bin 3754 -> 3754 bytes src/module-info.java | 5 + .../bludisko/rt/game/blocks/Block.java | 2 - .../bludisko/rt/game/blocks/game/Portal.java | 24 ++++ .../rt/game/blocks/game/WhiteTiles.java | 9 +- .../rt/game/blocks/group/PortalPlaceable.java | 9 ++ .../bludisko/rt/game/items/PortalGun.java | 25 ++++- .../bludisko/rt/game/map/Chamber1.java | 9 +- src/sk/bytecode/bludisko/rt/game/map/Map.java | 72 +++++++++++- .../bludisko/rt/game/map/PortalManager.java | 104 ++++++++++++++++++ .../bludisko/rt/game/util/NullSafe.java | 16 +++ .../rt/game/window/screens/GameScreen.java | 2 +- test/MapTest.java | 4 +- 14 files changed, 279 insertions(+), 37 deletions(-) create mode 100644 src/module-info.java create mode 100644 src/sk/bytecode/bludisko/rt/game/blocks/group/PortalPlaceable.java create mode 100644 src/sk/bytecode/bludisko/rt/game/map/PortalManager.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 9a9f52f..589f020 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -12,13 +12,16 @@ + + - - - - + - + + + + + @@ -70,6 +74,7 @@ + @@ -79,18 +84,18 @@ - + + - @@ -133,20 +138,21 @@ @@ -241,6 +247,7 @@ + diff --git a/res/maps/chamber1.map b/res/maps/chamber1.map index 4924106da12220cdd0bae0df045c98522a74454f..5a747f0e3b2471121e89b817d42be0d9fb31feff 100644 GIT binary patch delta 22 dcmZ1_yGnKeGb7_>MrM1)$-J!moBdg$xd23E1xf$_ delta 20 bcmZ1_yGnKeGb6`lMrM0PM)u8)ESX#YIBx`{ diff --git a/src/module-info.java b/src/module-info.java new file mode 100644 index 0000000..662bcf6 --- /dev/null +++ b/src/module-info.java @@ -0,0 +1,5 @@ +open module bludisko.rt { + requires jdk.unsupported; + requires java.desktop; + requires org.jetbrains.annotations; +} \ No newline at end of file diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/Block.java b/src/sk/bytecode/bludisko/rt/game/blocks/Block.java index d8ab6c2..566f744 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/Block.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/Block.java @@ -63,7 +63,6 @@ public Side getSide(Vector2 position) { } else { return Side.SOUTH; } - } if(position.y % 1 == 0) { // East-West @@ -73,7 +72,6 @@ public Side getSide(Vector2 position) { return Side.EAST; } } - return Side.NONE; } diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java index 2b7a3ae..3cdef92 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java @@ -1,6 +1,7 @@ package sk.bytecode.bludisko.rt.game.blocks.game; import sk.bytecode.bludisko.rt.game.blocks.Block; +import sk.bytecode.bludisko.rt.game.blocks.group.PortalPlaceable; import sk.bytecode.bludisko.rt.game.graphics.*; import sk.bytecode.bludisko.rt.game.math.MathUtils; import sk.bytecode.bludisko.rt.game.math.Vector2; @@ -17,6 +18,8 @@ public enum Color { private final Portal.Color color; private Portal otherPortal; + private Block originalWall; + /** * Constructs a new portal. * @param side Side of the portal entrance/exit @@ -93,6 +96,10 @@ private Vector2 getExitRotation() { }; } + public Portal getOtherPortal() { + return otherPortal; + } + /** * Link two portals together. * @param otherPortal Paired portal @@ -100,4 +107,21 @@ private Vector2 getExitRotation() { public void setOtherPortal(Portal otherPortal) { this.otherPortal = otherPortal; } + + public Block getOriginalWall() { + return originalWall; + } + + public void setOriginalWall(Block originalWall) { + if(originalWall instanceof PortalPlaceable) { + this.originalWall = originalWall; + } else { + throw new IllegalArgumentException("Cannot put a portal here!"); + } + } + + public Portal.Color getColor() { + return color; + } + } diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/game/WhiteTiles.java b/src/sk/bytecode/bludisko/rt/game/blocks/game/WhiteTiles.java index 83d97c4..13de73d 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/game/WhiteTiles.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/game/WhiteTiles.java @@ -1,10 +1,15 @@ package sk.bytecode.bludisko.rt.game.blocks.game; import sk.bytecode.bludisko.rt.game.blocks.Block; -import sk.bytecode.bludisko.rt.game.graphics.*; +import sk.bytecode.bludisko.rt.game.blocks.group.PortalPlaceable; +import sk.bytecode.bludisko.rt.game.graphics.Ray; +import sk.bytecode.bludisko.rt.game.graphics.RayAction; +import sk.bytecode.bludisko.rt.game.graphics.Side; +import sk.bytecode.bludisko.rt.game.graphics.Texture; +import sk.bytecode.bludisko.rt.game.graphics.TextureManager; import sk.bytecode.bludisko.rt.game.math.Vector2; -public class WhiteTiles extends Block { +public final class WhiteTiles extends Block implements PortalPlaceable { private final Vector2 coordinates; diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/group/PortalPlaceable.java b/src/sk/bytecode/bludisko/rt/game/blocks/group/PortalPlaceable.java new file mode 100644 index 0000000..8a8c0e9 --- /dev/null +++ b/src/sk/bytecode/bludisko/rt/game/blocks/group/PortalPlaceable.java @@ -0,0 +1,9 @@ +package sk.bytecode.bludisko.rt.game.blocks.group; + +import sk.bytecode.bludisko.rt.game.blocks.game.WhiteTiles; + +/** + * Interface defining all blocks a Portal can be placed on. + * @see sk.bytecode.bludisko.rt.game.blocks.game.Portal + */ +public sealed interface PortalPlaceable permits WhiteTiles {} diff --git a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java index e1961bf..35468ac 100644 --- a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java +++ b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java @@ -1,20 +1,39 @@ package sk.bytecode.bludisko.rt.game.items; +import sk.bytecode.bludisko.rt.game.blocks.game.Portal; +import sk.bytecode.bludisko.rt.game.blocks.group.PortalPlaceable; +import sk.bytecode.bludisko.rt.game.entities.Player; +import sk.bytecode.bludisko.rt.game.graphics.BlockRay; +import sk.bytecode.bludisko.rt.game.graphics.RayHit; import sk.bytecode.bludisko.rt.game.graphics.TextureManager; +import sk.bytecode.bludisko.rt.game.map.PortalManager; +import sk.bytecode.bludisko.rt.game.util.NullSafe; import java.lang.ref.WeakReference; public class PortalGun extends Item { - // private WeakReference world; + private static final int RANGE = 100; - public PortalGun() { + private WeakReference player; + + public PortalGun(Player owner) { this.overlay = TextureManager.getTexture(13); + this.player = new WeakReference<>(owner); } @Override public void use() { - System.out.println("LMB"); + NullSafe.acceptWeak(player, player -> { + var map = player.getWorld().getMap(); + var ray = new BlockRay(map.walls(), player.getPosition(), player.getDirection()); + var hitList = ray.cast(RANGE); + + hitList.stream() + .findFirst() + .map(RayHit::position) + .ifPresent(position -> PortalManager.createPortal(map, position, Portal.Color.ORANGE)); + }); } @Override diff --git a/src/sk/bytecode/bludisko/rt/game/map/Chamber1.java b/src/sk/bytecode/bludisko/rt/game/map/Chamber1.java index e4b9dee..ee21ec5 100644 --- a/src/sk/bytecode/bludisko/rt/game/map/Chamber1.java +++ b/src/sk/bytecode/bludisko/rt/game/map/Chamber1.java @@ -1,8 +1,7 @@ package sk.bytecode.bludisko.rt.game.map; -import sk.bytecode.bludisko.rt.game.blocks.game.Portal; import sk.bytecode.bludisko.rt.game.entities.Player; -import sk.bytecode.bludisko.rt.game.items.PortalGun; +import sk.bytecode.bludisko.rt.game.math.Vector2; import javax.swing.JOptionPane; @@ -40,11 +39,7 @@ public void tick(float dt) { private void setupMap() { this.map = GameMap.load("chamber1"); - var p1 = ((Portal) map.walls().getBlock(7, 7)); - var p2 = ((Portal) map.walls().getBlock(4, 9)); - - p1.setOtherPortal(p2); - p2.setOtherPortal(p1); + PortalManager.createPortal(map, new Vector2(7.5f, 8.0f), new Vector2(5.0f, 9.5f)); } private boolean playerAtDoors(Player player) { diff --git a/src/sk/bytecode/bludisko/rt/game/map/Map.java b/src/sk/bytecode/bludisko/rt/game/map/Map.java index d9437c6..ea32cc8 100644 --- a/src/sk/bytecode/bludisko/rt/game/map/Map.java +++ b/src/sk/bytecode/bludisko/rt/game/map/Map.java @@ -1,12 +1,17 @@ package sk.bytecode.bludisko.rt.game.map; +import org.jetbrains.annotations.NotNull; import sk.bytecode.bludisko.rt.game.blocks.Block; import sk.bytecode.bludisko.rt.game.blocks.BlockManager; +import sk.bytecode.bludisko.rt.game.blocks.game.Portal; import sk.bytecode.bludisko.rt.game.blocks.technical.Air; import sk.bytecode.bludisko.rt.game.math.MathUtils; import sk.bytecode.bludisko.rt.game.math.Vector2; import sk.bytecode.bludisko.rt.game.serialization.Serializable; +import java.util.Arrays; +import java.util.stream.Stream; + /** * Map object containing Blocks. Used for serialization * of maps from files. @@ -71,6 +76,10 @@ public int getHeight() { return blocks.length; } + public boolean coordinateValid(int x, int y) { + return (x >= 0 && y >= 0) && (x < getHeight() && y < getWidth()); + } + /** * Returns block at coordinates [x, y] or returns an Air block * if it does not exist/the position is invalid. @@ -79,13 +88,10 @@ public int getHeight() { * @return Block at given coordinates */ public Block getBlock(int x, int y) { - if(x < 0 || y < 0) { - return new Air(new Vector2(x, y)); - } - if(x >= getHeight() || y >= getWidth()) { - return new Air(new Vector2(x, y)); + if(coordinateValid(x, y)) { + return blocks[x][y]; } - return blocks[x][y]; + return new Air(new Vector2(x, y)); } /** @@ -111,4 +117,58 @@ public Block[] getBlocksAt(Vector2 position) { } } + /** + * @return A stream of all blocks in a map + */ + public Stream blockStream() { + return Arrays.stream(blocks).flatMap(Arrays::stream); + } + + // MARK: - Setters + + /** + * Sets block at coordinates [X, Y] to a new block. + * @param x x coordinate + * @param y y coordinate + * @param newBlock Block to set + * @return true if action succeeded, false otherwise + */ + public boolean setBlock(int x, int y, Block newBlock) { + if(coordinateValid(x, y)) { + blocks[x][y] = newBlock; + return true; + } + return false; + } + + // MARK: - Block manipulation + + /** + * General function to replace a block in a map. Swaps a block + * for another one as long as coordinates of the first block are valid. + * @param oldBlock Block to replace + * @param newBlock Block to be replaced by + * @return true if action succeeded, false otherwise + */ + public boolean replaceBlock(@NotNull Block oldBlock, @NotNull Block newBlock) { + var coordinates = oldBlock.getCoordinates(); + var oldX = (int) coordinates.x; + var oldY = (int) coordinates.y; + + return setBlock(oldX, oldY, newBlock); + } + + /** + * Specific overload when a block is being replaced by a Portal. Stores the + * original block reference inside a Portals' attribute. + * @param oldBlock Block to replace + * @param portal Portal to be replaced by + * @return true if action succeeded, false otherwise + * @see Portal#setOriginalWall(Block) + */ + public boolean replaceBlock(@NotNull Block oldBlock, @NotNull Portal portal) { + portal.setOriginalWall(oldBlock); + return replaceBlock(oldBlock, (Block) portal); + } + } diff --git a/src/sk/bytecode/bludisko/rt/game/map/PortalManager.java b/src/sk/bytecode/bludisko/rt/game/map/PortalManager.java new file mode 100644 index 0000000..e4d14b8 --- /dev/null +++ b/src/sk/bytecode/bludisko/rt/game/map/PortalManager.java @@ -0,0 +1,104 @@ +package sk.bytecode.bludisko.rt.game.map; + +import sk.bytecode.bludisko.rt.game.blocks.game.Portal; +import sk.bytecode.bludisko.rt.game.blocks.group.PortalPlaceable; +import sk.bytecode.bludisko.rt.game.math.Vector2; +import sk.bytecode.bludisko.rt.game.util.NullSafe; + +import java.util.Arrays; + +public final class PortalManager { + + private PortalManager() {} + + public static void createPortal(GameMap map, Vector2 orangePortalCoordinates, Vector2 bluePortalCoordinates) { + removeAllPortals(map); + var walls = map.walls(); + + var blocksAtOrangePortalCoordinates = walls.getBlocksAt(orangePortalCoordinates); + var blocksAtBluePortalCoordinates = walls.getBlocksAt(bluePortalCoordinates); + + var orangePortalWall = Arrays.stream(blocksAtOrangePortalCoordinates) + .filter(block -> block instanceof PortalPlaceable) + .findFirst(); + + var bluePortalWall = Arrays.stream(blocksAtBluePortalCoordinates) + .filter(block -> block instanceof PortalPlaceable) + .findFirst(); + + var orangePortalSide = orangePortalWall.map(wall -> wall.getSide(orangePortalCoordinates)); + var bluePortalSide = bluePortalWall.map(wall -> wall.getSide(bluePortalCoordinates)); + + var orangePortal = NullSafe.apply( + orangePortalWall, + orangePortalSide, + (wall, side) -> new Portal(side, Portal.Color.ORANGE, wall.getCoordinates()) + ); + var bluePortal = NullSafe.apply( + bluePortalWall, + bluePortalSide, + (wall, side) -> new Portal(side, Portal.Color.BLUE, wall.getCoordinates()) + ); + + NullSafe.accept(orangePortalWall, orangePortal, walls::replaceBlock); + NullSafe.accept(bluePortalWall, bluePortal, walls::replaceBlock); + + NullSafe.accept(orangePortal, bluePortal, (orange, blue) -> { + orange.setOtherPortal(blue); + blue.setOtherPortal(orange); + }); + } + + public static void createPortal(GameMap map, Vector2 portalCoordinates, Portal.Color portalColor) { + var walls = map.walls(); + var portalBlocks = walls.getBlocksAt(portalCoordinates); + + var portalWall = Arrays.stream(portalBlocks) + .filter(block -> block instanceof PortalPlaceable) + .findFirst(); + + var portalSide = portalWall.map(wall -> wall.getSide(portalCoordinates)); + + var newPortal = NullSafe.apply( + portalWall, + portalSide, + (wall, side) -> new Portal(side, portalColor, wall.getCoordinates()) + ); + + var otherPortal = walls.blockStream() + .filter(block -> block instanceof Portal) + .map(block -> (Portal) block) + .filter(portal -> portal.getColor() != portalColor) + .findFirst(); + + if(newPortal.isEmpty()) return; + removePortal(map, portalColor); + + NullSafe.accept(portalWall, newPortal, walls::replaceBlock); + NullSafe.accept(newPortal, otherPortal, Portal::setOtherPortal); + NullSafe.accept(otherPortal, newPortal, Portal::setOtherPortal); + } + + public static void removePortal(GameMap map, Portal.Color portalColor) { + var walls = map.walls(); + var portalWall = walls.blockStream() + .filter(block -> block instanceof Portal) + .map(block -> (Portal) block) + .filter(portal -> portal.getColor() == portalColor) + .findFirst(); + + portalWall.ifPresent(portal -> { + portal.getOtherPortal().setOtherPortal(null); + walls.replaceBlock(portal, portal.getOriginalWall()); + }); + } + + public static void removeAllPortals(GameMap map) { + var walls = map.walls(); + walls.blockStream() + .filter(block -> block instanceof Portal) + .map(block -> (Portal) block) + .forEach(portal -> walls.replaceBlock(portal, portal.getOriginalWall())); + } + +} diff --git a/src/sk/bytecode/bludisko/rt/game/util/NullSafe.java b/src/sk/bytecode/bludisko/rt/game/util/NullSafe.java index 2b24f83..d7d4b74 100644 --- a/src/sk/bytecode/bludisko/rt/game/util/NullSafe.java +++ b/src/sk/bytecode/bludisko/rt/game/util/NullSafe.java @@ -3,6 +3,9 @@ import org.jetbrains.annotations.Nullable; import java.lang.ref.WeakReference; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; public final class NullSafe { @@ -22,4 +25,17 @@ public static void acceptWeak(@Nullable WeakReference weakNullable, Consu } } + public static void accept(Optional optionalT, Optional optionalU, BiConsumer biConsumer) { + if(optionalT.isPresent() && optionalU.isPresent()) { + biConsumer.accept(optionalT.get(), optionalU.get()); + } + } + + public static Optional apply(Optional optionalT, Optional optionalU, BiFunction biFunction) { + if(optionalT.isPresent() && optionalU.isPresent()) { + return Optional.of(biFunction.apply(optionalT.get(), optionalU.get())); + } + return Optional.empty(); + } + } diff --git a/src/sk/bytecode/bludisko/rt/game/window/screens/GameScreen.java b/src/sk/bytecode/bludisko/rt/game/window/screens/GameScreen.java index a9b5a08..ceca4aa 100644 --- a/src/sk/bytecode/bludisko/rt/game/window/screens/GameScreen.java +++ b/src/sk/bytecode/bludisko/rt/game/window/screens/GameScreen.java @@ -50,7 +50,7 @@ private void setupPlayer() { player.setCamera(camera); currentWorld.setPlayer(player); - player.equip(new PortalGun()); + player.equip(new PortalGun(player)); } private void setupInput() { diff --git a/test/MapTest.java b/test/MapTest.java index 2f8a988..ecb070b 100644 --- a/test/MapTest.java +++ b/test/MapTest.java @@ -96,10 +96,10 @@ void map1() throws IOException { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 1, 2, 8, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0,11, 0, 0, 0, 2, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 1, 1, 1, 3, 3, 0, 2, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 1, 0, 0, 7, 0, 0, 4, 2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 4, 2, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0,13, 0, 0, 6, 0, 0, 4, 2, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 0, 5, 5, 0, 2, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0 }, From f2968b6493bcea3e0aec19a165321364bc833b6f Mon Sep 17 00:00:00 2001 From: filipsasala Date: Sat, 14 May 2022 19:32:21 +0200 Subject: [PATCH 04/12] All-sided portals --- .idea/workspace.xml | 11 ++----- .../bludisko/rt/game/blocks/game/Portal.java | 32 ++++++++++++------- .../bludisko/rt/game/items/PortalGun.java | 18 +++++++---- .../bludisko/rt/game/math/MathUtils.java | 8 ++--- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 589f020..ce887dd 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -12,16 +12,9 @@ - - - - - - - - + diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java index 3cdef92..ecddeba 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java @@ -59,9 +59,9 @@ public RayAction hitAction(Ray ray) { var rayPosition = ray.getPosition(); var hitSide = getSide(rayPosition); if(hitSide == side && otherPortal != null) { - var thisExit = getExitRotation(); - var otherExit = otherPortal.getExitRotation(); - var exitRotation = thisExit.angleRad() - otherExit.angleRad(); + // var thisExit = getExitRotation(); + // var otherExit = otherPortal.getExitRotation(); + var exitRotation = getEntryRotation() - otherPortal.getExitRotation(); rayPosition.set(MathUtils.decimalPart(rayPosition)); rayPosition.rotateRad(exitRotation); @@ -79,20 +79,30 @@ public RayAction hitAction(Ray ray) { private Vector2 getExitOffset() { return switch(side) { case NONE -> null; - case NORTH -> null; + case NORTH -> new Vector2(-1f, 0f); case EAST -> new Vector2(0f, 1f); case SOUTH -> new Vector2(1f, 0f); - case WEST -> null; + case WEST -> new Vector2(0f, -1f); }; } - private Vector2 getExitRotation() { + private float getEntryRotation() { return switch(side) { - case NONE -> null; - case NORTH -> null; - case EAST -> new Vector2(0f, 1f); - case SOUTH -> new Vector2(1f, 0f); - case WEST -> null; + case NONE -> 0f; + case NORTH -> 0f; + case EAST -> MathUtils.PI / 2f; + case SOUTH -> MathUtils.PI; + case WEST -> 3 * MathUtils.PI / 2f; + }; + } + + private float getExitRotation() { + return switch(side) { + case NONE -> 0f; + case NORTH -> MathUtils.PI; + case EAST -> 3 * MathUtils.PI / 2f; + case SOUTH -> 0f; + case WEST -> MathUtils.PI / 2; }; } diff --git a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java index 35468ac..45fcbc8 100644 --- a/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java +++ b/src/sk/bytecode/bludisko/rt/game/items/PortalGun.java @@ -24,6 +24,17 @@ public PortalGun(Player owner) { @Override public void use() { + placePortal(Portal.Color.ORANGE); + } + + @Override + public void useSecondary() { + placePortal(Portal.Color.BLUE); + } + + // MARK: - Private + + private void placePortal(Portal.Color color) { NullSafe.acceptWeak(player, player -> { var map = player.getWorld().getMap(); var ray = new BlockRay(map.walls(), player.getPosition(), player.getDirection()); @@ -32,13 +43,8 @@ public void use() { hitList.stream() .findFirst() .map(RayHit::position) - .ifPresent(position -> PortalManager.createPortal(map, position, Portal.Color.ORANGE)); + .ifPresent(position -> PortalManager.createPortal(map, position, color)); }); } - @Override - public void useSecondary() { - System.out.println("RMB"); - } - } diff --git a/src/sk/bytecode/bludisko/rt/game/math/MathUtils.java b/src/sk/bytecode/bludisko/rt/game/math/MathUtils.java index 547ed80..348e7d3 100644 --- a/src/sk/bytecode/bludisko/rt/game/math/MathUtils.java +++ b/src/sk/bytecode/bludisko/rt/game/math/MathUtils.java @@ -1,14 +1,14 @@ package sk.bytecode.bludisko.rt.game.math; -import static java.lang.Math.PI; - /** * Collection of handy Math constants and shortcuts. */ public final class MathUtils { - public static final float radiansToDegrees = (float) (180f / PI); - public static final float degreesToRadians = (float) (PI / 180f); + public static final float PI = (float) Math.PI; + + public static final float radiansToDegrees = 180f / PI; + public static final float degreesToRadians = PI / 180f; public static final float FLOAT_ROUNDING_ERROR = 0.000001f; public static final int INT_MSB_MASK = 0xFF000000; From d4ab2165581b5a048c7b24dcf2dc739c10b060c5 Mon Sep 17 00:00:00 2001 From: filipsasala Date: Sun, 15 May 2022 16:29:53 +0200 Subject: [PATCH 05/12] Fixed offsets and rotations --- .../bludisko/rt/game/blocks/game/Portal.java | 81 ++++++++++++++----- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java index ecddeba..9be4f48 100644 --- a/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java +++ b/src/sk/bytecode/bludisko/rt/game/blocks/game/Portal.java @@ -13,6 +13,8 @@ public enum Color { ORANGE } + // MARK: - Attributes + private final Vector2 coordinates; private final Side side; private final Portal.Color color; @@ -20,6 +22,8 @@ public enum Color { private Block originalWall; + // MARK: - Constructor + /** * Constructs a new portal. * @param side Side of the portal entrance/exit @@ -32,6 +36,8 @@ public Portal(Side side, Portal.Color color, Vector2 coordinates) { this.side = side; } + // MARK: - Override + @Override public Texture getTexture(Side side) { if(side == this.side) { @@ -59,16 +65,15 @@ public RayAction hitAction(Ray ray) { var rayPosition = ray.getPosition(); var hitSide = getSide(rayPosition); if(hitSide == side && otherPortal != null) { - // var thisExit = getExitRotation(); - // var otherExit = otherPortal.getExitRotation(); - var exitRotation = getEntryRotation() - otherPortal.getExitRotation(); + var rotation = getRotation(side) - getPiComplementaryRotation(otherPortal.side); + var offset = getOffset(side, otherPortal.side); rayPosition.set(MathUtils.decimalPart(rayPosition)); - rayPosition.rotateRad(exitRotation); + rayPosition.rotateRad(rotation); rayPosition.add(otherPortal.getCoordinates()); - rayPosition.add(otherPortal.getExitOffset()); + rayPosition.add(offset); - ray.updateDirection(ray.getDirection().cpy().rotateRad(exitRotation)); + ray.updateDirection(ray.getDirection().cpy().rotateRad(rotation)); return RayAction.TELEPORT; } else { @@ -76,17 +81,45 @@ public RayAction hitAction(Ray ray) { } } - private Vector2 getExitOffset() { - return switch(side) { + // MARK: - Static + + /** + * When moving a ray from one portal to another and when the portals are on + * different sides, the ray will end up misaligned. Adding this offset + * corrects the difference. + * @param entrySide Side of a block the entrance portal is on + * @param exitSide Side of a block the exit portal is on + * @return [x, y] offset to add + * @see Side + * @see Portal#getRotation(Side) + */ + private static Vector2 getOffset(Side entrySide, Side exitSide) { + return switch(entrySide) { case NONE -> null; - case NORTH -> new Vector2(-1f, 0f); - case EAST -> new Vector2(0f, 1f); - case SOUTH -> new Vector2(1f, 0f); - case WEST -> new Vector2(0f, -1f); + case NORTH, EAST -> switch(exitSide) { + case NONE -> null; + case NORTH -> new Vector2(0f, 1f); + case EAST -> new Vector2(1f, 1f); + case SOUTH -> new Vector2(1f, 0f); + case WEST -> new Vector2(0f, 0f); + }; + case SOUTH, WEST -> switch(exitSide) { + case NONE -> null; + case NORTH -> new Vector2(0f, 0f); + case EAST -> new Vector2(0f, 1f); + case SOUTH -> new Vector2(1f, 1f); + case WEST -> new Vector2(1f, 0f); + }; }; } - private float getEntryRotation() { + /** + * Converts {@link Side} to rotation. North is at 0°, rotation increases in + * a clockwise direction. + * @param side Side of the block the portal is on + * @return Rotation of the side in radians + */ + private static float getRotation(Side side) { return switch(side) { case NONE -> 0f; case NORTH -> 0f; @@ -96,7 +129,19 @@ private float getEntryRotation() { }; } - private float getExitRotation() { + /** + * Degrees of rotation left to the nearest n*PI value where n*PI ≠ 0.

+ * When used in combination
+ * {@link Portal#getRotation(Side)} - {@link Portal#getPiComplementaryRotation(Side)}, + * results in degrees required to rotate from facing one side to another. + * + * @param side Side of the block the portal is on + * @return Rotation in radians + * + * @see Side + * @see Portal#getRotation(Side) + */ + private static float getPiComplementaryRotation(Side side) { return switch(side) { case NONE -> 0f; case NORTH -> MathUtils.PI; @@ -106,6 +151,8 @@ private float getExitRotation() { }; } + // MARK: - Public + public Portal getOtherPortal() { return otherPortal; } @@ -123,11 +170,7 @@ public Block getOriginalWall() { } public void setOriginalWall(Block originalWall) { - if(originalWall instanceof PortalPlaceable) { - this.originalWall = originalWall; - } else { - throw new IllegalArgumentException("Cannot put a portal here!"); - } + this.originalWall = originalWall; } public Portal.Color getColor() { From 242d90149dede90ddb37a4b23038347c94ba38e5 Mon Sep 17 00:00:00 2001 From: filipsasala Date: Sun, 15 May 2022 18:00:21 +0200 Subject: [PATCH 06/12] Separate Overlay class --- .idea/workspace.xml | 30 ++++++--- bludisko_rt.iml | 61 +++--------------- .../bludisko/rt/game/entities/Entity.java | 3 +- .../bludisko/rt/game/entities/Player.java | 30 --------- .../bludisko/rt/game/graphics/Actor.java | 31 +++++++++ .../bludisko/rt/game/graphics/Camera.java | 8 +-- .../bludisko/rt/game/graphics/Overlay.java | 63 +++++++++++++++++++ .../bludisko/rt/game/graphics/Tickable.java | 14 +++++ .../bludisko/rt/game/input/InputManager.java | 11 ++-- .../rt/game/window/screens/GameScreen.java | 31 +++++---- .../rt/game/window/screens/Screen.java | 34 +++------- 11 files changed, 178 insertions(+), 138 deletions(-) create mode 100644 src/sk/bytecode/bludisko/rt/game/graphics/Actor.java create mode 100644 src/sk/bytecode/bludisko/rt/game/graphics/Overlay.java create mode 100644 src/sk/bytecode/bludisko/rt/game/graphics/Tickable.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index ce887dd..62f4674 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -12,9 +12,13 @@ - - - + + + + + + + @@ -53,10 +57,10 @@