🆕 Added BuildTools to the Emulator

This commit is contained in:
duckietm 2025-06-03 09:12:11 +02:00
parent 9d391865af
commit 2d207bc08a
5 changed files with 110 additions and 24 deletions

View File

@ -700,7 +700,6 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
} }
this.sendComposer(new UpdateStackHeightComposer(this, updatedTiles).compose()); this.sendComposer(new UpdateStackHeightComposer(this, updatedTiles).compose());
this.updateTiles(updatedTiles); this.updateTiles(updatedTiles);
for (RoomTile tile : updatedTiles) { for (RoomTile tile : updatedTiles) {
@ -1235,7 +1234,6 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.cycleTimestamp = System.currentTimeMillis(); this.cycleTimestamp = System.currentTimeMillis();
final boolean[] foundRightHolder = {false}; final boolean[] foundRightHolder = {false};
boolean loaded; boolean loaded;
synchronized (this.loadLock) { synchronized (this.loadLock) {
loaded = this.loaded; loaded = this.loaded;
@ -1280,11 +1278,14 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
for (Habbo habbo : this.currentHabbos.values()) { for (Habbo habbo : this.currentHabbos.values()) {
RoomUnit unit = habbo.getRoomUnit(); RoomUnit unit = habbo.getRoomUnit();
// Only run loop detection if the unit is on a roller.
if (!unit.hasStatus(RoomUnitStatus.MOVE) && isOnRoller(unit)) { if (!unit.hasStatus(RoomUnitStatus.MOVE) && isOnRoller(unit)) {
// Get the current coordinates of the unit on the roller:
double curX = unit.getX(); double curX = unit.getX();
double curY = unit.getY(); double curY = unit.getY();
double curZ = unit.getZ(); double curZ = unit.getZ();
// Get or create a loop tracker for this unit.
LoopTracker tracker = loopTrackers.computeIfAbsent(unit, u -> { LoopTracker tracker = loopTrackers.computeIfAbsent(unit, u -> {
RoomTile nextTile = null; RoomTile nextTile = null;
for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) { for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) {
@ -1292,13 +1293,15 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
RoomTile potentialNextTile = this.getLayout().getTileInFront(rollerTile, roller.getRotation()); RoomTile potentialNextTile = this.getLayout().getTileInFront(rollerTile, roller.getRotation());
if (potentialNextTile != null) { if (potentialNextTile != null) {
nextTile = potentialNextTile; nextTile = potentialNextTile;
break; break; // use the first valid next tile found
} }
} }
return new LoopTracker(curX, curY, curZ, nextTile); return new LoopTracker(curX, curY, curZ, nextTile);
}); });
// Check if the units current position is the same as last cycle.
if (tracker.isSamePosition(curX, curY, curZ)) { if (tracker.isSamePosition(curX, curY, curZ)) {
// Find the roller that is on the current tile.
InteractionRoller currentRoller = null; InteractionRoller currentRoller = null;
RoomTile currentTile = this.getLayout().getTile((short) curX, (short) curY); RoomTile currentTile = this.getLayout().getTile((short) curX, (short) curY);
for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) { for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) {
@ -1308,12 +1311,16 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
// Calculate the next tile using the current roller.
RoomTile currentCalculatedNextTile = null; RoomTile currentCalculatedNextTile = null;
if (currentRoller != null) { if (currentRoller != null) {
RoomTile rollerTile = this.getLayout().getTile((short) currentRoller.getX(), (short) currentRoller.getY()); RoomTile rollerTile = this.getLayout().getTile((short) currentRoller.getX(), (short) currentRoller.getY());
currentCalculatedNextTile = this.getLayout().getTileInFront(rollerTile, currentRoller.getRotation()); currentCalculatedNextTile = this.getLayout().getTileInFront(rollerTile, currentRoller.getRotation());
} }
// Here we add a check:
// If the unit's goal is either on the roller itself or is within a small distance (epsilon) of the roller tile,
// then we assume the habbo is "almost there" and do not increment the loop counter.
if (currentTile != null && unit.getGoal() != null) { if (currentTile != null && unit.getGoal() != null) {
double distanceToGoal = Math.hypot(unit.getGoal().x - currentTile.x, unit.getGoal().y - currentTile.y); double distanceToGoal = Math.hypot(unit.getGoal().x - currentTile.x, unit.getGoal().y - currentTile.y);
final double ACCEPTABLE_DISTANCE = 0.5; // adjust as needed final double ACCEPTABLE_DISTANCE = 0.5; // adjust as needed
@ -1324,6 +1331,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
// Otherwise, use your normal loop detection logic:
// (for example, checking if the next tile contains a roller, etc.)
boolean nextTileHasRoller = false; boolean nextTileHasRoller = false;
if (currentCalculatedNextTile != null) { if (currentCalculatedNextTile != null) {
for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) { for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) {
@ -1354,6 +1363,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
} else { } else {
// Unit has moved; update tracker accordingly.
InteractionRoller currentRoller = null; InteractionRoller currentRoller = null;
RoomTile currentTile = this.getLayout().getTile((short) curX, (short) curY); RoomTile currentTile = this.getLayout().getTile((short) curX, (short) curY);
for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) { for (InteractionRoller roller : this.roomSpecialTypes.getRollers().values()) {
@ -1422,6 +1432,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.sendComposer(new RoomUserIgnoredComposer(habbo, RoomUserIgnoredComposer.UNIGNORED).compose()); this.sendComposer(new RoomUserIgnoredComposer(habbo, RoomUserIgnoredComposer.UNIGNORED).compose());
} }
// Substract 1 from the chatCounter every odd cycle, which is every (500ms * 2).
if (this.cycleOdd && habbo.getHabboStats().chatCounter.get() > 0) { if (this.cycleOdd && habbo.getHabboStats().chatCounter.get() > 0) {
habbo.getHabboStats().chatCounter.decrementAndGet(); habbo.getHabboStats().chatCounter.decrementAndGet();
} }
@ -1503,15 +1514,19 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
List<InteractionRoller> rollers = new ArrayList<>(this.roomSpecialTypes.getRollers().values()); List<InteractionRoller> rollers = new ArrayList<>(this.roomSpecialTypes.getRollers().values());
// Sort rollers using a custom comparator that uses a projection of the roller's position
rollers.sort((r1, r2) -> { rollers.sort((r1, r2) -> {
// Convert the roller's rotation into an angle in radians.
double angle1 = Math.toRadians(r1.getRotation() * 45); double angle1 = Math.toRadians(r1.getRotation() * 45);
double angle2 = Math.toRadians(r2.getRotation() * 45); double angle2 = Math.toRadians(r2.getRotation() * 45);
// Compute the movement vector for each roller (a unit vector in its direction)
double vx1 = Math.cos(angle1); double vx1 = Math.cos(angle1);
double vy1 = Math.sin(angle1); double vy1 = Math.sin(angle1);
double vx2 = Math.cos(angle2); double vx2 = Math.cos(angle2);
double vy2 = Math.sin(angle2); double vy2 = Math.sin(angle2);
// Calculate the projection of the roller's position along its movement vector
double proj1 = r1.getX() * vx1 + r1.getY() * vy1; double proj1 = r1.getX() * vx1 + r1.getY() * vy1;
double proj2 = r2.getX() * vx2 + r2.getY() * vy2; double proj2 = r2.getX() * vx2 + r2.getY() * vy2;
@ -1545,13 +1560,18 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
// itemsOnRoller.addAll(this.getItemsAt(rollerTile));
itemsOnRoller.remove(roller); itemsOnRoller.remove(roller);
if (!rollerTile.hasUnits() && itemsOnRoller.isEmpty()) if (!rollerTile.hasUnits() && itemsOnRoller.isEmpty())
return true; return true;
// RoomTile currentTile = Room.this.layout.getTile(roller.getX(), roller.getY());
RoomTile tileInFront = Room.this.layout.getTileInFront(Room.this.layout.getTile(roller.getX(), roller.getY()), roller.getRotation()); RoomTile tileInFront = Room.this.layout.getTileInFront(Room.this.layout.getTile(roller.getX(), roller.getY()), roller.getRotation());
// LOGGER.debug("Roller at: (" + roller.getX() + ", " + roller.getY() + "), Rotation: " + roller.getRotation());
// LOGGER.debug("Next tile calculated: (" + (tileInFront != null ? tileInFront.x : "NULL") + ", " + (tileInFront != null ? tileInFront.y : "NULL") + ")");
if (tileInFront == null) if (tileInFront == null)
return true; return true;
@ -1809,6 +1829,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} }
private boolean cycleRoomUnit(RoomUnit unit, RoomUnitType type) { private boolean cycleRoomUnit(RoomUnit unit, RoomUnitType type) {
boolean update = unit.needsStatusUpdate(); boolean update = unit.needsStatusUpdate();
if (unit.hasStatus(RoomUnitStatus.SIGN)) { if (unit.hasStatus(RoomUnitStatus.SIGN)) {
@ -3236,6 +3257,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
roomUnit.setPreviousLocationZ(z); roomUnit.setPreviousLocationZ(z);
this.updateRoomUnit(roomUnit); this.updateRoomUnit(roomUnit);
} }
} }
@ -5053,32 +5075,44 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
return FurnitureMovementError.NONE; return FurnitureMovementError.NONE;
} }
public FurnitureMovementError slideFurniTo(HabboItem item, RoomTile tile, int rotation) { public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, double z, Habbo actor, boolean sendUpdates, boolean checkForUnits) {
if (tile == null) {
return FurnitureMovementError.INVALID_MOVE;
}
RoomTile oldLocation = this.layout.getTile(item.getX(), item.getY()); RoomTile oldLocation = this.layout.getTile(item.getX(), item.getY());
boolean pluginHelper = false;
HabboItem topItem = this.getTopItemAt(tile.x, tile.y);
boolean magicTile = item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic; if (Emulator.getPluginManager().isRegistered(FurnitureMovedEvent.class, true)) {
FurnitureMovedEvent event = Emulator.getPluginManager().fireEvent(new FurnitureMovedEvent(item, actor, oldLocation, tile));
Optional<HabboItem> stackHelper = this.getItemsAt(tile).stream().filter(i -> i instanceof InteractionStackHelper).findAny();THashSet<RoomTile> occupiedTiles = this.layout.getTilesAt(tile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation); if(event.isCancelled()) {
return FurnitureMovementError.CANCEL_PLUGIN_MOVE;
List<Pair<RoomTile, THashSet<HabboItem>>> tileFurniList = new ArrayList<>(); }
for (RoomTile t : occupiedTiles) { pluginHelper = event.hasPluginHelper();
tileFurniList.add(Pair.create(t, this.getItemsAt(t)));
} }
if (!magicTile && !item.canStackAt(this, tileFurniList)) { boolean magicTile = item instanceof InteractionStackHelper;
return FurnitureMovementError.CANT_STACK;
}
THashSet<RoomTile> occupiedTiles = this.layout.getTilesAt(tile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation);
THashSet<RoomTile> oldOccupiedTiles = this.layout.getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); THashSet<RoomTile> oldOccupiedTiles = this.layout.getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation());
int oldRotation = item.getRotation(); if (item.getRotation() != rotation) {
item.setRotation(rotation); item.setRotation(rotation);
}
//Place at new position if(z > MAXIMUM_FURNI_HEIGHT) return FurnitureMovementError.CANT_STACK;
if(z < this.getLayout().getHeightAtSquare(tile.x, tile.y)) return FurnitureMovementError.CANT_STACK; //prevent furni going under the floor
if (Emulator.getPluginManager().isRegistered(FurnitureBuildheightEvent.class, true)) {
FurnitureBuildheightEvent event = Emulator.getPluginManager().fireEvent(new FurnitureBuildheightEvent(item, actor, 0.00, z));
if (event.hasChangedHeight()) {
z = event.getUpdatedHeight();
}
}
item.setX(tile.x);
item.setY(tile.y);
item.setZ(z);
if (magicTile) { if (magicTile) {
item.setZ(tile.z); item.setZ(tile.z);
item.setExtradata("" + item.getZ() * 100); item.setExtradata("" + item.getZ() * 100);
@ -5086,12 +5120,31 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
if (item.getZ() > MAXIMUM_FURNI_HEIGHT) { if (item.getZ() > MAXIMUM_FURNI_HEIGHT) {
item.setZ(MAXIMUM_FURNI_HEIGHT); item.setZ(MAXIMUM_FURNI_HEIGHT);
} }
double offset = this.getStackHeight(tile.x, tile.y, false, item) - item.getZ();
this.sendComposer(new FloorItemOnRollerComposer(item, null, tile, offset, this).compose()); //Update Furniture
item.onMove(this, oldLocation, tile);
item.needsUpdate(true);
Emulator.getThreading().run(item);
if(sendUpdates) {
this.sendComposer(new FloorItemUpdateComposer(item).compose());
}
//Update old & new tiles
occupiedTiles.removeAll(oldOccupiedTiles);
occupiedTiles.addAll(oldOccupiedTiles);
this.updateTiles(occupiedTiles);
//Update Habbos at old position //Update Habbos at old position
for (RoomTile t : occupiedTiles) { for (RoomTile t : occupiedTiles) {
this.updateHabbosAt(t.x, t.y); this.updateHabbosAt(
t.x,
t.y,
this.getHabbosAt(t.x, t.y)
/*.stream()
.filter(h -> !h.getRoomUnit().hasStatus(RoomUnitStatus.MOVE) || h.getRoomUnit().getGoal() == t)
.collect(Collectors.toCollection(THashSet::new))*/
);
this.updateBotsAt(t.x, t.y); this.updateBotsAt(t.x, t.y);
} }
return FurnitureMovementError.NONE; return FurnitureMovementError.NONE;

View File

@ -467,6 +467,7 @@ public class PacketManager {
this.registerHandler(Incoming.LoveLockStartConfirmEvent, LoveLockStartConfirmEvent.class); this.registerHandler(Incoming.LoveLockStartConfirmEvent, LoveLockStartConfirmEvent.class);
this.registerHandler(Incoming.RoomUnFavoriteEvent, RoomUnFavoriteEvent.class); this.registerHandler(Incoming.RoomUnFavoriteEvent, RoomUnFavoriteEvent.class);
this.registerHandler(Incoming.UseRandomStateItemEvent, UseRandomStateItemEvent.class); this.registerHandler(Incoming.UseRandomStateItemEvent, UseRandomStateItemEvent.class);
this.registerHandler(Incoming.UpdateFurniturePositionEvent, UpdateFurniturePositionEvent.class);
} }
void registerPolls() throws Exception { void registerPolls() throws Exception {

View File

@ -404,4 +404,7 @@ public class Incoming {
public static final int UNKNOWN_SNOWSTORM_6024 = 6024; public static final int UNKNOWN_SNOWSTORM_6024 = 6024;
public static final int UNKNOWN_SNOWSTORM_6025 = 6025; public static final int UNKNOWN_SNOWSTORM_6025 = 6025;
public static final int SnowStormUserPickSnowballEvent = 6026; public static final int SnowStormUserPickSnowballEvent = 6026;
// CUSTOM
public static final int UpdateFurniturePositionEvent = 10019;
} }

View File

@ -0,0 +1,29 @@
package com.eu.habbo.messages.incoming.rooms.items;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemUpdateComposer;
public class UpdateFurniturePositionEvent extends MessageHandler {
@Override
public void handle() throws Exception {
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
if (room == null) return;
int furniId = this.packet.readInt();
HabboItem item = room.getHabboItem(furniId);
if (item == null) return;
int x = this.packet.readInt();
int y = this.packet.readInt();
double z = (double) this.packet.readInt() / 10000;
int rotation = this.packet.readInt();
RoomTile tile = room.getLayout().getTile((short) x, (short) y);
room.moveFurniTo(item, tile, rotation, z, this.client.getHabbo(), true, true);
this.client.sendResponse(new FloorItemUpdateComposer(item));
}
}