feat(wired): add freeze and furni movement effects

- register interaction types wf_act_freeze, wf_act_unfreeze, wf_act_furni_to_user, wf_act_user_to_furni and wf_act_furni_to_furni

- add effect types FREEZE, UNFREEZE, FURNI_TO_USER, USER_TO_FURNI and FURNI_TO_FURNI

- add freeze handling with cancel-on-teleport and cleanup on teleport or room leave

- support furni-to-furni secondary target selection and immediate furni position persistence

- prevent frozen users from walking until they are unfrozen or moved out of the frozen state
This commit is contained in:
Lorenzune
2026-03-17 03:27:55 +01:00
parent 1a8fe52a14
commit d8a68ad8d3
17 changed files with 1156 additions and 6 deletions
@@ -256,6 +256,11 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_act_alert", WiredEffectAlert.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_handitem", WiredEffectGiveHandItem.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_effect", WiredEffectGiveEffect.class));
this.interactionsList.add(new ItemInteraction("wf_act_freeze", WiredEffectFreeze.class));
this.interactionsList.add(new ItemInteraction("wf_act_unfreeze", WiredEffectUnfreeze.class));
this.interactionsList.add(new ItemInteraction("wf_act_furni_to_user", WiredEffectFurniToUser.class));
this.interactionsList.add(new ItemInteraction("wf_act_user_to_furni", WiredEffectUserToFurni.class));
this.interactionsList.add(new ItemInteraction("wf_act_furni_to_furni", WiredEffectFurniToFurni.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_area", WiredEffectFurniArea.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_neighborhood", WiredEffectFurniNeighborhood.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_bytype", WiredEffectFurniByType.class));
@@ -0,0 +1,175 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.InteractionWiredTrigger;
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.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class WiredEffectFreeze extends InteractionWiredEffect {
private static final Set<Integer> ALLOWED_EFFECT_IDS = Set.of(218, 12, 11, 53, 163);
public static final WiredEffectType type = WiredEffectType.FREEZE;
private int effectId = 218;
private boolean cancelOnTeleport = false;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectFreeze(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFreeze(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
if (room.getHabbo(roomUnit) == null) {
continue;
}
WiredFreezeUtil.freeze(room, roomUnit, this.effectId, this.cancelOnTeleport);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(this.effectId, this.cancelOnTeleport, this.getDelay(), this.userSource));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.effectId = ALLOWED_EFFECT_IDS.contains(data.effectId) ? data.effectId : 218;
this.cancelOnTeleport = data.cancelOnTeleport;
this.setDelay(data.delay);
this.userSource = data.userSource;
} else {
this.effectId = 218;
this.cancelOnTeleport = false;
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.effectId = 218;
this.cancelOnTeleport = false;
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(3);
message.appendInt(this.effectId);
message.appendInt(this.cancelOnTeleport ? 1 : 0);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getBaseItem().getSpriteId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
if (settings.getIntParams().length < 3) {
throw new WiredSaveException("Invalid data");
}
int nextEffectId = settings.getIntParams()[0];
if (!ALLOWED_EFFECT_IDS.contains(nextEffectId)) {
throw new WiredSaveException("Invalid freeze effect");
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.effectId = nextEffectId;
this.cancelOnTeleport = settings.getIntParams()[1] == 1;
this.userSource = settings.getIntParams()[2];
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int effectId;
boolean cancelOnTeleport;
int delay;
int userSource;
public JsonData(int effectId, boolean cancelOnTeleport, int delay, int userSource) {
this.effectId = effectId;
this.cancelOnTeleport = cancelOnTeleport;
this.delay = delay;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,354 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.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.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class WiredEffectFurniToFurni extends InteractionWiredEffect {
private static final int SOURCE_SECONDARY_SELECTED = 101;
private static final String FURNI_SPLIT_REGEX = "[;,\\t]";
private static final String FURNI_DELIMITER = ";";
public static final WiredEffectType type = WiredEffectType.FURNI_TO_FURNI;
private final List<HabboItem> moveItems = new ArrayList<>();
private final List<HabboItem> targetItems = new ArrayList<>();
private int moveSource = WiredSourceUtil.SOURCE_TRIGGER;
private int targetSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectFurniToFurni(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFurniToFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
if (room == null) {
return;
}
HabboItem moveItem = this.resolveLastMoveItem(ctx);
HabboItem targetItem = this.resolveLastTargetItem(ctx);
if (moveItem == null || targetItem == null || moveItem.getId() == targetItem.getId()) {
return;
}
RoomTile targetTile = room.getLayout().getTile(targetItem.getX(), targetItem.getY());
if (targetTile == null) {
return;
}
FurnitureMovementError error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), null, true, false);
if (error != FurnitureMovementError.NONE) {
room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, true, false);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.moveItems.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.targetItems.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.moveSource,
this.targetSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.moveItems.clear();
this.targetItems.clear();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.moveSource = data.moveSource;
this.targetSource = this.normalizeTargetSource(data.targetSource);
this.loadItems(room, data.itemIds, this.moveItems);
this.loadItems(room, data.targetItemIds, this.targetItems);
if (this.moveSource == WiredSourceUtil.SOURCE_TRIGGER && !this.moveItems.isEmpty()) {
this.moveSource = WiredSourceUtil.SOURCE_SELECTED;
}
if (this.targetSource == WiredSourceUtil.SOURCE_TRIGGER && !this.targetItems.isEmpty()) {
this.targetSource = SOURCE_SECONDARY_SELECTED;
}
return;
}
if (wiredData != null && !wiredData.isEmpty()) {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
}
if (wiredDataOld.length >= 2 && !wiredDataOld[1].trim().isEmpty()) {
this.loadItems(room, this.parseIds(wiredDataOld[1], room), this.moveItems);
}
}
this.moveSource = this.moveItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.targetSource = this.targetItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED;
}
@Override
public void onPickUp() {
this.moveItems.clear();
this.targetItems.clear();
this.moveSource = WiredSourceUtil.SOURCE_TRIGGER;
this.targetSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
this.validateItems(this.moveItems);
this.validateItems(this.targetItems);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.moveItems.size());
for (HabboItem item : this.moveItems) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.serializeIds(this.targetItems));
message.appendInt(2);
message.appendInt(this.moveSource);
message.appendInt(this.targetSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
this.moveSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.targetSource = this.normalizeTargetSource((settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER);
Room room = this.getRoom();
if (room == null) {
throw new WiredSaveException("Room not found");
}
List<HabboItem> newMoveItems = new ArrayList<>();
if (this.moveSource == WiredSourceUtil.SOURCE_SELECTED) {
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newMoveItems.add(item);
}
}
if (newMoveItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
List<HabboItem> newTargetItems = new ArrayList<>();
if (this.targetSource == SOURCE_SECONDARY_SELECTED) {
newTargetItems.addAll(this.parseItems(settings.getStringParam(), room));
}
if (newTargetItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.moveItems.clear();
this.moveItems.addAll(newMoveItems);
this.targetItems.clear();
this.targetItems.addAll(newTargetItems);
this.setDelay(delay);
return true;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
protected long requiredCooldown() {
return COOLDOWN_MOVEMENT;
}
private HabboItem resolveLastMoveItem(WiredContext ctx) {
return this.resolveLastItem(ctx, this.moveSource, this.moveItems);
}
private HabboItem resolveLastTargetItem(WiredContext ctx) {
int source = (this.targetSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.targetSource;
return this.resolveLastItem(ctx, source, this.targetItems);
}
private HabboItem resolveLastItem(WiredContext ctx, int source, List<HabboItem> items) {
if (source == WiredSourceUtil.SOURCE_SELECTED) {
this.validateItems(items);
}
List<HabboItem> resolvedItems = WiredSourceUtil.resolveItems(ctx, source, items);
if (resolvedItems.isEmpty()) {
return null;
}
for (int index = resolvedItems.size() - 1; index >= 0; index--) {
HabboItem item = resolvedItems.get(index);
if (item != null) {
return item;
}
}
return null;
}
private List<HabboItem> parseItems(String data, Room room) throws WiredSaveException {
List<HabboItem> items = new ArrayList<>();
if (data == null || data.trim().isEmpty() || room == null) {
return items;
}
Set<Integer> seen = new HashSet<>();
for (String part : data.split(FURNI_SPLIT_REGEX)) {
if (part == null) {
continue;
}
String trimmed = part.trim();
if (trimmed.isEmpty()) {
continue;
}
int itemId;
try {
itemId = Integer.parseInt(trimmed);
} catch (NumberFormatException e) {
continue;
}
if (itemId <= 0 || !seen.add(itemId)) {
continue;
}
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
items.add(item);
}
return items;
}
private List<Integer> parseIds(String data, Room room) {
try {
return this.parseItems(data, room).stream().map(HabboItem::getId).collect(Collectors.toList());
} catch (WiredSaveException e) {
return new ArrayList<>();
}
}
private void loadItems(Room room, List<Integer> itemIds, List<HabboItem> target) {
if (room == null || itemIds == null || itemIds.isEmpty()) {
return;
}
for (Integer itemId : itemIds) {
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
target.add(item);
}
}
}
private int normalizeTargetSource(int source) {
return (source == WiredSourceUtil.SOURCE_SELECTED) ? SOURCE_SECONDARY_SELECTED : source;
}
private String serializeIds(List<HabboItem> items) {
if (items == null || items.isEmpty()) {
return "";
}
return items.stream()
.map(HabboItem::getId)
.distinct()
.map(String::valueOf)
.collect(Collectors.joining(FURNI_DELIMITER));
}
static class JsonData {
int delay;
List<Integer> itemIds;
List<Integer> targetItemIds;
int moveSource;
int targetSource;
public JsonData(int delay, List<Integer> itemIds, List<Integer> targetItemIds, int moveSource, int targetSource) {
this.delay = delay;
this.itemIds = itemIds;
this.targetItemIds = targetItemIds;
this.moveSource = moveSource;
this.targetSource = targetSource;
}
}
}
@@ -0,0 +1,57 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.habbohotel.items.Item;
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.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 java.sql.ResultSet;
import java.sql.SQLException;
public class WiredEffectFurniToUser extends WiredEffectUserFurniBase {
public static final WiredEffectType type = WiredEffectType.FURNI_TO_USER;
public WiredEffectFurniToUser(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFurniToUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
HabboItem item = this.resolveLastItem(ctx);
Habbo habbo = this.resolveLastHabbo(room, ctx);
if (room == null || item == null || habbo == null || habbo.getRoomUnit() == null) {
return;
}
RoomTile targetTile = habbo.getRoomUnit().getCurrentLocation();
if (targetTile == null) {
return;
}
FurnitureMovementError error = room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false);
if (error != FurnitureMovementError.NONE && item.getBaseItem().getStateCount() > 0) {
room.moveFurniTo(item, targetTile, item.getRotation(), item.getZ(), null, true, false);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public WiredEffectType getType() {
return type;
}
}
@@ -0,0 +1,149 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.InteractionWiredTrigger;
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.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class WiredEffectUnfreeze extends InteractionWiredEffect {
public static final WiredEffectType type = WiredEffectType.UNFREEZE;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectUnfreeze(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUnfreeze(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
if (room.getHabbo(roomUnit) == null || !WiredFreezeUtil.isFrozen(roomUnit)) {
continue;
}
WiredFreezeUtil.unfreeze(room, roomUnit);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(this.getDelay(), this.userSource));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.userSource = data.userSource;
} else {
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(1);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getBaseItem().getSpriteId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int delay;
int userSource;
public JsonData(int delay, int userSource) {
this.delay = delay;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,260 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.InteractionWiredTrigger;
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.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.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public abstract class WiredEffectUserFurniBase extends InteractionWiredEffect {
protected final List<HabboItem> items = new ArrayList<>();
protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
protected int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectUserFurniBase(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUserFurniBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
protected HabboItem resolveLastItem(WiredContext ctx) {
Room room = ctx.room();
List<HabboItem> effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
this.items.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
}
if (effectiveItems.isEmpty()) {
return null;
}
for (int index = effectiveItems.size() - 1; index >= 0; index--) {
HabboItem item = effectiveItems.get(index);
if (item != null) {
return item;
}
}
return null;
}
protected Habbo resolveLastHabbo(Room room, WiredContext ctx) {
Habbo targetHabbo = null;
for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
Habbo habbo = room.getHabbo(unit);
if (habbo != null) {
targetHabbo = habbo;
}
}
return targetHabbo;
}
protected List<Habbo> resolveHabbos(Room room, WiredContext ctx) {
List<Habbo> habbos = new ArrayList<>();
for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
Habbo habbo = room.getHabbo(unit);
if (habbo != null) {
habbos.add(habbo);
}
}
return habbos;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.items.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.furniSource,
this.userSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.furniSource = data.furniSource;
this.userSource = data.userSource;
if (data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
} else {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
}
if (wiredDataOld.length == 2 && wiredDataOld[1].contains(";")) {
for (String s : wiredDataOld[1].split(";")) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null) {
this.items.add(item);
}
}
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.items.clear();
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
itemsSnapshot.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
this.items.clear();
this.items.addAll(itemsSnapshot);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(itemsSnapshot.size());
for (HabboItem item : itemsSnapshot) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(2);
message.appendInt(this.furniSource);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
this.furniSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER;
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
throw new WiredSaveException("Room not found");
}
List<HabboItem> newItems = new ArrayList<>();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newItems.add(item);
}
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.items.clear();
this.items.addAll(newItems);
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int delay;
List<Integer> itemIds;
int furniSource;
int userSource;
public JsonData(int delay, List<Integer> itemIds, int furniSource, int userSource) {
this.delay = delay;
this.itemIds = itemIds;
this.furniSource = furniSource;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,48 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.rooms.Room;
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 java.sql.ResultSet;
import java.sql.SQLException;
public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
public static final WiredEffectType type = WiredEffectType.USER_TO_FURNI;
public WiredEffectUserToFurni(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUserToFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
HabboItem item = this.resolveLastItem(ctx);
if (room == null || item == null) {
return;
}
for (Habbo habbo : this.resolveHabbos(room, ctx)) {
room.teleportHabboToItem(habbo, item);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public WiredEffectType getType() {
return type;
}
}
@@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.users.DanceType;
import com.eu.habbo.habbohotel.users.Habbo;
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.messages.outgoing.generic.alerts.GenericErrorMessagesComposer;
import com.eu.habbo.messages.outgoing.inventory.AddPetComposer;
@@ -217,6 +218,12 @@ public class RoomUnitManager {
return;
}
if (habbo.getRoomUnit() != null) {
WiredManager.triggerUserLeavesRoom(this.room, habbo.getRoomUnit());
if (WiredFreezeUtil.isFrozen(habbo.getRoomUnit())) {
WiredFreezeUtil.unfreeze(this.room, habbo.getRoomUnit());
}
}
if (habbo.getRoomUnit() != null && habbo.getRoomUnit().getCurrentLocation() != null) {
habbo.getRoomUnit().getCurrentLocation().removeUnit(habbo.getRoomUnit());
}
@@ -1299,6 +1306,8 @@ public class RoomUnitManager {
*/
public void teleportRoomUnitToLocation(RoomUnit roomUnit, short x, short y, double z) {
if (this.room.isLoaded()) {
WiredFreezeUtil.onTeleport(this.room, roomUnit);
RoomTile tile = this.room.getLayout().getTile(x, y);
if (z < tile.z) {
@@ -1310,6 +1319,7 @@ public class RoomUnitManager {
roomUnit.setZ(z);
roomUnit.setPreviousLocationZ(z);
this.room.updateRoomUnit(roomUnit);
WiredFreezeUtil.restoreWalkState(roomUnit);
}
}
@@ -32,7 +32,12 @@ public enum WiredEffectType {
FURNI_BYTYPE_SELECTOR(30),
USERS_AREA_SELECTOR(31),
USERS_NEIGHBORHOOD_SELECTOR(32),
SEND_SIGNAL(33);
SEND_SIGNAL(33),
FREEZE(34),
UNFREEZE(35),
FURNI_TO_USER(36),
USER_TO_FURNI(37),
FURNI_TO_FURNI(38);
public final int code;
@@ -0,0 +1,74 @@
package com.eu.habbo.habbohotel.wired.core;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
public final class WiredFreezeUtil {
private static final String CACHE_ACTIVE = "wired.freeze.active";
private static final String CACHE_EFFECT_ID = "wired.freeze.effect_id";
private static final String CACHE_CANCEL_ON_TELEPORT = "wired.freeze.cancel_on_teleport";
private WiredFreezeUtil() {
}
public static boolean isFrozen(RoomUnit roomUnit) {
return roomUnit != null && Boolean.TRUE.equals(roomUnit.getCacheable().get(CACHE_ACTIVE));
}
public static void freeze(Room room, RoomUnit roomUnit, int effectId, boolean cancelOnTeleport) {
if (room == null || roomUnit == null || effectId <= 0) {
return;
}
roomUnit.getCacheable().put(CACHE_ACTIVE, true);
roomUnit.getCacheable().put(CACHE_EFFECT_ID, effectId);
roomUnit.getCacheable().put(CACHE_CANCEL_ON_TELEPORT, cancelOnTeleport);
roomUnit.stopWalking();
roomUnit.setCanWalk(false);
roomUnit.statusUpdate(true);
room.giveEffect(roomUnit, effectId, Integer.MAX_VALUE);
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
}
public static void unfreeze(Room room, RoomUnit roomUnit) {
if (roomUnit == null) {
return;
}
roomUnit.getCacheable().remove(CACHE_ACTIVE);
roomUnit.getCacheable().remove(CACHE_EFFECT_ID);
roomUnit.getCacheable().remove(CACHE_CANCEL_ON_TELEPORT);
roomUnit.stopWalking();
roomUnit.setCanWalk(true);
roomUnit.statusUpdate(true);
if (room != null) {
room.giveEffect(roomUnit, 0, -1);
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
} else {
roomUnit.setEffectId(0, 0);
}
}
public static void onTeleport(Room room, RoomUnit roomUnit) {
if (!isFrozen(roomUnit)) {
return;
}
if (Boolean.TRUE.equals(roomUnit.getCacheable().get(CACHE_CANCEL_ON_TELEPORT))) {
unfreeze(room, roomUnit);
}
}
public static void restoreWalkState(RoomUnit roomUnit) {
if (roomUnit == null) {
return;
}
roomUnit.setCanWalk(!isFrozen(roomUnit));
}
}
@@ -10,6 +10,7 @@ import com.eu.habbo.habbohotel.rooms.BedProfile;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitOnRollerComposer;
import com.eu.habbo.plugin.events.users.UserIdleEvent;
@@ -46,7 +47,7 @@ public class RoomUserWalkEvent extends MessageHandler {
Room room = habboInfo.getCurrentRoom();
try {
if (roomUnit != null && roomUnit.isInRoom() && roomUnit.canWalk()) {
if (roomUnit != null && roomUnit.isInRoom() && roomUnit.canWalk() && !WiredFreezeUtil.isFrozen(roomUnit)) {
if (roomUnit.cmdTeleport) {
handleTeleport(room, (short) x, (short) y, roomUnit, habboInfo);
return;
@@ -5,6 +5,7 @@ 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.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +39,8 @@ public class RoomUnitTeleport implements Runnable {
return;
}
WiredFreezeUtil.onTeleport(this.room, this.roomUnit);
RoomTile lastLocation = this.roomUnit.getCurrentLocation();
RoomTile newLocation = this.room.getLayout().getTile((short) this.x, (short) this.y);
@@ -60,6 +63,7 @@ public class RoomUnitTeleport implements Runnable {
//this.room.sendComposer(teleportMessage);
this.roomUnit.statusUpdate(true);
roomUnit.isWiredTeleporting = false;
WiredFreezeUtil.restoreWalkState(this.roomUnit);
this.room.updateHabbosAt(newLocation.x, newLocation.y);
this.room.updateBotsAt(newLocation.x, newLocation.y);
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile;
import com.eu.habbo.habbohotel.rooms.*;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import com.eu.habbo.threading.runnables.RoomUnitWalkToLocation;
@@ -46,6 +47,7 @@ class TeleportActionFive implements Runnable {
List<Runnable> onSuccess = new ArrayList<Runnable>();
onSuccess.add(() -> {
unit.setCanLeaveRoomByDoor(true);
WiredFreezeUtil.restoreWalkState(unit);
Emulator.getThreading().run(() -> {
unit.isLeavingTeleporter = false;
@@ -57,6 +59,8 @@ class TeleportActionFive implements Runnable {
unit.statusUpdate(true);
unit.isLeavingTeleporter = true;
Emulator.getThreading().run(new RoomUnitWalkToLocation(unit, tile, room, onSuccess, onSuccess));
} else {
WiredFreezeUtil.restoreWalkState(unit);
}
this.currentTeleport.setExtradata("1");
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
class TeleportActionFour implements Runnable {
private final HabboItem currentTeleport;
@@ -21,7 +22,7 @@ class TeleportActionFour implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
this.currentTeleport.setExtradata("0");
this.room.updateItem(this.currentTeleport);
return;
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
public class TeleportActionOne implements Runnable {
@@ -25,7 +26,7 @@ public class TeleportActionOne implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}
@@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
class TeleportActionThree implements Runnable {
private final HabboItem currentTeleport;
@@ -26,7 +27,7 @@ class TeleportActionThree implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import org.slf4j.Logger;
@@ -41,7 +42,7 @@ class TeleportActionTwo implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}