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);