diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionRebugFootball.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionRebugFootball.java index 969ba5b2..4974c8fc 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionRebugFootball.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionRebugFootball.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomLayout; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer; import com.eu.habbo.threading.runnables.RebugKickBallAction; import com.eu.habbo.util.pathfinding.Direction8; @@ -46,12 +47,10 @@ public class InteractionRebugFootball extends InteractionDefault { Direction8 userDir = Direction8.getDirection(roomUnit.getBodyRotation().getValue()); this.lastDribbleDirection = userDir; - // If this tile is the user's final destination, they'll stop here → long shot RoomTile goal = roomUnit.getGoal(); if (goal != null && goal.x == this.getX() && goal.y == this.getY()) { this.kick(room, roomUnit, 55); } else { - // Dribble: ball moves 1 tile ahead of the user this.kick(room, roomUnit, 0); } } @@ -68,18 +67,18 @@ public class InteractionRebugFootball extends InteractionDefault { int dy = nextTile.y - fromTile.y; Direction8 walkDir = Direction8.fromDelta(dx, dy); - // User is walking in the same direction as the ball was dribbled → long shot if (this.lastDribbleDirection != null && walkDir.getRot() == this.lastDribbleDirection.getRot()) { this.kick(room, roomUnit, 55); return; } } - // Walking sideways or away → just stop the ball if (this.currentThread != null) { this.currentThread.dead = true; this.currentThread = null; } + this.setExtradata("0"); + room.sendComposer(new ItemStateComposer(this).compose()); } @Override @@ -89,18 +88,21 @@ public class InteractionRebugFootball extends InteractionDefault { if (client == null) return; RoomUnit unit = client.getHabbo().getRoomUnit(); if (RoomLayout.tilesAdjecent(unit.getCurrentLocation(), room.getLayout().getTile(this.getX(), this.getY()))) { - // Long shot when clicking the ball this.kick(room, unit, 55); } } private void kick(Room room, RoomUnit kicker, int momentum) { + boolean wasMoving = this.currentThread != null && !this.currentThread.dead && !this.currentThread.isDribble(); + if (this.currentThread != null) { this.currentThread.dead = true; } Direction8 direction = Direction8.getDirection(kicker.getBodyRotation().getValue()); - this.currentThread = new RebugKickBallAction(this, room, direction, momentum); + boolean zigzag = wasMoving && momentum > 0; + + this.currentThread = new RebugKickBallAction(this, room, direction, momentum, zigzag); Emulator.getThreading().run(this.currentThread, 50); } } diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/RebugKickBallAction.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/RebugKickBallAction.java index ae098e05..aebc389b 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/RebugKickBallAction.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/RebugKickBallAction.java @@ -7,13 +7,10 @@ import com.eu.habbo.habbohotel.rooms.RoomTileState; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; +import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer; import com.eu.habbo.util.pathfinding.Direction8; import gnu.trove.set.hash.THashSet; -/** - * Alternative football physics based on the Rebug plugin. - * Uses momentum decay (ball slows down over time) and simple 180-degree bounce. - */ public class RebugKickBallAction implements Runnable { private final HabboItem ball; @@ -23,21 +20,40 @@ public class RebugKickBallAction implements Runnable { private boolean isDribble; public boolean dead = false; - // Track tiles traveled since last wall bounce for user-bounce logic - private int tilesSinceBounce = -1; // -1 = no bounce has happened yet + private final boolean zigzag; + private Direction8 zigzagA; + private Direction8 zigzagB; + private boolean zigzagSide = false; + + private int tilesSinceBounce = -1; public RebugKickBallAction(HabboItem ball, Room room, Direction8 direction, int momentum) { + this(ball, room, direction, momentum, false); + } + + public RebugKickBallAction(HabboItem ball, Room room, Direction8 direction, int momentum, boolean zigzag) { this.ball = ball; this.room = room; this.direction = direction; this.momentum = momentum; this.isDribble = (momentum == 0); + this.zigzag = zigzag && !this.isDribble; + + if (this.zigzag) { + this.zigzagA = direction.rotateDirection45Degrees(false); + this.zigzagB = direction.rotateDirection45Degrees(true); + } + } + + public boolean isDribble() { + return this.isDribble; } private boolean isTileBlocked(int x, int y) { RoomTile tile = this.room.getLayout().getTile((short) x, (short) y); if (tile == null) return true; - return tile.getState() != RoomTileState.OPEN; + if (tile.getState() != RoomTileState.OPEN) return true; + return x == this.room.getLayout().getDoorX() && y == this.room.getLayout().getDoorY(); } @Override @@ -45,35 +61,87 @@ public class RebugKickBallAction implements Runnable { if (this.dead || !this.room.isLoaded()) return; try { - int nextX = this.ball.getX() + this.direction.getDiffX(); - int nextY = this.ball.getY() + this.direction.getDiffY(); + int nextX; + int nextY; + Direction8 moveDir; - if (isTileBlocked(nextX, nextY)) { - this.direction = this.direction.rotateDirection180Degrees(); - this.tilesSinceBounce = 0; + if (this.zigzag) { + Direction8 preferred = this.zigzagSide ? this.zigzagB : this.zigzagA; + Direction8 fallback = this.zigzagSide ? this.zigzagA : this.zigzagB; + + nextX = this.ball.getX() + preferred.getDiffX(); + nextY = this.ball.getY() + preferred.getDiffY(); + + if (isTileBlocked(nextX, nextY)) { + nextX = this.ball.getX() + fallback.getDiffX(); + nextY = this.ball.getY() + fallback.getDiffY(); + + if (isTileBlocked(nextX, nextY)) { + nextX = this.ball.getX() + this.direction.getDiffX(); + nextY = this.ball.getY() + this.direction.getDiffY(); + + if (isTileBlocked(nextX, nextY)) { + this.stopBall(); + return; + } + moveDir = this.direction; + } else { + moveDir = fallback; + } + } else { + moveDir = preferred; + this.zigzagSide = !this.zigzagSide; + } + } else { nextX = this.ball.getX() + this.direction.getDiffX(); nextY = this.ball.getY() + this.direction.getDiffY(); + + if (isTileBlocked(nextX, nextY)) { + int dx = this.direction.getDiffX(); + int dy = this.direction.getDiffY(); + + if (dx != 0 && dy != 0) { + boolean xBlocked = isTileBlocked(this.ball.getX() + dx, this.ball.getY()); + boolean yBlocked = isTileBlocked(this.ball.getX(), this.ball.getY() + dy); + + if (xBlocked && !yBlocked) { + this.direction = Direction8.fromDelta(-dx, dy); + } else if (!xBlocked && yBlocked) { + this.direction = Direction8.fromDelta(dx, -dy); + } else { + this.direction = this.direction.rotateDirection180Degrees(); + } + } else { + this.direction = this.direction.rotateDirection180Degrees(); + } + + this.tilesSinceBounce = 0; + nextX = this.ball.getX() + this.direction.getDiffX(); + nextY = this.ball.getY() + this.direction.getDiffY(); + } + moveDir = this.direction; } RoomTile nextTile = this.room.getLayout().getTile((short) nextX, (short) nextY); - if (nextTile == null) return; + if (nextTile == null) { + this.stopBall(); + return; + } RoomTile oldTile = this.room.getLayout().getTile(this.ball.getX(), this.ball.getY()); double oldZ = this.ball.getZ(); - this.ball.setRotation(this.direction.getRot()); + this.ball.setRotation(moveDir.getRot()); this.ball.setX(nextTile.x); this.ball.setY(nextTile.y); this.ball.setZ(nextTile.getStackHeight()); this.ball.needsUpdate(true); - // Count tiles since bounce - if (this.tilesSinceBounce >= 0) { + if (!this.zigzag && this.tilesSinceBounce >= 0) { this.tilesSinceBounce++; } - // After bouncing, if ball has traveled more than 1 tile from the wall, bounce off users - if (this.tilesSinceBounce > 1 && !this.isDribble) { + if (!this.zigzag && this.tilesSinceBounce > 1 && !this.isDribble) { THashSet habbos = this.room.getHabbosAt(nextTile.x, nextTile.y); if (!habbos.isEmpty()) { this.direction = this.direction.rotateDirection180Degrees(); @@ -81,19 +149,22 @@ public class RebugKickBallAction implements Runnable { } } - // Schedule next movement + this.ball.setExtradata(this.isDribble ? "2" : "5"); + this.room.sendComposer(new ItemStateComposer(this.ball).compose()); + + this.momentum -= 11; + if (!this.isDribble) { long delay = getDelayForMomentum(this.momentum); if (delay > 0) { Emulator.getThreading().run(this, delay); } else { - this.dead = true; + this.stopBall(); } } else { this.dead = true; } - // Update tiles this.room.updateTile(oldTile); this.room.updateTile(nextTile); @@ -103,18 +174,20 @@ public class RebugKickBallAction implements Runnable { } this.room.getItemsAt(nextTile).add(this.ball); - // Send rolling animation this.room.sendComposer(new FloorItemOnRollerComposer( this.ball, null, oldTile, oldZ, nextTile, this.ball.getZ(), 0.0D, this.room ).compose()); - - // Decay momentum - this.momentum -= 11; } catch (Exception e) { - this.dead = true; + this.stopBall(); } } + private void stopBall() { + this.dead = true; + this.ball.setExtradata("0"); + this.room.sendComposer(new ItemStateComposer(this.ball).compose()); + } + private long getDelayForMomentum(int momentum) { switch (momentum) { case 55: return 100L;