🆙 Added Rebug football

-- Rebug Football Physics
-- To use rebug football physics on a ball, set its interaction_type to 'rebug_football'.
-- The default 'football' interaction uses the standard physics.
--
-- Example: Change all footballs to use rebug physics:
-- UPDATE items_base SET interaction_type = 'rebug_football' WHERE interaction_type = 'football';
--
-- Example: Change a specific ball item to use rebug physics:
-- UPDATE items_base SET interaction_type = 'rebug_football' WHERE id = <item_base_id>;
--
-- To revert back to default physics:
-- UPDATE items_base SET interaction_type = 'football' WHERE interaction_type = 'rebug_football';
This commit is contained in:
duckietm
2026-03-16 11:25:59 +01:00
parent d63ab824d2
commit 56161d36b5
4 changed files with 257 additions and 0 deletions
@@ -17,6 +17,7 @@ import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.scoreboards
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.scoreboards.InteractionBattleBanzaiScoreboardYellow;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionFootball;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionFootballGate;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionRebugFootball;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalBlue;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalGreen;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalRed;
@@ -344,6 +345,7 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("football", InteractionFootball.class));
this.interactionsList.add(new ItemInteraction("rebug_football", InteractionRebugFootball.class));
this.interactionsList.add(new ItemInteraction("football_gate", InteractionFootballGate.class));
this.interactionsList.add(new ItemInteraction("football_counter_blue", InteractionFootballScoreboardBlue.class));
this.interactionsList.add(new ItemInteraction("football_counter_green", InteractionFootballScoreboardGreen.class));
@@ -0,0 +1,54 @@
package com.eu.habbo.habbohotel.items.interactions.games.football;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.threading.runnables.RebugKickBallAction;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Rebug-style football interaction.
* Uses simplified momentum-decay physics with 180-degree bounce.
* Set interaction_type to "rebug_football" on a ball item to use this instead of the default football physics.
*/
public class InteractionRebugFootball extends InteractionDefault {
private RebugKickBallAction currentThread;
public InteractionRebugFootball(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.setExtradata("0");
}
public InteractionRebugFootball(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.setExtradata("0");
}
@Override
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
return true;
}
@Override
public boolean isWalkable() {
return true;
}
@Override
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
super.onWalkOn(roomUnit, room, objects);
if (this.currentThread != null) {
this.currentThread.dead = true;
}
boolean hasPath = !roomUnit.getPath().isEmpty();
this.currentThread = new RebugKickBallAction(this, room, roomUnit, hasPath);
Emulator.getThreading().run(this.currentThread, 50);
}
}
@@ -0,0 +1,106 @@
package com.eu.habbo.threading.runnables;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomTileState;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer;
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;
private final Room room;
private Direction8 direction;
private int momentum;
public boolean dead = false;
public RebugKickBallAction(HabboItem ball, Room room, RoomUnit kicker, boolean hasPath) {
this.ball = ball;
this.room = room;
this.direction = Direction8.fromDelta(
ball.getX() - kicker.getX(),
ball.getY() - kicker.getY()
);
this.momentum = hasPath ? 55 : 0;
}
private boolean isTileBlocked(int x, int y) {
RoomTile tile = this.room.getLayout().getTile((short) x, (short) y);
if (tile == null) return true;
if (tile.hasUnits()) return true;
return tile.getState() != RoomTileState.OPEN;
}
@Override
public void run() {
if (this.dead || !this.room.isLoaded()) return;
try {
int nextX = this.ball.getX() + this.direction.getDiffX();
int nextY = this.ball.getY() + this.direction.getDiffY();
if (isTileBlocked(nextX, nextY)) {
this.direction = this.direction.rotateDirection180Degrees();
nextX = this.ball.getX() + this.direction.getDiffX();
nextY = this.ball.getY() + this.direction.getDiffY();
}
RoomTile nextTile = this.room.getLayout().getTile((short) nextX, (short) nextY);
if (nextTile == null) 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.setX(nextTile.x);
this.ball.setY(nextTile.y);
this.ball.setZ(nextTile.getStackHeight());
this.ball.needsUpdate(true);
// Schedule next movement based on momentum
long delay = getDelayForMomentum(this.momentum);
if (delay > 0) {
Emulator.getThreading().run(this, delay);
}
// Update tiles
this.room.updateTile(oldTile);
this.room.updateTile(nextTile);
THashSet<HabboItem> oldItems = this.room.getItemsAt(oldTile);
if (oldItems != null && !oldItems.isEmpty()) {
oldItems.remove(this.ball);
}
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;
}
}
private long getDelayForMomentum(int momentum) {
switch (momentum) {
case 55: return 100L;
case 44: return 100L;
case 33: return 200L;
case 22: return 250L;
case 11: return 500L;
default: return 0L;
}
}
}
@@ -0,0 +1,95 @@
package com.eu.habbo.util.pathfinding;
/**
* 8-directional movement utility for ball physics.
* Ported from the Rebug soccer plugin.
*/
public class Direction8 {
public static final Direction8[] DIRECTIONS = new Direction8[8];
public static final Direction8 N = new Direction8(0, "N", 0, -1);
public static final Direction8 NE = new Direction8(1, "NE", 1, -1);
public static final Direction8 E = new Direction8(2, "E", 1, 0);
public static final Direction8 SE = new Direction8(3, "SE", 1, 1);
public static final Direction8 S = new Direction8(4, "S", 0, 1);
public static final Direction8 SW = new Direction8(5, "SW", -1, 1);
public static final Direction8 W = new Direction8(6, "W", -1, 0);
public static final Direction8 NW = new Direction8(7, "NW", -1, -1);
private final int rot;
private final String rotName;
private final int xDiff;
private final int yDiff;
public Direction8(int rot, String rotName, int diffX, int diffY) {
this.rot = rot;
this.rotName = rotName;
this.xDiff = diffX;
this.yDiff = diffY;
DIRECTIONS[rot] = this;
}
public static Direction8 fromDelta(int deltaX, int deltaY) {
if (deltaX == 0) {
if (deltaY < 0) return N;
if (deltaY > 0) return S;
}
if (deltaX > 0) {
if (deltaY < 0) return NE;
if (deltaY == 0) return E;
if (deltaY > 0) return SE;
}
if (deltaX < 0) {
if (deltaY < 0) return NW;
if (deltaY == 0) return W;
if (deltaY > 0) return SW;
}
return N;
}
public static Direction8 getDirection(int dir) {
if (dir < 0 || dir > 7) return N;
return DIRECTIONS[dir];
}
public static int validateDirection8Value(int dir) {
return dir & 0x7;
}
public int getRot() {
return this.rot;
}
public String getRotName() {
return this.rotName;
}
public int getDiffX() {
return this.xDiff;
}
public int getDiffY() {
return this.yDiff;
}
public Direction8 rotateDirection180Degrees() {
return getDirectionAtRot(4);
}
public Direction8 rotateDirection90Degrees(boolean clockwise) {
return getDirectionAtRot(clockwise ? 2 : -2);
}
public Direction8 rotateDirection45Degrees(boolean clockwise) {
return getDirectionAtRot(clockwise ? 1 : -1);
}
public Direction8 getDirectionAtRot(int diff) {
return DIRECTIONS[validateDirection8Value(this.rot + diff)];
}
@Override
public String toString() {
return this.rotName + "(" + this.rot + ")";
}
}