You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
feat: update wired movement and show message behavior
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
INSERT INTO `chat_bubbles` (`type`, `name`, `permission`, `overridable`, `triggers_talking_furniture`) VALUES
|
||||
(200, 'SHOW_MESSAGE_RED', '', 1, 0),
|
||||
(201, 'SHOW_MESSAGE_GREEN', '', 1, 0),
|
||||
(202, 'SHOW_MESSAGE_BLUE', '', 1, 0),
|
||||
(210, 'SHOW_MESSAGE_ALERT', '', 1, 0),
|
||||
(211, 'SHOW_MESSAGE_INFO', '', 1, 0),
|
||||
(212, 'SHOW_MESSAGE_WARNING', '', 1, 0),
|
||||
(220, 'SHOW_MESSAGE_WRONG', '', 1, 0),
|
||||
(221, 'SHOW_MESSAGE_WRONG_CIRCLED', '', 1, 0),
|
||||
(222, 'SHOW_MESSAGE_CORRECT', '', 1, 0),
|
||||
(223, 'SHOW_MESSAGE_CORRECT_CIRCLED', '', 1, 0),
|
||||
(224, 'SHOW_MESSAGE_QUESTION', '', 1, 0),
|
||||
(225, 'SHOW_MESSAGE_QUESTION_CIRCLED', '', 1, 0),
|
||||
(226, 'SHOW_MESSAGE_ARROW_UP', '', 1, 0),
|
||||
(227, 'SHOW_MESSAGE_ARROW_UP_CIRCLED', '', 1, 0),
|
||||
(228, 'SHOW_MESSAGE_ARROW_DOWN', '', 1, 0),
|
||||
(229, 'SHOW_MESSAGE_ARROW_DOWN_CIRCLED', '', 1, 0),
|
||||
(250, 'SHOW_MESSAGE_SKULL', '', 1, 0),
|
||||
(251, 'SHOW_MESSAGE_SKULL_ALT', '', 1, 0),
|
||||
(252, 'SHOW_MESSAGE_MAGNIFIER', '', 1, 0)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`permission` = VALUES(`permission`),
|
||||
`overridable` = VALUES(`overridable`),
|
||||
`triggers_talking_furniture` = VALUES(`triggers_talking_furniture`);
|
||||
+17
-4
@@ -3,12 +3,14 @@ package com.eu.habbo.habbohotel.items.interactions.wired.effects;
|
||||
import com.eu.habbo.Emulator;
|
||||
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.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;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredEffectAlert extends WiredEffectWhisper {
|
||||
public WiredEffectAlert(ResultSet set, Item baseItem) throws SQLException {
|
||||
@@ -22,14 +24,25 @@ public class WiredEffectAlert extends WiredEffectWhisper {
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
List<RoomUnit> sourceUsers = resolveUsers(ctx);
|
||||
List<Habbo> recipients = resolveRecipients(ctx, sourceUsers);
|
||||
Habbo sharedSourceHabbo = (this.visibilitySelection == VISIBILITY_ALL_ROOM_USERS)
|
||||
? resolveMessageSourceHabbo(ctx, sourceUsers)
|
||||
: null;
|
||||
|
||||
for (com.eu.habbo.habbohotel.rooms.RoomUnit unit : resolveUsers(ctx)) {
|
||||
Habbo habbo = room.getHabbo(unit);
|
||||
if (habbo == null) continue;
|
||||
for (Habbo habbo : recipients) {
|
||||
if (!shouldDeliverToRecipient(ctx, habbo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Habbo referenceHabbo = (sharedSourceHabbo != null) ? sharedSourceHabbo : habbo;
|
||||
String username = (referenceHabbo != null && referenceHabbo.getHabboInfo() != null)
|
||||
? referenceHabbo.getHabboInfo().getUsername()
|
||||
: "";
|
||||
|
||||
String message = this.message
|
||||
.replace("%online%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "")
|
||||
.replace("%username%", habbo.getHabboInfo().getUsername())
|
||||
.replace("%username%", username)
|
||||
.replace("%roomsloaded%", Emulator.getGameEnvironment().getRoomManager().loadedRoomsCount() + "");
|
||||
habbo.alert(WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, message));
|
||||
}
|
||||
|
||||
+16
-2
@@ -595,7 +595,13 @@ public class WiredEffectChangeVariableValue extends InteractionWiredEffect {
|
||||
}
|
||||
|
||||
private boolean writeUserInternalValue(Room room, RoomUnit roomUnit, String key, int value) {
|
||||
return WiredInternalVariableSupport.writeUserValue(room, roomUnit, key, value);
|
||||
return WiredInternalVariableSupport.writeUserValue(
|
||||
room,
|
||||
roomUnit,
|
||||
key,
|
||||
value,
|
||||
WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION,
|
||||
false);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
@@ -676,7 +682,15 @@ public class WiredEffectChangeVariableValue extends InteractionWiredEffect {
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
return WiredUserMovementHelper.moveUser(
|
||||
room,
|
||||
roomUnit,
|
||||
targetTile,
|
||||
targetZ,
|
||||
roomUnit.getBodyRotation(),
|
||||
roomUnit.getHeadRotation(),
|
||||
WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION,
|
||||
false);
|
||||
}
|
||||
|
||||
private boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
|
||||
+9
-105
@@ -10,7 +10,6 @@ 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.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
@@ -19,7 +18,6 @@ import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import gnu.trove.procedure.TObjectProcedure;
|
||||
@@ -32,10 +30,6 @@ import java.util.List;
|
||||
public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
private static final int ROTATION_CLOCKWISE = 8;
|
||||
private static final int ROTATION_COUNTER_CLOCKWISE = 9;
|
||||
private static final String CACHE_ACTIVE_UNTIL = "wired.move_rotate_user.active_until";
|
||||
private static final String CACHE_WALK_IN_PLACE_UNTIL = "wired.move_rotate_user.walk_in_place_until";
|
||||
private static final int WALK_IN_PLACE_DURATION_MS = 550;
|
||||
private static final int ROTATION_ACTIVE_WINDOW_MS = 250;
|
||||
|
||||
public static final WiredEffectType type = WiredEffectType.MOVE_ROTATE_USER;
|
||||
|
||||
@@ -64,15 +58,21 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
boolean hasRotation = this.rotationDirection >= 0;
|
||||
RoomUserRotation targetBodyRotation = hasRotation ? this.getTargetRotation(roomUnit) : roomUnit.getBodyRotation();
|
||||
RoomUserRotation targetHeadRotation = hasRotation ? targetBodyRotation : roomUnit.getHeadRotation();
|
||||
|
||||
if (roomUnit.isWalking()) {
|
||||
if (hasRotation) {
|
||||
WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
RoomTile targetTile = (this.movementDirection >= 0) ? this.getTargetTile(room, roomUnit, this.movementDirection) : null;
|
||||
boolean canMove = this.canMoveTo(room, roomUnit, targetTile, movementPhysics);
|
||||
boolean noAnimation = WiredMoveCarryHelper.hasNoAnimationExtra(room, this);
|
||||
int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION);
|
||||
int activeWindowMs = this.resolveActiveWindow(canMove, hasRotation, noAnimation, animationDuration);
|
||||
|
||||
if (canMove) {
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
this.markActive(roomUnit, activeWindowMs);
|
||||
if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, targetBodyRotation, targetHeadRotation,
|
||||
animationDuration, noAnimation, movementPhysics)) {
|
||||
if (hasRotation) {
|
||||
@@ -83,7 +83,6 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
}
|
||||
|
||||
if (hasRotation) {
|
||||
this.markActive(roomUnit, activeWindowMs);
|
||||
WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation);
|
||||
}
|
||||
}
|
||||
@@ -272,103 +271,8 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
return WiredUserMovementHelper.canMoveTo(room, roomUnit, targetTile, movementPhysics);
|
||||
}
|
||||
|
||||
private void markActive(RoomUnit roomUnit, int durationMs) {
|
||||
if (roomUnit == null || durationMs <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long activeUntil = System.currentTimeMillis() + durationMs;
|
||||
roomUnit.getCacheable().put(CACHE_ACTIVE_UNTIL, activeUntil);
|
||||
}
|
||||
|
||||
private int resolveActiveWindow(boolean canMove, boolean hasRotation, boolean noAnimation, int animationDuration) {
|
||||
if (noAnimation) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (canMove) {
|
||||
return Math.max(1, animationDuration);
|
||||
}
|
||||
|
||||
if (hasRotation) {
|
||||
return ROTATION_ACTIVE_WINDOW_MS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean handleWalkWhileActive(Room room, RoomUnit roomUnit, RoomTile targetTile) {
|
||||
if (room == null || roomUnit == null || !isActive(roomUnit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long walkInPlaceUntil = System.currentTimeMillis() + WALK_IN_PLACE_DURATION_MS;
|
||||
roomUnit.getCacheable().put(CACHE_WALK_IN_PLACE_UNTIL, walkInPlaceUntil);
|
||||
roomUnit.stopWalking();
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setStatus(RoomUnitStatus.MOVE, roomUnit.getX() + "," + roomUnit.getY() + "," + roomUnit.getZ());
|
||||
roomUnit.statusUpdate(false);
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
|
||||
Emulator.getThreading().run(() -> clearWalkInPlace(room, roomUnit, walkInPlaceUntil), WALK_IN_PLACE_DURATION_MS);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isActive(RoomUnit roomUnit) {
|
||||
Long activeUntil = getCachedTimestamp(roomUnit, CACHE_ACTIVE_UNTIL);
|
||||
return activeUntil != null && activeUntil > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static boolean isWalkInPlaceActive(RoomUnit roomUnit) {
|
||||
Long walkInPlaceUntil = getCachedTimestamp(roomUnit, CACHE_WALK_IN_PLACE_UNTIL);
|
||||
|
||||
if (walkInPlaceUntil == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (walkInPlaceUntil <= System.currentTimeMillis()) {
|
||||
roomUnit.getCacheable().remove(CACHE_WALK_IN_PLACE_UNTIL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Long getCachedTimestamp(RoomUnit roomUnit, String key) {
|
||||
if (roomUnit == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = roomUnit.getCacheable().get(key);
|
||||
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void clearWalkInPlace(Room room, RoomUnit roomUnit, long expectedUntil) {
|
||||
if (room == null || roomUnit == null || !room.isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long currentUntil = getCachedTimestamp(roomUnit, CACHE_WALK_IN_PLACE_UNTIL);
|
||||
if (currentUntil == null || currentUntil.longValue() != expectedUntil) {
|
||||
return;
|
||||
}
|
||||
|
||||
roomUnit.getCacheable().remove(CACHE_WALK_IN_PLACE_UNTIL);
|
||||
|
||||
if (roomUnit.hasStatus(RoomUnitStatus.MOVE) && !roomUnit.isWalking()) {
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.statusUpdate(false);
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
|
||||
+6
-4
@@ -118,17 +118,19 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
|
||||
: Collections.singletonList(defaultFurni);
|
||||
|
||||
int nextDepth = currentDepth + 1;
|
||||
boolean isolateBranchContext = (signalPerUser && forwardedUsers.size() > 1)
|
||||
|| (signalPerFurni && forwardedFurni.size() > 1);
|
||||
|
||||
for (RoomUnit user : usersToSend) {
|
||||
for (HabboItem sourceItem : furniToSend) {
|
||||
for (HabboItem antenna : resolvedAntennas) {
|
||||
fireSignalAtAntenna(ctx, room, antenna, user, sourceItem, nextDepth);
|
||||
fireSignalAtAntenna(ctx, room, antenna, user, sourceItem, nextDepth, isolateBranchContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fireSignalAtAntenna(WiredContext ctx, Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) {
|
||||
private void fireSignalAtAntenna(WiredContext ctx, Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth, boolean isolateBranchContext) {
|
||||
if (antenna == null) return;
|
||||
RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY());
|
||||
if (tile == null) return;
|
||||
@@ -146,13 +148,13 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
|
||||
.signalChannel(signalChannel)
|
||||
.signalUserCount(actor != null ? 1 : 0)
|
||||
.signalFurniCount(sourceItem != null ? 1 : 0)
|
||||
.contextVariableScope(ctx.contextVariables())
|
||||
.contextVariableScope(isolateBranchContext ? ctx.contextVariables().copy() : ctx.contextVariables())
|
||||
.triggeredByEffect(true);
|
||||
|
||||
if (actor != null) builder.actor(actor);
|
||||
if (sourceItem != null) builder.sourceItem(sourceItem);
|
||||
|
||||
boolean result = WiredManager.handleEvent(builder.build());
|
||||
boolean result = WiredManager.dispatchEffectTriggeredEvent(builder.build());
|
||||
LOGGER.debug("[SendSignal] handleEvent returned: {}", result);
|
||||
}
|
||||
|
||||
|
||||
+129
-12
@@ -22,13 +22,23 @@ import gnu.trove.procedure.TObjectProcedure;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
public static final WiredEffectType type = WiredEffectType.SHOW_MESSAGE;
|
||||
protected static final int VISIBILITY_SOURCE_USERS = 0;
|
||||
protected static final int VISIBILITY_ALL_ROOM_USERS = 1;
|
||||
private static final long DELIVERY_DEDUP_TTL_MS = 60_000L;
|
||||
private static final int DELIVERY_DEDUP_CLEANUP_THRESHOLD = 512;
|
||||
private static final ConcurrentHashMap<String, Long> DELIVERY_DEDUP = new ConcurrentHashMap<>();
|
||||
|
||||
protected String message = "";
|
||||
protected int userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int visibilitySelection = VISIBILITY_SOURCE_USERS;
|
||||
protected int bubbleStyle = RoomChatMessageBubbles.WIRED.getType();
|
||||
|
||||
public WiredEffectWhisper(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
@@ -46,8 +56,10 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.message);
|
||||
message.appendInt(1);
|
||||
message.appendInt(3);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.visibilitySelection);
|
||||
message.appendInt(this.bubbleStyle);
|
||||
message.appendInt(0);
|
||||
message.appendInt(type.code);
|
||||
message.appendInt(this.getDelay());
|
||||
@@ -77,6 +89,10 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
String message = settings.getStringParam();
|
||||
int[] params = settings.getIntParams();
|
||||
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.visibilitySelection = (params.length > 1 && params[1] == VISIBILITY_ALL_ROOM_USERS)
|
||||
? VISIBILITY_ALL_ROOM_USERS
|
||||
: VISIBILITY_SOURCE_USERS;
|
||||
this.bubbleStyle = (params.length > 2) ? params[2] : RoomChatMessageBubbles.WIRED.getType();
|
||||
|
||||
if(gameClient.getHabbo() == null || !gameClient.getHabbo().hasPermission(Permission.ACC_SUPERWIRED)) {
|
||||
message = Emulator.getGameEnvironment().getWordFilter().filter(message, null);
|
||||
@@ -97,17 +113,106 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
return WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
}
|
||||
|
||||
protected List<Habbo> resolveRecipients(WiredContext ctx, List<RoomUnit> sourceUsers) {
|
||||
Room room = ctx.room();
|
||||
LinkedHashMap<Integer, Habbo> recipients = new LinkedHashMap<>();
|
||||
|
||||
if (room == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (this.visibilitySelection == VISIBILITY_ALL_ROOM_USERS) {
|
||||
for (Habbo habbo : room.getCurrentHabbos().values()) {
|
||||
addRecipient(recipients, habbo);
|
||||
}
|
||||
} else {
|
||||
for (RoomUnit roomUnit : sourceUsers) {
|
||||
addRecipient(recipients, room.getHabbo(roomUnit));
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(recipients.values());
|
||||
}
|
||||
|
||||
protected Habbo resolveMessageSourceHabbo(WiredContext ctx, List<RoomUnit> sourceUsers) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room != null) {
|
||||
for (RoomUnit roomUnit : sourceUsers) {
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo != null) {
|
||||
return habbo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (room == null) ? null : ctx.actor().map(roomUnit -> room.getHabbo(roomUnit)).orElse(null);
|
||||
}
|
||||
|
||||
protected String buildMessage(WiredContext ctx, Habbo referenceHabbo) {
|
||||
String username = "";
|
||||
|
||||
if (referenceHabbo != null && referenceHabbo.getHabboInfo() != null) {
|
||||
username = referenceHabbo.getHabboInfo().getUsername();
|
||||
}
|
||||
|
||||
String msg = this.message
|
||||
.replace("%user%", username)
|
||||
.replace("%online_count%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "")
|
||||
.replace("%room_count%", Emulator.getGameEnvironment().getRoomManager().getActiveRooms().size() + "");
|
||||
|
||||
return WiredTextPlaceholderUtil.applyUsernamePlaceholders(ctx, msg);
|
||||
}
|
||||
|
||||
private void addRecipient(LinkedHashMap<Integer, Habbo> recipients, Habbo habbo) {
|
||||
if (habbo == null || habbo.getHabboInfo() == null || habbo.getClient() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
recipients.putIfAbsent(habbo.getHabboInfo().getId(), habbo);
|
||||
}
|
||||
|
||||
protected boolean shouldDeliverToRecipient(WiredContext ctx, Habbo habbo) {
|
||||
if (ctx == null || habbo == null || habbo.getHabboInfo() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
cleanupDeliveryDedup(now);
|
||||
|
||||
String deliveryKey = buildDeliveryKey(ctx, habbo);
|
||||
|
||||
return DELIVERY_DEDUP.putIfAbsent(deliveryKey, now) == null;
|
||||
}
|
||||
|
||||
private String buildDeliveryKey(WiredContext ctx, Habbo habbo) {
|
||||
return ctx.room().getId() + ":" + this.getId() + ":" + habbo.getHabboInfo().getId() + ":" + ctx.event().getCreatedAtMs();
|
||||
}
|
||||
|
||||
private static void cleanupDeliveryDedup(long now) {
|
||||
if (DELIVERY_DEDUP.size() < DELIVERY_DEDUP_CLEANUP_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
|
||||
DELIVERY_DEDUP.entrySet().removeIf(entry -> (now - entry.getValue()) > DELIVERY_DEDUP_TTL_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
if (this.message.length() > 0) {
|
||||
for (RoomUnit roomUnit : resolveUsers(ctx)) {
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo == null) continue;
|
||||
List<RoomUnit> sourceUsers = resolveUsers(ctx);
|
||||
List<Habbo> recipients = resolveRecipients(ctx, sourceUsers);
|
||||
Habbo sharedSourceHabbo = (this.visibilitySelection == VISIBILITY_ALL_ROOM_USERS)
|
||||
? resolveMessageSourceHabbo(ctx, sourceUsers)
|
||||
: null;
|
||||
|
||||
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)));
|
||||
for (Habbo habbo : recipients) {
|
||||
if (!shouldDeliverToRecipient(ctx, habbo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String msg = buildMessage(ctx, (sharedSourceHabbo != null) ? sharedSourceHabbo : habbo);
|
||||
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(msg, habbo, habbo, RoomChatMessageBubbles.getBubble(this.bubbleStyle))));
|
||||
|
||||
if (habbo.getRoomUnit().isIdle()) {
|
||||
habbo.getRoomUnit().getRoom().unIdle(habbo);
|
||||
@@ -124,7 +229,7 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.message, this.getDelay(), this.userSource));
|
||||
return WiredManager.getGson().toJson(new JsonData(this.message, this.getDelay(), this.userSource, this.visibilitySelection, this.bubbleStyle));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +240,11 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
this.setDelay(data.delay);
|
||||
this.message = data.message;
|
||||
this.userSource = data.userSource;
|
||||
this.userSource = (data.userSource != null) ? data.userSource : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.visibilitySelection = (data.visibilitySelection != null && data.visibilitySelection == VISIBILITY_ALL_ROOM_USERS)
|
||||
? VISIBILITY_ALL_ROOM_USERS
|
||||
: VISIBILITY_SOURCE_USERS;
|
||||
this.bubbleStyle = (data.bubbleStyle != null) ? data.bubbleStyle : RoomChatMessageBubbles.WIRED.getType();
|
||||
}
|
||||
else {
|
||||
this.message = "";
|
||||
@@ -146,6 +255,8 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
}
|
||||
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.visibilitySelection = VISIBILITY_SOURCE_USERS;
|
||||
this.bubbleStyle = RoomChatMessageBubbles.WIRED.getType();
|
||||
this.needsUpdate(true);
|
||||
}
|
||||
}
|
||||
@@ -154,6 +265,8 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
public void onPickUp() {
|
||||
this.message = "";
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.visibilitySelection = VISIBILITY_SOURCE_USERS;
|
||||
this.bubbleStyle = RoomChatMessageBubbles.WIRED.getType();
|
||||
this.setDelay(0);
|
||||
}
|
||||
|
||||
@@ -170,12 +283,16 @@ public class WiredEffectWhisper extends InteractionWiredEffect {
|
||||
static class JsonData {
|
||||
String message;
|
||||
int delay;
|
||||
int userSource;
|
||||
Integer userSource;
|
||||
Integer visibilitySelection;
|
||||
Integer bubbleStyle;
|
||||
|
||||
public JsonData(String message, int delay, int userSource) {
|
||||
public JsonData(String message, int delay, int userSource, int visibilitySelection, int bubbleStyle) {
|
||||
this.message = message;
|
||||
this.delay = delay;
|
||||
this.userSource = userSource;
|
||||
this.visibilitySelection = visibilitySelection;
|
||||
this.bubbleStyle = bubbleStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -671,8 +671,16 @@ public class WiredExtraVariableEcho extends InteractionWiredExtra {
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
double targetZ = WiredUserMovementHelper.resolveUserTargetZ(room, targetTile);
|
||||
return WiredUserMovementHelper.moveUser(
|
||||
room,
|
||||
roomUnit,
|
||||
targetTile,
|
||||
targetZ,
|
||||
roomUnit.getBodyRotation(),
|
||||
roomUnit.getHeadRotation(),
|
||||
WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION,
|
||||
false);
|
||||
}
|
||||
|
||||
private boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
|
||||
@@ -52,14 +52,14 @@ public class RoomChatMessageBubbles {
|
||||
public static final RoomChatMessageBubbles UNKNOWN_43 = new RoomChatMessageBubbles(43, "UNKNOWN_43", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_44 = new RoomChatMessageBubbles(44, "UNKNOWN_44", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_45 = new RoomChatMessageBubbles(45, "UNKNOWN_45", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_46 = new RoomChatMessageBubbles(45, "UNKNOWN_46", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_47 = new RoomChatMessageBubbles(45, "UNKNOWN_47", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_48 = new RoomChatMessageBubbles(45, "UNKNOWN_48", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_49 = new RoomChatMessageBubbles(45, "UNKNOWN_49", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_50 = new RoomChatMessageBubbles(45, "UNKNOWN_50", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_51 = new RoomChatMessageBubbles(45, "UNKNOWN_51", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_52 = new RoomChatMessageBubbles(45, "UNKNOWN_52", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_53 = new RoomChatMessageBubbles(45, "UNKNOWN_53", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_46 = new RoomChatMessageBubbles(46, "UNKNOWN_46", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_47 = new RoomChatMessageBubbles(47, "UNKNOWN_47", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_48 = new RoomChatMessageBubbles(48, "UNKNOWN_48", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_49 = new RoomChatMessageBubbles(49, "UNKNOWN_49", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_50 = new RoomChatMessageBubbles(50, "UNKNOWN_50", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_51 = new RoomChatMessageBubbles(51, "UNKNOWN_51", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_52 = new RoomChatMessageBubbles(52, "UNKNOWN_52", "", true, false);
|
||||
public static final RoomChatMessageBubbles UNKNOWN_53 = new RoomChatMessageBubbles(53, "UNKNOWN_53", "", true, false);
|
||||
|
||||
|
||||
static {
|
||||
@@ -167,11 +167,11 @@ public class RoomChatMessageBubbles {
|
||||
|
||||
public static void removeDynamicBubbles() {
|
||||
synchronized (BUBBLES) {
|
||||
BUBBLES.entrySet().removeIf(entry -> entry.getKey() > 45);
|
||||
BUBBLES.entrySet().removeIf(entry -> entry.getKey() > 53);
|
||||
}
|
||||
}
|
||||
|
||||
public static RoomChatMessageBubbles[] values() {
|
||||
return BUBBLES.values().toArray(new RoomChatMessageBubbles[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,9 +581,11 @@ public final class WiredEngine {
|
||||
|
||||
WiredMoveCarryHelper.beginMovementCollection();
|
||||
|
||||
try {
|
||||
try (WiredInternalVariableSupport.UserMoveBatchScope ignored = WiredInternalVariableSupport.beginUserMoveBatch()) {
|
||||
// Execute selected effects
|
||||
for (IWiredEffect effect : toExecute) {
|
||||
for (int effectIndex = 0; effectIndex < toExecute.size(); effectIndex++) {
|
||||
IWiredEffect effect = toExecute.get(effectIndex);
|
||||
|
||||
// Check if effect requires actor
|
||||
if (effect.requiresActor() && !ctx.hasActor()) {
|
||||
continue;
|
||||
@@ -592,8 +594,30 @@ public final class WiredEngine {
|
||||
// Handle delay
|
||||
int delay = effect.getDelay();
|
||||
if (delay > 0) {
|
||||
// Schedule delayed execution
|
||||
scheduleDelayedEffect(effect, ctx, delay, currentTime);
|
||||
List<IWiredEffect> delayedBatch = new ArrayList<>();
|
||||
delayedBatch.add(effect);
|
||||
|
||||
while ((effectIndex + 1) < toExecute.size()) {
|
||||
IWiredEffect nextEffect = toExecute.get(effectIndex + 1);
|
||||
|
||||
if (nextEffect == null || nextEffect.getDelay() != delay) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextEffect.requiresActor() && !ctx.hasActor()) {
|
||||
effectIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
delayedBatch.add(nextEffect);
|
||||
effectIndex++;
|
||||
}
|
||||
|
||||
if (delayedBatch.size() == 1) {
|
||||
scheduleDelayedEffect(effect, ctx, delay, currentTime);
|
||||
} else {
|
||||
scheduleOrderedEffectBatch(delayedBatch, ctx, delay, currentTime);
|
||||
}
|
||||
} else {
|
||||
// Execute immediately
|
||||
ctx.state().step();
|
||||
@@ -672,82 +696,7 @@ public final class WiredEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> 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;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
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());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (ctx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = ctx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, ctx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
ctx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (ctx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = ctx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, ctx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
ctx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> limitIterable(Iterable<T> values, int limit) {
|
||||
List<T> 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));
|
||||
WiredSelectionFilterSupport.applySelectorFilters(room, stack.triggerItem(), ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -916,7 +865,7 @@ public final class WiredEngine {
|
||||
|
||||
WiredMoveCarryHelper.beginMovementCollection();
|
||||
|
||||
try {
|
||||
try (WiredInternalVariableSupport.UserMoveBatchScope ignored = WiredInternalVariableSupport.beginUserMoveBatch()) {
|
||||
for (IWiredEffect effect : batch) {
|
||||
try {
|
||||
if (!useExecutionTimeForCooldown) {
|
||||
|
||||
+239
-6
@@ -26,6 +26,10 @@ import java.time.temporal.WeekFields;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class WiredInternalVariableSupport {
|
||||
private static final ThreadLocal<Boolean> USER_MOVE_INSTANT_OVERRIDE = new ThreadLocal<>();
|
||||
private static final ThreadLocal<UserMoveBatch> USER_MOVE_BATCH = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Integer> USER_MOVE_BATCH_DEPTH = new ThreadLocal<>();
|
||||
|
||||
private WiredInternalVariableSupport() {
|
||||
}
|
||||
|
||||
@@ -225,15 +229,29 @@ public final class WiredInternalVariableSupport {
|
||||
}
|
||||
|
||||
public static boolean writeUserValue(Room room, RoomUnit roomUnit, String key, int value) {
|
||||
Boolean instantOverride = USER_MOVE_INSTANT_OVERRIDE.get();
|
||||
|
||||
if (instantOverride != null) {
|
||||
return writeUserValue(room, roomUnit, key, value, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION, instantOverride);
|
||||
}
|
||||
|
||||
return writeUserValue(room, roomUnit, key, value, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION, false);
|
||||
}
|
||||
|
||||
public static boolean writeUserValue(Room room, RoomUnit roomUnit, String key, int value, int animationDuration, boolean noAnimation) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
if (stageUserMoveIfPossible(room, roomUnit, normalized, value, animationDuration, noAnimation)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return switch (normalized) {
|
||||
case "@position_x" -> moveUserTo(room, roomUnit, value, roomUnit.getY());
|
||||
case "@position_y" -> moveUserTo(room, roomUnit, roomUnit.getX(), value);
|
||||
case "@position_x" -> moveUserTo(room, roomUnit, value, roomUnit.getY(), animationDuration, noAnimation);
|
||||
case "@position_y" -> moveUserTo(room, roomUnit, roomUnit.getX(), value, animationDuration, noAnimation);
|
||||
case "@direction" -> {
|
||||
RoomUserRotation rotation = RoomUserRotation.fromValue(value);
|
||||
yield WiredUserMovementHelper.updateUserDirection(room, roomUnit, rotation, rotation);
|
||||
@@ -242,6 +260,24 @@ public final class WiredInternalVariableSupport {
|
||||
};
|
||||
}
|
||||
|
||||
public static UserMoveInstantScope beginUserMoveInstantOverride(boolean instant) {
|
||||
Boolean previousValue = USER_MOVE_INSTANT_OVERRIDE.get();
|
||||
USER_MOVE_INSTANT_OVERRIDE.set(instant);
|
||||
return new UserMoveInstantScope(previousValue);
|
||||
}
|
||||
|
||||
public static UserMoveBatchScope beginUserMoveBatch() {
|
||||
Integer previousDepth = USER_MOVE_BATCH_DEPTH.get();
|
||||
int nextDepth = (previousDepth == null) ? 1 : (previousDepth + 1);
|
||||
USER_MOVE_BATCH_DEPTH.set(nextDepth);
|
||||
|
||||
if (nextDepth == 1) {
|
||||
USER_MOVE_BATCH.set(new UserMoveBatch());
|
||||
}
|
||||
|
||||
return new UserMoveBatchScope(previousDepth);
|
||||
}
|
||||
|
||||
public static Integer readFurniValue(Room room, HabboItem item, String key) {
|
||||
if (room == null || item == null) {
|
||||
return null;
|
||||
@@ -281,7 +317,7 @@ public final class WiredInternalVariableSupport {
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
if ("@state".equals(normalized)) {
|
||||
item.setExtradata(String.valueOf(value));
|
||||
item.setExtradata(String.valueOf(normalizeFurniStateValue(item, value)));
|
||||
room.updateItemState(item);
|
||||
return true;
|
||||
}
|
||||
@@ -492,7 +528,7 @@ public final class WiredInternalVariableSupport {
|
||||
return parseInteger(roomUnit.getStatus(status));
|
||||
}
|
||||
|
||||
private static boolean moveUserTo(Room room, RoomUnit roomUnit, int x, int y) {
|
||||
private static boolean moveUserTo(Room room, RoomUnit roomUnit, int x, int y, int animationDuration, boolean noAnimation) {
|
||||
if (room == null || roomUnit == null || room.getLayout() == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -502,8 +538,95 @@ public final class WiredInternalVariableSupport {
|
||||
return false;
|
||||
}
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
double targetZ = WiredUserMovementHelper.resolveUserTargetZ(room, targetTile);
|
||||
return WiredUserMovementHelper.moveUser(
|
||||
room,
|
||||
roomUnit,
|
||||
targetTile,
|
||||
targetZ,
|
||||
roomUnit.getBodyRotation(),
|
||||
roomUnit.getHeadRotation(),
|
||||
animationDuration,
|
||||
noAnimation);
|
||||
}
|
||||
|
||||
private static boolean stageUserMoveIfPossible(Room room, RoomUnit roomUnit, String normalizedKey, int value, int animationDuration, boolean noAnimation) {
|
||||
if (room == null || roomUnit == null || normalizedKey == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"@position_x".equals(normalizedKey) && !"@position_y".equals(normalizedKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserMoveBatch batch = USER_MOVE_BATCH.get();
|
||||
|
||||
if (batch == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserMoveBatchEntry entry = batch.entries.computeIfAbsent(roomUnit.getId(), ignored ->
|
||||
new UserMoveBatchEntry(room, roomUnit, roomUnit.getX(), roomUnit.getY(), animationDuration, noAnimation));
|
||||
|
||||
entry.animationDuration = animationDuration;
|
||||
entry.noAnimation = noAnimation;
|
||||
|
||||
if ("@position_x".equals(normalizedKey)) {
|
||||
entry.targetX = value;
|
||||
entry.xDirty = true;
|
||||
} else {
|
||||
entry.targetY = value;
|
||||
entry.yDirty = true;
|
||||
}
|
||||
|
||||
if (entry.xDirty && entry.yDirty && !entry.noAnimation) {
|
||||
executeUserMoveBatchEntry(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void flushUserMoveBatch(UserMoveBatch batch) {
|
||||
if (batch == null || batch.entries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (UserMoveBatchEntry entry : batch.entries.values()) {
|
||||
executeUserMoveBatchEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void executeUserMoveBatchEntry(UserMoveBatchEntry entry) {
|
||||
if (entry == null || entry.room == null || entry.roomUnit == null || entry.room.getLayout() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.xDirty && !entry.yDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
RoomTile targetTile = entry.room.getLayout().getTile((short) entry.targetX, (short) entry.targetY);
|
||||
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
double targetZ = WiredUserMovementHelper.resolveUserTargetZ(entry.room, targetTile);
|
||||
|
||||
WiredUserMovementHelper.moveUser(
|
||||
entry.room,
|
||||
entry.roomUnit,
|
||||
targetTile,
|
||||
targetZ,
|
||||
entry.roomUnit.getBodyRotation(),
|
||||
entry.roomUnit.getHeadRotation(),
|
||||
entry.animationDuration,
|
||||
entry.noAnimation);
|
||||
|
||||
entry.targetX = entry.roomUnit.getX();
|
||||
entry.targetY = entry.roomUnit.getY();
|
||||
entry.xDirty = false;
|
||||
entry.yDirty = false;
|
||||
}
|
||||
|
||||
private static boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
@@ -520,6 +643,24 @@ public final class WiredInternalVariableSupport {
|
||||
return error == FurnitureMovementError.NONE;
|
||||
}
|
||||
|
||||
private static int normalizeFurniStateValue(HabboItem item, int value) {
|
||||
if (item == null || item.getBaseItem() == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int stateCount = item.getBaseItem().getStateCount();
|
||||
if (stateCount <= 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int wrappedValue = value % stateCount;
|
||||
if (wrappedValue < 0) {
|
||||
wrappedValue += stateCount;
|
||||
}
|
||||
|
||||
return wrappedValue;
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
@@ -551,4 +692,96 @@ public final class WiredInternalVariableSupport {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UserMoveInstantScope implements AutoCloseable {
|
||||
private final Boolean previousValue;
|
||||
private boolean closed;
|
||||
|
||||
private UserMoveInstantScope(Boolean previousValue) {
|
||||
this.previousValue = previousValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
|
||||
if (this.previousValue == null) {
|
||||
USER_MOVE_INSTANT_OVERRIDE.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
USER_MOVE_INSTANT_OVERRIDE.set(this.previousValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UserMoveBatchScope implements AutoCloseable {
|
||||
private final Integer previousDepth;
|
||||
private boolean closed;
|
||||
|
||||
private UserMoveBatchScope(Integer previousDepth) {
|
||||
this.previousDepth = previousDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
|
||||
Integer currentDepth = USER_MOVE_BATCH_DEPTH.get();
|
||||
|
||||
if (currentDepth == null || currentDepth <= 1) {
|
||||
UserMoveBatch currentBatch = USER_MOVE_BATCH.get();
|
||||
|
||||
if (currentBatch != null) {
|
||||
flushUserMoveBatch(currentBatch);
|
||||
}
|
||||
|
||||
USER_MOVE_BATCH.remove();
|
||||
|
||||
if (this.previousDepth == null) {
|
||||
USER_MOVE_BATCH_DEPTH.remove();
|
||||
} else {
|
||||
USER_MOVE_BATCH_DEPTH.set(this.previousDepth);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
USER_MOVE_BATCH_DEPTH.set(currentDepth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UserMoveBatch {
|
||||
private final java.util.LinkedHashMap<Integer, UserMoveBatchEntry> entries = new java.util.LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private static final class UserMoveBatchEntry {
|
||||
private final Room room;
|
||||
private final RoomUnit roomUnit;
|
||||
private int targetX;
|
||||
private int targetY;
|
||||
private int animationDuration;
|
||||
private boolean noAnimation;
|
||||
private boolean xDirty;
|
||||
private boolean yDirty;
|
||||
|
||||
private UserMoveBatchEntry(Room room, RoomUnit roomUnit, int targetX, int targetY, int animationDuration, boolean noAnimation) {
|
||||
this.room = room;
|
||||
this.roomUnit = roomUnit;
|
||||
this.targetX = targetX;
|
||||
this.targetY = targetY;
|
||||
this.animationDuration = animationDuration;
|
||||
this.noAnimation = noAnimation;
|
||||
this.xDirty = false;
|
||||
this.yDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
/**
|
||||
* Manager class for the wired runtime.
|
||||
@@ -85,6 +86,8 @@ public final class WiredManager {
|
||||
|
||||
/** Whether the engine is initialized */
|
||||
private static volatile boolean initialized = false;
|
||||
private static final ThreadLocal<Integer> EVENT_HANDLING_DEPTH = new ThreadLocal<>();
|
||||
private static final ThreadLocal<ArrayDeque<WiredEvent>> DEFERRED_EFFECT_EVENTS = new ThreadLocal<>();
|
||||
private WiredManager() {
|
||||
// Static utility class
|
||||
}
|
||||
@@ -236,8 +239,65 @@ public final class WiredManager {
|
||||
if (event == null || RoomWiredDisableSupport.isWiredDisabled(event.getRoom())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return engine.handleEvent(event);
|
||||
|
||||
Integer previousDepth = EVENT_HANDLING_DEPTH.get();
|
||||
int nextDepth = (previousDepth == null) ? 1 : (previousDepth + 1);
|
||||
EVENT_HANDLING_DEPTH.set(nextDepth);
|
||||
|
||||
if (previousDepth == null) {
|
||||
DEFERRED_EFFECT_EVENTS.set(new ArrayDeque<>());
|
||||
}
|
||||
|
||||
boolean handled = false;
|
||||
|
||||
try {
|
||||
handled = engine.handleEvent(event);
|
||||
|
||||
if (nextDepth == 1) {
|
||||
ArrayDeque<WiredEvent> deferredEvents = DEFERRED_EFFECT_EVENTS.get();
|
||||
|
||||
while (deferredEvents != null && !deferredEvents.isEmpty()) {
|
||||
WiredEvent deferredEvent = deferredEvents.pollFirst();
|
||||
|
||||
if (deferredEvent == null || RoomWiredDisableSupport.isWiredDisabled(deferredEvent.getRoom())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handled = engine.handleEvent(deferredEvent) || handled;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
} finally {
|
||||
if (previousDepth == null) {
|
||||
EVENT_HANDLING_DEPTH.remove();
|
||||
DEFERRED_EFFECT_EVENTS.remove();
|
||||
} else {
|
||||
EVENT_HANDLING_DEPTH.set(previousDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean dispatchEffectTriggeredEvent(WiredEvent event) {
|
||||
if (!isEnabled() || engine == null || event == null || RoomWiredDisableSupport.isWiredDisabled(event.getRoom())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer currentDepth = EVENT_HANDLING_DEPTH.get();
|
||||
|
||||
if (currentDepth == null || currentDepth <= 0) {
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
ArrayDeque<WiredEvent> deferredEvents = DEFERRED_EFFECT_EVENTS.get();
|
||||
|
||||
if (deferredEvents == null) {
|
||||
deferredEvents = new ArrayDeque<>();
|
||||
DEFERRED_EFFECT_EVENTS.set(deferredEvents);
|
||||
}
|
||||
|
||||
deferredEvents.addLast(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+18
-1
@@ -35,6 +35,7 @@ public final class WiredMoveCarryHelper {
|
||||
private static final long USER_FOLLOWER_TTL_MS = 10000L;
|
||||
private static final ThreadLocal<Set<Integer>> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>();
|
||||
private static final ThreadLocal<List<WiredMovementsComposer.MovementData>> COLLECTED_MOVEMENTS = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Integer> MOVEMENT_COLLECTION_DEPTH = new ThreadLocal<>();
|
||||
private static final ConcurrentHashMap<Integer, Long> SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, UserFollowEntry>> ACTIVE_USER_FOLLOWERS = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -237,12 +238,28 @@ public final class WiredMoveCarryHelper {
|
||||
}
|
||||
|
||||
public static void beginMovementCollection() {
|
||||
COLLECTED_MOVEMENTS.set(new ArrayList<>());
|
||||
Integer currentDepth = MOVEMENT_COLLECTION_DEPTH.get();
|
||||
|
||||
if (currentDepth == null || currentDepth <= 0) {
|
||||
COLLECTED_MOVEMENTS.set(new ArrayList<>());
|
||||
MOVEMENT_COLLECTION_DEPTH.set(1);
|
||||
return;
|
||||
}
|
||||
|
||||
MOVEMENT_COLLECTION_DEPTH.set(currentDepth + 1);
|
||||
}
|
||||
|
||||
public static ServerMessage finishMovementCollection() {
|
||||
Integer currentDepth = MOVEMENT_COLLECTION_DEPTH.get();
|
||||
|
||||
if (currentDepth != null && currentDepth > 1) {
|
||||
MOVEMENT_COLLECTION_DEPTH.set(currentDepth - 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
List<WiredMovementsComposer.MovementData> movements = COLLECTED_MOVEMENTS.get();
|
||||
COLLECTED_MOVEMENTS.remove();
|
||||
MOVEMENT_COLLECTION_DEPTH.remove();
|
||||
|
||||
if (movements == null || movements.isEmpty()) {
|
||||
return null;
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
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.WiredExtraFilterFurniByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
|
||||
final class WiredSelectionFilterSupport {
|
||||
private static final ThreadLocal<Integer> FILTER_DEPTH = ThreadLocal.withInitial(() -> 0);
|
||||
|
||||
private WiredSelectionFilterSupport() {
|
||||
}
|
||||
|
||||
static void applySelectorFilters(Room room, HabboItem triggerItem, WiredContext ctx) {
|
||||
if (ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.targets().isItemsModifiedBySelector()) {
|
||||
ctx.targets().setItems(filterItems(room, triggerItem, ctx, ctx.targets().items()));
|
||||
}
|
||||
|
||||
if (ctx.targets().isUsersModifiedBySelector()) {
|
||||
ctx.targets().setUsers(filterUsers(room, triggerItem, ctx, ctx.targets().users()));
|
||||
}
|
||||
}
|
||||
|
||||
static List<HabboItem> filterItems(Room room, HabboItem triggerItem, WiredContext ctx, Iterable<HabboItem> values) {
|
||||
List<HabboItem> items = toItemList(values);
|
||||
|
||||
if (items.isEmpty() || shouldBypass(room, triggerItem, ctx)) {
|
||||
return items;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY());
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return items;
|
||||
}
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> variableFilters = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraFilterFurni) {
|
||||
furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
variableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (furniLimit == Integer.MAX_VALUE && variableFilters.isEmpty()) {
|
||||
return items;
|
||||
}
|
||||
|
||||
variableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
try (FilterScope ignored = enterScope()) {
|
||||
Iterable<HabboItem> filteredItems = items;
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : variableFilters) {
|
||||
filteredItems = extra.filterItems(room, ctx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
return toItemList(filteredItems);
|
||||
}
|
||||
}
|
||||
|
||||
static List<RoomUnit> filterUsers(Room room, HabboItem triggerItem, WiredContext ctx, Iterable<RoomUnit> values) {
|
||||
List<RoomUnit> users = toUserList(values);
|
||||
|
||||
if (users.isEmpty() || shouldBypass(room, triggerItem, ctx)) {
|
||||
return users;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY());
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return users;
|
||||
}
|
||||
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterUsersByVariable> variableFilters = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraFilterUser) {
|
||||
userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
variableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (userLimit == Integer.MAX_VALUE && variableFilters.isEmpty()) {
|
||||
return users;
|
||||
}
|
||||
|
||||
variableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
try (FilterScope ignored = enterScope()) {
|
||||
Iterable<RoomUnit> filteredUsers = users;
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : variableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, ctx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
return toUserList(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldBypass(Room room, HabboItem triggerItem, WiredContext ctx) {
|
||||
return room == null
|
||||
|| triggerItem == null
|
||||
|| ctx == null
|
||||
|| room.getRoomSpecialTypes() == null
|
||||
|| FILTER_DEPTH.get() > 0;
|
||||
}
|
||||
|
||||
private static FilterScope enterScope() {
|
||||
FILTER_DEPTH.set(FILTER_DEPTH.get() + 1);
|
||||
return new FilterScope();
|
||||
}
|
||||
|
||||
private static <T> List<T> limitIterable(Iterable<T> values, int limit) {
|
||||
List<T> 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));
|
||||
}
|
||||
|
||||
private static List<HabboItem> toItemList(Iterable<HabboItem> values) {
|
||||
List<HabboItem> result = new ArrayList<>();
|
||||
|
||||
if (values == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (HabboItem item : values) {
|
||||
if (item != null) {
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<RoomUnit> toUserList(Iterable<RoomUnit> values) {
|
||||
List<RoomUnit> result = new ArrayList<>();
|
||||
|
||||
if (values == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (RoomUnit unit : values) {
|
||||
if (unit != null) {
|
||||
result.add(unit);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final class FilterScope implements AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
int depth = FILTER_DEPTH.get() - 1;
|
||||
|
||||
if (depth <= 0) {
|
||||
FILTER_DEPTH.remove();
|
||||
} else {
|
||||
FILTER_DEPTH.set(depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,24 +29,36 @@ public final class WiredSourceUtil {
|
||||
}
|
||||
|
||||
public static List<HabboItem> resolveItems(WiredContext ctx, int sourceType, Collection<HabboItem> selectedItems) {
|
||||
List<HabboItem> resolvedItems;
|
||||
|
||||
switch (sourceType) {
|
||||
case SOURCE_TRIGGER:
|
||||
return ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedItems = ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
case SOURCE_SELECTED:
|
||||
return (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList();
|
||||
resolvedItems = (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList();
|
||||
break;
|
||||
case SOURCE_SELECTOR:
|
||||
WiredTargets itemTargets = getSelectorTargets(ctx);
|
||||
return itemTargets.isItemsModifiedBySelector()
|
||||
resolvedItems = itemTargets.isItemsModifiedBySelector()
|
||||
? new ArrayList<>(itemTargets.items())
|
||||
: Collections.emptyList();
|
||||
break;
|
||||
case SOURCE_SIGNAL:
|
||||
if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) {
|
||||
return ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedItems = ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
resolvedItems = Collections.emptyList();
|
||||
break;
|
||||
default:
|
||||
return ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedItems = ctx.sourceItem().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
return (sourceType == SOURCE_SELECTOR)
|
||||
? resolvedItems
|
||||
: WiredSelectionFilterSupport.filterItems(ctx.room(), ctx.triggerItem(), ctx, resolvedItems);
|
||||
}
|
||||
|
||||
public static List<RoomUnit> resolveUsers(WiredContext ctx, int sourceType) {
|
||||
@@ -54,29 +66,43 @@ public final class WiredSourceUtil {
|
||||
}
|
||||
|
||||
public static List<RoomUnit> resolveUsers(WiredContext ctx, int sourceType, Collection<RoomUnit> selectedUsers) {
|
||||
List<RoomUnit> resolvedUsers;
|
||||
|
||||
switch (sourceType) {
|
||||
case SOURCE_TRIGGER:
|
||||
return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedUsers = ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
case SOURCE_CLICKED_USER:
|
||||
if (ctx.eventType() == WiredEvent.Type.USER_CLICKS_USER) {
|
||||
return ctx.event().getTargetUnit().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedUsers = ctx.event().getTargetUnit().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
resolvedUsers = Collections.emptyList();
|
||||
break;
|
||||
case SOURCE_SELECTED:
|
||||
return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList();
|
||||
resolvedUsers = (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList();
|
||||
break;
|
||||
case SOURCE_SELECTOR:
|
||||
WiredTargets userTargets = getSelectorTargets(ctx);
|
||||
return userTargets.isUsersModifiedBySelector()
|
||||
resolvedUsers = userTargets.isUsersModifiedBySelector()
|
||||
? new ArrayList<>(userTargets.users())
|
||||
: Collections.emptyList();
|
||||
break;
|
||||
case SOURCE_SIGNAL:
|
||||
if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) {
|
||||
return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedUsers = ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
resolvedUsers = Collections.emptyList();
|
||||
break;
|
||||
default:
|
||||
return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
resolvedUsers = ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
return (sourceType == SOURCE_SELECTOR)
|
||||
? resolvedUsers
|
||||
: WiredSelectionFilterSupport.filterUsers(ctx.room(), ctx.triggerItem(), ctx, resolvedUsers);
|
||||
}
|
||||
|
||||
public static boolean isDefaultUserSource(int value) {
|
||||
@@ -223,83 +249,6 @@ public final class WiredSourceUtil {
|
||||
}
|
||||
|
||||
private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) {
|
||||
if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY());
|
||||
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
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());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = selectorCtx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, selectorCtx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = selectorCtx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, selectorCtx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> List<T> limitIterable(Iterable<T> values, int limit) {
|
||||
List<T> 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));
|
||||
WiredSelectionFilterSupport.applySelectorFilters(room, triggerItem, selectorCtx);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-77
@@ -162,82 +162,6 @@ public final class WiredTriggerSourceUtil {
|
||||
}
|
||||
|
||||
private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) {
|
||||
if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY());
|
||||
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
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());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = selectorCtx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, selectorCtx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = selectorCtx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, selectorCtx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> List<T> limitIterable(Iterable<T> values, int limit) {
|
||||
List<T> result = new ArrayList<>();
|
||||
|
||||
if (values == null || limit <= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (T value : values) {
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() <= limit) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return new ArrayList<>(result.subList(0, limit));
|
||||
WiredSelectionFilterSupport.applySelectorFilters(room, triggerItem, selectorCtx);
|
||||
}
|
||||
}
|
||||
|
||||
+236
-39
@@ -1,8 +1,13 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionStackWalkHelper;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionTileWalkMagic;
|
||||
import com.eu.habbo.habbohotel.items.interactions.interfaces.ConditionalGate;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionRoller;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomLayout;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
@@ -17,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@@ -26,7 +32,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class WiredUserMovementHelper {
|
||||
public static final int DEFAULT_ANIMATION_DURATION = WiredMovementsComposer.DEFAULT_DURATION;
|
||||
private static final int SUPPRESS_NEXT_WALK_WINDOW_MS = 250;
|
||||
private static final int STATUS_SUPPRESSION_GRACE_MS = 250;
|
||||
private static final String SUPPRESS_NEXT_WALK_CACHE_KEY = "wired_suppress_next_walk_until";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WiredUserMovementHelper.class);
|
||||
private static final ThreadLocal<Set<Integer>> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>();
|
||||
@@ -62,16 +70,29 @@ public final class WiredUserMovementHelper {
|
||||
|
||||
RoomUserRotation resolvedBodyRotation = bodyRotation == null ? roomUnit.getBodyRotation() : bodyRotation;
|
||||
RoomUserRotation resolvedHeadRotation = headRotation == null ? roomUnit.getHeadRotation() : headRotation;
|
||||
double oldZ = roomUnit.getZ();
|
||||
HabboItem oldTopItem = room.getTopItemAt(oldLocation.x, oldLocation.y);
|
||||
HabboItem newTopItem = room.getTopItemAt(targetTile.x, targetTile.y);
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
int animationDuration = Math.max(1, duration);
|
||||
|
||||
if (noAnimation) {
|
||||
return moveUserInstant(room, roomUnit, targetTile, targetZ, resolvedBodyRotation, resolvedHeadRotation, oldLocation, oldTopItem, newTopItem, habbo);
|
||||
return moveUserInstant(
|
||||
room,
|
||||
roomUnit,
|
||||
oldLocation,
|
||||
targetTile,
|
||||
roomUnit.getZ(),
|
||||
targetZ,
|
||||
resolvedBodyRotation,
|
||||
resolvedHeadRotation,
|
||||
room.getTopItemAt(oldLocation.x, oldLocation.y),
|
||||
resolveEnteredItem(room, targetTile),
|
||||
room.getHabbo(roomUnit));
|
||||
}
|
||||
|
||||
double oldZ = roomUnit.getZ();
|
||||
HabboItem oldTopItem = room.getTopItemAt(oldLocation.x, oldLocation.y);
|
||||
HabboItem newTopItem = resolveEnteredItem(room, targetTile);
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
|
||||
int animationDuration = (duration > 0) ? duration : DEFAULT_ANIMATION_DURATION;
|
||||
|
||||
runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> {
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setZ(targetZ);
|
||||
@@ -111,6 +132,61 @@ public final class WiredUserMovementHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void suppressNextWalkCommand(RoomUnit roomUnit) {
|
||||
if (roomUnit == null || roomUnit.getCacheable() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
roomUnit.getCacheable().put(SUPPRESS_NEXT_WALK_CACHE_KEY, System.currentTimeMillis() + SUPPRESS_NEXT_WALK_WINDOW_MS);
|
||||
}
|
||||
|
||||
public static boolean consumeSuppressedWalkCommand(RoomUnit roomUnit) {
|
||||
if (roomUnit == null || roomUnit.getCacheable() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object value = roomUnit.getCacheable().remove(SUPPRESS_NEXT_WALK_CACHE_KEY);
|
||||
|
||||
if (!(value instanceof Long)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((Long) value) >= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static boolean moveUserInstant(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, double oldZ, double targetZ,
|
||||
RoomUserRotation bodyRotation, RoomUserRotation headRotation, HabboItem oldTopItem,
|
||||
HabboItem newTopItem, Habbo habbo) {
|
||||
suppressNextWalkCommand(roomUnit);
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setPath(new LinkedList<>());
|
||||
roomUnit.setBodyRotation(bodyRotation);
|
||||
roomUnit.setHeadRotation(headRotation);
|
||||
roomUnit.setCurrentLocation(targetTile);
|
||||
roomUnit.setGoalLocation(targetTile);
|
||||
roomUnit.setZ(targetZ);
|
||||
roomUnit.setPreviousLocation(oldLocation);
|
||||
roomUnit.setPreviousLocationZ(oldZ);
|
||||
roomUnit.resetIdleTimer();
|
||||
roomUnit.statusUpdate(true);
|
||||
|
||||
if (habbo != null) {
|
||||
THashSet<Habbo> movedHabbos = new THashSet<>();
|
||||
movedHabbos.add(habbo);
|
||||
room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos);
|
||||
} else {
|
||||
switch (roomUnit.getRoomUnitType()) {
|
||||
case BOT -> room.updateBotsAt(targetTile.x, targetTile.y);
|
||||
case PET -> room.updatePetsAt(targetTile.x, targetTile.y);
|
||||
}
|
||||
}
|
||||
|
||||
processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem);
|
||||
clearStatusComposerSuppression(roomUnit);
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean updateUserDirection(Room room, RoomUnit roomUnit, RoomUserRotation bodyRotation, RoomUserRotation headRotation) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return false;
|
||||
@@ -244,39 +320,10 @@ public final class WiredUserMovementHelper {
|
||||
return hasIgnoredFurni;
|
||||
}
|
||||
|
||||
private static boolean moveUserInstant(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, RoomTile oldLocation, HabboItem oldTopItem, HabboItem newTopItem, Habbo habbo) {
|
||||
runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> {
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setZ(targetZ);
|
||||
roomUnit.setLocation(targetTile);
|
||||
roomUnit.setPath(new LinkedList<>());
|
||||
roomUnit.setBodyRotation(bodyRotation);
|
||||
roomUnit.setHeadRotation(headRotation);
|
||||
roomUnit.resetIdleTimer();
|
||||
|
||||
if (habbo != null) {
|
||||
THashSet<Habbo> movedHabbos = new THashSet<>();
|
||||
movedHabbos.add(habbo);
|
||||
room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos);
|
||||
}
|
||||
roomUnit.statusUpdate(false);
|
||||
});
|
||||
|
||||
processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem);
|
||||
roomUnit.setPreviousLocation(roomUnit.getCurrentLocation());
|
||||
roomUnit.setPreviousLocationZ(roomUnit.getZ());
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void scheduleTileCallbacks(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, HabboItem oldTopItem, HabboItem newTopItem, int delay) {
|
||||
if (oldTopItem == null && newTopItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem);
|
||||
}, Math.max(delay, InteractionRoller.DELAY));
|
||||
}, Math.max(delay, 1));
|
||||
}
|
||||
|
||||
private static void processTileCallbacks(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, HabboItem oldTopItem, HabboItem newTopItem) {
|
||||
@@ -288,7 +335,15 @@ public final class WiredUserMovementHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldTopItem != null && oldTopItem != newTopItem) {
|
||||
HabboItem resolvedNewTopItem = resolveEnteredItem(room, targetTile);
|
||||
if (resolvedNewTopItem == null) {
|
||||
resolvedNewTopItem = room.getTopItemAt(targetTile.x, targetTile.y);
|
||||
}
|
||||
if (resolvedNewTopItem == null) {
|
||||
resolvedNewTopItem = newTopItem;
|
||||
}
|
||||
|
||||
if (oldTopItem != null && (oldTopItem != resolvedNewTopItem || !occupiesTile(oldTopItem, targetTile))) {
|
||||
try {
|
||||
oldTopItem.onWalkOff(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
} catch (Exception exception) {
|
||||
@@ -296,13 +351,155 @@ public final class WiredUserMovementHelper {
|
||||
}
|
||||
}
|
||||
|
||||
if (newTopItem != null && newTopItem != oldTopItem) {
|
||||
for (HabboItem additionalOldItem : resolveAdditionalTileItems(room, oldLocation, oldTopItem)) {
|
||||
if (additionalOldItem == resolvedNewTopItem && occupiesTile(additionalOldItem, targetTile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
newTopItem.onWalkOn(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
additionalOldItem.onWalkOff(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error("Failed to process additional wired user walk off callback", exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvedNewTopItem != null) {
|
||||
try {
|
||||
if (resolvedNewTopItem != oldTopItem || !occupiesTile(resolvedNewTopItem, oldLocation)) {
|
||||
if (!resolvedNewTopItem.canWalkOn(roomUnit, room, null)) {
|
||||
if (resolvedNewTopItem instanceof ConditionalGate) {
|
||||
roomUnit.setLocation(oldLocation);
|
||||
roomUnit.setZ(oldLocation.getStackHeight());
|
||||
roomUnit.setPreviousLocation(oldLocation);
|
||||
roomUnit.setPreviousLocationZ(oldLocation.getStackHeight());
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
resolvedNewTopItem.onWalkOn(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
}
|
||||
} else {
|
||||
resolvedNewTopItem.onWalk(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error("Failed to process wired user walk on callback", exception);
|
||||
}
|
||||
}
|
||||
|
||||
for (HabboItem additionalNewItem : resolveAdditionalTileItems(room, targetTile, resolvedNewTopItem)) {
|
||||
try {
|
||||
if (occupiesTile(additionalNewItem, oldLocation)) {
|
||||
additionalNewItem.onWalk(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
} else {
|
||||
additionalNewItem.onWalkOn(roomUnit, room, new Object[]{oldLocation, targetTile});
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error("Failed to process additional wired user walk on callback", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HabboItem resolveEnteredItem(Room room, RoomTile tile) {
|
||||
if (room == null || tile == null || room.getItemManager() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (room.canSitAt(tile.x, tile.y)) {
|
||||
HabboItem tallestChair = room.getTallestChair(tile);
|
||||
if (tallestChair != null) {
|
||||
return tallestChair;
|
||||
}
|
||||
}
|
||||
|
||||
HabboItem candidate = null;
|
||||
|
||||
for (HabboItem item : room.getItemsAt(tile)) {
|
||||
if (item == null || !occupiesTile(item, tile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean preferred = item instanceof ConditionalGate
|
||||
|| item.isWalkable()
|
||||
|| item.getBaseItem().allowWalk()
|
||||
|| item.getBaseItem().allowSit()
|
||||
|| item.getBaseItem().allowLay();
|
||||
|
||||
if (!preferred) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidate == null || item.getZ() >= candidate.getZ()) {
|
||||
candidate = item;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return room.getTopItemAt(tile.x, tile.y);
|
||||
}
|
||||
|
||||
public static double resolveUserTargetZ(Room room, RoomTile targetTile) {
|
||||
if (room == null || targetTile == null || room.getLayout() == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HabboItem targetItem = resolveEnteredItem(room, targetTile);
|
||||
double targetZ = room.getLayout().getHeightAtSquare(targetTile.x, targetTile.y);
|
||||
|
||||
if (targetItem != null) {
|
||||
targetZ = targetItem.getZ();
|
||||
|
||||
if (!targetItem.getBaseItem().allowSit() && !targetItem.getBaseItem().allowLay()) {
|
||||
targetZ += Item.getCurrentHeight(targetItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (HabboItem item : room.getItemsAt(targetTile)) {
|
||||
if (item instanceof InteractionTileWalkMagic || item instanceof InteractionStackWalkHelper) {
|
||||
targetZ = item.getZ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return targetZ;
|
||||
}
|
||||
|
||||
private static boolean occupiesTile(HabboItem item, RoomTile tile) {
|
||||
if (item == null || tile == null || item.getBaseItem() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return RoomLayout.pointInSquare(
|
||||
item.getX(),
|
||||
item.getY(),
|
||||
item.getX() + item.getBaseItem().getWidth() - 1,
|
||||
item.getY() + item.getBaseItem().getLength() - 1,
|
||||
tile.x,
|
||||
tile.y);
|
||||
}
|
||||
|
||||
private static List<HabboItem> resolveAdditionalTileItems(Room room, RoomTile tile, HabboItem primaryItem) {
|
||||
if (room == null || tile == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : room.getItemsAt(tile)) {
|
||||
if (item == null || item == primaryItem || !occupiesTile(item, tile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
items.sort(Comparator
|
||||
.comparingDouble((HabboItem item) -> item.getZ() + Item.getCurrentHeight(item))
|
||||
.thenComparingInt(HabboItem::getId)
|
||||
.reversed());
|
||||
return items;
|
||||
}
|
||||
|
||||
private static void schedulePostureSync(Room room, RoomUnit roomUnit, RoomTile targetTile, int delay) {
|
||||
|
||||
+4
@@ -51,6 +51,10 @@ public class RoomUserWalkEvent extends MessageHandler {
|
||||
|
||||
try {
|
||||
if (roomUnit != null && roomUnit.isInRoom() && roomUnit.canWalk() && !WiredFreezeUtil.isFrozen(roomUnit)) {
|
||||
if (WiredUserMovementHelper.consumeSuppressedWalkCommand(roomUnit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomUnit.cmdTeleport) {
|
||||
handleTeleport(room, (short) x, (short) y, roomUnit, habboInfo);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user