Merge pull request #51 from Lorenzune/feature/pr-20260326

Add wired evaluation and text placeholder extras
This commit is contained in:
DuckieTM
2026-03-26 15:19:59 +01:00
committed by GitHub
17 changed files with 883 additions and 104 deletions
@@ -60,6 +60,7 @@ import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhys
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.*;
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.*;
@@ -353,6 +354,7 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_xtra_mov_physics", WiredExtraMovePhysics.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_exec_in_order", WiredExtraExecuteInOrder.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_execution_limit", WiredExtraExecutionLimit.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_username", WiredExtraTextOutputUsername.class));
this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class));
@@ -5,6 +5,7 @@ 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.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredTextPlaceholderUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -26,10 +27,11 @@ public class WiredEffectAlert extends WiredEffectWhisper {
Habbo habbo = room.getHabbo(unit);
if (habbo == null) continue;
habbo.alert(this.message
String message = this.message
.replace("%online%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "")
.replace("%username%", habbo.getHabboInfo().getUsername())
.replace("%roomsloaded%", Emulator.getGameEnvironment().getRoomManager().loadedRoomsCount() + ""));
.replace("%roomsloaded%", Emulator.getGameEnvironment().getRoomManager().loadedRoomsCount() + "");
habbo.alert(WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, message));
}
}
}
@@ -13,6 +13,7 @@ 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.WiredTextPlaceholderUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
@@ -111,6 +112,8 @@ public class WiredEffectBotTalk extends InteractionWiredEffect {
.replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + "");
}
message = WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, message);
List<Bot> bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName);
for (Bot bot : bots) {
@@ -177,7 +180,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect {
@Override
public boolean requiresTriggeringUser() {
return WiredBotSourceUtil.requiresTriggeringUser(this.botSource);
return WiredBotSourceUtil.requiresTriggeringUser(this.botSource) || WiredTextPlaceholderUtil.requiresActor(this.getRoom(), this);
}
public int getMode() {
@@ -15,6 +15,7 @@ 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;
import com.eu.habbo.habbohotel.wired.core.WiredTextPlaceholderUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
@@ -136,6 +137,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect {
.replace(Emulator.getTexts().getValue("wired.variable.item_count", "%item_count%"), room.itemCount() + "")
.replace(Emulator.getTexts().getValue("wired.variable.roomname", "%roomname%"), room.getName())
.replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + "");
m = WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, m);
for (Bot bot : bots) {
String botMessage = m.replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), bot.getName());
@@ -203,7 +205,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect {
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource);
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource) || WiredTextPlaceholderUtil.requiresActor(this.getRoom(), this);
}
static class JsonData {
@@ -16,6 +16,7 @@ import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.habbohotel.wired.core.WiredTextPlaceholderUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserWhisperComposer;
@@ -70,8 +71,10 @@ public class WiredEffectKickHabbo extends InteractionWiredEffect {
room.giveEffect(habbo, 4, 2);
if (!this.message.isEmpty())
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(this.message, habbo, habbo, RoomChatMessageBubbles.ALERT)));
if (!this.message.isEmpty()) {
String message = WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, this.message);
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(message, habbo, habbo, RoomChatMessageBubbles.ALERT)));
}
Emulator.getThreading().run(new RoomUnitKick(habbo, room, true), 2000);
}
@@ -183,7 +186,7 @@ public class WiredEffectKickHabbo extends InteractionWiredEffect {
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredTextPlaceholderUtil.requiresActor(this.getRoom(), this);
}
static class JsonData {
@@ -14,6 +14,7 @@ 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.habbohotel.wired.core.WiredTextPlaceholderUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserWhisperComposer;
@@ -78,7 +79,9 @@ public class WiredEffectMuteHabbo extends InteractionWiredEffect {
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)));
String message = this.message.replace("%user%", habbo.getHabboInfo().getUsername()).replace("%online_count%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "").replace("%room_count%", Emulator.getGameEnvironment().getRoomManager().getActiveRooms().size() + "");
message = WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, message);
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(message, habbo, habbo, RoomChatMessageBubbles.WIRED)));
}
}
@@ -137,7 +140,7 @@ public class WiredEffectMuteHabbo extends InteractionWiredEffect {
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredTextPlaceholderUtil.requiresActor(this.getRoom(), this);
}
static class JsonData {
@@ -13,6 +13,7 @@ import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.habbohotel.wired.core.WiredTextPlaceholderUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserWhisperComposer;
@@ -105,6 +106,7 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
if (habbo == null) continue;
String msg = this.message.replace("%user%", habbo.getHabboInfo().getUsername()).replace("%online_count%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "").replace("%room_count%", Emulator.getGameEnvironment().getRoomManager().getActiveRooms().size() + "");
msg = WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, msg);
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(msg, habbo, habbo, RoomChatMessageBubbles.WIRED)));
if (habbo.getRoomUnit().isIdle()) {
@@ -162,7 +164,7 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
return (this.userSource == WiredSourceUtil.SOURCE_TRIGGER) || WiredTextPlaceholderUtil.requiresActor(this.getRoom(), this);
}
static class JsonData {
@@ -1,21 +1,48 @@
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
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.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.users.HabboItem;
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 WiredExtraOrEval extends InteractionWiredExtra {
public static final int CODE = 66;
public static final int MODE_ALL = 0;
public static final int MODE_AT_LEAST_ONE = 1;
public static final int MODE_NOT_ALL = 2;
public static final int MODE_NONE = 3;
public static final int MODE_LESS_THAN = 4;
public static final int MODE_EXACTLY = 5;
public static final int MODE_MORE_THAN = 6;
public static final int MIN_COMPARE_VALUE = 0;
public static final int MAX_COMPARE_VALUE = 100;
private final THashSet<HabboItem> items;
private int evaluationMode = MODE_ALL;
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
private int compareValue = 1;
public WiredExtraOrEval(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
}
public WiredExtraOrEval(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
}
@Override
@@ -23,28 +50,252 @@ public class WiredExtraOrEval extends InteractionWiredExtra {
return false;
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) {
int[] params = settings.getIntParams();
this.evaluationMode = normalizeEvaluationMode((params.length > 0) ? params[0] : MODE_ALL);
this.furniSource = normalizeFurniSource((params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER);
this.compareValue = normalizeCompareValue((params.length > 2) ? params[2] : this.compareValue);
this.items.clear();
if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) {
return true;
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
return false;
}
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
return false;
}
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (isSelectableConditionOrExtra(item)) {
this.items.add(item);
}
}
return true;
}
@Override
public String getWiredData() {
return null;
return WiredManager.getGson().toJson(new JsonData(
this.evaluationMode,
this.furniSource,
this.compareValue,
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
));
}
@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.evaluationMode);
message.appendInt(this.furniSource);
message.appendInt(this.compareValue);
message.appendInt(0);
message.appendInt(CODE);
message.appendInt(0);
message.appendInt(0);
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data != null) {
this.evaluationMode = normalizeEvaluationMode(data.evaluationMode);
this.furniSource = normalizeFurniSource(data.furniSource);
this.compareValue = normalizeCompareValue(data.compareValue);
if (data.itemIds != null) {
for (Integer itemId : data.itemIds) {
HabboItem item = room.getHabboItem(itemId);
if (isSelectableConditionOrExtra(item)) {
this.items.add(item);
}
}
}
}
return;
}
String[] legacyData = wiredData.split("[;\t]");
try {
if (legacyData.length > 0) {
this.evaluationMode = normalizeEvaluationMode(Integer.parseInt(legacyData[0]));
}
if (legacyData.length > 1) {
this.furniSource = normalizeFurniSource(Integer.parseInt(legacyData[1]));
}
if (legacyData.length > 2) {
this.compareValue = normalizeCompareValue(Integer.parseInt(legacyData[2]));
}
} catch (NumberFormatException ignored) {
this.evaluationMode = MODE_ALL;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.compareValue = 1;
}
}
@Override
public void onPickUp() {
this.items.clear();
this.evaluationMode = MODE_ALL;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.compareValue = 1;
}
@Override
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
}
@Override
public boolean hasConfiguration() {
return true;
}
public int getEvaluationMode() {
return this.evaluationMode;
}
public int getFurniSource() {
return this.furniSource;
}
public int getCompareValue() {
return this.compareValue;
}
private void refresh(Room room) {
THashSet<HabboItem> remove = new THashSet<>();
for (HabboItem item : this.items) {
HabboItem roomItem = room.getHabboItem(item.getId());
if (!isSelectableConditionOrExtra(roomItem)) {
remove.add(item);
}
}
for (HabboItem item : remove) {
this.items.remove(item);
}
}
public static boolean matchesMode(int evaluationMode, int matchedRequirements, int totalRequirements, int compareValue) {
if (totalRequirements <= 0) {
return true;
}
switch (normalizeEvaluationMode(evaluationMode)) {
case MODE_AT_LEAST_ONE:
return matchedRequirements > 0;
case MODE_NOT_ALL:
return matchedRequirements > 0 && matchedRequirements < totalRequirements;
case MODE_NONE:
return matchedRequirements == 0;
case MODE_LESS_THAN:
return matchedRequirements < normalizeCompareValue(compareValue);
case MODE_EXACTLY:
return matchedRequirements == normalizeCompareValue(compareValue);
case MODE_MORE_THAN:
return matchedRequirements > normalizeCompareValue(compareValue);
case MODE_ALL:
default:
return matchedRequirements >= totalRequirements;
}
}
private static int normalizeEvaluationMode(int value) {
switch (value) {
case MODE_ALL:
case MODE_AT_LEAST_ONE:
case MODE_NOT_ALL:
case MODE_NONE:
case MODE_LESS_THAN:
case MODE_EXACTLY:
case MODE_MORE_THAN:
return value;
default:
return MODE_ALL;
}
}
private static int normalizeCompareValue(int value) {
return Math.max(MIN_COMPARE_VALUE, Math.min(MAX_COMPARE_VALUE, value));
}
private static 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 static boolean isSelectableConditionOrExtra(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 normalizedInteraction = interaction.toLowerCase();
return normalizedInteraction.startsWith("wf_cnd_") || normalizedInteraction.startsWith("wf_xtra_");
}
static class JsonData {
int evaluationMode;
int furniSource;
int compareValue;
List<Integer> itemIds;
JsonData(int evaluationMode, int furniSource, int compareValue, List<Integer> itemIds) {
this.evaluationMode = evaluationMode;
this.furniSource = furniSource;
this.compareValue = compareValue;
this.itemIds = itemIds;
}
}
}
@@ -0,0 +1,212 @@
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Pattern;
public class WiredExtraTextOutputUsername extends InteractionWiredExtra {
public static final int CODE = 67;
public static final int TYPE_SINGLE = 1;
public static final int TYPE_MULTIPLE = 2;
public static final String DEFAULT_PLACEHOLDER_NAME = "";
public static final String DEFAULT_DELIMITER = ", ";
public static final int MAX_PLACEHOLDER_NAME_LENGTH = 32;
public static final int MAX_DELIMITER_LENGTH = 16;
private static final Pattern WRAPPED_PLACEHOLDER_PATTERN = Pattern.compile("^\\$\\((.*)\\)$");
private String placeholderName = DEFAULT_PLACEHOLDER_NAME;
private int placeholderType = TYPE_SINGLE;
private String delimiter = DEFAULT_DELIMITER;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredExtraTextOutputUsername(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredExtraTextOutputUsername(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[] intParams = settings.getIntParams();
String[] stringData = splitStringData(settings.getStringParam());
this.placeholderType = normalizePlaceholderType((intParams.length > 0) ? intParams[0] : TYPE_SINGLE);
this.userSource = normalizeUserSource((intParams.length > 1) ? intParams[1] : WiredSourceUtil.SOURCE_TRIGGER);
this.placeholderName = normalizePlaceholderName(stringData[0]);
this.delimiter = normalizeDelimiter(stringData[1]);
return true;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(this.placeholderName, this.placeholderType, this.delimiter, this.userSource));
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(0);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.placeholderName + "\t" + this.delimiter);
message.appendInt(2);
message.appendInt(this.placeholderType);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(CODE);
message.appendInt(0);
message.appendInt(0);
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data != null) {
this.placeholderName = normalizePlaceholderName(data.placeholderName);
this.placeholderType = normalizePlaceholderType(data.placeholderType);
this.delimiter = normalizeDelimiter(data.delimiter);
this.userSource = normalizeUserSource(data.userSource);
}
return;
}
String[] legacyData = splitStringData(wiredData);
this.placeholderName = normalizePlaceholderName(legacyData[0]);
this.delimiter = normalizeDelimiter(legacyData[1]);
}
@Override
public void onPickUp() {
this.placeholderName = DEFAULT_PLACEHOLDER_NAME;
this.placeholderType = TYPE_SINGLE;
this.delimiter = DEFAULT_DELIMITER;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
}
@Override
public boolean hasConfiguration() {
return true;
}
public String getPlaceholderName() {
return this.placeholderName;
}
public String getPlaceholderToken() {
return this.placeholderName.isEmpty() ? "" : "$(" + this.placeholderName + ")";
}
public int getPlaceholderType() {
return this.placeholderType;
}
public String getDelimiter() {
return this.delimiter;
}
public int getUserSource() {
return this.userSource;
}
private static String[] splitStringData(String value) {
if (value == null) {
return new String[] { DEFAULT_PLACEHOLDER_NAME, DEFAULT_DELIMITER };
}
String[] parts = value.split("\t", -1);
if (parts.length <= 1) {
return new String[] { value, DEFAULT_DELIMITER };
}
return new String[] { parts[0], parts[1] };
}
private static int normalizePlaceholderType(int value) {
return (value == TYPE_MULTIPLE) ? TYPE_MULTIPLE : TYPE_SINGLE;
}
private static String normalizePlaceholderName(String value) {
if (value == null) {
return DEFAULT_PLACEHOLDER_NAME;
}
String normalized = value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
if (WRAPPED_PLACEHOLDER_PATTERN.matcher(normalized).matches()) {
normalized = normalized.substring(2, normalized.length() - 1).trim();
}
if (normalized.length() > MAX_PLACEHOLDER_NAME_LENGTH) {
normalized = normalized.substring(0, MAX_PLACEHOLDER_NAME_LENGTH);
}
return normalized;
}
private static String normalizeDelimiter(String value) {
if (value == null) {
return DEFAULT_DELIMITER;
}
String normalized = value.replace("\t", "").replace("\r", "").replace("\n", "");
if (normalized.length() > MAX_DELIMITER_LENGTH) {
normalized = normalized.substring(0, MAX_DELIMITER_LENGTH);
}
return normalized;
}
private static int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
String placeholderName;
int placeholderType;
String delimiter;
int userSource;
JsonData(String placeholderName, int placeholderType, String delimiter, int userSource) {
this.placeholderName = placeholderName;
this.placeholderType = placeholderType;
this.delimiter = delimiter;
this.userSource = userSource;
}
}
}
@@ -313,21 +313,22 @@ public class RoomChatManager {
}
}
String wiredSayMessage = roomChatMessage.getMessage();
// Handle commands and wired
boolean suppressSaysOutput = false;
if (chatType != RoomChatType.WHISPER) {
if (CommandHandler.handleCommand(habbo.getClient(), roomChatMessage.getUnfilteredMessage())) {
WiredManager.triggerUserSays(habbo.getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), roomChatMessage.getMessage());
WiredManager.triggerUserSays(habbo.getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), wiredSayMessage);
roomChatMessage.isCommand = true;
return;
}
if (!ignoreWired) {
if (WiredManager.triggerUserSays(habbo.getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), roomChatMessage.getMessage())) {
habbo.getClient().sendResponse(new RoomUserWhisperComposer(
new RoomChatMessage(roomChatMessage.getMessage(), habbo, habbo,
roomChatMessage.getBubble())));
return;
}
suppressSaysOutput = WiredManager.shouldSuppressUserSaysOutput(
habbo.getHabboInfo().getCurrentRoom(),
habbo.getRoomUnit(),
wiredSayMessage);
}
}
@@ -388,9 +389,25 @@ public class RoomChatManager {
if (chatType == RoomChatType.WHISPER) {
this.handleWhisper(habbo, roomChatMessage, prefixMessage, clearPrefixMessage);
} else if (chatType == RoomChatType.TALK) {
this.handleTalk(habbo, roomChatMessage, prefixMessage, clearPrefixMessage, tentRectangle);
if (suppressSaysOutput) {
habbo.getClient().sendResponse(new RoomUserWhisperComposer(
new RoomChatMessage(roomChatMessage.getMessage(), habbo, habbo,
roomChatMessage.getBubble())));
} else {
this.handleTalk(habbo, roomChatMessage, prefixMessage, clearPrefixMessage, tentRectangle);
}
} else if (chatType == RoomChatType.SHOUT) {
this.handleShout(habbo, roomChatMessage, prefixMessage, clearPrefixMessage, tentRectangle);
if (suppressSaysOutput) {
habbo.getClient().sendResponse(new RoomUserWhisperComposer(
new RoomChatMessage(roomChatMessage.getMessage(), habbo, habbo,
roomChatMessage.getBubble())));
} else {
this.handleShout(habbo, roomChatMessage, prefixMessage, clearPrefixMessage, tentRectangle);
}
}
if (chatType != RoomChatType.WHISPER && !ignoreWired && !roomChatMessage.isCommand) {
WiredManager.triggerUserSays(habbo.getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), wiredSayMessage);
}
// Notify bots and talking furniture
@@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectGiveR
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectTriggerStacks;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
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;
import com.eu.habbo.habbohotel.rooms.Room;
@@ -40,8 +41,10 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
public class WiredHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(WiredHandler.class);
@@ -185,20 +188,24 @@ public class WiredHandler {
}
if (!conditions.isEmpty()) {
ArrayList<WiredConditionType> matchedConditions = new ArrayList<>(conditions.size());
for (InteractionWiredCondition searchMatched : conditions) {
if (!matchedConditions.contains(searchMatched.getType()) && searchMatched.operator() == WiredConditionOperator.OR && searchMatched.execute(roomUnit, room, stuff)) {
matchedConditions.add(searchMatched.getType());
int conditionEvaluationMode = WiredExtraOrEval.MODE_ALL;
int conditionEvaluationValue = 1;
for (InteractionWiredExtra extra : extras) {
if (extra instanceof WiredExtraOrEval) {
conditionEvaluationMode = ((WiredExtraOrEval) extra).getEvaluationMode();
conditionEvaluationValue = ((WiredExtraOrEval) extra).getCompareValue();
break;
}
}
for (InteractionWiredCondition condition : conditions) {
if (!((condition.operator() == WiredConditionOperator.OR && matchedConditions.contains(condition.getType())) ||
(condition.operator() == WiredConditionOperator.AND && condition.execute(roomUnit, room, stuff))) &&
!Emulator.getPluginManager().fireEvent(new WiredConditionFailedEvent(room, roomUnit, trigger, condition)).isCancelled()) {
return false;
if (!evaluateConditions(conditions, roomUnit, room, stuff, conditionEvaluationMode, conditionEvaluationValue)) {
for (InteractionWiredCondition condition : conditions) {
if (!Emulator.getPluginManager().fireEvent(new WiredConditionFailedEvent(room, roomUnit, trigger, condition)).isCancelled()) {
break;
}
}
return false;
}
}
@@ -253,6 +260,40 @@ public class WiredHandler {
return false;
}
private static boolean evaluateConditions(THashSet<InteractionWiredCondition> conditions, RoomUnit roomUnit, Room room, Object[] stuff, int evaluationMode, int evaluationValue) {
if (conditions == null || conditions.isEmpty()) {
return true;
}
Map<WiredConditionType, Boolean> orGroupResults = new HashMap<>();
int matchedRequirements = 0;
int totalRequirements = 0;
for (InteractionWiredCondition condition : conditions) {
boolean result = condition.execute(roomUnit, room, stuff);
if (condition.operator() == WiredConditionOperator.OR) {
orGroupResults.merge(condition.getType(), result, (left, right) -> left || right);
continue;
}
totalRequirements++;
if (result) {
matchedRequirements++;
}
}
totalRequirements += orGroupResults.size();
for (Boolean groupResult : orGroupResults.values()) {
if (Boolean.TRUE.equals(groupResult)) {
matchedRequirements++;
}
}
return WiredExtraOrEval.matchesMode(evaluationMode, matchedRequirements, totalRequirements, evaluationValue);
}
private static boolean triggerEffect(InteractionWiredEffect effect, RoomUnit roomUnit, Room room, Object[] stuff, long millis) {
boolean executed = false;
if (effect != null && (effect.canExecute(millis) || (roomUnit != null && effect.requiresTriggeringUser() && Emulator.getConfig().getBoolean("wired.custom.enabled", false) && effect.userCanExecute(roomUnit.getId(), millis)))) {
@@ -36,7 +36,8 @@ public final class WiredStack {
private final List<IWiredEffect> effects;
// Extra modifiers
private final boolean useOrMode; // WiredExtraOrEval present
private final int conditionEvaluationMode; // WiredExtraOrEval mode
private final int conditionEvaluationValue; // WiredExtraOrEval numeric threshold
private final boolean useRandom; // WiredExtraRandom present
private final boolean useUnseen; // WiredExtraUnseen present
private final boolean executeInOrder; // WiredExtraExecuteInOrder present
@@ -53,7 +54,7 @@ public final class WiredStack {
IWiredTrigger trigger,
List<IWiredCondition> conditions,
List<IWiredEffect> effects) {
this(triggerItem, trigger, conditions, effects, false, false, false, false);
this(triggerItem, trigger, conditions, effects, 0, 1, false, false, false);
}
/**
@@ -63,7 +64,8 @@ public final class WiredStack {
* @param trigger the trigger implementation
* @param conditions list of conditions
* @param effects list of effects
* @param useOrMode if true, conditions use OR logic (any pass = success)
* @param conditionEvaluationMode condition evaluation mode from WiredExtraOrEval
* @param conditionEvaluationValue numeric comparison value from WiredExtraOrEval
* @param useRandom if true, select one random effect instead of all
* @param useUnseen if true, execute effects in "unseen" order (round-robin)
* @param executeInOrder if true, execute all regular effects in stable stack order
@@ -72,7 +74,8 @@ public final class WiredStack {
IWiredTrigger trigger,
List<IWiredCondition> conditions,
List<IWiredEffect> effects,
boolean useOrMode,
int conditionEvaluationMode,
int conditionEvaluationValue,
boolean useRandom,
boolean useUnseen,
boolean executeInOrder) {
@@ -80,7 +83,8 @@ public final class WiredStack {
this.trigger = trigger;
this.conditions = conditions != null ? Collections.unmodifiableList(conditions) : Collections.emptyList();
this.effects = effects != null ? Collections.unmodifiableList(effects) : Collections.emptyList();
this.useOrMode = useOrMode;
this.conditionEvaluationMode = conditionEvaluationMode;
this.conditionEvaluationValue = conditionEvaluationValue;
this.useRandom = useRandom;
this.useUnseen = useUnseen;
this.executeInOrder = executeInOrder;
@@ -135,12 +139,19 @@ public final class WiredStack {
}
/**
* Check if OR mode is enabled (WiredExtraOrEval).
* When true, any condition passing means all pass.
* @return true if OR mode is enabled
* Get the condition evaluation mode from WiredExtraOrEval.
* @return evaluation mode code
*/
public boolean useOrMode() {
return useOrMode;
public int conditionEvaluationMode() {
return conditionEvaluationMode;
}
/**
* Get the condition evaluation numeric value from WiredExtraOrEval.
* @return comparison value
*/
public int conditionEvaluationValue() {
return conditionEvaluationValue;
}
/**
@@ -193,7 +204,8 @@ public final class WiredStack {
", trigger=" + (trigger != null ? trigger.listensTo() : "null") +
", conditions=" + conditions.size() +
", effects=" + effects.size() +
", orMode=" + useOrMode +
", conditionEvaluationMode=" + conditionEvaluationMode +
", conditionEvaluationValue=" + conditionEvaluationValue +
", random=" + useRandom +
", unseen=" + useUnseen +
", executeInOrder=" + executeInOrder +
@@ -2,6 +2,7 @@ package com.eu.habbo.habbohotel.wired.core;
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.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
@@ -174,17 +175,30 @@ public final class RoomWiredStackIndex implements WiredStackIndex {
List<IWiredEffect> effects = collectEffects(rawEffects);
// Check for extras
boolean useOrMode = specialTypes.hasExtraType(x, y, WiredExtraOrEval.class);
THashSet<InteractionWiredExtra> extras = specialTypes.getExtras(x, y);
int conditionEvaluationMode = WiredExtraOrEval.MODE_ALL;
int conditionEvaluationValue = 1;
boolean useRandom = specialTypes.hasExtraType(x, y, WiredExtraRandom.class);
boolean useUnseen = specialTypes.hasExtraType(x, y, WiredExtraUnseen.class);
boolean executeInOrder = specialTypes.hasExtraType(x, y, WiredExtraExecuteInOrder.class);
if (extras != null) {
for (InteractionWiredExtra extra : extras) {
if (extra instanceof WiredExtraOrEval) {
conditionEvaluationMode = ((WiredExtraOrEval) extra).getEvaluationMode();
conditionEvaluationValue = ((WiredExtraOrEval) extra).getCompareValue();
break;
}
}
}
return new WiredStack(
trigger,
wrappedTrigger,
conditions,
effects,
useOrMode,
conditionEvaluationMode,
conditionEvaluationValue,
useRandom,
useUnseen,
executeInOrder
@@ -7,6 +7,7 @@ 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.wired.extra.WiredExtraExecutionLimit;
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;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
@@ -305,83 +306,102 @@ public final class WiredEngine {
return true;
}
private boolean wouldTriggerStack(WiredStack stack, WiredEvent event, long currentTime) {
Room room = event.getRoom();
if (!stack.trigger().matches(stack.triggerItem(), event)) {
return false;
}
if (stack.trigger().requiresActor() && !event.getActor().isPresent()) {
return false;
}
WiredState state = new WiredState(maxStepsPerStack);
WiredContext ctx = new WiredContext(event, stack.triggerItem(), stack, services, state, null);
state.step();
List<InteractionWiredEffect> executedSelectors = Collections.emptyList();
if (stack.hasEffects()) {
executedSelectors = executeSelectors(stack, ctx);
applySelectionFilterExtras(stack, ctx, executedSelectors);
}
if (stack.hasConditions() && !evaluateConditions(stack, ctx)) {
return false;
}
WiredExtraExecutionLimit executionLimitExtra = getExecutionLimitExtra(room, stack);
return executionLimitExtra == null || executionLimitExtra.canExecuteAt(currentTime);
}
/**
* Evaluate all conditions in a stack.
*/
private boolean evaluateConditions(WiredStack stack, WiredContext ctx) {
List<IWiredCondition> conditions = stack.conditions();
if (stack.useOrMode()) {
// OR mode: at least one condition must pass
return evaluateOrMode(conditions, ctx);
} else {
// Standard mode: use individual operators
return evaluateStandardMode(conditions, ctx);
}
return evaluateConditionsByMode(conditions, ctx, stack.conditionEvaluationMode(), stack.conditionEvaluationValue());
}
/**
* Evaluate conditions in OR mode (any pass = success).
* Evaluate conditions according to the configured stack mode.
*/
private boolean evaluateOrMode(List<IWiredCondition> conditions, WiredContext ctx) {
// Group by condition type (for legacy compatibility)
Map<String, Boolean> typeResults = new HashMap<>();
private boolean evaluateConditionsByMode(List<IWiredCondition> conditions, WiredContext ctx, int evaluationMode, int evaluationValue) {
if (conditions == null || conditions.isEmpty()) {
return true;
}
Room room = ctx.room();
Map<String, List<Boolean>> groupedOrResults = new LinkedHashMap<>();
int matchedRequirements = 0;
int totalRequirements = 0;
for (IWiredCondition condition : conditions) {
ctx.state().step();
String typeName = condition.getClass().getSimpleName();
if (!typeResults.containsKey(typeName) && condition.evaluate(ctx)) {
typeResults.put(typeName, true);
boolean result = condition.evaluate(ctx);
String conditionKey = getConditionGroupKey(condition);
if (condition.operator() == WiredConditionOperator.OR) {
groupedOrResults.computeIfAbsent(conditionKey, ignored -> new ArrayList<>()).add(result);
debug(room, " Condition (OR group {}) {}: {}", conditionKey, condition.getClass().getSimpleName(), result ? "PASS" : "FAIL");
continue;
}
totalRequirements++;
if (result) {
matchedRequirements++;
}
debug(room, " Condition {}: {}", condition.getClass().getSimpleName(), result ? "PASS" : "FAIL");
}
// At least one condition type must have passed
return !typeResults.isEmpty();
for (Map.Entry<String, List<Boolean>> entry : groupedOrResults.entrySet()) {
totalRequirements++;
boolean groupPassed = entry.getValue().stream().anyMatch(Boolean::booleanValue);
if (groupPassed) {
matchedRequirements++;
}
debug(room, " Condition (OR result {}) : {}", entry.getKey(), groupPassed ? "PASS" : "FAIL");
}
boolean matches = WiredExtraOrEval.matchesMode(evaluationMode, matchedRequirements, totalRequirements, evaluationValue);
debug(room, "Condition eval mode {} value {} matched {}/{} logical requirements => {}", evaluationMode, evaluationValue, matchedRequirements, totalRequirements, matches ? "PASS" : "FAIL");
return matches;
}
/**
* Evaluate conditions in standard mode using operators.
*/
private boolean evaluateStandardMode(List<IWiredCondition> conditions, WiredContext ctx) {
Room room = ctx.room();
// First pass: collect all OR conditions that passed
Map<String, Boolean> orResults = new HashMap<>();
for (IWiredCondition condition : conditions) {
if (condition.operator() == WiredConditionOperator.OR) {
ctx.state().step();
String typeName = condition.getClass().getSimpleName();
boolean result = condition.evaluate(ctx);
debug(room, " Condition (OR) {}: {}", typeName, result ? "PASS" : "FAIL");
if (!orResults.containsKey(typeName) && result) {
orResults.put(typeName, true);
}
}
private String getConditionGroupKey(IWiredCondition condition) {
if (condition instanceof InteractionWiredCondition) {
return String.valueOf(((InteractionWiredCondition) condition).getType());
}
// Second pass: verify all conditions
for (IWiredCondition condition : conditions) {
boolean passes;
String typeName = condition.getClass().getSimpleName();
if (condition.operator() == WiredConditionOperator.OR) {
// OR: passes if any of same type passed
passes = orResults.containsKey(typeName);
debug(room, " Condition (OR check) {}: {}", typeName, passes ? "PASS" : "FAIL");
} else {
// AND: must evaluate and pass
ctx.state().step();
passes = condition.evaluate(ctx);
debug(room, " Condition (AND) {}: {}", typeName, passes ? "PASS" : "FAIL");
}
if (!passes) {
return false;
}
}
return true;
return condition.getClass().getName();
}
/**
@@ -646,6 +666,51 @@ public final class WiredEngine {
}
}
/**
* Preview whether a USER_SAYS event should suppress the public room chat output.
* This mirrors trigger and condition eligibility without executing regular effects.
*/
public boolean shouldSuppressUserSaysOutput(WiredEvent event) {
if (event == null || event.getType() != WiredEvent.Type.USER_SAYS) {
return false;
}
Room room = event.getRoom();
if (room == null || !room.isLoaded()) {
return false;
}
List<WiredStack> stacks = index.getStacks(room, event.getType());
if (stacks.isEmpty()) {
return false;
}
long triggerTime = event.getCreatedAtMs();
for (WiredStack stack : stacks) {
if (!(stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword)) {
continue;
}
WiredTriggerHabboSaysKeyword trigger = (WiredTriggerHabboSaysKeyword) stack.triggerItem();
if (!trigger.isHideMessage()) {
continue;
}
try {
if (wouldTriggerStack(stack, event, triggerTime)) {
return true;
}
} catch (WiredLimitException limitEx) {
debug(room, "Suppression preview stopped (limit): {}", limitEx.getMessage());
} catch (Exception ex) {
LOGGER.warn("Error previewing USER_SAYS suppression in room {}: {}", room.getId(), ex.getMessage());
}
}
return false;
}
private void scheduleOrderedEffectBatch(List<IWiredEffect> batch, WiredContext ctx, int delay, long triggerTime) {
long delayMs = delay * 500L;
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - triggerTime);
@@ -316,6 +316,15 @@ public final class WiredManager {
return handleEvent(event);
}
public static boolean shouldSuppressUserSaysOutput(Room room, RoomUnit user, String message) {
if (!isEnabled() || engine == null || room == null || user == null) {
return false;
}
WiredEvent event = WiredEvents.userSays(room, user, message);
return engine.shouldSuppressUserSaysOutput(event);
}
/**
* Trigger when a user enters the room.
*/
@@ -0,0 +1,113 @@
package com.eu.habbo.habbohotel.wired.core;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
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 gnu.trove.set.hash.THashSet;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
public final class WiredTextPlaceholderUtil {
private WiredTextPlaceholderUtil() {
}
public static String applyUsernamePlaceholders(WiredContext ctx, String text) {
if (ctx == null || text == null || text.isEmpty()) {
return text;
}
Room room = ctx.room();
HabboItem triggerItem = ctx.triggerItem();
if (room == null || triggerItem == null || room.getRoomSpecialTypes() == null) {
return text;
}
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY());
if (extras == null || extras.isEmpty()) {
return text;
}
String resolvedText = text;
for (InteractionWiredExtra extra : WiredExecutionOrderUtil.sort(extras)) {
if (!(extra instanceof WiredExtraTextOutputUsername)) {
continue;
}
WiredExtraTextOutputUsername usernameExtra = (WiredExtraTextOutputUsername) extra;
String placeholderToken = usernameExtra.getPlaceholderToken();
if (placeholderToken.isEmpty() || !resolvedText.contains(placeholderToken)) {
continue;
}
resolvedText = resolvedText.replace(placeholderToken, buildUsernameReplacement(ctx, usernameExtra));
}
return resolvedText;
}
public static boolean requiresActor(Room room, HabboItem stackItem) {
if (room == null || stackItem == null || room.getRoomSpecialTypes() == null) {
return false;
}
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(stackItem.getX(), stackItem.getY());
if (extras == null || extras.isEmpty()) {
return false;
}
for (InteractionWiredExtra extra : extras) {
if (!(extra instanceof WiredExtraTextOutputUsername)) {
continue;
}
int userSource = ((WiredExtraTextOutputUsername) extra).getUserSource();
if ((userSource == WiredSourceUtil.SOURCE_TRIGGER) || (userSource == WiredSourceUtil.SOURCE_CLICKED_USER)) {
return true;
}
}
return false;
}
private static String buildUsernameReplacement(WiredContext ctx, WiredExtraTextOutputUsername extra) {
List<RoomUnit> users = WiredSourceUtil.resolveUsers(ctx, extra.getUserSource());
if (users.isEmpty()) {
return "";
}
Room room = ctx.room();
LinkedHashSet<Integer> seenUserIds = new LinkedHashSet<>();
List<String> usernames = new ArrayList<>();
for (RoomUnit unit : users) {
if ((unit == null) || !seenUserIds.add(unit.getId())) {
continue;
}
Habbo habbo = room.getHabbo(unit);
if ((habbo == null) || (habbo.getHabboInfo() == null)) {
continue;
}
usernames.add(habbo.getHabboInfo().getUsername());
}
if (usernames.isEmpty()) {
return "";
}
if (extra.getPlaceholderType() == WiredExtraTextOutputUsername.TYPE_MULTIPLE) {
return String.join(extra.getDelimiter(), usernames);
}
return usernames.get(0);
}
}
@@ -11,8 +11,19 @@ import gnu.trove.procedure.TIntProcedure;
import java.util.ArrayList;
public class UserClothesComposer extends MessageComposer {
private static class ClothEntry {
private final String name;
private final int[] setIds;
private ClothEntry(String name, int[] setIds) {
this.name = name;
this.setIds = setIds;
}
}
private final ArrayList<Integer> idList = new ArrayList<>();
private final ArrayList<String> nameList = new ArrayList<>();
private final ArrayList<ClothEntry> clothEntries = new ArrayList<>();
public UserClothesComposer(Habbo habbo) {
habbo.getInventory().getWardrobeComponent().getClothing().forEach(new TIntProcedure() {
@@ -31,6 +42,12 @@ public class UserClothesComposer extends MessageComposer {
return true;
}
});
for (ClothItem item : Emulator.getGameEnvironment().getCatalogManager().clothing.values()) {
if (item != null) {
this.clothEntries.add(new ClothEntry(item.name, item.setId));
}
}
}
@Override
@@ -40,6 +57,17 @@ public class UserClothesComposer extends MessageComposer {
this.idList.forEach(this.response::appendInt);
this.response.appendInt(this.nameList.size());
this.nameList.forEach(this.response::appendString);
this.response.appendInt(this.clothEntries.size());
for (ClothEntry entry : this.clothEntries) {
this.response.appendString(entry.name);
this.response.appendInt(entry.setIds.length);
for (int setId : entry.setIds) {
this.response.appendInt(setId);
}
}
return this.response;
}