From 33e266a76ec78a8877fbcd2da94f4e184df264f8 Mon Sep 17 00:00:00 2001 From: orangishcat Date: Sat, 30 Aug 2025 17:28:44 -0700 Subject: [PATCH 1/2] feat: better first person Signed-off-by: orangishcat --- src/main/java/basewindow/BaseWindow.java | 10 + src/main/java/lwjglwindow/ShaderHandler.java | 3 +- src/main/java/tanks/Game.java | 1 + src/main/java/tanks/Panel.java | 22 +- .../java/tanks/gui/input/InputBindings.java | 3 + .../tanks/gui/screen/IGameOverlayScreen.java | 6 + .../gui/screen/ScreenControlsCamera.java | 17 +- .../java/tanks/gui/screen/ScreenGame.java | 193 +++++++++++++++--- .../java/tanks/gui/screen/ScreenOptions.java | 12 +- .../tanks/gui/screen/ScreenOptionsMisc.java | 23 ++- src/main/java/tanks/hotbar/Hotbar.java | 2 +- .../java/tanks/network/NetworkHandler.java | 171 ++++++++++++++++ .../java/tanks/tank/TankPlayerRemote.java | 1 - .../resources/shaders/main_out_of_bounds.vert | 2 +- 14 files changed, 418 insertions(+), 48 deletions(-) create mode 100644 src/main/java/tanks/gui/screen/IGameOverlayScreen.java create mode 100644 src/main/java/tanks/network/NetworkHandler.java diff --git a/src/main/java/basewindow/BaseWindow.java b/src/main/java/basewindow/BaseWindow.java index 8f1615bd9..8e9101ff9 100644 --- a/src/main/java/basewindow/BaseWindow.java +++ b/src/main/java/basewindow/BaseWindow.java @@ -108,6 +108,8 @@ public abstract class BaseWindow public static final HashMap keyNames = new HashMap<>(); + public float[] outOfBoundsColor = new float[]{1f, 1f, 1f, 1f}; + public BasePlatformHandler platformHandler; public ModelPart.ShapeDrawer shapeDrawer; @@ -193,6 +195,14 @@ public void stopTiming() public abstract void setColor(double r, double g, double b); + public void setOutOfBoundsColor(float r, float g, float b, float a) + { + outOfBoundsColor[0] = r / 255; + outOfBoundsColor[1] = g / 255; + outOfBoundsColor[2] = b / 255; + outOfBoundsColor[3] = a / 255; + } + public abstract void setUpPerspective(); public abstract void applyTransformations(); diff --git a/src/main/java/lwjglwindow/ShaderHandler.java b/src/main/java/lwjglwindow/ShaderHandler.java index 086a4b936..b1627d1a6 100644 --- a/src/main/java/lwjglwindow/ShaderHandler.java +++ b/src/main/java/lwjglwindow/ShaderHandler.java @@ -3,7 +3,6 @@ import org.lwjgl.opengl.GL13; import java.nio.ByteBuffer; -import java.util.Arrays; import static org.lwjgl.opengl.EXTFramebufferObject.*; import static org.lwjgl.opengl.GL20.*; @@ -44,6 +43,8 @@ public void createDepthTexture(int size) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, window.outOfBoundsColor); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size, size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, (ByteBuffer) null); glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/src/main/java/tanks/Game.java b/src/main/java/tanks/Game.java index 7fbe4825e..4b5e9081f 100644 --- a/src/main/java/tanks/Game.java +++ b/src/main/java/tanks/Game.java @@ -154,6 +154,7 @@ public int hashCode() public static boolean drawFaces = false; public static boolean drawAvoidObjects = false; public static boolean recordMovableData = false; + public static boolean recordEventData = false; public static final boolean cinematic = false; public static long steamLobbyInvite = -1; diff --git a/src/main/java/tanks/Panel.java b/src/main/java/tanks/Panel.java index 48bbfe615..72d9b4c6d 100644 --- a/src/main/java/tanks/Panel.java +++ b/src/main/java/tanks/Panel.java @@ -1,6 +1,7 @@ package tanks; import basewindow.InputCodes; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import tanks.extension.Extension; import tanks.gui.*; import tanks.gui.ScreenElement.*; @@ -100,7 +101,10 @@ public class Panel /** Set to a directory to have the game screenshot the next frame and save it to that directory */ public String saveScreenshotDir = null; - public static void initialize() + public final Object2IntOpenHashMap> eventsInCount = new Object2IntOpenHashMap<>(); + public final Object2IntOpenHashMap> eventsOutCount = new Object2IntOpenHashMap<>(); + + public static void initialize() { if (!initialized) panel = new Panel(); @@ -348,11 +352,23 @@ else if (Game.deterministicMode) if (!(Game.eventsIn.get(i) instanceof IOnlineServerEvent)) { INetworkEvent e = Game.eventsIn.get(i); + INetworkEvent prev = null; if (e instanceof IStackableEvent) - stackedEventsIn.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); + prev = stackedEventsIn.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); else - Game.eventsIn.get(i).execute(); + Game.eventsIn.get(i).execute(); + + if (Game.recordEventData && prev == null) + { + if (e instanceof EventStackedGroup) + { + for (INetworkEvent e2 : ((EventStackedGroup) e).events) + eventsInCount.addTo(e2.getClass(), 1); + } + else + eventsInCount.addTo(e.getClass(), 1); + } } } diff --git a/src/main/java/tanks/gui/input/InputBindings.java b/src/main/java/tanks/gui/input/InputBindings.java index 574cf8745..52ecaf642 100644 --- a/src/main/java/tanks/gui/input/InputBindings.java +++ b/src/main/java/tanks/gui/input/InputBindings.java @@ -48,6 +48,8 @@ public class InputBindings public InputBindingGroup chat = new InputBindingGroup("game.chat", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_T)); public InputBindingGroup hidePause = new InputBindingGroup("game.hidePauseMenu", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F1)); public InputBindingGroup fullscreen = new InputBindingGroup("game.fullscreen", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F11), new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F10)); + public InputBindingGroup perspective = new InputBindingGroup("game.perspective", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F5)); + public InputBindingGroup tilt = new InputBindingGroup("game.pitch", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F), new InputBinding(InputBinding.InputType.mouse, InputCodes.MOUSE_BUTTON_5)); public InputBindingGroup screenshot = new InputBindingGroup("game.screenshot", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_F12)); public InputBindingGroup editorPause = new InputBindingGroup("editor.pause", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_ESCAPE)); @@ -63,6 +65,7 @@ public class InputBindings public InputBindingGroup editorRotate = new InputBindingGroup("editor.rotate", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_R)); public InputBindingGroup editorHeight = new InputBindingGroup("editor.height", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_H)); public InputBindingGroup editorGroupID = new InputBindingGroup("editor.groupID", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_G)); + public InputBindingGroup fcZoom = new InputBindingGroup("game.fcZoom", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_C)); public InputBindingGroup editorBuild = new InputBindingGroup("editor.build", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_B)); public InputBindingGroup editorErase = new InputBindingGroup("editor.erase", new InputBinding(InputBinding.InputType.keyboard, InputCodes.KEY_E)); diff --git a/src/main/java/tanks/gui/screen/IGameOverlayScreen.java b/src/main/java/tanks/gui/screen/IGameOverlayScreen.java new file mode 100644 index 000000000..1893617c0 --- /dev/null +++ b/src/main/java/tanks/gui/screen/IGameOverlayScreen.java @@ -0,0 +1,6 @@ +package tanks.gui.screen; + +public interface IGameOverlayScreen +{ + ScreenGame getGameScreen(); +} diff --git a/src/main/java/tanks/gui/screen/ScreenControlsCamera.java b/src/main/java/tanks/gui/screen/ScreenControlsCamera.java index 17b04227a..d5d93ae10 100644 --- a/src/main/java/tanks/gui/screen/ScreenControlsCamera.java +++ b/src/main/java/tanks/gui/screen/ScreenControlsCamera.java @@ -6,10 +6,13 @@ public class ScreenControlsCamera extends Screen { - InputSelector toggleZoom = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 - 135, 700, 40, "Toggle zoom", Game.game.input.zoom); - InputSelector zoomIn = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 - 45, 700, 40, "Zoom in", Game.game.input.zoomIn); - InputSelector zoomOut = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 45, 700, 40, "Zoom out", Game.game.input.zoomOut); - InputSelector zoomAuto = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 135, 700, 40, "Toggle automatic zoom", Game.game.input.zoomAuto); + InputSelector toggleZoom = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 - 225, 700, 40, "Toggle zoom", Game.game.input.zoom); + InputSelector zoomIn = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 - 135, 700, 40, "Zoom in", Game.game.input.zoomIn); + InputSelector zoomOut = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 - 45, 700, 40, "Zoom out", Game.game.input.zoomOut); + InputSelector zoomAuto = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 45, 700, 40, "Toggle automatic zoom", Game.game.input.zoomAuto); + InputSelector togglePerspective = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 135, 700, 40, "Toggle Perspective", Game.game.input.perspective); + InputSelector cameraPitch = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 225, 700, 40, "Third person camera tilt", Game.game.input.tilt); + InputSelector cameraZoom = new InputSelector(Drawing.drawing.interfaceSizeX * 2 / 3, Drawing.drawing.interfaceSizeY / 2 + 315, 700, 40, "Third person camera zoom", Game.game.input.fcZoom); public ScreenControlsCamera() { @@ -24,6 +27,9 @@ public void update() zoomIn.update(); zoomOut.update(); zoomAuto.update(); + togglePerspective.update(); + cameraPitch.update(); + cameraZoom.update(); ScreenOverlayControls.overlay.update(); } @@ -37,6 +43,9 @@ public void draw() zoomOut.draw(); zoomIn.draw(); zoomAuto.draw(); + togglePerspective.draw(); + cameraPitch.draw(); + cameraZoom.draw(); Drawing.drawing.setInterfaceFontSize(this.titleSize); Drawing.drawing.setColor(0, 0, 0); diff --git a/src/main/java/tanks/gui/screen/ScreenGame.java b/src/main/java/tanks/gui/screen/ScreenGame.java index 082969525..2e99272d1 100644 --- a/src/main/java/tanks/gui/screen/ScreenGame.java +++ b/src/main/java/tanks/gui/screen/ScreenGame.java @@ -3,7 +3,7 @@ import basewindow.*; import basewindow.transformation.*; import tanks.*; -import tanks.bullet.Bullet; +import tanks.bullet.*; import tanks.generator.LevelGeneratorVersus; import tanks.gui.*; import tanks.gui.screen.leveleditor.ScreenLevelEditor; @@ -102,8 +102,14 @@ public class ScreenGame extends Screen implements IHiddenChatboxScreen, IPartyGa protected boolean musicStarted = false; protected float pausedMusicPos = 0; - public boolean zoomPressed = false; - public boolean zoomScrolled = false; + public boolean zoomPressed, zoomScrolled; + + public static boolean fcZoomPressed = false; + public static double fcSensitivity = 1f; + public static double fcZoom, fcTargetZoom; + public double fcZoomLastTap, fcPitch, fcZoomCap; + public boolean selectedArcBullet = false; + public double fcArcAim; public boolean playedIntro = false; @@ -564,6 +570,9 @@ public ScreenGame() this.drawDarkness = false; this.drawDebugInternally = true; + float f = (float) (Level.currentLightIntensity / 255); + Game.game.window.setOutOfBoundsColor(f, f, f, 255); + Game.clouds.clear(); if (Game.currentLevel instanceof Minigame && !(Game.currentLevel instanceof Tutorial)) @@ -994,6 +1003,9 @@ public void update() } } + if (Game.game.input.perspective.isValid()) + togglePerspective(); + if (Game.game.input.zoom.isPressed() && playing) { if (Panel.autoZoom) @@ -1689,13 +1701,7 @@ else if (this.shopScreen) playing = true; this.age += Panel.frameFrequency; - if (Game.followingCam) - { - Game.playerTank.angle += (Drawing.drawing.getInterfaceMouseX() - prevCursorX) / 200; - Game.game.window.setCursorLocked(true); - this.prevCursorX = Drawing.drawing.getInterfaceMouseX(); - this.prevCursorY = Drawing.drawing.getInterfaceMouseX(); - } + updateFollowingCam(); Obstacle.draw_size = Math.min(Game.tile_size, Obstacle.draw_size); ArrayList aliveTeams = new ArrayList<>(); @@ -2122,7 +2128,7 @@ public static void handleRemovals() ModAPI.removeMenus.clear(); } - public void updateGameField() + public static void updateGameField() { for (Movable m : Game.movables) m.preUpdate(); @@ -2464,19 +2470,16 @@ else if (Panel.panel.zoomTimer >= 1) public void setPerspective() { - Game.game.window.clipMultiplier = 100; - Game.game.window.clipDistMultiplier = 1; - - if (Game.angledView) + if (!Game.game.window.drawingShadow) { - if (!Game.game.window.drawingShadow) - { - if (this.playing && (!this.paused || ScreenPartyHost.isServer || ScreenPartyLobby.isClient) && !ScreenGame.finished) - slant = Math.min(1, slant + 0.01 * Panel.frameFrequency); - else if (ScreenGame.finished) - slant = Math.max(0, slant - 0.01 * Panel.frameFrequency); - } + if (Game.angledView && this.playing && (!this.paused || ScreenPartyHost.isServer || ScreenPartyLobby.isClient) && !ScreenGame.finished) + slant = Math.min(1, slant + 0.01 * Panel.frameFrequency); + else if (!Game.angledView || ScreenGame.finished) + slant = Math.max(0, slant - 0.01 * Panel.frameFrequency); + } + if (Game.angledView || slant > 0) + { this.slantRotation.pitch = this.slant * -Math.PI / 16; this.slantTranslation.y = -this.slant * 0.05; @@ -2496,29 +2499,143 @@ else if (ScreenGame.finished) Game.game.window.clipMultiplier = 1; Game.game.window.clipDistMultiplier = 100; + Tank t = focusedTank(); + if (!Game.firstPerson) { - Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, 0, frac * ((Game.playerTank.angle + Math.PI * 3 / 2) % (Math.PI * 2) - Math.PI), 0, -Drawing.drawing.statsHeight / Game.game.window.absoluteHeight / 2, 0)); + Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, 0, frac * ((t.angle + Math.PI * 3 / 2) % (Math.PI * 2) - Math.PI), 0, -Drawing.drawing.statsHeight / Game.game.window.absoluteHeight / 2, 0)); Game.game.window.transformations.add(new Translation(Game.game.window, 0, 0.1 * frac, 0)); - Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, -Math.PI * 0.35 * frac, 0, 0, 0, -1)); + Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, -Math.PI * 0.35 * frac + fcPitch, 0, fcPitch * 3, fcPitch * 3, -1)); Game.game.window.transformations.add(new Translation(Game.game.window, 0, 0, 0.5 * frac)); } else { - Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, 0, frac * ((Game.playerTank.angle + Math.PI * 3 / 2) % (Math.PI * 2) - Math.PI), 0, -Drawing.drawing.statsHeight / Game.game.window.absoluteHeight / 2, 0)); + Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, 0, frac * ((t.angle + Math.PI * 3 / 2) % (Math.PI * 2) - Math.PI), 0, -Drawing.drawing.statsHeight / Game.game.window.absoluteHeight / 2, 0)); Game.game.window.transformations.add(new Translation(Game.game.window, 0, 0.1 * frac, 0)); Game.game.window.transformations.add(new RotationAboutPoint(Game.game.window, 0, -Math.PI * 0.5 * frac, 0, 0, 0, -1)); Game.game.window.transformations.add(new Translation(Game.game.window, 0, 0.0575 * frac, 0.9 * frac)); } + if (fcZoom > 0) + Game.game.window.transformations.add(new ScaleAboutPoint(Game.game.window, 1, 1, fcZoom + 1, 0, 0, 0)); + Game.game.window.loadPerspective(); } } + public void updateFollowingCam() + { + if (!Game.followingCam || Game.playerTank == null || Game.playerTank.destroy) + return; + + ItemBar b = Game.player.hotbar.itemBar; + selectedArcBullet = b.selected >= 0 && b.selected < ItemBar.item_bar_size && b.slots[b.selected].item instanceof ItemBullet && ((ItemBullet) b.slots[b.selected].item).bullet instanceof BulletArc; + + Game.playerTank.angle += (Drawing.drawing.getInterfaceMouseX() - prevCursorX) * fcSensitivity / 150; + Game.game.window.setCursorLocked(true); + if (Game.game.input.tilt.isPressed()) + fcPitch += (Drawing.drawing.getInterfaceMouseY() - this.prevCursorY) * fcSensitivity * 5e-4; + else if (selectedArcBullet) + fcArcAim += (this.prevCursorY - Drawing.drawing.getInterfaceMouseY()) * (fcSensitivity * 3); + fcPitch = Math.max(0, Math.min(0.5, fcPitch)); + Game.game.window.setCursorPos(Drawing.drawing.interfaceSizeX / 2, Drawing.drawing.interfaceSizeY / 2); + updateMousePos(); + + if (selectedArcBullet) + { + Bullet bullet = ((ItemBullet) b.slots[b.selected].item).bullet; + fcArcAim = Math.max(bullet.getRangeMin(), Math.min(bullet.getRangeMax() > 0 ? bullet.getRangeMax() : Double.MAX_VALUE, fcArcAim)); + } + + fcZoomPressed = Game.game.input.fcZoom.isPressed(); + if (Math.abs(fcTargetZoom - fcZoom) < 0.05) + fcZoom = fcTargetZoom; + else + fcZoom += (fcTargetZoom - fcZoom) / 10; + + if (fcZoomPressed) + { + if (Game.game.input.fcZoom.isValid()) + { + if (age - fcZoomLastTap < 50) + fcTargetZoom = 0; + + fcZoomLastTap = age; + Game.game.input.fcZoom.invalidate(); + } + + if (Game.game.window.validScrollUp && fcTargetZoom < 0.9) + { + fcTargetZoom += 0.1; + Game.game.window.validScrollUp = false; + } + + if (Game.game.window.validScrollDown && fcTargetZoom > -0.9) + { + fcTargetZoom -= 0.1; + Game.game.window.validScrollDown = false; + } + } + } + + private static int perspectiveID = 0; + + public void togglePerspective() + { + if (Game.game.window.shift) + perspectiveID--; + else if (Game.game.window.pressedKeys.contains(InputCodes.KEY_LEFT_CONTROL)) + perspectiveID = 0; + else + perspectiveID++; + + perspectiveID = (perspectiveID + 4) % 4; + + switch (perspectiveID) + { + case 0: + Game.angledView = false; + Game.followingCam = false; + Game.firstPerson = false; + break; + case 1: + Game.angledView = true; + Game.followingCam = false; + Game.firstPerson = false; + break; + case 2: + Game.angledView = false; + Game.followingCam = true; + Game.firstPerson = false; + break; + case 3: + Game.angledView = false; + Game.followingCam = true; + Game.firstPerson = true; + break; + } + + if (Game.followingCam) + { + Drawing.drawing.movingCamera = true; + Panel.autoZoom = false; + Panel.zoomTarget = -1; + } + + this.enableMargins = !Game.followingCam; + Game.game.input.perspective.invalidate(); + } + + public void updateMousePos() + { + this.prevCursorX = Drawing.drawing.getInterfaceMouseX(); + this.prevCursorY = Drawing.drawing.getInterfaceMouseY(); + } + @Override public void draw() { - this.showDefaultMouse = !(((!this.paused && !this.npcShopScreen) && this.playing && Game.angledView || Game.firstPerson)); + this.showDefaultMouse = !(!this.paused && !this.npcShopScreen && this.playing && (Game.angledView || Game.followingCam)); this.setPerspective(); @@ -2663,7 +2780,7 @@ public void draw() if (!(paused && screenshotMode) && Game.player.hotbar.enabledItemBar) Game.player.hotbar.itemBar.drawOverlay(); - if (!this.showDefaultMouse) + if (!this.showDefaultMouse && Game.angledView) Panel.panel.drawMouseTarget(true); Game.game.window.transformations.clear(); @@ -3153,6 +3270,28 @@ else if (name != null) Drawing.drawing.setInterfaceFontSize(this.textSize); } + public static ScreenGame getInstance() + { + if (Game.screen instanceof ScreenGame) + return (ScreenGame) Game.screen; + if (Game.screen instanceof IGameOverlayScreen) + return ((IGameOverlayScreen) Game.screen).getGameScreen(); + return null; + } + + public static Tank focusedTank() + { + ScreenGame g = getInstance(); + if (g == null || Game.playerTank == null) + return Game.playerTank; + + if (Game.playerTank.destroy && g.spectatingTank != null) + return g.spectatingTank; + + return Game.playerTank; + } + + public static boolean isUpdatingGame() { if (!(Game.screen instanceof ScreenGame)) diff --git a/src/main/java/tanks/gui/screen/ScreenOptions.java b/src/main/java/tanks/gui/screen/ScreenOptions.java index 025479826..0a60c349c 100644 --- a/src/main/java/tanks/gui/screen/ScreenOptions.java +++ b/src/main/java/tanks/gui/screen/ScreenOptions.java @@ -85,9 +85,7 @@ else if (ScreenPartyLobby.isClient) Button miscOptions = new Button(this.centerX - this.objXSpace / 2, this.centerY + this.objYSpace * 2, this.objWidth, this.objHeight, "Miscellaneous options", () -> Game.screen = new ScreenOptionsMisc()); Button openFolder = new Button(this.centerX + this.objXSpace / 2, this.centerY + this.objYSpace * 2, this.objWidth, this.objHeight, "Open game folder", () -> - { - Game.game.fileManager.openFileManager(Game.homedir + Game.directoryPath); - }); + Game.game.fileManager.openFileManager(Game.homedir + Game.directoryPath)); @Override @@ -180,7 +178,7 @@ public static void initOptions(String homedir) } catch (IOException e) { - Game.logger.println (new Date().toString() + " (syserr) file permissions are broken! cannot initialize options file."); + Game.logger.println (new Date() + " (syserr) file permissions are broken! cannot initialize options file."); System.exit(1); } @@ -254,6 +252,7 @@ public static void saveOptions(String homedir) f.println("disable_party_friendly_fire=" + Game.disablePartyFriendlyFire); f.println("party_countdown=" + Game.partyStartTime); f.println("party_bots=" + Game.botPlayerCount); + f.println("first_person_sens=" + (int) (ScreenGame.fcSensitivity * 100)); f.println("tank_secondary_color=" + Game.player.enableSecondaryColor); f.println("tank_tertiary_color=" + Game.player.enableTertiaryColor); f.println("tank_red=" + (int) Game.player.color.red); @@ -489,6 +488,9 @@ public static void loadOptions(String homedir) case "party_bots": Game.botPlayerCount = Integer.parseInt(optionLine[1]); break; + case "first_person_sens": + ScreenGame.fcSensitivity = Double.parseDouble(optionLine[1]) / 100; + break; case "tank_secondary_color": Game.player.enableSecondaryColor = Boolean.parseBoolean(optionLine[1]); break; @@ -569,7 +571,7 @@ public static void loadOptions(String homedir) } catch (Exception e) { - Game.logger.println (new Date().toString() + " Options file is nonexistent or broken, using default:"); + Game.logger.println (new Date() + " Options file is nonexistent or broken, using default:"); e.printStackTrace(Game.logger); System.err.println("Failed to load options!"); e.printStackTrace(); diff --git a/src/main/java/tanks/gui/screen/ScreenOptionsMisc.java b/src/main/java/tanks/gui/screen/ScreenOptionsMisc.java index 7cba32fdf..d391a37ff 100644 --- a/src/main/java/tanks/gui/screen/ScreenOptionsMisc.java +++ b/src/main/java/tanks/gui/screen/ScreenOptionsMisc.java @@ -2,7 +2,7 @@ import tanks.Drawing; import tanks.Game; -import tanks.gui.Button; +import tanks.gui.*; import tanks.hotbar.Hotbar; public class ScreenOptionsMisc extends Screen @@ -15,7 +15,7 @@ public class ScreenOptionsMisc extends Screen public static final String circularText = "\u00A7000100200255circular"; public static final String bottomText = "\u00A7200100000255bottom"; - Button autostart = new Button(this.centerX, this.centerY - this.objYSpace * 1.5, this.objWidth, this.objHeight, "", new Runnable() + Button autostart = new Button(this.centerX - this.objXSpace / 2, this.centerY - this.objYSpace * 1.5, this.objWidth, this.objHeight, "", new Runnable() { @Override public void run() @@ -30,7 +30,7 @@ public void run() }, "When enabled, levels will---start playing automatically---4 seconds after they are---loaded (if the play button---isn't clicked earlier)"); - Button fullStats = new Button(this.centerX, this.centerY - this.objYSpace * 0.5, this.objWidth, this.objHeight, "", new Runnable() + Button fullStats = new Button(this.centerX - this.objXSpace / 2, this.centerY - this.objYSpace * 0.5, this.objWidth, this.objHeight, "", new Runnable() { @Override public void run() @@ -45,7 +45,7 @@ public void run() }, "When off, skips directly to the summary tab---of the crusade end stats screen"); - Button previewCrusades = new Button(this.centerX, this.centerY + this.objYSpace * 0.5, this.objWidth, this.objHeight, "", new Runnable() + Button previewCrusades = new Button(this.centerX + this.objXSpace / 2, this.centerY - this.objYSpace * 1.5, this.objWidth, this.objHeight, "", new Runnable() { @Override public void run() @@ -60,7 +60,7 @@ public void run() }, "When enabled, the backgrounds of---the crusade preview and stats---screens display an animation of all---the crusade levels scrolling by."); - Button circularHotbar = new Button(this.centerX, this.centerY + this.objYSpace * 1.5, this.objWidth, this.objHeight, "", new Runnable() + Button circularHotbar = new Button(this.centerX + this.objXSpace / 2, this.centerY - this.objYSpace * 0.5, this.objWidth, this.objHeight, "", new Runnable() { @Override public void run() @@ -77,6 +77,17 @@ public void run() "In the 'bottom' setting, all this information---will be at the bottom of the screen.------" + "In the 'circular' setting, this information will---either be overlaid on your tank---or placed around your cursor."); + TextBoxSlider firstPersonSens = new TextBoxSlider(this.centerX, this.centerY + this.objYSpace * 1.5, this.objWidth, this.objHeight, "First person sensitivity", new Runnable() + { + @Override + public void run() + { + if (firstPersonSens.inputText.isEmpty()) + firstPersonSens.inputText = firstPersonSens.previousInputText; + ScreenGame.fcSensitivity = Integer.parseInt(firstPersonSens.inputText) / 100.0; + } + }, ScreenGame.fcSensitivity * 100, 1, 200, 1); + Button back = new Button(this.centerX, this.centerY + this.objYSpace * 3.5, this.objWidth, this.objHeight, "Back", () -> Game.screen = new ScreenOptions()); public ScreenOptionsMisc() @@ -116,6 +127,7 @@ public void update() fullStats.update(); previewCrusades.update(); circularHotbar.update(); + firstPersonSens.update(); } @Override @@ -128,6 +140,7 @@ public void draw() fullStats.draw(); autostart.draw(); circularHotbar.draw(); + firstPersonSens.draw(); Drawing.drawing.setInterfaceFontSize(this.titleSize); Drawing.drawing.setColor(0, 0, 0); diff --git a/src/main/java/tanks/hotbar/Hotbar.java b/src/main/java/tanks/hotbar/Hotbar.java index 3a9bf1707..f9107ff4d 100644 --- a/src/main/java/tanks/hotbar/Hotbar.java +++ b/src/main/java/tanks/hotbar/Hotbar.java @@ -17,7 +17,7 @@ public class Hotbar { - public ItemBar itemBar; + public ItemBar itemBar; public int coins; public boolean enabledAmmunitionBar = true; diff --git a/src/main/java/tanks/network/NetworkHandler.java b/src/main/java/tanks/network/NetworkHandler.java new file mode 100644 index 000000000..b81a71f2f --- /dev/null +++ b/src/main/java/tanks/network/NetworkHandler.java @@ -0,0 +1,171 @@ +package tanks.network; + +import com.codedisaster.steamworks.*; +import io.netty.buffer.ByteBuf; +import io.netty.channel.*; +import io.netty.util.ReferenceCountUtil; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import tanks.*; +import tanks.network.event.*; + +import java.util.*; + +public abstract class NetworkHandler extends ChannelInboundHandlerAdapter +{ + public ChannelHandlerContext ctx; + public SteamID steamID; + + protected SynchronizedList events = new SynchronizedList<>(); + protected Int2ObjectOpenHashMap stackedEvents = new Int2ObjectOpenHashMap<>(); + + public boolean joined = false; + public boolean closed = false; + + public MessageReader reader = new MessageReader(); + + protected long lastStackedEventSend = 0; + protected LinkedHashMap, EventStackedGroup> groupedEvents = new LinkedHashMap<>(); + + public synchronized void sendEventAndClose(INetworkEvent e) + { + this.closed = true; + if (steamID != null) + Game.steamNetworkHandler.send(steamID.getAccountID(), e, SteamNetworking.P2PSend.Reliable); + else + this.sendEvent(e, true); + + if (ctx != null) + ctx.close(); + + if (steamID != null) + Game.steamNetworkHandler.queueClose(steamID.getAccountID()); + } + + public synchronized void sendEvent(INetworkEvent e) + { + sendEvent(e, true); + } + + public synchronized void sendEvent(INetworkEvent e, boolean flush) + { + if (steamID != null) + Game.steamNetworkHandler.send(steamID.getAccountID(), e, flush ? + SteamNetworking.P2PSend.Reliable : SteamNetworking.P2PSend.ReliableWithBuffering); + + if (Game.recordEventData) + { + if (e instanceof EventStackedGroup) + for (INetworkEvent e2 : ((EventStackedGroup) e).events) + Panel.panel.eventsOutCount.addTo(e2.getClass(), 1); + else + Panel.panel.eventsOutCount.addTo(e.getClass(), 1); + } + + ByteBuf b = ctx.channel().alloc().buffer(); + + int eventID = NetworkEventMap.get(e.getClass()); + if (eventID == -1) + throw new RuntimeException("The network event " + e.getClass() + " has not been registered!"); + + b.writeInt(eventID); + e.write(b); + + ByteBuf b2 = ctx.channel().alloc().buffer(); + b2.writeInt(b.readableBytes()); + MessageReader.upstreamBytes += b.readableBytes() + 4; + MessageReader.updateLastMessageTime(); + b2.writeBytes(b); + + if (flush) + ctx.channel().writeAndFlush(b2); + else + ctx.channel().write(b2); + + ReferenceCountUtil.release(b); + } + + @SuppressWarnings("unchecked") + public synchronized void stackEvent(INetworkEvent e) + { + if (!(e instanceof PersonalEvent) || e instanceof IClientThreadEvent || e instanceof IServerThreadEvent) + { + sendEvent(e, false); + return; + } + + groupedEvents.computeIfAbsent(e.getClass(), + k -> new EventStackedGroup((Class) e.getClass())).events.add((PersonalEvent) e); + } + + public synchronized void flushEvents() + { + if (groupedEvents.isEmpty()) + { + ctx.flush(); + return; + } + + int i = 0; + for (EventStackedGroup e : this.groupedEvents.values()) + { + boolean flush = i++ >= this.groupedEvents.size() - 1; + if (e.events.size() == 1) + sendEvent(e.events.get(0), flush); + else + sendEvent(e, flush); + } + groupedEvents.clear(); + } + + public void reply() + { + synchronized (this.events) + { + INetworkEvent prev = null; + for (INetworkEvent e : this.events) + { + INetworkEvent other = null; + if (e instanceof IStackableEvent && ((IStackableEvent) e).isStackable()) + other = this.stackedEvents.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); + else + { + if (prev != null) + this.stackEvent(prev); + + prev = e; + } + if (Game.recordEventData && other == null) + Panel.panel.eventsOutCount.addTo(e.getClass(), 1); + } + + long time = System.currentTimeMillis() * Game.networkRate / 1000; + if (time != lastStackedEventSend) + { + lastStackedEventSend = time; + + if (prev != null) + this.stackEvent(prev); + + for (IStackableEvent e : this.stackedEvents.values()) + this.stackEvent(e); + + this.stackedEvents.clear(); + flushEvents(); + } + else if (prev != null) + this.stackEvent(prev); + + if (steamID == null) + this.ctx.flush(); + + this.events.clear(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + { + cause.printStackTrace(); + ctx.close(); + } +} diff --git a/src/main/java/tanks/tank/TankPlayerRemote.java b/src/main/java/tanks/tank/TankPlayerRemote.java index 5219001da..7304373da 100644 --- a/src/main/java/tanks/tank/TankPlayerRemote.java +++ b/src/main/java/tanks/tank/TankPlayerRemote.java @@ -269,7 +269,6 @@ else if (b.slots[b.selected] instanceof ItemMine.ItemStackMine) double cooldown2 = i2 == null ? 0 : i2.cooldown; double cooldownBase2 = i2 == null ? 0 : i2.item.cooldownBase; - System.out.println(cooldown2); if (lastLiveBullets != lb || mlb != lastMaxLiveBullets || lm != lastLiveMines || mlm != lastMaxLiveMines || didAction) Game.eventsOut.add(new EventTankControllerUpdateAmmunition(this.player.clientID, lb, mlb, lm, mlm, cooldown, cooldownBase, cooldown2, cooldownBase2)); diff --git a/src/main/resources/shaders/main_out_of_bounds.vert b/src/main/resources/shaders/main_out_of_bounds.vert index 4fdd060d9..7381b7f96 100644 --- a/src/main/resources/shaders/main_out_of_bounds.vert +++ b/src/main/resources/shaders/main_out_of_bounds.vert @@ -27,4 +27,4 @@ void getVertVecs(out vec4 pos, out vec3 normal) vec4 getColor(vec4 colorIn) { return colorIn * (1.0 - obstacleSizeFrac) + vec4(0.6823, 0.3608, 0.0628, 1.0) * obstacleSizeFrac; -} \ No newline at end of file +} From 8fcee585b42f56e615e310e0fa668a14c3b7440d Mon Sep 17 00:00:00 2001 From: orangishcat Date: Mon, 6 Oct 2025 19:15:55 -0700 Subject: [PATCH 2/2] refactor: revert unwanted changes Signed-off-by: orangishcat --- src/main/java/tanks/Game.java | 1 - src/main/java/tanks/Panel.java | 22 +-- .../java/tanks/network/NetworkHandler.java | 171 ------------------ 3 files changed, 3 insertions(+), 191 deletions(-) delete mode 100644 src/main/java/tanks/network/NetworkHandler.java diff --git a/src/main/java/tanks/Game.java b/src/main/java/tanks/Game.java index 4b5e9081f..7fbe4825e 100644 --- a/src/main/java/tanks/Game.java +++ b/src/main/java/tanks/Game.java @@ -154,7 +154,6 @@ public int hashCode() public static boolean drawFaces = false; public static boolean drawAvoidObjects = false; public static boolean recordMovableData = false; - public static boolean recordEventData = false; public static final boolean cinematic = false; public static long steamLobbyInvite = -1; diff --git a/src/main/java/tanks/Panel.java b/src/main/java/tanks/Panel.java index 72d9b4c6d..48bbfe615 100644 --- a/src/main/java/tanks/Panel.java +++ b/src/main/java/tanks/Panel.java @@ -1,7 +1,6 @@ package tanks; import basewindow.InputCodes; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import tanks.extension.Extension; import tanks.gui.*; import tanks.gui.ScreenElement.*; @@ -101,10 +100,7 @@ public class Panel /** Set to a directory to have the game screenshot the next frame and save it to that directory */ public String saveScreenshotDir = null; - public final Object2IntOpenHashMap> eventsInCount = new Object2IntOpenHashMap<>(); - public final Object2IntOpenHashMap> eventsOutCount = new Object2IntOpenHashMap<>(); - - public static void initialize() + public static void initialize() { if (!initialized) panel = new Panel(); @@ -352,23 +348,11 @@ else if (Game.deterministicMode) if (!(Game.eventsIn.get(i) instanceof IOnlineServerEvent)) { INetworkEvent e = Game.eventsIn.get(i); - INetworkEvent prev = null; if (e instanceof IStackableEvent) - prev = stackedEventsIn.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); + stackedEventsIn.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); else - Game.eventsIn.get(i).execute(); - - if (Game.recordEventData && prev == null) - { - if (e instanceof EventStackedGroup) - { - for (INetworkEvent e2 : ((EventStackedGroup) e).events) - eventsInCount.addTo(e2.getClass(), 1); - } - else - eventsInCount.addTo(e.getClass(), 1); - } + Game.eventsIn.get(i).execute(); } } diff --git a/src/main/java/tanks/network/NetworkHandler.java b/src/main/java/tanks/network/NetworkHandler.java deleted file mode 100644 index b81a71f2f..000000000 --- a/src/main/java/tanks/network/NetworkHandler.java +++ /dev/null @@ -1,171 +0,0 @@ -package tanks.network; - -import com.codedisaster.steamworks.*; -import io.netty.buffer.ByteBuf; -import io.netty.channel.*; -import io.netty.util.ReferenceCountUtil; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import tanks.*; -import tanks.network.event.*; - -import java.util.*; - -public abstract class NetworkHandler extends ChannelInboundHandlerAdapter -{ - public ChannelHandlerContext ctx; - public SteamID steamID; - - protected SynchronizedList events = new SynchronizedList<>(); - protected Int2ObjectOpenHashMap stackedEvents = new Int2ObjectOpenHashMap<>(); - - public boolean joined = false; - public boolean closed = false; - - public MessageReader reader = new MessageReader(); - - protected long lastStackedEventSend = 0; - protected LinkedHashMap, EventStackedGroup> groupedEvents = new LinkedHashMap<>(); - - public synchronized void sendEventAndClose(INetworkEvent e) - { - this.closed = true; - if (steamID != null) - Game.steamNetworkHandler.send(steamID.getAccountID(), e, SteamNetworking.P2PSend.Reliable); - else - this.sendEvent(e, true); - - if (ctx != null) - ctx.close(); - - if (steamID != null) - Game.steamNetworkHandler.queueClose(steamID.getAccountID()); - } - - public synchronized void sendEvent(INetworkEvent e) - { - sendEvent(e, true); - } - - public synchronized void sendEvent(INetworkEvent e, boolean flush) - { - if (steamID != null) - Game.steamNetworkHandler.send(steamID.getAccountID(), e, flush ? - SteamNetworking.P2PSend.Reliable : SteamNetworking.P2PSend.ReliableWithBuffering); - - if (Game.recordEventData) - { - if (e instanceof EventStackedGroup) - for (INetworkEvent e2 : ((EventStackedGroup) e).events) - Panel.panel.eventsOutCount.addTo(e2.getClass(), 1); - else - Panel.panel.eventsOutCount.addTo(e.getClass(), 1); - } - - ByteBuf b = ctx.channel().alloc().buffer(); - - int eventID = NetworkEventMap.get(e.getClass()); - if (eventID == -1) - throw new RuntimeException("The network event " + e.getClass() + " has not been registered!"); - - b.writeInt(eventID); - e.write(b); - - ByteBuf b2 = ctx.channel().alloc().buffer(); - b2.writeInt(b.readableBytes()); - MessageReader.upstreamBytes += b.readableBytes() + 4; - MessageReader.updateLastMessageTime(); - b2.writeBytes(b); - - if (flush) - ctx.channel().writeAndFlush(b2); - else - ctx.channel().write(b2); - - ReferenceCountUtil.release(b); - } - - @SuppressWarnings("unchecked") - public synchronized void stackEvent(INetworkEvent e) - { - if (!(e instanceof PersonalEvent) || e instanceof IClientThreadEvent || e instanceof IServerThreadEvent) - { - sendEvent(e, false); - return; - } - - groupedEvents.computeIfAbsent(e.getClass(), - k -> new EventStackedGroup((Class) e.getClass())).events.add((PersonalEvent) e); - } - - public synchronized void flushEvents() - { - if (groupedEvents.isEmpty()) - { - ctx.flush(); - return; - } - - int i = 0; - for (EventStackedGroup e : this.groupedEvents.values()) - { - boolean flush = i++ >= this.groupedEvents.size() - 1; - if (e.events.size() == 1) - sendEvent(e.events.get(0), flush); - else - sendEvent(e, flush); - } - groupedEvents.clear(); - } - - public void reply() - { - synchronized (this.events) - { - INetworkEvent prev = null; - for (INetworkEvent e : this.events) - { - INetworkEvent other = null; - if (e instanceof IStackableEvent && ((IStackableEvent) e).isStackable()) - other = this.stackedEvents.put(IStackableEvent.f(NetworkEventMap.get(e.getClass()) + IStackableEvent.f(((IStackableEvent) e).getIdentifier())), (IStackableEvent) e); - else - { - if (prev != null) - this.stackEvent(prev); - - prev = e; - } - if (Game.recordEventData && other == null) - Panel.panel.eventsOutCount.addTo(e.getClass(), 1); - } - - long time = System.currentTimeMillis() * Game.networkRate / 1000; - if (time != lastStackedEventSend) - { - lastStackedEventSend = time; - - if (prev != null) - this.stackEvent(prev); - - for (IStackableEvent e : this.stackedEvents.values()) - this.stackEvent(e); - - this.stackedEvents.clear(); - flushEvents(); - } - else if (prev != null) - this.stackEvent(prev); - - if (steamID == null) - this.ctx.flush(); - - this.events.clear(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - { - cause.printStackTrace(); - ctx.close(); - } -}