diff --git a/Emulator/.idea/misc.xml b/Emulator/.idea/misc.xml index 5ddb3b31..6568344f 100644 --- a/Emulator/.idea/misc.xml +++ b/Emulator/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/Emulator/plugins/NitroWebsockets-3.1.jar b/Emulator/NitroWebsockets-3.1.jar similarity index 100% rename from Emulator/plugins/NitroWebsockets-3.1.jar rename to Emulator/NitroWebsockets-3.1.jar diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 11f29086..edca543c 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -56,6 +56,8 @@ public final class Emulator { public static String build = ""; public static long buildTimestamp = -1L; + + public static boolean isReady = false; public static boolean isShuttingDown = false; public static boolean stopped = false; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java index a417038a..d4ac19c7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java @@ -50,8 +50,12 @@ import com.eu.habbo.habbohotel.items.interactions.totems.InteractionTotemPlanet; import com.eu.habbo.habbohotel.items.interactions.wired.conditions.*; import com.eu.habbo.habbohotel.items.interactions.wired.effects.*; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimationTime; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhysics; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen; @@ -341,6 +345,10 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_xtra_filter_furni", WiredExtraFilterFurni.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_filter_user", WiredExtraFilterUser.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_filter_users", WiredExtraFilterUser.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_carry_users", WiredExtraMoveCarryUsers.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_no_animation", WiredExtraMoveNoAnimation.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_anim_time", WiredExtraAnimationTime.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_physics", WiredExtraMovePhysics.class)); this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java index fa9b5182..611b06d1 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java @@ -10,10 +10,10 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.*; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; @@ -22,6 +22,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public static final int ACTION_WAIT = 0; @@ -35,6 +36,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.MOVE_DIRECTION; private final THashMap items = new THashMap<>(0); + private final ConcurrentHashMap runtimeItems = new ConcurrentHashMap<>(); private RoomUserRotation startRotation = RoomUserRotation.NORTH; private int blockedAction = 0; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; @@ -57,6 +59,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { THashMap effectiveItems; if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.runtimeItems.clear(); THashSet toRemove = new THashSet<>(); for (HabboItem item : this.items.keySet()) { if (item == null || Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null) @@ -67,10 +70,13 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { } effectiveItems = this.items; } else { + this.pruneRuntimeItems(room); effectiveItems = new THashMap<>(); for (HabboItem item : resolvedItems) { if (item != null) { - WiredChangeDirectionSetting setting = this.items.get(item); + WiredChangeDirectionSetting setting = this.runtimeItems.computeIfAbsent( + item.getId(), + key -> new WiredChangeDirectionSetting(item.getId(), item.getRotation(), this.startRotation)); if (setting == null) { setting = new WiredChangeDirectionSetting(item.getId(), item.getRotation(), this.startRotation); } @@ -91,7 +97,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { RoomTile targetTile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); int count = 1; - while (this.shouldSearchNextDirection(room, item, targetTile) && count < 8) { + while (this.shouldSearchNextDirection(room, item, targetTile, ctx) && count < 8) { entry.getValue().direction = this.nextRotation(entry.getValue().direction); RoomTile tile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); @@ -115,35 +121,30 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { RoomTile targetTile = room.getLayout().getTileInFront(itemTile, newDirection); if(item.getRotation() != entry.getValue().rotation) { - if(targetTile == null || room.furnitureFitsAt(targetTile, item, entry.getValue().rotation, false) != FurnitureMovementError.NONE) + if (targetTile == null || room.furnitureFitsAt(targetTile, item, entry.getValue().rotation, false) != FurnitureMovementError.NONE) continue; - room.moveFurniTo(entry.getKey(), targetTile, entry.getValue().rotation, null, true); + WiredMoveCarryHelper.moveFurni(room, this, entry.getKey(), targetTile, entry.getValue().rotation, null, true, ctx); } if (targetTile == null) continue; - boolean hasRoomUnits = false; - THashSet newOccupiedTiles = room.getLayout().getTilesAt(targetTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - for(RoomTile tile : newOccupiedTiles) { - for (RoomUnit _roomUnit : room.getRoomUnits(tile)) { - hasRoomUnits = true; - if(_roomUnit.getCurrentLocation() == targetTile) { - Emulator.getThreading().run(() -> { - WiredManager.triggerBotCollision(room, _roomUnit); - }); + FurnitureMovementError movementError = WiredMoveCarryHelper.getMovementError(room, this, entry.getKey(), targetTile, item.getRotation(), ctx); + + if (movementError == FurnitureMovementError.TILE_HAS_HABBOS + || movementError == FurnitureMovementError.TILE_HAS_BOTS + || movementError == FurnitureMovementError.TILE_HAS_PETS) { + Emulator.getThreading().run(() -> { + for (RoomUnit roomUnit : room.getRoomUnits(targetTile)) { + WiredManager.triggerBotCollision(room, roomUnit); break; } - } + }); } - if (targetTile.state != RoomTileState.INVALID && room.furnitureFitsAt(targetTile, item, item.getRotation(), false) == FurnitureMovementError.NONE) { - if (!hasRoomUnits) { - RoomTile oldLocation = room.getLayout().getTile(entry.getKey().getX(), entry.getKey().getY()); - double oldZ = entry.getKey().getZ(); - if(oldLocation != null && room.moveFurniTo(entry.getKey(), targetTile, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(entry.getKey(), null, oldLocation, oldZ, targetTile, entry.getKey().getZ(), 0, room).compose()); - } + if (targetTile.state != RoomTileState.INVALID && movementError == FurnitureMovementError.NONE) { + RoomTile oldLocation = room.getLayout().getTile(entry.getKey().getX(), entry.getKey().getY()); + if (oldLocation != null && WiredMoveCarryHelper.moveFurni(room, this, entry.getKey(), targetTile, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } @@ -165,6 +166,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.runtimeItems.clear(); String wiredData = set.getString("wired_data"); @@ -228,6 +230,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public void onPickUp() { this.setDelay(0); this.items.clear(); + this.runtimeItems.clear(); this.blockedAction = 0; this.startRotation = RoomUserRotation.NORTH; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; @@ -305,6 +308,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.items.clear(); + this.runtimeItems.clear(); if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { this.items.putAll(newItems); } @@ -315,7 +319,15 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { return true; } - private boolean shouldSearchNextDirection(Room room, HabboItem item, RoomTile targetTile) { + private void pruneRuntimeItems(Room room) { + if (room == null || this.runtimeItems.isEmpty()) { + return; + } + + this.runtimeItems.entrySet().removeIf(entry -> room.getHabboItem(entry.getKey()) == null); + } + + private boolean shouldSearchNextDirection(Room room, HabboItem item, RoomTile targetTile, WiredContext ctx) { if (targetTile == null || targetTile.state == RoomTileState.INVALID) { return true; } @@ -328,7 +340,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { return false; } - FurnitureMovementError unitCollision = room.furnitureFitsAt(targetTile, item, item.getRotation(), true); + FurnitureMovementError unitCollision = WiredMoveCarryHelper.getMovementError(room, this, item, targetTile, item.getRotation(), ctx); return unitCollision == FurnitureMovementError.TILE_HAS_HABBOS; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java index 6102fa14..ed8bf0a9 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java @@ -12,10 +12,10 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import java.sql.ResultSet; import java.sql.SQLException; @@ -65,18 +65,14 @@ public class WiredEffectFurniToFurni extends InteractionWiredEffect { return; } - RoomTile oldLocation = room.getLayout().getTile(moveItem.getX(), moveItem.getY()); - double oldZ = moveItem.getZ(); - - FurnitureMovementError error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), null, false, false); + FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, moveItem, targetTile, moveItem.getRotation(), null, false, ctx); if (error == FurnitureMovementError.NONE) { - this.sendRollerAnimation(room, moveItem, oldLocation, oldZ, targetTile); return; } - error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, false, false); + error = WiredMoveCarryHelper.moveFurni(room, this, moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, false, ctx); if (error == FurnitureMovementError.NONE) { - this.sendRollerAnimation(room, moveItem, oldLocation, oldZ, targetTile); + return; } } @@ -232,14 +228,6 @@ public class WiredEffectFurniToFurni extends InteractionWiredEffect { return type; } - private void sendRollerAnimation(Room room, HabboItem item, RoomTile oldLocation, double oldZ, RoomTile newLocation) { - if (room == null || item == null || oldLocation == null || newLocation == null) { - return; - } - - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } - @Override protected long requiredCooldown() { return COOLDOWN_MOVEMENT; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java index 462fbddf..9d4fad7d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java @@ -8,7 +8,7 @@ import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,19 +39,15 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { return; } - RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - double oldZ = item.getZ(); - - FurnitureMovementError error = room.moveFurniTo(item, targetTile, item.getRotation(), null, false, false); + FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), null, false, ctx); if (error == FurnitureMovementError.NONE) { - this.sendRollerAnimation(room, item, oldLocation, oldZ, targetTile); return; } if (item.getBaseItem().getStateCount() > 0) { - error = room.moveFurniTo(item, targetTile, item.getRotation(), item.getZ(), null, false, false); + error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), item.getZ(), null, false, ctx); if (error == FurnitureMovementError.NONE) { - this.sendRollerAnimation(room, item, oldLocation, oldZ, targetTile); + return; } } } @@ -67,11 +63,4 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { return type; } - private void sendRollerAnimation(Room room, HabboItem item, RoomTile oldLocation, double oldZ, RoomTile newLocation) { - if (room == null || item == null || oldLocation == null || newLocation == null) { - return; - } - - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java index 894058cb..dd616217 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java @@ -12,10 +12,10 @@ import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredMatchFurniSetting; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +64,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int for (WiredMatchFurniSetting setting : this.settings) { HabboItem item = room.getHabboItem(setting.item_id); if (item != null) { - this.applySetting(room, item, setting); + this.applySetting(room, item, setting, ctx); } } @@ -82,7 +82,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); if (setting == null) continue; - this.applySetting(room, item, setting); + this.applySetting(room, item, setting, ctx); } } @@ -106,7 +106,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int return fallback; } - private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting) { + private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting, WiredContext ctx) { if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) { if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) { item.setExtradata(setting.state); @@ -117,17 +117,16 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); if (oldLocation == null) return; - double oldZ = item.getZ(); if(this.direction && !this.position) { if(item.getRotation() != setting.rotation && room.furnitureFitsAt(oldLocation, item, setting.rotation, false) == FurnitureMovementError.NONE) { - room.moveFurniTo(item, oldLocation, setting.rotation, null, true); + WiredMoveCarryHelper.moveFurni(room, this, item, oldLocation, setting.rotation, null, true, ctx); } } else if(this.altitude && !this.position) { int newRotation = this.direction ? setting.rotation : item.getRotation(); if(BigDecimal.valueOf(item.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0 || newRotation != item.getRotation()) { - room.moveFurniTo(item, oldLocation, newRotation, setting.z, null, true, false); + WiredMoveCarryHelper.moveFurni(room, this, item, oldLocation, newRotation, setting.z, null, true, ctx); } } else if(this.position) { @@ -136,11 +135,10 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int int newRotation = this.direction ? setting.rotation : item.getRotation(); double newZ = this.altitude ? setting.z : item.getZ(); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) && room.furnitureFitsAt(newLocation, item, newRotation, true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(item, newLocation, newRotation, newZ, null, !slideAnimation, true) == FurnitureMovementError.NONE) { - if(slideAnimation) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } + if (newLocation != null && newLocation.state != RoomTileState.INVALID + && (newLocation != oldLocation || newRotation != item.getRotation()) + && WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, newRotation, ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, newRotation, newZ, null, !slideAnimation, ctx) == FurnitureMovementError.NONE) { } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java index 3cd56464..63d01717 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java @@ -10,11 +10,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -98,11 +98,10 @@ public class WiredEffectMoveFurniAway extends InteractionWiredEffect { RoomTile newLocation = room.getLayout().getTile((short) (item.getX() + x), (short) (item.getY() + y)); RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - double oldZ = item.getZ(); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && newLocation != oldLocation && room.furnitureFitsAt(newLocation, item, item.getRotation(), true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(item, newLocation, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); + if (newLocation != null && newLocation.state != RoomTileState.INVALID && newLocation != oldLocation + && WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, item.getRotation(), ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java index d2087c9f..ecdc90a7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java @@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.FurnitureMovementError; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; @@ -12,13 +13,12 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; - import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -112,11 +112,6 @@ public class WiredEffectMoveFurniTo extends InteractionWiredEffect { RoomTile objectTile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); if (objectTile != null) { - RoomTile sourceTile = room.getLayout().getTile(((HabboItem) object).getX(), ((HabboItem) object).getY()); - if (sourceTile == null) continue; - - THashSet refreshTiles = room.getLayout().getTilesAt(sourceTile, ((HabboItem) object).getBaseItem().getWidth(), ((HabboItem) object).getBaseItem().getLength(), ((HabboItem) object).getRotation()); - RoomTile tile = room.getLayout().getTileInFront(objectTile, this.direction, indexOffset); if (tile == null || !tile.getAllowStack()) { indexOffset = 0; @@ -127,13 +122,18 @@ public class WiredEffectMoveFurniTo extends InteractionWiredEffect { continue; } - room.sendComposer(new FloorItemOnRollerComposer((HabboItem) object, null, tile, tile.getStackHeight() - ((HabboItem) object).getZ(), room).compose()); - - RoomTile newSourceTile = room.getLayout().getTile(((HabboItem) object).getX(), ((HabboItem) object).getY()); - if (newSourceTile != null) { - refreshTiles.addAll(room.getLayout().getTilesAt(newSourceTile, ((HabboItem) object).getBaseItem().getWidth(), ((HabboItem) object).getBaseItem().getLength(), ((HabboItem) object).getRotation())); + HabboItem movingItem = (HabboItem) object; + RoomTile oldLocation = room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + if (oldLocation == null) { + continue; } - room.updateTiles(refreshTiles); + + FurnitureMovementError movementError = WiredMoveCarryHelper.moveFurni(room, this, movingItem, tile, movingItem.getRotation(), null, false, ctx); + + if (movementError != FurnitureMovementError.NONE) { + continue; + } + this.indexOffset.put(targetItem.getId(), indexOffset); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java index d01a9fc5..be02d463 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java @@ -10,11 +10,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import com.eu.habbo.threading.runnables.WiredCollissionRunnable; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; @@ -53,7 +53,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { this.lastDirections = new THashMap<>(); } - public List getAvailableDirections(HabboItem item, Room room) { + public List getAvailableDirections(HabboItem item, Room room, WiredContext ctx) { List availableDirections = new ArrayList<>(); RoomLayout layout = room.getLayout(); @@ -73,7 +73,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { if (!layout.tileExists(tile.x, tile.y)) continue; - if (room.furnitureFitsAt(tile, item, item.getRotation()) == FurnitureMovementError.INVALID_MOVE) + if (WiredMoveCarryHelper.getMovementError(room, this, item, tile, item.getRotation(), ctx) != FurnitureMovementError.NONE) continue; HabboItem topItem = room.getTopItemAt(tile.x, tile.y); @@ -192,7 +192,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { 3+ available - move in random direction, but never the opposite */ - List availableDirections = this.getAvailableDirections(item, room); + List availableDirections = this.getAvailableDirections(item, room, ctx); if (moveDirection != null && !availableDirections.contains(moveDirection)) moveDirection = null; @@ -228,13 +228,11 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { RoomTile newTile = room.getLayout().getTileInFront(oldLocation, moveDirection.getValue()); - double oldZ = item.getZ(); - if(newTile != null) { lastDirections.put(item.getId(), moveDirection); - if(newTile.state != RoomTileState.INVALID && newTile != oldLocation && room.furnitureFitsAt(newTile, item, item.getRotation(), true) == FurnitureMovementError.NONE) { - if (room.moveFurniTo(item, newTile, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newTile, item.getZ(), 0, room).compose()); + if (newTile.state != RoomTileState.INVALID && newTile != oldLocation + && WiredMoveCarryHelper.getMovementError(room, this, item, newTile, item.getRotation(), ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newTile, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java index b9f43fc3..26bbe3ab 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java @@ -11,11 +11,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -61,7 +61,6 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement int newRotation = this.rotation > 0 ? this.getNewRotation(item) : item.getRotation(); RoomTile newLocation = room.getLayout().getTile(item.getX(), item.getY()); RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - double oldZ = item.getZ(); if(this.direction > 0) { // Use pre-selected direction if available, otherwise pick random @@ -77,13 +76,14 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement boolean slideAnimation = item.getRotation() == newRotation; - FurnitureMovementError furniMoveTest = room.furnitureFitsAt(newLocation, item, newRotation, true); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) && (furniMoveTest == FurnitureMovementError.NONE || ((furniMoveTest == FurnitureMovementError.TILE_HAS_BOTS || furniMoveTest == FurnitureMovementError.TILE_HAS_HABBOS || furniMoveTest == FurnitureMovementError.TILE_HAS_PETS) && newLocation == oldLocation))) { - if(room.furnitureFitsAt(newLocation, item, newRotation, false) == FurnitureMovementError.NONE && room.moveFurniTo(item, newLocation, newRotation, null, !slideAnimation) == FurnitureMovementError.NONE) { + FurnitureMovementError furniMoveTest = WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, newRotation, ctx); + if (newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) + && (furniMoveTest == FurnitureMovementError.NONE + || ((furniMoveTest == FurnitureMovementError.TILE_HAS_BOTS + || furniMoveTest == FurnitureMovementError.TILE_HAS_HABBOS + || furniMoveTest == FurnitureMovementError.TILE_HAS_PETS) && newLocation == oldLocation))) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, newRotation, null, !slideAnimation, ctx) == FurnitureMovementError.NONE) { this.itemCooldowns.add(item); - if(slideAnimation) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java index 4ef1c57f..c08487fb 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java @@ -14,10 +14,11 @@ import com.eu.habbo.habbohotel.rooms.RoomUserRotation; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; import gnu.trove.procedure.TObjectProcedure; import java.sql.ResultSet; @@ -53,21 +54,26 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect { } boolean hasRotation = this.rotationDirection >= 0; + RoomUserRotation targetBodyRotation = hasRotation ? this.getTargetRotation(roomUnit) : roomUnit.getBodyRotation(); + RoomUserRotation targetHeadRotation = hasRotation ? targetBodyRotation : roomUnit.getHeadRotation(); RoomTile targetTile = (this.movementDirection >= 0) ? this.getTargetTile(room, roomUnit, this.movementDirection) : null; boolean canMove = this.canMoveTo(room, roomUnit, targetTile); - - if (hasRotation) { - roomUnit.setRotation(this.getTargetRotation(roomUnit)); - } + boolean noAnimation = WiredMoveCarryHelper.hasNoAnimationExtra(room, this); if (canMove) { double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0); - room.teleportRoomUnitToLocation(roomUnit, targetTile.x, targetTile.y, targetZ); + int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION); + if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, targetBodyRotation, targetHeadRotation, + animationDuration, noAnimation)) { + if (hasRotation) { + WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation); + } + } continue; } if (hasRotation) { - room.sendComposer(new RoomUserStatusComposer(roomUnit).compose()); + WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation); } } } @@ -209,9 +215,6 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect { private RoomTile getTargetTile(Room room, RoomUnit roomUnit, int direction) { RoomTile currentTile = roomUnit.getCurrentLocation(); - if (currentTile == null) { - currentTile = room.getLayout().getTile(roomUnit.getX(), roomUnit.getY()); - } if (currentTile == null) { return null; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java index e2fcf249..9ba042d5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java @@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; @@ -81,7 +82,7 @@ public class WiredEffectRelativeMove extends InteractionWiredEffect { continue; } - room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false); + WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), null, true, ctx); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java index e43a4a68..9233daed 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java @@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; @@ -73,7 +74,7 @@ public class WiredEffectSetAltitude extends InteractionWiredEffect { } double nextAltitude = this.computeAltitude(item.getZ()); - room.moveFurniTo(item, tile, item.getRotation(), nextAltitude, null, true, false); + WiredMoveCarryHelper.moveFurni(room, this, item, tile, item.getRotation(), nextAltitude, null, true, ctx); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java index 667400c5..2d2491d5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java @@ -8,15 +8,17 @@ import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitOnRollerComposer; import gnu.trove.procedure.TObjectProcedure; import java.sql.ResultSet; @@ -234,39 +236,58 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { RoomTile oldLocation = roomUnit.getCurrentLocation(); RoomTile previousGoal = roomUnit.getGoal(); boolean wasWalking = roomUnit.isWalking(); - - if (oldLocation == null) { - oldLocation = room.getLayout().getTile(roomUnit.getX(), roomUnit.getY()); - } + boolean noAnimation = WiredMoveCarryHelper.hasNoAnimationExtra(room, this); if (oldLocation == null) { return; } - double oldZ = roomUnit.getZ(); double newZ = item.getZ() + Item.getCurrentHeight(item); - room.sendComposer(new RoomUnitOnRollerComposer(roomUnit, null, oldLocation, oldZ, targetTile, newZ, room).compose()); + int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION); + if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, newZ, + roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), animationDuration, noAnimation)) { + return; + } - this.applyWalkMode(roomUnit, oldLocation, previousGoal, targetTile, wasWalking); - roomUnit.setPreviousLocationZ(newZ); + this.applyWalkMode(roomUnit, oldLocation, previousGoal, targetTile, wasWalking, + animationDuration); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); } - private void applyWalkMode(RoomUnit roomUnit, RoomTile oldLocation, RoomTile previousGoal, RoomTile targetTile, boolean wasWalking) { + private void applyWalkMode(RoomUnit roomUnit, RoomTile oldLocation, RoomTile previousGoal, RoomTile targetTile, boolean wasWalking, int delay) { if (roomUnit == null || targetTile == null) { return; } - if (this.walkMode == WALKMODE_STOP || !wasWalking || previousGoal == null) { - roomUnit.setGoalLocation(targetTile); + Runnable applyGoal = () -> { + if (roomUnit.getCurrentLocation() == null + || roomUnit.isWalking() + || roomUnit.hasStatus(RoomUnitStatus.SIT) + || roomUnit.hasStatus(RoomUnitStatus.LAY) + || roomUnit.getCurrentLocation().x != targetTile.x + || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + if (this.walkMode == WALKMODE_STOP || !wasWalking || previousGoal == null) { + roomUnit.setGoalLocation(targetTile); + return; + } + + if (this.walkMode == WALKMODE_IF_CLOSER && !this.isCloserToGoal(oldLocation, targetTile, previousGoal)) { + roomUnit.setGoalLocation(targetTile); + return; + } + + roomUnit.setGoalLocation(previousGoal); + }; + + if (delay > 0) { + Emulator.getThreading().run(applyGoal, delay); return; } - if (this.walkMode == WALKMODE_IF_CLOSER && !this.isCloserToGoal(oldLocation, targetTile, previousGoal)) { - roomUnit.setGoalLocation(targetTile); - return; - } - - roomUnit.setGoalLocation(previousGoal); + applyGoal.run(); } private boolean isCloserToGoal(RoomTile oldLocation, RoomTile newLocation, RoomTile goalLocation) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java new file mode 100644 index 00000000..2c9a1def --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java @@ -0,0 +1,125 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraAnimationTime extends InteractionWiredExtra { + public static final int CODE = 60; + public static final int MIN_DURATION_MS = 50; + public static final int MAX_DURATION_MS = 2000; + + private int durationMs = WiredMovementsComposer.DEFAULT_DURATION; + + public WiredExtraAnimationTime(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraAnimationTime(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int value = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : this.durationMs; + + if (value == this.durationMs && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = this.durationMs; + } + } + + this.durationMs = normalizeDuration(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.durationMs)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(1); + message.appendInt(this.durationMs); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.durationMs = normalizeDuration((data != null) ? data.durationMs : WiredMovementsComposer.DEFAULT_DURATION); + return; + } + + try { + this.durationMs = normalizeDuration(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.durationMs = WiredMovementsComposer.DEFAULT_DURATION; + } + } + + @Override + public void onPickUp() { + this.durationMs = WiredMovementsComposer.DEFAULT_DURATION; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getDurationMs() { + return this.durationMs; + } + + private static int normalizeDuration(int value) { + return Math.max(MIN_DURATION_MS, Math.min(MAX_DURATION_MS, value)); + } + + static class JsonData { + int durationMs; + + JsonData(int durationMs) { + this.durationMs = durationMs; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java new file mode 100644 index 00000000..401f5cb4 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java @@ -0,0 +1,157 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMoveCarryUsers extends InteractionWiredExtra { + public static final int CODE = 58; + public static final int MODE_DIRECTLY_ON_FURNI = 0; + public static final int MODE_SAME_TILE = 1; + public static final int SOURCE_ALL_ROOM_USERS = 900; + + private int carryMode = MODE_DIRECTLY_ON_FURNI; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredExtraMoveCarryUsers(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMoveCarryUsers(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.carryMode = this.normalizeCarryMode((params.length > 0) ? params[0] : MODE_DIRECTLY_ON_FURNI); + this.userSource = this.normalizeUserSource((params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER); + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.carryMode, this.userSource)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.carryMode); + message.appendInt(this.userSource); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data != null) { + this.carryMode = this.normalizeCarryMode(data.carryMode); + this.userSource = this.normalizeUserSource(data.userSource); + } + + return; + } + + String[] legacyData = wiredData.split("\t"); + if (legacyData.length > 0) { + try { + this.carryMode = this.normalizeCarryMode(Integer.parseInt(legacyData[0])); + } catch (NumberFormatException ignored) { + this.carryMode = MODE_DIRECTLY_ON_FURNI; + } + } + + if (legacyData.length > 1) { + try { + this.userSource = this.normalizeUserSource(Integer.parseInt(legacyData[1])); + } catch (NumberFormatException ignored) { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + } + } + + @Override + public void onPickUp() { + this.carryMode = MODE_DIRECTLY_ON_FURNI; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getCarryMode() { + return this.carryMode; + } + + public int getUserSource() { + return this.userSource; + } + + private int normalizeCarryMode(int value) { + return (value == MODE_SAME_TILE) ? MODE_SAME_TILE : MODE_DIRECTLY_ON_FURNI; + } + + private int normalizeUserSource(int value) { + switch (value) { + case SOURCE_ALL_ROOM_USERS: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int carryMode; + int userSource; + + JsonData(int carryMode, int userSource) { + this.carryMode = carryMode; + this.userSource = userSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java new file mode 100644 index 00000000..3149aae3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java @@ -0,0 +1,78 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMoveNoAnimation extends InteractionWiredExtra { + public static final int CODE = 59; + + public WiredExtraMoveNoAnimation(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMoveNoAnimation(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData()); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(0); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + } + + @Override + public void onPickUp() { + + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + static class JsonData { + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java new file mode 100644 index 00000000..cd890418 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java @@ -0,0 +1,229 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMovePhysics extends InteractionWiredExtra { + public static final int CODE = 61; + public static final int SOURCE_ALL_ROOM = 900; + + private boolean keepAltitude = false; + private boolean moveThroughFurni = false; + private boolean moveThroughUsers = false; + private boolean blockByFurni = false; + private int moveThroughFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int moveThroughUsersSource = WiredSourceUtil.SOURCE_TRIGGER; + private int blockByFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredExtraMovePhysics(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMovePhysics(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.keepAltitude = readFlag(params, 0); + this.moveThroughFurni = readFlag(params, 1); + this.moveThroughUsers = readFlag(params, 2); + this.blockByFurni = readFlag(params, 3); + this.moveThroughFurniSource = normalizeSource(readInt(params, 4, WiredSourceUtil.SOURCE_TRIGGER)); + this.blockByFurniSource = normalizeSource(readInt(params, 5, WiredSourceUtil.SOURCE_TRIGGER)); + this.moveThroughUsersSource = normalizeSource(readInt(params, 6, WiredSourceUtil.SOURCE_TRIGGER)); + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.keepAltitude, + this.moveThroughFurni, + this.moveThroughUsers, + this.blockByFurni, + this.moveThroughFurniSource, + this.blockByFurniSource, + this.moveThroughUsersSource)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(7); + message.appendInt(this.keepAltitude ? 1 : 0); + message.appendInt(this.moveThroughFurni ? 1 : 0); + message.appendInt(this.moveThroughUsers ? 1 : 0); + message.appendInt(this.blockByFurni ? 1 : 0); + message.appendInt(this.moveThroughFurniSource); + message.appendInt(this.blockByFurniSource); + message.appendInt(this.moveThroughUsersSource); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data != null) { + this.keepAltitude = data.keepAltitude; + this.moveThroughFurni = data.moveThroughFurni; + this.moveThroughUsers = data.moveThroughUsers; + this.blockByFurni = data.blockByFurni; + this.moveThroughFurniSource = normalizeSource(data.moveThroughFurniSource); + this.blockByFurniSource = normalizeSource(data.blockByFurniSource); + this.moveThroughUsersSource = normalizeSource(data.moveThroughUsersSource); + } + + return; + } + + String[] legacyData = wiredData.split("\t"); + this.keepAltitude = readLegacyFlag(legacyData, 0); + this.moveThroughFurni = readLegacyFlag(legacyData, 1); + this.moveThroughUsers = readLegacyFlag(legacyData, 2); + this.blockByFurni = readLegacyFlag(legacyData, 3); + this.moveThroughFurniSource = normalizeSource(readLegacyInt(legacyData, 4, WiredSourceUtil.SOURCE_TRIGGER)); + this.blockByFurniSource = normalizeSource(readLegacyInt(legacyData, 5, WiredSourceUtil.SOURCE_TRIGGER)); + this.moveThroughUsersSource = normalizeSource(readLegacyInt(legacyData, 6, WiredSourceUtil.SOURCE_TRIGGER)); + } + + @Override + public void onPickUp() { + this.keepAltitude = false; + this.moveThroughFurni = false; + this.moveThroughUsers = false; + this.blockByFurni = false; + this.moveThroughFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.moveThroughUsersSource = WiredSourceUtil.SOURCE_TRIGGER; + this.blockByFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public boolean isKeepAltitude() { + return this.keepAltitude; + } + + public boolean isMoveThroughFurni() { + return this.moveThroughFurni; + } + + public boolean isMoveThroughUsers() { + return this.moveThroughUsers; + } + + public boolean isBlockByFurni() { + return this.blockByFurni; + } + + public int getMoveThroughFurniSource() { + return this.moveThroughFurniSource; + } + + public int getMoveThroughUsersSource() { + return this.moveThroughUsersSource; + } + + public int getBlockByFurniSource() { + return this.blockByFurniSource; + } + + private static boolean readFlag(int[] params, int index) { + return readInt(params, index, 0) == 1; + } + + private static int readInt(int[] params, int index, int fallback) { + return (params.length > index) ? params[index] : fallback; + } + + private static boolean readLegacyFlag(String[] data, int index) { + return readLegacyInt(data, index, 0) == 1; + } + + private static int readLegacyInt(String[] data, int index, int fallback) { + if (data.length <= index) { + return fallback; + } + + try { + return Integer.parseInt(data[index]); + } catch (NumberFormatException ignored) { + return fallback; + } + } + + private static int normalizeSource(int value) { + switch (value) { + case SOURCE_ALL_ROOM: + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + boolean keepAltitude; + boolean moveThroughFurni; + boolean moveThroughUsers; + boolean blockByFurni; + int moveThroughFurniSource; + int blockByFurniSource; + int moveThroughUsersSource; + + JsonData(boolean keepAltitude, boolean moveThroughFurni, boolean moveThroughUsers, boolean blockByFurni, int moveThroughFurniSource, int blockByFurniSource, int moveThroughUsersSource) { + this.keepAltitude = keepAltitude; + this.moveThroughFurni = moveThroughFurni; + this.moveThroughUsers = moveThroughUsers; + this.blockByFurni = blockByFurni; + this.moveThroughFurniSource = moveThroughFurniSource; + this.blockByFurniSource = blockByFurniSource; + this.moveThroughUsersSource = moveThroughUsersSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java index 30db5f3d..8b223fe5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java @@ -28,6 +28,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { private int rootY = 0; private int areaWidth = 0; private int areaHeight = 0; + private boolean filterExisting = false; + private boolean invert = false; public WiredEffectFurniArea(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -43,9 +45,12 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { if (room == null || areaWidth <= 0 || areaHeight <= 0) return; List furniInArea = getFurniInArea(room); - if (!furniInArea.isEmpty()) { - ctx.targets().setItems(furniInArea); - } + ctx.targets().setItems(this.applySelectorModifiers( + furniInArea, + this.getSelectableFloorItems(room), + ctx.targets().items(), + this.filterExisting, + this.invert)); } private List getFurniInArea(Room room) { @@ -78,6 +83,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = params[1]; this.areaWidth = params[2]; this.areaHeight = params[3]; + this.filterExisting = params.length >= 5 && params[4] == 1; + this.invert = params.length >= 6 && params[5] == 1; this.setDelay(settings.getDelay()); return true; @@ -95,7 +102,7 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, getDelay())); + return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, getDelay())); } @Override @@ -108,6 +115,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = data.rootY; this.areaWidth = data.width; this.areaHeight = data.height; + this.filterExisting = data.filterExisting; + this.invert = data.invert; this.setDelay(data.delay); } } @@ -118,6 +127,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = 0; this.areaWidth = 0; this.areaHeight = 0; + this.filterExisting = false; + this.invert = false; this.setDelay(0); } @@ -131,11 +142,13 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(6); message.appendInt(this.rootX); message.appendInt(this.rootY); message.appendInt(this.areaWidth); message.appendInt(this.areaHeight); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); @@ -153,13 +166,17 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { int rootY; int width; int height; + boolean filterExisting; + boolean invert; int delay; - JsonData(int rootX, int rootY, int width, int height, int delay) { + JsonData(int rootX, int rootY, int width, int height, boolean filterExisting, boolean invert, int delay) { this.rootX = rootX; this.rootY = rootY; this.width = width; this.height = height; + this.filterExisting = filterExisting; + this.invert = invert; this.delay = delay; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java index 7e491a54..77bf5f29 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java @@ -21,6 +21,7 @@ import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -34,7 +35,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { private int channel = 0; // signal channel (0-based) private THashSet items; - private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int furniSource = WiredSourceUtil.SOURCE_SELECTED; private final AtomicLong activationToken = new AtomicLong(); public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException { @@ -51,18 +52,16 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public boolean matches(HabboItem triggerItem, WiredEvent event) { if (event.getType() != WiredEvent.Type.SIGNAL_RECEIVED) return false; - if (event.getSignalChannel() != this.channel) { - return false; - } - - HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem == null) { - return false; - } - - return WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() + List resolvedAntennas = WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() .filter(this::isAntennaItem) - .anyMatch(item -> item != null && item.getId() == sourceItem.getId()); + .collect(Collectors.toList()); + + if (!resolvedAntennas.isEmpty()) { + return resolvedAntennas.stream() + .anyMatch(item -> item != null && item.getId() == event.getSignalChannel()); + } + + return this.channel > 0 && event.getSignalChannel() == this.channel; } public int getChannel() { @@ -90,7 +89,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { int senderCount = 0; try { if (room != null && room.getRoomSpecialTypes() != null) { - senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); + senderCount = this.getSenderCount(room); } } catch (Exception e) { } @@ -153,6 +152,11 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { } } + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty()) { + HabboItem firstItem = this.items.iterator().next(); + this.channel = (firstItem != null) ? firstItem.getId() : this.channel; + } + return true; } @@ -212,6 +216,10 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { } if (this.furniSource != WiredSourceUtil.SOURCE_SELECTOR) this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty() && this.channel <= 0) { + HabboItem firstItem = this.items.iterator().next(); + if (firstItem != null) this.channel = firstItem.getId(); + } } } @@ -252,4 +260,30 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { String interaction = item.getBaseItem().getInteractionType().getName(); return interaction != null && ANTENNA_INTERACTION.equalsIgnoreCase(interaction); } + + private int getSenderCount(Room room) { + if (room == null || room.getRoomSpecialTypes() == null) { + return 0; + } + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty()) { + List antennaIds = new ArrayList<>(); + + for (HabboItem item : this.items) { + if (this.isAntennaItem(item)) { + antennaIds.add(item.getId()); + } + } + + if (!antennaIds.isEmpty()) { + return room.getRoomSpecialTypes().countSendersTargetingAnyReceiver(antennaIds); + } + } + + if (this.channel > 0) { + return room.getRoomSpecialTypes().countSendersTargetingReceiver(this.channel); + } + + return 0; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java index 7b27a858..989f7f57 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java @@ -18,6 +18,7 @@ import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredUserActionType; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics; import com.eu.habbo.messages.ISerialize; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.guilds.GuildInfoComposer; @@ -2440,6 +2441,11 @@ public class Room implements Comparable, ISerialize, Runnable { return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits); } + public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, + boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); + } + public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, Habbo owner) { return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner); @@ -2476,6 +2482,16 @@ public class Room implements Comparable, ISerialize, Runnable { return this.itemManager.moveFurniTo(item, tile, rotation, z, actor, sendUpdates, checkForUnits); } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, actor, sendUpdates, checkForUnits, physics); + } + + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, z, actor, sendUpdates, checkForUnits, physics); + } + public FurnitureMovementError slideFurniTo(HabboItem item, RoomTile tile, int rotation) { return this.itemManager.slideFurniTo(item, tile, rotation); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java index 3f1d8ccf..d3c11dab 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java @@ -23,6 +23,7 @@ import com.eu.habbo.habbohotel.users.HabboInfo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.users.HabboManager; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics; import com.eu.habbo.habbohotel.wired.tick.WiredTickable; import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer; import com.eu.habbo.messages.outgoing.rooms.items.*; @@ -1301,6 +1302,56 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return furnitureFitsAt(tile, item, rotation, checkForUnits); + } + + RoomLayout layout = this.room.getLayout(); + if (!layout.fitsOnMap(tile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation)) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic) { + return FurnitureMovementError.NONE; + } + + THashSet occupiedTiles = layout.getTilesAt(tile, item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), rotation); + for (RoomTile t : occupiedTiles) { + if (t.state == RoomTileState.INVALID) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (shouldCheckUnits(item, checkForUnits)) { + FurnitureMovementError unitCollision = this.getPhysicsUnitCollision(t, physics); + if (unitCollision != FurnitureMovementError.NONE) { + return unitCollision; + } + } + } + + if (this.hasBlockingPhysicsFurni(occupiedTiles, item, physics)) { + return FurnitureMovementError.CANT_STACK; + } + + java.util.List>> tileFurniList = new java.util.ArrayList<>(); + for (RoomTile t : occupiedTiles) { + tileFurniList.add(Pair.create(t, this.getPhysicsItemsAt(t, item, physics))); + + HabboItem topItem = this.getTopPhysicsItemAt(t.x, t.y, item, physics); + if (topItem != null && !topItem.getBaseItem().allowStack() && !t.getAllowStack()) { + return FurnitureMovementError.CANT_STACK; + } + } + + if (!item.canStackAt(this.room, tileFurniList)) { + return FurnitureMovementError.CANT_STACK; + } + + return FurnitureMovementError.NONE; + } + /** * Places a floor furniture item at a position. */ @@ -1569,6 +1620,158 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return moveFurniTo(item, tile, rotation, z, actor, sendUpdates, checkForUnits); + } + + if (item == null || tile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + RoomLayout layout = this.room.getLayout(); + RoomTile oldLocation = layout.getTile(item.getX(), item.getY()); + + boolean pluginHelper = false; + + if (Emulator.getPluginManager().isRegistered(FurnitureMovedEvent.class, true)) { + FurnitureMovedEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureMovedEvent(item, actor, oldLocation, tile)); + + if (event.isCancelled()) { + return FurnitureMovementError.CANCEL_PLUGIN_MOVE; + } + + pluginHelper = event.hasPluginHelper(); + } + + rotation %= 8; + + boolean magicTile = + item instanceof InteractionStackHelper || + item instanceof InteractionTileWalkMagic; + + THashSet occupiedTiles = layout.getTilesAt( + tile, + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + rotation + ); + + THashSet oldOccupiedTiles = layout.getTilesAt( + layout.getTile(item.getX(), item.getY()), + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + item.getRotation() + ); + + if (!pluginHelper) { + FurnitureMovementError fits = furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); + if (fits != FurnitureMovementError.NONE) { + return fits; + } + } + + int oldRotation = item.getRotation(); + + if (oldRotation != rotation) { + item.setRotation(rotation); + + if (Emulator.getPluginManager().isRegistered(FurnitureRotatedEvent.class, true)) { + Event rotatedEvent = new FurnitureRotatedEvent(item, actor, oldRotation); + Emulator.getPluginManager().fireEvent(rotatedEvent); + + if (rotatedEvent.isCancelled()) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANCEL_PLUGIN_ROTATE; + } + } + } + + if (z > Room.MAXIMUM_FURNI_HEIGHT) { + return FurnitureMovementError.CANT_STACK; + } + + if (z < layout.getHeightAtSquare(tile.x, tile.y)) { + return FurnitureMovementError.CANT_STACK; + } + + if (Emulator.getPluginManager().isRegistered(FurnitureBuildheightEvent.class, true)) { + FurnitureBuildheightEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureBuildheightEvent(item, actor, 0.00, z)); + + if (event.hasChangedHeight()) { + z = layout.getHeightAtSquare(tile.x, tile.y) + event.getUpdatedHeight(); + } + } + + item.setX(tile.x); + item.setY(tile.y); + item.setZ(z); + + if (magicTile) { + item.setZ(tile.z); + item.setExtradata("" + (item.getZ() * 100)); + } + + if (item.getZ() > Room.MAXIMUM_FURNI_HEIGHT) { + item.setZ(Room.MAXIMUM_FURNI_HEIGHT); + } + + if (oldLocation != null) { + if (item instanceof InteractionWiredTrigger) { + this.room.getRoomSpecialTypes().updateTriggerLocation((InteractionWiredTrigger) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredEffect) { + this.room.getRoomSpecialTypes().updateEffectLocation((InteractionWiredEffect) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredCondition) { + this.room.getRoomSpecialTypes().updateConditionLocation((InteractionWiredCondition) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredExtra) { + this.room.getRoomSpecialTypes().updateExtraLocation((InteractionWiredExtra) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } + } + + item.onMove(this.room, oldLocation, tile); + item.needsUpdate(true); + Emulator.getThreading().run(item); + + if (sendUpdates) { + this.room.sendComposer(new FloorItemUpdateComposer(item).compose()); + } + + occupiedTiles.removeAll(oldOccupiedTiles); + occupiedTiles.addAll(oldOccupiedTiles); + this.room.updateTiles(occupiedTiles); + + for (RoomTile t : occupiedTiles) { + this.room.updateHabbosAt(t.x, t.y, this.room.getHabbosAt(t.x, t.y)); + this.room.updateBotsAt(t.x, t.y); + } + + if (Emulator.getConfig().getBoolean("wired.place.under", false)) { + THashSet newOccupiedTiles = layout.getTilesAt( + tile, + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + rotation + ); + + for (RoomTile t : newOccupiedTiles) { + for (Habbo h : this.room.getHabbosAt(t.x, t.y)) { + try { + item.onWalkOn(h.getRoomUnit(), this.room, null); + } catch (Exception ignored) { + } + } + } + } + + return FurnitureMovementError.NONE; + } + /** * Moves furniture to a new position. */ @@ -1791,6 +1994,203 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return moveFurniTo(item, tile, rotation, actor, sendUpdates, checkForUnits); + } + + RoomLayout layout = this.room.getLayout(); + RoomTile oldLocation = layout.getTile(item.getX(), item.getY()); + + boolean pluginHelper = false; + if (Emulator.getPluginManager().isRegistered(FurnitureMovedEvent.class, true)) { + FurnitureMovedEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureMovedEvent(item, actor, oldLocation, tile)); + if (event.isCancelled()) { + return FurnitureMovementError.CANCEL_PLUGIN_MOVE; + } + pluginHelper = event.hasPluginHelper(); + } + + boolean magicTile = item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic; + + java.util.Optional stackHelper = this.getItemsAt(tile).stream() + .filter(i -> i instanceof InteractionStackHelper).findAny(); + + THashSet occupiedTiles = layout.getTilesAt(tile, item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), rotation); + THashSet newOccupiedTiles = layout.getTilesAt(tile, + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation); + + HabboItem topItem = this.getTopPhysicsItemAt(occupiedTiles, null, physics); + + if (!stackHelper.isPresent() && !pluginHelper) { + if (oldLocation != tile) { + for (RoomTile t : occupiedTiles) { + HabboItem tileTopItem = this.getTopPhysicsItemAt(t.x, t.y, item, physics); + if (!magicTile && ((tileTopItem != null && tileTopItem != item ? ( + t.state.equals(RoomTileState.INVALID) || !t.getAllowStack() + || !tileTopItem.getBaseItem().allowStack()) + : this.room.calculateTileState(t, item).equals(RoomTileState.INVALID)))) { + return FurnitureMovementError.CANT_STACK; + } + + if (shouldCheckUnits(item, checkForUnits)) { + FurnitureMovementError unitCollision = this.getPhysicsUnitCollision(t, physics); + if (!magicTile && unitCollision != FurnitureMovementError.NONE) { + return unitCollision; + } + } + } + } + + if (this.hasBlockingPhysicsFurni(occupiedTiles, item, physics)) { + return FurnitureMovementError.CANT_STACK; + } + + java.util.List>> tileFurniList = new java.util.ArrayList<>(); + for (RoomTile t : occupiedTiles) { + tileFurniList.add(Pair.create(t, this.getPhysicsItemsAt(t, item, physics))); + } + + if (!magicTile && !item.canStackAt(this.room, tileFurniList)) { + return FurnitureMovementError.CANT_STACK; + } + } + + THashSet oldOccupiedTiles = layout.getTilesAt( + layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), item.getRotation()); + + int oldRotation = item.getRotation(); + + if (oldRotation != rotation) { + item.setRotation(rotation); + if (Emulator.getPluginManager().isRegistered(FurnitureRotatedEvent.class, true)) { + Event furnitureRotatedEvent = new FurnitureRotatedEvent(item, actor, oldRotation); + Emulator.getPluginManager().fireEvent(furnitureRotatedEvent); + + if (furnitureRotatedEvent.isCancelled()) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANCEL_PLUGIN_ROTATE; + } + } + + if ((!stackHelper.isPresent() && topItem != null && topItem != item && !topItem.getBaseItem() + .allowStack()) || (topItem != null && topItem != item + && topItem.getZ() + Item.getCurrentHeight(topItem) + Item.getCurrentHeight(item) + > Room.MAXIMUM_FURNI_HEIGHT)) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANT_STACK; + } + } + + double height; + + if (stackHelper.isPresent()) { + height = stackHelper.get().getExtradata().isEmpty() ? Double.parseDouble("0.0") + : (Double.parseDouble(stackHelper.get().getExtradata()) / 100); + } else if (item == topItem) { + height = item.getZ(); + } else if (magicTile) { + if (topItem == null) { + height = this.getPhysicsStackHeight(tile.x, tile.y, item, physics); + for (RoomTile til : occupiedTiles) { + double sHeight = this.getPhysicsStackHeight(til.x, til.y, item, physics); + if (sHeight > height) { + height = sHeight; + } + } + } else { + height = topItem.getZ() + topItem.getBaseItem().getHeight(); + } + } else { + height = this.getPhysicsStackHeight(tile.x, tile.y, item, physics); + for (RoomTile til : occupiedTiles) { + double sHeight = this.getPhysicsStackHeight(til.x, til.y, item, physics); + if (sHeight > height) { + height = sHeight; + } + } + } + + boolean cantStack = false; + boolean pluginHeight = false; + + if (height > Room.MAXIMUM_FURNI_HEIGHT) { + cantStack = true; + } + if (height < layout.getHeightAtSquare(tile.x, tile.y)) { + cantStack = true; + } + + if (Emulator.getPluginManager().isRegistered(FurnitureBuildheightEvent.class, true)) { + FurnitureBuildheightEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureBuildheightEvent(item, actor, 0.00, height)); + if (event.hasChangedHeight()) { + height = layout.getHeightAtSquare(tile.x, tile.y) + event.getUpdatedHeight(); + pluginHeight = true; + } + } + + if (!pluginHeight && cantStack) { + return FurnitureMovementError.CANT_STACK; + } + + item.setX(tile.x); + item.setY(tile.y); + item.setZ(height); + if (magicTile) { + item.setZ(tile.z); + item.setExtradata("" + item.getZ() * 100); + } + if (item.getZ() > Room.MAXIMUM_FURNI_HEIGHT) { + item.setZ(Room.MAXIMUM_FURNI_HEIGHT); + } + + if (item instanceof InteractionWiredTrigger) { + this.room.getRoomSpecialTypes().updateTriggerLocation((InteractionWiredTrigger) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredEffect) { + this.room.getRoomSpecialTypes().updateEffectLocation((InteractionWiredEffect) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredCondition) { + this.room.getRoomSpecialTypes().updateConditionLocation((InteractionWiredCondition) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredExtra) { + this.room.getRoomSpecialTypes().updateExtraLocation((InteractionWiredExtra) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } + + item.onMove(this.room, oldLocation, tile); + item.needsUpdate(true); + Emulator.getThreading().run(item); + + if (sendUpdates) { + this.room.sendComposer(new FloorItemUpdateComposer(item).compose()); + } + + occupiedTiles.removeAll(oldOccupiedTiles); + occupiedTiles.addAll(oldOccupiedTiles); + this.room.updateTiles(occupiedTiles); + + for (RoomTile t : occupiedTiles) { + this.room.updateHabbosAt(t.x, t.y, this.room.getHabbosAt(t.x, t.y)); + this.room.updateBotsAt(t.x, t.y); + } + if (Emulator.getConfig().getBoolean("wired.place.under", false)) { + for (RoomTile t : newOccupiedTiles) { + for (Habbo h : this.room.getHabbosAt(t.x, t.y)) { + try { + item.onWalkOn(h.getRoomUnit(), this.room, null); + } catch (Exception e) { + } + } + } + } + return FurnitureMovementError.NONE; + } + /** * Slides furniture to a new position. */ @@ -1832,4 +2232,153 @@ public class RoomItemManager { } return FurnitureMovementError.NONE; } + + private boolean shouldCheckUnits(HabboItem item, boolean checkForUnits) { + if (!checkForUnits) { + return false; + } + + if (!Emulator.getConfig().getBoolean("wired.place.under", false)) { + return true; + } + + return !item.isWalkable() + && !item.getBaseItem().allowSit() + && !item.getBaseItem().allowLay(); + } + + private FurnitureMovementError getPhysicsUnitCollision(RoomTile tile, WiredMovementPhysics physics) { + for (RoomUnit roomUnit : this.room.getRoomUnits(tile)) { + if (roomUnit == null) { + continue; + } + + switch (roomUnit.getRoomUnitType()) { + case BOT: + return FurnitureMovementError.TILE_HAS_BOTS; + case PET: + return FurnitureMovementError.TILE_HAS_PETS; + case USER: + if (physics == null || !physics.shouldIgnoreUser(roomUnit)) { + return FurnitureMovementError.TILE_HAS_HABBOS; + } + break; + default: + return FurnitureMovementError.TILE_HAS_HABBOS; + } + } + + return FurnitureMovementError.NONE; + } + + private boolean hasBlockingPhysicsFurni(THashSet occupiedTiles, HabboItem exclude, WiredMovementPhysics physics) { + if (physics == null || !physics.hasBlockingFurni()) { + return false; + } + + for (RoomTile tile : occupiedTiles) { + for (HabboItem item : this.getItemsAt(tile)) { + if (item == null || item == exclude) { + continue; + } + + if (physics.isBlockingFurni(item)) { + return true; + } + } + } + + return false; + } + + private THashSet getPhysicsItemsAt(RoomTile tile, HabboItem exclude, WiredMovementPhysics physics) { + THashSet items = new THashSet<>(); + + for (HabboItem item : this.getItemsAt(tile)) { + if (item == null || item == exclude) { + continue; + } + + if (physics != null && physics.shouldIgnoreFurni(item)) { + continue; + } + + items.add(item); + } + + return items; + } + + private HabboItem getTopPhysicsItemAt(int x, int y, HabboItem exclude, WiredMovementPhysics physics) { + RoomTile tile = this.room.getLayout().getTile((short) x, (short) y); + + if (tile == null) { + return null; + } + + HabboItem highestItem = null; + + for (HabboItem item : this.getPhysicsItemsAt(tile, exclude, physics)) { + if (highestItem != null && highestItem.getZ() + Item.getCurrentHeight(highestItem) + > item.getZ() + Item.getCurrentHeight(item)) { + continue; + } + + highestItem = item; + } + + return highestItem; + } + + private HabboItem getTopPhysicsItemAt(THashSet tiles, HabboItem exclude, WiredMovementPhysics physics) { + HabboItem highestItem = null; + + for (RoomTile tile : tiles) { + if (tile == null) { + continue; + } + + HabboItem topItem = this.getTopPhysicsItemAt(tile.x, tile.y, exclude, physics); + if (topItem == null) { + continue; + } + + if (highestItem != null && highestItem.getZ() + Item.getCurrentHeight(highestItem) + > topItem.getZ() + Item.getCurrentHeight(topItem)) { + continue; + } + + highestItem = topItem; + } + + return highestItem; + } + + private double getPhysicsStackHeight(short x, short y, HabboItem exclude, WiredMovementPhysics physics) { + RoomLayout layout = this.room.getLayout(); + + if (x < 0 || y < 0 || layout == null) { + return 0.0; + } + + double height = layout.getHeightAtSquare(x, y); + + RoomTile tile = layout.getTile(x, y); + if (tile == null) { + return height; + } + + for (HabboItem item : this.getPhysicsItemsAt(tile, exclude, physics)) { + if (item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic) { + return item.getZ(); + } + } + + HabboItem topItem = this.getTopPhysicsItemAt(x, y, exclude, physics); + if (topItem != null) { + return topItem.getZ() + (topItem.getBaseItem().allowSit() ? 0 : Item.getCurrentHeight(topItem)); + } + + return height; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java index 2e56b6b0..9f6e1b5e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java @@ -30,7 +30,9 @@ import gnu.trove.set.hash.THashSet; import java.awt.*; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -376,6 +378,39 @@ public class RoomSpecialTypes { return countSendersTargetingReceiver(receiverItemId, null); } + public int countSendersTargetingAnyReceiver(Collection receiverItemIds, InteractionWiredEffect excludeSender) { + if (receiverItemIds == null || receiverItemIds.isEmpty()) { + return 0; + } + + Set senders = this.wiredEffects.get(WiredEffectType.SEND_SIGNAL); + if (senders == null) { + return 0; + } + + Set uniqueSenderIds = new HashSet<>(); + + for (InteractionWiredEffect effect : senders) { + if (excludeSender != null && effect.getId() == excludeSender.getId()) continue; + if (!(effect instanceof WiredEffectSendSignal)) continue; + + WiredEffectSendSignal sender = (WiredEffectSendSignal) effect; + for (Integer receiverItemId : receiverItemIds) { + if (receiverItemId == null) continue; + if (!sender.hasPickedItem(receiverItemId)) continue; + + uniqueSenderIds.add(effect.getId()); + break; + } + } + + return uniqueSenderIds.size(); + } + + public int countSendersTargetingAnyReceiver(Collection receiverItemIds) { + return countSendersTargetingAnyReceiver(receiverItemIds, null); + } + public void addTrigger(InteractionWiredTrigger trigger) { // Add to type-based index this.wiredTriggers.computeIfAbsent(trigger.getType(), k -> ConcurrentHashMap.newKeySet()) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java index e7a08aea..66d2e5e5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java @@ -9,6 +9,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWaterItem; import com.eu.habbo.habbohotel.items.interactions.interfaces.ConditionalGate; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.pets.RideablePet; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.users.DanceType; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; @@ -614,6 +616,11 @@ public class RoomUnit { public void setStatus(RoomUnitStatus key, String value) { if (key != null && value != null) { + if (key == RoomUnitStatus.MOVE) { + WiredMoveCarryHelper.clearStatusComposerSuppression(this); + WiredUserMovementHelper.clearStatusComposerSuppression(this); + } + this.status.put(key, value); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java index 8f4f0772..e18b3823 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java @@ -14,6 +14,8 @@ import com.eu.habbo.habbohotel.users.HabboGender; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.wired.WiredUserActionType; import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer; import com.eu.habbo.messages.outgoing.inventory.AddPetComposer; @@ -360,6 +362,11 @@ public class RoomUnitManager { continue; } + if (WiredMoveCarryHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit())) { + continue; + } + double z = habbo.getRoomUnit().getCurrentLocation().getStackHeight(); boolean hadLayStatus = habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); @@ -432,9 +439,17 @@ public class RoomUnitManager { if (!habbos.isEmpty()) { THashSet roomUnits = new THashSet<>(); for (Habbo habbo : habbos) { + if (habbo.getRoomUnit() == null + || WiredMoveCarryHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit())) { + continue; + } roomUnits.add(habbo.getRoomUnit()); } - this.room.sendComposer(new RoomUserStatusComposer(roomUnits, true).compose()); + + if (!roomUnits.isEmpty()) { + this.room.sendComposer(new RoomUserStatusComposer(roomUnits, true).compose()); + } } if (topItem != null && topItem.getBaseItem().allowLay()) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java index 1002a7ce..8aa7b883 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java @@ -197,7 +197,7 @@ public final class RoomWiredStackIndex implements WiredStackIndex { } List conditions = new ArrayList<>(rawConditions.size()); - for (InteractionWiredCondition condition : rawConditions) { + for (InteractionWiredCondition condition : WiredExecutionOrderUtil.sort(rawConditions)) { conditions.add(condition); } return conditions; @@ -212,7 +212,7 @@ public final class RoomWiredStackIndex implements WiredStackIndex { } List effects = new ArrayList<>(rawEffects.size()); - for (InteractionWiredEffect effect : rawEffects) { + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(rawEffects)) { effects.add(effect); } return effects; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredExecutionOrderUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredExecutionOrderUtil.java new file mode 100644 index 00000000..2b2bf606 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredExecutionOrderUtil.java @@ -0,0 +1,34 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.users.HabboItem; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +public final class WiredExecutionOrderUtil { + private static final Comparator WIRED_STACK_ORDER = Comparator + .comparingDouble(HabboItem::getZ) + .thenComparingInt(HabboItem::getId); + + private WiredExecutionOrderUtil() { + } + + public static List sort(Collection items) { + List sorted = new ArrayList<>(); + + if (items == null || items.isEmpty()) { + return sorted; + } + + for (T item : items) { + if (item != null) { + sorted.add(item); + } + } + + sorted.sort(WIRED_STACK_ORDER); + return sorted; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMoveCarryHelper.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMoveCarryHelper.java new file mode 100644 index 00000000..40b86acd --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMoveCarryHelper.java @@ -0,0 +1,714 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimationTime; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhysics; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation; +import com.eu.habbo.habbohotel.rooms.FurnitureMovementError; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; +import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; +import gnu.trove.set.hash.THashSet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public final class WiredMoveCarryHelper { + private static final double DIRECT_HEIGHT_TOLERANCE = 0.1D; + private static final int STATUS_SUPPRESSION_GRACE_MS = 250; + private static final ThreadLocal> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>(); + private static final ConcurrentHashMap SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>(); + + private WiredMoveCarryHelper() { + } + + public static FurnitureMovementError getMovementError(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, WiredContext ctx) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (!hasMovementBehaviorExtra(room, stackItem)) { + return room.furnitureFitsAt(targetTile, movingItem, rotation, true); + } + + CarryContext carryContext = prepareCarryContext(room, stackItem, movingItem, ctx); + WiredMovementPhysics movementPhysics = getMovementPhysics(room, stackItem, movingItem, ctx); + FurnitureMovementError movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, false, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + + if (!carryContext.active) { + return room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, true, movementPhysics); + } + + return getBlockingUnitError(room, movingItem, targetTile, rotation, carryContext, movementPhysics); + } + + public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Habbo actor, boolean sendUpdates, WiredContext ctx) { + return moveFurni(room, stackItem, movingItem, targetTile, rotation, null, actor, sendUpdates, ctx); + } + + public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (!hasMovementBehaviorExtra(room, stackItem)) { + return moveFurniLegacy(room, movingItem, targetTile, rotation, z, actor, sendUpdates); + } + + RoomTile oldLocation = room.getLayout() == null ? null : room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + double oldZ = movingItem.getZ(); + CarryContext carryContext = prepareCarryContext(room, stackItem, movingItem, ctx); + WiredMovementPhysics movementPhysics = getMovementPhysics(room, stackItem, movingItem, ctx); + FurnitureMovementError movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, false, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + + if (carryContext.active) { + movementError = getBlockingUnitError(room, movingItem, targetTile, rotation, carryContext, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + } else { + movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, true, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + } + + boolean useWiredMovements = !hasNoAnimationExtra(room, stackItem); + int animationDuration = getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION); + Set previousSuppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + + if (carryContext.active) { + HashSet suppressedRoomUnitIds = previousSuppressedRoomUnitIds == null + ? new HashSet<>() + : new HashSet<>(previousSuppressedRoomUnitIds); + suppressedRoomUnitIds.addAll(carryContext.carriedUserIds); + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(suppressedRoomUnitIds); + } + + FurnitureMovementError result; + Double targetZ = z; + + if (targetZ == null && movementPhysics.isKeepAltitude()) { + targetZ = oldZ; + } + + try { + result = (targetZ == null) + ? room.moveFurniToWithPhysics(movingItem, targetTile, rotation, actor, !useWiredMovements, false, movementPhysics) + : room.moveFurniToWithPhysics(movingItem, targetTile, rotation, targetZ, actor, !useWiredMovements, false, movementPhysics); + } finally { + if (carryContext.active) { + if (previousSuppressedRoomUnitIds == null || previousSuppressedRoomUnitIds.isEmpty()) { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.remove(); + } else { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(previousSuppressedRoomUnitIds); + } + } + } + + if (result == FurnitureMovementError.NONE) { + if (!useWiredMovements) { + applyInstantCarryState(room, movingItem, targetTile, rotation, carryContext); + } else if (oldLocation != null) { + sendAnimatedMove(room, movingItem, oldLocation, oldZ, targetTile, rotation, carryContext, animationDuration); + } + } + + return result; + } + + private static FurnitureMovementError moveFurniLegacy(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + RoomTile oldLocation = room.getLayout() == null ? null : room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + double oldZ = movingItem.getZ(); + + FurnitureMovementError result = (z == null) + ? room.moveFurniTo(movingItem, targetTile, rotation, actor, sendUpdates) + : room.moveFurniTo(movingItem, targetTile, rotation, z, actor, sendUpdates, false); + + if (result == FurnitureMovementError.NONE + && !sendUpdates + && oldLocation != null + && (oldLocation.x != targetTile.x || oldLocation.y != targetTile.y || Double.compare(oldZ, movingItem.getZ()) != 0)) { + room.sendComposer(new FloorItemOnRollerComposer(movingItem, null, oldLocation, oldZ, targetTile, movingItem.getZ(), 0, room).compose()); + } + + return result; + } + + public static boolean shouldSuppressStatusUpdate(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Set suppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + return suppressedRoomUnitIds != null && suppressedRoomUnitIds.contains(roomUnit.getId()); + } + + public static boolean shouldSuppressStatusComposer(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Long suppressedUntil = SUPPRESSED_STATUS_COMPOSER_UNTIL.get(roomUnit.getId()); + + if (suppressedUntil == null) { + return false; + } + + if (suppressedUntil <= System.currentTimeMillis()) { + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId(), suppressedUntil); + return false; + } + + return true; + } + + public static void clearStatusComposerSuppression(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId()); + } + + public static boolean hasNoAnimationExtra(Room room, HabboItem stackItem) { + return getNoAnimationExtra(room, stackItem) != null; + } + + public static int getAnimationDuration(Room room, HabboItem stackItem, int fallbackDuration) { + WiredExtraAnimationTime extra = getAnimationTimeExtra(room, stackItem); + return (extra != null) ? extra.getDurationMs() : fallbackDuration; + } + + private static boolean hasMovementBehaviorExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return false; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveCarryUsers + || extra instanceof WiredExtraMoveNoAnimation + || extra instanceof WiredExtraAnimationTime + || extra instanceof WiredExtraMovePhysics) { + return true; + } + } + + return false; + } + + private static CarryContext prepareCarryContext(Room room, HabboItem stackItem, HabboItem movingItem, WiredContext ctx) { + WiredExtraMoveCarryUsers extra = getActiveExtra(room, stackItem); + + if (extra == null || ctx == null || room.getLayout() == null) { + return CarryContext.disabled(); + } + + RoomTile anchorTile = room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + if (anchorTile == null) { + return CarryContext.disabled(); + } + + THashSet occupiedTiles = room.getLayout().getTilesAt( + anchorTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + movingItem.getRotation()); + + if (occupiedTiles == null || occupiedTiles.isEmpty()) { + return CarryContext.disabled(); + } + + Collection targetUsers = resolveUsers(room, ctx, extra.getUserSource()); + if (targetUsers == null || targetUsers.isEmpty()) { + return CarryContext.disabled(); + } + + List carriedUnits = new ArrayList<>(); + HashSet carriedIds = new HashSet<>(); + + for (RoomUnit roomUnit : targetUsers) { + if (!isEligibleUser(room, movingItem, roomUnit, occupiedTiles, extra.getCarryMode())) { + continue; + } + + CarriedRoomUnit carriedRoomUnit = new CarriedRoomUnit( + roomUnit, + roomUnit.getCurrentLocation(), + roomUnit.getZ(), + roomUnit.getZ() - getCarrySurfaceZ(movingItem, roomUnit, roomUnit.getZ()), + roomUnit.getX() - anchorTile.x, + roomUnit.getY() - anchorTile.y); + + carriedUnits.add(carriedRoomUnit); + carriedIds.add(roomUnit.getId()); + } + + if (carriedUnits.isEmpty()) { + return CarryContext.disabled(); + } + + return new CarryContext(true, carriedUnits, carriedIds); + } + + private static Collection resolveUsers(Room room, WiredContext ctx, int userSource) { + if (userSource == WiredExtraMoveCarryUsers.SOURCE_ALL_ROOM_USERS) { + return new ArrayList<>(room.getRoomUnits()); + } + + return WiredSourceUtil.resolveUsers(ctx, userSource); + } + + private static boolean isEligibleUser(Room room, HabboItem movingItem, RoomUnit roomUnit, THashSet occupiedTiles, int carryMode) { + if (roomUnit == null + || roomUnit.getRoomUnitType() != RoomUnitType.USER + || roomUnit.getCurrentLocation() == null + || !roomUnit.isInRoom() + || roomUnit.isWalking()) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getRiding() != null) { + return false; + } + + if (!occupiesMovingFurni(occupiedTiles, roomUnit)) { + return false; + } + + if (carryMode == WiredExtraMoveCarryUsers.MODE_DIRECTLY_ON_FURNI) { + return isDirectlyOnMovingFurni(room, movingItem, roomUnit); + } + + return true; + } + + private static boolean isDirectlyOnMovingFurni(Room room, HabboItem movingItem, RoomUnit roomUnit) { + HabboItem topItem = room.getTopItemAt(roomUnit.getX(), roomUnit.getY()); + if (topItem == movingItem) { + return true; + } + + double carrySurfaceZ = getCarrySurfaceZ(movingItem, roomUnit, roomUnit.getZ()); + return Math.abs(roomUnit.getZ() - carrySurfaceZ) <= DIRECT_HEIGHT_TOLERANCE; + } + + private static boolean occupiesMovingFurni(THashSet occupiedTiles, RoomUnit roomUnit) { + for (RoomTile occupiedTile : occupiedTiles) { + if (occupiedTile != null + && occupiedTile.x == roomUnit.getX() + && occupiedTile.y == roomUnit.getY()) { + return true; + } + } + + return false; + } + + private static FurnitureMovementError getBlockingUnitError(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext, WiredMovementPhysics movementPhysics) { + THashSet occupiedTiles = room.getLayout().getTilesAt( + targetTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + rotation); + + if (occupiedTiles == null || occupiedTiles.isEmpty()) { + return FurnitureMovementError.NONE; + } + + for (RoomTile tile : occupiedTiles) { + for (RoomUnit roomUnit : room.getRoomUnits(tile)) { + if (roomUnit == null || carryContext.carriedUserIds.contains(roomUnit.getId())) { + continue; + } + + if (movementPhysics.shouldIgnoreUser(roomUnit)) { + continue; + } + + switch (roomUnit.getRoomUnitType()) { + case BOT: + return FurnitureMovementError.TILE_HAS_BOTS; + case PET: + return FurnitureMovementError.TILE_HAS_PETS; + case USER: + default: + return FurnitureMovementError.TILE_HAS_HABBOS; + } + } + } + + return FurnitureMovementError.NONE; + } + + private static void sendAnimatedMove(Room room, HabboItem movingItem, RoomTile oldLocation, double oldZ, RoomTile targetTile, int rotation, CarryContext carryContext, int animationDuration) { + List carriedMoves = getCarriedUnitMoves(room, movingItem, targetTile, rotation, carryContext); + List movements = new ArrayList<>(); + movements.add(WiredMovementsComposer.furniMovement( + movingItem.getId(), + oldLocation.x, + oldLocation.y, + targetTile.x, + targetTile.y, + oldZ, + movingItem.getZ(), + movingItem.getRotation(), + animationDuration)); + + for (CarriedUnitMove carriedMove : carriedMoves) { + suppressStatusComposer(carriedMove.roomUnit, animationDuration); + movements.add(WiredMovementsComposer.userSlideMovement( + carriedMove.roomUnit.getId(), + carriedMove.oldLocation.x, + carriedMove.oldLocation.y, + carriedMove.destinationTile.x, + carriedMove.destinationTile.y, + carriedMove.oldZ, + carriedMove.newZ, + carriedMove.roomUnit.getBodyRotation().getValue(), + carriedMove.roomUnit.getHeadRotation().getValue(), + animationDuration)); + } + + room.sendComposer(new WiredMovementsComposer(movements).compose()); + + for (CarriedUnitMove carriedMove : carriedMoves) { + updateCarriedUnitState(carriedMove); + } + } + + private static void applyInstantCarryState(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext) { + if (!carryContext.active || room == null || movingItem == null || targetTile == null) { + return; + } + + List carriedMoves = getCarriedUnitMoves(room, movingItem, targetTile, rotation, carryContext); + + for (CarriedUnitMove carriedMove : carriedMoves) { + updateCarriedUnitStateInstant(carriedMove); + + Habbo habbo = room.getHabbo(carriedMove.roomUnit); + if (habbo != null && shouldRefreshPostureWithTileUpdate(carriedMove.roomUnit)) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(carriedMove.destinationTile.x, carriedMove.destinationTile.y, movedHabbos); + } + + room.sendComposer(new RoomUserStatusComposer(carriedMove.roomUnit).compose()); + } + } + + private static List getCarriedUnitMoves(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext) { + List carriedMoves = new ArrayList<>(); + + if (!carryContext.active) { + return carriedMoves; + } + + THashSet occupiedTiles = room.getLayout().getTilesAt( + targetTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + rotation); + + for (CarriedRoomUnit carriedRoomUnit : carryContext.carriedUsers) { + RoomUnit roomUnit = carriedRoomUnit.roomUnit; + + if (roomUnit == null || roomUnit.getCurrentLocation() == null || roomUnit.isWalking()) { + continue; + } + + RoomTile destinationTile = room.getLayout().getTile( + (short) (targetTile.x + carriedRoomUnit.relativeX), + (short) (targetTile.y + carriedRoomUnit.relativeY)); + + if (destinationTile == null || destinationTile.state == null || !occupiedTiles.contains(destinationTile)) { + destinationTile = targetTile; + } + + double carrySurfaceZ = getCarrySurfaceZ(movingItem, roomUnit, carriedRoomUnit.oldZ); + double newZ = carrySurfaceZ + carriedRoomUnit.heightOffset; + carriedMoves.add(new CarriedUnitMove(roomUnit, carriedRoomUnit.oldLocation, carriedRoomUnit.oldZ, destinationTile, newZ)); + } + + return carriedMoves; + } + + private static boolean shouldRefreshPostureWithTileUpdate(RoomUnit roomUnit) { + return roomUnit != null + && (roomUnit.hasStatus(RoomUnitStatus.SIT) || roomUnit.hasStatus(RoomUnitStatus.LAY)); + } + + private static double getCarrySurfaceZ(HabboItem movingItem, RoomUnit roomUnit, double referenceZ) { + if (movingItem == null) { + return referenceZ; + } + + double baseZ = movingItem.getZ(); + double topZ = baseZ + Item.getCurrentHeight(movingItem); + + if (roomUnit != null && (roomUnit.hasStatus(RoomUnitStatus.SIT) || roomUnit.hasStatus(RoomUnitStatus.LAY))) { + return baseZ; + } + + if (movingItem.getBaseItem().allowSit() || movingItem.getBaseItem().allowLay()) { + return (Math.abs(referenceZ - baseZ) <= Math.abs(referenceZ - topZ)) ? baseZ : topZ; + } + + return topZ; + } + + private static void updateCarriedUnitState(CarriedUnitMove carriedMove) { + carriedMove.roomUnit.setLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setZ(carriedMove.newZ); + carriedMove.roomUnit.setLastRollerTime(System.currentTimeMillis()); + carriedMove.roomUnit.setPreviousLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setPreviousLocationZ(carriedMove.newZ); + + if (carriedMove.roomUnit.hasStatus(RoomUnitStatus.SIT)) { + carriedMove.roomUnit.sitUpdate = true; + } + } + + private static void updateCarriedUnitStateInstant(CarriedUnitMove carriedMove) { + carriedMove.roomUnit.setLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setZ(carriedMove.newZ); + carriedMove.roomUnit.setPreviousLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setPreviousLocationZ(carriedMove.newZ); + carriedMove.roomUnit.statusUpdate(false); + + if (carriedMove.roomUnit.hasStatus(RoomUnitStatus.SIT)) { + carriedMove.roomUnit.sitUpdate = true; + } + } + + private static void suppressStatusComposer(RoomUnit roomUnit, int duration) { + if (roomUnit == null) { + return; + } + + long suppressedUntil = System.currentTimeMillis() + Math.max(duration, WiredMovementsComposer.DEFAULT_DURATION) + STATUS_SUPPRESSION_GRACE_MS; + SUPPRESSED_STATUS_COMPOSER_UNTIL.put(roomUnit.getId(), suppressedUntil); + } + + private static WiredExtraMoveCarryUsers getActiveExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveCarryUsers) { + return (WiredExtraMoveCarryUsers) extra; + } + } + + return null; + } + + private static WiredExtraMoveNoAnimation getNoAnimationExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveNoAnimation) { + return (WiredExtraMoveNoAnimation) extra; + } + } + + return null; + } + + private static WiredExtraAnimationTime getAnimationTimeExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraAnimationTime) { + return (WiredExtraAnimationTime) extra; + } + } + + return null; + } + + private static WiredMovementPhysics getMovementPhysics(Room room, HabboItem stackItem, HabboItem movingItem, WiredContext ctx) { + WiredExtraMovePhysics extra = getMovementPhysicsExtra(room, stackItem); + if (extra == null) { + return WiredMovementPhysics.NONE; + } + + HashSet passThroughFurniIds = new HashSet<>(); + HashSet passThroughUserIds = new HashSet<>(); + HashSet blockingFurniIds = new HashSet<>(); + + if (extra.isMoveThroughFurni()) { + for (HabboItem item : resolveFurniSources(room, ctx, extra.getMoveThroughFurniSource())) { + if (item != null && item != movingItem) { + passThroughFurniIds.add(item.getId()); + } + } + } + + if (extra.isMoveThroughUsers()) { + for (RoomUnit roomUnit : resolvePhysicsUsers(room, ctx, extra.getMoveThroughUsersSource())) { + if (roomUnit != null && roomUnit.getRoomUnitType() == RoomUnitType.USER) { + passThroughUserIds.add(roomUnit.getId()); + } + } + } + + if (extra.isBlockByFurni()) { + for (HabboItem item : resolveFurniSources(room, ctx, extra.getBlockByFurniSource())) { + if (item != null && item != movingItem) { + blockingFurniIds.add(item.getId()); + } + } + } + + return new WiredMovementPhysics(extra.isKeepAltitude(), passThroughFurniIds, passThroughUserIds, blockingFurniIds); + } + + private static Collection resolveFurniSources(Room room, WiredContext ctx, int sourceType) { + if (room == null) { + return new ArrayList<>(); + } + + if (sourceType == WiredExtraMovePhysics.SOURCE_ALL_ROOM) { + return new ArrayList<>(room.getFloorItems()); + } + + if (ctx == null) { + return new ArrayList<>(); + } + + return WiredSourceUtil.resolveItems(ctx, sourceType, null); + } + + private static Collection resolvePhysicsUsers(Room room, WiredContext ctx, int userSource) { + if (room == null) { + return new ArrayList<>(); + } + + if (userSource == WiredExtraMovePhysics.SOURCE_ALL_ROOM) { + return new ArrayList<>(room.getRoomUnits()); + } + + if (ctx == null) { + return new ArrayList<>(); + } + + return WiredSourceUtil.resolveUsers(ctx, userSource); + } + + private static WiredExtraMovePhysics getMovementPhysicsExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMovePhysics) { + return (WiredExtraMovePhysics) extra; + } + } + + return null; + } + + private static THashSet getMovementExtras(Room room, HabboItem stackItem) { + if (room == null || stackItem == null || room.getRoomSpecialTypes() == null) { + return null; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(stackItem.getX(), stackItem.getY()); + if (extras == null || extras.isEmpty()) { + return null; + } + + return extras; + } + + private static final class CarryContext { + private final boolean active; + private final List carriedUsers; + private final HashSet carriedUserIds; + + private CarryContext(boolean active, List carriedUsers, HashSet carriedUserIds) { + this.active = active; + this.carriedUsers = carriedUsers; + this.carriedUserIds = carriedUserIds; + } + + private static CarryContext disabled() { + return new CarryContext(false, new ArrayList<>(), new HashSet<>()); + } + } + + private static final class CarriedRoomUnit { + private final RoomUnit roomUnit; + private final RoomTile oldLocation; + private final double oldZ; + private final double heightOffset; + private final int relativeX; + private final int relativeY; + + private CarriedRoomUnit(RoomUnit roomUnit, RoomTile oldLocation, double oldZ, double heightOffset, int relativeX, int relativeY) { + this.roomUnit = roomUnit; + this.oldLocation = oldLocation; + this.oldZ = oldZ; + this.heightOffset = heightOffset; + this.relativeX = relativeX; + this.relativeY = relativeY; + } + } + + private static final class CarriedUnitMove { + private final RoomUnit roomUnit; + private final RoomTile oldLocation; + private final double oldZ; + private final RoomTile destinationTile; + private final double newZ; + + private CarriedUnitMove(RoomUnit roomUnit, RoomTile oldLocation, double oldZ, RoomTile destinationTile, double newZ) { + this.roomUnit = roomUnit; + this.oldLocation = oldLocation; + this.oldZ = oldZ; + this.destinationTile = destinationTile; + this.newZ = newZ; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java new file mode 100644 index 00000000..538baa2b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java @@ -0,0 +1,56 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.users.HabboItem; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public final class WiredMovementPhysics { + public static final WiredMovementPhysics NONE = new WiredMovementPhysics(false, Collections.emptySet(), Collections.emptySet(), Collections.emptySet()); + + private final boolean keepAltitude; + private final Set passThroughFurniIds; + private final Set passThroughUserIds; + private final Set blockingFurniIds; + + public WiredMovementPhysics(boolean keepAltitude, Set passThroughFurniIds, Set passThroughUserIds, Set blockingFurniIds) { + this.keepAltitude = keepAltitude; + this.passThroughFurniIds = Collections.unmodifiableSet(new HashSet<>(passThroughFurniIds)); + this.passThroughUserIds = Collections.unmodifiableSet(new HashSet<>(passThroughUserIds)); + this.blockingFurniIds = Collections.unmodifiableSet(new HashSet<>(blockingFurniIds)); + } + + public boolean isKeepAltitude() { + return this.keepAltitude; + } + + public boolean isActive() { + return this.keepAltitude + || !this.passThroughFurniIds.isEmpty() + || !this.passThroughUserIds.isEmpty() + || !this.blockingFurniIds.isEmpty(); + } + + public boolean hasBlockingFurni() { + return !this.blockingFurniIds.isEmpty(); + } + + public boolean shouldIgnoreFurni(HabboItem item) { + return item != null + && this.passThroughFurniIds.contains(item.getId()) + && !this.blockingFurniIds.contains(item.getId()); + } + + public boolean isBlockingFurni(HabboItem item) { + return item != null && this.blockingFurniIds.contains(item.getId()); + } + + public boolean shouldIgnoreUser(RoomUnit roomUnit) { + return roomUnit != null + && roomUnit.getRoomUnitType() == RoomUnitType.USER + && this.passThroughUserIds.contains(roomUnit.getId()); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java index ed65404a..b8ec7046 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java @@ -1,7 +1,15 @@ package com.eu.habbo.habbohotel.wired.core; +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser; +import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.api.IWiredEffect; +import gnu.trove.set.hash.THashSet; import java.util.ArrayList; import java.util.Collection; @@ -24,8 +32,9 @@ public final class WiredSourceUtil { case SOURCE_SELECTED: return (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList(); case SOURCE_SELECTOR: - return ctx.targets().isItemsModifiedBySelector() - ? new ArrayList<>(ctx.targets().items()) + WiredTargets itemTargets = getSelectorTargets(ctx); + return itemTargets.isItemsModifiedBySelector() + ? new ArrayList<>(itemTargets.items()) : Collections.emptyList(); case SOURCE_SIGNAL: if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { @@ -48,8 +57,9 @@ public final class WiredSourceUtil { case SOURCE_SELECTED: return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList(); case SOURCE_SELECTOR: - return ctx.targets().isUsersModifiedBySelector() - ? new ArrayList<>(ctx.targets().users()) + WiredTargets userTargets = getSelectorTargets(ctx); + return userTargets.isUsersModifiedBySelector() + ? new ArrayList<>(userTargets.users()) : Collections.emptyList(); case SOURCE_SIGNAL: if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { @@ -60,4 +70,147 @@ public final class WiredSourceUtil { return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList()); } } + + private static WiredTargets getSelectorTargets(WiredContext ctx) { + if (ctx == null) { + return new WiredTargets(); + } + + if (ctx.targets().isItemsModifiedBySelector() || ctx.targets().isUsersModifiedBySelector()) { + return ctx.targets(); + } + + WiredContext selectorContext = executeSelectors(ctx); + + if (selectorContext == null) { + return ctx.targets(); + } + + if (selectorContext.targets().isItemsModifiedBySelector()) { + ctx.targets().setItems(selectorContext.targets().items()); + } + + if (selectorContext.targets().isUsersModifiedBySelector()) { + ctx.targets().setUsers(selectorContext.targets().users()); + } + + return ctx.targets(); + } + + private static WiredContext executeSelectors(WiredContext originalCtx) { + if (originalCtx == null) { + return null; + } + + Room room = originalCtx.room(); + HabboItem triggerItem = originalCtx.triggerItem(); + + if (room == null || triggerItem == null || room.getRoomSpecialTypes() == null) { + return null; + } + + WiredContext selectorCtx = new WiredContext( + originalCtx.event(), + triggerItem, + originalCtx.stack(), + originalCtx.services(), + new WiredState(100), + originalCtx.legacySettings() + ); + + List selectorEffects = getOrderedSelectorEffects(originalCtx, room, triggerItem); + + for (InteractionWiredEffect effect : selectorEffects) { + if (effect.requiresActor() && !selectorCtx.hasActor()) { + continue; + } + + try { + selectorCtx.state().step(); + effect.execute(selectorCtx); + } catch (Exception ignored) { + } + } + + applySelectionFilterExtras(room, triggerItem, selectorCtx); + + return selectorCtx; + } + + private static List getOrderedSelectorEffects(WiredContext originalCtx, Room room, HabboItem triggerItem) { + List selectorEffects = new ArrayList<>(); + + if (originalCtx != null && originalCtx.hasStack()) { + for (IWiredEffect effect : originalCtx.stack().effects()) { + if (effect instanceof InteractionWiredEffect && effect.isSelector()) { + selectorEffects.add((InteractionWiredEffect) effect); + } + } + + if (!selectorEffects.isEmpty()) { + return selectorEffects; + } + } + + THashSet roomEffects = room.getRoomSpecialTypes().getEffects(triggerItem.getX(), triggerItem.getY()); + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(roomEffects)) { + if (effect != null && effect.isSelector()) { + selectorEffects.add(effect); + } + } + + return selectorEffects; + } + + private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) { + if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY()); + + if (extras == null || extras.isEmpty()) { + return; + } + + int furniLimit = Integer.MAX_VALUE; + int userLimit = Integer.MAX_VALUE; + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraFilterFurni) { + furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount()); + } else if (extra instanceof WiredExtraFilterUser) { + userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount()); + } + } + + if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit)); + } + + if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit)); + } + } + + private static List limitIterable(Iterable values, int limit) { + List result = new ArrayList<>(); + + if (values == null || limit <= 0) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + if (result.size() <= limit) { + return result; + } + + Collections.shuffle(result, Emulator.getRandom()); + return new ArrayList<>(result.subList(0, limit)); + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java index 807d4c47..17bad58f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java @@ -1,7 +1,10 @@ package com.eu.habbo.habbohotel.wired.core; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; @@ -120,18 +123,9 @@ public final class WiredTriggerSourceUtil { return null; } - THashSet effects = room.getRoomSpecialTypes().getEffects(trigger.getX(), trigger.getY()); - if (effects == null || effects.isEmpty()) { - return null; - } - WiredContext ctx = new WiredContext(event, trigger, DefaultWiredServices.getInstance(), new WiredState(100)); - for (InteractionWiredEffect effect : effects) { - if (effect == null || !effect.isSelector()) { - continue; - } - + for (InteractionWiredEffect effect : getOrderedSelectorEffects(room, trigger)) { if (effect.requiresActor() && !ctx.hasActor()) { continue; } @@ -143,6 +137,76 @@ public final class WiredTriggerSourceUtil { } } + applySelectionFilterExtras(room, trigger, ctx); + return ctx; } + + private static List getOrderedSelectorEffects(Room room, InteractionWiredTrigger trigger) { + if (room == null || trigger == null || room.getRoomSpecialTypes() == null) { + return Collections.emptyList(); + } + + THashSet effects = room.getRoomSpecialTypes().getEffects(trigger.getX(), trigger.getY()); + List selectorEffects = new ArrayList<>(); + + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(effects)) { + if (effect != null && effect.isSelector()) { + selectorEffects.add(effect); + } + } + + return selectorEffects; + } + + private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) { + if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY()); + + if (extras == null || extras.isEmpty()) { + return; + } + + int furniLimit = Integer.MAX_VALUE; + int userLimit = Integer.MAX_VALUE; + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraFilterFurni) { + furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount()); + } else if (extra instanceof WiredExtraFilterUser) { + userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount()); + } + } + + if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit)); + } + + if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit)); + } + } + + private static List limitIterable(Iterable values, int limit) { + List result = new ArrayList<>(); + + if (values == null || limit <= 0) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + if (result.size() <= limit) { + return result; + } + + return new ArrayList<>(result.subList(0, limit)); + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java new file mode 100644 index 00000000..9777db22 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java @@ -0,0 +1,305 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.interactions.InteractionRoller; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.rooms.RoomUserRotation; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public final class WiredUserMovementHelper { + public static final int DEFAULT_ANIMATION_DURATION = WiredMovementsComposer.DEFAULT_DURATION; + private static final int STATUS_SUPPRESSION_GRACE_MS = 250; + + private static final Logger LOGGER = LoggerFactory.getLogger(WiredUserMovementHelper.class); + private static final ThreadLocal> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>(); + private static final ConcurrentHashMap SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>(); + + private WiredUserMovementHelper() { + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, int duration) { + return moveUser(room, roomUnit, targetTile, targetZ, roomUnit == null ? null : roomUnit.getBodyRotation(), + roomUnit == null ? null : roomUnit.getHeadRotation(), duration, false); + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration) { + return moveUser(room, roomUnit, targetTile, targetZ, bodyRotation, headRotation, duration, false); + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration, boolean noAnimation) { + if (room == null || roomUnit == null || targetTile == null || room.getLayout() == null) { + return false; + } + + RoomTile oldLocation = roomUnit.getCurrentLocation(); + if (oldLocation == null || hasBlockingUnits(room, roomUnit, targetTile)) { + return false; + } + + RoomUserRotation resolvedBodyRotation = bodyRotation == null ? roomUnit.getBodyRotation() : bodyRotation; + RoomUserRotation resolvedHeadRotation = headRotation == null ? roomUnit.getHeadRotation() : headRotation; + double oldZ = roomUnit.getZ(); + HabboItem oldTopItem = room.getTopItemAt(oldLocation.x, oldLocation.y); + HabboItem newTopItem = room.getTopItemAt(targetTile.x, targetTile.y); + Habbo habbo = room.getHabbo(roomUnit); + int animationDuration = Math.max(1, duration); + + if (noAnimation) { + return moveUserInstant(room, roomUnit, targetTile, targetZ, resolvedBodyRotation, resolvedHeadRotation, oldLocation, oldTopItem, newTopItem, habbo); + } + + runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> { + roomUnit.setPreviousLocation(oldLocation); + roomUnit.setCurrentLocation(targetTile); + roomUnit.removeStatus(RoomUnitStatus.MOVE); + roomUnit.setZ(targetZ); + roomUnit.setBodyRotation(resolvedBodyRotation); + roomUnit.setHeadRotation(resolvedHeadRotation); + roomUnit.stopWalking(); + roomUnit.resetIdleTimer(); + + if (habbo != null) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos); + } + + roomUnit.statusUpdate(false); + }); + + List movements = new ArrayList<>(); + movements.add(WiredMovementsComposer.userSlideMovement( + roomUnit.getId(), + oldLocation.x, + oldLocation.y, + targetTile.x, + targetTile.y, + oldZ, + roomUnit.getZ(), + resolvedBodyRotation.getValue(), + resolvedHeadRotation.getValue(), + animationDuration)); + suppressStatusComposer(roomUnit, animationDuration); + room.sendComposer(new WiredMovementsComposer(movements).compose()); + + roomUnit.setPreviousLocation(targetTile); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); + + scheduleTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem, animationDuration); + schedulePostureSync(room, roomUnit, targetTile, animationDuration); + return true; + } + + public static boolean updateUserDirection(Room room, RoomUnit roomUnit, RoomUserRotation bodyRotation, RoomUserRotation headRotation) { + if (room == null || roomUnit == null) { + return false; + } + + RoomUserRotation resolvedBodyRotation = bodyRotation == null ? roomUnit.getBodyRotation() : bodyRotation; + RoomUserRotation resolvedHeadRotation = headRotation == null ? roomUnit.getHeadRotation() : headRotation; + + roomUnit.setBodyRotation(resolvedBodyRotation); + roomUnit.setHeadRotation(resolvedHeadRotation); + room.sendComposer(new WiredMovementsComposer(Collections.singletonList( + WiredMovementsComposer.userDirectionUpdate( + roomUnit.getId(), + resolvedHeadRotation.getValue(), + resolvedBodyRotation.getValue()))).compose()); + return true; + } + + public static boolean shouldSuppressStatusUpdate(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Set suppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + return suppressedRoomUnitIds != null && suppressedRoomUnitIds.contains(roomUnit.getId()); + } + + public static boolean shouldSuppressStatusComposer(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Long suppressedUntil = SUPPRESSED_STATUS_COMPOSER_UNTIL.get(roomUnit.getId()); + + if (suppressedUntil == null) { + return false; + } + + if (suppressedUntil <= System.currentTimeMillis()) { + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId(), suppressedUntil); + return false; + } + + return true; + } + + public static void clearStatusComposerSuppression(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId()); + } + + private static boolean hasBlockingUnits(Room room, RoomUnit roomUnit, RoomTile targetTile) { + Collection units = room.getRoomUnitsAt(targetTile); + + if (units == null || units.isEmpty()) { + return false; + } + + for (RoomUnit targetUnit : units) { + if (targetUnit != null && targetUnit != roomUnit) { + return true; + } + } + + return false; + } + + private static boolean moveUserInstant(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, RoomTile oldLocation, HabboItem oldTopItem, HabboItem newTopItem, Habbo habbo) { + runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> { + roomUnit.setPreviousLocation(oldLocation); + roomUnit.setCurrentLocation(targetTile); + roomUnit.removeStatus(RoomUnitStatus.MOVE); + roomUnit.setZ(targetZ); + roomUnit.setBodyRotation(bodyRotation); + roomUnit.setHeadRotation(headRotation); + roomUnit.stopWalking(); + roomUnit.resetIdleTimer(); + + if (habbo != null) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos); + } + + roomUnit.setPreviousLocation(targetTile); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); + roomUnit.statusUpdate(false); + }); + + processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem); + room.sendComposer(new RoomUserStatusComposer(roomUnit).compose()); + return true; + } + + private static void scheduleTileCallbacks(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, HabboItem oldTopItem, HabboItem newTopItem, int delay) { + if (oldTopItem == null && newTopItem == null) { + return; + } + + Emulator.getThreading().run(() -> { + processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem); + }, Math.max(delay, InteractionRoller.DELAY)); + } + + private static void processTileCallbacks(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, HabboItem oldTopItem, HabboItem newTopItem) { + if (room == null || !room.isLoaded() || roomUnit == null || roomUnit.getCurrentLocation() == null) { + return; + } + + if (roomUnit.getCurrentLocation().x != targetTile.x || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + if (oldTopItem != null && oldTopItem != newTopItem) { + try { + oldTopItem.onWalkOff(roomUnit, room, new Object[]{oldLocation, targetTile}); + } catch (Exception exception) { + LOGGER.error("Failed to process wired user walk off callback", exception); + } + } + + if (newTopItem != null && newTopItem != oldTopItem) { + try { + newTopItem.onWalkOn(roomUnit, room, new Object[]{oldLocation, targetTile}); + } catch (Exception exception) { + LOGGER.error("Failed to process wired user walk on callback", exception); + } + } + } + + private static void schedulePostureSync(Room room, RoomUnit roomUnit, RoomTile targetTile, int delay) { + if (!roomUnit.hasStatus(RoomUnitStatus.SIT) && !roomUnit.hasStatus(RoomUnitStatus.LAY)) { + return; + } + + Emulator.getThreading().run(() -> { + if (room == null || !room.isLoaded() || roomUnit == null || roomUnit.getCurrentLocation() == null) { + return; + } + + if (roomUnit.getCurrentLocation().x != targetTile.x || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + room.sendComposer(new RoomUserStatusComposer(roomUnit).compose()); + }, delay + STATUS_SUPPRESSION_GRACE_MS + 25); + } + + private static void suppressStatusComposer(RoomUnit roomUnit, int duration) { + if (roomUnit == null) { + return; + } + + long suppressedUntil = System.currentTimeMillis() + Math.max(duration, InteractionRoller.DELAY) + STATUS_SUPPRESSION_GRACE_MS; + SUPPRESSED_STATUS_COMPOSER_UNTIL.put(roomUnit.getId(), suppressedUntil); + } + + private static void runWithSuppressedStatusUpdates(Collection roomUnits, Runnable action) { + if (action == null) { + return; + } + + Set previousSuppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + HashSet suppressedRoomUnitIds = previousSuppressedRoomUnitIds == null + ? new HashSet<>() + : new HashSet<>(previousSuppressedRoomUnitIds); + + if (roomUnits != null) { + for (RoomUnit roomUnit : roomUnits) { + if (roomUnit != null) { + suppressedRoomUnitIds.add(roomUnit.getId()); + } + } + } + + if (suppressedRoomUnitIds.isEmpty()) { + action.run(); + return; + } + + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(suppressedRoomUnitIds); + + try { + action.run(); + } finally { + if (previousSuppressedRoomUnitIds == null || previousSuppressedRoomUnitIds.isEmpty()) { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.remove(); + } else { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(previousSuppressedRoomUnitIds); + } + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java index e6bd117e..b558520d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java @@ -41,6 +41,7 @@ public class Outgoing { public final static int HotelViewComposer = 122; public final static int UpdateFriendComposer = 2800; public final static int FloorItemUpdateComposer = 3776; + public final static int WiredMovementsComposer = 3999; public final static int RoomAccessDeniedComposer = 878; public final static int GuildFurniWidgetComposer = 3293; public final static int GiftConfigurationComposer = 2234; diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java new file mode 100644 index 00000000..07c61622 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java @@ -0,0 +1,206 @@ +package com.eu.habbo.messages.outgoing.rooms; + +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +import java.util.ArrayList; +import java.util.List; + +public class WiredMovementsComposer extends MessageComposer { + public static final int TYPE_USER_MOVE = 0; + public static final int TYPE_FURNI_MOVE = 1; + public static final int TYPE_WALL_ITEM_MOVE = 2; + public static final int TYPE_USER_DIRECTION = 3; + + public static final int USER_MOVEMENT_WALK = 0; + public static final int USER_MOVEMENT_SLIDE = 1; + public static final int DEFAULT_DURATION = 500; + + private final List movements; + + public WiredMovementsComposer(List movements) { + this.movements = movements == null ? new ArrayList<>() : movements; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.WiredMovementsComposer); + this.response.appendInt(this.movements.size()); + + for (MovementData movement : this.movements) { + this.response.appendInt(movement.getType()); + movement.append(this.response); + } + + return this.response; + } + + public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ) { + return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, 0, DEFAULT_DURATION); + } + + public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) { + return new FurniMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration); + } + + public static MovementData userWalkMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int bodyDirection, int headDirection, int duration) { + return new UserMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, USER_MOVEMENT_WALK, bodyDirection, headDirection, duration); + } + + public static MovementData userSlideMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int bodyDirection, int headDirection, int duration) { + return new UserMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, USER_MOVEMENT_SLIDE, bodyDirection, headDirection, duration); + } + + public static MovementData userDirectionUpdate(int id, int headDirection, int bodyDirection) { + return new UserDirectionData(id, headDirection, bodyDirection); + } + + public static MovementData wallItemMovement(int id, boolean enabled, int[] values) { + return new WallItemMovementData(id, enabled, values); + } + + public interface MovementData { + int getType(); + + void append(ServerMessage response); + } + + private abstract static class BaseMovementData implements MovementData { + private final int type; + + private BaseMovementData(int type) { + this.type = type; + } + + @Override + public int getType() { + return this.type; + } + } + + private static final class UserMovementData extends BaseMovementData { + private final int fromX; + private final int fromY; + private final int toX; + private final int toY; + private final double fromZ; + private final double toZ; + private final int id; + private final int movementType; + private final int bodyDirection; + private final int headDirection; + private final int duration; + + private UserMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int movementType, int bodyDirection, int headDirection, int duration) { + super(TYPE_USER_MOVE); + this.id = id; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + this.fromZ = fromZ; + this.toZ = toZ; + this.movementType = movementType; + this.bodyDirection = bodyDirection; + this.headDirection = headDirection; + this.duration = duration; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.fromX); + response.appendInt(this.fromY); + response.appendInt(this.toX); + response.appendInt(this.toY); + response.appendString(Double.toString(this.fromZ)); + response.appendString(Double.toString(this.toZ)); + response.appendInt(this.id); + response.appendInt(this.movementType); + response.appendInt(this.bodyDirection); + response.appendInt(this.headDirection); + response.appendInt(this.duration); + } + } + + private static final class FurniMovementData extends BaseMovementData { + private final int fromX; + private final int fromY; + private final int toX; + private final int toY; + private final double fromZ; + private final double toZ; + private final int id; + private final int rotation; + private final int duration; + + private FurniMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) { + super(TYPE_FURNI_MOVE); + this.id = id; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + this.fromZ = fromZ; + this.toZ = toZ; + this.rotation = rotation; + this.duration = duration; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.fromX); + response.appendInt(this.fromY); + response.appendInt(this.toX); + response.appendInt(this.toY); + response.appendString(Double.toString(this.fromZ)); + response.appendString(Double.toString(this.toZ)); + response.appendInt(this.id); + response.appendInt(this.rotation); + response.appendInt(this.duration); + } + } + + private static final class UserDirectionData extends BaseMovementData { + private final int id; + private final int headDirection; + private final int bodyDirection; + + private UserDirectionData(int id, int headDirection, int bodyDirection) { + super(TYPE_USER_DIRECTION); + this.id = id; + this.headDirection = headDirection; + this.bodyDirection = bodyDirection; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.id); + response.appendInt(this.headDirection); + response.appendInt(this.bodyDirection); + } + } + + private static final class WallItemMovementData extends BaseMovementData { + private final int id; + private final boolean enabled; + private final int[] values; + + private WallItemMovementData(int id, boolean enabled, int[] values) { + super(TYPE_WALL_ITEM_MOVE); + this.id = id; + this.enabled = enabled; + this.values = values == null ? new int[9] : values; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.id); + response.appendBoolean(this.enabled); + + for (int index = 0; index < 9; index++) { + response.appendInt(index < this.values.length ? this.values[index] : 0); + } + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java index 1a6d2e96..1e3d2af4 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java @@ -1,5 +1,7 @@ package com.eu.habbo.messages.outgoing.rooms.users; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; import com.eu.habbo.habbohotel.users.Habbo; @@ -8,7 +10,9 @@ import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; import gnu.trove.set.hash.THashSet; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; public class RoomUserStatusComposer extends MessageComposer { @@ -38,8 +42,20 @@ public class RoomUserStatusComposer extends MessageComposer { protected ServerMessage composeInternal() { this.response.init(Outgoing.RoomUserStatusComposer); if (this.roomUnits != null) { - this.response.appendInt(this.roomUnits.size()); + List roomUnits = new ArrayList<>(); + for (RoomUnit roomUnit : this.roomUnits) { + if (roomUnit == null + || WiredMoveCarryHelper.shouldSuppressStatusComposer(roomUnit) + || WiredUserMovementHelper.shouldSuppressStatusComposer(roomUnit)) { + continue; + } + + roomUnits.add(roomUnit); + } + + this.response.appendInt(roomUnits.size()); + for (RoomUnit roomUnit : roomUnits) { this.response.appendInt(roomUnit.getId()); this.response.appendInt(roomUnit.getPreviousLocation().x); this.response.appendInt(roomUnit.getPreviousLocation().y); @@ -59,8 +75,21 @@ public class RoomUserStatusComposer extends MessageComposer { } } else { synchronized (this.habbos) { - this.response.appendInt(this.habbos.size()); + List habbos = new ArrayList<>(); + for (Habbo habbo : this.habbos) { + if (habbo == null + || habbo.getRoomUnit() == null + || WiredMoveCarryHelper.shouldSuppressStatusComposer(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusComposer(habbo.getRoomUnit())) { + continue; + } + + habbos.add(habbo); + } + + this.response.appendInt(habbos.size()); + for (Habbo habbo : habbos) { this.response.appendInt(habbo.getRoomUnit().getId()); this.response.appendInt(habbo.getRoomUnit().getPreviousLocation().x); this.response.appendInt(habbo.getRoomUnit().getPreviousLocation().y);