From e035a5dca3da9813d1da3851bc0b5de0d3b00236 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Mon, 16 Mar 2026 15:12:42 +0100 Subject: [PATCH 1/9] feat(wired): add antenna signals and selector-aware snapshots --- .../habbo/habbohotel/items/ItemManager.java | 1 + .../WiredConditionMatchStatePosition.java | 113 ++++---- .../wired/effects/WiredEffectMatchFurni.java | 139 ++++++---- .../wired/effects/WiredEffectSendSignal.java | 255 +++++++++++++----- .../selector/WiredEffectFurniByType.java | 18 +- .../triggers/WiredTriggerReceiveSignal.java | 119 +++++++- .../habbohotel/wired/core/WiredEngine.java | 50 ++-- .../wired/WiredTriggerSaveDataEvent.java | 22 +- .../wired/WiredTriggerSaveException.java | 7 + 9 files changed, 503 insertions(+), 221 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveException.java 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 b33f1c10..d24339e2 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 @@ -201,6 +201,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("random_state", InteractionRandomState.class)); this.interactionsList.add(new ItemInteraction("vendingmachine_no_sides", InteractionNoSidesVendingMachine.class)); this.interactionsList.add(new ItemInteraction("tile_walkmagic", InteractionTileWalkMagic.class)); + this.interactionsList.add(new ItemInteraction("antenna", InteractionDefault.class)); this.interactionsList.add(new ItemInteraction("game_timer", InteractionGameTimer.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java index bc965875..7966d563 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java @@ -19,9 +19,7 @@ import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class WiredConditionMatchStatePosition extends InteractionWiredCondition implements InteractionWiredMatchFurniSettings { public static final WiredConditionType type = WiredConditionType.MATCH_SSHOT; @@ -92,14 +90,12 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.settings.clear(); - if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - for (int i = 0; i < count; i++) { - int itemId = settings.getFurniIds()[i]; - HabboItem item = room.getHabboItem(itemId); + for (int i = 0; i < count; i++) { + int itemId = settings.getFurniIds()[i]; + HabboItem item = room.getHabboItem(itemId); - if (item != null) - this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY())); - } + if (item != null) + this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY())); } return true; @@ -108,65 +104,71 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition @Override public boolean evaluate(WiredContext ctx) { Room room = ctx.room(); + this.refresh(); + if (this.settings.isEmpty()) return true; - List targets = null; - Set targetIds = null; - if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { - targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); if (targets.isEmpty()) return false; - targetIds = new HashSet<>(); - for (HabboItem item : targets) { - if (item != null) targetIds.add(item.getId()); - } - if (targetIds.isEmpty()) return false; - } - THashSet toRemove = new THashSet<>(); - Set settingsIds = new HashSet<>(); + for (HabboItem item : targets) { + if (item == null) return false; + + WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); + if (setting == null) { + return false; + } + + if (!this.matchesSetting(item, setting)) { + return false; + } + } + + return true; + } for (WiredMatchFurniSetting setting : this.settings) { - if (targetIds != null && !targetIds.contains(setting.item_id)) { - continue; - } HabboItem item = room.getHabboItem(setting.item_id); - - if (item != null) { - settingsIds.add(setting.item_id); - if (this.state) { - if (!item.getExtradata().equals(setting.state)) - return false; - } - - if (this.position) { - if (!(setting.x == item.getX() && setting.y == item.getY())) - return false; - } - - if (this.direction) { - if (setting.rotation != item.getRotation()) - return false; - } - } else { - toRemove.add(setting); - } - } - - if (targetIds != null && !settingsIds.containsAll(targetIds)) { - return false; - } - - if (!toRemove.isEmpty()) { - for (WiredMatchFurniSetting setting : toRemove) { - this.settings.remove(setting); - } + if (item == null) continue; + if (!this.matchesSetting(item, setting)) + return false; } return true; } + private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) { + WiredMatchFurniSetting fallback = null; + + for (WiredMatchFurniSetting setting : this.settings) { + HabboItem sourceItem = room.getHabboItem(setting.item_id); + if (sourceItem == null) continue; + if (sourceItem.getBaseItem().getId() != target.getBaseItem().getId()) continue; + + if (setting.state.equals(target.getExtradata())) { + return setting; + } + + if (fallback == null) { + fallback = setting; + } + } + + return fallback; + } + + private boolean matchesSetting(HabboItem item, WiredMatchFurniSetting setting) { + if (this.state && !item.getExtradata().equals(setting.state)) + return false; + + if (this.position && !(setting.x == item.getX() && setting.y == item.getY())) + return false; + + return !this.direction || setting.rotation == item.getRotation(); + } + @Deprecated @Override public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { @@ -214,9 +216,6 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.position = data[4].equals("1"); this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.settings.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; - } } @Override 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 bab1896a..90348f35 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 @@ -50,6 +50,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int @Override public void execute(WiredContext ctx) { Room room = ctx.room(); + this.refresh(); if(this.settings.isEmpty()) return; @@ -57,54 +58,80 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int if (room.getLayout() == null) return; - java.util.Set allowedItemIds = null; - if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { - allowedItemIds = new java.util.HashSet<>(); - for (HabboItem si : WiredSourceUtil.resolveItems(ctx, this.furniSource, null)) { - if (si != null) { - allowedItemIds.add(si.getId()); + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + for (WiredMatchFurniSetting setting : this.settings) { + HabboItem item = room.getHabboItem(setting.item_id); + if (item != null) { + this.applySetting(room, item, setting); } } - if (allowedItemIds.isEmpty()) { - return; + + return; + } + + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); + if (targets.isEmpty()) { + return; + } + + for (HabboItem item : targets) { + if (item == null) continue; + + WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); + if (setting == null) continue; + + this.applySetting(room, item, setting); + } + } + + private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) { + WiredMatchFurniSetting fallback = null; + + for (WiredMatchFurniSetting setting : this.settings) { + HabboItem sourceItem = room.getHabboItem(setting.item_id); + if (sourceItem == null) continue; + if (sourceItem.getBaseItem().getId() != target.getBaseItem().getId()) continue; + + if (setting.state.equals(target.getExtradata())) { + return setting; + } + + if (fallback == null) { + fallback = setting; } } - for (WiredMatchFurniSetting setting : this.settings) { - if (allowedItemIds != null && !allowedItemIds.contains(setting.item_id)) continue; + return fallback; + } - HabboItem item = room.getHabboItem(setting.item_id); - if (item != null) { - if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) { - if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) { - item.setExtradata(setting.state); - room.updateItemState(item); + private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting) { + if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) { + if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) { + item.setExtradata(setting.state); + room.updateItemState(item); + } + } + + 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); + } + } + else if(this.position) { + boolean slideAnimation = !this.direction || item.getRotation() == setting.rotation; + RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); + int newRotation = this.direction ? setting.rotation : item.getRotation(); + + 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, null, !slideAnimation) == FurnitureMovementError.NONE) { + if(slideAnimation) { + room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); } } - - RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - if (oldLocation == null) continue; - 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); - } - } - else if(this.position) { - boolean slideAnimation = !this.direction || item.getRotation() == setting.rotation; - RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); - int newRotation = this.direction ? setting.rotation : item.getRotation(); - - 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, null, !slideAnimation) == FurnitureMovementError.NONE) { - if(slideAnimation) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } - } - } - } - } } } @@ -134,9 +161,6 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.settings.clear(); this.settings.addAll(data.items); this.furniSource = data.furniSource; - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.settings.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; - } } else { String[] data = set.getString("wired_data").split(":"); @@ -221,23 +245,22 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int if (room == null) throw new WiredSaveException("Trying to save wired in unloaded room"); + int itemsCount = settings.getFurniIds().length; + + if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + List newSettings = new ArrayList<>(); - if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - int itemsCount = settings.getFurniIds().length; - if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { - throw new WiredSaveException("Too many furni selected"); - } + for (int i = 0; i < itemsCount; i++) { + int itemId = settings.getFurniIds()[i]; + HabboItem it = room.getHabboItem(itemId); - for (int i = 0; i < itemsCount; i++) { - int itemId = settings.getFurniIds()[i]; - HabboItem it = room.getHabboItem(itemId); + if(it == null) + throw new WiredSaveException(String.format("Item %s not found", itemId)); - if(it == null) - throw new WiredSaveException(String.format("Item %s not found", itemId)); - - newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY())); - } + newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY())); } int delay = settings.getDelay(); @@ -249,9 +272,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.direction = setDirection; this.position = setPosition; this.settings.clear(); - if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - this.settings.addAll(newSettings); - } + this.settings.addAll(newSettings); this.setDelay(delay); return true; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java index 66a4429d..82bdd99c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -36,14 +37,16 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { private static final int ANTENNA_PICKED = 0; private static final int ANTENNA_TRIGGER = 1; - - private static final int FORWARD_NONE = 0; - private static final int FORWARD_TRIGGER = 1; + private static final String ANTENNA_INTERACTION = "antenna"; + private static final String FORWARD_ITEM_SPLIT_REGEX = "[;,\\t]"; + private static final long ANTENNA_PULSE_MS = 300L; + private static final ConcurrentHashMap ANTENNA_PULSE_TOKENS = new ConcurrentHashMap<>(); private THashSet items; + private THashSet forwardItems; private int antennaSource = ANTENNA_PICKED; - private int furniForward = FORWARD_NONE; - private int userForward = FORWARD_NONE; + private int furniForward = WiredSourceUtil.SOURCE_TRIGGER; + private int userForward = WiredSourceUtil.SOURCE_TRIGGER; private boolean signalPerFurni = false; private boolean signalPerUser = false; private int channel = 0; @@ -51,11 +54,13 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { public WiredEffectSendSignal(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); this.items = new THashSet<>(); + this.forwardItems = new THashSet<>(); } public WiredEffectSendSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { super(id, userId, item, extradata, limitedStack, limitedSells); this.items = new THashSet<>(); + this.forwardItems = new THashSet<>(); } @Override @@ -77,73 +82,68 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { .map(Collections::singleton) .orElse(Collections.emptySet()); } else { - antennas = ctx.targets().isItemsModifiedBySelector() - ? new ArrayList<>(ctx.targets().items()) - : new ArrayList<>(this.items); + Collection baseAntennas = new ArrayList<>(this.items); + + if (baseAntennas.isEmpty() && antennaSource > ANTENNA_TRIGGER) { + HabboItem antenna = room.getHabboItem(antennaSource); + antennas = (antenna != null) ? Collections.singleton(antenna) : Collections.emptySet(); + } else { + antennas = baseAntennas; + } } - if (antennas.isEmpty()) { + List resolvedAntennas = antennas.stream() + .filter(Objects::nonNull) + .filter(this::isAntennaItem) + .collect(Collectors.toList()); + + if (resolvedAntennas.isEmpty()) { LOGGER.debug("[SendSignal] No antennas resolved, aborting. antennaSource={}, selectorModified={}", antennaSource, ctx.targets().isItemsModifiedBySelector()); return; } - LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", antennas.size()); + LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", resolvedAntennas.size()); - RoomUnit forwardedUser = null; - if (userForward == FORWARD_TRIGGER) { - forwardedUser = ctx.actor().orElse(null); - } + List forwardedUsers = WiredSourceUtil.resolveUsers(ctx, this.userForward); + List forwardedFurni = WiredSourceUtil.resolveItems(ctx, this.furniForward, this.forwardItems); - HabboItem forwardedFurni = null; - if (furniForward == FORWARD_TRIGGER) { - forwardedFurni = ctx.sourceItem().orElse(null); - } + RoomUnit defaultUser = forwardedUsers.isEmpty() ? null : forwardedUsers.get(0); + HabboItem defaultFurni = forwardedFurni.isEmpty() ? null : forwardedFurni.get(0); - Set visitedTiles = new HashSet<>(); - List antennaTiles = new ArrayList<>(); - for (HabboItem antenna : antennas) { - if (antenna == null) continue; - String key = antenna.getX() + "," + antenna.getY(); - if (visitedTiles.add(key)) { - RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY()); - if (tile != null) { - antennaTiles.add(tile); - } - } - } + Collection usersToSend = (signalPerUser && !forwardedUsers.isEmpty()) + ? forwardedUsers + : Collections.singletonList(defaultUser); + + Collection furniToSend = (signalPerFurni && !forwardedFurni.isEmpty()) + ? forwardedFurni + : Collections.singletonList(defaultFurni); int nextDepth = currentDepth + 1; - if (signalPerFurni || signalPerUser) { - if (signalPerFurni) { - for (RoomTile tile : antennaTiles) { - fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); + for (RoomUnit user : usersToSend) { + for (HabboItem sourceItem : furniToSend) { + for (HabboItem antenna : resolvedAntennas) { + fireSignalAtAntenna(room, antenna, user, sourceItem, nextDepth); } } - if (signalPerUser && ctx.targets().hasUsers()) { - for (RoomUnit user : ctx.targets().users()) { - for (RoomTile tile : antennaTiles) { - fireSignalAtTile(room, tile, user, forwardedFurni, nextDepth); - } - } - } else if (!signalPerFurni) { - for (RoomTile tile : antennaTiles) { - fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); - } - } - } else { - for (RoomTile tile : antennaTiles) { - fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); - } } } - private void fireSignalAtTile(Room room, RoomTile tile, RoomUnit actor, HabboItem sourceItem, int depth) { - LOGGER.debug("[SendSignal] fireSignalAtTile: tile={},{} depth={} channel={} actor={} sourceItem={}", tile.x, tile.y, depth, channel, actor != null ? actor.getId() : "null", sourceItem != null ? sourceItem.getId() : "null"); + private void fireSignalAtAntenna(Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) { + if (antenna == null) return; + RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY()); + if (tile == null) return; + + pulseAntenna(room, antenna); + + int signalChannel = antenna.getId(); + + LOGGER.debug("[SendSignal] fireSignalAtAntenna: antennaId={} tile={},{} depth={} channel={} actor={} sourceItem={}", + signalChannel, tile.x, tile.y, depth, signalChannel, actor != null ? actor.getId() : "null", sourceItem != null ? sourceItem.getId() : "null"); WiredEvent.Builder builder = WiredEvent.builder(WiredEvent.Type.SIGNAL_RECEIVED, room) .tile(tile) .callStackDepth(depth) - .signalChannel(this.channel) + .signalChannel(signalChannel) .triggeredByEffect(true); if (actor != null) builder.actor(actor); @@ -153,6 +153,33 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { LOGGER.debug("[SendSignal] handleEvent returned: {}", result); } + private void pulseAntenna(Room room, HabboItem antenna) { + if (room == null || antenna == null || antenna.getBaseItem() == null) return; + if (antenna.getBaseItem().getStateCount() <= 1) return; + + final long token = System.currentTimeMillis(); + ANTENNA_PULSE_TOKENS.put(antenna.getId(), token); + + if ("1".equals(antenna.getExtradata())) { + antenna.setExtradata("0"); + room.updateItemState(antenna); + } + + antenna.setExtradata("1"); + room.updateItemState(antenna); + + Emulator.getThreading().run(() -> { + if (!room.isLoaded()) return; + + Long currentToken = ANTENNA_PULSE_TOKENS.get(antenna.getId()); + if (currentToken == null || currentToken.longValue() != token) return; + + antenna.setExtradata("0"); + room.updateItemState(antenna); + ANTENNA_PULSE_TOKENS.remove(antenna.getId(), token); + }, ANTENNA_PULSE_MS); + } + @Override public void serializeWiredData(ServerMessage message, Room room) { List itemsSnapshot = new ArrayList<>(this.items); @@ -161,6 +188,16 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null); this.items.retainAll(itemsSnapshot); + List forwardSnapshot = new ArrayList<>(this.forwardItems); + forwardSnapshot.removeIf(item -> + item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null); + this.forwardItems.retainAll(forwardSnapshot); + + String forwardString = forwardSnapshot.stream() + .filter(Objects::nonNull) + .map(item -> Integer.toString(item.getId())) + .collect(Collectors.joining(";")); + message.appendBoolean(false); message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); message.appendInt(itemsSnapshot.size()); @@ -169,7 +206,7 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { } message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); - message.appendString(""); + message.appendString(forwardString); message.appendInt(6); message.appendInt(antennaSource); @@ -219,6 +256,12 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { newItems.add(it); } + for (HabboItem receiver : newItems) { + if (!isAntennaItem(receiver)) { + throw new WiredSaveException("Only antenna furni can be selected"); + } + } + if (room != null && room.getRoomSpecialTypes() != null) { for (HabboItem receiver : newItems) { int count = room.getRoomSpecialTypes().countSendersTargetingReceiver(receiver.getId(), this); @@ -234,18 +277,36 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { } int[] params = settings.getIntParams(); - this.antennaSource = params.length > 0 ? params[0] : ANTENNA_PICKED; - this.furniForward = params.length > 1 ? params[1] : FORWARD_NONE; - this.userForward = params.length > 2 ? params[2] : FORWARD_NONE; + int requestedAntennaSource = params.length > 0 ? params[0] : ANTENNA_PICKED; + this.furniForward = normalizeSource(params.length > 1 ? params[1] : WiredSourceUtil.SOURCE_TRIGGER); + this.userForward = normalizeSource(params.length > 2 ? params[2] : WiredSourceUtil.SOURCE_TRIGGER); this.signalPerFurni = params.length > 3 && params[3] == 1; this.signalPerUser = params.length > 4 && params[4] == 1; this.channel = params.length > 5 ? params[5] : 0; + this.antennaSource = requestedAntennaSource; + if (!newItems.isEmpty()) { + this.antennaSource = newItems.get(0).getId(); + } + + List newForwardItems = new ArrayList<>(); + if (this.furniForward == WiredSourceUtil.SOURCE_SELECTED && room != null) { + newForwardItems = parseForwardItems(settings.getStringParam(), room); + } + if (newForwardItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + this.items.clear(); this.items.addAll(newItems); + + this.forwardItems.clear(); + if (this.furniForward == WiredSourceUtil.SOURCE_SELECTED) { + this.forwardItems.addAll(newForwardItems); + } this.setDelay(delay); - LOGGER.debug("[SendSignal] saveData: antennaSource={}, furniForward={}, userForward={}, signalPerFurni={}, signalPerUser={}, channel={}, items={}", - antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel, items.size()); + LOGGER.debug("[SendSignal] saveData: antennaSource={}, furniForward={}, userForward={}, signalPerFurni={}, signalPerUser={}, channel={}, items={}, forwardItems={}", + antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel, items.size(), forwardItems.size()); return true; } @@ -259,9 +320,11 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { @Override public String getWiredData() { List itemsSnapshot = new ArrayList<>(this.items); + List forwardSnapshot = new ArrayList<>(this.forwardItems); return WiredManager.getGson().toJson(new JsonData( this.getDelay(), itemsSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()), + forwardSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()), antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel )); } @@ -269,14 +332,15 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items = new THashSet<>(); + this.forwardItems = new THashSet<>(); String wiredData = set.getString("wired_data"); if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); this.antennaSource = data.antennaSource; - this.furniForward = data.furniForward; - this.userForward = data.userForward; + this.furniForward = normalizeSource(data.furniForward); + this.userForward = normalizeSource(data.userForward); this.signalPerFurni = data.signalPerFurni; this.signalPerUser = data.signalPerUser; this.channel = data.channel; @@ -286,21 +350,84 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { if (item != null) this.items.add(item); } } + if (data.forwardItemIds != null) { + for (Integer id : data.forwardItemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) this.forwardItems.add(item); + } + } + + if (this.antennaSource <= ANTENNA_TRIGGER && !this.items.isEmpty()) { + HabboItem first = this.items.iterator().next(); + if (first != null) this.antennaSource = first.getId(); + } } } @Override public void onPickUp() { this.items.clear(); + this.forwardItems.clear(); this.antennaSource = ANTENNA_PICKED; - this.furniForward = FORWARD_NONE; - this.userForward = FORWARD_NONE; + this.furniForward = WiredSourceUtil.SOURCE_TRIGGER; + this.userForward = WiredSourceUtil.SOURCE_TRIGGER; this.signalPerFurni = false; this.signalPerUser = false; this.channel = 0; this.setDelay(0); } + private int normalizeSource(int source) { + if (source == 1) return WiredSourceUtil.SOURCE_TRIGGER; + if (source == WiredSourceUtil.SOURCE_TRIGGER + || source == WiredSourceUtil.SOURCE_SELECTED + || source == WiredSourceUtil.SOURCE_SELECTOR + || source == WiredSourceUtil.SOURCE_SIGNAL) { + return source; + } + return WiredSourceUtil.SOURCE_TRIGGER; + } + + private List parseForwardItems(String data, Room room) throws WiredSaveException { + List results = new ArrayList<>(); + if (data == null || data.trim().isEmpty() || room == null) return results; + + Set seen = new HashSet<>(); + String[] parts = data.split(FORWARD_ITEM_SPLIT_REGEX); + + for (String part : parts) { + 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)); + + results.add(item); + } + + return results; + } + + private boolean isAntennaItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false; + String interaction = item.getBaseItem().getInteractionType().getName(); + if (interaction == null) return false; + + String normalized = interaction.toLowerCase(); + return normalized.equals(ANTENNA_INTERACTION); + } + @Override public WiredEffectType getType() { return type; @@ -328,6 +455,7 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { static class JsonData { int delay; List itemIds; + List forwardItemIds; int antennaSource; int furniForward; int userForward; @@ -335,10 +463,11 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { boolean signalPerUser; int channel; - public JsonData(int delay, List itemIds, int antennaSource, int furniForward, + public JsonData(int delay, List itemIds, List forwardItemIds, int antennaSource, int furniForward, int userForward, boolean signalPerFurni, boolean signalPerUser, int channel) { this.delay = delay; this.itemIds = itemIds; + this.forwardItemIds = forwardItemIds; this.antennaSource = antennaSource; this.furniForward = furniForward; this.userForward = userForward; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java index 5edfed63..14bae162 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java @@ -112,17 +112,17 @@ public class WiredEffectFurniByType extends InteractionWiredEffect { @Override public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { int[] params = settings.getIntParams(); - if (params == null || params.length < 1) { - throw new WiredSaveException("wf_slc_furni_bytype: intParams must have at least 1 element"); + if (params == null || params.length < 4) { + throw new WiredSaveException("wf_slc_furni_bytype: intParams must have at least 4 elements"); } - this.sourceType = params[0]; + this.sourceType = SOURCE_FURNI_PICKED; this.matchState = params.length > 1 && params[1] == 1; this.filterExisting = params.length > 2 && params[2] == 1; this.invert = params.length > 3 && params[3] == 1; this.pickedFurniIds = new ArrayList<>(); - if (this.sourceType == SOURCE_FURNI_PICKED && settings.getFurniIds() != null) { + if (settings.getFurniIds() != null) { for (int id : settings.getFurniIds()) { if (pickedFurniIds.size() >= MAX_PICKED_FURNI) break; pickedFurniIds.add(id); @@ -135,12 +135,10 @@ public class WiredEffectFurniByType extends InteractionWiredEffect { @Override public void serializeWiredData(ServerMessage message, Room room) { - boolean pickMode = (sourceType == SOURCE_FURNI_PICKED); + message.appendBoolean(true); + message.appendInt(MAX_PICKED_FURNI); - message.appendBoolean(pickMode); - message.appendInt(pickMode ? MAX_PICKED_FURNI : 0); - - if (pickMode && !pickedFurniIds.isEmpty()) { + if (!pickedFurniIds.isEmpty()) { message.appendInt(pickedFurniIds.size()); pickedFurniIds.forEach(message::appendInt); } else { @@ -152,7 +150,7 @@ public class WiredEffectFurniByType extends InteractionWiredEffect { message.appendString(""); message.appendInt(4); - message.appendInt(sourceType); + message.appendInt(SOURCE_FURNI_PICKED); message.appendInt(matchState ? 1 : 0); message.appendInt(filterExisting ? 1 : 0); message.appendInt(invert ? 1 : 0); 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 c2e96e93..13463c7b 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 @@ -1,5 +1,6 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; +import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -11,27 +12,49 @@ import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; +import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer; +import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.RECEIVE_SIGNAL; + private static final String ANTENNA_INTERACTION = "antenna"; + private static final long ACTIVATION_PULSE_MS = 300L; + private int channel = 0; // signal channel (0-based) + private THashSet items; + private final AtomicLong activationToken = new AtomicLong(); public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); + this.items = new THashSet<>(); } public WiredTriggerReceiveSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); } @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { - return event.getType() == WiredEvent.Type.SIGNAL_RECEIVED - && event.getSignalChannel() == this.channel; + if (event.getType() != WiredEvent.Type.SIGNAL_RECEIVED) return false; + + if (!this.items.isEmpty()) { + int signalChannel = event.getSignalChannel(); + for (HabboItem antenna : this.items) { + if (antenna != null && antenna.getId() == signalChannel) return true; + } + return false; + } + + return event.getSignalChannel() == this.channel; } public int getChannel() { @@ -59,14 +82,33 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { int senderCount = 0; try { if (room != null && room.getRoomSpecialTypes() != null) { - senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); + if (!this.items.isEmpty()) { + for (HabboItem item : this.items) { + senderCount += room.getRoomSpecialTypes().countSendersTargetingReceiver(item.getId()); + } + } else { + senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); + } } } catch (Exception e) { } + THashSet itemsToRemove = new THashSet<>(); + for (HabboItem item : this.items) { + if (item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null) { + itemsToRemove.add(item); + } + } + for (HabboItem item : itemsToRemove) { + this.items.remove(item); + } + message.appendBoolean(false); - message.appendInt(0); - message.appendInt(0); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); @@ -82,37 +124,100 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + this.items.clear(); + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + int count = settings.getFurniIds().length; + + for (int i = 0; i < count; i++) { + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); + if (item == null) continue; + if (!isAntennaItem(item)) throw new WiredTriggerSaveException("wiredfurni.error.require_antenna_furni"); + this.items.add(item); + } + int[] params = settings.getIntParams(); this.channel = params.length > 0 ? params[0] : 0; return true; } + @Override + public void activateBox(Room room, RoomUnit roomUnit, long millis) { + if (roomUnit != null) { + this.addUserExecutionCache(roomUnit.getId(), millis); + } + + if (room == null || room.isHideWired() || this.getBaseItem().getStateCount() <= 1) { + return; + } + + final long token = this.activationToken.incrementAndGet(); + + if ("1".equals(this.getExtradata())) { + this.setExtradata("0"); + room.sendComposer(new ItemStateComposer(this).compose()); + } + + this.setExtradata("1"); + room.sendComposer(new ItemStateComposer(this).compose()); + + Emulator.getThreading().run(() -> { + if (!room.isLoaded()) return; + if (this.activationToken.get() != token) return; + + this.setExtradata("0"); + room.sendComposer(new ItemStateComposer(this).compose()); + }, ACTIVATION_PULSE_MS); + } + @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(channel)); + return WiredManager.getGson().toJson(new JsonData( + channel, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items = new THashSet<>(); String wiredData = set.getString("wired_data"); if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.channel = data.channel; + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) this.items.add(item); + } + } } } @Override public void onPickUp() { this.channel = 0; + this.items.clear(); + } + + private boolean isAntennaItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false; + String interaction = item.getBaseItem().getInteractionType().getName(); + if (interaction == null) return false; + + String normalized = interaction.toLowerCase(); + return normalized.equals(ANTENNA_INTERACTION); } static class JsonData { int channel; + List itemIds; public JsonData() {} - public JsonData(int channel) { + public JsonData(int channel, List itemIds) { this.channel = channel; + this.itemIds = itemIds; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java index 28300ca6..0cc66859 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java @@ -214,24 +214,16 @@ public final class WiredEngine { // Initial step for trigger state.step(); - // Activate the trigger box animation - if (stack.triggerItem() instanceof InteractionWiredTrigger) { - InteractionWiredTrigger trigger = (InteractionWiredTrigger) stack.triggerItem(); - trigger.activateBox(room, event.getActor().orElse(null), currentTime); - } - debug(room, "Trigger matched: {} at item {} (conditions: {}, effects: {})", event.getType(), stack.triggerItem() != null ? stack.triggerItem().getId() : "null", stack.conditions().size(), stack.effects().size()); - - // Activate extras (for their animation) - activateExtras(room, stack.triggerItem(), event.getActor().orElse(null), currentTime); // Run selectors before conditions so targets are available + List executedSelectors = Collections.emptyList(); if (stack.hasEffects()) { - executeSelectors(stack, ctx, currentTime); + executedSelectors = executeSelectors(stack, ctx); } // Evaluate conditions @@ -253,6 +245,17 @@ public final class WiredEngine { return false; } + RoomUnit actor = event.getActor().orElse(null); + + // Only show the trigger/selector activation when the stack is actually allowed to continue. + if (stack.triggerItem() instanceof InteractionWiredTrigger) { + InteractionWiredTrigger trigger = (InteractionWiredTrigger) stack.triggerItem(); + trigger.activateBox(room, actor, currentTime); + } + + activateExtras(room, stack.triggerItem(), actor, currentTime); + finalizeSelectors(executedSelectors, ctx, currentTime); + // Execute effects if (stack.hasEffects()) { executeEffects(stack, ctx, currentTime); @@ -420,9 +423,11 @@ public final class WiredEngine { /** * Execute selector effects before conditions so ctx.targets() is populated. */ - private void executeSelectors(WiredStack stack, WiredContext ctx, long currentTime) { + private List executeSelectors(WiredStack stack, WiredContext ctx) { List effects = stack.effects(); - if (effects.isEmpty()) return; + if (effects.isEmpty()) return Collections.emptyList(); + + List executedSelectors = new ArrayList<>(); for (IWiredEffect effect : effects) { if (!effect.isSelector()) continue; @@ -433,16 +438,29 @@ public final class WiredEngine { ctx.state().step(); try { effect.execute(ctx); - if (effect instanceof InteractionWiredEffect) { - InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect; - wiredEffect.setCooldown(currentTime); - wiredEffect.activateBox(ctx.room(), ctx.actor().orElse(null), currentTime); + executedSelectors.add((InteractionWiredEffect) effect); } } catch (Exception e) { LOGGER.warn("Error executing selector: {}", e.getMessage()); } } + + return executedSelectors; + } + + private void finalizeSelectors(List executedSelectors, WiredContext ctx, long currentTime) { + if (executedSelectors == null || executedSelectors.isEmpty()) { + return; + } + + Room room = ctx.room(); + RoomUnit actor = ctx.actor().orElse(null); + + for (InteractionWiredEffect wiredEffect : executedSelectors) { + wiredEffect.setCooldown(currentTime); + wiredEffect.activateBox(room, actor, currentTime); + } } /** diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java index 83da31e5..bf27dc0e 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java @@ -34,17 +34,21 @@ public class WiredTriggerSaveDataEvent extends MessageHandler { if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { WiredSettings settings = InteractionWired.readSettings(this.packet, false); - if (trigger.saveData(settings)) { - this.client.sendResponse(new WiredSavedComposer()); + try { + if (trigger.saveData(settings)) { + this.client.sendResponse(new WiredSavedComposer()); - trigger.needsUpdate(true); + trigger.needsUpdate(true); - Emulator.getThreading().run(trigger); - - // Invalidate wired cache when trigger is saved - WiredManager.invalidateRoom(room); - } else { - this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); + Emulator.getThreading().run(trigger); + + // Invalidate wired cache when trigger is saved + WiredManager.invalidateRoom(room); + } else { + this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); + } + } catch (WiredTriggerSaveException e) { + this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); } } else { if ((boolean) saveMethod.get().invoke(trigger, this.packet)) { diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveException.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveException.java new file mode 100644 index 00000000..63fc33a2 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveException.java @@ -0,0 +1,7 @@ +package com.eu.habbo.messages.incoming.wired; + +public class WiredTriggerSaveException extends RuntimeException { + public WiredTriggerSaveException(String message) { + super(message); + } +} From 9253c630a7d6b09db155cf62386d27cbad70daf4 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Tue, 17 Mar 2026 01:34:18 +0100 Subject: [PATCH 2/9] feat(wired): add leave/click/action/short-period triggers - add wf_trg_leave_room, wf_trg_stuff_state, wf_trg_period_short, wf_trg_click_furni, wf_trg_click_tile, wf_trg_click_user and wf_trg_user_performs_action\n- add interaction type room_invisible_click_tile\n- persist selector-driven MatchFurni and ToggleRandom state changes\n- use configured duration in WiredEffectMuteHabbo --- .../habbo/habbohotel/items/ItemManager.java | 8 + .../wired/effects/WiredEffectMatchFurni.java | 1 + .../wired/effects/WiredEffectMuteHabbo.java | 2 +- .../effects/WiredEffectToggleRandom.java | 1 + .../WiredTriggerFurniStateToggled.java | 169 +++++++++++--- .../WiredTriggerHabboClicksFurni.java | 164 +++++++++++++ .../triggers/WiredTriggerHabboClicksTile.java | 47 ++++ .../triggers/WiredTriggerHabboClicksUser.java | 80 +++++++ .../triggers/WiredTriggerHabboLeavesRoom.java | 116 ++++++++++ .../WiredTriggerHabboPerformsAction.java | 217 ++++++++++++++++++ .../triggers/WiredTriggerRepeaterLong.java | 2 +- .../triggers/WiredTriggerRepeaterShort.java | 125 ++++++++++ .../com/eu/habbo/habbohotel/rooms/Room.java | 10 + .../habbohotel/rooms/RoomUnitManager.java | 10 + .../habbo/habbohotel/wired/WiredHandler.java | 2 +- .../habbohotel/wired/WiredTriggerType.java | 6 + .../habbohotel/wired/WiredUserActionType.java | 18 ++ .../habbohotel/wired/core/WiredEvent.java | 42 ++++ .../habbohotel/wired/core/WiredManager.java | 84 +++++++ .../habbohotel/wired/migrate/WiredEvents.java | 89 +++++++ .../com/eu/habbo/messages/PacketManager.java | 4 +- .../eu/habbo/messages/incoming/Incoming.java | 2 + .../incoming/rooms/items/ClickFurniEvent.java | 43 ++++ .../incoming/rooms/users/ClickUserEvent.java | 33 +++ .../rooms/users/RoomUserActionEvent.java | 28 +++ .../rooms/users/RoomUserDanceEvent.java | 13 +- .../rooms/users/RoomUserSignEvent.java | 3 + .../rooms/users/RoomUserSitEvent.java | 9 +- 28 files changed, 1283 insertions(+), 45 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboLeavesRoom.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboPerformsAction.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterShort.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredUserActionType.java create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java 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 2a944f95..78bdda77 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 @@ -203,16 +203,24 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("vendingmachine_no_sides", InteractionNoSidesVendingMachine.class)); this.interactionsList.add(new ItemInteraction("tile_walkmagic", InteractionTileWalkMagic.class)); this.interactionsList.add(new ItemInteraction("antenna", InteractionDefault.class)); + this.interactionsList.add(new ItemInteraction("room_invisible_click_tile", InteractionDefault.class)); this.interactionsList.add(new ItemInteraction("game_timer", InteractionGameTimer.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_on_furni", WiredTriggerHabboWalkOnFurni.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_off_furni", WiredTriggerHabboWalkOffFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_click_furni", WiredTriggerHabboClicksFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_click_tile", WiredTriggerHabboClicksTile.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_click_user", WiredTriggerHabboClicksUser.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_user_performs_action", WiredTriggerHabboPerformsAction.class)); this.interactionsList.add(new ItemInteraction("wf_trg_enter_room", WiredTriggerHabboEntersRoom.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_leave_room", WiredTriggerHabboLeavesRoom.class)); this.interactionsList.add(new ItemInteraction("wf_trg_says_something", WiredTriggerHabboSaysKeyword.class)); this.interactionsList.add(new ItemInteraction("wf_trg_periodically", WiredTriggerRepeater.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_period_short", WiredTriggerRepeaterShort.class)); this.interactionsList.add(new ItemInteraction("wf_trg_period_long", WiredTriggerRepeaterLong.class)); this.interactionsList.add(new ItemInteraction("wf_trg_state_changed", WiredTriggerFurniStateToggled.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_stuff_state", WiredTriggerFurniStateToggled.class)); this.interactionsList.add(new ItemInteraction("wf_trg_at_given_time", WiredTriggerAtSetTime.class)); this.interactionsList.add(new ItemInteraction("wf_trg_at_time_long", WiredTriggerAtTimeLong.class)); this.interactionsList.add(new ItemInteraction("wf_trg_collision", WiredTriggerCollision.class)); 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 90348f35..e075f486 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 @@ -108,6 +108,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) { if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) { item.setExtradata(setting.state); + item.needsUpdate(true); room.updateItemState(item); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMuteHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMuteHabbo.java index 41acb60e..443cb24c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMuteHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMuteHabbo.java @@ -76,7 +76,7 @@ public class WiredEffectMuteHabbo extends InteractionWiredEffect { if (room.hasRights(habbo)) continue; - room.muteHabbo(habbo, 60); + room.muteHabbo(habbo, Math.max(1, this.length)); habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(this.message.replace("%user%", habbo.getHabboInfo().getUsername()).replace("%online_count%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "").replace("%room_count%", Emulator.getGameEnvironment().getRoomManager().getActiveRooms().size() + ""), habbo, habbo, RoomChatMessageBubbles.WIRED))); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleRandom.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleRandom.java index 370655af..8ba458c2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleRandom.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleRandom.java @@ -197,6 +197,7 @@ public class WiredEffectToggleRandom extends InteractionWiredEffect { try { item.setExtradata(Emulator.getRandom().nextInt(item.getBaseItem().getStateCount() + 1) + ""); + item.needsUpdate(true); room.updateItem(item); } catch (Exception e) { LOGGER.error("Caught exception", e); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java index 4f4a82f4..6c15531f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java @@ -15,36 +15,48 @@ 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.stream.Collectors; public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.STATE_CHANGED; + private static final int MODE_ALL_STATES = 0; + private static final int MODE_SAVED_STATE = 1; - private THashSet items; + private THashSet snapshots; + private int triggerMode = MODE_ALL_STATES; public WiredTriggerFurniStateToggled(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); - this.items = new THashSet<>(); + this.snapshots = new THashSet<>(); } public WiredTriggerFurniStateToggled(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { super(id, userId, item, extradata, limitedStack, limitedSells); - this.items = new THashSet<>(); + this.snapshots = new THashSet<>(); } @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { - // Reject if this was triggered by a wired effect (to prevent loops) if (event.isTriggeredByEffect()) { return false; } HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem != null) { - return this.items.contains(sourceItem); + if (sourceItem == null) { + return false; } - return false; + + StateSnapshot snapshot = this.getSnapshot(sourceItem.getId()); + if (snapshot == null) { + return false; + } + + if (this.triggerMode == MODE_SAVED_STATE) { + return snapshot.state.equals(this.normalizeState(sourceItem.getExtradata())); + } + + return true; } @Deprecated @@ -56,21 +68,36 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( - this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + this.triggerMode, + new ArrayList<>(this.snapshots) )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { - this.items = new THashSet<>(); + this.snapshots = new THashSet<>(); + this.triggerMode = MODE_ALL_STATES; String wiredData = set.getString("wired_data"); - if (wiredData.startsWith("{")) { + if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - for (Integer id: data.itemIds) { - HabboItem item = room.getHabboItem(id); - if (item != null) { - this.items.add(item); + this.triggerMode = (data != null) ? data.triggerMode : MODE_ALL_STATES; + + if (data != null && data.snapshots != null && !data.snapshots.isEmpty()) { + for (StateSnapshot snapshot : data.snapshots) { + if (snapshot == null) continue; + + HabboItem item = room.getHabboItem(snapshot.itemId); + if (item != null) { + this.snapshots.add(new StateSnapshot(item.getId(), snapshot.state)); + } + } + } else if (data != null && data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) { + this.snapshots.add(this.captureSnapshot(item)); + } } } } else { @@ -79,10 +106,15 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { if (!wiredData.split(":")[2].equals("\t")) { for (String s : wiredData.split(":")[2].split(";")) { + if (s.isEmpty()) { + continue; + } + HabboItem item = room.getHabboItem(Integer.parseInt(s)); - if (item != null) - this.items.add(item); + if (item != null) { + this.snapshots.add(this.captureSnapshot(item)); + } } } } @@ -91,7 +123,8 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { @Override public void onPickUp() { - this.items.clear(); + this.snapshots.clear(); + this.triggerMode = MODE_ALL_STATES; } @Override @@ -101,33 +134,31 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { @Override public void serializeWiredData(ServerMessage message, Room room) { - THashSet items = new THashSet<>(); + THashSet snapshotsToRemove = new THashSet<>(); - for (HabboItem item : this.items) { - if (item.getRoomId() != this.getRoomId()) { - items.add(item); + for (StateSnapshot snapshot : this.snapshots) { + HabboItem item = room.getHabboItem(snapshot.itemId); + if (item == null || item.getRoomId() != this.getRoomId()) { + snapshotsToRemove.add(snapshot); continue; } - - if (room.getHabboItem(item.getId()) == null) { - items.add(item); - } } - for (HabboItem item : items) { - this.items.remove(item); + for (StateSnapshot snapshot : snapshotsToRemove) { + this.snapshots.remove(snapshot); } message.appendBoolean(false); message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); - message.appendInt(this.items.size()); - for (HabboItem item : this.items) { - message.appendInt(item.getId()); + message.appendInt(this.snapshots.size()); + for (StateSnapshot snapshot : this.snapshots) { + message.appendInt(snapshot.itemId); } message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.triggerMode); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -135,14 +166,22 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { - this.items.clear(); + this.snapshots.clear(); + this.triggerMode = (settings.getIntParams().length > 0 && settings.getIntParams()[0] == MODE_SAVED_STATE) + ? MODE_SAVED_STATE + : MODE_ALL_STATES; + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return true; + } int count = settings.getFurniIds().length; for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { - this.items.add(item); + this.snapshots.add(this.captureSnapshot(item)); } } @@ -154,11 +193,71 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { return true; } + private StateSnapshot captureSnapshot(HabboItem item) { + return new StateSnapshot(item.getId(), this.normalizeState(item.getExtradata())); + } + + private StateSnapshot getSnapshot(int itemId) { + for (StateSnapshot snapshot : this.snapshots) { + if (snapshot.itemId == itemId) { + return snapshot; + } + } + + return null; + } + + private String normalizeState(String state) { + return (state == null) ? "" : state; + } + static class JsonData { + int triggerMode; + List snapshots; List itemIds; + public JsonData() { + } + public JsonData(List itemIds) { this.itemIds = itemIds; } + + public JsonData(int triggerMode, List snapshots) { + this.triggerMode = triggerMode; + this.snapshots = snapshots; + } + } + + static class StateSnapshot { + int itemId; + String state; + + public StateSnapshot() { + } + + public StateSnapshot(int itemId, String state) { + this.itemId = itemId; + this.state = (state == null) ? "" : state; + } + + @Override + public int hashCode() { + return Integer.hashCode(this.itemId); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof StateSnapshot)) { + return false; + } + + StateSnapshot that = (StateSnapshot) object; + return this.itemId == that.itemId; + } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java new file mode 100644 index 00000000..9bf25e62 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java @@ -0,0 +1,164 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { + public static final WiredTriggerType type = WiredTriggerType.CLICKS_FURNI; + + private THashSet items; + + public WiredTriggerHabboClicksFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredTriggerHabboClicksFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + HabboItem sourceItem = event.getSourceItem().orElse(null); + return sourceItem != null && this.items.contains(sourceItem); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public WiredTriggerType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + THashSet items = new THashSet<>(); + + if (Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()) == null) { + items.addAll(this.items); + } else { + for (HabboItem item : this.items) { + if (Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null) { + items.add(item); + } + } + } + + for (HabboItem item : items) { + this.items.remove(item); + } + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + this.items.clear(); + + int count = settings.getFurniIds().length; + + for (int i = 0; i < count; i++) { + HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + if (item != null) { + this.items.add(item); + } + } + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @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); + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) { + this.items.add(item); + } + } + } else { + if (wiredData.split(":").length >= 3) { + super.setDelay(Integer.parseInt(wiredData.split(":")[0])); + + if (!wiredData.split(":")[2].equals("\t")) { + for (String s : wiredData.split(":")[2].split(";")) { + if (s.isEmpty()) { + continue; + } + + try { + HabboItem item = room.getHabboItem(Integer.parseInt(s)); + + if (item != null) { + this.items.add(item); + } + } catch (Exception e) { + } + } + } + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + } + + @Override + public boolean isTriggeredByRoomUnit() { + return true; + } + + static class JsonData { + List itemIds; + + public JsonData(List itemIds) { + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java new file mode 100644 index 00000000..6ddcc503 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java @@ -0,0 +1,47 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredTriggerHabboClicksTile extends WiredTriggerHabboClicksFurni { + public static final WiredTriggerType type = WiredTriggerType.CLICKS_TILE; + + private static final String CLICK_TILE_INTERACTION = "room_invisible_click_tile"; + + public WiredTriggerHabboClicksTile(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredTriggerHabboClicksTile(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + if (!super.matches(triggerItem, event)) { + return false; + } + + HabboItem sourceItem = event.getSourceItem().orElse(null); + return isClickTileItem(sourceItem); + } + + @Override + public WiredTriggerType getType() { + return type; + } + + private boolean isClickTileItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) { + return false; + } + + String interaction = item.getBaseItem().getInteractionType().getName(); + return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java new file mode 100644 index 00000000..f64ca1c0 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java @@ -0,0 +1,80 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.habbohotel.items.Item; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { + public static final WiredTriggerType type = WiredTriggerType.CLICKS_USER; + + public WiredTriggerHabboClicksUser(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredTriggerHabboClicksUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + return event.getActor().isPresent(); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return ""; + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + } + + @Override + public void onPickUp() { + } + + @Override + public WiredTriggerType 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(0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + return true; + } + + @Override + public boolean isTriggeredByRoomUnit() { + return true; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboLeavesRoom.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboLeavesRoom.java new file mode 100644 index 00000000..36583ce3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboLeavesRoom.java @@ -0,0 +1,116 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.habbohotel.items.Item; +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.core.WiredManager; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredTriggerHabboLeavesRoom extends InteractionWiredTrigger { + public static final WiredTriggerType type = WiredTriggerType.LEAVE_ROOM; + + private String username = ""; + + public WiredTriggerHabboLeavesRoom(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredTriggerHabboLeavesRoom(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + RoomUnit roomUnit = event.getActor().orElse(null); + Room room = event.getRoom(); + Habbo habbo = room.getHabbo(roomUnit); + + if (habbo != null) { + if (this.username.length() > 0) { + return habbo.getHabboInfo().getUsername().equalsIgnoreCase(this.username); + } + + return true; + } + return false; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.username + )); + } + + @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.username = data.username; + } else { + this.username = wiredData; + } + } + + @Override + public void onPickUp() { + this.username = ""; + } + + @Override + public WiredTriggerType 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(this.username); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + this.username = settings.getStringParam(); + + return true; + } + + @Override + public boolean isTriggeredByRoomUnit() { + return true; + } + + static class JsonData { + String username; + + public JsonData(String username) { + this.username = username; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboPerformsAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboPerformsAction.java new file mode 100644 index 00000000..07b2195d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboPerformsAction.java @@ -0,0 +1,217 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.habbohotel.items.Item; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredTriggerHabboPerformsAction extends InteractionWiredTrigger { + private static final WiredTriggerType type = WiredTriggerType.USER_PERFORMS_ACTION; + private static final int DEFAULT_ACTION = WiredUserActionType.WAVE; + + private int selectedAction = DEFAULT_ACTION; + private boolean signFilterEnabled = false; + private int signId = 0; + private boolean danceFilterEnabled = false; + private int danceId = 1; + + public WiredTriggerHabboPerformsAction(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredTriggerHabboPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + if (!event.getActor().isPresent()) { + return false; + } + + if (event.getActionId() != this.selectedAction) { + return false; + } + + if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) { + return event.getActionParameter() == this.signId; + } + + if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) { + return event.getActionParameter() == this.danceId; + } + + return true; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.selectedAction, + this.signFilterEnabled, + this.signId, + this.danceFilterEnabled, + this.danceId + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + + String wiredData = set.getString("wired_data"); + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data == null) { + return; + } + + this.selectedAction = normalizeAction(data.selectedAction); + this.signFilterEnabled = data.signFilterEnabled; + this.signId = normalizeSignId(data.signId); + this.danceFilterEnabled = data.danceFilterEnabled; + this.danceId = normalizeDanceId(data.danceId); + } + } + + @Override + public void onPickUp() { + this.resetSettings(); + } + + @Override + public WiredTriggerType 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(5); + message.appendInt(this.selectedAction); + message.appendInt(this.signFilterEnabled ? 1 : 0); + message.appendInt(this.signId); + message.appendInt(this.danceFilterEnabled ? 1 : 0); + message.appendInt(this.danceId); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] intParams = settings.getIntParams(); + + this.resetSettings(); + + if (intParams.length > 0) { + this.selectedAction = normalizeAction(intParams[0]); + } + + if (intParams.length > 1) { + this.signFilterEnabled = (intParams[1] == 1); + } + + if (intParams.length > 2) { + this.signId = normalizeSignId(intParams[2]); + } + + if (intParams.length > 3) { + this.danceFilterEnabled = (intParams[3] == 1); + } + + if (intParams.length > 4) { + this.danceId = normalizeDanceId(intParams[4]); + } + + return true; + } + + @Override + public boolean isTriggeredByRoomUnit() { + return true; + } + + private void resetSettings() { + this.selectedAction = DEFAULT_ACTION; + this.signFilterEnabled = false; + this.signId = 0; + this.danceFilterEnabled = false; + this.danceId = 1; + } + + private int normalizeAction(int action) { + switch (action) { + case WiredUserActionType.WAVE: + case WiredUserActionType.BLOW_KISS: + case WiredUserActionType.LAUGH: + case WiredUserActionType.AWAKE: + case WiredUserActionType.RELAX: + case WiredUserActionType.SIT: + case WiredUserActionType.STAND: + case WiredUserActionType.LAY: + case WiredUserActionType.SIGN: + case WiredUserActionType.DANCE: + case WiredUserActionType.THUMB_UP: + return action; + default: + return DEFAULT_ACTION; + } + } + + private int normalizeSignId(int signId) { + if (signId < 0 || signId > 17) { + return 0; + } + + return signId; + } + + private int normalizeDanceId(int danceId) { + if (danceId < 1 || danceId > 4) { + return 1; + } + + return danceId; + } + + static class JsonData { + int selectedAction; + boolean signFilterEnabled; + int signId; + boolean danceFilterEnabled; + int danceId; + + public JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId) { + this.selectedAction = selectedAction; + this.signFilterEnabled = signFilterEnabled; + this.signId = signId; + this.danceFilterEnabled = danceFilterEnabled; + this.danceId = danceId; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterLong.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterLong.java index f9094c09..dadac6be 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterLong.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterLong.java @@ -139,7 +139,7 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements // Fire when elapsed time is a multiple of repeat time if (elapsedMs % this.repeatTime == 0) { if (this.getRoomId() != 0 && room.isLoaded()) { - WiredManager.triggerTimerRepeat(room, this); + WiredManager.triggerTimerRepeatLong(room, this); } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterShort.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterShort.java new file mode 100644 index 00000000..00800e67 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerRepeaterShort.java @@ -0,0 +1,125 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +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.Room; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import gnu.trove.procedure.TObjectProcedure; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class WiredTriggerRepeaterShort extends WiredTriggerRepeater { + public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY_SHORT; + public static final int STEP_MS = 50; + public static final int DEFAULT_DELAY = 10 * STEP_MS; + public static final int MIN_DELAY = STEP_MS; + public static final int MAX_DELAY = 10 * STEP_MS; + + public WiredTriggerRepeaterShort(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.repeatTime = DEFAULT_DELAY; + } + + public WiredTriggerRepeaterShort(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.repeatTime = DEFAULT_DELAY; + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + String wiredData = set.getString("wired_data"); + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.repeatTime = (data != null) ? data.repeatTime : DEFAULT_DELAY; + } else if (wiredData != null && wiredData.length() >= 1) { + this.repeatTime = Integer.parseInt(wiredData); + } else { + this.repeatTime = DEFAULT_DELAY; + } + + this.repeatTime = clampRepeatTime(this.repeatTime); + } + + @Override + public void onPickUp() { + this.repeatTime = DEFAULT_DELAY; + } + + @Override + public WiredTriggerType 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.repeatTime / STEP_MS); + message.appendInt(0); + message.appendInt(this.getType().code); + + if (!this.isTriggeredByRoomUnit()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getEffects(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @Override + public boolean execute(InteractionWiredEffect object) { + if (object.requiresTriggeringUser()) { + 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) { + if (settings.getIntParams().length < 1) return false; + + int newRepeatTime = settings.getIntParams()[0] * STEP_MS; + this.repeatTime = clampRepeatTime(newRepeatTime); + + return true; + } + + @Override + public void onWiredTick(Room room, long tickCount, int tickIntervalMs) { + long elapsedMs = tickCount * tickIntervalMs; + + if (elapsedMs % this.repeatTime == 0) { + if (this.getRoomId() != 0 && room.isLoaded()) { + WiredManager.triggerTimerRepeatShort(room, this); + } + } + } + + private int clampRepeatTime(int repeatTime) { + if (repeatTime < MIN_DELAY) { + return DEFAULT_DELAY; + } + + if (repeatTime > MAX_DELAY) { + return MAX_DELAY; + } + + return repeatTime; + } +} 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 e47431ed..2d9ff42a 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 @@ -15,6 +15,8 @@ import com.eu.habbo.habbohotel.pets.PetManager; import com.eu.habbo.habbohotel.users.DanceType; 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.messages.ISerialize; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.guilds.GuildInfoComposer; @@ -2151,6 +2153,7 @@ public class Room implements Comparable, ISerialize, Runnable { - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + ""); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); + WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1); } public void makeStand(Habbo habbo) { @@ -2160,12 +2163,19 @@ public class Room implements Comparable, ISerialize, Runnable { HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY()); if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) { + boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) + || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); habbo.getRoomUnit().cmdStand = true; habbo.getRoomUnit().setBodyRotation( RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT); + habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); + + if (wasSittingOrLaying) { + WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.STAND, -1); + } } } 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 9ac9c726..7a33df06 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 @@ -13,6 +13,7 @@ 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.WiredManager; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer; import com.eu.habbo.messages.outgoing.inventory.AddPetComposer; import com.eu.habbo.messages.outgoing.rooms.pets.RoomPetComposer; @@ -217,6 +218,10 @@ public class RoomUnitManager { return; } + if (habbo.getRoomUnit() != null) { + WiredManager.triggerUserLeavesRoom(this.room, habbo.getRoomUnit()); + } + if (habbo.getRoomUnit() != null && habbo.getRoomUnit().getCurrentLocation() != null) { habbo.getRoomUnit().getCurrentLocation().removeUnit(habbo.getRoomUnit()); } @@ -352,6 +357,7 @@ public class RoomUnitManager { } double z = habbo.getRoomUnit().getCurrentLocation().getStackHeight(); + boolean hadLayStatus = habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) || (topItem != null && topItem.getBaseItem().allowSit())) { @@ -413,6 +419,10 @@ public class RoomUnitManager { } habbo.getRoomUnit().statusUpdate(true); + + if (!hadLayStatus && habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY)) { + WiredManager.triggerUserPerformsAction(this.room, habbo.getRoomUnit(), WiredUserActionType.LAY, -1); + } } if (!habbos.isEmpty()) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java index 6aab1031..b837f0c0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java @@ -466,7 +466,7 @@ public class WiredHandler { room.getRoomSpecialTypes().getTriggers().forEach(t -> { if (t == null) return; - if (t.getType() == WiredTriggerType.AT_GIVEN_TIME || t.getType() == WiredTriggerType.PERIODICALLY || t.getType() == WiredTriggerType.PERIODICALLY_LONG) { + if (t.getType() == WiredTriggerType.AT_GIVEN_TIME || t.getType() == WiredTriggerType.PERIODICALLY || t.getType() == WiredTriggerType.PERIODICALLY_LONG || t.getType() == WiredTriggerType.PERIODICALLY_SHORT) { ((WiredTriggerReset) t).resetTimer(); } }); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java index e1767c1b..2672758b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java @@ -15,6 +15,12 @@ public enum WiredTriggerType { PERIODICALLY_LONG(12), BOT_REACHED_STF(13), BOT_REACHED_AVTR(14), + LEAVE_ROOM(16), + PERIODICALLY_SHORT(17), + CLICKS_FURNI(18), + CLICKS_TILE(19), + CLICKS_USER(20), + USER_PERFORMS_ACTION(21), SAY_COMMAND(0), IDLES(11), UNIDLES(11), diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredUserActionType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredUserActionType.java new file mode 100644 index 00000000..eaf12377 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredUserActionType.java @@ -0,0 +1,18 @@ +package com.eu.habbo.habbohotel.wired; + +public final class WiredUserActionType { + public static final int WAVE = 1; + public static final int BLOW_KISS = 2; + public static final int LAUGH = 3; + public static final int AWAKE = 4; + public static final int RELAX = 5; + public static final int SIT = 6; + public static final int STAND = 7; + public static final int LAY = 8; + public static final int SIGN = 9; + public static final int DANCE = 10; + public static final int THUMB_UP = 11; + + private WiredUserActionType() { + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java index ca3c543b..369d7fba 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java @@ -42,6 +42,18 @@ public final class WiredEvent { /** User walks off furniture */ USER_WALKS_OFF(WiredTriggerType.WALKS_OFF_FURNI), + + /** User clicks furniture */ + USER_CLICKS_FURNI(WiredTriggerType.CLICKS_FURNI), + + /** User clicks invisible click tile furniture */ + USER_CLICKS_TILE(WiredTriggerType.CLICKS_TILE), + + /** User clicks another user */ + USER_CLICKS_USER(WiredTriggerType.CLICKS_USER), + + /** User performs an avatar action */ + USER_PERFORMS_ACTION(WiredTriggerType.USER_PERFORMS_ACTION), /** Furniture state is toggled/changed */ FURNI_STATE_CHANGED(WiredTriggerType.STATE_CHANGED), @@ -54,9 +66,15 @@ public final class WiredEvent { /** Long timer repeat */ TIMER_REPEAT_LONG(WiredTriggerType.PERIODICALLY_LONG), + + /** Short timer repeat */ + TIMER_REPEAT_SHORT(WiredTriggerType.PERIODICALLY_SHORT), /** User enters the room */ USER_ENTERS_ROOM(WiredTriggerType.ENTER_ROOM), + + /** User leaves the room */ + USER_LEAVES_ROOM(WiredTriggerType.LEAVE_ROOM), /** Game starts */ GAME_STARTS(WiredTriggerType.GAME_STARTS), @@ -141,6 +159,8 @@ public final class WiredEvent { private final boolean triggeredByEffect; // true if triggered by a wired effect (to prevent loops) private final int callStackDepth; // recursion depth for trigger stacks effect private final int signalChannel; // channel for signal routing (0-based) + private final int actionId; // user action id for USER_PERFORMS_ACTION + private final int actionParameter; // sign/dance parameter when relevant private final long createdAtMs; private WiredEvent(Builder builder) { @@ -156,6 +176,8 @@ public final class WiredEvent { this.triggeredByEffect = builder.triggeredByEffect; this.callStackDepth = builder.callStackDepth; this.signalChannel = builder.signalChannel; + this.actionId = builder.actionId; + this.actionParameter = builder.actionParameter; this.createdAtMs = builder.createdAtMs; } @@ -258,6 +280,14 @@ public final class WiredEvent { return signalChannel; } + public int getActionId() { + return actionId; + } + + public int getActionParameter() { + return actionParameter; + } + /** * Get the timestamp when this event was created. * @return milliseconds since epoch @@ -313,6 +343,8 @@ public final class WiredEvent { private boolean triggeredByEffect; private int callStackDepth; private int signalChannel; + private int actionId; + private int actionParameter = -1; private long createdAtMs = System.currentTimeMillis(); private Builder(Type type, Room room) { @@ -417,6 +449,16 @@ public final class WiredEvent { return this; } + public Builder actionId(int actionId) { + this.actionId = actionId; + return this; + } + + public Builder actionParameter(int actionParameter) { + this.actionParameter = actionParameter; + return this; + } + /** * Set a custom creation timestamp. * @param createdAtMs milliseconds since epoch diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index e31b948c..e758edf8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -235,6 +235,54 @@ public final class WiredManager { return handleEvent(event); } + /** + * Trigger when a user clicks furniture. + */ + public static boolean triggerUserClicksFurni(Room room, RoomUnit user, HabboItem item) { + if (!isEnabled() || room == null || user == null || item == null) { + return false; + } + + WiredEvent event = WiredEvents.userClicksFurni(room, user, item); + return handleEvent(event); + } + + /** + * Trigger when a user clicks invisible click tile furniture. + */ + public static boolean triggerUserClicksTile(Room room, RoomUnit user, HabboItem item) { + if (!isEnabled() || room == null || user == null || item == null) { + return false; + } + + WiredEvent event = WiredEvents.userClicksTile(room, user, item); + return handleEvent(event); + } + + /** + * Trigger when a user clicks another user. + */ + public static boolean triggerUserClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) { + if (!isEnabled() || room == null || clickingUser == null || clickedUser == null) { + return false; + } + + WiredEvent event = WiredEvents.userClicksUser(room, clickingUser, clickedUser); + return handleEvent(event); + } + + /** + * Trigger when a user performs an avatar action. + */ + public static boolean triggerUserPerformsAction(Room room, RoomUnit user, int actionId, int actionParameter) { + if (!isEnabled() || room == null || user == null) { + return false; + } + + WiredEvent event = WiredEvents.userPerformsAction(room, user, actionId, actionParameter); + return handleEvent(event); + } + /** * Trigger when a user says something. */ @@ -259,6 +307,18 @@ public final class WiredManager { return handleEvent(event); } + /** + * Trigger when a user leaves the room. + */ + public static boolean triggerUserLeavesRoom(Room room, RoomUnit user) { + if (!isEnabled() || room == null || user == null) { + return false; + } + + WiredEvent event = WiredEvents.userLeavesRoom(room, user); + return handleEvent(event); + } + /** * Trigger when furniture state changes. */ @@ -295,6 +355,30 @@ public final class WiredManager { return handleEvent(event); } + /** + * Trigger a long periodic timer. + */ + public static boolean triggerTimerRepeatLong(Room room, HabboItem timerItem) { + if (!isEnabled() || room == null) { + return false; + } + + WiredEvent event = WiredEvents.timerRepeatLong(room, timerItem); + return handleEvent(event); + } + + /** + * Trigger a short periodic timer. + */ + public static boolean triggerTimerRepeatShort(Room room, HabboItem timerItem) { + if (!isEnabled() || room == null) { + return false; + } + + WiredEvent event = WiredEvents.timerRepeatShort(room, timerItem); + return handleEvent(event); + } + /** * Trigger game start. */ diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java index 91bba744..e93072b2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java @@ -67,6 +67,70 @@ public final class WiredEvents { .build(); } + /** + * Create an event for when a user clicks furniture. + * @param room the room + * @param user the clicking user + * @param item the clicked furniture + * @return the event + */ + public static WiredEvent userClicksFurni(Room room, RoomUnit user, HabboItem item) { + RoomTile tile = room.getLayout().getTile(item.getX(), item.getY()); + return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_FURNI, room) + .actor(user) + .sourceItem(item) + .tile(tile) + .build(); + } + + /** + * Create an event for when a user clicks invisible click tile furniture. + * @param room the room + * @param user the clicking user + * @param item the clicked furniture + * @return the event + */ + public static WiredEvent userClicksTile(Room room, RoomUnit user, HabboItem item) { + RoomTile tile = room.getLayout().getTile(item.getX(), item.getY()); + return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_TILE, room) + .actor(user) + .sourceItem(item) + .tile(tile) + .build(); + } + + /** + * Create an event for when a user clicks another user. + * @param room the room + * @param clickingUser the user performing the click + * @param clickedUser the user who was clicked + * @return the event + */ + public static WiredEvent userClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) { + return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_USER, room) + .actor(clickedUser) + .targetUnit(clickingUser) + .tile(clickedUser.getCurrentLocation()) + .build(); + } + + /** + * Create an event for when a user performs an avatar action. + * @param room the room + * @param user the acting user + * @param actionId the wired action id + * @param actionParameter sign/dance parameter, or -1 when unused + * @return the event + */ + public static WiredEvent userPerformsAction(Room room, RoomUnit user, int actionId, int actionParameter) { + return WiredEvent.builder(WiredEvent.Type.USER_PERFORMS_ACTION, room) + .actor(user) + .tile(user.getCurrentLocation()) + .actionId(actionId) + .actionParameter(actionParameter) + .build(); + } + /** * Create an event for when a user enters the room. * @param room the room @@ -80,6 +144,19 @@ public final class WiredEvents { .build(); } + /** + * Create an event for when a user leaves the room. + * @param room the room + * @param user the user who left + * @return the event + */ + public static WiredEvent userLeavesRoom(Room room, RoomUnit user) { + return WiredEvent.builder(WiredEvent.Type.USER_LEAVES_ROOM, room) + .actor(user) + .tile(user.getCurrentLocation()) + .build(); + } + // ========== User Interaction Events ========== /** @@ -153,6 +230,18 @@ public final class WiredEvents { .build(); } + /** + * Create an event for a short periodic timer. + * @param room the room + * @param timerItem the timer furniture + * @return the event + */ + public static WiredEvent timerRepeatShort(Room room, HabboItem timerItem) { + return WiredEvent.builder(WiredEvent.Type.TIMER_REPEAT_SHORT, room) + .sourceItem(timerItem) + .build(); + } + // ========== Game Events ========== /** diff --git a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java index 6bd33db3..757e1636 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java @@ -387,6 +387,8 @@ public class PacketManager { this.registerHandler(Incoming.RoomPlacePaintEvent, RoomPlacePaintEvent.class); this.registerHandler(Incoming.RoomUserStartTypingEvent, RoomUserStartTypingEvent.class); this.registerHandler(Incoming.RoomUserStopTypingEvent, RoomUserStopTypingEvent.class); + this.registerHandler(Incoming.ClickFurniEvent, ClickFurniEvent.class); + this.registerHandler(Incoming.ClickUserEvent, ClickUserEvent.class); this.registerHandler(Incoming.ToggleFloorItemEvent, ToggleFloorItemEvent.class); this.registerHandler(Incoming.ToggleWallItemEvent, ToggleWallItemEvent.class); this.registerHandler(Incoming.RoomBackgroundEvent, RoomBackgroundEvent.class); @@ -647,4 +649,4 @@ public class PacketManager { this.registerHandler(Incoming.GameCenterEvent, GameCenterEvent.class); this.registerHandler(Incoming.GameCenterRequestGameStatusEvent, GameCenterRequestGameStatusEvent.class); } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java index 8c9625e7..5bdc6847 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java @@ -205,6 +205,7 @@ public class Incoming { public static final int RequestRoomDataEvent = 2230; public static final int RequestRoomHeightmapEvent = 2300; public static final int RequestGuildFurniWidgetEvent = 2651; + public static final int ClickFurniEvent = 6002; public static final int RequestOwnItemsEvent = 2105; public static final int RequestReportRoomEvent = 3267; public static final int ReportEvent = 1691; @@ -407,6 +408,7 @@ public class Incoming { // CUSTOM public static final int UpdateFurniturePositionEvent = 10019; + public static final int ClickUserEvent = 10020; public static final int RequestInventoryPetDelete = 10030; public static final int RequestInventoryBadgeDelete = 10031; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java new file mode 100644 index 00000000..6ae99006 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java @@ -0,0 +1,43 @@ +package com.eu.habbo.messages.incoming.rooms.items; + +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.incoming.MessageHandler; + +public class ClickFurniEvent extends MessageHandler { + private static final String CLICK_TILE_INTERACTION = "room_invisible_click_tile"; + + @Override + public void handle() throws Exception { + Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom(); + + if (room == null) { + return; + } + + int itemId = Math.abs(this.packet.readInt()); + this.packet.readInt(); + + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + return; + } + + WiredManager.triggerUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + + if (isClickTileItem(item)) { + WiredManager.triggerUserClicksTile(room, this.client.getHabbo().getRoomUnit(), item); + } + } + + private boolean isClickTileItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) { + return false; + } + + String interaction = item.getBaseItem().getInteractionType().getName(); + return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java new file mode 100644 index 00000000..5426e842 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java @@ -0,0 +1,33 @@ +package com.eu.habbo.messages.incoming.rooms.users; + +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.wired.core.WiredManager; +import com.eu.habbo.messages.incoming.MessageHandler; + +public class ClickUserEvent extends MessageHandler { + @Override + public void handle() throws Exception { + Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom(); + + if (room == null) { + return; + } + + RoomUnit clickingUser = this.client.getHabbo().getRoomUnit(); + + if (clickingUser == null) { + return; + } + + int roomUnitId = this.packet.readInt(); + Habbo clickedHabbo = room.getHabboByRoomUnitId(roomUnitId); + + if (clickedHabbo == null || clickedHabbo.getRoomUnit() == null) { + return; + } + + WiredManager.triggerUserClicksUser(room, clickingUser, clickedHabbo.getRoomUnit()); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserActionEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserActionEvent.java index 41c29f25..bc32465b 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserActionEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserActionEvent.java @@ -4,6 +4,8 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUserAction; import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserActionComposer; import com.eu.habbo.plugin.events.users.UserIdleEvent; @@ -26,6 +28,7 @@ public class RoomUserActionEvent extends MessageHandler { } int action = this.packet.readInt(); + int wiredAction = 0; if (action == 5) { UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.ACTION, true); @@ -34,8 +37,10 @@ public class RoomUserActionEvent extends MessageHandler { if (!event.isCancelled()) { if (event.idle) { room.idle(habbo); + wiredAction = WiredUserActionType.RELAX; } else { room.unIdle(habbo); + wiredAction = WiredUserActionType.AWAKE; } } } else { @@ -51,6 +56,29 @@ public class RoomUserActionEvent extends MessageHandler { } room.sendComposer(new RoomUserActionComposer(habbo.getRoomUnit(), RoomUserAction.fromValue(action)).compose()); + + if (wiredAction == 0) { + switch (action) { + case 1: + wiredAction = WiredUserActionType.WAVE; + break; + case 2: + wiredAction = WiredUserActionType.BLOW_KISS; + break; + case 3: + wiredAction = WiredUserActionType.LAUGH; + break; + case 7: + wiredAction = WiredUserActionType.THUMB_UP; + break; + default: + break; + } + } + + if (wiredAction != 0) { + WiredManager.triggerUserPerformsAction(room, habbo.getRoomUnit(), wiredAction, -1); + } } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserDanceEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserDanceEvent.java index bafd54dd..bda9bd4e 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserDanceEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserDanceEvent.java @@ -3,8 +3,9 @@ package com.eu.habbo.messages.incoming.rooms.users; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.users.DanceType; import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; -import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDanceComposer; import com.eu.habbo.plugin.events.users.UserIdleEvent; public class RoomUserDanceEvent extends MessageHandler { @@ -14,7 +15,7 @@ public class RoomUserDanceEvent extends MessageHandler { return; int danceId = this.packet.readInt(); - if (danceId >= 0 && danceId <= 5) { + if (danceId >= 0 && danceId <= 4) { if (this.client.getHabbo().getRoomUnit().isInRoom()) { Habbo habbo = this.client.getHabbo(); @@ -29,8 +30,6 @@ public class RoomUserDanceEvent extends MessageHandler { } } - habbo.getRoomUnit().setDanceType(DanceType.values()[danceId]); - UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.DANCE, false); Emulator.getPluginManager().fireEvent(event); @@ -40,7 +39,11 @@ public class RoomUserDanceEvent extends MessageHandler { } } - this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDanceComposer(habbo.getRoomUnit()).compose()); + this.client.getHabbo().getHabboInfo().getCurrentRoom().dance(habbo, DanceType.values()[danceId]); + + if (danceId > 0) { + WiredManager.triggerUserPerformsAction(this.client.getHabbo().getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), WiredUserActionType.DANCE, danceId); + } } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSignEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSignEvent.java index 3b2aaf93..fba5fccf 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSignEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSignEvent.java @@ -5,6 +5,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionVoteCounter; 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.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.plugin.events.users.UserSignEvent; @@ -22,6 +24,7 @@ public class RoomUserSignEvent extends MessageHandler { if (!Emulator.getPluginManager().fireEvent(event).isCancelled()) { this.client.getHabbo().getRoomUnit().setStatus(RoomUnitStatus.SIGN, event.sign + ""); this.client.getHabbo().getHabboInfo().getCurrentRoom().unIdle(this.client.getHabbo()); + WiredManager.triggerUserPerformsAction(room, this.client.getHabbo().getRoomUnit(), WiredUserActionType.SIGN, event.sign); if(signId <= 10) { diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSitEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSitEvent.java index f3c8df7f..debdb49c 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSitEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserSitEvent.java @@ -7,11 +7,18 @@ import com.eu.habbo.plugin.events.users.UserIdleEvent; public class RoomUserSitEvent extends MessageHandler { @Override public void handle() throws Exception { + int posture = this.packet.readInt(); + if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != null) { if (this.client.getHabbo().getRoomUnit().isWalking()) { this.client.getHabbo().getRoomUnit().stopWalking(); } - this.client.getHabbo().getHabboInfo().getCurrentRoom().makeSit(this.client.getHabbo()); + + if (posture == 0) { + this.client.getHabbo().getHabboInfo().getCurrentRoom().makeStand(this.client.getHabbo()); + } else { + this.client.getHabbo().getHabboInfo().getCurrentRoom().makeSit(this.client.getHabbo()); + } UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.WALKED, false); Emulator.getPluginManager().fireEvent(event); From 1f0fbc0bce3fbec24fce9ec5b6d859bdaf632636 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Tue, 17 Mar 2026 03:27:55 +0100 Subject: [PATCH 3/9] 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 --- .../habbo/habbohotel/items/ItemManager.java | 5 + .../wired/effects/WiredEffectFreeze.java | 175 +++++++++ .../effects/WiredEffectFurniToFurni.java | 354 ++++++++++++++++++ .../wired/effects/WiredEffectFurniToUser.java | 57 +++ .../wired/effects/WiredEffectUnfreeze.java | 149 ++++++++ .../effects/WiredEffectUserFurniBase.java | 260 +++++++++++++ .../wired/effects/WiredEffectUserToFurni.java | 48 +++ .../habbohotel/rooms/RoomUnitManager.java | 7 + .../habbohotel/wired/WiredEffectType.java | 7 +- .../wired/core/WiredFreezeUtil.java | 74 ++++ .../rooms/users/RoomUserWalkEvent.java | 3 +- .../threading/runnables/RoomUnitTeleport.java | 4 + .../teleport/TeleportActionFive.java | 4 + .../teleport/TeleportActionFour.java | 3 +- .../runnables/teleport/TeleportActionOne.java | 3 +- .../teleport/TeleportActionThree.java | 3 +- .../runnables/teleport/TeleportActionTwo.java | 3 +- 17 files changed, 1153 insertions(+), 6 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFreeze.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUnfreeze.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserFurniBase.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredFreezeUtil.java 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 78bdda77..2f317f1f 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 @@ -264,6 +264,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)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFreeze.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFreeze.java new file mode 100644 index 00000000..66f11eae --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFreeze.java @@ -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 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 invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @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; + } + } +} 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 new file mode 100644 index 00000000..bdaa461b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java @@ -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 moveItems = new ArrayList<>(); + private final List 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 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 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 items) { + if (source == WiredSourceUtil.SOURCE_SELECTED) { + this.validateItems(items); + } + + List 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 parseItems(String data, Room room) throws WiredSaveException { + List items = new ArrayList<>(); + if (data == null || data.trim().isEmpty() || room == null) { + return items; + } + + Set 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 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 itemIds, List 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 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 itemIds; + List targetItemIds; + int moveSource; + int targetSource; + + public JsonData(int delay, List itemIds, List targetItemIds, int moveSource, int targetSource) { + this.delay = delay; + this.itemIds = itemIds; + this.targetItemIds = targetItemIds; + this.moveSource = moveSource; + this.targetSource = targetSource; + } + } +} 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 new file mode 100644 index 00000000..62df4762 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java @@ -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; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUnfreeze.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUnfreeze.java new file mode 100644 index 00000000..40978a4b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUnfreeze.java @@ -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 invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @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; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserFurniBase.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserFurniBase.java new file mode 100644 index 00000000..bc86f16d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserFurniBase.java @@ -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 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 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 resolveHabbos(Room room, WiredContext ctx) { + List 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 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 invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @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 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 itemIds; + int furniSource; + int userSource; + + public JsonData(int delay, List itemIds, int furniSource, int userSource) { + this.delay = delay; + this.itemIds = itemIds; + this.furniSource = furniSource; + this.userSource = userSource; + } + } +} 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 new file mode 100644 index 00000000..b5d50cc3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java @@ -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; + } +} 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 7a33df06..8f4f0772 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 @@ -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.habbohotel.wired.WiredUserActionType; import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer; @@ -220,6 +221,9 @@ public class RoomUnitManager { 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) { @@ -1309,6 +1313,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) { @@ -1320,6 +1326,7 @@ public class RoomUnitManager { roomUnit.setZ(z); roomUnit.setPreviousLocationZ(z); this.room.updateRoomUnit(roomUnit); + WiredFreezeUtil.restoreWalkState(roomUnit); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index 0d5e74d2..d1f01bcc 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -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; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredFreezeUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredFreezeUtil.java new file mode 100644 index 00000000..70b73254 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredFreezeUtil.java @@ -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)); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserWalkEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserWalkEvent.java index 2b5d5a5e..0e174672 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserWalkEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserWalkEvent.java @@ -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; diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/RoomUnitTeleport.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/RoomUnitTeleport.java index 9747026e..43ddeb97 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/RoomUnitTeleport.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/RoomUnitTeleport.java @@ -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); diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFive.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFive.java index 9fab330b..ea5d1ee8 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFive.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFive.java @@ -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 onSuccess = new ArrayList(); 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"); diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFour.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFour.java index 5fea22ef..7b13dec4 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFour.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionFour.java @@ -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; diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionOne.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionOne.java index 757db39c..8ba24c37 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionOne.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionOne.java @@ -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; } diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionThree.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionThree.java index ed3a5d84..efcd9ca7 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionThree.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionThree.java @@ -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; } diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionTwo.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionTwo.java index eb5c4bb0..53cdc9c7 100644 --- a/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionTwo.java +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/teleport/TeleportActionTwo.java @@ -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; } From ce522eb3b38d1efe55ea99ab2bb3cdf72c6c1941 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Wed, 18 Mar 2026 14:38:21 +0100 Subject: [PATCH 4/9] feat(wired): add altitude and relative move effects --- .../habbo/habbohotel/items/ItemManager.java | 2 + .../effects/WiredEffectRelativeMove.java | 283 +++++++++++++++++ .../wired/effects/WiredEffectSetAltitude.java | 288 ++++++++++++++++++ .../habbohotel/wired/WiredEffectType.java | 4 +- 4 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java 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 2f317f1f..51845de6 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 @@ -269,6 +269,8 @@ public class ItemManager { 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_act_set_altitude", WiredEffectSetAltitude.class)); + this.interactionsList.add(new ItemInteraction("wf_act_rel_mov", WiredEffectRelativeMove.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)); 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 new file mode 100644 index 00000000..e2fcf249 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java @@ -0,0 +1,283 @@ +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +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.List; +import java.util.stream.Collectors; + +public class WiredEffectRelativeMove extends InteractionWiredEffect { + private static final int HORIZONTAL_NEGATIVE = 0; + private static final int HORIZONTAL_POSITIVE = 1; + private static final int VERTICAL_NEGATIVE = 0; + private static final int VERTICAL_POSITIVE = 1; + private static final int MAX_DISTANCE = 20; + + public static final WiredEffectType type = WiredEffectType.RELATIVE_MOVE; + + private final List items = new ArrayList<>(); + private int horizontalDirection = HORIZONTAL_POSITIVE; + private int horizontalDistance = 0; + private int verticalDirection = VERTICAL_POSITIVE; + private int verticalDistance = 0; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectRelativeMove(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectRelativeMove(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 || room.getLayout() == null) { + return; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + int deltaX = this.getHorizontalOffset(); + int deltaY = this.getVerticalOffset(); + + if (deltaX == 0 && deltaY == 0) { + return; + } + + for (HabboItem item : effectiveItems) { + if (item == null || item.getRoomId() != this.getRoomId()) { + continue; + } + + short targetX = (short) (item.getX() + deltaX); + short targetY = (short) (item.getY() + deltaY); + + RoomTile targetTile = room.getLayout().getTile(targetX, targetY); + if (targetTile == null) { + continue; + } + + room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false); + } + } + + @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.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.horizontalDirection, + this.horizontalDistance, + this.verticalDirection, + this.verticalDistance, + this.furniSource + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.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.horizontalDirection = this.normalizeBinary(data.horizontalDirection, HORIZONTAL_POSITIVE); + this.horizontalDistance = this.normalizeDistance(data.horizontalDistance); + this.verticalDirection = this.normalizeBinary(data.verticalDirection, VERTICAL_POSITIVE); + this.verticalDistance = this.normalizeDistance(data.verticalDistance); + this.furniSource = data.furniSource; + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.horizontalDirection = HORIZONTAL_POSITIVE; + this.horizontalDistance = 0; + this.verticalDirection = VERTICAL_POSITIVE; + this.verticalDistance = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.horizontalDirection = HORIZONTAL_POSITIVE; + this.horizontalDistance = 0; + this.verticalDirection = VERTICAL_POSITIVE; + this.verticalDistance = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List 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(5); + message.appendInt(this.horizontalDirection); + message.appendInt(this.horizontalDistance); + message.appendInt(this.verticalDirection); + message.appendInt(this.verticalDistance); + message.appendInt(this.furniSource); + 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 { + int[] params = settings.getIntParams(); + + if (params.length < 5) { + throw new WiredSaveException("Invalid data"); + } + + this.horizontalDirection = this.normalizeBinary(params[0], HORIZONTAL_POSITIVE); + this.horizontalDistance = this.normalizeDistance(params[1]); + this.verticalDirection = this.normalizeBinary(params[2], VERTICAL_POSITIVE); + this.verticalDistance = this.normalizeDistance(params[3]); + this.furniSource = params[4]; + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + 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); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int getHorizontalOffset() { + if (this.horizontalDistance <= 0) { + return 0; + } + + return (this.horizontalDirection == HORIZONTAL_NEGATIVE) ? -this.horizontalDistance : this.horizontalDistance; + } + + private int getVerticalOffset() { + if (this.verticalDistance <= 0) { + return 0; + } + + return (this.verticalDirection == VERTICAL_NEGATIVE) ? -this.verticalDistance : this.verticalDistance; + } + + private int normalizeBinary(int value, int fallback) { + if (value == 0 || value == 1) { + return value; + } + + return fallback; + } + + private int normalizeDistance(int value) { + return Math.max(0, Math.min(MAX_DISTANCE, value)); + } + + static class JsonData { + int delay; + List itemIds; + int horizontalDirection; + int horizontalDistance; + int verticalDirection; + int verticalDistance; + int furniSource; + + public JsonData(int delay, List itemIds, int horizontalDirection, int horizontalDistance, int verticalDirection, int verticalDistance, int furniSource) { + this.delay = delay; + this.itemIds = itemIds; + this.horizontalDirection = horizontalDirection; + this.horizontalDistance = horizontalDistance; + this.verticalDirection = verticalDirection; + this.verticalDistance = verticalDistance; + this.furniSource = furniSource; + } + } +} 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 new file mode 100644 index 00000000..e43a4a68 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java @@ -0,0 +1,288 @@ +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +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.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class WiredEffectSetAltitude extends InteractionWiredEffect { + private static final Pattern ALTITUDE_PATTERN = Pattern.compile("^\\d+(\\.\\d{1,2})?$"); + + private static final int OPERATOR_INCREASE = 0; + private static final int OPERATOR_DECREASE = 1; + private static final int OPERATOR_SET = 2; + + public static final WiredEffectType type = WiredEffectType.SET_ALTITUDE; + + private final List items = new ArrayList<>(); + private int operator = OPERATOR_SET; + private double altitude = 0.0D; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectSetAltitude(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectSetAltitude(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; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + for (HabboItem item : effectiveItems) { + if (item == null || item.getRoomId() != this.getRoomId()) { + continue; + } + + RoomTile tile = room.getLayout().getTile(item.getX(), item.getY()); + if (tile == null) { + continue; + } + + double nextAltitude = this.computeAltitude(item.getZ()); + room.moveFurniTo(item, tile, item.getRotation(), nextAltitude, null, true, false); + } + } + + @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.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.operator, + this.formatAltitude(this.altitude), + this.furniSource + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.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.operator = this.normalizeOperator(data.operator); + this.altitude = this.parseAltitudeOrDefault(data.altitude); + this.furniSource = data.furniSource; + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.operator = OPERATOR_SET; + this.altitude = 0.0D; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.operator = OPERATOR_SET; + this.altitude = 0.0D; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List 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(this.formatAltitude(this.altitude)); + message.appendInt(2); + message.appendInt(this.operator); + message.appendInt(this.furniSource); + 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 { + int[] params = settings.getIntParams(); + this.operator = (params.length > 0) ? this.normalizeOperator(params[0]) : OPERATOR_SET; + this.furniSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + 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); + } + + this.altitude = this.parseAltitude(settings.getStringParam()); + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int normalizeOperator(int value) { + if (value < OPERATOR_INCREASE || value > OPERATOR_SET) { + return OPERATOR_SET; + } + + return value; + } + + private double computeAltitude(double currentAltitude) { + double nextAltitude; + + switch (this.operator) { + case OPERATOR_INCREASE: + nextAltitude = currentAltitude + this.altitude; + break; + case OPERATOR_DECREASE: + nextAltitude = currentAltitude - this.altitude; + break; + case OPERATOR_SET: + default: + nextAltitude = this.altitude; + break; + } + + return this.normalizeAltitude(nextAltitude); + } + + private double parseAltitude(String value) throws WiredSaveException { + String normalized = (value != null) ? value.trim() : ""; + + if (normalized.isEmpty()) { + return 0.0D; + } + + if (!ALTITUDE_PATTERN.matcher(normalized).matches()) { + throw new WiredSaveException("Invalid altitude value"); + } + + try { + return this.normalizeAltitude(new BigDecimal(normalized).doubleValue()); + } catch (NumberFormatException exception) { + throw new WiredSaveException("Invalid altitude value"); + } + } + + private double parseAltitudeOrDefault(String value) { + try { + return this.parseAltitude(value); + } catch (WiredSaveException exception) { + return 0.0D; + } + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + private String formatAltitude(double value) { + BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros(); + return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString(); + } + + static class JsonData { + int delay; + List itemIds; + int operator; + String altitude; + int furniSource; + + public JsonData(int delay, List itemIds, int operator, String altitude, int furniSource) { + this.delay = delay; + this.itemIds = itemIds; + this.operator = operator; + this.altitude = altitude; + this.furniSource = furniSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index d1f01bcc..8ece3410 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -37,7 +37,9 @@ public enum WiredEffectType { UNFREEZE(35), FURNI_TO_USER(36), USER_TO_FURNI(37), - FURNI_TO_FURNI(38); + FURNI_TO_FURNI(38), + SET_ALTITUDE(39), + RELATIVE_MOVE(40); public final int code; From c9f5c62c7fc0b4c21c1823a9f90ea5ac3f133449 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Wed, 18 Mar 2026 17:01:10 +0100 Subject: [PATCH 5/9] fix(wired): improve rewards and room mute feedback --- .../habbohotel/commands/UnmuteCommand.java | 4 +- .../wired/effects/WiredEffectGiveReward.java | 9 - .../com/eu/habbo/habbohotel/rooms/Room.java | 8 +- .../habbohotel/rooms/RoomChatManager.java | 14 +- .../habbo/habbohotel/wired/WiredHandler.java | 167 ++++++++------- .../habbohotel/wired/core/WiredManager.java | 192 +++++++++++------- .../outgoing/users/MutedWhisperComposer.java | 2 +- 7 files changed, 239 insertions(+), 157 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java index 376f8b94..7cd922e9 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java @@ -29,7 +29,7 @@ public class UnmuteCommand extends Command { } if (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo)) { - habbo.getHabboInfo().getCurrentRoom().muteHabbo(habbo, 1); + habbo.getHabboInfo().getCurrentRoom().unmuteHabbo(habbo); } gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_unmute").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT); @@ -41,4 +41,4 @@ public class UnmuteCommand extends Command { return true; } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveReward.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveReward.java index a2df0c12..e57bc582 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveReward.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveReward.java @@ -140,15 +140,6 @@ public class WiredEffectGiveReward extends InteractionWiredEffect { return type; } - @Override - public void onClick(GameClient client, Room room, Object[] objects) throws Exception { - super.onClick(client, room, objects); - - if (client.getHabbo().hasPermission(Permission.ACC_SUPERWIRED)) { - client.getHabbo().whisper(Emulator.getTexts().getValue("hotel.wired.superwired.info"), RoomChatMessageBubbles.BOT); - } - } - @Override public void serializeWiredData(ServerMessage message, Room room) { message.appendBoolean(false); 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 2d9ff42a..16294e82 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 @@ -1865,11 +1865,15 @@ public class Room implements Comparable, ISerialize, Runnable { } public void muteHabbo(Habbo habbo, int minutes) { - this.rightsManager.muteHabbo(habbo, minutes); + this.chatManager.muteHabbo(habbo, minutes); + } + + public void unmuteHabbo(Habbo habbo) { + this.chatManager.unmuteHabbo(habbo); } public boolean isMuted(Habbo habbo) { - return this.rightsManager.isMuted(habbo); + return this.chatManager.isMuted(habbo); } public void habboEntered(Habbo habbo) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatManager.java index 3665aca1..dae39207 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatManager.java @@ -156,6 +156,15 @@ public class RoomChatManager { } } + /** + * Removes a room mute from a Habbo. + */ + public void unmuteHabbo(Habbo habbo) { + synchronized (this.mutedHabbos) { + this.mutedHabbos.remove(habbo.getHabboInfo().getId()); + } + } + /** * Checks if a Habbo is muted. */ @@ -183,7 +192,8 @@ public class RoomChatManager { */ public int getMuteTimeRemaining(Habbo habbo) { if (this.mutedHabbos.containsKey(habbo.getHabboInfo().getId())) { - return this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp(); + return Math.max(0, + this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp()); } return 0; } @@ -298,7 +308,7 @@ public class RoomChatManager { if (this.isMuted(habbo)) { habbo.getClient().sendResponse(new MutedWhisperComposer( - this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp())); + Math.max(1, this.getMuteTimeRemaining(habbo)))); return; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java index b837f0c0..9490bcf1 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredHandler.java @@ -285,88 +285,115 @@ public class WiredHandler { } } - private static void giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) { - if (wiredBox.limit > 0) - wiredBox.given++; - + private static void persistReward(int wiredId, int habboId, int rewardId, int timestamp) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO wired_rewards_given (wired_item, user_id, reward_id, timestamp) VALUES ( ?, ?, ?, ?)")) { - statement.setInt(1, wiredBox.getId()); - statement.setInt(2, habbo.getHabboInfo().getId()); - statement.setInt(3, reward.id); - statement.setInt(4, Emulator.getIntUnixTimestamp()); + statement.setInt(1, wiredId); + statement.setInt(2, habboId); + statement.setInt(3, rewardId); + statement.setInt(4, timestamp); statement.execute(); } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } + } + private static void completeReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward, int successCode) { + if (wiredBox.limit > 0) + wiredBox.given++; + + persistReward(wiredBox.getId(), habbo.getHabboInfo().getId(), reward.id, Emulator.getIntUnixTimestamp()); + habbo.getClient().sendResponse(new WiredRewardAlertComposer(successCode)); + } + + private static boolean giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) { if (reward.badge) { UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, "badge", reward.data); if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) - return; + return false; if (rewardReceived.value.isEmpty()) - return; + return false; - if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) - return; + if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) { + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALREADY_RECEIVED)); + return false; + } HabboBadge badge = new HabboBadge(0, rewardReceived.value, 0, habbo); Emulator.getThreading().run(badge); habbo.getInventory().getBadgesComponent().addBadge(badge); habbo.getClient().sendResponse(new AddUserBadgeComposer(badge)); - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_BADGE)); - } else { - String[] data = reward.data.split("#"); - - if (data.length == 2) { - UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]); - if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) - return; - - if (rewardReceived.value.isEmpty()) - return; - - if (rewardReceived.type.equalsIgnoreCase("credits")) { - int credits = Integer.parseInt(rewardReceived.value); - habbo.giveCredits(credits); - } else if (rewardReceived.type.equalsIgnoreCase("pixels")) { - int pixels = Integer.parseInt(rewardReceived.value); - habbo.givePixels(pixels); - } else if (rewardReceived.type.startsWith("points")) { - int points = Integer.parseInt(rewardReceived.value); - int type = 5; - - try { - type = Integer.parseInt(rewardReceived.type.replace("points", "")); - } catch (Exception e) { - } - - habbo.givePoints(type, points); - } else if (rewardReceived.type.equalsIgnoreCase("furni")) { - Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value)); - if (baseItem != null) { - HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, ""); - - if (item != null) { - habbo.getClient().sendResponse(new AddHabboItemComposer(item)); - habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item); - habbo.getClient().sendResponse(new PurchaseOKComposer(null)); - habbo.getClient().sendResponse(new InventoryRefreshComposer()); - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM)); - } - } - } else if (rewardReceived.type.equalsIgnoreCase("respect")) { - habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value); - } else if (rewardReceived.type.equalsIgnoreCase("cata")) { - CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value)); - - if (item != null) { - Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true); - } - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM)); - } - } + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_BADGE); + return true; } + + String[] data = reward.data.split("#"); + + if (data.length != 2) + return false; + + UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]); + if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) + return false; + + if (rewardReceived.value.isEmpty()) + return false; + + if (rewardReceived.type.equalsIgnoreCase("credits")) { + habbo.giveCredits(Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.equalsIgnoreCase("diamonds") || rewardReceived.type.equalsIgnoreCase("diamond")) { + habbo.givePoints(5, Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.equalsIgnoreCase("pixels")) { + habbo.givePixels(Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.startsWith("points")) { + int points = Integer.parseInt(rewardReceived.value); + int type = 5; + + try { + type = Integer.parseInt(rewardReceived.type.replace("points", "")); + } catch (Exception e) { + } + + habbo.givePoints(type, points); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.equalsIgnoreCase("furni")) { + Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value)); + if (baseItem == null) + return false; + + HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, ""); + if (item == null) + return false; + + habbo.getClient().sendResponse(new AddHabboItemComposer(item)); + habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item); + habbo.getClient().sendResponse(new PurchaseOKComposer(null)); + habbo.getClient().sendResponse(new InventoryRefreshComposer()); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.equalsIgnoreCase("respect")) { + habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } else if (rewardReceived.type.equalsIgnoreCase("cata")) { + CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value)); + + if (item == null) + return false; + + Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + return false; } public static boolean getReward(Habbo habbo, WiredEffectGiveReward wiredBox) { @@ -433,22 +460,26 @@ public class WiredHandler { } if (!found) { - giveReward(habbo, wiredBox, item); - return true; + return giveReward(habbo, wiredBox, item); } } + + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALL_COLLECTED)); + return false; } else { int randomNumber = Emulator.getRandom().nextInt(101); int count = 0; for (WiredGiveRewardItem item : wiredBox.rewardItems) { if (randomNumber >= count && randomNumber <= (count + item.probability)) { - giveReward(habbo, wiredBox, item); - return true; + return giveReward(habbo, wiredBox, item); } count += item.probability; } + + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.UNLUCKY_NO_REWARD)); + return false; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index e758edf8..ea712884 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -771,17 +771,9 @@ public final class WiredManager { }); } - private static void giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) { - if (wiredBox.getLimit() > 0) - wiredBox.incrementGiven(); - - final int wiredId = wiredBox.getId(); - final int habboId = habbo.getHabboInfo().getId(); - final int rewardId = reward.id; - final int timestamp = Emulator.getIntUnixTimestamp(); - + private static void persistReward(int wiredId, int habboId, int rewardId, int timestamp) { Emulator.getThreading().run(() -> { - try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO wired_rewards_given (wired_item, user_id, reward_id, timestamp) VALUES ( ?, ?, ?, ?)")) { statement.setInt(1, wiredId); statement.setInt(2, habboId); @@ -792,75 +784,125 @@ public final class WiredManager { LOGGER.error("Caught SQL exception", e); } }); + } + private static void completeReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward, int successCode) { + if (wiredBox.getLimit() > 0) { + wiredBox.incrementGiven(); + } + + persistReward(wiredBox.getId(), habbo.getHabboInfo().getId(), reward.id, Emulator.getIntUnixTimestamp()); + habbo.getClient().sendResponse(new WiredRewardAlertComposer(successCode)); + } + + private static boolean giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) { if (reward.badge) { UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, "badge", reward.data); - if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) - return; + if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) { + return false; + } - if (rewardReceived.value.isEmpty()) - return; - - if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) - return; + if (rewardReceived.value.isEmpty()) { + return false; + } + + if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) { + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALREADY_RECEIVED)); + return false; + } HabboBadge badge = new HabboBadge(0, rewardReceived.value, 0, habbo); Emulator.getThreading().run(badge); habbo.getInventory().getBadgesComponent().addBadge(badge); habbo.getClient().sendResponse(new AddUserBadgeComposer(badge)); - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_BADGE)); - } else { - String[] data = reward.data.split("#"); - - if (data.length == 2) { - UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]); - if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) - return; - - if (rewardReceived.value.isEmpty()) - return; - - if (rewardReceived.type.equalsIgnoreCase("credits")) { - int credits = Integer.parseInt(rewardReceived.value); - habbo.giveCredits(credits); - } else if (rewardReceived.type.equalsIgnoreCase("pixels")) { - int pixels = Integer.parseInt(rewardReceived.value); - habbo.givePixels(pixels); - } else if (rewardReceived.type.startsWith("points")) { - int points = Integer.parseInt(rewardReceived.value); - int type = 5; - - try { - type = Integer.parseInt(rewardReceived.type.replace("points", "")); - } catch (Exception e) { - } - - habbo.givePoints(type, points); - } else if (rewardReceived.type.equalsIgnoreCase("furni")) { - Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value)); - if (baseItem != null) { - HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, ""); - - if (item != null) { - habbo.getClient().sendResponse(new AddHabboItemComposer(item)); - habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item); - habbo.getClient().sendResponse(new PurchaseOKComposer(null)); - habbo.getClient().sendResponse(new InventoryRefreshComposer()); - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM)); - } - } - } else if (rewardReceived.type.equalsIgnoreCase("respect")) { - habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value); - } else if (rewardReceived.type.equalsIgnoreCase("cata")) { - CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value)); - - if (item != null) { - Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true); - } - habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM)); - } - } + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_BADGE); + return true; } + + String[] data = reward.data.split("#"); + + if (data.length != 2) { + return false; + } + + UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]); + if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) { + return false; + } + + if (rewardReceived.value.isEmpty()) { + return false; + } + + if (rewardReceived.type.equalsIgnoreCase("credits")) { + habbo.giveCredits(Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.equalsIgnoreCase("diamonds") || rewardReceived.type.equalsIgnoreCase("diamond")) { + habbo.givePoints(5, Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.equalsIgnoreCase("pixels")) { + habbo.givePixels(Integer.parseInt(rewardReceived.value)); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.startsWith("points")) { + int points = Integer.parseInt(rewardReceived.value); + int type = 5; + + try { + type = Integer.parseInt(rewardReceived.type.replace("points", "")); + } catch (Exception e) { + } + + habbo.givePoints(type, points); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.equalsIgnoreCase("furni")) { + Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value)); + if (baseItem == null) { + return false; + } + + HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, ""); + if (item == null) { + return false; + } + + habbo.getClient().sendResponse(new AddHabboItemComposer(item)); + habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item); + habbo.getClient().sendResponse(new PurchaseOKComposer(null)); + habbo.getClient().sendResponse(new InventoryRefreshComposer()); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.equalsIgnoreCase("respect")) { + habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + if (rewardReceived.type.equalsIgnoreCase("cata")) { + CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value)); + if (item == null) { + return false; + } + + Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true); + completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM); + return true; + } + + return false; } public static boolean getReward(Habbo habbo, WiredEffectGiveReward wiredBox) { @@ -927,22 +969,26 @@ public final class WiredManager { } if (!found) { - giveReward(habbo, wiredBox, item); - return true; + return giveReward(habbo, wiredBox, item); } } + + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALL_COLLECTED)); + return false; } else { int randomNumber = Emulator.getRandom().nextInt(101); int count = 0; for (WiredGiveRewardItem item : wiredBox.getRewardItems()) { if (randomNumber >= count && randomNumber <= (count + item.probability)) { - giveReward(habbo, wiredBox, item); - return true; + return giveReward(habbo, wiredBox, item); } count += item.probability; } + + habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.UNLUCKY_NO_REWARD)); + return false; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/MutedWhisperComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/MutedWhisperComposer.java index bae0cc7a..ef24051a 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/MutedWhisperComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/MutedWhisperComposer.java @@ -8,7 +8,7 @@ public class MutedWhisperComposer extends MessageComposer { private final int seconds; public MutedWhisperComposer(int seconds) { - this.seconds = seconds; + this.seconds = Math.max(0, seconds); } @Override From 75151accb57c56dfa12c27d8b76262526ccbff8d Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Thu, 19 Mar 2026 00:01:59 +0100 Subject: [PATCH 6/9] feat(wired): add upcounter clock controls and checks --- .../habbo/habbohotel/items/ItemManager.java | 6 + .../games/InteractionGameTimer.java | 85 ++++-- .../games/InteractionGameUpCounter.java | 214 ++++++++++++++ .../WiredConditionCounterTimeMatches.java | 270 ++++++++++++++++++ .../wired/effects/WiredEffectAdjustClock.java | 252 ++++++++++++++++ .../effects/WiredEffectControlClock.java | 244 ++++++++++++++++ .../triggers/WiredTriggerClockCounter.java | 229 +++++++++++++++ .../com/eu/habbo/habbohotel/rooms/Room.java | 7 +- .../eu/habbo/habbohotel/users/HabboItem.java | 5 +- .../habbohotel/wired/WiredConditionType.java | 3 +- .../habbohotel/wired/WiredEffectType.java | 4 +- .../habbohotel/wired/WiredTriggerType.java | 1 + .../habbohotel/wired/core/WiredEvent.java | 3 + .../habbohotel/wired/core/WiredManager.java | 9 + .../habbohotel/wired/migrate/WiredEvents.java | 6 + .../runnables/games/GameUpCounter.java | 46 +++ 16 files changed, 1356 insertions(+), 28 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java 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 51845de6..4e645d4b 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 @@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.items; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.*; import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTimer; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiPuck; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiSphere; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiTeleporter; @@ -206,6 +207,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("room_invisible_click_tile", InteractionDefault.class)); this.interactionsList.add(new ItemInteraction("game_timer", InteractionGameTimer.class)); + this.interactionsList.add(new ItemInteraction("game_upcounter", InteractionGameUpCounter.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_on_furni", WiredTriggerHabboWalkOnFurni.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_off_furni", WiredTriggerHabboWalkOffFurni.class)); @@ -216,6 +218,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_trg_enter_room", WiredTriggerHabboEntersRoom.class)); this.interactionsList.add(new ItemInteraction("wf_trg_leave_room", WiredTriggerHabboLeavesRoom.class)); this.interactionsList.add(new ItemInteraction("wf_trg_says_something", WiredTriggerHabboSaysKeyword.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_clock_counter", WiredTriggerClockCounter.class)); this.interactionsList.add(new ItemInteraction("wf_trg_periodically", WiredTriggerRepeater.class)); this.interactionsList.add(new ItemInteraction("wf_trg_period_short", WiredTriggerRepeaterShort.class)); this.interactionsList.add(new ItemInteraction("wf_trg_period_long", WiredTriggerRepeaterLong.class)); @@ -271,6 +274,8 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_act_furni_to_furni", WiredEffectFurniToFurni.class)); this.interactionsList.add(new ItemInteraction("wf_act_set_altitude", WiredEffectSetAltitude.class)); this.interactionsList.add(new ItemInteraction("wf_act_rel_mov", WiredEffectRelativeMove.class)); + this.interactionsList.add(new ItemInteraction("wf_act_control_clock", WiredEffectControlClock.class)); + this.interactionsList.add(new ItemInteraction("wf_act_adjust_clock", WiredEffectAdjustClock.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)); @@ -303,6 +308,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_cnd_has_handitem", WiredConditionHabboHasHandItem.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_date_rng_active", WiredConditionDateRangeActive.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_valid_moves", WiredConditionMovementValidation.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java index e93d5308..62160f59 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java @@ -24,13 +24,13 @@ import java.util.Arrays; public class InteractionGameTimer extends HabboItem { private static final Logger LOGGER = LoggerFactory.getLogger(InteractionGameTimer.class); - private int[] TIMER_INTERVAL_STEPS = new int[] { 30, 60, 120, 180, 300, 600 }; + protected int[] TIMER_INTERVAL_STEPS = new int[] { 30, 60, 120, 180, 300, 600 }; - private int baseTime = 0; - private int timeNow = 0; - private boolean isRunning = false; - private boolean isPaused = false; - private boolean threadActive = false; + protected int baseTime = 0; + protected int timeNow = 0; + protected boolean isRunning = false; + protected boolean isPaused = false; + protected boolean threadActive = false; public enum InteractionGameTimerAction { START_STOP(1), @@ -83,7 +83,7 @@ public class InteractionGameTimer extends HabboItem { parseCustomParams(item); } - private void parseCustomParams(Item baseItem) { + protected void parseCustomParams(Item baseItem) { try { TIMER_INTERVAL_STEPS = Arrays.stream(baseItem.getCustomParams().split(",")) .mapToInt(s -> { @@ -114,7 +114,7 @@ public class InteractionGameTimer extends HabboItem { } } - private void createNewGame(Room room) { + protected void createNewGame(Room room) { for(Class gameClass : Emulator.getGameEnvironment().getRoomManager().getGameTypes()) { Game existingGame = room.getGame(gameClass); @@ -132,13 +132,13 @@ public class InteractionGameTimer extends HabboItem { } } - private void pause(Room room) { + protected void pause(Room room) { for (Game game : room.getGames()) { game.pause(); } } - private void unpause(Room room) { + protected void unpause(Room room) { for (Game game : room.getGames()) { game.unpause(); } @@ -155,7 +155,8 @@ public class InteractionGameTimer extends HabboItem { public void onPickUp(Room room) { this.endGame(room); - this.setExtradata(this.baseTime + "\t" + this.baseTime); + this.timeNow = this.getInitialTimeValue(); + this.setExtradata(this.timeNow + "\t" + this.baseTime); this.needsUpdate(true); } @@ -165,7 +166,7 @@ public class InteractionGameTimer extends HabboItem { this.baseTime = this.TIMER_INTERVAL_STEPS[0]; } - this.timeNow = this.baseTime; + this.timeNow = this.getInitialTimeValue(); this.setExtradata(this.timeNow + "\t" + this.baseTime); room.updateItem(this); @@ -212,7 +213,7 @@ public class InteractionGameTimer extends HabboItem { this.createNewGame(room); - this.timeNow = this.baseTime; + this.resetTimeForStart(); this.isRunning = true; this.isPaused = false; @@ -221,7 +222,7 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } else if (client != null) { if (!(room.hasRights(client.getHabbo()) || client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER))) @@ -244,13 +245,13 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this)); + this.scheduleTimerRunnable(this.getTimerResumeDelayMs()); } } } else { this.isPaused = false; this.isRunning = true; - this.timeNow = this.baseTime; + this.resetTimeForStart(); room.updateItem(this); this.createNewGame(room); @@ -258,7 +259,7 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } @@ -290,15 +291,15 @@ public class InteractionGameTimer extends HabboItem { if (!isRunning) { isRunning = true; isPaused = false; - if(timeNow <= 0) { - timeNow = baseTime; + if (this.shouldResetTimeOnStart()) { + this.resetTimeForStart(); room.updateItem(this); } this.createNewGame(room); WiredManager.triggerGameStarts(room); if (!threadActive) { threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } } @@ -322,12 +323,12 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerResumeDelayMs()); } } } - private void increaseTimer(Room room) { + protected void increaseTimer(Room room) { if (this.isRunning) return; @@ -347,13 +348,45 @@ public class InteractionGameTimer extends HabboItem { } this.baseTime = baseTime; + this.timeNow = this.getInitialTimeValue(); this.setExtradata(this.timeNow + "\t" + this.baseTime); - - this.timeNow = this.baseTime; room.updateItem(this); this.needsUpdate(true); } + protected int getInitialTimeValue() { + return this.baseTime; + } + + protected boolean shouldResetTimeOnStart() { + return this.timeNow <= 0; + } + + protected void resetTimeForStart() { + this.timeNow = this.baseTime; + } + + protected Runnable createTimerRunnable() { + return new GameTimer(this); + } + + protected long getTimerStartDelayMs() { + return 1000L; + } + + protected long getTimerResumeDelayMs() { + return 0L; + } + + protected void scheduleTimerRunnable(long delayMs) { + if (delayMs <= 0) { + Emulator.getThreading().run(this.createTimerRunnable()); + return; + } + + Emulator.getThreading().run(this.createTimerRunnable(), delayMs); + } + @Override public String getDatabaseExtraData() { return this.getExtradata(); @@ -391,4 +424,8 @@ public class InteractionGameTimer extends HabboItem { public void setTimeNow(int timeNow) { this.timeNow = timeNow; } + + public int getBaseTime() { + return this.baseTime; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java new file mode 100644 index 00000000..41e05a7a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java @@ -0,0 +1,214 @@ +package com.eu.habbo.habbohotel.items.interactions.games; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.threading.runnables.games.GameUpCounter; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class InteractionGameUpCounter extends InteractionGameTimer { + private static final int ONE_SECOND_MS = 1000; + private static final int HALF_SECOND_MS = 500; + private static final int MAX_UPCOUNTER_TIME = ((99 * 60) + 59); + private int subSecondOffsetMs = 0; + + public InteractionGameUpCounter(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.normalizeCounterState(); + } + + public InteractionGameUpCounter(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.normalizeCounterState(); + } + + @Override + protected void parseCustomParams(Item baseItem) { + this.TIMER_INTERVAL_STEPS = new int[] { MAX_UPCOUNTER_TIME }; + } + + private void normalizeCounterState() { + this.baseTime = MAX_UPCOUNTER_TIME; + this.setCurrentTimeInMs(this.parseStoredTime() * ONE_SECOND_MS); + this.setExtradata(this.timeNow + "\t" + this.baseTime); + } + + private int parseStoredTime() { + try { + String[] data = this.getExtradata().split("\t"); + + if (data.length > 0) { + int storedTime = Integer.parseInt(data[0]); + return Math.max(0, Math.min(storedTime, this.baseTime)); + } + } catch (Exception ignored) { + } + + return Math.max(0, Math.min(this.timeNow, this.baseTime)); + } + + @Override + protected int getInitialTimeValue() { + return 0; + } + + @Override + protected boolean shouldResetTimeOnStart() { + return this.timeNow >= this.baseTime; + } + + @Override + protected void resetTimeForStart() { + this.setCurrentTimeInMs(0); + } + + @Override + protected void increaseTimer(Room room) { + if (this.isRunning && !this.isPaused) { + return; + } + + if (this.isRunning) { + this.endGame(room); + WiredManager.triggerGameEnds(room); + } + + this.baseTime = MAX_UPCOUNTER_TIME; + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + } + + @Override + protected Runnable createTimerRunnable() { + return new GameUpCounter(this); + } + + @Override + protected long getTimerStartDelayMs() { + return this.getNextTickDelayMs(); + } + + @Override + protected long getTimerResumeDelayMs() { + return this.getNextTickDelayMs(); + } + + public int getCurrentTimeInMs() { + return (this.timeNow * ONE_SECOND_MS) + this.subSecondOffsetMs; + } + + public int getMaximumTimeInMs() { + return this.baseTime * ONE_SECOND_MS; + } + + public long getNextTickDelayMs() { + return (this.subSecondOffsetMs > 0) ? HALF_SECOND_MS : ONE_SECOND_MS; + } + + public void setCurrentTimeInMs(int totalMs) { + int clamped = Math.max(0, Math.min(totalMs, this.getMaximumTimeInMs())); + int remainder = clamped % ONE_SECOND_MS; + + this.timeNow = (clamped / ONE_SECOND_MS); + this.subSecondOffsetMs = (remainder >= HALF_SECOND_MS) ? HALF_SECOND_MS : 0; + } + + public void advanceCounterInMs(int deltaMs) { + this.setCurrentTimeInMs(this.getCurrentTimeInMs() + deltaMs); + } + + private void applyCounterState(Room room, boolean updateRoom) { + this.setExtradata(this.timeNow + "\t" + this.baseTime); + + if (updateRoom && room != null) { + room.updateItem(this); + } + + this.needsUpdate(true); + } + + public void restartFromZero(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + if (wasActive) { + this.endGame(room); + WiredManager.triggerGameEnds(room); + } + + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + + this.startTimer(room); + } + + public void stopCounter(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + this.endGame(room); + this.applyCounterState(room, true); + + if (wasActive) { + WiredManager.triggerGameEnds(room); + } + } + + public void resetCounter(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + this.endGame(room); + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + + if (wasActive) { + WiredManager.triggerGameEnds(room); + } + } + + public void pauseCounter(Room room) { + if (!this.isRunning || this.isPaused) { + return; + } + + this.pauseTimer(room); + this.applyCounterState(room, true); + } + + public void resumeCounter(Room room) { + if (!this.isPaused) { + return; + } + + this.resumeTimer(room); + this.applyCounterState(room, true); + } + + public void adjustCounter(Room room, int operator, int minutes, int halfSecondSteps) { + int deltaMs = (Math.max(0, minutes) * 60000) + (Math.max(0, halfSecondSteps) * HALF_SECOND_MS); + int nextTimeMs = this.getCurrentTimeInMs(); + + switch (operator) { + case 0: + nextTimeMs += deltaMs; + break; + case 1: + nextTimeMs -= deltaMs; + break; + case 2: + default: + nextTimeMs = deltaMs; + break; + } + + this.setCurrentTimeInMs(nextTimeMs); + this.applyCounterState(room, true); + } + + public void resetOnRoomUnload(Room room) { + this.endGame(room); + this.setThreadActive(false); + this.setCurrentTimeInMs(0); + this.applyCounterState(null, false); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java new file mode 100644 index 00000000..639eb38c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java @@ -0,0 +1,270 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +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 gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredConditionCounterTimeMatches extends InteractionWiredCondition { + private static final int COMPARISON_LESS = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER = 2; + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredConditionType type = WiredConditionType.COUNTER_TIME_MATCHES; + + private final THashSet items; + private int comparison = COMPARISON_EQUAL; + private int minutes = 0; + private int halfSecondSteps = 0; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredConditionCounterTimeMatches(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredConditionCounterTimeMatches(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean evaluate(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return false; + } + + this.refresh(room); + + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (targets.isEmpty()) { + return false; + } + + int targetTimeInMs = this.getTargetTimeInMs(); + + for (HabboItem item : targets) { + if (!(item instanceof InteractionGameUpCounter)) { + return false; + } + + int currentTimeInMs = ((InteractionGameUpCounter) item).getCurrentTimeInMs(); + + switch (this.comparison) { + case COMPARISON_LESS: + if (currentTimeInMs >= targetTimeInMs) { + return false; + } + break; + case COMPARISON_GREATER: + if (currentTimeInMs <= targetTimeInMs) { + return false; + } + break; + default: + if (currentTimeInMs != targetTimeInMs) { + return false; + } + break; + } + } + + return true; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.minutes, + this.halfSecondSteps, + this.furniSource, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + this.furniSource = data.furniSource; + + if (data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.comparison); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(this.furniSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL; + this.minutes = (params.length > 1) ? this.normalizeMinutes(params[1]) : 0; + this.halfSecondSteps = (params.length > 2) ? this.normalizeHalfSecondSteps(params[2]) : 0; + this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER; + + this.items.clear(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + + return true; + } + + private void refresh(Room room) { + THashSet remove = new THashSet<>(); + + for (HabboItem item : this.items) { + HabboItem roomItem = room.getHabboItem(item.getId()); + if (!(roomItem instanceof InteractionGameUpCounter)) { + remove.add(item); + } + } + + for (HabboItem item : remove) { + this.items.remove(item); + } + } + + private int getTargetTimeInMs() { + return (this.minutes * 60_000) + (this.halfSecondSteps * 500); + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + return value; + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + static class JsonData { + int comparison; + int minutes; + int halfSecondSteps; + int furniSource; + List itemIds; + + public JsonData(int comparison, int minutes, int halfSecondSteps, int furniSource, List itemIds) { + this.comparison = comparison; + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + this.furniSource = furniSource; + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java new file mode 100644 index 00000000..183eec7d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java @@ -0,0 +1,252 @@ +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.games.InteractionGameUpCounter; +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.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.List; +import java.util.stream.Collectors; + +public class WiredEffectAdjustClock extends InteractionWiredEffect { + private static final int OPERATOR_INCREASE = 0; + private static final int OPERATOR_DECREASE = 1; + private static final int OPERATOR_SET = 2; + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredEffectType type = WiredEffectType.ADJUST_CLOCK; + + private final List items = new ArrayList<>(); + private int operator = OPERATOR_SET; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int minutes = 0; + private int halfSecondSteps = 0; + + public WiredEffectAdjustClock(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectAdjustClock(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; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + for (HabboItem item : effectiveItems) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + ((InteractionGameUpCounter) item).adjustCounter(room, this.operator, this.minutes, this.halfSecondSteps); + } + } + + @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.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.operator, + this.furniSource, + this.minutes, + this.halfSecondSteps + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.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.operator = this.normalizeOperator(data.operator); + this.furniSource = data.furniSource; + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.operator = OPERATOR_SET; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.minutes = 0; + this.halfSecondSteps = 0; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.operator = OPERATOR_SET; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.minutes = 0; + this.halfSecondSteps = 0; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List 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(4); + message.appendInt(this.operator); + message.appendInt(this.furniSource); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + 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 { + int[] params = settings.getIntParams(); + + if (params.length < 4) { + throw new WiredSaveException("Invalid data"); + } + + this.operator = this.normalizeOperator(params[0]); + this.furniSource = params[1]; + this.minutes = this.normalizeMinutes(params[2]); + this.halfSecondSteps = this.normalizeHalfSecondSteps(params[3]); + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + throw new WiredSaveException(String.format("Item %s not found", itemId)); + } + + if (!(item instanceof InteractionGameUpCounter)) { + throw new WiredSaveException("wiredfurni.error.require_counter_furni"); + } + + newItems.add(item); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int normalizeOperator(int value) { + if (value < OPERATOR_INCREASE || value > OPERATOR_SET) { + return OPERATOR_SET; + } + + return value; + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + static class JsonData { + int delay; + List itemIds; + int operator; + int furniSource; + int minutes; + int halfSecondSteps; + + public JsonData(int delay, List itemIds, int operator, int furniSource, int minutes, int halfSecondSteps) { + this.delay = delay; + this.itemIds = itemIds; + this.operator = operator; + this.furniSource = furniSource; + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java new file mode 100644 index 00000000..b4f55831 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java @@ -0,0 +1,244 @@ +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.games.InteractionGameUpCounter; +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.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.List; +import java.util.stream.Collectors; + +public class WiredEffectControlClock extends InteractionWiredEffect { + private static final int ACTION_START = 0; + private static final int ACTION_STOP = 1; + private static final int ACTION_RESET = 2; + private static final int ACTION_PAUSE = 3; + private static final int ACTION_RESUME = 4; + + public static final WiredEffectType type = WiredEffectType.CONTROL_CLOCK; + + private final List items = new ArrayList<>(); + private int action = ACTION_START; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectControlClock(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectControlClock(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; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + for (HabboItem item : effectiveItems) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + InteractionGameUpCounter counter = (InteractionGameUpCounter) item; + + switch (this.action) { + case ACTION_START: + counter.restartFromZero(room); + break; + case ACTION_STOP: + counter.stopCounter(room); + break; + case ACTION_RESET: + counter.resetCounter(room); + break; + case ACTION_PAUSE: + counter.pauseCounter(room); + break; + case ACTION_RESUME: + counter.resumeCounter(room); + break; + } + } + } + + @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.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.action, + this.furniSource + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.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.action = this.normalizeAction(data.action); + this.furniSource = data.furniSource; + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.action = ACTION_START; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.action = ACTION_START; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List 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.action); + message.appendInt(this.furniSource); + 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 { + int[] params = settings.getIntParams(); + + if (params.length < 2) { + throw new WiredSaveException("Invalid data"); + } + + this.action = this.normalizeAction(params[0]); + this.furniSource = params[1]; + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + throw new WiredSaveException(String.format("Item %s not found", itemId)); + } + + if (!(item instanceof InteractionGameUpCounter)) { + throw new WiredSaveException("wiredfurni.error.require_counter_furni"); + } + + newItems.add(item); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int normalizeAction(int value) { + if (value < ACTION_START || value > ACTION_RESUME) { + return ACTION_START; + } + + return value; + } + + static class JsonData { + int delay; + List itemIds; + int action; + int furniSource; + + public JsonData(int delay, List itemIds, int action, int furniSource) { + this.delay = delay; + this.itemIds = itemIds; + this.action = action; + this.furniSource = furniSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java new file mode 100644 index 00000000..2e6c9d78 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java @@ -0,0 +1,229 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredTriggerClockCounter extends InteractionWiredTrigger { + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredTriggerType type = WiredTriggerType.CLOCK_COUNTER; + + private final THashSet items; + private int minutes = 0; + private int halfSecondSteps = 0; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredTriggerClockCounter(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredTriggerClockCounter(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + HabboItem sourceItem = event.getSourceItem().orElse(null); + + if (!(sourceItem instanceof InteractionGameUpCounter)) { + return false; + } + + if (((InteractionGameUpCounter) sourceItem).getCurrentTimeInMs() != this.getTargetTimeInMs()) { + return false; + } + + return (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) || this.items.contains(sourceItem); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public WiredTriggerType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(this.furniSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.minutes = (params.length > 0) ? this.normalizeMinutes(params[0]) : 0; + this.halfSecondSteps = (params.length > 1) ? this.normalizeHalfSecondSteps(params[1]) : 0; + this.furniSource = (params.length > 2) ? this.normalizeFurniSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER; + + this.items.clear(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.minutes, + this.halfSecondSteps, + this.furniSource, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (!wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + this.furniSource = this.normalizeFurniSource(data.furniSource); + + if (data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + private void refresh(Room room) { + THashSet remove = new THashSet<>(); + + for (HabboItem item : this.items) { + HabboItem roomItem = room.getHabboItem(item.getId()); + if (!(roomItem instanceof InteractionGameUpCounter)) { + remove.add(item); + } + } + + for (HabboItem item : remove) { + this.items.remove(item); + } + } + + private int getTargetTimeInMs() { + return (this.minutes * 60_000) + (this.halfSecondSteps * 500); + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + private int normalizeFurniSource(int value) { + return (value == WiredSourceUtil.SOURCE_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER; + } + + static class JsonData { + int minutes; + int halfSecondSteps; + int furniSource; + List itemIds; + + public JsonData(int minutes, int halfSecondSteps, int furniSource, List itemIds) { + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + this.furniSource = furniSource; + this.itemIds = itemIds; + } + } +} 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 323abce6..7b27a858 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 @@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.items.FurnitureType; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.*; import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTimer; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.pets.PetManager; @@ -898,7 +899,11 @@ public class Room implements Comparable, ISerialize, Runnable { } for (InteractionGameTimer timer : this.getRoomSpecialTypes().getGameTimers().values()) { - timer.setRunning(false); + if (timer instanceof InteractionGameUpCounter) { + ((InteractionGameUpCounter) timer).resetOnRoomUnload(this); + } else { + timer.setRunning(false); + } } for (Game game : this.games) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java index ee85190c..7f37a1ae 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java @@ -294,7 +294,10 @@ public abstract class HabboItem implements Runnable, IEventTriggers { } } - if ((this.getBaseItem().getStateCount() > 1 && !(this instanceof InteractionDice)) || Arrays.asList(HabboItem.TOGGLING_INTERACTIONS).contains(this.getClass()) || (objects != null && objects.length == 1 && objects[0].equals("TOGGLE_OVERRIDE"))) { + boolean isTogglingInteraction = Arrays.stream(HabboItem.TOGGLING_INTERACTIONS) + .anyMatch(type -> type.isAssignableFrom(this.getClass())); + + if ((this.getBaseItem().getStateCount() > 1 && !(this instanceof InteractionDice)) || isTogglingInteraction || (objects != null && objects.length == 1 && objects[0].equals("TOGGLE_OVERRIDE"))) { WiredManager.triggerFurniStateChanged(room, client.getHabbo().getRoomUnit(), this); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java index e620b4e0..b2f435d0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java @@ -25,7 +25,8 @@ public enum WiredConditionType { NOT_ACTOR_WEARS_EFFECT(23), DATE_RANGE(24), ACTOR_HAS_HANDITEM(25), - MOVEMENT_VALIDATION(26); // i dont know what type it is but its needed + MOVEMENT_VALIDATION(26), // i dont know what type it is but its needed + COUNTER_TIME_MATCHES(27); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index 8ece3410..332cbe7f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -39,7 +39,9 @@ public enum WiredEffectType { USER_TO_FURNI(37), FURNI_TO_FURNI(38), SET_ALTITUDE(39), - RELATIVE_MOVE(40); + RELATIVE_MOVE(40), + CONTROL_CLOCK(41), + ADJUST_CLOCK(42); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java index 2672758b..b9d17bf6 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java @@ -21,6 +21,7 @@ public enum WiredTriggerType { CLICKS_TILE(19), CLICKS_USER(20), USER_PERFORMS_ACTION(21), + CLOCK_COUNTER(22), SAY_COMMAND(0), IDLES(11), UNIDLES(11), diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java index 369d7fba..fb194c2c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java @@ -63,6 +63,9 @@ public final class WiredEvent { /** Timer fires periodically/repeatedly */ TIMER_REPEAT(WiredTriggerType.PERIODICALLY), + + /** Up-counter reaches a configured elapsed time */ + CLOCK_COUNTER_REACHED(WiredTriggerType.CLOCK_COUNTER), /** Long timer repeat */ TIMER_REPEAT_LONG(WiredTriggerType.PERIODICALLY_LONG), diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index ea712884..cfe08f6b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -355,6 +355,15 @@ public final class WiredManager { return handleEvent(event); } + public static boolean triggerClockCounter(Room room, HabboItem counterItem) { + if (!isEnabled() || room == null || counterItem == null) { + return false; + } + + WiredEvent event = WiredEvents.clockCounter(room, counterItem); + return handleEvent(event); + } + /** * Trigger a long periodic timer. */ diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java index e93072b2..165f0c1d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java @@ -218,6 +218,12 @@ public final class WiredEvents { .build(); } + public static WiredEvent clockCounter(Room room, HabboItem counterItem) { + return WiredEvent.builder(WiredEvent.Type.CLOCK_COUNTER_REACHED, room) + .sourceItem(counterItem) + .build(); + } + /** * Create an event for a long periodic timer. * @param room the room diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java new file mode 100644 index 00000000..19e45f5c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java @@ -0,0 +1,46 @@ +package com.eu.habbo.threading.runnables.games; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.wired.core.WiredManager; + +public class GameUpCounter implements Runnable { + private final InteractionGameUpCounter timer; + + public GameUpCounter(InteractionGameUpCounter timer) { + this.timer = timer; + } + + @Override + public void run() { + if (timer.getRoomId() == 0) { + timer.setRunning(false); + timer.setThreadActive(false); + return; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(timer.getRoomId()); + + if (room == null || !timer.isRunning() || timer.isPaused()) { + timer.setThreadActive(false); + return; + } + + int tickDelayMs = (int) timer.getNextTickDelayMs(); + timer.advanceCounterInMs(tickDelayMs); + WiredManager.triggerClockCounter(room, timer); + + if (timer.getCurrentTimeInMs() < timer.getMaximumTimeInMs()) { + timer.setThreadActive(true); + Emulator.getThreading().run(this, timer.getNextTickDelayMs()); + } else { + timer.setThreadActive(false); + timer.setCurrentTimeInMs(timer.getMaximumTimeInMs()); + timer.endGame(room); + WiredManager.triggerGameEnds(room); + } + + room.updateItem(timer); + } +} From e234debfbd26550705ca087239a89172213ddf58 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Thu, 19 Mar 2026 14:27:26 +0100 Subject: [PATCH 7/9] feat(wired): add advanced match conditions and hotel timezone --- Database Updates/19032026_hotel_timezone.sql | 22 ++ .../src/main/java/com/eu/habbo/Emulator.java | 87 ++++- .../eu/habbo/core/ConfigurationManager.java | 1 + .../habbo/habbohotel/items/ItemManager.java | 10 + .../WiredConditionHabboHasHandItem.java | 8 + .../conditions/WiredConditionHasAltitude.java | 285 ++++++++++++++ .../conditions/WiredConditionMatchDate.java | 253 ++++++++++++ .../conditions/WiredConditionMatchTime.java | 237 +++++++++++ .../WiredConditionNotHabboHasHandItem.java | 42 ++ .../WiredConditionNotTriggererMatch.java | 31 ++ .../WiredConditionNotUserPerformsAction.java | 46 +++ .../WiredConditionTeamGameBase.java | 197 ++++++++++ .../conditions/WiredConditionTeamHasRank.java | 175 +++++++++ .../WiredConditionTeamHasScore.java | 162 ++++++++ .../WiredConditionTriggererMatch.java | 368 ++++++++++++++++++ .../WiredConditionUserPerformsAction.java | 346 ++++++++++++++++ .../habbohotel/wired/WiredConditionType.java | 12 +- .../habbohotel/wired/core/WiredManager.java | 7 + .../highscores/WiredHighscoreManager.java | 17 +- .../WiredHighscoreMidnightUpdater.java | 6 +- .../HotelViewRequestSecondsUntilEvent.java | 4 +- .../com/eu/habbo/util/HotelDateTimeUtil.java | 59 +++ 22 files changed, 2350 insertions(+), 25 deletions(-) create mode 100644 Database Updates/19032026_hotel_timezone.sql create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java create mode 100644 Emulator/src/main/java/com/eu/habbo/util/HotelDateTimeUtil.java diff --git a/Database Updates/19032026_hotel_timezone.sql b/Database Updates/19032026_hotel_timezone.sql new file mode 100644 index 00000000..8c08a899 --- /dev/null +++ b/Database Updates/19032026_hotel_timezone.sql @@ -0,0 +1,22 @@ +SET NAMES utf8mb4; + +-- Create the hotel timezone setting if it does not exist yet. +INSERT INTO `emulator_settings` (`key`, `value`) +SELECT 'hotel.timezone', 'Europe/Rome' +WHERE NOT EXISTS ( + SELECT 1 + FROM `emulator_settings` + WHERE `key` = 'hotel.timezone' +); + +-- Keep the default/example value aligned for existing installs too. +UPDATE `emulator_settings` +SET `value` = 'Europe/Rome' +WHERE `key` = 'hotel.timezone'; + +-- Helper query for a timezone selector. +-- If MySQL/MariaDB timezone tables are populated, this returns the available timezone ids. +SELECT `Name` AS `timezone_id` +FROM `mysql`.`time_zone_name` +WHERE `Name` IS NOT NULL +ORDER BY `Name`; diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 69f1deab..455cb540 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -20,6 +20,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; +import java.net.JarURLConnection; +import java.net.URL; import java.security.MessageDigest; import java.security.SecureRandom; import java.sql.Timestamp; @@ -52,6 +54,7 @@ public final class Emulator { "Still Rocking in 2026.\n"; public static String build = ""; + public static long buildTimestamp = -1L; public static boolean isReady = false; public static boolean isShuttingDown = false; public static boolean stopped = false; @@ -103,13 +106,6 @@ public final class Emulator { System.out.println(logo); - System.out.println(); - LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, "); - System.out.println(); - LOGGER.info("This project is for educational purposes only. This Emulator is an open-source fork of Arcturus created by TheGeneral."); - LOGGER.info("Version: {}", version); - LOGGER.info("Build: {}", build); - long startTime = System.nanoTime(); Emulator.runtime = Runtime.getRuntime(); @@ -141,6 +137,15 @@ public final class Emulator { Emulator.config.register("camera.price.points", "0"); Emulator.config.register("camera.price.points.type", "5"); Emulator.config.register("camera.render.delay", "5"); + Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId()); + String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId()); + System.out.println(); + LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, "); + System.out.println(); + LOGGER.info("This project is for educational purposes only. This Emulator is an open-source fork of Arcturus created by TheGeneral."); + LOGGER.info("Version: {}", version); + LOGGER.info("Build: {}", build); + LOGGER.info("Build Timestamp: {} [{}]", formatBuildTimestamp(buildTimestamp, hotelTimezoneId), hotelTimezoneId); Emulator.texts.register("camera.permission", "You don't have permission to use the camera!"); Emulator.texts.register("camera.wait", "Please wait %seconds% seconds before making another picture."); Emulator.texts.register("camera.error.creation", "Failed to create your picture. *sadpanda*"); @@ -216,12 +221,21 @@ public final class Emulator { private static void setBuild() { if (Emulator.class.getProtectionDomain().getCodeSource() == null) { build = "UNKNOWN"; + buildTimestamp = -1L; return; } StringBuilder sb = new StringBuilder(); try { - String filepath = new File(Emulator.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath(); + File buildFile = new File(Emulator.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + buildTimestamp = resolveBuildTimestamp(buildFile); + + if (!buildFile.isFile()) { + build = "DEV"; + return; + } + + String filepath = buildFile.getAbsolutePath(); MessageDigest md = MessageDigest.getInstance("MD5"); try (FileInputStream fis = new FileInputStream(filepath)) { byte[] dataBytes = new byte[1024]; @@ -234,14 +248,69 @@ public final class Emulator { } } catch (Exception e) { build = "UNKNOWN"; + buildTimestamp = -1L; return; } build = sb.toString(); } + private static long resolveBuildTimestamp(File buildFile) { + if (buildFile != null && buildFile.exists() && buildFile.isFile()) { + return buildFile.lastModified(); + } + + try { + URL classUrl = Emulator.class.getResource("Emulator.class"); + + if (classUrl != null) { + if ("file".equalsIgnoreCase(classUrl.getProtocol())) { + File classFile = new File(classUrl.toURI()); + + if (classFile.exists()) { + return classFile.lastModified(); + } + } + + if ("jar".equalsIgnoreCase(classUrl.getProtocol())) { + JarURLConnection connection = (JarURLConnection) classUrl.openConnection(); + File jarFile = new File(connection.getJarFileURL().toURI()); + + if (jarFile.exists()) { + return jarFile.lastModified(); + } + } + } + } catch (Exception ignored) { + } + + if (buildFile != null && buildFile.exists()) { + return buildFile.lastModified(); + } + + return -1L; + } + + private static String formatBuildTimestamp(long buildTimestamp, String timezoneId) { + if (buildTimestamp <= 0) { + return "UNKNOWN"; + } + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + try { + format.setTimeZone(TimeZone.getTimeZone(java.time.ZoneId.of(timezoneId))); + } catch (Exception ignored) { + format.setTimeZone(TimeZone.getDefault()); + } + + return format.format(new Timestamp(buildTimestamp)); + } + private static void dispose() { - Emulator.getThreading().setCanAdd(false); + if (Emulator.threading != null) { + Emulator.threading.setCanAdd(false); + } Emulator.isShuttingDown = true; Emulator.isReady = false; diff --git a/Emulator/src/main/java/com/eu/habbo/core/ConfigurationManager.java b/Emulator/src/main/java/com/eu/habbo/core/ConfigurationManager.java index 3a6ba211..2e4819a4 100644 --- a/Emulator/src/main/java/com/eu/habbo/core/ConfigurationManager.java +++ b/Emulator/src/main/java/com/eu/habbo/core/ConfigurationManager.java @@ -87,6 +87,7 @@ public class ConfigurationManager { // Runtime envMapping.put("runtime.threads", "RT_THREADS"); envMapping.put("logging.errors.runtime", "RT_LOG_ERRORS"); + envMapping.put("hotel.timezone", "HOTEL_TIMEZONE"); for (Map.Entry entry : envMapping.entrySet()) { String envValue = System.getenv(entry.getValue()); 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 4e645d4b..265f160c 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 @@ -306,9 +306,19 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_cnd_actor_in_team", WiredConditionTeamMember.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_trggrer_on_frn", WiredConditionTriggerOnFurni.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_handitem", WiredConditionHabboHasHandItem.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_not_has_handitem", WiredConditionNotHabboHasHandItem.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_date_rng_active", WiredConditionDateRangeActive.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_valid_moves", WiredConditionMovementValidation.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_match_time", WiredConditionMatchTime.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_match_date", WiredConditionMatchDate.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_user_performs_action", WiredConditionUserPerformsAction.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_not_user_performs_action", WiredConditionNotUserPerformsAction.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_has_altitude", WiredConditionHasAltitude.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_triggerer_match", WiredConditionTriggererMatch.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_not_triggerer_match", WiredConditionNotTriggererMatch.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_score", WiredConditionTeamHasScore.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_rank", WiredConditionTeamHasRank.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java index 1a064793..64aade52 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java @@ -115,6 +115,14 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } + protected int getHandItem() { + return this.handItem; + } + + protected int getUserSource() { + return this.userSource; + } + static class JsonData { int handItemId; int userSource; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java new file mode 100644 index 00000000..155da036 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java @@ -0,0 +1,285 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +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 gnu.trove.set.hash.THashSet; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredConditionHasAltitude extends InteractionWiredCondition { + private static final int COMPARISON_LESS = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER = 2; + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + + public static final WiredConditionType type = WiredConditionType.HAS_ALTITUDE; + + private final THashSet items; + private int comparison = COMPARISON_EQUAL; + private double altitude = 0.0D; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionHasAltitude(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredConditionHasAltitude(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean evaluate(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return false; + } + + this.refresh(room); + + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (targets.isEmpty()) { + return false; + } + + if (this.quantifier == QUANTIFIER_ANY) { + return targets.stream().anyMatch(this::matchesAltitude); + } + + return targets.stream().allMatch(this::matchesAltitude); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.formatAltitude(this.altitude), + this.furniSource, + this.quantifier, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.altitude = 0.0D; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.altitude = this.parseAltitudeOrDefault(data.altitude); + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + + if (data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.altitude = 0.0D; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(this.formatAltitude(this.altitude)); + message.appendInt(3); + message.appendInt(this.comparison); + message.appendInt(this.furniSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL; + this.furniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; + this.altitude = this.parseAltitudeOrDefault(settings.getStringParam()); + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + this.items.clear(); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.items.add(item); + } + } + } + + return true; + } + + private boolean matchesAltitude(HabboItem item) { + if (item == null) { + return false; + } + + double normalizedAltitude = this.normalizeAltitude(item.getZ()); + + switch (this.comparison) { + case COMPARISON_LESS: + return normalizedAltitude < this.altitude; + case COMPARISON_GREATER: + return normalizedAltitude > this.altitude; + default: + return BigDecimal.valueOf(normalizedAltitude).compareTo(BigDecimal.valueOf(this.altitude)) == 0; + } + } + + private void refresh(Room room) { + THashSet remove = new THashSet<>(); + + for (HabboItem item : this.items) { + if (room.getHabboItem(item.getId()) == null) { + remove.add(item); + } + } + + for (HabboItem item : remove) { + this.items.remove(item); + } + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + return value; + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + private int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + private double parseAltitudeOrDefault(String value) { + if (value == null || value.trim().isEmpty()) { + return 0.0D; + } + + try { + return this.normalizeAltitude(new BigDecimal(value.trim()).doubleValue()); + } catch (NumberFormatException exception) { + return 0.0D; + } + } + + private String formatAltitude(double value) { + BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros(); + return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString(); + } + + static class JsonData { + int comparison; + String altitude; + int furniSource; + int quantifier; + List itemIds; + + public JsonData(int comparison, String altitude, int furniSource, int quantifier, List itemIds) { + this.comparison = comparison; + this.altitude = altitude; + this.furniSource = furniSource; + this.quantifier = quantifier; + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java new file mode 100644 index 00000000..3574480b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java @@ -0,0 +1,253 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.util.HotelDateTimeUtil; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; + +public class WiredConditionMatchDate extends InteractionWiredCondition { + private static final int MODE_SKIP = 0; + private static final int MODE_EXACT = 1; + private static final int MODE_RANGE = 2; + private static final int ALL_WEEKDAYS_MASK = createMask(1, 7); + private static final int ALL_MONTHS_MASK = createMask(1, 12); + + public static final WiredConditionType type = WiredConditionType.MATCH_DATE; + + private int weekdayMask = ALL_WEEKDAYS_MASK; + private int dayMode = MODE_SKIP; + private int dayFrom = 1; + private int dayTo = 31; + private int monthMask = ALL_MONTHS_MASK; + private int yearMode = MODE_SKIP; + private int yearFrom = HotelDateTimeUtil.localDateNow().getYear(); + private int yearTo = HotelDateTimeUtil.localDateNow().getYear(); + + public WiredConditionMatchDate(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionMatchDate(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType 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(8); + message.appendInt(this.weekdayMask); + message.appendInt(this.dayMode); + message.appendInt(this.dayFrom); + message.appendInt(this.dayTo); + message.appendInt(this.monthMask); + message.appendInt(this.yearMode); + message.appendInt(this.yearFrom); + message.appendInt(this.yearTo); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.weekdayMask = (params.length > 0) ? this.normalizeWeekdayMask(params[0]) : ALL_WEEKDAYS_MASK; + this.dayMode = (params.length > 1) ? this.normalizeMode(params[1]) : MODE_SKIP; + this.dayFrom = (params.length > 2) ? this.normalizeDay(params[2]) : 1; + this.dayTo = (params.length > 3) ? this.normalizeDay(params[3]) : this.dayFrom; + this.monthMask = (params.length > 4) ? this.normalizeMonthMask(params[4]) : ALL_MONTHS_MASK; + this.yearMode = (params.length > 5) ? this.normalizeMode(params[5]) : MODE_SKIP; + this.yearFrom = (params.length > 6) ? this.normalizeYear(params[6]) : HotelDateTimeUtil.localDateNow().getYear(); + this.yearTo = (params.length > 7) ? this.normalizeYear(params[7]) : this.yearFrom; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + LocalDate now = HotelDateTimeUtil.localDateNow(); + + return this.matchesMask(now.getDayOfWeek().getValue(), this.weekdayMask) + && this.matchesMask(now.getMonthValue(), this.monthMask) + && this.matchesDatePart(now.getDayOfMonth(), this.dayMode, this.dayFrom, this.dayTo) + && this.matchesDatePart(now.getYear(), this.yearMode, this.yearFrom, this.yearTo); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.weekdayMask, + this.dayMode, + this.dayFrom, + this.dayTo, + this.monthMask, + this.yearMode, + this.yearFrom, + this.yearTo + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.reset(); + + 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) { + return; + } + + this.weekdayMask = this.normalizeWeekdayMask(data.weekdayMask); + this.dayMode = this.normalizeMode(data.dayMode); + this.dayFrom = this.normalizeDay(data.dayFrom); + this.dayTo = this.normalizeDay(data.dayTo); + this.monthMask = this.normalizeMonthMask(data.monthMask); + this.yearMode = this.normalizeMode(data.yearMode); + this.yearFrom = this.normalizeYear(data.yearFrom); + this.yearTo = this.normalizeYear(data.yearTo); + return; + } + + String[] data = wiredData.split("\t"); + if (data.length != 8) { + return; + } + + try { + this.weekdayMask = this.normalizeWeekdayMask(Integer.parseInt(data[0])); + this.dayMode = this.normalizeMode(Integer.parseInt(data[1])); + this.dayFrom = this.normalizeDay(Integer.parseInt(data[2])); + this.dayTo = this.normalizeDay(Integer.parseInt(data[3])); + this.monthMask = this.normalizeMonthMask(Integer.parseInt(data[4])); + this.yearMode = this.normalizeMode(Integer.parseInt(data[5])); + this.yearFrom = this.normalizeYear(Integer.parseInt(data[6])); + this.yearTo = this.normalizeYear(Integer.parseInt(data[7])); + } catch (NumberFormatException ignored) { + this.reset(); + } + } + + @Override + public void onPickUp() { + this.reset(); + } + + private void reset() { + int currentYear = HotelDateTimeUtil.localDateNow().getYear(); + + this.weekdayMask = ALL_WEEKDAYS_MASK; + this.dayMode = MODE_SKIP; + this.dayFrom = 1; + this.dayTo = 31; + this.monthMask = ALL_MONTHS_MASK; + this.yearMode = MODE_SKIP; + this.yearFrom = currentYear; + this.yearTo = currentYear; + } + + private boolean matchesMask(int value, int mask) { + return (mask & (1 << value)) != 0; + } + + private boolean matchesDatePart(int currentValue, int mode, int fromValue, int toValue) { + switch (mode) { + case MODE_EXACT: + return currentValue == fromValue; + case MODE_RANGE: + return currentValue >= Math.min(fromValue, toValue) && currentValue <= Math.max(fromValue, toValue); + default: + return true; + } + } + + private int normalizeMode(int value) { + if (value < MODE_SKIP || value > MODE_RANGE) { + return MODE_SKIP; + } + + return value; + } + + private int normalizeDay(int value) { + return Math.max(1, Math.min(31, value)); + } + + private int normalizeYear(int value) { + return Math.max(1, Math.min(9999, value)); + } + + private int normalizeWeekdayMask(int value) { + int normalized = value & ALL_WEEKDAYS_MASK; + return (normalized == 0) ? ALL_WEEKDAYS_MASK : normalized; + } + + private int normalizeMonthMask(int value) { + int normalized = value & ALL_MONTHS_MASK; + return (normalized == 0) ? ALL_MONTHS_MASK : normalized; + } + + private static int createMask(int startValue, int endValue) { + int mask = 0; + + for (int value = startValue; value <= endValue; value++) { + mask |= (1 << value); + } + + return mask; + } + + static class JsonData { + int weekdayMask; + int dayMode; + int dayFrom; + int dayTo; + int monthMask; + int yearMode; + int yearFrom; + int yearTo; + + public JsonData(int weekdayMask, int dayMode, int dayFrom, int dayTo, int monthMask, int yearMode, int yearFrom, int yearTo) { + this.weekdayMask = weekdayMask; + this.dayMode = dayMode; + this.dayFrom = dayFrom; + this.dayTo = dayTo; + this.monthMask = monthMask; + this.yearMode = yearMode; + this.yearFrom = yearFrom; + this.yearTo = yearTo; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java new file mode 100644 index 00000000..982b56ec --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java @@ -0,0 +1,237 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.util.HotelDateTimeUtil; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalTime; + +public class WiredConditionMatchTime extends InteractionWiredCondition { + private static final int MODE_SKIP = 0; + private static final int MODE_EXACT = 1; + private static final int MODE_RANGE = 2; + + public static final WiredConditionType type = WiredConditionType.MATCH_TIME; + + private int hourMode = MODE_SKIP; + private int hourFrom = 0; + private int hourTo = 0; + private int minuteMode = MODE_SKIP; + private int minuteFrom = 0; + private int minuteTo = 0; + private int secondMode = MODE_SKIP; + private int secondFrom = 0; + private int secondTo = 0; + + public WiredConditionMatchTime(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionMatchTime(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType 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(9); + message.appendInt(this.hourMode); + message.appendInt(this.hourFrom); + message.appendInt(this.hourTo); + message.appendInt(this.minuteMode); + message.appendInt(this.minuteFrom); + message.appendInt(this.minuteTo); + message.appendInt(this.secondMode); + message.appendInt(this.secondFrom); + message.appendInt(this.secondTo); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.hourMode = (params.length > 0) ? this.normalizeMode(params[0]) : MODE_SKIP; + this.hourFrom = (params.length > 1) ? this.normalizeHour(params[1]) : 0; + this.hourTo = (params.length > 2) ? this.normalizeHour(params[2]) : this.hourFrom; + this.minuteMode = (params.length > 3) ? this.normalizeMode(params[3]) : MODE_SKIP; + this.minuteFrom = (params.length > 4) ? this.normalizeMinuteOrSecond(params[4]) : 0; + this.minuteTo = (params.length > 5) ? this.normalizeMinuteOrSecond(params[5]) : this.minuteFrom; + this.secondMode = (params.length > 6) ? this.normalizeMode(params[6]) : MODE_SKIP; + this.secondFrom = (params.length > 7) ? this.normalizeMinuteOrSecond(params[7]) : 0; + this.secondTo = (params.length > 8) ? this.normalizeMinuteOrSecond(params[8]) : this.secondFrom; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + LocalTime now = HotelDateTimeUtil.localTimeNow(); + + return this.matchesTimePart(now.getHour(), this.hourMode, this.hourFrom, this.hourTo) + && this.matchesTimePart(now.getMinute(), this.minuteMode, this.minuteFrom, this.minuteTo) + && this.matchesTimePart(now.getSecond(), this.secondMode, this.secondFrom, this.secondTo); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.hourMode, + this.hourFrom, + this.hourTo, + this.minuteMode, + this.minuteFrom, + this.minuteTo, + this.secondMode, + this.secondFrom, + this.secondTo + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.reset(); + + 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) { + return; + } + + this.hourMode = this.normalizeMode(data.hourMode); + this.hourFrom = this.normalizeHour(data.hourFrom); + this.hourTo = this.normalizeHour(data.hourTo); + this.minuteMode = this.normalizeMode(data.minuteMode); + this.minuteFrom = this.normalizeMinuteOrSecond(data.minuteFrom); + this.minuteTo = this.normalizeMinuteOrSecond(data.minuteTo); + this.secondMode = this.normalizeMode(data.secondMode); + this.secondFrom = this.normalizeMinuteOrSecond(data.secondFrom); + this.secondTo = this.normalizeMinuteOrSecond(data.secondTo); + return; + } + + String[] data = wiredData.split("\t"); + if (data.length != 9) { + return; + } + + try { + this.hourMode = this.normalizeMode(Integer.parseInt(data[0])); + this.hourFrom = this.normalizeHour(Integer.parseInt(data[1])); + this.hourTo = this.normalizeHour(Integer.parseInt(data[2])); + this.minuteMode = this.normalizeMode(Integer.parseInt(data[3])); + this.minuteFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[4])); + this.minuteTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[5])); + this.secondMode = this.normalizeMode(Integer.parseInt(data[6])); + this.secondFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[7])); + this.secondTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[8])); + } catch (NumberFormatException ignored) { + this.reset(); + } + } + + @Override + public void onPickUp() { + this.reset(); + } + + private void reset() { + this.hourMode = MODE_SKIP; + this.hourFrom = 0; + this.hourTo = 0; + this.minuteMode = MODE_SKIP; + this.minuteFrom = 0; + this.minuteTo = 0; + this.secondMode = MODE_SKIP; + this.secondFrom = 0; + this.secondTo = 0; + } + + private boolean matchesTimePart(int currentValue, int mode, int fromValue, int toValue) { + switch (mode) { + case MODE_EXACT: + return currentValue == fromValue; + case MODE_RANGE: + if (fromValue <= toValue) { + return currentValue >= fromValue && currentValue <= toValue; + } + + return currentValue >= fromValue || currentValue <= toValue; + default: + return true; + } + } + + private int normalizeMode(int value) { + if (value < MODE_SKIP || value > MODE_RANGE) { + return MODE_SKIP; + } + + return value; + } + + private int normalizeHour(int value) { + return Math.max(0, Math.min(23, value)); + } + + private int normalizeMinuteOrSecond(int value) { + return Math.max(0, Math.min(59, value)); + } + + static class JsonData { + int hourMode; + int hourFrom; + int hourTo; + int minuteMode; + int minuteFrom; + int minuteTo; + int secondMode; + int secondFrom; + int secondTo; + + public JsonData(int hourMode, int hourFrom, int hourTo, int minuteMode, int minuteFrom, int minuteTo, int secondMode, int secondFrom, int secondTo) { + this.hourMode = hourMode; + this.hourFrom = hourFrom; + this.hourTo = hourTo; + this.minuteMode = minuteMode; + this.minuteFrom = minuteFrom; + this.minuteTo = minuteTo; + this.secondMode = secondMode; + this.secondFrom = secondFrom; + this.secondTo = secondTo; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java new file mode 100644 index 00000000..24421924 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java @@ -0,0 +1,42 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionNotHabboHasHandItem extends WiredConditionHabboHasHandItem { + public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_HAS_HANDITEM; + + public WiredConditionNotHabboHasHandItem(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionNotHabboHasHandItem(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource()); + if (targets.isEmpty()) return false; + + for (RoomUnit roomUnit : targets) { + if (roomUnit == null || roomUnit.getHandItem() == this.getHandItem()) { + return false; + } + } + + return true; + } + + @Override + public WiredConditionType getType() { + return type; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java new file mode 100644 index 00000000..3e945329 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java @@ -0,0 +1,31 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredConditionNotTriggererMatch extends WiredConditionTriggererMatch { + public static final WiredConditionType type = WiredConditionType.NOT_TRIGGERER_MATCH; + + public WiredConditionNotTriggererMatch(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionNotTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + MatchResult result = this.evaluateMatch(ctx); + return result.valid && !result.matched; + } + + @Override + public WiredConditionType getType() { + return type; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java new file mode 100644 index 00000000..d4ff4f38 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java @@ -0,0 +1,46 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionNotUserPerformsAction extends WiredConditionUserPerformsAction { + private static final int QUANTIFIER_ANY_NOT_MATCH = 0; + private static final int QUANTIFIER_NONE_MATCH = 1; + + public static final WiredConditionType type = WiredConditionType.NOT_USER_PERFORMS_ACTION; + + public WiredConditionNotUserPerformsAction(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionNotUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource()); + if (targets.isEmpty()) { + return false; + } + + if (this.getQuantifier() == QUANTIFIER_NONE_MATCH) { + return targets.stream().noneMatch(roomUnit -> this.matchesAction(ctx, roomUnit)); + } + + return targets.stream().anyMatch(roomUnit -> !this.matchesAction(ctx, roomUnit)); + } + + @Override + public WiredConditionType getType() { + return type; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java new file mode 100644 index 00000000..8e05c562 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java @@ -0,0 +1,197 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.games.Game; +import com.eu.habbo.habbohotel.games.GameState; +import com.eu.habbo.habbohotel.games.GameTeam; +import com.eu.habbo.habbohotel.games.GameTeamColors; +import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame; +import com.eu.habbo.habbohotel.games.freeze.FreezeGame; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +abstract class WiredConditionTeamGameBase extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + protected static final int COMPARISON_LOWER = 0; + protected static final int COMPARISON_EQUAL = 1; + protected static final int COMPARISON_HIGHER = 2; + protected static final int TEAM_TRIGGERER = 0; + + private static final GameTeamColors[] SUPPORTED_TEAM_COLORS = new GameTeamColors[] { + GameTeamColors.RED, + GameTeamColors.GREEN, + GameTeamColors.BLUE, + GameTeamColors.YELLOW + }; + + protected WiredConditionTeamGameBase(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + protected WiredConditionTeamGameBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + protected List resolveUsers(WiredContext ctx, int userSource) { + Map deduplicated = new LinkedHashMap<>(); + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, userSource)) { + if (roomUnit != null) { + deduplicated.putIfAbsent(roomUnit.getId(), roomUnit); + } + } + + return new ArrayList<>(deduplicated.values()); + } + + protected boolean matchesQuantifier(List users, int quantifier, Predicate predicate) { + if (users.isEmpty()) { + return false; + } + + if (quantifier == QUANTIFIER_ANY) { + return users.stream().anyMatch(predicate); + } + + return users.stream().allMatch(predicate); + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + protected int normalizeComparison(int value) { + switch (value) { + case COMPARISON_LOWER: + case COMPARISON_HIGHER: + return value; + default: + return COMPARISON_EQUAL; + } + } + + protected int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + protected int normalizePlacement(int value) { + if (value < 1 || value > 4) { + return 1; + } + + return value; + } + + protected int normalizeScore(int value) { + return Math.max(0, value); + } + + protected int normalizeExplicitTeamType(int value) { + GameTeamColors color = GameTeamColors.fromType(value); + return (color.type >= GameTeamColors.RED.type && color.type <= GameTeamColors.YELLOW.type) + ? color.type + : GameTeamColors.RED.type; + } + + protected int normalizeRankTeamType(int value) { + if (value == TEAM_TRIGGERER) { + return TEAM_TRIGGERER; + } + + return this.normalizeExplicitTeamType(value); + } + + protected GameTeamColors resolveConfiguredTeamColor(int value) { + return GameTeamColors.fromType(this.normalizeExplicitTeamType(value)); + } + + protected boolean compareValue(int actual, int expected, int comparison) { + switch (comparison) { + case COMPARISON_LOWER: + return actual < expected; + case COMPARISON_HIGHER: + return actual > expected; + default: + return actual == expected; + } + } + + protected UserGameContext resolveUserGameContext(Room room, RoomUnit roomUnit) { + if (room == null || roomUnit == null) { + return null; + } + + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getCurrentGame() == null) { + return null; + } + + Game game = room.getGame(habbo.getHabboInfo().getCurrentGame()); + if (!this.isSupportedGame(game)) { + return null; + } + + GameTeam team = game.getTeamForHabbo(habbo); + if (team == null) { + return null; + } + + return new UserGameContext(habbo, game, team); + } + + protected int getTeamRank(Game game, GameTeam team) { + if (game == null || team == null) { + return Integer.MAX_VALUE; + } + + int rank = 1; + int targetScore = team.getTotalScore(); + + for (GameTeamColors teamColor : SUPPORTED_TEAM_COLORS) { + GameTeam otherTeam = game.getTeam(teamColor); + if (otherTeam != null && otherTeam != team && otherTeam.getTotalScore() > targetScore) { + rank++; + } + } + + return rank; + } + + private boolean isSupportedGame(Game game) { + return game != null + && game.getState() != GameState.IDLE + && (game instanceof FreezeGame || game instanceof BattleBanzaiGame); + } + + protected static class UserGameContext { + protected final Habbo habbo; + protected final Game game; + protected final GameTeam team; + + protected UserGameContext(Habbo habbo, Game game, GameTeam team) { + this.habbo = habbo; + this.game = game; + this.team = team; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java new file mode 100644 index 00000000..58272197 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java @@ -0,0 +1,175 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.games.GameTeam; +import com.eu.habbo.habbohotel.games.GameTeamColors; +import com.eu.habbo.habbohotel.items.Item; +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.WiredConditionType; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionTeamHasRank extends WiredConditionTeamGameBase { + public static final WiredConditionType type = WiredConditionType.TEAM_HAS_RANK; + + private int teamType = GameTeamColors.RED.type; + private int placement = 1; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionTeamHasRank(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionTeamHasRank(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + Room room = ctx.room(); + List users = this.resolveUsers(ctx, this.userSource); + + return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(ctx, 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.teamType, + this.placement, + this.userSource, + this.quantifier + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.teamType = this.normalizeRankTeamType(data.teamType); + this.placement = this.normalizePlacement(data.placement); + this.userSource = this.normalizeUserSource(data.userSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + } + + @Override + public void onPickUp() { + this.resetSettings(); + } + + @Override + public WiredConditionType 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(4); + message.appendInt(this.teamType); + message.appendInt(this.placement); + message.appendInt(this.userSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.resetSettings(); + + if (params.length > 0) this.teamType = this.normalizeRankTeamType(params[0]); + if (params.length > 1) this.placement = this.normalizePlacement(params[1]); + if (params.length > 2) this.userSource = this.normalizeUserSource(params[2]); + if (params.length > 3) this.quantifier = this.normalizeQuantifier(params[3]); + + return true; + } + + private boolean matchesUser(WiredContext ctx, Room room, RoomUnit roomUnit) { + UserGameContext context = this.resolveUserGameContext(room, roomUnit); + if (context == null) { + return false; + } + + GameTeamColors requiredTeam = this.resolveRequiredTeamColor(ctx, room, context.game); + if (requiredTeam == GameTeamColors.NONE || context.team.teamColor != requiredTeam) { + return false; + } + + GameTeam team = context.game.getTeam(requiredTeam); + if (team == null) { + return false; + } + + return this.getTeamRank(context.game, team) == this.placement; + } + + private GameTeamColors resolveRequiredTeamColor(WiredContext ctx, Room room, com.eu.habbo.habbohotel.games.Game game) { + if (this.teamType == TEAM_TRIGGERER) { + RoomUnit actor = ctx.actor().orElse(null); + UserGameContext triggererContext = this.resolveUserGameContext(room, actor); + + if (triggererContext == null || triggererContext.game != game) { + return GameTeamColors.NONE; + } + + return triggererContext.team.teamColor; + } + + return this.resolveConfiguredTeamColor(this.teamType); + } + + private void resetSettings() { + this.teamType = GameTeamColors.RED.type; + this.placement = 1; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + static class JsonData { + int teamType; + int placement; + int userSource; + int quantifier; + + public JsonData(int teamType, int placement, int userSource, int quantifier) { + this.teamType = teamType; + this.placement = placement; + this.userSource = userSource; + this.quantifier = quantifier; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java new file mode 100644 index 00000000..e85741a6 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java @@ -0,0 +1,162 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.games.GameTeamColors; +import com.eu.habbo.habbohotel.items.Item; +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.WiredConditionType; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionTeamHasScore extends WiredConditionTeamGameBase { + public static final WiredConditionType type = WiredConditionType.TEAM_HAS_SCORE; + + private int teamType = GameTeamColors.RED.type; + private int comparison = COMPARISON_EQUAL; + private int score = 0; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionTeamHasScore(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionTeamHasScore(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + Room room = ctx.room(); + List users = this.resolveUsers(ctx, this.userSource); + + return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(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.teamType, + this.comparison, + this.score, + this.userSource, + this.quantifier + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.teamType = this.normalizeExplicitTeamType(data.teamType); + this.comparison = this.normalizeComparison(data.comparison); + this.score = this.normalizeScore(data.score); + this.userSource = this.normalizeUserSource(data.userSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + } + + @Override + public void onPickUp() { + this.resetSettings(); + } + + @Override + public WiredConditionType 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(5); + message.appendInt(this.teamType); + message.appendInt(this.comparison); + message.appendInt(this.score); + message.appendInt(this.userSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.resetSettings(); + + if (params.length > 0) this.teamType = this.normalizeExplicitTeamType(params[0]); + if (params.length > 1) this.comparison = this.normalizeComparison(params[1]); + if (params.length > 2) this.score = this.normalizeScore(params[2]); + if (params.length > 3) this.userSource = this.normalizeUserSource(params[3]); + if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]); + + return true; + } + + private boolean matchesUser(Room room, RoomUnit roomUnit) { + UserGameContext context = this.resolveUserGameContext(room, roomUnit); + if (context == null) { + return false; + } + + GameTeamColors requiredTeam = this.resolveConfiguredTeamColor(this.teamType); + if (context.team.teamColor != requiredTeam) { + return false; + } + + return this.compareValue(context.team.getTotalScore(), this.score, this.comparison); + } + + private void resetSettings() { + this.teamType = GameTeamColors.RED.type; + this.comparison = COMPARISON_EQUAL; + this.score = 0; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + static class JsonData { + int teamType; + int comparison; + int score; + int userSource; + int quantifier; + + public JsonData(int teamType, int comparison, int score, int userSource, int quantifier) { + this.teamType = teamType; + this.comparison = comparison; + this.score = score; + this.userSource = userSource; + this.quantifier = quantifier; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java new file mode 100644 index 00000000..f9997436 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java @@ -0,0 +1,368 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.bots.Bot; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.pets.Pet; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredConditionTriggererMatch extends InteractionWiredCondition { + protected static final int ENTITY_HABBO = 1; + protected static final int ENTITY_PET = 2; + protected static final int ENTITY_BOT = 4; + protected static final int AVATAR_MODE_ANY = 0; + protected static final int AVATAR_MODE_CERTAIN = 1; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + protected static final int SOURCE_SPECIFIED_USERNAME = 101; + + public static final WiredConditionType type = WiredConditionType.TRIGGERER_MATCH; + + private int entityType = ENTITY_HABBO; + private int avatarMode = AVATAR_MODE_ANY; + private int matchUserSource = WiredSourceUtil.SOURCE_TRIGGER; + private int compareUserSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + private String username = ""; + + public WiredConditionTriggererMatch(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + MatchResult result = this.evaluateMatch(ctx); + return result.valid && result.matched; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.entityType, + this.avatarMode, + this.matchUserSource, + this.compareUserSource, + this.quantifier, + this.username + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.entityType = this.normalizeEntityType(data.entityType); + this.avatarMode = this.normalizeAvatarMode(data.avatarMode); + this.matchUserSource = this.normalizePrimaryUserSource(data.matchUserSource); + this.compareUserSource = this.normalizeCompareUserSource(data.compareUserSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + this.username = this.normalizeUsername(data.username); + } + + @Override + public void onPickUp() { + this.resetSettings(); + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(true); + message.appendInt(5); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(this.username); + message.appendInt(5); + message.appendInt(this.entityType); + message.appendInt(this.avatarMode); + message.appendInt(this.matchUserSource); + message.appendInt(this.compareUserSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.resetSettings(); + + if (params.length > 0) this.entityType = this.normalizeEntityType(params[0]); + if (params.length > 1) this.avatarMode = this.normalizeAvatarMode(params[1]); + if (params.length > 2) this.matchUserSource = this.normalizePrimaryUserSource(params[2]); + if (params.length > 3) this.compareUserSource = this.normalizeCompareUserSource(params[3]); + if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]); + + this.username = this.normalizeUsername(settings.getStringParam()); + + return true; + } + + protected MatchResult evaluateMatch(WiredContext ctx) { + List matchUsers = this.resolvePrimaryUsers(ctx); + if (matchUsers.isEmpty()) { + return MatchResult.invalid(); + } + + List compareUsers = this.resolveCompareUsers(ctx); + if (compareUsers.isEmpty()) { + return MatchResult.valid(false); + } + + Set compareUserIds = compareUsers.stream() + .filter(this::matchesEntityType) + .map(RoomUnit::getId) + .collect(Collectors.toSet()); + + if (compareUserIds.isEmpty()) { + return MatchResult.valid(false); + } + + boolean matched; + if (this.quantifier == QUANTIFIER_ANY) { + matched = matchUsers.stream().anyMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds)); + } else { + matched = matchUsers.stream().allMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds)); + } + + return MatchResult.valid(matched); + } + + protected int getQuantifier() { + return this.quantifier; + } + + private void resetSettings() { + this.entityType = ENTITY_HABBO; + this.avatarMode = AVATAR_MODE_ANY; + this.matchUserSource = WiredSourceUtil.SOURCE_TRIGGER; + this.compareUserSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + this.username = ""; + } + + private List resolvePrimaryUsers(WiredContext ctx) { + return this.deduplicate(WiredSourceUtil.resolveUsers(ctx, this.matchUserSource)); + } + + private List resolveCompareUsers(WiredContext ctx) { + List resolved; + + if (this.compareUserSource == SOURCE_SPECIFIED_USERNAME) { + resolved = this.resolveUsersByName(ctx.room(), this.username); + } else { + resolved = WiredSourceUtil.resolveUsers(ctx, this.compareUserSource); + } + + if (this.avatarMode == AVATAR_MODE_CERTAIN) { + String normalizedName = this.normalizeUsername(this.username); + if (normalizedName.isEmpty()) { + return new ArrayList<>(); + } + + resolved = resolved.stream() + .filter(roomUnit -> normalizedName.equalsIgnoreCase(this.getRoomUnitName(ctx.room(), roomUnit))) + .collect(Collectors.toList()); + } + + return this.deduplicate(resolved); + } + + private List resolveUsersByName(Room room, String username) { + List result = new ArrayList<>(); + String normalizedName = this.normalizeUsername(username); + if (room == null || normalizedName.isEmpty()) { + return result; + } + + Habbo habbo = room.getHabbo(normalizedName); + if (habbo != null && habbo.getRoomUnit() != null) { + result.add(habbo.getRoomUnit()); + } + + for (Bot bot : room.getBots(normalizedName)) { + if (bot != null && bot.getRoomUnit() != null) { + result.add(bot.getRoomUnit()); + } + } + + for (Pet pet : room.getUnitManager().getPets()) { + if (pet != null && pet.getRoomUnit() != null && normalizedName.equalsIgnoreCase(pet.getName())) { + result.add(pet.getRoomUnit()); + } + } + + return result; + } + + private List deduplicate(List users) { + Map deduplicated = new LinkedHashMap<>(); + + for (RoomUnit user : users) { + if (user != null) { + deduplicated.putIfAbsent(user.getId(), user); + } + } + + return new ArrayList<>(deduplicated.values()); + } + + private boolean matchesCandidate(RoomUnit roomUnit, Set compareUserIds) { + return roomUnit != null && this.matchesEntityType(roomUnit) && compareUserIds.contains(roomUnit.getId()); + } + + private boolean matchesEntityType(RoomUnit roomUnit) { + return roomUnit != null && roomUnit.getRoomUnitType().getTypeId() == this.entityType; + } + + private String getRoomUnitName(Room room, RoomUnit roomUnit) { + if (room == null || roomUnit == null) { + return ""; + } + + if (roomUnit.getRoomUnitType() == RoomUnitType.USER) { + Habbo habbo = room.getHabbo(roomUnit); + return (habbo != null && habbo.getHabboInfo() != null) ? habbo.getHabboInfo().getUsername() : ""; + } + + if (roomUnit.getRoomUnitType() == RoomUnitType.BOT) { + Bot bot = room.getBot(roomUnit); + return (bot != null) ? bot.getName() : ""; + } + + if (roomUnit.getRoomUnitType() == RoomUnitType.PET) { + Pet pet = room.getPet(roomUnit); + return (pet != null) ? pet.getName() : ""; + } + + return ""; + } + + private int normalizeEntityType(int value) { + switch (value) { + case ENTITY_HABBO: + case ENTITY_PET: + case ENTITY_BOT: + return value; + default: + return ENTITY_HABBO; + } + } + + private int normalizeAvatarMode(int value) { + return (value == AVATAR_MODE_CERTAIN) ? AVATAR_MODE_CERTAIN : AVATAR_MODE_ANY; + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + private int normalizePrimaryUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeCompareUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + case SOURCE_SPECIFIED_USERNAME: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private String normalizeUsername(String value) { + return (value == null) ? "" : value.trim(); + } + + protected static class MatchResult { + protected final boolean valid; + protected final boolean matched; + + private MatchResult(boolean valid, boolean matched) { + this.valid = valid; + this.matched = matched; + } + + private static MatchResult invalid() { + return new MatchResult(false, false); + } + + private static MatchResult valid(boolean matched) { + return new MatchResult(true, matched); + } + } + + static class JsonData { + int entityType; + int avatarMode; + int matchUserSource; + int compareUserSource; + int quantifier; + String username; + + public JsonData(int entityType, int avatarMode, int matchUserSource, int compareUserSource, int quantifier, String username) { + this.entityType = entityType; + this.avatarMode = avatarMode; + this.matchUserSource = matchUserSource; + this.compareUserSource = compareUserSource; + this.quantifier = quantifier; + this.username = username; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java new file mode 100644 index 00000000..a24ce86e --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java @@ -0,0 +1,346 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionUserPerformsAction extends InteractionWiredCondition { + private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id"; + private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter"; + private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp"; + private static final long TRANSIENT_ACTION_WINDOW_MS = 5_000L; + protected static final int DEFAULT_ACTION = WiredUserActionType.WAVE; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + + public static final WiredConditionType type = WiredConditionType.USER_PERFORMS_ACTION; + + private int selectedAction = DEFAULT_ACTION; + private boolean signFilterEnabled = false; + private int signId = 0; + private boolean danceFilterEnabled = false; + private int danceId = 1; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionUserPerformsAction(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean evaluate(WiredContext ctx) { + List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); + if (targets.isEmpty()) { + return false; + } + + if (this.quantifier == QUANTIFIER_ANY) { + return targets.stream().anyMatch(roomUnit -> this.matchesAction(ctx, roomUnit)); + } + + return targets.stream().allMatch(roomUnit -> this.matchesAction(ctx, 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.selectedAction, + this.signFilterEnabled, + this.signId, + this.danceFilterEnabled, + this.danceId, + this.userSource, + this.quantifier + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + + String wiredData = set.getString("wired_data"); + + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data == null) { + return; + } + + this.selectedAction = normalizeAction(data.selectedAction); + this.signFilterEnabled = data.signFilterEnabled; + this.signId = normalizeSignId(data.signId); + this.danceFilterEnabled = data.danceFilterEnabled; + this.danceId = normalizeDanceId(data.danceId); + this.userSource = this.normalizeUserSource(data.userSource); + this.quantifier = normalizeQuantifier(data.quantifier); + } + + @Override + public void onPickUp() { + this.resetSettings(); + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(true); + message.appendInt(5); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(7); + message.appendInt(this.selectedAction); + message.appendInt(this.signFilterEnabled ? 1 : 0); + message.appendInt(this.signId); + message.appendInt(this.danceFilterEnabled ? 1 : 0); + message.appendInt(this.danceId); + message.appendInt(this.userSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] intParams = settings.getIntParams(); + + this.resetSettings(); + + if (intParams.length > 0) this.selectedAction = normalizeAction(intParams[0]); + if (intParams.length > 1) this.signFilterEnabled = (intParams[1] == 1); + if (intParams.length > 2) this.signId = normalizeSignId(intParams[2]); + if (intParams.length > 3) this.danceFilterEnabled = (intParams[3] == 1); + if (intParams.length > 4) this.danceId = normalizeDanceId(intParams[4]); + if (intParams.length > 5) this.userSource = this.normalizeUserSource(intParams[5]); + if (intParams.length > 6) this.quantifier = normalizeQuantifier(intParams[6]); + + return true; + } + + protected void resetSettings() { + this.selectedAction = DEFAULT_ACTION; + this.signFilterEnabled = false; + this.signId = 0; + this.danceFilterEnabled = false; + this.danceId = 1; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + protected int normalizeAction(int action) { + switch (action) { + case WiredUserActionType.WAVE: + case WiredUserActionType.BLOW_KISS: + case WiredUserActionType.LAUGH: + case WiredUserActionType.AWAKE: + case WiredUserActionType.RELAX: + case WiredUserActionType.SIT: + case WiredUserActionType.STAND: + case WiredUserActionType.LAY: + case WiredUserActionType.SIGN: + case WiredUserActionType.DANCE: + case WiredUserActionType.THUMB_UP: + return action; + default: + return DEFAULT_ACTION; + } + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + protected int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + protected int normalizeSignId(int value) { + return (value < 0 || value > 17) ? 0 : value; + } + + protected int normalizeDanceId(int value) { + return (value < 1 || value > 4) ? 1 : value; + } + + protected boolean matchesAction(WiredContext ctx, RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + if (this.matchesEventAction(ctx, roomUnit)) { + return true; + } + + if (this.matchesCurrentState(roomUnit)) { + return true; + } + + return this.matchesRecentAction(roomUnit); + } + + protected boolean matchesEventAction(WiredContext ctx, RoomUnit roomUnit) { + RoomUnit actor = ctx.actor().orElse(null); + + if (actor == null || actor.getId() != roomUnit.getId()) { + return false; + } + + if (ctx.eventType() != com.eu.habbo.habbohotel.wired.core.WiredEvent.Type.USER_PERFORMS_ACTION) { + return false; + } + + return this.matchesConfiguredAction(ctx.event().getActionId(), ctx.event().getActionParameter()); + } + + protected boolean matchesCurrentState(RoomUnit roomUnit) { + switch (this.selectedAction) { + case WiredUserActionType.SIT: + return roomUnit.hasStatus(RoomUnitStatus.SIT); + case WiredUserActionType.LAY: + return roomUnit.hasStatus(RoomUnitStatus.LAY); + case WiredUserActionType.RELAX: + return roomUnit.isIdle(); + case WiredUserActionType.SIGN: + return this.matchesSignState(roomUnit); + case WiredUserActionType.DANCE: + return this.matchesDanceState(roomUnit); + default: + return false; + } + } + + protected boolean matchesRecentAction(RoomUnit roomUnit) { + Object actionValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_ID); + Object parameterValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_PARAMETER); + Object timestampValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_TIMESTAMP); + + if (!(actionValue instanceof Integer) || !(timestampValue instanceof Long)) { + return false; + } + + long timestamp = (Long) timestampValue; + if ((System.currentTimeMillis() - timestamp) > TRANSIENT_ACTION_WINDOW_MS) { + return false; + } + + int actionId = (Integer) actionValue; + int parameter = (parameterValue instanceof Integer) ? (Integer) parameterValue : -1; + + return this.matchesConfiguredAction(actionId, parameter); + } + + protected boolean matchesConfiguredAction(int actionId, int actionParameter) { + if (actionId != this.selectedAction) { + return false; + } + + if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) { + return actionParameter == this.signId; + } + + if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) { + return actionParameter == this.danceId; + } + + return true; + } + + protected boolean matchesSignState(RoomUnit roomUnit) { + String signStatus = roomUnit.getStatus(RoomUnitStatus.SIGN); + if (signStatus == null) { + return false; + } + + if (!this.signFilterEnabled) { + return true; + } + + try { + return Integer.parseInt(signStatus) == this.signId; + } catch (NumberFormatException ignored) { + return false; + } + } + + protected boolean matchesDanceState(RoomUnit roomUnit) { + int currentDance = roomUnit.getDanceType().getType(); + if (currentDance <= 0) { + return false; + } + + if (!this.danceFilterEnabled) { + return true; + } + + return currentDance == this.danceId; + } + + protected int getUserSource() { + return this.userSource; + } + + protected int getQuantifier() { + return this.quantifier; + } + + static class JsonData { + int selectedAction; + boolean signFilterEnabled; + int signId; + boolean danceFilterEnabled; + int danceId; + int userSource; + int quantifier; + + public JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId, int userSource, int quantifier) { + this.selectedAction = selectedAction; + this.signFilterEnabled = signFilterEnabled; + this.signId = signId; + this.danceFilterEnabled = danceFilterEnabled; + this.danceId = danceId; + this.userSource = userSource; + this.quantifier = quantifier; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java index b2f435d0..96172f84 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java @@ -26,7 +26,17 @@ public enum WiredConditionType { DATE_RANGE(24), ACTOR_HAS_HANDITEM(25), MOVEMENT_VALIDATION(26), // i dont know what type it is but its needed - COUNTER_TIME_MATCHES(27); + COUNTER_TIME_MATCHES(27), + USER_PERFORMS_ACTION(28), + HAS_ALTITUDE(29), + NOT_USER_PERFORMS_ACTION(30), + NOT_ACTOR_HAS_HANDITEM(31), + TRIGGERER_MATCH(32), + NOT_TRIGGERER_MATCH(33), + TEAM_HAS_SCORE(34), + TEAM_HAS_RANK(35), + MATCH_TIME(36), + MATCH_DATE(37); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index cfe08f6b..38464b93 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -64,6 +64,9 @@ import java.sql.SQLException; * @see WiredEvents */ public final class WiredManager { + private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id"; + private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter"; + private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp"; private static final Logger LOGGER = LoggerFactory.getLogger(WiredManager.class); @@ -279,6 +282,10 @@ public final class WiredManager { return false; } + user.getCacheable().put(CACHE_LAST_ACTION_ID, actionId); + user.getCacheable().put(CACHE_LAST_ACTION_PARAMETER, actionParameter); + user.getCacheable().put(CACHE_LAST_ACTION_TIMESTAMP, System.currentTimeMillis()); + WiredEvent event = WiredEvents.userPerformsAction(room, user, actionId, actionParameter); return handleEvent(event); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java index f00380ff..f39363d0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java @@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.wired.highscores; import com.eu.habbo.Emulator; import com.eu.habbo.plugin.EventHandler; import com.eu.habbo.plugin.events.emulator.EmulatorLoadedEvent; +import com.eu.habbo.util.HotelDateTimeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,9 +12,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.DayOfWeek; -import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.temporal.TemporalAdjusters; import java.time.temporal.WeekFields; import java.util.*; @@ -31,8 +30,6 @@ public class WiredHighscoreManager { private final static DayOfWeek firstDayOfWeek = WeekFields.of(Locale.of(locale, country)).getFirstDayOfWeek(); private final static DayOfWeek lastDayOfWeek = DayOfWeek.of(((firstDayOfWeek.getValue() + 5) % DayOfWeek.values().length) + 1); - private final static ZoneId zoneId = ZoneId.systemDefault(); - public static ScheduledFuture midnightUpdater = null; public void load() { @@ -183,26 +180,26 @@ public class WiredHighscoreManager { } private long getTodayStartTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT)); } private long getTodayEndTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1)); } private long getWeekStartTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.previousOrSame(firstDayOfWeek)).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.previousOrSame(firstDayOfWeek))); } private long getWeekEndTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.nextOrSame(lastDayOfWeek)).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.nextOrSame(lastDayOfWeek))); } private long getMonthStartTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.firstDayOfMonth()).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.firstDayOfMonth())); } private long getMonthEndTimestamp() { - return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.lastDayOfMonth()).atZone(zoneId).toEpochSecond(); + return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.lastDayOfMonth())); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreMidnightUpdater.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreMidnightUpdater.java index 7b60b22c..bb53dd4b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreMidnightUpdater.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreMidnightUpdater.java @@ -4,11 +4,10 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredHighscore; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.util.HotelDateTimeUtil; import gnu.trove.set.hash.THashSet; -import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.util.List; public class WiredHighscoreMidnightUpdater implements Runnable { @@ -30,6 +29,7 @@ public class WiredHighscoreMidnightUpdater implements Runnable { } public static int getNextUpdaterRun() { - return Math.toIntExact(LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).atZone(ZoneId.systemDefault()).toEpochSecond() - Emulator.getIntUnixTimestamp()) + 5; + long nextRunTimestamp = HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1)); + return Math.toIntExact(nextRunTimestamp - Emulator.getIntUnixTimestamp()) + 5; } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/hotelview/HotelViewRequestSecondsUntilEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/hotelview/HotelViewRequestSecondsUntilEvent.java index d948ffc8..9856b076 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/hotelview/HotelViewRequestSecondsUntilEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/hotelview/HotelViewRequestSecondsUntilEvent.java @@ -3,9 +3,9 @@ package com.eu.habbo.messages.incoming.hotelview; import com.eu.habbo.Emulator; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.hotelview.HotelViewSecondsUntilComposer; +import com.eu.habbo.util.HotelDateTimeUtil; import java.time.LocalDateTime; -import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -18,7 +18,7 @@ public class HotelViewRequestSecondsUntilEvent extends MessageHandler { try { LocalDateTime dt = LocalDateTime.parse(date, formatter); - int secondsUntil = Math.max(0, (int) dt.atZone(ZoneId.systemDefault()).toEpochSecond() - Emulator.getIntUnixTimestamp()); + int secondsUntil = Math.max(0, (int) HotelDateTimeUtil.toEpochSecond(dt) - Emulator.getIntUnixTimestamp()); this.client.sendResponse(new HotelViewSecondsUntilComposer(date, secondsUntil)); } catch (DateTimeParseException ignored) { } diff --git a/Emulator/src/main/java/com/eu/habbo/util/HotelDateTimeUtil.java b/Emulator/src/main/java/com/eu/habbo/util/HotelDateTimeUtil.java new file mode 100644 index 00000000..0abb37f3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/util/HotelDateTimeUtil.java @@ -0,0 +1,59 @@ +package com.eu.habbo.util; + +import com.eu.habbo.Emulator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +public final class HotelDateTimeUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(HotelDateTimeUtil.class); + private static final String CONFIG_KEY = "hotel.timezone"; + private static volatile String lastInvalidTimezoneId = null; + + private HotelDateTimeUtil() { + } + + public static String getTimezoneId() { + return getZoneId().getId(); + } + + public static ZoneId getZoneId() { + String configuredZoneId = Emulator.getConfig().getValue(CONFIG_KEY, ZoneId.systemDefault().getId()); + + try { + lastInvalidTimezoneId = null; + return ZoneId.of(configuredZoneId.trim()); + } catch (Exception e) { + if (!configuredZoneId.equals(lastInvalidTimezoneId)) { + LOGGER.warn("Invalid {} '{}', falling back to system timezone '{}'.", CONFIG_KEY, configuredZoneId, ZoneId.systemDefault().getId()); + lastInvalidTimezoneId = configuredZoneId; + } + return ZoneId.systemDefault(); + } + } + + public static ZonedDateTime now() { + return ZonedDateTime.now(getZoneId()); + } + + public static LocalDateTime localDateTimeNow() { + return LocalDateTime.now(getZoneId()); + } + + public static LocalDate localDateNow() { + return LocalDate.now(getZoneId()); + } + + public static LocalTime localTimeNow() { + return LocalTime.now(getZoneId()); + } + + public static long toEpochSecond(LocalDateTime dateTime) { + return dateTime.atZone(getZoneId()).toEpochSecond(); + } +} From 12079a43f8a4c1d699f9ed1282f224645f681a99 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Thu, 19 Mar 2026 15:22:49 +0100 Subject: [PATCH 8/9] feat(commands): add wired tools launcher --- .../habbohotel/commands/CommandHandler.java | 1 + .../habbohotel/commands/WiredCommand.java | 35 +++++++++++++++++++ .../eu/habbo/messages/outgoing/Outgoing.java | 1 + .../outgoing/users/InClientLinkComposer.java | 20 +++++++++++ 4 files changed, 57 insertions(+) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/InClientLinkComposer.java diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java index 956b0430..2137439a 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java @@ -297,6 +297,7 @@ public class CommandHandler { addCommand(new SoftKickCommand()); addCommand(new SubscriptionCommand()); addCommand(new UpdateChatBubblesCommand()); + addCommand(new WiredCommand()); addCommand(new TestCommand()); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java new file mode 100644 index 00000000..d8d869b9 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java @@ -0,0 +1,35 @@ +package com.eu.habbo.habbohotel.commands; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.permissions.Permission; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; +import com.eu.habbo.messages.outgoing.users.InClientLinkComposer; + +public class WiredCommand extends Command { + public WiredCommand() { + super(Permission.ACC_PLACEFURNI, new String[]{"wired"}); + } + + @Override + public boolean handle(GameClient gameClient, String[] params) throws Exception { + Room room = gameClient.getHabbo().getHabboInfo().getCurrentRoom(); + + if (room == null) { + gameClient.getHabbo().whisper("You need to be inside a room to use :wired.", RoomChatMessageBubbles.ALERT); + return true; + } + + boolean hasRights = room.hasRights(gameClient.getHabbo()) + || room.isOwner(gameClient.getHabbo()) + || gameClient.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER); + + if (!hasRights) { + gameClient.getHabbo().whisper("You need room rights to open the Wired Creator Tools.", RoomChatMessageBubbles.ALERT); + return true; + } + + gameClient.sendResponse(new InClientLinkComposer("wired-tools/show")); + return true; + } +} 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 173ec13f..310a1404 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 @@ -504,6 +504,7 @@ public class Outgoing { public final static int WiredOpenComposer = 1830; public final static int UnknownCatalogPageOfferComposer = 1889; public final static int NuxAlertComposer = 2023; + public final static int InClientLinkComposer = 2023; public final static int HotelViewExpiringCatalogPageCommposer = 2515; public final static int UnknownHabboWayQuizComposer = 2772; public final static int PetLevelUpdatedComposer = 2824; diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/InClientLinkComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/InClientLinkComposer.java new file mode 100644 index 00000000..61c8548f --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/users/InClientLinkComposer.java @@ -0,0 +1,20 @@ +package com.eu.habbo.messages.outgoing.users; + +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +public class InClientLinkComposer extends MessageComposer { + private final String link; + + public InClientLinkComposer(String link) { + this.link = link; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.InClientLinkComposer); + this.response.appendString(this.link); + return this.response; + } +} From 3a5d1e21e333516ad1d337d156d60357d0a74ae3 Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Sat, 21 Mar 2026 14:27:57 +0100 Subject: [PATCH 9/9] feat(wired): expand advanced wired gameplay tools --- .../eu/habbo/habbohotel/games/GamePlayer.java | 10 +- .../habbohotel/games/wired/WiredGame.java | 23 +- .../habbo/habbohotel/items/ItemManager.java | 32 ++ .../interactions/InteractionWiredEffect.java | 54 +++ .../interactions/InteractionWiredExtra.java | 16 +- .../interactions/InteractionWiredTrigger.java | 4 + .../conditions/WiredConditionActorDir.java | 200 +++++++++++ .../WiredConditionCounterTimeMatches.java | 66 ++-- .../WiredConditionFurniHaveHabbo.java | 43 ++- .../WiredConditionFurniTypeMatch.java | 306 +++++++++++++--- .../conditions/WiredConditionGroupMember.java | 137 ++++++- .../WiredConditionHabboHasEffect.java | 60 +++- .../WiredConditionHabboHasHandItem.java | 65 +++- .../WiredConditionHabboWearsBadge.java | 77 +++- .../WiredConditionMatchStatePosition.java | 100 +++++- .../WiredConditionNotFurniHaveHabbo.java | 43 ++- .../WiredConditionNotFurniTypeMatch.java | 153 +------- .../WiredConditionNotHabboHasEffect.java | 90 +---- .../WiredConditionNotHabboHasHandItem.java | 8 +- .../WiredConditionNotHabboWearsBadge.java | 93 +---- .../conditions/WiredConditionNotInGroup.java | 131 ++++++- .../conditions/WiredConditionNotInTeam.java | 100 +----- .../WiredConditionNotMatchStatePosition.java | 12 +- .../WiredConditionNotTriggerOnFurni.java | 6 +- .../WiredConditionSelectionQuantity.java | 213 +++++++++++ .../conditions/WiredConditionTeamMember.java | 68 +++- .../WiredConditionTriggerOnFurni.java | 49 ++- .../wired/effects/WiredEffectBotClothes.java | 27 +- .../effects/WiredEffectBotFollowHabbo.java | 35 +- .../effects/WiredEffectBotGiveHandItem.java | 81 ++++- .../wired/effects/WiredEffectBotTalk.java | 35 +- .../effects/WiredEffectBotTalkToHabbo.java | 37 +- .../wired/effects/WiredEffectBotTeleport.java | 40 ++- .../effects/WiredEffectBotWalkToFurni.java | 46 ++- .../WiredEffectChangeFurniDirection.java | 35 +- .../effects/WiredEffectFurniToFurni.java | 24 +- .../wired/effects/WiredEffectFurniToUser.java | 26 +- .../effects/WiredEffectGiveHandItem.java | 2 +- .../wired/effects/WiredEffectGiveScore.java | 83 ++--- .../effects/WiredEffectGiveScoreToTeam.java | 59 +-- .../wired/effects/WiredEffectJoinTeam.java | 70 +++- .../wired/effects/WiredEffectMatchFurni.java | 38 +- .../effects/WiredEffectMoveRotateFurni.java | 45 +-- .../effects/WiredEffectMoveRotateUser.java | 288 +++++++++++++++ .../wired/effects/WiredEffectTeleport.java | 51 ++- .../wired/effects/WiredEffectToggleFurni.java | 68 +++- .../wired/effects/WiredEffectUserToFurni.java | 263 +++++++++++++- .../wired/extra/WiredExtraFilterFurni.java | 123 +++++++ .../wired/extra/WiredExtraFilterUser.java | 123 +++++++ .../InteractionWiredMatchFurniSettings.java | 1 + .../selector/WiredEffectFurniAltitude.java | 223 ++++++++++++ .../selector/WiredEffectFurniByType.java | 22 +- .../WiredEffectFurniNeighborhood.java | 47 +-- .../selector/WiredEffectFurniOnFurni.java | 335 ++++++++++++++++++ .../wired/selector/WiredEffectFurniPicks.java | 167 +++++++++ .../selector/WiredEffectFurniSignal.java | 142 ++++++++ .../wired/selector/WiredEffectUsersArea.java | 33 +- .../selector/WiredEffectUsersByAction.java | 333 +++++++++++++++++ .../selector/WiredEffectUsersByName.java | 202 +++++++++++ .../selector/WiredEffectUsersByType.java | 167 +++++++++ .../wired/selector/WiredEffectUsersGroup.java | 179 ++++++++++ .../selector/WiredEffectUsersHandItem.java | 145 ++++++++ .../WiredEffectUsersNeighborhood.java | 48 ++- .../selector/WiredEffectUsersOnFurni.java | 269 ++++++++++++++ .../selector/WiredEffectUsersSignal.java | 138 ++++++++ .../wired/selector/WiredEffectUsersTeam.java | 177 +++++++++ .../triggers/WiredTriggerBotReachedFurni.java | 95 ++++- .../triggers/WiredTriggerBotReachedHabbo.java | 34 +- .../triggers/WiredTriggerClockCounter.java | 31 +- .../WiredTriggerFurniStateToggled.java | 50 ++- .../WiredTriggerHabboClicksFurni.java | 87 ++++- .../triggers/WiredTriggerHabboClicksTile.java | 10 + .../triggers/WiredTriggerHabboClicksUser.java | 111 +++++- .../WiredTriggerHabboSaysKeyword.java | 87 ++++- .../WiredTriggerHabboWalkOffFurni.java | 68 ++-- .../WiredTriggerHabboWalkOnFurni.java | 66 ++-- .../triggers/WiredTriggerReceiveSignal.java | 96 +++-- .../triggers/WiredTriggerScoreAchieved.java | 45 ++- .../habbohotel/rooms/RoomSpecialTypes.java | 9 + .../habbohotel/wired/WiredConditionType.java | 4 +- .../habbohotel/wired/WiredEffectType.java | 15 +- .../wired/WiredMatchFurniSetting.java | 8 +- .../wired/core/WiredBotSourceUtil.java | 59 +++ .../habbohotel/wired/core/WiredEngine.java | 85 +++++ .../habbohotel/wired/core/WiredManager.java | 49 +++ .../wired/core/WiredTriggerSourceUtil.java | 148 ++++++++ .../habbohotel/wired/migrate/WiredEvents.java | 4 +- .../incoming/rooms/items/ClickFurniEvent.java | 2 +- .../rooms/items/ToggleFloorItemEvent.java | 3 + .../rooms/items/ToggleWallItemEvent.java | 3 + .../incoming/rooms/users/ClickUserEvent.java | 10 + .../rooms/users/RoomUserLookAtPoint.java | 4 + .../wired/WiredApplySetConditionsEvent.java | 11 +- .../wired/WiredEffectSaveDataEvent.java | 53 ++- .../wired/WiredTriggerSaveDataEvent.java | 49 +-- .../wired/WiredExtraDataComposer.java | 25 ++ 96 files changed, 6557 insertions(+), 1150 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java index a76f01dd..114aa86d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java @@ -36,8 +36,16 @@ public class GamePlayer { if (this.score < 0) this.score = 0; - if(isWired && this.score > 0) { + if(isWired) { this.wiredScore += amount; + + if (this.wiredScore < 0) { + this.wiredScore = 0; + } + + if (this.wiredScore > this.score) { + this.wiredScore = this.score; + } } WiredManager.triggerScoreAchieved(this.habbo.getHabboInfo().getCurrentRoom(), this.habbo.getRoomUnit(), this.score, amount); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java index 80fe874d..631345e2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java @@ -6,6 +6,10 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.Habbo; public class WiredGame extends Game { + public static final int RED_EFFECT_ID = 223; + public static final int BLUE_EFFECT_ID = 224; + public static final int YELLOW_EFFECT_ID = 225; + public static final int GREEN_EFFECT_ID = 226; public GameState state = GameState.RUNNING; public WiredGame(Room room) { @@ -28,7 +32,7 @@ public class WiredGame extends Game { @Override public boolean addHabbo(Habbo habbo, GameTeamColors teamColor) { - this.room.giveEffect(habbo, FreezeGame.effectId + teamColor.type, -1); + this.room.giveEffect(habbo, this.getEffectId(teamColor), -1); return super.addHabbo(habbo, teamColor); } @@ -47,4 +51,19 @@ public class WiredGame extends Game { public GameState getState() { return GameState.RUNNING; } -} \ No newline at end of file + + private int getEffectId(GameTeamColors teamColor) { + switch (teamColor) { + case RED: + return RED_EFFECT_ID; + case BLUE: + return BLUE_EFFECT_ID; + case YELLOW: + return YELLOW_EFFECT_ID; + case GREEN: + return GREEN_EFFECT_ID; + default: + return FreezeGame.effectId + teamColor.type; + } + } +} 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 265f160c..e0853214 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 @@ -54,7 +54,21 @@ import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUser import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersNeighborhood; import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniNeighborhood; import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniByType; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniAltitude; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniOnFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniPicks; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniSignal; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersSignal; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersByType; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersTeam; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersByAction; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersByName; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersHandItem; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersOnFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersGroup; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob; +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.WiredExtraOrEval; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen; @@ -276,11 +290,24 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_act_rel_mov", WiredEffectRelativeMove.class)); this.interactionsList.add(new ItemInteraction("wf_act_control_clock", WiredEffectControlClock.class)); this.interactionsList.add(new ItemInteraction("wf_act_adjust_clock", WiredEffectAdjustClock.class)); + this.interactionsList.add(new ItemInteraction("wf_act_move_rotate_user", WiredEffectMoveRotateUser.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)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_altitude", WiredEffectFurniAltitude.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_onfurni", WiredEffectFurniOnFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_picks", WiredEffectFurniPicks.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_signal", WiredEffectFurniSignal.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_area", WiredEffectUsersArea.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_neighborhood", WiredEffectUsersNeighborhood.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_signal", WiredEffectUsersSignal.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_bytype", WiredEffectUsersByType.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_team", WiredEffectUsersTeam.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_byaction", WiredEffectUsersByAction.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_byname", WiredEffectUsersByName.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_handitem", WiredEffectUsersHandItem.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_onfurni", WiredEffectUsersOnFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_group", WiredEffectUsersGroup.class)); this.interactionsList.add(new ItemInteraction("wf_act_send_signal", WiredEffectSendSignal.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_furni_on", WiredConditionFurniHaveFurni.class)); @@ -312,6 +339,8 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_match_time", WiredConditionMatchTime.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_match_date", WiredConditionMatchDate.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_actor_dir", WiredConditionActorDir.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_slc_quantity", WiredConditionSelectionQuantity.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_user_performs_action", WiredConditionUserPerformsAction.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_not_user_performs_action", WiredConditionNotUserPerformsAction.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_altitude", WiredConditionHasAltitude.class)); @@ -325,6 +354,9 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_xtra_unseen", WiredExtraUnseen.class)); this.interactionsList.add(new ItemInteraction("wf_blob", WiredBlob.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_or_eval", WiredExtraOrEval.class)); + 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_highscore", InteractionWiredHighscore.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java index d303291d..84c7453a 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java @@ -16,6 +16,8 @@ import com.eu.habbo.messages.outgoing.wired.WiredEffectDataComposer; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.function.Predicate; /** @@ -198,4 +200,56 @@ public abstract class InteractionWiredEffect extends InteractionWired implements || additionalRemoveCondition.test(item)); return sizeBefore - items.size(); } + + protected LinkedHashSet applySelectorModifiers(Iterable matchedTargets, + Iterable availableTargets, + Iterable existingTargets, + boolean filterExisting, + boolean invert) { + LinkedHashSet matched = toLinkedHashSet(matchedTargets); + LinkedHashSet base = filterExisting + ? toLinkedHashSet(existingTargets) + : toLinkedHashSet(availableTargets); + + if (invert) { + base.removeAll(matched); + return base; + } + + if (filterExisting) { + matched.retainAll(base); + } + + return matched; + } + + protected LinkedHashSet getSelectableFloorItems(Room room) { + LinkedHashSet result = new LinkedHashSet<>(); + if (room == null) { + return result; + } + + room.getFloorItems().forEach(item -> { + if (item != null && !(item instanceof InteractionWired)) { + result.add(item); + } + }); + + return result; + } + + protected LinkedHashSet toLinkedHashSet(Iterable values) { + LinkedHashSet result = new LinkedHashSet<>(); + if (values == null) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + return result; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java index b946d7d9..727684f8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java @@ -2,8 +2,11 @@ package com.eu.habbo.habbohotel.items.interactions; import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; +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.messages.incoming.wired.WiredSaveException; +import com.eu.habbo.messages.outgoing.wired.WiredExtraDataComposer; import java.sql.ResultSet; import java.sql.SQLException; @@ -21,6 +24,9 @@ public abstract class InteractionWiredExtra extends InteractionWired { public void onClick(GameClient client, Room room, Object[] objects) throws Exception { if (client != null) { if (room.hasRights(client.getHabbo())) { + if (this.hasConfiguration()) { + client.sendResponse(new WiredExtraDataComposer(this, room)); + } this.activateBox(room); } } @@ -35,4 +41,12 @@ public abstract class InteractionWiredExtra extends InteractionWired { public boolean isWalkable() { return true; } -} \ No newline at end of file + + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + return true; + } + + public boolean hasConfiguration() { + return false; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java index e6410040..16e5b4c8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java @@ -56,6 +56,10 @@ public abstract class InteractionWiredTrigger extends InteractionWired implement public abstract boolean saveData(WiredSettings settings); + public boolean saveData(WiredSettings settings, GameClient gameClient) { + return this.saveData(settings); + } + protected int getDelay() { return this.delay; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java new file mode 100644 index 00000000..8c0e79af --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java @@ -0,0 +1,200 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.WiredConditionType; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionActorDir extends InteractionWiredCondition { + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + private static final int ALL_DIRECTIONS_MASK = createDirectionMask(); + + public static final WiredConditionType type = WiredConditionType.ACTOR_DIR; + + private int directionMask = 0; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionActorDir(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionActorDir(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType 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.directionMask); + message.appendInt(this.userSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.directionMask = (params.length > 0) ? this.normalizeDirectionMask(params[0]) : 0; + this.userSource = (params.length > 1) ? this.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + if (this.directionMask == 0) { + return false; + } + + List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); + if (targets.isEmpty()) { + return false; + } + + if (this.quantifier == QUANTIFIER_ANY) { + return targets.stream().anyMatch(this::matchesDirection); + } + + return targets.stream().allMatch(this::matchesDirection); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.directionMask, + this.userSource, + this.quantifier + )); + } + + @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) { + return; + } + + this.directionMask = this.normalizeDirectionMask(data.directionMask); + this.userSource = this.normalizeUserSource(data.userSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; + } + + String[] parts = wiredData.split("\t"); + + try { + if (parts.length > 0) { + this.directionMask = this.normalizeDirectionMask(Integer.parseInt(parts[0])); + } + if (parts.length > 1) { + this.userSource = this.normalizeUserSource(Integer.parseInt(parts[1])); + } + if (parts.length > 2) { + this.quantifier = this.normalizeQuantifier(Integer.parseInt(parts[2])); + } + } catch (NumberFormatException ignored) { + this.onPickUp(); + } + } + + @Override + public void onPickUp() { + this.directionMask = 0; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + private boolean matchesDirection(RoomUnit roomUnit) { + if (roomUnit == null || roomUnit.getBodyRotation() == null) { + return false; + } + + int direction = roomUnit.getBodyRotation().getValue(); + + return (this.directionMask & (1 << direction)) != 0; + } + + private int normalizeDirectionMask(int value) { + return value & ALL_DIRECTIONS_MASK; + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + private static int createDirectionMask() { + int mask = 0; + + for (int direction = 0; direction < 8; direction++) { + mask |= (1 << direction); + } + + return mask; + } + + static class JsonData { + int directionMask; + int userSource; + int quantifier; + + public JsonData(int directionMask, int userSource, int quantifier) { + this.directionMask = directionMask; + this.userSource = userSource; + this.quantifier = quantifier; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java index 639eb38c..a28ea00b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java @@ -24,6 +24,8 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition private static final int COMPARISON_LESS = 0; private static final int COMPARISON_EQUAL = 1; private static final int COMPARISON_GREATER = 2; + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; private static final int MAX_MINUTES = 99; private static final int MAX_HALF_SECOND_STEPS = 119; @@ -34,6 +36,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition private int minutes = 0; private int halfSecondSteps = 0; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; public WiredConditionCounterTimeMatches(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -61,29 +64,27 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition int targetTimeInMs = this.getTargetTimeInMs(); + if (this.quantifier == QUANTIFIER_ANY) { + for (HabboItem item : targets) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + if (this.matchesCounter((InteractionGameUpCounter) item, targetTimeInMs)) { + return true; + } + } + + return false; + } + for (HabboItem item : targets) { if (!(item instanceof InteractionGameUpCounter)) { return false; } - int currentTimeInMs = ((InteractionGameUpCounter) item).getCurrentTimeInMs(); - - switch (this.comparison) { - case COMPARISON_LESS: - if (currentTimeInMs >= targetTimeInMs) { - return false; - } - break; - case COMPARISON_GREATER: - if (currentTimeInMs <= targetTimeInMs) { - return false; - } - break; - default: - if (currentTimeInMs != targetTimeInMs) { - return false; - } - break; + if (!this.matchesCounter((InteractionGameUpCounter) item, targetTimeInMs)) { + return false; } } @@ -103,6 +104,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition this.minutes, this.halfSecondSteps, this.furniSource, + this.quantifier, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -114,6 +116,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition this.minutes = 0; this.halfSecondSteps = 0; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; String wiredData = set.getString("wired_data"); if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) { @@ -129,6 +132,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition this.minutes = this.normalizeMinutes(data.minutes); this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); this.furniSource = data.furniSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); if (data.itemIds == null) { return; @@ -149,6 +153,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition this.minutes = 0; this.halfSecondSteps = 0; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override @@ -171,11 +176,12 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(5); message.appendInt(this.comparison); message.appendInt(this.minutes); message.appendInt(this.halfSecondSteps); message.appendInt(this.furniSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -190,6 +196,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition this.minutes = (params.length > 1) ? this.normalizeMinutes(params[1]) : 0; this.halfSecondSteps = (params.length > 2) ? this.normalizeHalfSecondSteps(params[2]) : 0; this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 4) ? this.normalizeQuantifier(params[4]) : QUANTIFIER_ALL; this.items.clear(); @@ -236,6 +243,19 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition return (this.minutes * 60_000) + (this.halfSecondSteps * 500); } + private boolean matchesCounter(InteractionGameUpCounter counter, int targetTimeInMs) { + int currentTimeInMs = counter.getCurrentTimeInMs(); + + switch (this.comparison) { + case COMPARISON_LESS: + return currentTimeInMs < targetTimeInMs; + case COMPARISON_GREATER: + return currentTimeInMs > targetTimeInMs; + default: + return currentTimeInMs == targetTimeInMs; + } + } + private int normalizeComparison(int value) { if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { return COMPARISON_EQUAL; @@ -252,18 +272,24 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); } + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { int comparison; int minutes; int halfSecondSteps; int furniSource; + int quantifier; List itemIds; - public JsonData(int comparison, int minutes, int halfSecondSteps, int furniSource, List itemIds) { + public JsonData(int comparison, int minutes, int halfSecondSteps, int furniSource, int quantifier, List itemIds) { this.comparison = comparison; this.minutes = minutes; this.halfSecondSteps = halfSecondSteps; this.furniSource = furniSource; + this.quantifier = quantifier; this.itemIds = itemIds; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java index 1863c07c..a12c1874 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { public static final WiredConditionType type = WiredConditionType.FURNI_HAVE_HABBO; protected THashSet items; + protected boolean all; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredConditionFurniHaveHabbo(ResultSet set, Item baseItem) throws SQLException { @@ -42,6 +43,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.all = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -62,15 +64,11 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { Collection bots = room.getCurrentBots().valueCollection(); Collection pets = room.getCurrentPets().valueCollection(); - return targets.stream().filter(item -> item != null).allMatch(item -> { - RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); - if (baseTile == null) return false; - - THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - return habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())); - }); + if (this.all) { + return targets.stream().filter(item -> item != null).allMatch(item -> this.hasAvatarOnItem(item, room, habbos, bots, pets)); + } + + return targets.stream().filter(item -> item != null).anyMatch(item -> this.hasAvatarOnItem(item, room, habbos, bots, pets)); } @Deprecated @@ -84,7 +82,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.furniSource, + this.all )); } @@ -96,6 +95,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.furniSource = data.furniSource; + this.all = data.all; for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -119,6 +119,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { } } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.all = false; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -144,7 +145,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.all ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -159,7 +161,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.all = (params.length > 0) && (params[0] == 1); + this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER); this.items.clear(); @@ -179,6 +182,18 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { return true; } + protected boolean hasAvatarOnItem(HabboItem item, Room room, Collection habbos, Collection bots, Collection pets) { + RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); + if (baseTile == null) return false; + + THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + return occupiedTiles != null && ( + habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) + ); + } + private void refresh() { THashSet items = new THashSet<>(); @@ -200,10 +215,12 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { static class JsonData { List itemIds; int furniSource; + boolean all; - public JsonData(List itemIds, int furniSource) { + public JsonData(List itemIds, int furniSource, boolean all) { this.itemIds = itemIds; this.furniSource = furniSource; + this.all = all; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java index 72552354..0b91d791 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java @@ -8,22 +8,30 @@ 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.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; 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 gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { + protected static final int SOURCE_SECONDARY_SELECTED = 101; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.STUFF_IS; - private THashSet items = new THashSet<>(); - private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected THashSet items = new THashSet<>(); + protected THashSet secondaryItems = new THashSet<>(); + protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int compareFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionFurniTypeMatch(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,19 +44,90 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.secondaryItems.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.compareFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override public boolean evaluate(WiredContext ctx) { + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyMatches(ctx); + } + + return this.evaluateAllMatches(ctx); + } + + protected boolean evaluateAllMatches(WiredContext ctx) { + List matchTargets = this.resolveMatchTargets(ctx); + if (matchTargets.isEmpty()) { + return false; + } + + THashSet compareTypeIds = this.resolveCompareTypeIds(ctx); + if (compareTypeIds.isEmpty()) { + return false; + } + + for (HabboItem item : matchTargets) { + if (!this.matchesType(item, compareTypeIds)) { + return false; + } + } + + return true; + } + + protected boolean evaluateAnyMatches(WiredContext ctx) { + List matchTargets = this.resolveMatchTargets(ctx); + if (matchTargets.isEmpty()) { + return false; + } + + THashSet compareTypeIds = this.resolveCompareTypeIds(ctx); + if (compareTypeIds.isEmpty()) { + return false; + } + + for (HabboItem item : matchTargets) { + if (this.matchesType(item, compareTypeIds)) { + return true; + } + } + + return false; + } + + protected List resolveMatchTargets(WiredContext ctx) { + this.refresh(); + return this.resolveConfiguredItems(ctx, this.furniSource); + } + + protected THashSet resolveCompareTypeIds(WiredContext ctx) { this.refresh(); - if(items.isEmpty()) - return false; + THashSet compareTypeIds = new THashSet<>(); - List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); - if (targets.isEmpty()) return false; - return targets.stream().anyMatch(this.items::contains); + for (HabboItem item : this.resolveConfiguredItems(ctx, this.compareFurniSource)) { + if (item != null && item.getBaseItem() != null) { + compareTypeIds.add(item.getBaseItem().getId()); + } + } + + return compareTypeIds; + } + + protected boolean matchesType(HabboItem item, THashSet compareTypeIds) { + return item != null && item.getBaseItem() != null && compareTypeIds.contains(item.getBaseItem().getId()); + } + + protected List resolveConfiguredItems(WiredContext ctx, int sourceType) { + if (sourceType == SOURCE_SECONDARY_SELECTED) { + return new ArrayList<>(this.secondaryItems); + } + + return WiredSourceUtil.resolveItems(ctx, sourceType, this.items); } @Deprecated @@ -62,41 +141,53 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.secondaryItems.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.furniSource, + this.compareFurniSource, + this.quantifier )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { - this.items.clear(); + 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.furniSource = data.furniSource; - - for(int id : data.itemIds) { - HabboItem item = room.getHabboItem(id); - - if (item != null) { - this.items.add(item); - } + if (data == null) { + return; } - } else { - String[] data = wiredData.split(";"); - for (String s : data) { - HabboItem item = room.getHabboItem(Integer.parseInt(s)); + List primaryIds = (data.primaryItemIds != null) ? data.primaryItemIds : new ArrayList<>(); + List compareIds = (data.secondaryItemIds != null) ? data.secondaryItemIds : ((data.itemIds != null) ? data.itemIds : new ArrayList<>()); - if (item != null) { - this.items.add(item); - } + this.furniSource = this.normalizeFurniSource((data.furniSource != null) ? data.furniSource : WiredSourceUtil.SOURCE_TRIGGER); + this.compareFurniSource = this.normalizeFurniSource((data.compareFurniSource != null) ? data.compareFurniSource : (compareIds.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED)); + this.quantifier = this.normalizeQuantifier((data.quantifier != null) ? data.quantifier : QUANTIFIER_ANY); + + this.loadItems(room, primaryIds, this.items); + this.loadItems(room, compareIds, this.secondaryItems); + return; + } + + String[] data = wiredData.split(";"); + List compareIds = new ArrayList<>(); + + for (String value : data) { + try { + compareIds.add(Integer.parseInt(value)); + } catch (NumberFormatException ignored) { } - this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; - } - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; } + + this.loadItems(room, compareIds, this.secondaryItems); + this.compareFurniSource = this.secondaryItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -112,14 +203,17 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); message.appendInt(this.items.size()); - for (HabboItem item : this.items) + for (HabboItem item : this.items) { message.appendInt(item.getId()); + } message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); - message.appendString(""); - message.appendInt(1); + message.appendString(this.serializeIds(this.secondaryItems)); + message.appendInt(3); message.appendInt(this.furniSource); + message.appendInt(this.compareFurniSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -132,16 +226,51 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + String stringParam = (settings.getStringParam() != null) ? settings.getStringParam().trim() : ""; + boolean legacyData = (params.length <= 1) && stringParam.isEmpty(); - this.items.clear(); + this.onPickUp(); + + if (legacyData) { + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; + } else { + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.compareFurniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + if (legacyData) { + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.secondaryItems.add(item); + } + } + + this.compareFurniSource = this.secondaryItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED; + return true; + } if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.items.add(item); + } + } + } - if (room != null) { - for (int i = 0; i < count; i++) { - this.items.add(room.getHabboItem(settings.getFurniIds()[i])); + if (this.compareFurniSource == SOURCE_SECONDARY_SELECTED) { + for (Integer itemId : this.parseIds(stringParam)) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.secondaryItems.add(item); } } } @@ -149,31 +278,110 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { return true; } - private void refresh() { - THashSet items = new THashSet<>(); + protected int getQuantifier() { + return this.quantifier; + } + + protected void refresh() { + this.refreshSelection(this.items); + this.refreshSelection(this.secondaryItems); + } + + private void refreshSelection(THashSet selection) { + THashSet remove = new THashSet<>(); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room == null) { - items.addAll(this.items); + remove.addAll(selection); } else { - for (HabboItem item : this.items) { - if (room.getHabboItem(item.getId()) == null) - items.add(item); + for (HabboItem item : selection) { + if (room.getHabboItem(item.getId()) == null) { + remove.add(item); + } } } - for (HabboItem item : items) { - this.items.remove(item); + for (HabboItem item : remove) { + selection.remove(item); } } + private void loadItems(Room room, List itemIds, THashSet target) { + if (itemIds == null) { + return; + } + + for (Integer id : itemIds) { + if (id == null) { + continue; + } + + HabboItem item = room.getHabboItem(id); + if (item != null) { + target.add(item); + } + } + } + + private String serializeIds(THashSet source) { + return source.stream() + .map(HabboItem::getId) + .filter(id -> id > 0) + .map(String::valueOf) + .collect(Collectors.joining(";")); + } + + private List parseIds(String value) { + List result = new ArrayList<>(); + if (value == null || value.isEmpty()) { + return result; + } + + for (String part : value.split("[;,\\t]")) { + if (part == null || part.trim().isEmpty()) { + continue; + } + + try { + result.add(Integer.parseInt(part.trim())); + } catch (NumberFormatException ignored) { + } + } + + return result; + } + + protected int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTED: + case SOURCE_SECONDARY_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { + List primaryItemIds; + List secondaryItemIds; List itemIds; - int furniSource; + Integer furniSource; + Integer compareFurniSource; + Integer quantifier; - public JsonData(List itemIds, int furniSource) { - this.itemIds = itemIds; + public JsonData(List primaryItemIds, List secondaryItemIds, int furniSource, int compareFurniSource, int quantifier) { + this.primaryItemIds = primaryItemIds; + this.secondaryItemIds = secondaryItemIds; this.furniSource = furniSource; + this.compareFurniSource = compareFurniSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java index aa9631b5..d442add5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredConditionType; 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; @@ -16,8 +17,16 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionGroupMember extends InteractionWiredCondition { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_IN_GROUP; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private int quantifier = QUANTIFIER_ALL; public WiredConditionGroupMember(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,19 +39,32 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { Room room = ctx.room(); - if (room.getGuildId() == 0) + int targetGroupId = this.resolveTargetGroupId(room); + if (targetGroupId == 0) return false; List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ANY) { + for (RoomUnit roomUnit : targets) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo != null && habbo.getHabboStats().hasGuild(targetGroupId)) { + return true; + } + } + + return false; + } + for (RoomUnit roomUnit : targets) { Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboStats().hasGuild(room.getGuildId())) { - return true; + if (habbo == null || !habbo.getHabboStats().hasGuild(targetGroupId)) { + return false; } } - return false; + + return true; } @Deprecated @@ -53,26 +75,45 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public String getWiredData() { - return String.valueOf(this.userSource); + return WiredManager.getGson().toJson(new JsonData( + this.userSource, + this.groupType, + this.selectedGroupId, + this.quantifier + )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + String wiredData = set.getString("wired_data"); - if (wiredData != null && !wiredData.isEmpty()) { - try { - this.userSource = Integer.parseInt(wiredData); - } catch (NumberFormatException ignored) { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + try { + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.userSource = this.normalizeUserSource(data.userSource); + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = this.normalizeSelectedGroupId(data.selectedGroupId); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; } - } else { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = this.normalizeUserSource(Integer.parseInt(wiredData)); + } catch (Exception ignored) { + this.resetSettings(); } } @Override public void onPickUp() { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.resetSettings(); } @Override @@ -88,8 +129,11 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(4); message.appendInt(this.userSource); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -99,7 +143,70 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 0) ? this.normalizeUserSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = (params.length > 1) ? this.normalizeGroupType(params[1]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 2) ? this.normalizeSelectedGroupId(params[2]) : 0; + this.quantifier = (params.length > 3) ? this.normalizeQuantifier(params[3]) : QUANTIFIER_ALL; return true; } + + private void resetSettings() { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.quantifier = QUANTIFIER_ALL; + } + + private int resolveTargetGroupId(Room room) { + if (room == null) { + return 0; + } + + if (this.groupType == GROUP_SELECTED) { + return this.selectedGroupId; + } + + return room.getGuildId(); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + private int normalizeSelectedGroupId(int value) { + return Math.max(0, value); + } + + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + static class JsonData { + int userSource; + int groupType; + int selectedGroupId; + int quantifier; + + public JsonData(int userSource, int groupType, int selectedGroupId, int quantifier) { + this.userSource = userSource; + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.quantifier = quantifier; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java index a184beaf..2075513d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java @@ -16,10 +16,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionHabboHasEffect extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_EFFECT; protected int effectId = 0; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ANY; public WiredConditionHabboHasEffect(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -33,14 +37,38 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + + if (this.quantifier == QUANTIFIER_ALL) { + return this.matchesAllTargets(targets); + } + + return this.matchesAnyTarget(targets); + } + + protected boolean matchesAllTargets(List targets) { for (RoomUnit roomUnit : targets) { - if (roomUnit != null && roomUnit.getEffectId() == this.effectId) { + if (!this.matchesEffect(roomUnit)) { + return false; + } + } + + return true; + } + + protected boolean matchesAnyTarget(List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesEffect(roomUnit)) { return true; } } + return false; } + protected boolean matchesEffect(RoomUnit roomUnit) { + return roomUnit != null && roomUnit.getEffectId() == this.effectId; + } + @Deprecated @Override public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { @@ -51,7 +79,8 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.effectId, - this.userSource + this.userSource, + this.quantifier )); } @@ -63,9 +92,11 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.effectId = data.effectId; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { this.effectId = Integer.parseInt(wiredData); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } @@ -73,6 +104,7 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public void onPickUp() { this.effectId = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -88,9 +120,10 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.effectId); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -100,20 +133,35 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.effectId = settings.getIntParams()[0]; int[] params = settings.getIntParams(); + this.effectId = params[0]; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ANY) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { int effectId; int userSource; + Integer quantifier; - public JsonData(int effectId, int userSource) { + public JsonData(int effectId, int userSource, int quantifier) { this.effectId = effectId; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java index 64aade52..d949e4bd 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java @@ -19,11 +19,14 @@ import java.util.List; public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { private static final Logger LOGGER = LoggerFactory.getLogger(WiredConditionHabboHasHandItem.class); + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; public static final WiredConditionType type = WiredConditionType.ACTOR_HAS_HANDITEM; private int handItem; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; public WiredConditionHabboHasHandItem(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -46,9 +49,10 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.handItem); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -58,9 +62,10 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.handItem = settings.getIntParams()[0]; + this.handItem = this.normalizeHandItem(settings.getIntParams()[0]); int[] params = settings.getIntParams(); this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; return true; } @@ -69,12 +74,12 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit != null && roomUnit.getHandItem() == this.handItem) { - return true; - } + + if (this.quantifier == QUANTIFIER_ANY) { + return this.matchesAnyTarget(targets); } - return false; + + return this.matchesAllTargets(targets); } @Deprecated @@ -87,7 +92,8 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.handItem, - this.userSource + this.userSource, + this.quantifier )); } @@ -98,11 +104,13 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.handItem = data.handItemId; + this.handItem = this.normalizeHandItem(data.handItemId); this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); } else { - this.handItem = Integer.parseInt(wiredData); + this.handItem = this.normalizeHandItem(Integer.parseInt(wiredData)); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } } catch (Exception e) { LOGGER.error("Caught exception", e); @@ -113,6 +121,7 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public void onPickUp() { this.handItem = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } protected int getHandItem() { @@ -123,13 +132,47 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { return this.userSource; } + protected int getQuantifier() { + return this.quantifier; + } + + protected boolean matchesAnyTarget(List targets) { + for (RoomUnit roomUnit : targets) { + if (roomUnit != null && roomUnit.getHandItem() == this.handItem) { + return true; + } + } + + return false; + } + + protected boolean matchesAllTargets(List targets) { + for (RoomUnit roomUnit : targets) { + if (roomUnit == null || roomUnit.getHandItem() != this.handItem) { + return false; + } + } + + return true; + } + + protected int normalizeHandItem(int value) { + return Math.max(0, value); + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { int handItemId; int userSource; + int quantifier; - public JsonData(int handItemId, int userSource) { + public JsonData(int handItemId, int userSource, int quantifier) { this.handItemId = handItemId; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java index 844ef89c..83c85a8f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java @@ -18,10 +18,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_BADGE; protected String badge = ""; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ANY; public WiredConditionHabboWearsBadge(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,18 +41,47 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ALL) { + return this.matchesAllTargets(room, targets); + } + + return this.matchesAnyTarget(room, targets); + } + + protected boolean matchesAllTargets(Room room, List targets) { for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null) { - synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { - for (HabboBadge badge : habbo.getInventory().getBadgesComponent().getWearingBadges()) { - if (badge.getCode().equalsIgnoreCase(this.badge)) { - return true; - } - } + if (!this.matchesBadge(room, roomUnit)) { + return false; + } + } + + return true; + } + + protected boolean matchesAnyTarget(Room room, List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesBadge(room, roomUnit)) { + return true; + } + } + + return false; + } + + protected boolean matchesBadge(Room room, RoomUnit roomUnit) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null) { + return false; + } + + synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { + for (HabboBadge badge : habbo.getInventory().getBadgesComponent().getWearingBadges()) { + if (badge.getCode().equalsIgnoreCase(this.badge)) { + return true; } } } + return false; } @@ -62,7 +95,8 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.badge, - this.userSource + this.userSource, + this.quantifier )); } @@ -74,9 +108,11 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.badge = data.badge; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { this.badge = wiredData; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } @@ -84,6 +120,7 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { public void onPickUp() { this.badge = ""; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -99,8 +136,9 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.badge); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -112,17 +150,32 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { this.badge = settings.getStringParam(); int[] params = settings.getIntParams(); this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 1) ? this.normalizeQuantifier(params[1], QUANTIFIER_ANY) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { String badge; int userSource; + Integer quantifier; - public JsonData(String badge, int userSource) { + public JsonData(String badge, int userSource, int quantifier) { this.badge = badge; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java index 7966d563..ddc0477e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java @@ -20,8 +20,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.math.BigDecimal; public class WiredConditionMatchStatePosition extends InteractionWiredCondition implements InteractionWiredMatchFurniSettings { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.MATCH_SSHOT; private THashSet settings; @@ -29,7 +33,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition private boolean state; private boolean position; private boolean direction; + private boolean altitude; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; public WiredConditionMatchStatePosition(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -60,11 +66,13 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(6); message.appendInt(this.state ? 1 : 0); message.appendInt(this.direction ? 1 : 0); message.appendInt(this.position ? 1 : 0); + message.appendInt(this.altitude ? 1 : 0); message.appendInt(this.furniSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -78,7 +86,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state = params[0] == 1; this.direction = params[1] == 1; this.position = params[2] == 1; - this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER; + this.altitude = (params.length > 3) && (params[3] == 1); + this.furniSource = (params.length > 4) ? params[4] : ((params.length > 3 && params[3] > 1) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER); + this.quantifier = (params.length > 5) ? this.normalizeQuantifier(params[5]) : QUANTIFIER_ALL; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -95,7 +105,7 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition HabboItem item = room.getHabboItem(itemId); if (item != null) - this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY())); + this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ())); } return true; @@ -103,12 +113,21 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition @Override public boolean evaluate(WiredContext ctx) { - Room room = ctx.room(); this.refresh(); if (this.settings.isEmpty()) return true; + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyTargetMatches(ctx); + } + + return this.evaluateAllTargetsMatch(ctx); + } + + protected boolean evaluateAllTargetsMatch(WiredContext ctx) { + Room room = ctx.room(); + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); if (targets.isEmpty()) return false; @@ -139,6 +158,41 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition return true; } + protected boolean evaluateAnyTargetMatches(WiredContext ctx) { + Room room = ctx.room(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); + if (targets.isEmpty()) return false; + + for (HabboItem item : targets) { + if (item == null) continue; + + WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); + if (setting != null && this.matchesSetting(item, setting)) { + return true; + } + } + + return false; + } + + for (WiredMatchFurniSetting setting : this.settings) { + HabboItem item = room.getHabboItem(setting.item_id); + if (item == null) continue; + + if (this.matchesSetting(item, setting)) { + return true; + } + } + + return false; + } + + protected int getQuantifier() { + return this.quantifier; + } + private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) { WiredMatchFurniSetting fallback = null; @@ -166,6 +220,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition if (this.position && !(setting.x == item.getX() && setting.y == item.getY())) return false; + if (this.altitude && BigDecimal.valueOf(item.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0) + return false; + return !this.direction || setting.rotation == item.getRotation(); } @@ -181,8 +238,10 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state, this.position, this.direction, + this.altitude, new ArrayList<>(this.settings), - this.furniSource + this.furniSource, + this.quantifier )); } @@ -195,8 +254,12 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state = data.state; this.position = data.position; this.direction = data.direction; - this.settings.addAll(data.settings); + this.altitude = data.altitude; + if (data.settings != null) { + this.settings.addAll(data.settings); + } this.furniSource = data.furniSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); } else { String[] data = wiredData.split(":"); @@ -207,14 +270,18 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition for (int i = 0; i < itemCount; i++) { String[] stuff = items[i].split("-"); - if (stuff.length >= 5) + if (stuff.length >= 6) + this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]), Double.parseDouble(stuff[5]))); + else if (stuff.length >= 5) this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]))); } this.state = data[2].equals("1"); this.direction = data[3].equals("1"); this.position = data[4].equals("1"); + this.altitude = false; this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.quantifier = QUANTIFIER_ALL; } } @@ -224,10 +291,16 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.direction = false; this.position = false; this.state = false; + this.altitude = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } - private void refresh() { + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + protected void refresh() { Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room != null) { @@ -266,19 +339,28 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition return this.position; } + @Override + public boolean shouldMatchAltitude() { + return this.altitude; + } + static class JsonData { boolean state; boolean position; boolean direction; + boolean altitude; List settings; int furniSource; + int quantifier; - public JsonData(boolean state, boolean position, boolean direction, List settings, int furniSource) { + public JsonData(boolean state, boolean position, boolean direction, boolean altitude, List settings, int furniSource, int quantifier) { this.state = state; this.position = position; this.direction = direction; + this.altitude = altitude; this.settings = settings; this.furniSource = furniSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java index dff23b8a..3aec0268 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java @@ -28,6 +28,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { public static final WiredConditionType type = WiredConditionType.NOT_FURNI_HAVE_HABBO; protected THashSet items; + protected boolean all; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredConditionNotFurniHaveHabbo(ResultSet set, Item baseItem) throws SQLException { @@ -43,6 +44,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.all = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -63,15 +65,11 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { Collection bots = room.getCurrentBots().valueCollection(); Collection pets = room.getCurrentPets().valueCollection(); - return targets.stream().filter(item -> item != null).noneMatch(item -> { - RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); - if (baseTile == null) return false; - - THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - return habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())); - }); + if (this.all) { + return targets.stream().filter(item -> item != null).allMatch(item -> !this.hasAvatarOnItem(item, room, habbos, bots, pets)); + } + + return targets.stream().filter(item -> item != null).anyMatch(item -> !this.hasAvatarOnItem(item, room, habbos, bots, pets)); } @Deprecated @@ -85,7 +83,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.furniSource, + this.all )); } @@ -97,6 +96,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { if (wiredData.startsWith("{")) { WiredConditionFurniHaveHabbo.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionFurniHaveHabbo.JsonData.class); this.furniSource = data.furniSource; + this.all = data.all; for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -119,6 +119,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { } } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.all = false; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -144,7 +145,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.all ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -158,7 +160,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.all = (params.length > 0) && (params[0] == 1); + this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER); this.items.clear(); @@ -178,6 +181,18 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { return true; } + protected boolean hasAvatarOnItem(HabboItem item, Room room, Collection habbos, Collection bots, Collection pets) { + RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); + if (baseTile == null) return false; + + THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + return occupiedTiles != null && ( + habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) + ); + } + private void refresh() { THashSet items = new THashSet<>(); @@ -199,10 +214,12 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { static class JsonData { List itemIds; int furniSource; + boolean all; - public JsonData(List itemIds, int furniSource) { + public JsonData(List itemIds, int furniSource, boolean all) { this.itemIds = itemIds; this.furniSource = furniSource; + this.all = all; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java index b9aaf539..49dc6775 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java @@ -1,30 +1,15 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; -import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -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.HabboItem; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; -import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; -import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.List; -import java.util.stream.Collectors; -public class WiredConditionNotFurniTypeMatch extends InteractionWiredCondition { +public class WiredConditionNotFurniTypeMatch extends WiredConditionFurniTypeMatch { public static final WiredConditionType type = WiredConditionType.NOT_STUFF_IS; - private THashSet items = new THashSet<>(); - private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotFurniTypeMatch(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -35,145 +20,15 @@ public class WiredConditionNotFurniTypeMatch extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { - this.refresh(); - - if(items.isEmpty()) - return true; - - List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); - if (targets.isEmpty()) return true; - return targets.stream().noneMatch(this.items::contains); - } - - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - this.refresh(); - return WiredManager.getGson().toJson(new JsonData( - this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - this.items.clear(); - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - WiredConditionFurniTypeMatch.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionFurniTypeMatch.JsonData.class); - this.furniSource = data.furniSource; - - for(int id : data.itemIds) { - HabboItem item = room.getHabboItem(id); - - if (item != null) { - this.items.add(item); - } - } - } else { - String[] data = set.getString("wired_data").split(";"); - - for (String s : data) { - 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; + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.evaluateAllMatches(ctx); } - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; - } - } - @Override - public void onPickUp() { - this.items.clear(); - this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.evaluateAnyMatches(ctx); } @Override public WiredConditionType getType() { return type; } - - @Override - public void serializeWiredData(ServerMessage message, Room room) { - this.refresh(); - - message.appendBoolean(false); - message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); - message.appendInt(this.items.size()); - - for (HabboItem item : this.items) - message.appendInt(item.getId()); - - message.appendInt(this.getBaseItem().getSpriteId()); - message.appendInt(this.getId()); - message.appendString(""); - message.appendInt(1); - message.appendInt(this.furniSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - int count = settings.getFurniIds().length; - if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; - - int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - - this.items.clear(); - - if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); - - if (room != null) { - for (int i = 0; i < count; i++) { - this.items.add(room.getHabboItem(settings.getFurniIds()[i])); - } - } - } - - return true; - } - - private void refresh() { - THashSet items = new THashSet<>(); - - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); - if (room == null) { - items.addAll(this.items); - } else { - for (HabboItem item : this.items) { - if (room.getHabboItem(item.getId()) == null) - items.add(item); - } - } - - for (HabboItem item : items) { - this.items.remove(item); - } - } - - static class JsonData { - List itemIds; - int furniSource; - - public JsonData(List itemIds, int furniSource) { - this.itemIds = itemIds; - this.furniSource = furniSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java index d061c0a8..b7c72361 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java @@ -1,26 +1,18 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -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.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotHabboHasEffect extends InteractionWiredCondition { +public class WiredConditionNotHabboHasEffect extends WiredConditionHabboHasEffect { private static final WiredConditionType type = WiredConditionType.NOT_ACTOR_WEARS_EFFECT; - protected int effectId; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotHabboHasEffect(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -33,88 +25,16 @@ public class WiredConditionNotHabboHasEffect extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit == null) return false; - if (roomUnit.getEffectId() == this.effectId) { - return false; - } + + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.matchesAllTargets(targets); } - return true; - } - @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.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 = data.effectId; - this.userSource = data.userSource; - } else { - this.effectId = Integer.parseInt(wiredData); - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.effectId = 0; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.matchesAnyTarget(targets); } @Override public WiredConditionType 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(this.effectId + ""); - message.appendInt(2); - message.appendInt(this.effectId); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.effectId = settings.getIntParams()[0]; - int[] params = settings.getIntParams(); - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - int effectId; - int userSource; - - public JsonData(int effectId, int userSource) { - this.effectId = effectId; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java index 24421924..060b6089 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java @@ -26,13 +26,11 @@ public class WiredConditionNotHabboHasHandItem extends WiredConditionHabboHasHan List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource()); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit == null || roomUnit.getHandItem() == this.getHandItem()) { - return false; - } + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.matchesAnyTarget(targets); } - return true; + return !this.matchesAllTargets(targets); } @Override diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java index 5521d5d8..18a6c869 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java @@ -1,28 +1,19 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -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.HabboBadge; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotHabboWearsBadge extends InteractionWiredCondition { +public class WiredConditionNotHabboWearsBadge extends WiredConditionHabboWearsBadge { public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_WEARS_BADGE; - protected String badge = ""; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotHabboWearsBadge(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -37,91 +28,15 @@ public class WiredConditionNotHabboWearsBadge extends InteractionWiredCondition List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return true; - for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null) { - synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { - for (HabboBadge b : habbo.getInventory().getBadgesComponent().getWearingBadges()) { - if (b.getCode().equalsIgnoreCase(this.badge)) - return false; - } - } - } + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.matchesAllTargets(room, targets); } - return true; - } - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData( - this.badge, - 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.badge = data.badge; - this.userSource = data.userSource; - } else { - this.badge = wiredData; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.badge = ""; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.matchesAnyTarget(room, targets); } @Override public WiredConditionType 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(this.badge); - message.appendInt(1); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - this.badge = settings.getStringParam(); - int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - String badge; - int userSource; - - public JsonData(String badge, int userSource) { - this.badge = badge; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java index 237be63d..04bbe22c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredConditionType; 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; @@ -16,8 +17,16 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionNotInGroup extends InteractionWiredCondition { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_IN_GROUP; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private int quantifier = QUANTIFIER_ALL; public WiredConditionNotInGroup(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,18 +39,31 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { Room room = ctx.room(); - if (room.getGuildId() == 0) + int targetGroupId = this.resolveTargetGroupId(room); + if (targetGroupId == 0) return false; List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); - if (targets.isEmpty()) return true; + if (targets.isEmpty()) return false; + + if (this.quantifier == QUANTIFIER_ANY) { + for (RoomUnit roomUnit : targets) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null || !habbo.getHabboStats().hasGuild(targetGroupId)) { + return true; + } + } + + return false; + } for (RoomUnit roomUnit : targets) { Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboStats().hasGuild(room.getGuildId())) { + if (habbo != null && habbo.getHabboStats().hasGuild(targetGroupId)) { return false; } } + return true; } @@ -53,26 +75,45 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public String getWiredData() { - return String.valueOf(this.userSource); + return WiredManager.getGson().toJson(new JsonData( + this.userSource, + this.groupType, + this.selectedGroupId, + this.quantifier + )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + String wiredData = set.getString("wired_data"); - if (wiredData != null && !wiredData.isEmpty()) { - try { - this.userSource = Integer.parseInt(wiredData); - } catch (NumberFormatException ignored) { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + try { + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.userSource = this.normalizeUserSource(data.userSource); + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = this.normalizeSelectedGroupId(data.selectedGroupId); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; } - } else { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = this.normalizeUserSource(Integer.parseInt(wiredData)); + } catch (Exception ignored) { + this.resetSettings(); } } @Override public void onPickUp() { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.resetSettings(); } @Override @@ -88,8 +129,11 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(4); message.appendInt(this.userSource); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -99,7 +143,66 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 0) ? this.normalizeUserSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = (params.length > 1) ? this.normalizeGroupType(params[1]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 2) ? this.normalizeSelectedGroupId(params[2]) : 0; + this.quantifier = (params.length > 3) ? this.normalizeQuantifier(params[3]) : QUANTIFIER_ALL; return true; } + + private void resetSettings() { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.quantifier = QUANTIFIER_ALL; + } + + private int resolveTargetGroupId(Room room) { + if (room == null) { + return 0; + } + + if (this.groupType == GROUP_SELECTED) { + return this.selectedGroupId; + } + + return room.getGuildId(); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + private int normalizeSelectedGroupId(int value) { + return Math.max(0, value); + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + static class JsonData { + int userSource; + int groupType; + int selectedGroupId; + int quantifier; + + public JsonData(int userSource, int groupType, int selectedGroupId, int quantifier) { + this.userSource = userSource; + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.quantifier = quantifier; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java index 9eead216..d2349c84 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java @@ -1,28 +1,19 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; -import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -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.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotInTeam extends InteractionWiredCondition { +public class WiredConditionNotInTeam extends WiredConditionTeamMember { public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_IN_TEAM; - private GameTeamColors teamColor = GameTeamColors.RED; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotInTeam(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -37,96 +28,15 @@ public class WiredConditionNotInTeam extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return true; - for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboInfo().getGamePlayer() != null) { - if (habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor)) { - return false; - } - } + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.evaluateAllTargetsMatch(room, targets); } - return true; - } - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData( - this.teamColor, - this.userSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - try { - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.teamColor = data.teamColor; - this.userSource = data.userSource; - } else { - if (!wiredData.equals("")) - this.teamColor = GameTeamColors.values()[Integer.parseInt(wiredData)]; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } catch (Exception e) { - this.teamColor = GameTeamColors.RED; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.teamColor = GameTeamColors.RED; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.evaluateAnyTargetMatches(room, targets); } @Override public WiredConditionType 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(2); - message.appendInt(this.teamColor.type); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.teamColor = GameTeamColors.values()[settings.getIntParams()[0]]; - int[] params = settings.getIntParams(); - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - GameTeamColors teamColor; - int userSource; - - public JsonData(GameTeamColors teamColor, int userSource) { - this.teamColor = teamColor; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java index 2121bf97..e52315dc 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java @@ -22,7 +22,17 @@ public class WiredConditionNotMatchStatePosition extends WiredConditionMatchStat @Override public boolean evaluate(WiredContext ctx) { - return !super.evaluate(ctx); + this.refresh(); + + if (this.getMatchFurniSettings().isEmpty()) { + return false; + } + + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.evaluateAnyTargetMatches(ctx); + } + + return !this.evaluateAllTargetsMatch(ctx); } @Deprecated diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java index 4fe1d474..7b726e80 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java @@ -37,7 +37,11 @@ public class WiredConditionNotTriggerOnFurni extends WiredConditionTriggerOnFurn if (itemTargets.isEmpty()) return true; - return !isAnyUserOnFurni(userTargets, itemTargets, room); + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.isAnyUserOnFurni(userTargets, itemTargets, room); + } + + return !this.areAllUsersOnFurni(userTargets, itemTargets, room); } @Deprecated diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java new file mode 100644 index 00000000..87e09812 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java @@ -0,0 +1,213 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +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.WiredConditionType; +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.habbohotel.users.HabboItem; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionSelectionQuantity extends InteractionWiredCondition { + private static final int COMPARISON_LESS_THAN = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER_THAN = 2; + + private static final int SOURCE_GROUP_USERS = 0; + private static final int SOURCE_GROUP_FURNI = 1; + + public static final WiredConditionType type = WiredConditionType.SLC_QUANTITY; + + private int comparison = COMPARISON_EQUAL; + private int quantity = 0; + private int sourceGroup = SOURCE_GROUP_USERS; + private int sourceType = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredConditionSelectionQuantity(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionSelectionQuantity(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType 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(4); + message.appendInt(this.comparison); + message.appendInt(this.quantity); + message.appendInt(this.sourceGroup); + message.appendInt(this.sourceType); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL; + this.quantity = (params.length > 1) ? this.normalizeQuantity(params[1]) : 0; + this.sourceGroup = (params.length > 2) ? this.normalizeSourceGroup(params[2]) : SOURCE_GROUP_USERS; + this.sourceType = (params.length > 3) ? this.normalizeSourceType(this.sourceGroup, params[3]) : WiredSourceUtil.SOURCE_TRIGGER; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + int count = this.resolveCount(ctx); + + switch (this.comparison) { + case COMPARISON_LESS_THAN: + return count < this.quantity; + case COMPARISON_GREATER_THAN: + return count > this.quantity; + default: + return count == this.quantity; + } + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.quantity, + this.sourceGroup, + this.sourceType + )); + } + + @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) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.quantity = this.normalizeQuantity(data.quantity); + this.sourceGroup = this.normalizeSourceGroup(data.sourceGroup); + this.sourceType = this.normalizeSourceType(this.sourceGroup, data.sourceType); + return; + } + + String[] parts = wiredData.split("\t"); + + try { + if (parts.length > 0) { + this.comparison = this.normalizeComparison(Integer.parseInt(parts[0])); + } + if (parts.length > 1) { + this.quantity = this.normalizeQuantity(Integer.parseInt(parts[1])); + } + if (parts.length > 2) { + this.sourceGroup = this.normalizeSourceGroup(Integer.parseInt(parts[2])); + } + if (parts.length > 3) { + this.sourceType = this.normalizeSourceType(this.sourceGroup, Integer.parseInt(parts[3])); + } + } catch (NumberFormatException ignored) { + this.onPickUp(); + } + } + + @Override + public void onPickUp() { + this.comparison = COMPARISON_EQUAL; + this.quantity = 0; + this.sourceGroup = SOURCE_GROUP_USERS; + this.sourceType = WiredSourceUtil.SOURCE_TRIGGER; + } + + private int resolveCount(WiredContext ctx) { + if (this.sourceGroup == SOURCE_GROUP_FURNI) { + List items = WiredSourceUtil.resolveItems(ctx, this.sourceType, null); + + return items.size(); + } + + List users = WiredSourceUtil.resolveUsers(ctx, this.sourceType); + + return users.size(); + } + + private int normalizeComparison(int value) { + switch (value) { + case COMPARISON_LESS_THAN: + case COMPARISON_GREATER_THAN: + return value; + default: + return COMPARISON_EQUAL; + } + } + + private int normalizeQuantity(int value) { + return Math.max(0, Math.min(100, value)); + } + + private int normalizeSourceGroup(int value) { + return (value == SOURCE_GROUP_FURNI) ? SOURCE_GROUP_FURNI : SOURCE_GROUP_USERS; + } + + private int normalizeSourceType(int group, int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int comparison; + int quantity; + int sourceGroup; + int sourceType; + + public JsonData(int comparison, int quantity, int sourceGroup, int sourceType) { + this.comparison = comparison; + this.quantity = quantity; + this.sourceGroup = sourceGroup; + this.sourceType = sourceType; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java index 658f8b7d..4ca7b4e1 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java @@ -18,10 +18,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionTeamMember extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_IN_TEAM; private GameTeamColors teamColor = GameTeamColors.RED; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionTeamMember(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,17 +41,40 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyTargetMatches(room, targets); + } + + return this.evaluateAllTargetsMatch(room, targets); + } + + protected boolean evaluateAllTargetsMatch(Room room, List targets) { for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboInfo().getGamePlayer() != null) { - if (habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor)) { - return true; - } + if (!this.matchesTeam(room, roomUnit)) { + return false; } } + + return true; + } + + protected boolean evaluateAnyTargetMatches(Room room, List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesTeam(room, roomUnit)) { + return true; + } + } + return false; } + protected boolean matchesTeam(Room room, RoomUnit roomUnit) { + Habbo habbo = room.getHabbo(roomUnit); + return habbo != null + && habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor); + } + @Deprecated @Override public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { @@ -58,7 +85,8 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.teamColor, - this.userSource + this.userSource, + this.quantifier )); } @@ -71,14 +99,17 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.teamColor = data.teamColor; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { if (!wiredData.equals("")) this.teamColor = GameTeamColors.values()[Integer.parseInt(wiredData)]; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } catch (Exception e) { this.teamColor = GameTeamColors.RED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } } @@ -86,6 +117,7 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { public void onPickUp() { this.teamColor = GameTeamColors.RED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override @@ -101,9 +133,10 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.teamColor.type); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -113,20 +146,35 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.teamColor = GameTeamColors.values()[settings.getIntParams()[0]]; int[] params = settings.getIntParams(); + this.teamColor = GameTeamColors.values()[params[0]]; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ALL) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { GameTeamColors teamColor; int userSource; + Integer quantifier; - public JsonData(GameTeamColors teamColor, int userSource) { + public JsonData(GameTeamColors teamColor, int userSource, int quantifier) { this.teamColor = teamColor; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java index 560ac2d8..bcc3fa5c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java @@ -22,11 +22,15 @@ import java.util.List; import java.util.stream.Collectors; public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.TRIGGER_ON_FURNI; protected THashSet items = new THashSet<>(); protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionTriggerOnFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -38,8 +42,6 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { - Room room = ctx.room(); - this.refresh(); List userTargets = WiredSourceUtil.resolveUsers(ctx, this.userSource); @@ -50,7 +52,11 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { if (itemTargets.isEmpty()) return false; - return isAnyUserOnFurni(userTargets, itemTargets, room); + if (this.quantifier == QUANTIFIER_ANY) { + return this.isAnyUserOnFurni(userTargets, itemTargets, ctx.room()); + } + + return this.areAllUsersOnFurni(userTargets, itemTargets, ctx.room()); } @Deprecated @@ -70,13 +76,29 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { return false; } + protected boolean areAllUsersOnFurni(Collection users, Collection items, Room room) { + for (RoomUnit roomUnit : users) { + if (roomUnit == null) { + return false; + } + + THashSet itemsAtUser = room.getItemsAt(roomUnit.getCurrentLocation()); + if (itemsAtUser == null || items.stream().noneMatch(itemsAtUser::contains)) { + return false; + } + } + + return true; + } + @Override public String getWiredData() { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), this.furniSource, - this.userSource + this.userSource, + this.quantifier )); } @@ -89,6 +111,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.furniSource = data.furniSource; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -109,6 +132,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -120,6 +144,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { this.items.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override @@ -141,9 +166,10 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.furniSource); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -158,6 +184,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; this.items.clear(); @@ -194,6 +221,14 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { this.items.removeAll(items); } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + @Override public WiredConditionOperator operator() { return WiredConditionOperator.AND; @@ -203,11 +238,13 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { List itemIds; int furniSource; int userSource; + int quantifier; - public JsonData(List itemIds, int furniSource, int userSource) { + public JsonData(List itemIds, int furniSource, int userSource, int quantifier) { this.itemIds = itemIds; this.furniSource = furniSource; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java index 6cc5af2b..345fbb0c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java @@ -9,6 +9,7 @@ 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.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.ServerMessage; @@ -24,6 +25,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { private String botName = ""; private String botLook = ""; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotClothes(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -41,7 +43,8 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + ((char) 9) + "" + this.botLook); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -65,6 +68,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { if (data.length != 2) throw new WiredSaveException("Malformed data string. Invalid data length"); + this.botSource = (settings.getIntParams().length > 0) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[0]) : WiredBotSourceUtil.SOURCE_BOT_NAME; this.botName = data[0].substring(0, Math.min(data[0].length(), Emulator.getConfig().getInt("hotel.wired.message.max_length", 100))); this.botLook = data[1]; this.setDelay(delay); @@ -80,10 +84,9 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() == 1) { - Bot bot = bots.get(0); + for (Bot bot : bots) { bot.setFigure(this.botLook); } } @@ -96,7 +99,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.botLook, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.botLook, this.getDelay(), this.botSource)); } @Override @@ -108,6 +111,9 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.botLook = data.look; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -119,6 +125,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { } this.needsUpdate(true); + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -126,9 +133,15 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { public void onPickUp() { this.botLook = ""; this.botName = ""; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + public String getBotName() { return this.botName; } @@ -149,11 +162,13 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { String bot_name; String look; int delay; + Integer botSource; - public JsonData(String bot_name, String look, int delay) { + public JsonData(String bot_name, String look, int delay, int botSource) { this.bot_name = bot_name; this.look = look; this.delay = delay; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java index 6bdb028d..a1a80fe7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java @@ -11,6 +11,7 @@ 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.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -29,6 +30,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { private String botName = ""; private int mode = 0; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotFollowHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -46,9 +48,10 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.mode); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(1); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -79,6 +82,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { int mode = settings.getIntParams()[0]; this.userSource = settings.getIntParams()[1]; + this.botSource = (settings.getIntParams().length > 2) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[2]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -111,15 +115,15 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { RoomUnit roomUnit = targets.get(0); Habbo habbo = room.getHabbo(roomUnit); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (habbo != null && bots.size() == 1) { - Bot bot = bots.get(0); - - if (this.mode == 1) { - bot.startFollowingHabbo(habbo); - } else { - bot.stopFollowingHabbo(); + if (habbo != null && !bots.isEmpty()) { + for (Bot bot : bots) { + if (this.mode == 1) { + bot.startFollowingHabbo(habbo); + } else { + bot.stopFollowingHabbo(); + } } } } @@ -132,7 +136,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -145,6 +149,9 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.mode = data.mode; this.botName = data.bot_name; this.userSource = data.userSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -157,6 +164,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -165,12 +173,13 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.botName = ""; this.mode = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource); } static class JsonData { @@ -178,12 +187,14 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { int mode; int delay; int userSource; + Integer botSource; - public JsonData(String bot_name, int mode, int delay, int userSource) { + public JsonData(String bot_name, int mode, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.mode = mode; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java index c5350555..62fef4e0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java @@ -28,10 +28,12 @@ import java.util.List; public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.BOT_GIVE_HANDITEM; + private static final int BOT_SOURCE_NAME = 100; private String botName = ""; private int itemId; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = BOT_SOURCE_NAME; public WiredEffectBotGiveHandItem(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -49,9 +51,10 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.itemId); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -80,11 +83,9 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 2) throw new WiredSaveException("Missing item id"); - int itemId = settings.getIntParams()[0]; - this.userSource = settings.getIntParams()[1]; - - if(itemId < 0) - itemId = 0; + int itemId = this.normalizeHandItem(settings.getIntParams()[0]); + this.userSource = this.normalizeUserSource(settings.getIntParams()[1]); + this.botSource = (settings.getIntParams().length > 2) ? this.normalizeBotSource(settings.getIntParams()[2]) : BOT_SOURCE_NAME; String botName = settings.getStringParam(); @@ -113,10 +114,9 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { RoomUnit roomUnit = targets.get(0); Habbo habbo = room.getHabbo(roomUnit); - List bots = room.getBots(this.botName); + Bot bot = this.resolveBot(ctx, room); - if (habbo != null && bots.size() == 1) { - Bot bot = bots.get(0); + if (habbo != null && bot != null) { List tasks = new ArrayList<>(); tasks.add(new RoomUnitGiveHanditem(roomUnit, room, this.itemId)); @@ -146,7 +146,7 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.itemId, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.itemId, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -156,21 +156,25 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); - this.itemId = data.item_id; + this.itemId = this.normalizeHandItem(data.item_id); this.botName = data.bot_name; - this.userSource = data.userSource; + this.userSource = this.normalizeUserSource(data.userSource); + this.botSource = ((data.botSource == WiredSourceUtil.SOURCE_TRIGGER) && this.botName != null && !this.botName.isEmpty()) + ? BOT_SOURCE_NAME + : this.normalizeBotSource(data.botSource); } else { String[] data = wiredData.split(((char) 9) + ""); if (data.length == 3) { this.setDelay(Integer.parseInt(data[0])); - this.itemId = Integer.parseInt(data[1]); + this.itemId = this.normalizeHandItem(Integer.parseInt(data[1])); this.botName = data[2]; } this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; } } @@ -179,12 +183,13 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { this.botName = ""; this.itemId = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return (this.userSource == WiredSourceUtil.SOURCE_TRIGGER) || (this.botSource == WiredSourceUtil.SOURCE_TRIGGER); } static class JsonData { @@ -192,12 +197,58 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { int item_id; int delay; int userSource; + int botSource; - public JsonData(String bot_name, int item_id, int delay, int userSource) { + public JsonData(String bot_name, int item_id, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.item_id = item_id; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } + + private int normalizeHandItem(int value) { + return Math.max(0, value); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeBotSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case BOT_SOURCE_NAME: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return BOT_SOURCE_NAME; + } + } + + private Bot resolveBot(WiredContext ctx, Room room) { + if (this.botSource == BOT_SOURCE_NAME) { + List bots = room.getBots(this.botName); + return (bots.size() == 1) ? bots.get(0) : null; + } + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.botSource)) { + Bot bot = room.getBot(roomUnit); + + if (bot != null) { + return bot; + } + } + + return null; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java index 8301f508..dfb0aa88 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java @@ -10,6 +10,7 @@ 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.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.messages.ServerMessage; @@ -26,6 +27,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { private int mode; private String botName = ""; private String message = ""; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTalk(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -43,8 +45,9 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + "" + ((char) 9) + "" + this.message); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.mode); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -55,6 +58,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 1) throw new WiredSaveException("Mode is invalid"); int mode = settings.getIntParams()[0]; + this.botSource = (settings.getIntParams().length > 1) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -103,21 +107,20 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { .replace(Emulator.getTexts().getValue("wired.variable.points", "%points%"), habbo.getHabboInfo().getCurrencyAmount(Emulator.getConfig().getInt("seasonal.primary.type")) + "") .replace(Emulator.getTexts().getValue("wired.variable.owner", "%owner%"), room.getOwnerName()) .replace(Emulator.getTexts().getValue("wired.variable.item_count", "%item_count%"), room.itemCount() + "") - .replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), this.botName) .replace(Emulator.getTexts().getValue("wired.variable.roomname", "%roomname%"), room.getName()) .replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + ""); } - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() == 1) { - Bot bot = bots.get(0); + for (Bot bot : bots) { + String botMessage = message.replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), bot.getName()); - if(!WiredManager.triggerUserSays(room, bot.getRoomUnit(), message)) { + if(!WiredManager.triggerUserSays(room, bot.getRoomUnit(), botMessage)) { if (this.mode == 1) { - bot.shout(message); + bot.shout(botMessage); } else { - bot.talk(message); + bot.talk(botMessage); } } } @@ -131,7 +134,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.botSource)); } @Override @@ -144,6 +147,9 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { this.mode = data.mode; this.botName = data.bot_name; this.message = data.message; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -156,6 +162,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { } this.needsUpdate(true); + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -164,9 +171,15 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { this.mode = 0; this.botName = ""; this.message = ""; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + public int getMode() { return this.mode; } @@ -201,12 +214,14 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { int mode; String message; int delay; + Integer botSource; - public JsonData(String bot_name, int mode, String message, int delay) { + public JsonData(String bot_name, int mode, String message, int delay, int botSource) { this.bot_name = bot_name; this.mode = mode; this.message = message; this.delay = delay; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java index c7dc2eb5..3532b276 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java @@ -11,6 +11,7 @@ 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.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -31,6 +32,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { private String botName = ""; private String message = ""; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTalkToHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -48,9 +50,10 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + "" + ((char) 9) + "" + this.message); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.mode); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -80,6 +83,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { if(settings.getIntParams().length < 2) throw new WiredSaveException("Missing mode"); int mode = settings.getIntParams()[0]; this.userSource = settings.getIntParams()[1]; + this.botSource = (settings.getIntParams().length > 2) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[2]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -116,9 +120,8 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); - if (bots.size() != 1) return; - Bot bot = bots.get(0); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); + if (bots.isEmpty()) return; for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) { Habbo habbo = room.getHabbo(roomUnit); @@ -131,15 +134,18 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { .replace(Emulator.getTexts().getValue("wired.variable.points", "%points%"), habbo.getHabboInfo().getCurrencyAmount(Emulator.getConfig().getInt("seasonal.primary.type")) + "") .replace(Emulator.getTexts().getValue("wired.variable.owner", "%owner%"), room.getOwnerName()) .replace(Emulator.getTexts().getValue("wired.variable.item_count", "%item_count%"), room.itemCount() + "") - .replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), this.botName) .replace(Emulator.getTexts().getValue("wired.variable.roomname", "%roomname%"), room.getName()) .replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + ""); - if (!WiredManager.triggerUserSays(room, bot.getRoomUnit(), m)) { - if (this.mode == 1) { - bot.whisper(m, habbo); - } else { - bot.talk(habbo.getHabboInfo().getUsername() + ": " + m); + for (Bot bot : bots) { + String botMessage = m.replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), bot.getName()); + + if (!WiredManager.triggerUserSays(room, bot.getRoomUnit(), botMessage)) { + if (this.mode == 1) { + bot.whisper(botMessage, habbo); + } else { + bot.talk(habbo.getHabboInfo().getUsername() + ": " + botMessage); + } } } } @@ -153,7 +159,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -167,6 +173,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.botName = data.bot_name; this.message = data.message; this.userSource = data.userSource; + this.botSource = (data.botSource != null) ? WiredBotSourceUtil.normalizeBotSource(data.botSource) : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -180,6 +187,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -189,12 +197,13 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.message = ""; this.mode = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource); } static class JsonData { @@ -203,13 +212,15 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { String message; int delay; int userSource; + Integer botSource; - public JsonData(String bot_name, int mode, String message, int delay, int userSource) { + public JsonData(String bot_name, int mode, String message, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.mode = mode; this.message = message; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java index 650877d7..582a8f19 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java @@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.rooms.RoomTileState; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -34,6 +35,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { private THashSet items; private String botName = ""; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTeleport(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -110,8 +112,9 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -123,6 +126,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { String botName = settings.getStringParam(); int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = (params.length > 1) ? WiredBotSourceUtil.normalizeBotSource(params[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; int itemsCount = settings.getFurniIds().length; if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { @@ -177,20 +181,20 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { if (validItems.isEmpty()) return; if (room.getLayout() == null) return; - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() != 1) { + if (bots.isEmpty()) { return; } - Bot bot = bots.get(0); + for (Bot bot : bots) { + HabboItem targetItem = validItems.get(Emulator.getRandom().nextInt(validItems.size())); - HabboItem targetItem = validItems.get(Emulator.getRandom().nextInt(validItems.size())); - - if (targetItem.getRoomId() == bot.getRoom().getId()) { - RoomTile tile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); - if (tile != null) { - teleportUnitToTile(bot.getRoomUnit(), tile); + if (targetItem.getRoomId() == bot.getRoom().getId()) { + RoomTile tile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); + if (tile != null) { + teleportUnitToTile(bot.getRoomUnit(), tile); + } } } } @@ -214,7 +218,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { } } - return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource, this.botSource)); } @Override @@ -228,6 +232,9 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.furniSource = data.furniSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; for(int itemId : data.items) { HabboItem item = room.getHabboItem(itemId); @@ -260,6 +267,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.needsUpdate(true); this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -268,20 +276,28 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.botName = ""; this.items.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + static class JsonData { String bot_name; List items; int delay; int furniSource; + Integer botSource; - public JsonData(String bot_name, List items, int delay, int furniSource) { + public JsonData(String bot_name, List items, int delay, int furniSource, int botSource) { this.bot_name = bot_name; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java index b8a2f059..bb345e12 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java @@ -10,6 +10,7 @@ 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.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -29,6 +30,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { private List items; private String botName = ""; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotWalkToFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -62,8 +64,9 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -75,6 +78,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { String botName = settings.getStringParam(); int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = (params.length > 1) ? WiredBotSourceUtil.normalizeBotSource(params[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; int itemsCount = settings.getFurniIds().length; if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { @@ -118,32 +122,30 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { this.items.removeIf(item -> item == null || item.getRoomId() != this.getRoomId() || Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null); } - if (effectiveItems.isEmpty() || bots.size() != 1) { + if (effectiveItems.isEmpty() || bots.isEmpty()) { return; } - Bot bot = bots.get(0); + for (Bot bot : bots) { + List possibleItems = effectiveItems.stream() + .filter(item -> !room.getBotsOnItem(item).contains(bot)) + .collect(Collectors.toList()); - // Bots shouldn't walk to the tile they are already standing on - List possibleItems = effectiveItems.stream() - .filter(item -> !room.getBotsOnItem(item).contains(bot)) - .collect(Collectors.toList()); + if (possibleItems.isEmpty()) { + continue; + } - // Get a random tile of possible tiles to walk to - if (possibleItems.size() > 0) { HabboItem item = possibleItems.get(Emulator.getRandom().nextInt(possibleItems.size())); - if (item.getRoomId() != 0 && item.getRoomId() == bot.getRoom().getId()) { - if (room.getLayout() != null) { - bot.getRoomUnit().setGoalLocation(room.getLayout().getTile(item.getX(), item.getY())); - } + if (item.getRoomId() != 0 && item.getRoomId() == bot.getRoom().getId() && room.getLayout() != null) { + bot.getRoomUnit().setGoalLocation(room.getLayout().getTile(item.getX(), item.getY())); } } } @@ -166,7 +168,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { } } - return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource, this.botSource)); } @Override @@ -180,6 +182,9 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.furniSource = data.furniSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; for(int itemId : data.items) { HabboItem item = room.getHabboItem(itemId); @@ -212,6 +217,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.needsUpdate(true); this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -220,20 +226,28 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.items.clear(); this.botName = ""; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + static class JsonData { String bot_name; List items; int delay; int furniSource; + Integer botSource; - public JsonData(String bot_name, List items, int delay, int furniSource) { + public JsonData(String bot_name, List items, int delay, int furniSource, int botSource) { this.bot_name = bot_name; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.botSource = botSource; } } } 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 529f206b..fa9b5182 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 @@ -38,6 +38,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { private RoomUserRotation startRotation = RoomUserRotation.NORTH; private int blockedAction = 0; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean blockOnUserCollision = false; public WiredEffectChangeFurniDirection(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -90,7 +91,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { RoomTile targetTile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); int count = 1; - while ((targetTile == null || targetTile.state == RoomTileState.INVALID || room.furnitureFitsAt(targetTile, item, item.getRotation(), false) != FurnitureMovementError.NONE) && count < 8) { + while (this.shouldSearchNextDirection(room, item, targetTile) && count < 8) { entry.getValue().direction = this.nextRotation(entry.getValue().direction); RoomTile tile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); @@ -157,7 +158,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { @Override public String getWiredData() { ArrayList settings = new ArrayList<>(this.items.values()); - return WiredManager.getGson().toJson(new JsonData(this.startRotation, this.blockedAction, settings, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.startRotation, this.blockedAction, settings, this.getDelay(), this.furniSource, this.blockOnUserCollision)); } @Override @@ -173,6 +174,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { this.startRotation = data.start_direction; this.blockedAction = data.blocked_action; this.furniSource = data.furniSource; + this.blockOnUserCollision = data.blockOnUserCollision; for(WiredChangeDirectionSetting setting : data.items) { HabboItem item = room.getHabboItem(setting.item_id); @@ -217,6 +219,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.blockOnUserCollision = false; this.needsUpdate(true); } } @@ -228,6 +231,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { this.blockedAction = 0; this.startRotation = RoomUserRotation.NORTH; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.blockOnUserCollision = false; } @Override @@ -246,10 +250,11 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(3); + message.appendInt(4); message.appendInt(this.startRotation != null ? this.startRotation.getValue() : 0); message.appendInt(this.blockedAction); message.appendInt(this.furniSource); + message.appendInt(this.blockOnUserCollision ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -262,7 +267,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { int startDirectionInt = settings.getIntParams()[0]; - if(startDirectionInt < 0 || startDirectionInt > 7 || (startDirectionInt % 2) != 0) { + if(startDirectionInt < 0 || startDirectionInt > 7) { throw new WiredSaveException("Start direction is invalid"); } @@ -270,6 +275,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { int blockedActionInt = settings.getIntParams()[1]; this.furniSource = settings.getIntParams()[2]; + this.blockOnUserCollision = settings.getIntParams().length > 3 && settings.getIntParams()[3] == 1; if(blockedActionInt < 0 || blockedActionInt > 6) { throw new WiredSaveException("Blocked action is invalid"); @@ -309,6 +315,23 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { return true; } + private boolean shouldSearchNextDirection(Room room, HabboItem item, RoomTile targetTile) { + if (targetTile == null || targetTile.state == RoomTileState.INVALID) { + return true; + } + + if (room.furnitureFitsAt(targetTile, item, item.getRotation(), false) != FurnitureMovementError.NONE) { + return true; + } + + if (this.blockOnUserCollision) { + return false; + } + + FurnitureMovementError unitCollision = room.furnitureFitsAt(targetTile, item, item.getRotation(), true); + return unitCollision == FurnitureMovementError.TILE_HAS_HABBOS; + } + private RoomUserRotation nextRotation(RoomUserRotation currentRotation) { switch (this.blockedAction) { case ACTION_TURN_BACK: @@ -340,13 +363,15 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { List items; int delay; int furniSource; + boolean blockOnUserCollision; - public JsonData(RoomUserRotation start_direction, int blocked_action, List items, int delay, int furniSource) { + public JsonData(RoomUserRotation start_direction, int blocked_action, List items, int delay, int furniSource, boolean blockOnUserCollision) { this.start_direction = start_direction; this.blocked_action = blocked_action; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.blockOnUserCollision = blockOnUserCollision; } } } 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 bdaa461b..6102fa14 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 @@ -15,6 +15,7 @@ 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 com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import java.sql.ResultSet; import java.sql.SQLException; @@ -64,9 +65,18 @@ public class WiredEffectFurniToFurni extends InteractionWiredEffect { 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); + RoomTile oldLocation = room.getLayout().getTile(moveItem.getX(), moveItem.getY()); + double oldZ = moveItem.getZ(); + + FurnitureMovementError error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), null, false, false); + if (error == FurnitureMovementError.NONE) { + this.sendRollerAnimation(room, moveItem, oldLocation, oldZ, targetTile); + return; + } + + error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, false, false); + if (error == FurnitureMovementError.NONE) { + this.sendRollerAnimation(room, moveItem, oldLocation, oldZ, targetTile); } } @@ -222,6 +232,14 @@ 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 62df4762..462fbddf 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,6 +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 java.sql.ResultSet; import java.sql.SQLException; @@ -38,9 +39,20 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { 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); + RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); + double oldZ = item.getZ(); + + FurnitureMovementError error = room.moveFurniTo(item, targetTile, item.getRotation(), null, false, false); + 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); + if (error == FurnitureMovementError.NONE) { + this.sendRollerAnimation(room, item, oldLocation, oldZ, targetTile); + } } } @@ -54,4 +66,12 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { public WiredEffectType getType() { 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/WiredEffectGiveHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java index b8d1ceaa..e249a9ac 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java @@ -20,7 +20,7 @@ public class WiredEffectGiveHandItem extends WiredEffectWhisper { @Override public void execute(WiredContext ctx) { try { - int itemId = Integer.parseInt(this.message); + int itemId = Math.max(0, Integer.parseInt(this.message)); Room room = ctx.room(); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java index b2b07b4d..546f721d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java @@ -16,27 +16,22 @@ import com.eu.habbo.habbohotel.wired.core.WiredContext; 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.iterator.TObjectIntIterator; -import gnu.trove.map.TObjectIntMap; -import gnu.trove.map.hash.TObjectIntHashMap; import gnu.trove.procedure.TObjectProcedure; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class WiredEffectGiveScore extends InteractionWiredEffect { + private static final int OPERATION_ADD = 0; + private static final int OPERATION_REMOVE = 1; public static final WiredEffectType type = WiredEffectType.GIVE_SCORE; private int score; - private int count; + private int operation = OPERATION_ADD; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - private TObjectIntMap> data = new TObjectIntHashMap<>(); - public WiredEffectGiveScore(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -58,45 +53,8 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if (game == null) continue; - int gameStartTime = game.getStartTime(); - - TObjectIntMap> dataClone = new TObjectIntHashMap<>(this.data); - - TObjectIntIterator> iterator = dataClone.iterator(); - - boolean alreadyCounted = false; - for (int i = dataClone.size(); i-- > 0; ) { - iterator.advance(); - - Map.Entry map = iterator.key(); - - if (map.getValue() == habbo.getHabboInfo().getId()) { - if (map.getKey() == gameStartTime) { - if (iterator.value() < this.count) { - iterator.setValue(iterator.value() + 1); - - habbo.getHabboInfo().getGamePlayer().addScore(this.score, true); - - alreadyCounted = true; - break; - } - } else { - iterator.remove(); - } - } - } - - if (!alreadyCounted) { - try { - this.data.put(new AbstractMap.SimpleEntry<>(gameStartTime, habbo.getHabboInfo().getId()), 1); - } - catch(IllegalArgumentException e) { - - } - - if (habbo.getHabboInfo().getGamePlayer() != null) { - habbo.getHabboInfo().getGamePlayer().addScore(this.score, true); - } + if (habbo.getHabboInfo().getGamePlayer() != null) { + habbo.getHabboInfo().getGamePlayer().addScore(this.getAppliedAmount(), true); } } } @@ -109,7 +67,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.score, this.count, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.score, this.operation, this.getDelay(), this.userSource)); } @Override @@ -119,7 +77,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.score = data.score; - this.count = data.count; + this.operation = this.normalizeOperation(data.operation); this.setDelay(data.delay); this.userSource = data.userSource; } @@ -128,7 +86,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if (data.length == 3) { this.score = Integer.parseInt(data[0]); - this.count = Integer.parseInt(data[1]); + this.operation = OPERATION_ADD; this.setDelay(Integer.parseInt(data[2])); } @@ -140,7 +98,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { @Override public void onPickUp() { this.score = 0; - this.count = 0; + this.operation = OPERATION_ADD; this.setDelay(0); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -160,7 +118,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { message.appendString(""); message.appendInt(3); message.appendInt(this.score); - message.appendInt(this.count); + message.appendInt(this.operation); message.appendInt(this.userSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -195,10 +153,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if(score < 1 || score > 100) throw new WiredSaveException("Score is invalid"); - int timesPerGame = settings.getIntParams()[1]; - - if(timesPerGame < 1 || timesPerGame > 10) - throw new WiredSaveException("Times per game is invalid"); + int operation = this.normalizeOperation(settings.getIntParams()[1]); this.userSource = settings.getIntParams()[2]; int delay = settings.getDelay(); @@ -207,7 +162,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.score = score; - this.count = timesPerGame; + this.operation = operation; this.setDelay(delay); return true; @@ -218,15 +173,23 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; } + private int normalizeOperation(int value) { + return (value == OPERATION_REMOVE) ? OPERATION_REMOVE : OPERATION_ADD; + } + + private int getAppliedAmount() { + return (this.operation == OPERATION_REMOVE) ? -this.score : this.score; + } + static class JsonData { int score; - int count; + int operation; int delay; int userSource; - public JsonData(int score, int count, int delay, int userSource) { + public JsonData(int score, int operation, int delay, int userSource) { this.score = score; - this.count = count; + this.operation = operation; this.delay = delay; this.userSource = userSource; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java index 7b2c0efd..e9ff423a 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java @@ -16,20 +16,19 @@ import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import gnu.trove.map.hash.TIntIntHashMap; import java.sql.ResultSet; import java.sql.SQLException; public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { + private static final int OPERATION_ADD = 0; + private static final int OPERATION_REMOVE = 1; public static final WiredEffectType type = WiredEffectType.GIVE_SCORE_TEAM; private int points; - private int count; + private int operation = OPERATION_ADD; private GameTeamColors teamColor = GameTeamColors.RED; - private TIntIntHashMap startTimes = new TIntIntHashMap(); - public WiredEffectGiveScoreToTeam(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { super(id, userId, item, extradata, limitedStack, limitedSells); } @@ -43,16 +42,10 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { Room room = ctx.room(); for (Game game : room.getGames()) { if (game != null && game.state.equals(GameState.RUNNING)) { - int c = this.startTimes.get(game.getStartTime()); + GameTeam team = game.getTeam(this.teamColor); - if (c < this.count) { - GameTeam team = game.getTeam(this.teamColor); - - if (team != null) { - team.addTeamScore(this.points); - - this.startTimes.put(game.getStartTime(), c + 1); - } + if (team != null) { + team.addTeamScore(this.getAppliedAmount(team)); } } } @@ -66,7 +59,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.points, this.count, this.teamColor, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.points, this.operation, this.teamColor, this.getDelay())); } @Override @@ -76,7 +69,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.points = data.score; - this.count = data.count; + this.operation = this.normalizeOperation(data.operation); this.teamColor = data.team; this.setDelay(data.delay); } @@ -85,8 +78,8 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if (data.length == 4) { this.points = Integer.parseInt(data[0]); - this.count = Integer.parseInt(data[1]); - this.teamColor = GameTeamColors.values()[Integer.parseInt(data[2])]; + this.operation = OPERATION_ADD; + this.teamColor = GameTeamColors.fromType(Integer.parseInt(data[2])); this.setDelay(Integer.parseInt(data[3])); } @@ -96,9 +89,8 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { @Override public void onPickUp() { - this.startTimes.clear(); this.points = 0; - this.count = 0; + this.operation = OPERATION_ADD; this.teamColor = GameTeamColors.RED; this.setDelay(0); } @@ -118,7 +110,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { message.appendString(""); message.appendInt(3); message.appendInt(this.points); - message.appendInt(this.count); + message.appendInt(this.operation); message.appendInt(this.teamColor.type); message.appendInt(0); message.appendInt(this.getType().code); @@ -135,10 +127,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if(points < 1 || points > 100) throw new WiredSaveException("Points is invalid"); - int timesPerGame = settings.getIntParams()[1]; - - if(timesPerGame < 1 || timesPerGame > 10) - throw new WiredSaveException("Times per game is invalid"); + int operation = this.normalizeOperation(settings.getIntParams()[1]); int team = settings.getIntParams()[2]; @@ -151,22 +140,34 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.points = points; - this.count = timesPerGame; - this.teamColor = GameTeamColors.values()[team]; + this.operation = operation; + this.teamColor = GameTeamColors.fromType(team); this.setDelay(delay); return true; } + private int normalizeOperation(int value) { + return (value == OPERATION_REMOVE) ? OPERATION_REMOVE : OPERATION_ADD; + } + + private int getAppliedAmount(GameTeam team) { + if (this.operation != OPERATION_REMOVE) { + return this.points; + } + + return -Math.min(this.points, team.getTeamScore()); + } + static class JsonData { int score; - int count; + int operation; GameTeamColors team; int delay; - public JsonData(int score, int count, GameTeamColors team, int delay) { + public JsonData(int score, int operation, GameTeamColors team, int delay) { this.score = score; - this.count = count; + this.operation = operation; this.team = team; this.delay = delay; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java index 82b94846..525f0fc5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java @@ -2,6 +2,8 @@ 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.games.battlebanzai.BattleBanzaiGame; +import com.eu.habbo.habbohotel.games.freeze.FreezeGame; import com.eu.habbo.habbohotel.games.Game; import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.games.wired.WiredGame; @@ -26,9 +28,13 @@ import java.util.ArrayList; import java.util.List; public class WiredEffectJoinTeam extends InteractionWiredEffect { + private static final int TEAM_TYPE_WIRED = 0; + private static final int TEAM_TYPE_BANZAI = 1; + private static final int TEAM_TYPE_FREEZE = 2; public static final WiredEffectType type = WiredEffectType.JOIN_TEAM; private GameTeamColors teamColor = GameTeamColors.RED; + private int teamType = TEAM_TYPE_WIRED; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredEffectJoinTeam(ResultSet set, Item baseItem) throws SQLException { @@ -42,20 +48,30 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); + Class targetGameType = this.resolveGameType(); for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) { Habbo habbo = room.getHabbo(unit); if (habbo == null) continue; - WiredGame game = (WiredGame) room.getGameOrCreate(WiredGame.class); + Game currentGame = null; + if (habbo.getHabboInfo().getCurrentGame() != null) { + currentGame = room.getGame(habbo.getHabboInfo().getCurrentGame()); + } - if (habbo.getHabboInfo().getGamePlayer() != null && habbo.getHabboInfo().getCurrentGame() != null && (habbo.getHabboInfo().getCurrentGame() != WiredGame.class || (habbo.getHabboInfo().getCurrentGame() == WiredGame.class && habbo.getHabboInfo().getGamePlayer().getTeamColor() != this.teamColor))) { - // remove from current game - Game currentGame = room.getGame(habbo.getHabboInfo().getCurrentGame()); + if (habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getCurrentGame() != null + && (habbo.getHabboInfo().getCurrentGame() != targetGameType + || habbo.getHabboInfo().getGamePlayer().getTeamColor() != this.teamColor) + && currentGame != null) { currentGame.removeHabbo(habbo); } if(habbo.getHabboInfo().getGamePlayer() == null) { + Game game = room.getGameOrCreate(targetGameType); + if (game == null) { + continue; + } game.addHabbo(habbo, this.teamColor); } } @@ -69,7 +85,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.teamColor, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.teamColor, this.teamType, this.getDelay(), this.userSource)); } @Override @@ -80,6 +96,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); this.teamColor = data.team; + this.teamType = this.normalizeTeamType(data.teamType); this.userSource = data.userSource; } else { @@ -89,11 +106,12 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { this.setDelay(Integer.parseInt(data[0])); if (data.length >= 2) { - this.teamColor = GameTeamColors.values()[Integer.parseInt(data[1])]; + this.teamColor = GameTeamColors.fromType(Integer.parseInt(data[1])); } } this.needsUpdate(true); + this.teamType = TEAM_TYPE_WIRED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } } @@ -101,6 +119,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public void onPickUp() { this.teamColor = GameTeamColors.RED; + this.teamType = TEAM_TYPE_WIRED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -118,7 +137,8 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); + message.appendInt(this.teamType); message.appendInt(this.teamColor.type); message.appendInt(this.userSource); message.appendInt(0); @@ -149,8 +169,15 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 2) throw new WiredSaveException("invalid data"); - int team = settings.getIntParams()[0]; - this.userSource = settings.getIntParams()[1]; + if (settings.getIntParams().length > 2) { + this.teamType = this.normalizeTeamType(settings.getIntParams()[0]); + this.userSource = settings.getIntParams()[2]; + } else { + this.teamType = TEAM_TYPE_WIRED; + this.userSource = settings.getIntParams()[1]; + } + + int team = (settings.getIntParams().length > 2) ? settings.getIntParams()[1] : settings.getIntParams()[0]; if(team < 1 || team > 4) throw new WiredSaveException("Team is invalid"); @@ -160,7 +187,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { if(delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) throw new WiredSaveException("Delay too long"); - this.teamColor = GameTeamColors.values()[team]; + this.teamColor = GameTeamColors.fromType(team); this.setDelay(delay); return true; @@ -171,13 +198,34 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; } + private int normalizeTeamType(int value) { + if (value == TEAM_TYPE_BANZAI || value == TEAM_TYPE_FREEZE) { + return value; + } + + return TEAM_TYPE_WIRED; + } + + private Class resolveGameType() { + switch (this.teamType) { + case TEAM_TYPE_BANZAI: + return BattleBanzaiGame.class; + case TEAM_TYPE_FREEZE: + return FreezeGame.class; + default: + return WiredGame.class; + } + } + static class JsonData { GameTeamColors team; + int teamType; int delay; int userSource; - public JsonData(GameTeamColors team, int delay, int userSource) { + public JsonData(GameTeamColors team, int teamType, int delay, int userSource) { this.team = team; + this.teamType = teamType; this.delay = delay; this.userSource = userSource; } 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 e075f486..894058cb 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 @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import java.math.BigDecimal; public class WiredEffectMatchFurni extends InteractionWiredEffect implements InteractionWiredMatchFurniSettings { private static final Logger LOGGER = LoggerFactory.getLogger(WiredEffectMatchFurni.class); @@ -35,6 +36,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int private boolean state = false; private boolean direction = false; private boolean position = false; + private boolean altitude = false; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredEffectMatchFurni(ResultSet set, Item baseItem) throws SQLException { @@ -122,13 +124,20 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int room.moveFurniTo(item, oldLocation, setting.rotation, null, true); } } + 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); + } + } else if(this.position) { boolean slideAnimation = !this.direction || item.getRotation() == setting.rotation; RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); 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, null, !slideAnimation) == 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()); } @@ -146,7 +155,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int @Override public String getWiredData() { this.refresh(); - return WiredManager.getGson().toJson(new JsonData(this.state, this.direction, this.position, new ArrayList(this.settings), this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.state, this.direction, this.position, this.altitude, new ArrayList(this.settings), this.getDelay(), this.furniSource)); } @Override @@ -159,6 +168,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = data.state; this.direction = data.direction; this.position = data.position; + this.altitude = data.altitude; this.settings.clear(); this.settings.addAll(data.items); this.furniSource = data.furniSource; @@ -175,7 +185,9 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int String[] stuff = items[i].split(Pattern.quote("-")); - if (stuff.length >= 5) { + if (stuff.length >= 6) { + this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]), Double.parseDouble(stuff[5]))); + } else if (stuff.length >= 5) { this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]))); } @@ -187,6 +199,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = data[2].equals("1"); this.direction = data[3].equals("1"); this.position = data[4].equals("1"); + this.altitude = false; this.setDelay(Integer.parseInt(data[5])); this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.needsUpdate(true); @@ -199,6 +212,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = false; this.direction = false; this.position = false; + this.altitude = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -222,10 +236,11 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(5); message.appendInt(this.state ? 1 : 0); message.appendInt(this.direction ? 1 : 0); message.appendInt(this.position ? 1 : 0); + message.appendInt(this.altitude ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -239,7 +254,8 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int boolean setState = settings.getIntParams()[0] == 1; boolean setDirection = settings.getIntParams()[1] == 1; boolean setPosition = settings.getIntParams()[2] == 1; - this.furniSource = settings.getIntParams()[3]; + boolean setAltitude = (settings.getIntParams().length > 4) ? (settings.getIntParams()[3] == 1) : false; + this.furniSource = (settings.getIntParams().length > 4) ? settings.getIntParams()[4] : settings.getIntParams()[3]; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -261,7 +277,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int if(it == null) throw new WiredSaveException(String.format("Item %s not found", itemId)); - newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY())); + newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY(), it.getZ())); } int delay = settings.getDelay(); @@ -272,6 +288,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = setState; this.direction = setDirection; this.position = setPosition; + this.altitude = setAltitude; this.settings.clear(); this.settings.addAll(newSettings); this.setDelay(delay); @@ -308,18 +325,25 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int return this.position; } + @Override + public boolean shouldMatchAltitude() { + return this.altitude; + } + static class JsonData { boolean state; boolean direction; boolean position; + boolean altitude; List items; int delay; int furniSource; - public JsonData(boolean state, boolean direction, boolean position, List items, int delay, int furniSource) { + public JsonData(boolean state, boolean direction, boolean position, boolean altitude, List items, int delay, int furniSource) { this.state = state; this.direction = direction; this.position = position; + this.altitude = altitude; this.items = items; this.delay = delay; this.furniSource = furniSource; 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 0b4cbb55..b9f43fc3 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 @@ -352,27 +352,32 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement * @return direction */ private RoomUserRotation getMovementDirection() { - RoomUserRotation movemementDirection = RoomUserRotation.NORTH; - if (this.direction == 1) { - movemementDirection = RoomUserRotation.values()[Emulator.getRandom().nextInt(RoomUserRotation.values().length / 2) * 2]; - } else if (this.direction == 2) { - if (Emulator.getRandom().nextInt(2) == 1) { - movemementDirection = RoomUserRotation.EAST; - } else { - movemementDirection = RoomUserRotation.WEST; - } - } else if (this.direction == 3) { - if (Emulator.getRandom().nextInt(2) != 1) { - movemementDirection = RoomUserRotation.SOUTH; - } - } else if (this.direction == 4) { - movemementDirection = RoomUserRotation.SOUTH; - } else if (this.direction == 5) { - movemementDirection = RoomUserRotation.EAST; - } else if (this.direction == 7) { - movemementDirection = RoomUserRotation.WEST; + switch (this.direction) { + case 1: + return RoomUserRotation.values()[Emulator.getRandom().nextInt(RoomUserRotation.values().length / 2) * 2]; + case 2: + return Emulator.getRandom().nextInt(2) == 1 ? RoomUserRotation.EAST : RoomUserRotation.WEST; + case 3: + return Emulator.getRandom().nextInt(2) == 1 ? RoomUserRotation.NORTH : RoomUserRotation.SOUTH; + case 4: + return RoomUserRotation.SOUTH; + case 5: + return RoomUserRotation.EAST; + case 6: + return RoomUserRotation.NORTH; + case 7: + return RoomUserRotation.WEST; + case 8: + return RoomUserRotation.NORTH_EAST; + case 9: + return RoomUserRotation.SOUTH_EAST; + case 10: + return RoomUserRotation.SOUTH_WEST; + case 11: + return RoomUserRotation.NORTH_WEST; + default: + return RoomUserRotation.NORTH; } - return movemementDirection; } @Override 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 new file mode 100644 index 00000000..4ef1c57f --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java @@ -0,0 +1,288 @@ +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.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomTileState; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +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.WiredSourceUtil; +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; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class WiredEffectMoveRotateUser extends InteractionWiredEffect { + private static final int ROTATION_CLOCKWISE = 8; + private static final int ROTATION_COUNTER_CLOCKWISE = 9; + + public static final WiredEffectType type = WiredEffectType.MOVE_ROTATE_USER; + + private int movementDirection = -1; + private int rotationDirection = -1; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectMoveRotateUser(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectMoveRotateUser(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 (roomUnit == null || roomUnit.getRoom() != room) { + continue; + } + + boolean hasRotation = this.rotationDirection >= 0; + 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)); + } + + if (canMove) { + double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0); + room.teleportRoomUnitToLocation(roomUnit, targetTile.x, targetTile.y, targetZ); + continue; + } + + if (hasRotation) { + room.sendComposer(new RoomUserStatusComposer(roomUnit).compose()); + } + } + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + this.movementDirection, + this.rotationDirection, + 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.movementDirection = this.normalizeDirection(data.movementDirection); + this.rotationDirection = this.normalizeRotation(data.rotationDirection); + this.userSource = data.userSource; + return; + } + + this.setDelay(0); + this.movementDirection = -1; + this.rotationDirection = -1; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onPickUp() { + this.setDelay(0); + this.movementDirection = -1; + this.rotationDirection = -1; + 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.movementDirection); + message.appendInt(this.rotationDirection); + message.appendInt(this.userSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + + if (this.requiresTriggeringUser()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @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 delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + this.movementDirection = this.normalizeDirection(settings.getIntParams()[0]); + this.rotationDirection = this.normalizeRotation(settings.getIntParams()[1]); + this.userSource = settings.getIntParams()[2]; + this.setDelay(delay); + + return true; + } + + @Override + public boolean requiresTriggeringUser() { + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + protected long requiredCooldown() { + return COOLDOWN_MOVEMENT; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeDirection(int direction) { + return (direction >= 0 && direction <= 7) ? direction : -1; + } + + private int normalizeRotation(int rotation) { + return ((rotation >= 0 && rotation <= 7) || rotation == ROTATION_CLOCKWISE || rotation == ROTATION_COUNTER_CLOCKWISE) ? rotation : -1; + } + + private RoomUserRotation getTargetRotation(RoomUnit roomUnit) { + RoomUserRotation currentRotation = (roomUnit != null && roomUnit.getBodyRotation() != null) ? roomUnit.getBodyRotation() : RoomUserRotation.NORTH; + + if (this.rotationDirection == ROTATION_CLOCKWISE) { + return RoomUserRotation.clockwise(currentRotation); + } + + if (this.rotationDirection == ROTATION_COUNTER_CLOCKWISE) { + return RoomUserRotation.counterClockwise(currentRotation); + } + + return RoomUserRotation.fromValue(this.rotationDirection); + } + + 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; + } + + int deltaX = 0; + int deltaY = 0; + + switch (RoomUserRotation.fromValue(direction)) { + case NORTH: + deltaY = 1; + break; + case NORTH_EAST: + deltaX = 1; + deltaY = 1; + break; + case EAST: + deltaX = 1; + break; + case SOUTH_EAST: + deltaX = 1; + deltaY = -1; + break; + case SOUTH: + deltaY = -1; + break; + case SOUTH_WEST: + deltaX = -1; + deltaY = -1; + break; + case WEST: + deltaX = -1; + break; + case NORTH_WEST: + deltaX = -1; + deltaY = 1; + break; + } + + return room.getLayout().getTile((short) (currentTile.x + deltaX), (short) (currentTile.y + deltaY)); + } + + private boolean canMoveTo(Room room, RoomUnit roomUnit, RoomTile targetTile) { + if (targetTile == null || targetTile.state == RoomTileState.INVALID || targetTile.state == RoomTileState.BLOCKED) { + return false; + } + + if (!room.tileWalkable(targetTile)) { + return false; + } + + for (RoomUnit unit : room.getRoomUnitsAt(targetTile)) { + if (unit != null && unit != roomUnit) { + return false; + } + } + + return true; + } + + static class JsonData { + int delay; + int movementDirection; + int rotationDirection; + int userSource; + + public JsonData(int delay, int movementDirection, int rotationDirection, int userSource) { + this.delay = delay; + this.movementDirection = movementDirection; + this.rotationDirection = rotationDirection; + this.userSource = userSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java index 03ecbf13..591880ac 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java @@ -39,6 +39,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.TELEPORT; protected List items; + private boolean fastTeleport = false; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; @@ -53,6 +54,10 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } public static void teleportUnitToTile(RoomUnit roomUnit, RoomTile tile) { + teleportUnitToTile(roomUnit, tile, false); + } + + public static void teleportUnitToTile(RoomUnit roomUnit, RoomTile tile, boolean fastTeleport) { if (roomUnit == null || tile == null || roomUnit.isWiredTeleporting) return; @@ -85,9 +90,10 @@ public class WiredEffectTeleport extends InteractionWiredEffect { // makes a temporary effect + int teleportDelay = getTeleportDelay(fastTeleport); + roomUnit.getRoom().unIdle(roomUnit.getRoom().getHabbo(roomUnit)); - room.sendComposer(new RoomUserEffectComposer(roomUnit, 4).compose()); - Emulator.getThreading().run(new SendRoomUnitEffectComposer(room, roomUnit), WiredManager.TELEPORT_DELAY + 1000); + sendTeleportEffect(room, roomUnit, fastTeleport); if (tile == roomUnit.getCurrentLocation()) { return; @@ -110,8 +116,8 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } } - Emulator.getThreading().run(() -> { roomUnit.isWiredTeleporting = true; }, Math.max(0, WiredManager.TELEPORT_DELAY - 500)); - Emulator.getThreading().run(new RoomUnitTeleport(roomUnit, room, tile.x, tile.y, tile.getStackHeight() + (tile.state == RoomTileState.SIT ? -0.5 : 0), roomUnit.getEffectId()), WiredManager.TELEPORT_DELAY); + Emulator.getThreading().run(() -> { roomUnit.isWiredTeleporting = true; }, Math.max(0, teleportDelay - 500)); + Emulator.getThreading().run(new RoomUnitTeleport(roomUnit, room, tile.x, tile.y, tile.getStackHeight() + (tile.state == RoomTileState.SIT ? -0.5 : 0), roomUnit.getEffectId()), teleportDelay); } @Override @@ -137,7 +143,8 @@ public class WiredEffectTeleport extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); + message.appendInt(this.fastTeleport ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(this.userSource); message.appendInt(0); @@ -166,8 +173,15 @@ public class WiredEffectTeleport extends InteractionWiredEffect { @Override public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + if (params.length > 2) { + this.fastTeleport = params[0] == 1; + this.furniSource = params[1]; + this.userSource = params[2]; + } else { + this.fastTeleport = false; + this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + } int itemsCount = settings.getFurniIds().length; @@ -227,7 +241,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { RoomTile tile = room.getLayout().getTile(item.getX(), item.getY()); if (tile != null) { - teleportUnitToTile(roomUnit, tile); + teleportUnitToTile(roomUnit, tile, this.fastTeleport); } } } @@ -244,6 +258,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { return WiredManager.getGson().toJson(new JsonData( this.getDelay(), itemsSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.fastTeleport, this.furniSource, this.userSource )); @@ -257,6 +272,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); + this.fastTeleport = data.fastTeleport; this.furniSource = data.furniSource; this.userSource = data.userSource; for (Integer id: data.itemIds) { @@ -284,6 +300,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } } } + this.fastTeleport = false; this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -292,6 +309,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { @Override public void onPickUp() { this.items.clear(); + this.fastTeleport = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); @@ -312,15 +330,30 @@ public class WiredEffectTeleport extends InteractionWiredEffect { return COOLDOWN_DEFAULT; } + private static int getTeleportDelay(boolean fastTeleport) { + return fastTeleport ? Math.max(75, WiredManager.TELEPORT_DELAY / 5) : WiredManager.TELEPORT_DELAY; + } + + private static int getTeleportEffectDuration(boolean fastTeleport) { + return fastTeleport ? Math.max(300, (WiredManager.TELEPORT_DELAY + 1000) / 3) : (WiredManager.TELEPORT_DELAY + 1000); + } + + private static void sendTeleportEffect(Room room, RoomUnit roomUnit, boolean fastTeleport) { + room.sendComposer(new RoomUserEffectComposer(roomUnit, 4).compose()); + Emulator.getThreading().run(new SendRoomUnitEffectComposer(room, roomUnit), getTeleportEffectDuration(fastTeleport)); + } + static class JsonData { int delay; List itemIds; + boolean fastTeleport; int furniSource; int userSource; - public JsonData(int delay, List itemIds, int furniSource, int userSource) { + public JsonData(int delay, List itemIds, boolean fastTeleport, int furniSource, int userSource) { this.delay = delay; this.itemIds = itemIds; + this.fastTeleport = fastTeleport; this.furniSource = furniSource; this.userSource = userSource; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java index 09a14d67..5c4e4489 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java @@ -39,10 +39,13 @@ import java.util.stream.Collectors; public class WiredEffectToggleFurni extends InteractionWiredEffect { private static final Logger LOGGER = LoggerFactory.getLogger(WiredEffectToggleFurni.class); + private static final int TOGGLE_TYPE_NEXT = 0; + private static final int TOGGLE_TYPE_PREVIOUS = 1; public static final WiredEffectType type = WiredEffectType.TOGGLE_STATE; private final THashSet items; + private int toggleType = TOGGLE_TYPE_NEXT; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; private static final List> FORBIDDEN_TYPES = new ArrayList>() { @@ -122,7 +125,8 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.toggleType); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -151,7 +155,13 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { @Override public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + if (params.length > 1) { + this.toggleType = normalizeToggleType(params[0]); + this.furniSource = params[1]; + } else { + this.toggleType = TOGGLE_TYPE_NEXT; + this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + } int itemsCount = settings.getFurniIds().length; @@ -204,15 +214,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { try { if (item.getBaseItem().getStateCount() > 1 || item instanceof InteractionGameTimer) { - int state = 0; - if (!item.getExtradata().isEmpty()) { - try { - state = Integer.parseInt(item.getExtradata()); // assumes that extradata is state, could be something else for trophies etc. - } catch (NumberFormatException ignored) { - - } - } - item.onClick(habbo != null && !(item instanceof InteractionGameTimer) ? habbo.getClient() : null, room, new Object[]{state, this.getType()}); + this.toggleItemState(room, habbo, item); } } catch (Exception e) { LOGGER.error("Caught exception", e); @@ -235,6 +237,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { return WiredManager.getGson().toJson(new JsonData( this.getDelay(), new ArrayList<>(this.items).stream().map(HabboItem::getId).collect(Collectors.toList()), + this.toggleType, this.furniSource )); } @@ -247,6 +250,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); + this.toggleType = normalizeToggleType(data.toggleType); this.furniSource = data.furniSource; for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -281,6 +285,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { } } } + this.toggleType = TOGGLE_TYPE_NEXT; this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @@ -288,6 +293,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { @Override public void onPickUp() { this.items.clear(); + this.toggleType = TOGGLE_TYPE_NEXT; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -297,14 +303,52 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { return type; } + private int normalizeToggleType(int value) { + return (value == TOGGLE_TYPE_PREVIOUS) ? TOGGLE_TYPE_PREVIOUS : TOGGLE_TYPE_NEXT; + } + + private void toggleItemState(Room room, Habbo habbo, HabboItem item) throws Exception { + if (item.getBaseItem().getStateCount() <= 1) { + return; + } + + int stateCount = item.getBaseItem().getStateCount(); + int currentState = 0; + + if (!item.getExtradata().isEmpty()) { + try { + currentState = Integer.parseInt(item.getExtradata()); + } catch (NumberFormatException ignored) { + if (this.toggleType == TOGGLE_TYPE_NEXT) { + item.onClick(habbo != null ? habbo.getClient() : null, room, new Object[]{0, this.getType()}); + } + return; + } + } + + int nextState = (this.toggleType == TOGGLE_TYPE_PREVIOUS) + ? ((currentState - 1 + stateCount) % stateCount) + : ((currentState + 1) % stateCount); + + if (currentState == nextState) { + return; + } + + item.setExtradata(Integer.toString(nextState)); + item.needsUpdate(true); + room.updateItemState(item); + } + static class JsonData { int delay; List itemIds; + int toggleType; int furniSource; - public JsonData(int delay, List itemIds, int furniSource) { + public JsonData(int delay, List itemIds, int toggleType, int furniSource) { this.delay = delay; this.itemIds = itemIds; + this.toggleType = toggleType; this.furniSource = furniSource; } } 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 b5d50cc3..667400c5 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 @@ -1,17 +1,37 @@ 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.InteractionWiredTrigger; +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.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 com.eu.habbo.messages.outgoing.rooms.users.RoomUnitOnRollerComposer; +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 class WiredEffectUserToFurni extends WiredEffectUserFurniBase { + private static final int WALKMODE_IF_CLOSER = 0; + private static final int WALKMODE_CONTINUE = 1; + private static final int WALKMODE_STOP = 2; + public static final WiredEffectType type = WiredEffectType.USER_TO_FURNI; + private int walkMode = WALKMODE_CONTINUE; public WiredEffectUserToFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,8 +50,13 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { return; } + RoomTile targetTile = room.getLayout().getTile(item.getX(), item.getY()); + if (targetTile == null) { + return; + } + for (Habbo habbo : this.resolveHabbos(room, ctx)) { - room.teleportHabboToItem(habbo, item); + this.moveHabboSmooth(room, habbo, item, targetTile); } } @@ -45,4 +70,240 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { public WiredEffectType getType() { return type; } + + @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, + this.walkMode + )); + } + + @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; + this.walkMode = this.normalizeWalkMode((data.walkMode != null) ? data.walkMode : WALKMODE_CONTINUE); + + 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; + } + + return; + } + + 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; + this.walkMode = WALKMODE_CONTINUE; + } + + @Override + public void onPickUp() { + super.onPickUp(); + this.walkMode = WALKMODE_CONTINUE; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List 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(3); + message.appendInt(this.furniSource); + message.appendInt(this.userSource); + message.appendInt(this.walkMode); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + + if (this.requiresTriggeringUser()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @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 { + this.furniSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.walkMode = this.normalizeWalkMode((settings.getIntParams().length > 2) ? settings.getIntParams()[2] : WALKMODE_CONTINUE); + + 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 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; + } + + private void moveHabboSmooth(Room room, Habbo habbo, HabboItem item, RoomTile targetTile) { + if (room == null || habbo == null || item == null || targetTile == null || habbo.getRoomUnit() == null) { + return; + } + + RoomUnit roomUnit = habbo.getRoomUnit(); + RoomTile oldLocation = roomUnit.getCurrentLocation(); + RoomTile previousGoal = roomUnit.getGoal(); + boolean wasWalking = roomUnit.isWalking(); + + if (oldLocation == null) { + oldLocation = room.getLayout().getTile(roomUnit.getX(), roomUnit.getY()); + } + + 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()); + + this.applyWalkMode(roomUnit, oldLocation, previousGoal, targetTile, wasWalking); + roomUnit.setPreviousLocationZ(newZ); + } + + private void applyWalkMode(RoomUnit roomUnit, RoomTile oldLocation, RoomTile previousGoal, RoomTile targetTile, boolean wasWalking) { + if (roomUnit == null || targetTile == null) { + 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); + } + + private boolean isCloserToGoal(RoomTile oldLocation, RoomTile newLocation, RoomTile goalLocation) { + if (oldLocation == null || newLocation == null || goalLocation == null) { + return false; + } + + return this.distanceSquared(newLocation, goalLocation) < this.distanceSquared(oldLocation, goalLocation); + } + + private int distanceSquared(RoomTile first, RoomTile second) { + int dx = first.x - second.x; + int dy = first.y - second.y; + return (dx * dx) + (dy * dy); + } + + private int normalizeWalkMode(int walkMode) { + if (walkMode < WALKMODE_IF_CLOSER || walkMode > WALKMODE_STOP) { + return WALKMODE_CONTINUE; + } + + return walkMode; + } + + static class JsonData { + int delay; + List itemIds; + int furniSource; + int userSource; + Integer walkMode; + + public JsonData(int delay, List itemIds, int furniSource, int userSource, int walkMode) { + this.delay = delay; + this.itemIds = itemIds; + this.furniSource = furniSource; + this.userSource = userSource; + this.walkMode = walkMode; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java new file mode 100644 index 00000000..293b5fbf --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java @@ -0,0 +1,123 @@ +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 WiredExtraFilterFurni extends InteractionWiredExtra { + public static final int CODE = 56; + private static final int MAX_FILTER_AMOUNT = 10000; + + private int amount = 0; + + public WiredExtraFilterFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraFilterFurni(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] : 0; + + if (value == 0 && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = 0; + } + } + + this.amount = normalizeAmount(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.amount)); + } + + @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.amount); + 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.amount = normalizeAmount((data != null) ? data.amount : 0); + return; + } + + try { + this.amount = normalizeAmount(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.amount = 0; + } + } + + @Override + public void onPickUp() { + this.amount = 0; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getAmount() { + return this.amount; + } + + private static int normalizeAmount(int value) { + return Math.max(0, Math.min(MAX_FILTER_AMOUNT, value)); + } + + static class JsonData { + int amount; + + JsonData(int amount) { + this.amount = amount; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java new file mode 100644 index 00000000..bdc9c6c6 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java @@ -0,0 +1,123 @@ +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 WiredExtraFilterUser extends InteractionWiredExtra { + public static final int CODE = 57; + private static final int MAX_FILTER_AMOUNT = 10000; + + private int amount = 0; + + public WiredExtraFilterUser(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraFilterUser(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] : 0; + + if (value == 0 && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = 0; + } + } + + this.amount = normalizeAmount(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.amount)); + } + + @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.amount); + 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.amount = normalizeAmount((data != null) ? data.amount : 0); + return; + } + + try { + this.amount = normalizeAmount(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.amount = 0; + } + } + + @Override + public void onPickUp() { + this.amount = 0; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getAmount() { + return this.amount; + } + + private static int normalizeAmount(int value) { + return Math.max(0, Math.min(MAX_FILTER_AMOUNT, value)); + } + + static class JsonData { + int amount; + + JsonData(int amount) { + this.amount = amount; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java index 6db447f7..25bbb7b4 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java @@ -8,4 +8,5 @@ public interface InteractionWiredMatchFurniSettings { public boolean shouldMatchState(); public boolean shouldMatchRotation(); public boolean shouldMatchPosition(); + public boolean shouldMatchAltitude(); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java new file mode 100644 index 00000000..a2b6473a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java @@ -0,0 +1,223 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +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.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.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectFurniAltitude extends InteractionWiredEffect { + private static final int COMPARISON_LESS = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER = 2; + + public static final WiredEffectType type = WiredEffectType.FURNI_ALTITUDE_SELECTOR; + + private int comparison = COMPARISON_EQUAL; + private double altitude = 0.0D; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniAltitude(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniAltitude(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; + } + + Set matchingItems = new LinkedHashSet<>(); + + room.getFloorItems().forEach(item -> { + if (item == null || item instanceof InteractionWired) { + return; + } + + if (this.matchesAltitude(item)) { + matchingItems.add(item); + } + }); + + Set result = new LinkedHashSet<>(matchingItems); + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + int[] params = settings.getIntParams(); + if (params == null || params.length < 3) { + throw new WiredSaveException("wf_slc_furni_altitude requires 3 int params: comparison, filterExisting, invert"); + } + + this.comparison = this.normalizeComparison(params[0]); + this.filterExisting = params[1] == 1; + this.invert = params[2] == 1; + this.altitude = this.parseAltitudeOrDefault(settings.getStringParam()); + this.setDelay(settings.getDelay()); + + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.formatAltitude(this.altitude), + this.filterExisting, + this.invert, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.altitude = this.parseAltitudeOrDefault(data.altitude); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.comparison = COMPARISON_EQUAL; + this.altitude = 0.0D; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(this.formatAltitude(this.altitude)); + message.appendInt(3); + message.appendInt(this.comparison); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private boolean matchesAltitude(HabboItem item) { + if (item == null) { + return false; + } + + double normalizedAltitude = this.normalizeAltitude(item.getZ()); + + switch (this.comparison) { + case COMPARISON_LESS: + return normalizedAltitude < this.altitude; + case COMPARISON_GREATER: + return normalizedAltitude > this.altitude; + default: + return BigDecimal.valueOf(normalizedAltitude).compareTo(BigDecimal.valueOf(this.altitude)) == 0; + } + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + return value; + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + private double parseAltitudeOrDefault(String value) { + if (value == null || value.trim().isEmpty()) { + return 0.0D; + } + + try { + return this.normalizeAltitude(new BigDecimal(value.trim()).doubleValue()); + } catch (NumberFormatException exception) { + return 0.0D; + } + } + + private String formatAltitude(double value) { + BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros(); + return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString(); + } + + static class JsonData { + int comparison; + String altitude; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int comparison, String altitude, boolean filterExisting, boolean invert, int delay) { + this.comparison = comparison; + this.altitude = altitude; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java index 14bae162..e936fabe 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java @@ -59,33 +59,19 @@ public class WiredEffectFurniByType extends InteractionWiredEffect { matchKeys.add(key); } - Set result = new LinkedHashSet<>(); + Set matched = new LinkedHashSet<>(); room.getFloorItems().forEach(item -> { if (item instanceof InteractionWired) return; String key = matchState ? item.getBaseItem().getId() + ":" + item.getExtradata() : String.valueOf(item.getBaseItem().getId()); if (matchKeys.contains(key)) { - result.add(item); + matched.add(item); } }); - if (filterExisting) { - result.retainAll(ctx.targets().items()); - } - - if (invert) { - Set all = new LinkedHashSet<>(); - room.getFloorItems().forEach(item -> { - if (!(item instanceof InteractionWired)) all.add(item); - }); - all.removeAll(result); - if (!all.isEmpty()) { - ctx.targets().setItems(all); - } - } else if (!result.isEmpty()) { - ctx.targets().setItems(result); - } + Set result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert); + ctx.targets().setItems(result); } private List resolveSourceFurni(WiredContext ctx, Room room) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java index f3439707..2dd2c5b5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java @@ -42,6 +42,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { private int sourceType = SOURCE_USER_TRIGGER; private boolean filterExisting = false; private boolean invert = false; + private int targetOffsetX = 0; + private int targetOffsetY = 0; private List tileOffsets = new ArrayList<>(); private List pickedFurniIds = new ArrayList<>(); @@ -68,8 +70,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { for (int[] src : sourcePositions) { LOGGER.info("[FurniNeighborhood] Source: ({},{}), offsets: {}", src[0], src[1], tileOffsets.size()); for (int[] offset : tileOffsets) { - int tx = src[0] + offset[0]; - int ty = src[1] + offset[1]; + int tx = src[0] + (offset[0] - this.targetOffsetX); + int ty = src[1] + (offset[1] - this.targetOffsetY); for (HabboItem item : room.getItemsAt(tx, ty)) { if (item == null) continue; totalRaw++; @@ -87,18 +89,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { } LOGGER.info("[FurniNeighborhood] Raw={}, wiredSkipped={}, kept={}", totalRaw, wiredSkipped, result.size()); - if (filterExisting) { - result.retainAll(ctx.targets().items()); - } - - if (invert) { - Set all = new LinkedHashSet<>(); - room.getFloorItems().forEach(item -> { - if (!(item instanceof InteractionWired)) all.add(item); - }); - all.removeAll(result); - result = all; - } + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert); // Always set the selector result — even if empty. // An empty result means no items matched the neighborhood, so downstream @@ -158,12 +149,14 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = params[0]; this.filterExisting = params.length > 1 && params[1] == 1; this.invert = params.length > 2 && params[2] == 1; + this.targetOffsetX = params.length > 3 ? params[3] : 0; + this.targetOffsetY = params.length > 4 ? params[4] : 0; this.tileOffsets = new ArrayList<>(); - if (params.length > 3) { - int n = params[3]; + if (params.length > 5) { + int n = params[5]; for (int i = 0; i < n && i < MAX_TILE_OFFSETS; i++) { - int xi = 4 + i * 2; + int xi = 6 + i * 2; if (xi + 1 < params.length) { tileOffsets.add(new int[]{ params[xi], params[xi + 1] }); } @@ -180,8 +173,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.setDelay(settings.getDelay()); - LOGGER.info("[FurniNeighborhood] saveData: sourceType={}, filterExisting={}, invert={}, offsets={}, pickedFurniIds={}", - sourceType, filterExisting, invert, tileOffsets.size(), pickedFurniIds); + LOGGER.info("[FurniNeighborhood] saveData: sourceType={}, filterExisting={}, invert={}, target=({},{}), offsets={}, pickedFurniIds={}", + sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets.size(), pickedFurniIds); for (int[] o : tileOffsets) { LOGGER.info("[FurniNeighborhood] offset: ({}, {})", o[0], o[1]); } @@ -208,11 +201,13 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { message.appendInt(this.getId()); message.appendString(""); - int paramCount = 4 + tileOffsets.size() * 2; + int paramCount = 6 + tileOffsets.size() * 2; message.appendInt(paramCount); message.appendInt(sourceType); message.appendInt(filterExisting ? 1 : 0); message.appendInt(invert ? 1 : 0); + message.appendInt(targetOffsetX); + message.appendInt(targetOffsetY); message.appendInt(tileOffsets.size()); for (int[] offset : tileOffsets) { message.appendInt(offset[0]); @@ -236,7 +231,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { @Override public String getWiredData() { return WiredManager.getGson().toJson( - new JsonData(sourceType, filterExisting, invert, tileOffsets, pickedFurniIds, getDelay())); + new JsonData(sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets, pickedFurniIds, getDelay())); } @Override @@ -247,6 +242,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = data.sourceType; this.filterExisting = data.filterExisting; this.invert = data.invert; + this.targetOffsetX = data.targetOffsetX; + this.targetOffsetY = data.targetOffsetY; this.tileOffsets = data.tileOffsets != null ? data.tileOffsets : new ArrayList<>(); this.pickedFurniIds = data.pickedFurniIds != null ? data.pickedFurniIds : new ArrayList<>(); this.setDelay(data.delay); @@ -258,6 +255,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = SOURCE_USER_TRIGGER; this.filterExisting = false; this.invert = false; + this.targetOffsetX = 0; + this.targetOffsetY = 0; this.tileOffsets = new ArrayList<>(); this.pickedFurniIds = new ArrayList<>(); this.setDelay(0); @@ -270,15 +269,19 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { int sourceType; boolean filterExisting; boolean invert; + int targetOffsetX; + int targetOffsetY; List tileOffsets; List pickedFurniIds; int delay; - JsonData(int sourceType, boolean filterExisting, boolean invert, + JsonData(int sourceType, boolean filterExisting, boolean invert, int targetOffsetX, int targetOffsetY, List tileOffsets, List pickedFurniIds, int delay) { this.sourceType = sourceType; this.filterExisting = filterExisting; this.invert = invert; + this.targetOffsetX = targetOffsetX; + this.targetOffsetY = targetOffsetY; this.tileOffsets = tileOffsets; this.pickedFurniIds = pickedFurniIds; this.delay = delay; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java new file mode 100644 index 00000000..0ec8730c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java @@ -0,0 +1,335 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +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.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 java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectFurniOnFurni extends InteractionWiredEffect { + private static final double EPSILON = 0.0001D; + + private static final int SELECT_FURNI_ABOVE = 0; + private static final int SELECT_FURNI_BELOW = 1; + private static final int SELECT_FURNI_SAME_HEIGHT = 2; + private static final int SELECT_ALL_FURNI_ON_TILE = 3; + + public static final WiredEffectType type = WiredEffectType.FURNI_ON_FURNI_SELECTOR; + + private final Set items = new LinkedHashSet<>(); + private int selectionType = SELECT_FURNI_ABOVE; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniOnFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniOnFurni(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 || room.getLayout() == null) { + ctx.targets().setItems(Collections.emptySet()); + return; + } + + this.refresh(room); + + List sourceItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (sourceItems.isEmpty()) { + ctx.targets().setItems(Collections.emptySet()); + return; + } + + Set result = new LinkedHashSet<>(); + + for (HabboItem sourceItem : sourceItems) { + result.addAll(this.resolveRelatedItems(room, sourceItem)); + } + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.selectionType = (params.length > 0) ? this.normalizeSelectionType(params[0]) : SELECT_FURNI_ABOVE; + this.furniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = params.length > 2 && params[2] == 1; + this.invert = params.length > 3 && params[3] == 1; + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + this.items.clear(); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + + if (room == null) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item != null) { + this.items.add(item); + } + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + this.refresh(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId())); + + return WiredManager.getGson().toJson(new JsonData( + this.selectionType, + this.furniSource, + this.filterExisting, + this.invert, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.selectionType = this.normalizeSelectionType(data.selectionType); + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + + if (room == null || data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.selectionType = SELECT_FURNI_ABOVE; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.selectionType); + message.appendInt(this.furniSource); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private Set resolveRelatedItems(Room room, HabboItem sourceItem) { + Set result = new LinkedHashSet<>(); + + if (sourceItem == null) { + return result; + } + + RoomTile baseTile = room.getLayout().getTile(sourceItem.getX(), sourceItem.getY()); + if (baseTile == null) { + return result; + } + + Set occupiedTiles = room.getLayout().getTilesAt(baseTile, sourceItem.getBaseItem().getWidth(), sourceItem.getBaseItem().getLength(), sourceItem.getRotation()); + if (occupiedTiles == null) { + return result; + } + + double sourceBase = this.normalizeAltitude(sourceItem.getZ()); + double sourceTop = this.normalizeAltitude(sourceItem.getZ() + Item.getCurrentHeight(sourceItem)); + + for (RoomTile tile : occupiedTiles) { + if (tile == null) { + continue; + } + + for (HabboItem matchedItem : room.getItemsAt(tile)) { + if (matchedItem == null || matchedItem instanceof InteractionWired) { + continue; + } + + if (matchedItem == sourceItem) { + if (this.selectionType == SELECT_FURNI_SAME_HEIGHT || this.selectionType == SELECT_ALL_FURNI_ON_TILE) { + result.add(matchedItem); + } + continue; + } + + if (this.matchesSelectionType(sourceBase, sourceTop, matchedItem)) { + result.add(matchedItem); + } + } + } + + return result; + } + + private boolean matchesSelectionType(double sourceBase, double sourceTop, HabboItem matchedItem) { + double matchedBase = this.normalizeAltitude(matchedItem.getZ()); + double matchedTop = this.normalizeAltitude(matchedItem.getZ() + Item.getCurrentHeight(matchedItem)); + + switch (this.selectionType) { + case SELECT_FURNI_BELOW: + return matchedTop <= (sourceBase + EPSILON); + case SELECT_FURNI_SAME_HEIGHT: + return BigDecimal.valueOf(matchedBase).compareTo(BigDecimal.valueOf(sourceBase)) == 0; + case SELECT_ALL_FURNI_ON_TILE: + return true; + case SELECT_FURNI_ABOVE: + default: + return matchedBase >= (sourceTop - EPSILON); + } + } + + private void refresh(Room room) { + Set invalidItems = new LinkedHashSet<>(); + + if (room == null) { + invalidItems.addAll(this.items); + } else { + for (HabboItem item : this.items) { + if (room.getHabboItem(item.getId()) == null) { + invalidItems.add(item); + } + } + } + + this.items.removeAll(invalidItems); + } + + private int normalizeSelectionType(int value) { + if (value < SELECT_FURNI_ABOVE || value > SELECT_ALL_FURNI_ON_TILE) { + return SELECT_FURNI_ABOVE; + } + + return value; + } + + private int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + static class JsonData { + int selectionType; + int furniSource; + boolean filterExisting; + boolean invert; + List itemIds; + int delay; + + JsonData(int selectionType, int furniSource, boolean filterExisting, boolean invert, List itemIds, int delay) { + this.selectionType = selectionType; + this.furniSource = furniSource; + this.filterExisting = filterExisting; + this.invert = invert; + this.itemIds = itemIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java new file mode 100644 index 00000000..53a3c92c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java @@ -0,0 +1,167 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +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.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.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectFurniPicks extends InteractionWiredEffect { + private static final int MAX_PICKED_FURNI = 20; + + public static final WiredEffectType type = WiredEffectType.FURNI_PICKS_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + private List pickedFurniIds = new ArrayList<>(); + + public WiredEffectFurniPicks(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniPicks(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; + } + + Set result = this.pickedFurniIds.stream() + .map(room::getHabboItem) + .filter(item -> item != null && !(item instanceof InteractionWired)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + + this.pickedFurniIds = new ArrayList<>(); + if (settings.getFurniIds() != null) { + for (int id : settings.getFurniIds()) { + if (this.pickedFurniIds.size() >= MAX_PICKED_FURNI) break; + this.pickedFurniIds.add(id); + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.filterExisting, + this.invert, + this.pickedFurniIds, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.pickedFurniIds = (data.pickedFurniIds != null) ? data.pickedFurniIds : new ArrayList<>(); + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.pickedFurniIds = new ArrayList<>(); + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(true); + message.appendInt(MAX_PICKED_FURNI); + + if (!this.pickedFurniIds.isEmpty()) { + message.appendInt(this.pickedFurniIds.size()); + this.pickedFurniIds.forEach(message::appendInt); + } else { + message.appendInt(0); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + List pickedFurniIds; + int delay; + + JsonData(boolean filterExisting, boolean invert, List pickedFurniIds, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.pickedFurniIds = pickedFurniIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java new file mode 100644 index 00000000..2dc15691 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java @@ -0,0 +1,142 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +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.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +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; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class WiredEffectFurniSignal extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.FURNI_SIGNAL_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniSignal(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; + } + + Set result = new LinkedHashSet<>(); + + if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { + List signalItems = WiredSourceUtil.resolveItems(ctx, WiredSourceUtil.SOURCE_SIGNAL, null); + Set matched = signalItems.stream() + .filter(item -> item != null && !(item instanceof InteractionWired)) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + + result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + } + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + int delay; + + JsonData(boolean filterExisting, boolean invert, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java index 46984906..b03f4bb7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java @@ -6,7 +6,6 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; 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.rooms.RoomUnitType; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; @@ -29,8 +28,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { private int areaHeight = 0; private boolean filterExisting = false; private boolean invert = false; - private boolean excludeBots = false; - private boolean excludePets = false; public WiredEffectUsersArea(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -50,23 +47,15 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { List usersInArea = new ArrayList<>(); for (RoomUnit unit : room.getRoomUnits()) { - if (excludeBots && unit.getRoomUnitType() == RoomUnitType.BOT) continue; - if (excludePets && unit.getRoomUnitType() == RoomUnitType.PET) continue; int x = unit.getX(); int y = unit.getY(); boolean inArea = x >= rootX && x <= maxX && y >= rootY && y <= maxY; - if (invert ? !inArea : inArea) { + if (inArea) { usersInArea.add(unit); } } - if (filterExisting) { - usersInArea.retainAll(ctx.targets().users()); - } - - if (!usersInArea.isEmpty()) { - ctx.targets().setUsers(usersInArea); - } + ctx.targets().setUsers(this.applySelectorModifiers(usersInArea, room.getRoomUnits(), ctx.targets().users(), filterExisting, invert)); } @Override @@ -82,8 +71,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = params[3]; this.filterExisting = params.length >= 5 && params[4] == 1; this.invert = params.length >= 6 && params[5] == 1; - this.excludeBots = params.length >= 7 && params[6] == 1; - this.excludePets = params.length >= 8 && params[7] == 1; this.setDelay(settings.getDelay()); return true; @@ -101,7 +88,7 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, excludeBots, excludePets, getDelay())); + return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, getDelay())); } @Override @@ -116,8 +103,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = data.height; this.filterExisting = data.filterExisting; this.invert = data.invert; - this.excludeBots = data.excludeBots; - this.excludePets = data.excludePets; this.setDelay(data.delay); } } @@ -130,8 +115,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = 0; this.filterExisting = false; this.invert = false; - this.excludeBots = false; - this.excludePets = false; this.setDelay(0); } @@ -145,15 +128,13 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { message.appendInt(this.getId()); message.appendString(""); - message.appendInt(8); + 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(this.excludeBots ? 1 : 0); - message.appendInt(this.excludePets ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); @@ -173,19 +154,15 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { int height; boolean filterExisting; boolean invert; - boolean excludeBots; - boolean excludePets; int delay; - JsonData(int rootX, int rootY, int width, int height, boolean filterExisting, boolean invert, boolean excludeBots, boolean excludePets, 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.excludeBots = excludeBots; - this.excludePets = excludePets; this.delay = delay; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java new file mode 100644 index 00000000..3f88d99a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java @@ -0,0 +1,333 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersByAction extends InteractionWiredEffect { + private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id"; + private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter"; + private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp"; + private static final long TRANSIENT_ACTION_WINDOW_MS = 5_000L; + private static final int DEFAULT_ACTION = WiredUserActionType.WAVE; + + public static final WiredEffectType type = WiredEffectType.USERS_BY_ACTION_SELECTOR; + + private int selectedAction = DEFAULT_ACTION; + private boolean signFilterEnabled = false; + private int signId = 0; + private boolean danceFilterEnabled = false; + private int danceId = 1; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByAction(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByAction(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; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesAction(ctx, roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.onPickUp(); + + if (params.length > 0) this.selectedAction = this.normalizeAction(params[0]); + if (params.length > 1) this.signFilterEnabled = (params[1] == 1); + if (params.length > 2) this.signId = this.normalizeSignId(params[2]); + if (params.length > 3) this.danceFilterEnabled = (params[3] == 1); + if (params.length > 4) this.danceId = this.normalizeDanceId(params[4]); + if (params.length > 5) this.filterExisting = (params[5] == 1); + if (params.length > 6) this.invert = (params[6] == 1); + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.selectedAction, + this.signFilterEnabled, + this.signId, + this.danceFilterEnabled, + this.danceId, + this.filterExisting, + this.invert, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.selectedAction = this.normalizeAction(data.selectedAction); + this.signFilterEnabled = data.signFilterEnabled; + this.signId = this.normalizeSignId(data.signId); + this.danceFilterEnabled = data.danceFilterEnabled; + this.danceId = this.normalizeDanceId(data.danceId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.selectedAction = DEFAULT_ACTION; + this.signFilterEnabled = false; + this.signId = 0; + this.danceFilterEnabled = false; + this.danceId = 1; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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.selectedAction); + message.appendInt(this.signFilterEnabled ? 1 : 0); + message.appendInt(this.signId); + message.appendInt(this.danceFilterEnabled ? 1 : 0); + message.appendInt(this.danceId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeAction(int action) { + switch (action) { + case WiredUserActionType.WAVE: + case WiredUserActionType.BLOW_KISS: + case WiredUserActionType.LAUGH: + case WiredUserActionType.AWAKE: + case WiredUserActionType.RELAX: + case WiredUserActionType.SIT: + case WiredUserActionType.STAND: + case WiredUserActionType.LAY: + case WiredUserActionType.SIGN: + case WiredUserActionType.DANCE: + case WiredUserActionType.THUMB_UP: + return action; + default: + return DEFAULT_ACTION; + } + } + + private int normalizeSignId(int value) { + return (value < 0 || value > 17) ? 0 : value; + } + + private int normalizeDanceId(int value) { + return (value < 1 || value > 4) ? 1 : value; + } + + private boolean matchesAction(WiredContext ctx, RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + if (this.matchesEventAction(ctx, roomUnit)) { + return true; + } + + if (this.matchesCurrentState(roomUnit)) { + return true; + } + + return this.matchesRecentAction(roomUnit); + } + + private boolean matchesEventAction(WiredContext ctx, RoomUnit roomUnit) { + RoomUnit actor = ctx.actor().orElse(null); + + if (actor == null || actor.getId() != roomUnit.getId()) { + return false; + } + + if (ctx.eventType() != WiredEvent.Type.USER_PERFORMS_ACTION) { + return false; + } + + return this.matchesConfiguredAction(ctx.event().getActionId(), ctx.event().getActionParameter()); + } + + private boolean matchesCurrentState(RoomUnit roomUnit) { + switch (this.selectedAction) { + case WiredUserActionType.SIT: + return roomUnit.hasStatus(RoomUnitStatus.SIT); + case WiredUserActionType.LAY: + return roomUnit.hasStatus(RoomUnitStatus.LAY); + case WiredUserActionType.RELAX: + return roomUnit.isIdle(); + case WiredUserActionType.SIGN: + return this.matchesSignState(roomUnit); + case WiredUserActionType.DANCE: + return this.matchesDanceState(roomUnit); + default: + return false; + } + } + + private boolean matchesRecentAction(RoomUnit roomUnit) { + Object actionValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_ID); + Object parameterValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_PARAMETER); + Object timestampValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_TIMESTAMP); + + if (!(actionValue instanceof Integer) || !(timestampValue instanceof Long)) { + return false; + } + + long timestamp = (Long) timestampValue; + if ((System.currentTimeMillis() - timestamp) > TRANSIENT_ACTION_WINDOW_MS) { + return false; + } + + int actionId = (Integer) actionValue; + int parameter = (parameterValue instanceof Integer) ? (Integer) parameterValue : -1; + + return this.matchesConfiguredAction(actionId, parameter); + } + + private boolean matchesConfiguredAction(int actionId, int actionParameter) { + if (actionId != this.selectedAction) { + return false; + } + + if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) { + return actionParameter == this.signId; + } + + if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) { + return actionParameter == this.danceId; + } + + return true; + } + + private boolean matchesSignState(RoomUnit roomUnit) { + String signStatus = roomUnit.getStatus(RoomUnitStatus.SIGN); + if (signStatus == null) { + return false; + } + + if (!this.signFilterEnabled) { + return true; + } + + try { + return Integer.parseInt(signStatus) == this.signId; + } catch (NumberFormatException ignored) { + return false; + } + } + + private boolean matchesDanceState(RoomUnit roomUnit) { + int currentDance = roomUnit.getDanceType().getType(); + if (currentDance <= 0) { + return false; + } + + if (!this.danceFilterEnabled) { + return true; + } + + return currentDance == this.danceId; + } + + static class JsonData { + int selectedAction; + boolean signFilterEnabled; + int signId; + boolean danceFilterEnabled; + int danceId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId, boolean filterExisting, boolean invert, int delay) { + this.selectedAction = selectedAction; + this.signFilterEnabled = signFilterEnabled; + this.signId = signId; + this.danceFilterEnabled = danceFilterEnabled; + this.danceId = danceId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java new file mode 100644 index 00000000..a8dc2ef8 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java @@ -0,0 +1,202 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +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.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersByName extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_BY_NAME_SELECTOR; + + private String namesText = ""; + private Set usernames = new LinkedHashSet<>(); + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByName(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByName(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; + } + + Set result = new LinkedHashSet<>(); + + for (Habbo habbo : room.getHabbos()) { + if (habbo == null || habbo.getHabboInfo() == null || habbo.getRoomUnit() == null) { + continue; + } + + String username = habbo.getHabboInfo().getUsername(); + if (username == null) { + continue; + } + + if (this.usernames.contains(username.trim().toLowerCase(Locale.ROOT))) { + result.add(habbo.getRoomUnit()); + } + } + + Set availableUsers = room.getHabbos().stream() + .filter(habbo -> habbo != null && habbo.getRoomUnit() != null) + .map(Habbo::getRoomUnit) + .collect(Collectors.toCollection(LinkedHashSet::new)); + result = this.applySelectorModifiers(result, availableUsers, ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.namesText = this.normalizeNamesText(settings.getStringParam()); + this.usernames = this.parseUsernames(this.namesText); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.namesText, this.filterExisting, this.invert, this.getDelay())); + } + + @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) { + return; + } + + this.namesText = this.normalizeNamesText(data.namesText); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } else { + this.namesText = this.normalizeNamesText(wiredData); + } + + this.usernames = this.parseUsernames(this.namesText); + } + + @Override + public void onPickUp() { + this.namesText = ""; + this.usernames = new LinkedHashSet<>(); + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(this.namesText); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private String normalizeNamesText(String value) { + if (value == null || value.trim().isEmpty()) { + return ""; + } + + Set normalizedLines = new LinkedHashSet<>(); + + for (String line : value.split("\\R")) { + String normalized = line.trim(); + if (!normalized.isEmpty()) { + normalizedLines.add(normalized); + } + } + + return normalizedLines.stream().collect(Collectors.joining("\n")); + } + + private Set parseUsernames(String value) { + Set result = new LinkedHashSet<>(); + + if (value == null || value.trim().isEmpty()) { + return result; + } + + for (String line : value.split("\\R")) { + String normalized = line.trim(); + if (!normalized.isEmpty()) { + result.add(normalized.toLowerCase(Locale.ROOT)); + } + } + + return result; + } + + static class JsonData { + String namesText; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(String namesText, boolean filterExisting, boolean invert, int delay) { + this.namesText = namesText; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java new file mode 100644 index 00000000..b9280099 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java @@ -0,0 +1,167 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +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.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersByType extends InteractionWiredEffect { + private static final int ENTITY_HABBO = 1; + private static final int ENTITY_PET = 2; + private static final int ENTITY_BOT = 4; + + public static final WiredEffectType type = WiredEffectType.USERS_BY_TYPE_SELECTOR; + + private int entityType = ENTITY_HABBO; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByType(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByType(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; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesType(roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.entityType = (params.length > 0) ? this.normalizeEntityType(params[0]) : ENTITY_HABBO; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.entityType, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.entityType = this.normalizeEntityType(data.entityType); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.entityType = ENTITY_HABBO; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(3); + message.appendInt(this.entityType); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeEntityType(int value) { + switch (value) { + case ENTITY_PET: + case ENTITY_BOT: + return value; + default: + return ENTITY_HABBO; + } + } + + private boolean matchesType(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + RoomUnitType roomUnitType = roomUnit.getRoomUnitType(); + return roomUnitType != null && roomUnitType.getTypeId() == this.entityType; + } + + static class JsonData { + int entityType; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int entityType, boolean filterExisting, boolean invert, int delay) { + this.entityType = entityType; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java new file mode 100644 index 00000000..f74de64a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java @@ -0,0 +1,179 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +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.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersGroup extends InteractionWiredEffect { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + + public static final WiredEffectType type = WiredEffectType.USERS_GROUP_SELECTOR; + + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersGroup(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersGroup(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; + } + + int targetGroupId = this.resolveTargetGroupId(room); + Set result = new LinkedHashSet<>(); + + if (targetGroupId > 0) { + for (Habbo habbo : room.getHabbos()) { + if (habbo == null || habbo.getRoomUnit() == null || habbo.getHabboStats() == null) { + continue; + } + + if (habbo.getHabboStats().hasGuild(targetGroupId)) { + result.add(habbo.getRoomUnit()); + } + } + } + + Set availableUsers = room.getHabbos().stream() + .filter(habbo -> habbo != null && habbo.getRoomUnit() != null) + .map(Habbo::getRoomUnit) + .collect(Collectors.toCollection(LinkedHashSet::new)); + result = this.applySelectorModifiers(result, availableUsers, ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.groupType = (params.length > 0) ? this.normalizeGroupType(params[0]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 1) ? Math.max(0, params[1]) : 0; + this.filterExisting = params.length > 2 && params[2] == 1; + this.invert = params.length > 3 && params[3] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.groupType, this.selectedGroupId, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = Math.max(0, data.selectedGroupId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(4); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int resolveTargetGroupId(Room room) { + if (this.groupType == GROUP_CURRENT_ROOM) { + return room.getGuildId(); + } + + return this.selectedGroupId; + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + static class JsonData { + int groupType; + int selectedGroupId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int groupType, int selectedGroupId, boolean filterExisting, boolean invert, int delay) { + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java new file mode 100644 index 00000000..e34ef8e7 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java @@ -0,0 +1,145 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.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.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersHandItem extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_HANDITEM_SELECTOR; + + private int handItemId = 0; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersHandItem(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersHandItem(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; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (roomUnit != null && roomUnit.getHandItem() == this.handItemId) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.handItemId = (params.length > 0) ? Math.max(0, params[0]) : 0; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.handItemId, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.handItemId = Math.max(0, data.handItemId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.handItemId = 0; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(3); + message.appendInt(this.handItemId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + int handItemId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int handItemId, boolean filterExisting, boolean invert, int delay) { + this.handItemId = handItemId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java index 7c63b2fb..9c5dd91c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java @@ -6,7 +6,6 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; 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.rooms.RoomUnitType; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; @@ -43,8 +42,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { private int sourceType = SOURCE_USER_TRIGGER; private boolean filterExisting = false; private boolean invert = false; - private boolean excludeBots = false; - private boolean excludePets = false; + private int targetOffsetX = 0; + private int targetOffsetY = 0; private List tileOffsets = new ArrayList<>(); private List pickedFurniIds = new ArrayList<>(); @@ -77,13 +76,11 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { tileOffsets.stream().map(o -> o[0] + "," + o[1]).collect(Collectors.joining(";")), filterExisting, invert); - // Apply tile offsets relative to each source position. - // The offsets define a neighborhood pattern around the source furni/user. Set targetTiles = new HashSet<>(); for (int[] src : sourcePositions) { for (int[] offset : tileOffsets) { - int tx = src[0] + offset[0]; - int ty = src[1] + offset[1]; + int tx = src[0] + (offset[0] - this.targetOffsetX); + int ty = src[1] + (offset[1] - this.targetOffsetY); targetTiles.add(tx + "," + ty); } } @@ -92,22 +89,17 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { List result = new ArrayList<>(); for (RoomUnit unit : room.getRoomUnits()) { - if (excludeBots && unit.getRoomUnitType() == RoomUnitType.BOT) continue; - if (excludePets && unit.getRoomUnitType() == RoomUnitType.PET) continue; - String pos = unit.getX() + "," + unit.getY(); boolean onTile = targetTiles.contains(pos); LOGGER.debug("[Neighborhood] Unit id={} type={} pos={} onTile={}", unit.getId(), unit.getRoomUnitType(), pos, onTile); - if (invert ? !onTile : onTile) { + if (onTile) { result.add(unit); } } - if (filterExisting) { - result.retainAll(ctx.targets().users()); - } + result = new ArrayList<>(this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), filterExisting, invert)); LOGGER.debug("[Neighborhood] Result: {} users selected", result.size()); @@ -169,8 +161,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = params[0]; this.filterExisting = params.length > 1 && params[1] == 1; this.invert = params.length > 2 && params[2] == 1; - this.excludeBots = params.length > 3 && params[3] == 1; - this.excludePets = params.length > 4 && params[4] == 1; + this.targetOffsetX = params.length > 3 ? params[3] : 0; + this.targetOffsetY = params.length > 4 ? params[4] : 0; this.tileOffsets = new ArrayList<>(); if (params.length > 5) { @@ -218,8 +210,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { message.appendInt(sourceType); message.appendInt(filterExisting ? 1 : 0); message.appendInt(invert ? 1 : 0); - message.appendInt(excludeBots ? 1 : 0); - message.appendInt(excludePets ? 1 : 0); + message.appendInt(targetOffsetX); + message.appendInt(targetOffsetY); message.appendInt(tileOffsets.size()); for (int[] offset : tileOffsets) { message.appendInt(offset[0]); @@ -243,7 +235,7 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { @Override public String getWiredData() { return WiredManager.getGson().toJson( - new JsonData(sourceType, filterExisting, invert, excludeBots, excludePets, tileOffsets, pickedFurniIds, getDelay())); + new JsonData(sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets, pickedFurniIds, getDelay())); } @Override @@ -254,8 +246,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = data.sourceType; this.filterExisting = data.filterExisting; this.invert = data.invert; - this.excludeBots = data.excludeBots; - this.excludePets = data.excludePets; + this.targetOffsetX = data.targetOffsetX; + this.targetOffsetY = data.targetOffsetY; this.tileOffsets = data.tileOffsets != null ? data.tileOffsets : new ArrayList<>(); this.pickedFurniIds = data.pickedFurniIds != null ? data.pickedFurniIds : new ArrayList<>(); this.setDelay(data.delay); @@ -267,8 +259,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = SOURCE_USER_TRIGGER; this.filterExisting = false; this.invert = false; - this.excludeBots = false; - this.excludePets = false; + this.targetOffsetX = 0; + this.targetOffsetY = 0; this.tileOffsets = new ArrayList<>(); this.pickedFurniIds = new ArrayList<>(); this.setDelay(0); @@ -281,20 +273,20 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { int sourceType; boolean filterExisting; boolean invert; - boolean excludeBots; - boolean excludePets; + int targetOffsetX; + int targetOffsetY; List tileOffsets; List pickedFurniIds; int delay; JsonData(int sourceType, boolean filterExisting, boolean invert, - boolean excludeBots, boolean excludePets, + int targetOffsetX, int targetOffsetY, List tileOffsets, List pickedFurniIds, int delay) { this.sourceType = sourceType; this.filterExisting = filterExisting; this.invert = invert; - this.excludeBots = excludeBots; - this.excludePets = excludePets; + this.targetOffsetX = targetOffsetX; + this.targetOffsetY = targetOffsetY; this.tileOffsets = tileOffsets; this.pickedFurniIds = pickedFurniIds; this.delay = delay; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java new file mode 100644 index 00000000..833d10a4 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java @@ -0,0 +1,269 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +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 java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersOnFurni extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_ON_FURNI_SELECTOR; + + private final Set items = new LinkedHashSet<>(); + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersOnFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersOnFurni(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 || room.getLayout() == null) { + ctx.targets().setUsers(Collections.emptySet()); + return; + } + + this.refresh(room); + + List sourceItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (sourceItems.isEmpty()) { + ctx.targets().setUsers(Collections.emptySet()); + return; + } + + Set result = new LinkedHashSet<>(); + + for (HabboItem sourceItem : sourceItems) { + result.addAll(this.resolveUnitsOnItem(room, sourceItem)); + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + this.items.clear(); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + + if (room == null) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item != null) { + this.items.add(item); + } + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + this.refresh(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId())); + + return WiredManager.getGson().toJson(new JsonData( + this.furniSource, + this.filterExisting, + this.invert, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + + if (room == null || data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.furniSource); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private Set resolveUnitsOnItem(Room room, HabboItem sourceItem) { + Set result = new LinkedHashSet<>(); + + if (sourceItem == null) { + return result; + } + + RoomTile baseTile = room.getLayout().getTile(sourceItem.getX(), sourceItem.getY()); + if (baseTile == null) { + return result; + } + + Set occupiedTiles = room.getLayout().getTilesAt(baseTile, sourceItem.getBaseItem().getWidth(), sourceItem.getBaseItem().getLength(), sourceItem.getRotation()); + if (occupiedTiles == null) { + return result; + } + + for (RoomTile tile : occupiedTiles) { + if (tile == null) { + continue; + } + + result.addAll(room.getUnitManager().getRoomUnitsAt(tile)); + } + + return result; + } + + private void refresh(Room room) { + Set invalidItems = new LinkedHashSet<>(); + + if (room == null) { + invalidItems.addAll(this.items); + } else { + for (HabboItem item : this.items) { + if (room.getHabboItem(item.getId()) == null) { + invalidItems.add(item); + } + } + } + + this.items.removeAll(invalidItems); + } + + private int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int furniSource; + boolean filterExisting; + boolean invert; + List itemIds; + int delay; + + JsonData(int furniSource, boolean filterExisting, boolean invert, List itemIds, int delay) { + this.furniSource = furniSource; + this.filterExisting = filterExisting; + this.invert = invert; + this.itemIds = itemIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java new file mode 100644 index 00000000..1ce2eaba --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java @@ -0,0 +1,138 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +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.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.WiredEvent; +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; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class WiredEffectUsersSignal extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_SIGNAL_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersSignal(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; + } + + Set result = new LinkedHashSet<>(); + + if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { + List signalUsers = WiredSourceUtil.resolveUsers(ctx, WiredSourceUtil.SOURCE_SIGNAL); + result.addAll(signalUsers); + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + } + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + int delay; + + JsonData(boolean filterExisting, boolean invert, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java new file mode 100644 index 00000000..8b16f73f --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java @@ -0,0 +1,177 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.games.GamePlayer; +import com.eu.habbo.habbohotel.games.GameTeamColors; +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.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +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.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersTeam extends InteractionWiredEffect { + private static final int TEAM_ANY = 0; + + public static final WiredEffectType type = WiredEffectType.USERS_TEAM_SELECTOR; + + private int teamType = TEAM_ANY; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersTeam(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersTeam(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; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesTeam(room, roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.teamType = (params.length > 0) ? this.normalizeTeamType(params[0]) : TEAM_ANY; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.teamType, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.teamType = this.normalizeTeamType(data.teamType); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.teamType = TEAM_ANY; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @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(3); + message.appendInt(this.teamType); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeTeamType(int value) { + if (value == TEAM_ANY) { + return TEAM_ANY; + } + + GameTeamColors teamColor = GameTeamColors.fromType(value); + return (teamColor.type >= GameTeamColors.RED.type && teamColor.type <= GameTeamColors.YELLOW.type) + ? teamColor.type + : TEAM_ANY; + } + + private boolean matchesTeam(Room room, RoomUnit roomUnit) { + if (room == null || roomUnit == null) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null || habbo.getHabboInfo() == null) { + return false; + } + + GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer(); + if (gamePlayer == null || gamePlayer.getTeamColor() == null || gamePlayer.getTeamColor() == GameTeamColors.NONE) { + return false; + } + + return (this.teamType == TEAM_ANY) || gamePlayer.getTeamColor().type == this.teamType; + } + + static class JsonData { + int teamType; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int teamType, boolean filterExisting, boolean invert, int delay) { + this.teamType = teamType; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java index 419dcb90..0bc3cee5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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; @@ -11,6 +12,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; @@ -25,11 +28,15 @@ import java.util.stream.Collectors; public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { private static final Logger LOGGER = LoggerFactory.getLogger(WiredTriggerBotReachedFurni.class); + private static final int BOT_SOURCE_NAME = 100; + private static final int BOT_SOURCE_SELECTOR = 200; - public final static WiredTriggerType type = WiredTriggerType.WALKS_ON_FURNI; + public final static WiredTriggerType type = WiredTriggerType.BOT_REACHED_STF; - private THashSet items; + private final THashSet items; private String botName = ""; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = BOT_SOURCE_NAME; public WiredTriggerBotReachedFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -72,9 +79,11 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); + message.appendInt(2); + message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); - message.appendInt(0); - message.appendInt(WiredTriggerType.BOT_REACHED_STF.code); + message.appendInt(this.getType().code); if (!this.isTriggeredByRoomUnit()) { List invalidTriggers = new ArrayList<>(); @@ -98,9 +107,22 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.botName = settings.getStringParam(); + int[] params = settings.getIntParams(); + this.furniSource = (params.length > 0) + ? this.normalizeFurniSource(params[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + this.botSource = (params.length > 1) ? this.normalizeBotSource(params[1]) : BOT_SOURCE_NAME; this.items.clear(); + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -127,20 +149,14 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { return false; } - boolean isCorrectBot = room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); - if (!isCorrectBot) { + if (!this.matchesBotSource(event, roomUnit, room) || !isCorrectBotForName(roomUnit, room)) { return false; } - if (this.items.contains(sourceItem)) { - return true; - } - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -153,6 +169,8 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.botName, + this.furniSource, + this.botSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -165,12 +183,18 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.botName = data.botName; + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.botSource = this.normalizeBotSource(data.botSource); 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[] data = wiredData.split(":"); @@ -192,6 +216,9 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = BOT_SOURCE_NAME; } } @@ -199,15 +226,51 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { public void onPickUp() { this.items.clear(); this.botName = ""; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; } static class JsonData { String botName; + int furniSource; + int botSource; List itemIds; - public JsonData(String botName, List itemIds) { + public JsonData(String botName, int furniSource, int botSource, List itemIds) { this.botName = botName; + this.furniSource = furniSource; + this.botSource = botSource; this.itemIds = itemIds; } } + + private boolean matchesBotSource(WiredEvent event, RoomUnit roomUnit, Room room) { + if (this.botSource == BOT_SOURCE_SELECTOR) { + return WiredTriggerSourceUtil.containsUser( + WiredTriggerSourceUtil.resolveUsers(this, event, WiredSourceUtil.SOURCE_SELECTOR, null), + roomUnit); + } + + return true; + } + + private boolean isCorrectBotForName(RoomUnit roomUnit, Room room) { + if (this.botSource != BOT_SOURCE_NAME) { + return true; + } + + return room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); + } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + + private int normalizeBotSource(int value) { + return (value == BOT_SOURCE_SELECTOR) ? BOT_SOURCE_SELECTOR : BOT_SOURCE_NAME; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java index 50c39e71..cc671a58 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java @@ -9,6 +9,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; @@ -16,8 +18,11 @@ import java.sql.SQLException; public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { public final static WiredTriggerType type = WiredTriggerType.BOT_REACHED_AVTR; + private static final int BOT_SOURCE_NAME = 100; + private static final int BOT_SOURCE_SELECTOR = 200; private String botName = ""; + private int botSource = BOT_SOURCE_NAME; public WiredTriggerBotReachedHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -40,7 +45,8 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -50,6 +56,7 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { this.botName = settings.getStringParam(); + this.botSource = (settings.getIntParams().length > 0) ? this.normalizeBotSource(settings.getIntParams()[0]) : BOT_SOURCE_NAME; return true; } @@ -58,6 +65,17 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { public boolean matches(HabboItem triggerItem, WiredEvent event) { RoomUnit roomUnit = event.getActor().orElse(null); Room room = event.getRoom(); + + if (roomUnit == null || room == null) { + return false; + } + + if (this.botSource == BOT_SOURCE_SELECTOR) { + return WiredTriggerSourceUtil.containsUser( + WiredTriggerSourceUtil.resolveUsers(this, event, WiredSourceUtil.SOURCE_SELECTOR, null), + roomUnit); + } + return room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); } @@ -70,7 +88,8 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( - this.botName + this.botName, + this.botSource )); } @@ -81,14 +100,17 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.botName = data.botName; + this.botSource = this.normalizeBotSource(data.botSource); } else { this.botName = wiredData; + this.botSource = BOT_SOURCE_NAME; } } @Override public void onPickUp() { this.botName = ""; + this.botSource = BOT_SOURCE_NAME; } @Override @@ -98,9 +120,15 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { static class JsonData { String botName; + int botSource; - public JsonData(String botName) { + public JsonData(String botName, int botSource) { this.botName = botName; + this.botSource = botSource; } } + + private int normalizeBotSource(int value) { + return (value == BOT_SOURCE_SELECTOR) ? BOT_SOURCE_SELECTOR : BOT_SOURCE_NAME; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java index 2e6c9d78..dc536b3b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; @@ -12,7 +13,9 @@ import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -53,7 +56,8 @@ public class WiredTriggerClockCounter extends InteractionWiredTrigger { return false; } - return (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) || this.items.contains(sourceItem); + return WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() + .anyMatch(item -> item != null && item.getId() == sourceItem.getId()); } @Deprecated @@ -94,11 +98,18 @@ public class WiredTriggerClockCounter extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { int[] params = settings.getIntParams(); this.minutes = (params.length > 0) ? this.normalizeMinutes(params[0]) : 0; this.halfSecondSteps = (params.length > 1) ? this.normalizeHalfSecondSteps(params[1]) : 0; - this.furniSource = (params.length > 2) ? this.normalizeFurniSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER; + this.furniSource = (params.length > 2) + ? this.normalizeFurniSource(params[2]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); this.items.clear(); @@ -119,9 +130,11 @@ public class WiredTriggerClockCounter extends InteractionWiredTrigger { for (int itemId : settings.getFurniIds()) { HabboItem item = room.getHabboItem(itemId); - if (item instanceof InteractionGameUpCounter) { - this.items.add(item); + if (!(item instanceof InteractionGameUpCounter)) { + throw new WiredTriggerSaveException("wiredfurni.error.require_counter_furni"); } + + this.items.add(item); } return true; @@ -172,6 +185,10 @@ public class WiredTriggerClockCounter extends InteractionWiredTrigger { this.items.add(item); } } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } } @Override @@ -210,7 +227,11 @@ public class WiredTriggerClockCounter extends InteractionWiredTrigger { } private int normalizeFurniSource(int value) { - return (value == WiredSourceUtil.SOURCE_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER; + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; } static class JsonData { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java index 6c15531f..45a8ccd8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java @@ -10,6 +10,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -17,6 +19,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.STATE_CHANGED; @@ -25,6 +28,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { private THashSet snapshots; private int triggerMode = MODE_ALL_STATES; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerFurniStateToggled(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -48,11 +52,15 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } StateSnapshot snapshot = this.getSnapshot(sourceItem.getId()); - if (snapshot == null) { + if (!this.matchesSourceItem(event, sourceItem)) { return false; } if (this.triggerMode == MODE_SAVED_STATE) { + if (snapshot == null) { + return false; + } + return snapshot.state.equals(this.normalizeState(sourceItem.getExtradata())); } @@ -69,6 +77,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.triggerMode, + this.furniSource, new ArrayList<>(this.snapshots) )); } @@ -77,11 +86,13 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public void loadWiredData(ResultSet set, Room room) throws SQLException { this.snapshots = new THashSet<>(); this.triggerMode = MODE_ALL_STATES; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.triggerMode = (data != null) ? data.triggerMode : MODE_ALL_STATES; + this.furniSource = (data != null) ? this.normalizeFurniSource(data.furniSource) : WiredSourceUtil.SOURCE_TRIGGER; if (data != null && data.snapshots != null && !data.snapshots.isEmpty()) { for (StateSnapshot snapshot : data.snapshots) { @@ -100,6 +111,10 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } } } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.snapshots.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } } else { if (wiredData.split(":").length >= 3) { super.setDelay(Integer.parseInt(wiredData.split(":")[0])); @@ -118,6 +133,8 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } } } + + this.furniSource = this.snapshots.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @@ -125,6 +142,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public void onPickUp() { this.snapshots.clear(); this.triggerMode = MODE_ALL_STATES; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -157,8 +175,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.triggerMode); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -170,6 +189,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { this.triggerMode = (settings.getIntParams().length > 0 && settings.getIntParams()[0] == MODE_SAVED_STATE) ? MODE_SAVED_STATE : MODE_ALL_STATES; + this.furniSource = (settings.getIntParams().length > 1) + ? this.normalizeFurniSource(settings.getIntParams()[1]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room == null) { @@ -211,8 +233,29 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { return (state == null) ? "" : state; } + private boolean matchesSourceItem(WiredEvent event, HabboItem sourceItem) { + List selectedItems = event.getRoom() == null + ? new ArrayList<>() + : this.snapshots.stream() + .map(snapshot -> event.getRoom().getHabboItem(snapshot.itemId)) + .filter(item -> item != null) + .collect(Collectors.toList()); + + return WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, selectedItems).stream() + .anyMatch(item -> item != null && item.getId() == sourceItem.getId()); + } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + static class JsonData { int triggerMode; + int furniSource; List snapshots; List itemIds; @@ -223,8 +266,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { this.itemIds = itemIds; } - public JsonData(int triggerMode, List snapshots) { + public JsonData(int triggerMode, int furniSource, List snapshots) { this.triggerMode = triggerMode; + this.furniSource = furniSource; this.snapshots = snapshots; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java index 9bf25e62..a801ed88 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,7 +11,10 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -21,7 +25,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.CLICKS_FURNI; - private THashSet items; + protected final THashSet items; + protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboClicksFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,7 +41,21 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - return sourceItem != null && this.items.contains(sourceItem); + if (sourceItem == null) { + return false; + } + + switch (this.furniSource) { + case WiredSourceUtil.SOURCE_SELECTED: + return this.matchesSourceItem(this.items, sourceItem); + case WiredSourceUtil.SOURCE_SELECTOR: + return this.matchesSourceItem( + WiredTriggerSourceUtil.resolveItems(this, event, WiredSourceUtil.SOURCE_SELECTOR, this.items), + sourceItem); + case WiredSourceUtil.SOURCE_TRIGGER: + default: + return true; + } } @Deprecated @@ -77,7 +96,8 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -86,13 +106,32 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { + if (!this.isSelectableItem(item)) { + throw new WiredTriggerSaveException(this.getInvalidSelectionErrorKey()); + } this.items.add(item); } } @@ -103,6 +142,7 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -110,10 +150,12 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id : data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -141,12 +183,15 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -155,10 +200,42 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + protected boolean isSelectableItem(HabboItem item) { + return item != null; + } + + protected String getInvalidSelectionErrorKey() { + return "There was an error while saving that trigger"; + } + + protected int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + + private boolean matchesSourceItem(Iterable candidateItems, HabboItem sourceItem) { + if (candidateItems == null || sourceItem == null) { + return false; + } + + for (HabboItem item : candidateItems) { + if (item != null && item.getId() == sourceItem.getId()) { + return true; + } + } + + return false; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java index 6ddcc503..118c265e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java @@ -44,4 +44,14 @@ public class WiredTriggerHabboClicksTile extends WiredTriggerHabboClicksFurni { String interaction = item.getBaseItem().getInteractionType().getName(); return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION); } + + @Override + protected boolean isSelectableItem(HabboItem item) { + return this.isClickTileItem(item); + } + + @Override + protected String getInvalidSelectionErrorKey() { + return "wiredfurni.error.require_click_tiles"; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java index f64ca1c0..53a42939 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; @@ -15,6 +16,11 @@ import java.sql.SQLException; public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.CLICKS_USER; + private static final String CACHE_BLOCK_MENU_OPEN = "wired.click_user.block_menu_open"; + private static final String CACHE_IGNORE_LOOK_UNTIL = "wired.click_user.ignore_look_until"; + private static final long IGNORE_LOOK_WINDOW_MS = 500L; + private boolean blockMenuOpen = false; + private boolean doNotRotate = false; public WiredTriggerHabboClicksUser(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,15 +43,29 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { @Override public String getWiredData() { - return ""; + return WiredManager.getGson().toJson(new JsonData(this.blockMenuOpen, this.doNotRotate)); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + String wiredData = set.getString("wired_data"); + + this.blockMenuOpen = false; + this.doNotRotate = false; + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = com.eu.habbo.habbohotel.wired.core.WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data != null) { + this.blockMenuOpen = data.blockMenuOpen; + this.doNotRotate = data.doNotRotate; + } + } } @Override public void onPickUp() { + this.blockMenuOpen = false; + this.doNotRotate = false; } @Override @@ -61,7 +81,9 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(2); + message.appendInt(this.blockMenuOpen ? 1 : 0); + message.appendInt(this.doNotRotate ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -70,6 +92,9 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.blockMenuOpen = (params.length > 0) && (params[0] == 1); + this.doNotRotate = (params.length > 1) && (params[1] == 1); return true; } @@ -77,4 +102,86 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { public boolean isTriggeredByRoomUnit() { return true; } + + public boolean isBlockMenuOpen() { + return this.blockMenuOpen; + } + + public boolean isDoNotRotate() { + return this.doNotRotate; + } + + public static void clearRuntimeFlags(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + roomUnit.getCacheable().remove(CACHE_BLOCK_MENU_OPEN); + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + } + + public static void applyRuntimeOptions(RoomUnit roomUnit, boolean blockMenuOpen, boolean doNotRotate) { + if (roomUnit == null) { + return; + } + + if (blockMenuOpen) { + roomUnit.getCacheable().put(CACHE_BLOCK_MENU_OPEN, Boolean.TRUE); + } + + if (doNotRotate) { + roomUnit.getCacheable().put(CACHE_IGNORE_LOOK_UNTIL, System.currentTimeMillis() + IGNORE_LOOK_WINDOW_MS); + } + } + + public static boolean consumeBlockMenuOpen(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().remove(CACHE_BLOCK_MENU_OPEN); + return Boolean.TRUE.equals(value); + } + + public static boolean consumeIgnoreLook(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().get(CACHE_IGNORE_LOOK_UNTIL); + + if (!(value instanceof Long)) { + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + return false; + } + + long expiresAt = (Long) value; + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + + return System.currentTimeMillis() <= expiresAt; + } + + public static boolean hasPendingIgnoreLook(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().get(CACHE_IGNORE_LOOK_UNTIL); + + if (!(value instanceof Long)) { + return false; + } + + return System.currentTimeMillis() <= (Long) value; + } + + static class JsonData { + boolean blockMenuOpen; + boolean doNotRotate; + + public JsonData(boolean blockMenuOpen, boolean doNotRotate) { + this.blockMenuOpen = blockMenuOpen; + this.doNotRotate = doNotRotate; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java index b05746eb..e2200047 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java @@ -17,9 +17,14 @@ import java.sql.SQLException; public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.SAY_SOMETHING; + private static final int MATCH_CONTAINS = 0; + private static final int MATCH_EXACT = 1; + private static final int MATCH_ALL_WORDS = 2; + private boolean hideMessage = false; private boolean ownerOnly = false; private String key = ""; + private int matchMode = MATCH_CONTAINS; public WiredTriggerHabboSaysKeyword(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -31,16 +36,20 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { - if (this.key.length() > 0) { - String text = event.getText().orElse(null); - if (text != null && text.toLowerCase().contains(this.key.toLowerCase())) { - RoomUnit roomUnit = event.getActor().orElse(null); - Room room = event.getRoom(); - Habbo habbo = room.getHabbo(roomUnit); - return !this.ownerOnly || (habbo != null && room.getOwnerId() == habbo.getHabboInfo().getId()); - } + if (this.key.length() <= 0) { + return false; } - return false; + + String text = event.getText().orElse(null); + RoomUnit roomUnit = event.getActor().orElse(null); + Room room = event.getRoom(); + + if (text == null || roomUnit == null || room == null || !this.matchesText(text)) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + return !this.ownerOnly || (habbo != null && room.getOwnerId() == habbo.getHabboInfo().getId()); } @Deprecated @@ -52,8 +61,10 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.hideMessage, this.ownerOnly, - this.key + this.key, + this.matchMode )); } @@ -64,21 +75,27 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.ownerOnly = data.ownerOnly; + this.hideMessage = data.hideMessage; this.key = data.key; + this.matchMode = this.normalizeMatchMode(data.matchMode); } else { String[] data = wiredData.split("\t"); if (data.length == 2) { this.ownerOnly = data[0].equalsIgnoreCase("1"); this.key = data[1]; + this.hideMessage = false; + this.matchMode = MATCH_CONTAINS; } } } @Override public void onPickUp() { + this.hideMessage = false; this.ownerOnly = false; this.key = ""; + this.matchMode = MATCH_CONTAINS; } @Override @@ -94,8 +111,11 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.key); + message.appendInt(3); + message.appendInt(this.matchMode); + message.appendInt(this.hideMessage ? 1 : 0); + message.appendInt(this.ownerOnly ? 1 : 0); message.appendInt(0); - message.appendInt(1); message.appendInt(this.getType().code); message.appendInt(0); message.appendInt(0); @@ -103,8 +123,10 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.ownerOnly = settings.getIntParams()[0] == 1; + int[] params = settings.getIntParams(); + this.matchMode = (params.length > 0) ? this.normalizeMatchMode(params[0]) : MATCH_CONTAINS; + this.hideMessage = (params.length > 1) && (params[1] == 1); + this.ownerOnly = (params.length > 2) && (params[2] == 1); this.key = settings.getStringParam(); return true; @@ -115,13 +137,50 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { return true; } + public boolean isHideMessage() { + return this.hideMessage; + } + + private boolean matchesText(String text) { + String normalizedText = text.toLowerCase().trim(); + String normalizedKey = this.key.toLowerCase().trim(); + + switch (this.matchMode) { + case MATCH_EXACT: + return normalizedText.equals(normalizedKey); + case MATCH_ALL_WORDS: + String[] requiredParts = normalizedKey.split("\\s+"); + for (String part : requiredParts) { + if (!part.isEmpty() && !normalizedText.contains(part)) { + return false; + } + } + return true; + case MATCH_CONTAINS: + default: + return normalizedText.contains(normalizedKey); + } + } + + private int normalizeMatchMode(int value) { + if (value < MATCH_CONTAINS || value > MATCH_ALL_WORDS) { + return MATCH_CONTAINS; + } + + return value; + } + static class JsonData { + boolean hideMessage; boolean ownerOnly; String key; + int matchMode; - public JsonData(boolean ownerOnly, String key) { + public JsonData(boolean hideMessage, boolean ownerOnly, String key, int matchMode) { + this.hideMessage = hideMessage; this.ownerOnly = ownerOnly; this.key = key; + this.matchMode = matchMode; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java index 3e83d923..d08790a8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,6 +11,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -21,7 +24,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.WALKS_OFF_FURNI; - private THashSet items; + private final THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboWalkOffFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,24 +40,11 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem == null) { - return false; - } - - if (this.items.contains(sourceItem)) { - return true; - } - Room room = event.getRoom(); - if (room != null) { - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - } - - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -64,7 +55,8 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new WiredTriggerFurniStateToggled.JsonData( + return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -72,10 +64,12 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -101,12 +95,15 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -140,7 +137,8 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -149,12 +147,28 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { this.items.add(item); } @@ -169,10 +183,20 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java index 6f8d7ddc..d6fb8f96 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,6 +11,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -21,7 +24,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.WALKS_ON_FURNI; - private THashSet items; + private final THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboWalkOnFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,24 +40,11 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem == null) { - return false; - } - - if (this.items.contains(sourceItem)) { - return true; - } - Room room = event.getRoom(); - if (room != null) { - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - } - - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -93,7 +84,8 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -102,12 +94,28 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { this.items.add(item); } @@ -119,6 +127,7 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -126,10 +135,12 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -155,12 +166,15 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -169,10 +183,20 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } } 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 13463c7b..7e491a54 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 @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; 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.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -11,6 +12,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer; @@ -25,11 +28,13 @@ import java.util.stream.Collectors; public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.RECEIVE_SIGNAL; - private static final String ANTENNA_INTERACTION = "antenna"; private static final long ACTIVATION_PULSE_MS = 300L; + private static final String ANTENNA_INTERACTION = "antenna"; + private static final String REQUIRE_ANTENNA_ERROR = "Puoi selezionare solo furni antenna."; private int channel = 0; // signal channel (0-based) private THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; private final AtomicLong activationToken = new AtomicLong(); public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException { @@ -46,15 +51,18 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public boolean matches(HabboItem triggerItem, WiredEvent event) { if (event.getType() != WiredEvent.Type.SIGNAL_RECEIVED) return false; - if (!this.items.isEmpty()) { - int signalChannel = event.getSignalChannel(); - for (HabboItem antenna : this.items) { - if (antenna != null && antenna.getId() == signalChannel) return true; - } + if (event.getSignalChannel() != this.channel) { return false; } - return event.getSignalChannel() == this.channel; + HabboItem sourceItem = event.getSourceItem().orElse(null); + if (sourceItem == null) { + return false; + } + + return WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() + .filter(this::isAntennaItem) + .anyMatch(item -> item != null && item.getId() == sourceItem.getId()); } public int getChannel() { @@ -82,13 +90,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { int senderCount = 0; try { if (room != null && room.getRoomSpecialTypes() != null) { - if (!this.items.isEmpty()) { - for (HabboItem item : this.items) { - senderCount += room.getRoomSpecialTypes().countSendersTargetingReceiver(item.getId()); - } - } else { - senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); - } + senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); } } catch (Exception e) { } @@ -112,10 +114,11 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(3); + message.appendInt(4); message.appendInt(channel); message.appendInt(senderCount); message.appendInt(RoomSpecialTypes.MAX_SENDERS_PER_RECEIVER); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -124,20 +127,32 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); int count = settings.getFurniIds().length; - for (int i = 0; i < count; i++) { - HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); - if (item == null) continue; - if (!isAntennaItem(item)) throw new WiredTriggerSaveException("wiredfurni.error.require_antenna_furni"); - this.items.add(item); - } - int[] params = settings.getIntParams(); this.channel = params.length > 0 ? params[0] : 0; + this.furniSource = (params.length > 1) + ? this.normalizeFurniSource(params[params.length - 1]) + : WiredSourceUtil.SOURCE_SELECTED; + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) { + for (int i = 0; i < count; i++) { + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); + if (item == null) continue; + if (!this.isAntennaItem(item)) throw new WiredTriggerSaveException(REQUIRE_ANTENNA_ERROR); + + this.items.add(item); + } + } + return true; } @@ -174,6 +189,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( channel, + furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -182,15 +198,20 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items = new THashSet<>(); String wiredData = set.getString("wired_data"); + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.channel = data.channel; + this.furniSource = this.normalizeFurniSource(data.furniSource); 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_SELECTOR) this.furniSource = WiredSourceUtil.SOURCE_SELECTED; } } @@ -198,26 +219,37 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public void onPickUp() { this.channel = 0; this.items.clear(); - } - - private boolean isAntennaItem(HabboItem item) { - if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false; - String interaction = item.getBaseItem().getInteractionType().getName(); - if (interaction == null) return false; - - String normalized = interaction.toLowerCase(); - return normalized.equals(ANTENNA_INTERACTION); + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; } static class JsonData { int channel; + int furniSource; List itemIds; public JsonData() {} - public JsonData(int channel, List itemIds) { + public JsonData(int channel, int furniSource, List itemIds) { this.channel = channel; + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_SELECTED; + } + + private boolean isAntennaItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) { + return false; + } + + String interaction = item.getBaseItem().getInteractionType().getName(); + return interaction != null && ANTENNA_INTERACTION.equalsIgnoreCase(interaction); + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java index 179e9597..8bf9c7b2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java @@ -1,10 +1,12 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; +import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.items.Item; 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.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; @@ -17,6 +19,7 @@ import java.sql.SQLException; public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.SCORE_ACHIEVED; private int score = 0; + private int teamType = GameTeamColors.NONE.type; public WiredTriggerScoreAchieved(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -32,7 +35,23 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { int amountAdded = event.getScoreAdded(); // Check if this score addition crossed the threshold - return points - amountAdded < this.score && points >= this.score; + if (!(points - amountAdded < this.score && points >= this.score)) { + return false; + } + + if (this.teamType == GameTeamColors.NONE.type) { + return true; + } + + if (!event.getActor().isPresent()) { + return false; + } + + Habbo habbo = event.getRoom().getHabbo(event.getActor().get()); + + return habbo != null + && habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getGamePlayer().getTeamColor().type == this.teamType; } @Deprecated @@ -44,7 +63,8 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( - this.score + this.score, + this.teamType )); } @@ -55,17 +75,20 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.score = data.score; + this.teamType = normalizeTeamType(data.teamType); } else { try { this.score = Integer.parseInt(wiredData); } catch (Exception e) { } + this.teamType = GameTeamColors.NONE.type; } } @Override public void onPickUp() { this.score = 0; + this.teamType = GameTeamColors.NONE.type; } @Override @@ -81,8 +104,9 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.score); + message.appendInt(this.teamType); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -93,6 +117,9 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; this.score = settings.getIntParams()[0]; + this.teamType = (settings.getIntParams().length > 1) + ? normalizeTeamType(settings.getIntParams()[1]) + : GameTeamColors.NONE.type; return true; } @@ -101,11 +128,21 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { return true; } + private int normalizeTeamType(int value) { + if (value >= GameTeamColors.RED.type && value <= GameTeamColors.YELLOW.type) { + return value; + } + + return GameTeamColors.NONE.type; + } + static class JsonData { int score; + int teamType; - public JsonData(int score) { + public JsonData(int score, int teamType) { this.score = score; + this.teamType = teamType; } } } 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 6d00aafe..2e56b6b0 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 @@ -686,6 +686,15 @@ public class RoomSpecialTypes { return result; } + /** + * Finds a wired extra by its item ID. + * @param itemId The item ID to search for + * @return The extra if found, null otherwise + */ + public InteractionWiredExtra getExtra(int itemId) { + return this.wiredExtras.get(itemId); + } + /** * Gets all wired extras at specific coordinates using spatial index. * @param x The X coordinate diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java index 96172f84..63fe4f9e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java @@ -36,7 +36,9 @@ public enum WiredConditionType { TEAM_HAS_SCORE(34), TEAM_HAS_RANK(35), MATCH_TIME(36), - MATCH_DATE(37); + MATCH_DATE(37), + ACTOR_DIR(38), + SLC_QUANTITY(39); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index 332cbe7f..d8755d12 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -41,7 +41,20 @@ public enum WiredEffectType { SET_ALTITUDE(39), RELATIVE_MOVE(40), CONTROL_CLOCK(41), - ADJUST_CLOCK(42); + ADJUST_CLOCK(42), + MOVE_ROTATE_USER(43), + FURNI_ALTITUDE_SELECTOR(44), + FURNI_ON_FURNI_SELECTOR(45), + FURNI_PICKS_SELECTOR(46), + FURNI_SIGNAL_SELECTOR(47), + USERS_SIGNAL_SELECTOR(48), + USERS_BY_TYPE_SELECTOR(49), + USERS_TEAM_SELECTOR(50), + USERS_BY_ACTION_SELECTOR(51), + USERS_BY_NAME_SELECTOR(52), + USERS_ON_FURNI_SELECTOR(53), + USERS_GROUP_SELECTOR(54), + USERS_HANDITEM_SELECTOR(55); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java index 7771450b..5179e503 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java @@ -6,13 +6,19 @@ public class WiredMatchFurniSetting { public final int rotation; public final int x; public final int y; + public final double z; public WiredMatchFurniSetting(int itemId, String state, int rotation, int x, int y) { + this(itemId, state, rotation, x, y, 0.0D); + } + + public WiredMatchFurniSetting(int itemId, String state, int rotation, int x, int y, double z) { this.item_id = itemId; this.state = state.replace("\t\t\t", " "); this.rotation = rotation; this.x = x; this.y = y; + this.z = z; } @Override @@ -21,7 +27,7 @@ public class WiredMatchFurniSetting { } public String toString(boolean includeState) { - return this.item_id + "-" + (this.state.isEmpty() || !includeState ? " " : this.state) + "-" + this.rotation + "-" + this.x + "-" + this.y; + return this.item_id + "-" + (this.state.isEmpty() || !includeState ? " " : this.state) + "-" + this.rotation + "-" + this.x + "-" + this.y + "-" + this.z; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java new file mode 100644 index 00000000..fa40b95e --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java @@ -0,0 +1,59 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.bots.Bot; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class WiredBotSourceUtil { + public static final int SOURCE_BOT_NAME = 100; + + private WiredBotSourceUtil() { + } + + public static int normalizeBotSource(int value) { + return normalizeBotSource(value, SOURCE_BOT_NAME); + } + + public static int normalizeBotSource(int value, int fallback) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case SOURCE_BOT_NAME: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return fallback; + } + } + + public static List resolveBots(WiredContext ctx, Room room, int botSource, String botName) { + if (ctx == null || room == null) { + return Collections.emptyList(); + } + + if (botSource == SOURCE_BOT_NAME) { + List bots = room.getBots(botName); + return (bots != null) ? new ArrayList<>(bots) : Collections.emptyList(); + } + + List resolved = new ArrayList<>(); + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, botSource)) { + Bot bot = room.getBot(roomUnit); + + if (bot != null && !resolved.contains(bot)) { + resolved.add(bot); + } + } + + return resolved; + } + + public static boolean requiresTriggeringUser(int botSource) { + return botSource == WiredSourceUtil.SOURCE_TRIGGER; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java index 0cc66859..beee0014 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java @@ -4,7 +4,11 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; 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.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboSaysKeyword; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; @@ -172,6 +176,7 @@ public final class WiredEngine { debug(room, "Processing {} stacks for event type {}", stacks.size(), event.getType()); boolean anyTriggered = false; + boolean suppressSaysOutput = false; long currentTime = System.currentTimeMillis(); for (WiredStack stack : stacks) { @@ -179,6 +184,12 @@ public final class WiredEngine { boolean triggered = processStack(stack, event, currentTime); if (triggered) { anyTriggered = true; + + if ((event.getType() == WiredEvent.Type.USER_SAYS) + && (stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword) + && ((WiredTriggerHabboSaysKeyword) stack.triggerItem()).isHideMessage()) { + suppressSaysOutput = true; + } } } catch (WiredLimitException limitEx) { debug(room, "Stack execution stopped (limit): {}", limitEx.getMessage()); @@ -188,6 +199,10 @@ public final class WiredEngine { } } + if (event.getType() == WiredEvent.Type.USER_SAYS) { + return suppressSaysOutput; + } + return anyTriggered; } @@ -224,6 +239,7 @@ public final class WiredEngine { List executedSelectors = Collections.emptyList(); if (stack.hasEffects()) { executedSelectors = executeSelectors(stack, ctx); + applySelectionFilterExtras(stack, ctx, executedSelectors); } // Evaluate conditions @@ -245,6 +261,16 @@ public final class WiredEngine { return false; } + if ((event.getType() == WiredEvent.Type.USER_CLICKS_USER) + && (stack.triggerItem() instanceof WiredTriggerHabboClicksUser) + && event.getActor().isPresent()) { + WiredTriggerHabboClicksUser clickUserTrigger = (WiredTriggerHabboClicksUser) stack.triggerItem(); + WiredTriggerHabboClicksUser.applyRuntimeOptions( + event.getActor().get(), + clickUserTrigger.isBlockMenuOpen(), + clickUserTrigger.isDoNotRotate()); + } + RoomUnit actor = event.getActor().orElse(null); // Only show the trigger/selector activation when the stack is actually allowed to continue. @@ -462,6 +488,65 @@ public final class WiredEngine { wiredEffect.activateBox(room, actor, currentTime); } } + + private void applySelectionFilterExtras(WiredStack stack, WiredContext ctx, List executedSelectors) { + if (executedSelectors == null || executedSelectors.isEmpty()) { + return; + } + + Room room = ctx.room(); + if (room == null || stack.triggerItem() == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras( + stack.triggerItem().getX(), + stack.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 (ctx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + ctx.targets().setItems(limitIterable(ctx.targets().items(), furniLimit)); + } + + if (ctx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + ctx.targets().setUsers(limitIterable(ctx.targets().users(), userLimit)); + } + } + + private 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)); + } /** * Schedule a delayed effect execution. diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index 38464b93..0a517592 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectGiveReward; import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectTriggerStacks; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; @@ -35,6 +36,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; /** * Manager class for the new wired engine system. @@ -80,6 +83,7 @@ public final class WiredManager { private static final boolean DEFAULT_ENABLED = false; private static final boolean DEFAULT_EXCLUSIVE = false; private static final int DEFAULT_MAX_STEPS = 100; + private static final long FURNI_CLICK_TRIGGER_DELAY_MS = 400L; /** The singleton engine instance */ private static volatile WiredEngine engine; @@ -89,6 +93,7 @@ public final class WiredManager { /** Whether the engine is initialized */ private static volatile boolean initialized = false; + private static final ConcurrentHashMap> pendingFurniClickTriggers = new ConcurrentHashMap<>(); private WiredManager() { // Static utility class @@ -163,6 +168,13 @@ public final class WiredManager { engine.clearUnseenCache(); } + pendingFurniClickTriggers.values().forEach(future -> { + if (future != null) { + future.cancel(false); + } + }); + pendingFurniClickTriggers.clear(); + initialized = false; LOGGER.info("Wired Manager shutdown complete"); } @@ -250,6 +262,41 @@ public final class WiredManager { return handleEvent(event); } + public static void queueUserClicksFurni(Room room, RoomUnit user, HabboItem item) { + if (!isEnabled() || room == null || user == null || item == null) { + return; + } + + String clickKey = getPendingFurniClickKey(room, user, item); + + cancelPendingUserClicksFurni(room, user, item); + + ScheduledFuture future = Emulator.getThreading().run(() -> { + pendingFurniClickTriggers.remove(clickKey); + triggerUserClicksFurni(room, user, item); + }, FURNI_CLICK_TRIGGER_DELAY_MS); + + if (future != null) { + pendingFurniClickTriggers.put(clickKey, future); + } + } + + public static void cancelPendingUserClicksFurni(Room room, RoomUnit user, HabboItem item) { + if (room == null || user == null || item == null) { + return; + } + + ScheduledFuture future = pendingFurniClickTriggers.remove(getPendingFurniClickKey(room, user, item)); + + if (future != null) { + future.cancel(false); + } + } + + private static String getPendingFurniClickKey(Room room, RoomUnit user, HabboItem item) { + return room.getId() + ":" + user.getId() + ":" + item.getId(); + } + /** * Trigger when a user clicks invisible click tile furniture. */ @@ -270,6 +317,8 @@ public final class WiredManager { return false; } + WiredTriggerHabboClicksUser.clearRuntimeFlags(clickingUser); + WiredEvent event = WiredEvents.userClicksUser(room, clickingUser, clickedUser); return handleEvent(event); } 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 new file mode 100644 index 00000000..807d4c47 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java @@ -0,0 +1,148 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import gnu.trove.set.hash.THashSet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class WiredTriggerSourceUtil { + private WiredTriggerSourceUtil() { + } + + public static List resolveItems(InteractionWiredTrigger trigger, + WiredEvent event, + int sourceType, + Collection selectedItems) { + switch (sourceType) { + case WiredSourceUtil.SOURCE_TRIGGER: + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + case WiredSourceUtil.SOURCE_SELECTED: + return (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList(); + case WiredSourceUtil.SOURCE_SELECTOR: + return resolveSelectorItems(trigger, event); + case WiredSourceUtil.SOURCE_SIGNAL: + if (event.getType() == WiredEvent.Type.SIGNAL_RECEIVED) { + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + } + return Collections.emptyList(); + default: + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + } + } + + public static List resolveUsers(InteractionWiredTrigger trigger, + WiredEvent event, + int sourceType, + Collection selectedUsers) { + switch (sourceType) { + case WiredSourceUtil.SOURCE_TRIGGER: + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + case WiredSourceUtil.SOURCE_SELECTED: + return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList(); + case WiredSourceUtil.SOURCE_SELECTOR: + return resolveSelectorUsers(trigger, event); + case WiredSourceUtil.SOURCE_SIGNAL: + if (event.getType() == WiredEvent.Type.SIGNAL_RECEIVED) { + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + } + return Collections.emptyList(); + default: + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + } + } + + public static boolean containsItemOrTile(Room room, Collection items, HabboItem sourceItem) { + if (room == null || items == null || items.isEmpty() || sourceItem == null) { + return false; + } + + if (items.contains(sourceItem)) { + return true; + } + + for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { + if (items.contains(item)) { + return true; + } + } + + return false; + } + + public static boolean containsUser(Collection users, RoomUnit target) { + if (users == null || users.isEmpty() || target == null) { + return false; + } + + for (RoomUnit user : users) { + if (user != null && user.getId() == target.getId()) { + return true; + } + } + + return false; + } + + private static List resolveSelectorItems(InteractionWiredTrigger trigger, WiredEvent event) { + WiredContext ctx = executeSelectors(trigger, event); + + if (ctx == null || !ctx.targets().isItemsModifiedBySelector()) { + return Collections.emptyList(); + } + + return new ArrayList<>(ctx.targets().items()); + } + + private static List resolveSelectorUsers(InteractionWiredTrigger trigger, WiredEvent event) { + WiredContext ctx = executeSelectors(trigger, event); + + if (ctx == null || !ctx.targets().isUsersModifiedBySelector()) { + return Collections.emptyList(); + } + + return new ArrayList<>(ctx.targets().users()); + } + + private static WiredContext executeSelectors(InteractionWiredTrigger trigger, WiredEvent event) { + if (trigger == null || event == null) { + return null; + } + + Room room = event.getRoom(); + if (room == null || room.getRoomSpecialTypes() == null) { + 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; + } + + if (effect.requiresActor() && !ctx.hasActor()) { + continue; + } + + try { + ctx.state().step(); + effect.execute(ctx); + } catch (Exception ignored) { + } + } + + return ctx; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java index 165f0c1d..155001ec 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java @@ -108,8 +108,8 @@ public final class WiredEvents { */ public static WiredEvent userClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) { return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_USER, room) - .actor(clickedUser) - .targetUnit(clickingUser) + .actor(clickingUser) + .targetUnit(clickedUser) .tile(clickedUser.getCurrentLocation()) .build(); } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java index 6ae99006..640d0ce0 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java @@ -25,7 +25,7 @@ public class ClickFurniEvent extends MessageHandler { return; } - WiredManager.triggerUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + WiredManager.queueUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); if (isClickTileItem(item)) { WiredManager.triggerUserClicksTile(room, this.client.getHabbo().getRoomUnit(), item); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java index 3cf6eec0..e81a651b 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionMonsterPlantSe import com.eu.habbo.habbohotel.pets.MonsterplantPet; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rooms.items.RemoveFloorItemComposer; import com.eu.habbo.messages.outgoing.rooms.pets.PetPackageComposer; @@ -40,6 +41,8 @@ public class ToggleFloorItemEvent extends MessageHandler { if (item == null || item instanceof InteractionDice) return; + WiredManager.cancelPendingUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + Event furnitureToggleEvent = new FurnitureToggleEvent(item, this.client.getHabbo(), state); Emulator.getPluginManager().fireEvent(furnitureToggleEvent); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java index d4020edf..b24b6f66 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java @@ -3,6 +3,7 @@ package com.eu.habbo.messages.incoming.rooms.items; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.plugin.Event; import com.eu.habbo.plugin.events.furniture.FurnitureToggleEvent; @@ -23,6 +24,8 @@ public class ToggleWallItemEvent extends MessageHandler { if (item == null) return; + WiredManager.cancelPendingUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + Event furnitureToggleEvent = new FurnitureToggleEvent(item, this.client.getHabbo(), state); Emulator.getPluginManager().fireEvent(furnitureToggleEvent); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java index 5426e842..d8c0b2f2 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java @@ -3,8 +3,10 @@ package com.eu.habbo.messages.incoming.rooms.users; 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.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.users.InClientLinkComposer; public class ClickUserEvent extends MessageHandler { @Override @@ -29,5 +31,13 @@ public class ClickUserEvent extends MessageHandler { } WiredManager.triggerUserClicksUser(room, clickingUser, clickedHabbo.getRoomUnit()); + + if (WiredTriggerHabboClicksUser.hasPendingIgnoreLook(clickingUser)) { + this.client.sendResponse(new InClientLinkComposer("avatar-info/block-rotate")); + } + + if (WiredTriggerHabboClicksUser.consumeBlockMenuOpen(clickingUser)) { + this.client.sendResponse(new InClientLinkComposer("avatar-info/block-menu")); + } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java index 473b362c..50fc065d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java @@ -4,6 +4,7 @@ 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.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.messages.incoming.MessageHandler; @@ -40,6 +41,9 @@ public class RoomUserLookAtPoint extends MessageHandler { if (roomUnit.isIdle()) return; + if (WiredTriggerHabboClicksUser.consumeIgnoreLook(roomUnit)) + return; + int x = this.packet.readInt(); int y = this.packet.readInt(); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java index 1d9266f3..90365132 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java @@ -14,6 +14,7 @@ import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.math.BigDecimal; public class WiredApplySetConditionsEvent extends MessageHandler { @@ -81,13 +82,21 @@ public class WiredApplySetConditionsEvent extends MessageHandler { room.moveFurniTo(matchItem, oldLocation, setting.rotation, null, true); } } + else if(wired.shouldMatchAltitude() && !wired.shouldMatchPosition()) { + int newRotation = wired.shouldMatchRotation() ? setting.rotation : matchItem.getRotation(); + if (BigDecimal.valueOf(matchItem.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0 + || newRotation != matchItem.getRotation()) { + room.moveFurniTo(matchItem, oldLocation, newRotation, setting.z, null, true, false); + } + } else if(wired.shouldMatchPosition()) { boolean slideAnimation = !wired.shouldMatchRotation() || matchItem.getRotation() == setting.rotation; RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); int newRotation = wired.shouldMatchRotation() ? setting.rotation : matchItem.getRotation(); + double newZ = wired.shouldMatchAltitude() ? setting.z : matchItem.getZ(); if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != matchItem.getRotation()) && room.furnitureFitsAt(newLocation, matchItem, newRotation, true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(matchItem, newLocation, newRotation, null, !slideAnimation) == FurnitureMovementError.NONE) { + if(room.moveFurniTo(matchItem, newLocation, newRotation, newZ, null, !slideAnimation, true) == FurnitureMovementError.NONE) { if(slideAnimation) { room.sendComposer(new FloorItemOnRollerComposer(matchItem, null, oldLocation, oldZ, newLocation, matchItem.getZ(), 0, room).compose()); } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java index aac7ec7a..265a3a07 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java @@ -3,6 +3,7 @@ package com.eu.habbo.messages.incoming.wired; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionWired; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.rooms.Room; @@ -11,10 +12,6 @@ import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.generic.alerts.UpdateFailedComposer; import com.eu.habbo.messages.outgoing.wired.WiredSavedComposer; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Optional; - public class WiredEffectSaveDataEvent extends MessageHandler { @Override public void handle() throws Exception { @@ -25,40 +22,36 @@ public class WiredEffectSaveDataEvent extends MessageHandler { if (room != null) { if (room.hasRights(this.client.getHabbo()) || room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER) || this.client.getHabbo().hasPermission(Permission.ACC_MOVEROTATE)) { InteractionWiredEffect effect = room.getRoomSpecialTypes().getEffect(itemId); + InteractionWiredExtra extra = room.getRoomSpecialTypes().getExtra(itemId); try { - if (effect == null) - throw new WiredSaveException(String.format("Wired effect with item id %s not found in room", itemId)); + if (effect == null && extra == null) + throw new WiredSaveException(String.format("Wired effect/extra with item id %s not found in room", itemId)); - Optional saveMethod = Arrays.stream(effect.getClass().getMethods()).filter(x -> x.getName().equals("saveData")).findFirst(); + WiredSettings settings = InteractionWired.readSettings(this.packet, true); + boolean saved; - if(saveMethod.isPresent()) { - if(saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { - WiredSettings settings = InteractionWired.readSettings(this.packet, true); - if (effect.saveData(settings, this.client)) { - this.client.sendResponse(new WiredSavedComposer()); - effect.needsUpdate(true); - Emulator.getThreading().run(effect); - - // Invalidate wired cache when effect is saved - WiredManager.invalidateRoom(room); - } - } - else { - if ((boolean) saveMethod.get().invoke(effect, this.packet, this.client)) { - this.client.sendResponse(new WiredSavedComposer()); - effect.needsUpdate(true); - Emulator.getThreading().run(effect); - - // Invalidate wired cache when effect is saved - WiredManager.invalidateRoom(room); - } - } + if (effect != null) { + saved = effect.saveData(settings, this.client); } else { - this.client.sendResponse(new UpdateFailedComposer("Save method was not found")); + saved = extra.saveData(settings, this.client); } + if (saved) { + this.client.sendResponse(new WiredSavedComposer()); + if (effect != null) { + effect.needsUpdate(true); + Emulator.getThreading().run(effect); + } else { + extra.needsUpdate(true); + Emulator.getThreading().run(extra); + } + // Invalidate wired cache when effect is saved + WiredManager.invalidateRoom(room); + } else { + this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that effect")); + } } catch (WiredSaveException e) { this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java index bf27dc0e..dfa480a3 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java @@ -11,10 +11,6 @@ import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.generic.alerts.UpdateFailedComposer; import com.eu.habbo.messages.outgoing.wired.WiredSavedComposer; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Optional; - public class WiredTriggerSaveDataEvent extends MessageHandler { @Override public void handle() throws Exception { @@ -27,44 +23,21 @@ public class WiredTriggerSaveDataEvent extends MessageHandler { InteractionWiredTrigger trigger = room.getRoomSpecialTypes().getTrigger(itemId); if (trigger != null) { + WiredSettings settings = InteractionWired.readSettings(this.packet, false); - Optional saveMethod = Arrays.stream(trigger.getClass().getMethods()).filter(x -> x.getName().equals("saveData")).findFirst(); + try { + boolean saved = trigger.saveData(settings, this.client); - if(saveMethod.isPresent()) { - if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { - WiredSettings settings = InteractionWired.readSettings(this.packet, false); - - try { - if (trigger.saveData(settings)) { - this.client.sendResponse(new WiredSavedComposer()); - - trigger.needsUpdate(true); - - Emulator.getThreading().run(trigger); - - // Invalidate wired cache when trigger is saved - WiredManager.invalidateRoom(room); - } else { - this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); - } - } catch (WiredTriggerSaveException e) { - this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); - } + if (saved) { + this.client.sendResponse(new WiredSavedComposer()); + trigger.needsUpdate(true); + Emulator.getThreading().run(trigger); + WiredManager.invalidateRoom(room); } else { - if ((boolean) saveMethod.get().invoke(trigger, this.packet)) { - this.client.sendResponse(new WiredSavedComposer()); - trigger.needsUpdate(true); - Emulator.getThreading().run(trigger); - - // Invalidate wired cache when trigger is saved - WiredManager.invalidateRoom(room); - } else { - this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); - } + this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); } - } - else { - this.client.sendResponse(new UpdateFailedComposer("Save method was not found")); + } catch (WiredTriggerSaveException e) { + this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java new file mode 100644 index 00000000..1028a6bb --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java @@ -0,0 +1,25 @@ +package com.eu.habbo.messages.outgoing.wired; + +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +public class WiredExtraDataComposer extends MessageComposer { + private final InteractionWiredExtra extra; + private final Room room; + + public WiredExtraDataComposer(InteractionWiredExtra extra, Room room) { + this.extra = extra; + this.room = room; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.WiredEffectDataComposer); + this.extra.serializeWiredData(this.response, this.room); + this.extra.needsUpdate(true); + return this.response; + } +}