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
Merge pull request #42 from Lorenzune/feature/wired-followups-20260324
Refine wired movement and extra behavior
This commit is contained in:
+109
-1
@@ -10,6 +10,7 @@ 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;
|
||||
@@ -17,6 +18,7 @@ import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import gnu.trove.procedure.TObjectProcedure;
|
||||
@@ -29,6 +31,10 @@ 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;
|
||||
|
||||
@@ -59,10 +65,12 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
RoomTile targetTile = (this.movementDirection >= 0) ? this.getTargetTile(room, roomUnit, this.movementDirection) : null;
|
||||
boolean canMove = this.canMoveTo(room, roomUnit, targetTile);
|
||||
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);
|
||||
int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION);
|
||||
this.markActive(roomUnit, activeWindowMs);
|
||||
if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, targetBodyRotation, targetHeadRotation,
|
||||
animationDuration, noAnimation)) {
|
||||
if (hasRotation) {
|
||||
@@ -73,6 +81,7 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
}
|
||||
|
||||
if (hasRotation) {
|
||||
this.markActive(roomUnit, activeWindowMs);
|
||||
WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation);
|
||||
}
|
||||
}
|
||||
@@ -275,6 +284,105 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
return true;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int delay;
|
||||
int movementDirection;
|
||||
|
||||
+193
-3
@@ -1,15 +1,41 @@
|
||||
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.InteractionWiredEffect;
|
||||
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.api.IWiredEffect;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
public class WiredExtraRandom extends InteractionWiredExtra {
|
||||
public static final int CODE = 63;
|
||||
private static final int DEFAULT_PICK_AMOUNT = 1;
|
||||
private static final int DEFAULT_SKIP_EXECUTIONS = 0;
|
||||
private static final int MAX_PICK_AMOUNT = 1000;
|
||||
private static final int MAX_SKIP_EXECUTIONS = 1000;
|
||||
|
||||
private final Deque<List<Integer>> recentExecutionEffectIds = new ArrayDeque<>();
|
||||
|
||||
private int pickAmount = DEFAULT_PICK_AMOUNT;
|
||||
private int skipExecutions = DEFAULT_SKIP_EXECUTIONS;
|
||||
|
||||
public WiredExtraRandom(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
@@ -23,28 +49,192 @@ public class WiredExtraRandom extends InteractionWiredExtra {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
int resolvedPickAmount = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : DEFAULT_PICK_AMOUNT;
|
||||
int resolvedSkipExecutions = (settings.getIntParams().length > 1) ? settings.getIntParams()[1] : DEFAULT_SKIP_EXECUTIONS;
|
||||
|
||||
this.pickAmount = normalizePickAmount(resolvedPickAmount);
|
||||
this.skipExecutions = normalizeSkipExecutions(resolvedSkipExecutions);
|
||||
this.clearRecentExecutions();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return null;
|
||||
return WiredManager.getGson().toJson(new JsonData(this.pickAmount, this.skipExecutions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString("");
|
||||
message.appendInt(2);
|
||||
message.appendInt(this.pickAmount);
|
||||
message.appendInt(this.skipExecutions);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
this.pickAmount = normalizePickAmount((data != null) ? data.pickAmount : DEFAULT_PICK_AMOUNT);
|
||||
this.skipExecutions = normalizeSkipExecutions((data != null) ? data.skipExecutions : DEFAULT_SKIP_EXECUTIONS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
|
||||
this.pickAmount = DEFAULT_PICK_AMOUNT;
|
||||
this.skipExecutions = DEFAULT_SKIP_EXECUTIONS;
|
||||
this.clearRecentExecutions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(Room room, com.eu.habbo.habbohotel.rooms.RoomTile oldLocation, com.eu.habbo.habbohotel.rooms.RoomTile newLocation) {
|
||||
super.onMove(room, oldLocation, newLocation);
|
||||
this.clearRecentExecutions();
|
||||
}
|
||||
|
||||
public List<InteractionWiredEffect> selectEffects(List<InteractionWiredEffect> effects) {
|
||||
return this.selectRandomEffects(effects, InteractionWiredEffect::getId);
|
||||
}
|
||||
|
||||
public List<IWiredEffect> selectWiredEffects(List<IWiredEffect> effects) {
|
||||
return this.selectRandomEffects(effects, effect -> {
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
return ((InteractionWiredEffect) effect).getId();
|
||||
}
|
||||
|
||||
return System.identityHashCode(effect);
|
||||
});
|
||||
}
|
||||
|
||||
public int getPickAmount() {
|
||||
return this.pickAmount;
|
||||
}
|
||||
|
||||
public int getSkipExecutions() {
|
||||
return this.skipExecutions;
|
||||
}
|
||||
|
||||
private synchronized <T> List<T> selectRandomEffects(List<T> effects, ToIntFunction<T> idResolver) {
|
||||
if (effects == null || effects.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<T> shuffledEffects = new ArrayList<>(effects);
|
||||
Collections.shuffle(shuffledEffects, Emulator.getRandom());
|
||||
|
||||
int desiredAmount = Math.min(this.pickAmount, shuffledEffects.size());
|
||||
Set<Integer> recentEffectIds = this.getRecentEffectIds();
|
||||
LinkedHashSet<T> selectedEffects = new LinkedHashSet<>();
|
||||
|
||||
for (T effect : shuffledEffects) {
|
||||
if (recentEffectIds.contains(idResolver.applyAsInt(effect))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
selectedEffects.add(effect);
|
||||
if (selectedEffects.size() >= desiredAmount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedEffects.size() < desiredAmount) {
|
||||
for (T effect : shuffledEffects) {
|
||||
selectedEffects.add(effect);
|
||||
if (selectedEffects.size() >= desiredAmount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.recordExecution(selectedEffects, idResolver);
|
||||
return new ArrayList<>(selectedEffects);
|
||||
}
|
||||
|
||||
private synchronized void clearRecentExecutions() {
|
||||
this.recentExecutionEffectIds.clear();
|
||||
}
|
||||
|
||||
private Set<Integer> getRecentEffectIds() {
|
||||
LinkedHashSet<Integer> ids = new LinkedHashSet<>();
|
||||
|
||||
if (this.skipExecutions <= 0) {
|
||||
return ids;
|
||||
}
|
||||
|
||||
for (List<Integer> executionIds : this.recentExecutionEffectIds) {
|
||||
ids.addAll(executionIds);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private <T> void recordExecution(Collection<T> selectedEffects, ToIntFunction<T> idResolver) {
|
||||
if (this.skipExecutions <= 0) {
|
||||
this.recentExecutionEffectIds.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> executionIds = new ArrayList<>();
|
||||
if (selectedEffects != null) {
|
||||
for (T effect : selectedEffects) {
|
||||
if (effect != null) {
|
||||
executionIds.add(idResolver.applyAsInt(effect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.recentExecutionEffectIds.addLast(executionIds);
|
||||
|
||||
while (this.recentExecutionEffectIds.size() > this.skipExecutions) {
|
||||
this.recentExecutionEffectIds.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizePickAmount(int value) {
|
||||
return Math.max(1, Math.min(MAX_PICK_AMOUNT, value));
|
||||
}
|
||||
|
||||
private static int normalizeSkipExecutions(int value) {
|
||||
return Math.max(0, Math.min(MAX_SKIP_EXECUTIONS, value));
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int pickAmount;
|
||||
int skipExecutions;
|
||||
|
||||
JsonData(int pickAmount, int skipExecutions) {
|
||||
this.pickAmount = pickAmount;
|
||||
this.skipExecutions = skipExecutions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-3
@@ -1,11 +1,15 @@
|
||||
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.InteractionWiredEffect;
|
||||
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.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.api.IWiredEffect;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
@@ -16,6 +20,8 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class WiredExtraUnseen extends InteractionWiredExtra {
|
||||
public static final int CODE = 62;
|
||||
|
||||
/**
|
||||
* Maximum number of effect IDs to track to prevent memory leaks.
|
||||
* When limit is reached, oldest entries are removed automatically.
|
||||
@@ -41,19 +47,34 @@ public class WiredExtraUnseen extends InteractionWiredExtra {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return null;
|
||||
return WiredManager.getGson().toJson(new JsonData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString("");
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
|
||||
this.onPickUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,6 +129,47 @@ public class WiredExtraUnseen extends InteractionWiredExtra {
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
|
||||
public List<IWiredEffect> selectWiredEffects(List<IWiredEffect> effects) {
|
||||
synchronized (this.seenList) {
|
||||
List<IWiredEffect> unseenEffects = new ArrayList<>();
|
||||
|
||||
for (IWiredEffect effect : effects) {
|
||||
if ((effect instanceof InteractionWiredEffect)
|
||||
&& !this.seenList.contains(((InteractionWiredEffect) effect).getId())) {
|
||||
unseenEffects.add(effect);
|
||||
}
|
||||
}
|
||||
|
||||
IWiredEffect effect = null;
|
||||
if (!unseenEffects.isEmpty()) {
|
||||
effect = unseenEffects.get(0);
|
||||
} else {
|
||||
this.seenList.clear();
|
||||
|
||||
if (!effects.isEmpty()) {
|
||||
effect = effects.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
if (this.seenList.size() >= MAX_SEEN_LIST_SIZE) {
|
||||
Integer oldest = this.seenList.iterator().next();
|
||||
this.seenList.remove(oldest);
|
||||
}
|
||||
|
||||
this.seenList.add(((InteractionWiredEffect) effect).getId());
|
||||
}
|
||||
|
||||
if (effect == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<IWiredEffect> selectedEffects = new ArrayList<>();
|
||||
selectedEffects.add(effect);
|
||||
return selectedEffects;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current size of the seen list.
|
||||
@@ -127,4 +189,12 @@ public class WiredExtraUnseen extends InteractionWiredExtra {
|
||||
this.seenList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -45,17 +45,7 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.furniSource) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED:
|
||||
return this.matchesSourceItem(this.items, sourceItem);
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
return this.matchesSourceItem(
|
||||
WiredTriggerSourceUtil.resolveItems(this, event, WiredSourceUtil.SOURCE_SELECTOR, this.items),
|
||||
sourceItem);
|
||||
case WiredSourceUtil.SOURCE_TRIGGER:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return this.matchesSourceItem(this.resolveCandidateItems(triggerItem, event), sourceItem);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -225,6 +215,18 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger {
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private Iterable<HabboItem> resolveCandidateItems(HabboItem triggerItem, WiredEvent event) {
|
||||
switch (this.furniSource) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED:
|
||||
return this.items;
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
return WiredTriggerSourceUtil.resolveItems(this, event, WiredSourceUtil.SOURCE_SELECTOR, this.items);
|
||||
case WiredSourceUtil.SOURCE_TRIGGER:
|
||||
default:
|
||||
return (triggerItem != null) ? java.util.Collections.singletonList(triggerItem) : java.util.Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matchesSourceItem(Iterable<HabboItem> candidateItems, HabboItem sourceItem) {
|
||||
if (candidateItems == null || sourceItem == null) {
|
||||
return false;
|
||||
|
||||
+3
-9
@@ -36,7 +36,7 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger {
|
||||
|
||||
@Override
|
||||
public boolean matches(HabboItem triggerItem, WiredEvent event) {
|
||||
if (this.key.length() <= 0) {
|
||||
if ((this.matchMode != MATCH_ALL_WORDS) && this.key.length() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger {
|
||||
this.matchMode = (params.length > 0) ? this.normalizeMatchMode(params[0]) : MATCH_CONTAINS;
|
||||
this.hideMessage = (params.length > 1) && (params[1] == 1);
|
||||
this.ownerOnly = (params.length > 2) && (params[2] == 1);
|
||||
this.key = settings.getStringParam();
|
||||
this.key = (this.matchMode == MATCH_ALL_WORDS) ? "" : settings.getStringParam();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -149,13 +149,7 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger {
|
||||
case MATCH_EXACT:
|
||||
return normalizedText.equals(normalizedKey);
|
||||
case MATCH_ALL_WORDS:
|
||||
String[] requiredParts = normalizedKey.split("\\s+");
|
||||
for (String part : requiredParts) {
|
||||
if (!part.isEmpty() && !normalizedText.contains(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return !normalizedText.isEmpty();
|
||||
case MATCH_CONTAINS:
|
||||
default:
|
||||
return normalizedText.contains(normalizedKey);
|
||||
|
||||
@@ -189,21 +189,23 @@ public class WiredHandler {
|
||||
|
||||
trigger.setCooldown(millis);
|
||||
|
||||
boolean hasExtraRandom = room.getRoomSpecialTypes().hasExtraType(trigger.getX(), trigger.getY(), WiredExtraRandom.class);
|
||||
boolean hasExtraUnseen = room.getRoomSpecialTypes().hasExtraType(trigger.getX(), trigger.getY(), WiredExtraUnseen.class);
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY());
|
||||
WiredExtraRandom randomExtra = null;
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
extra.activateBox(room, roomUnit, millis);
|
||||
if (randomExtra == null && extra instanceof WiredExtraRandom) {
|
||||
randomExtra = (WiredExtraRandom) extra;
|
||||
}
|
||||
}
|
||||
|
||||
List<InteractionWiredEffect> effectList = new ArrayList<>(effects);
|
||||
|
||||
if (hasExtraRandom || hasExtraUnseen) {
|
||||
if (randomExtra != null || hasExtraUnseen) {
|
||||
Collections.shuffle(effectList);
|
||||
}
|
||||
|
||||
|
||||
if (hasExtraUnseen) {
|
||||
for (InteractionWiredExtra extra : room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY())) {
|
||||
if (extra instanceof WiredExtraUnseen) {
|
||||
@@ -213,12 +215,11 @@ public class WiredHandler {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (randomExtra != null) {
|
||||
effectsToExecute.addAll(randomExtra.selectEffects(effectList));
|
||||
} else {
|
||||
for (final InteractionWiredEffect effect : effectList) {
|
||||
boolean executed = effectsToExecute.add(effect); //triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
if (hasExtraRandom && executed) {
|
||||
break;
|
||||
}
|
||||
effectsToExecute.add(effect); //triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboSaysKeyword;
|
||||
@@ -392,22 +394,38 @@ public final class WiredEngine {
|
||||
List<IWiredEffect> toExecute;
|
||||
|
||||
if (stack.useRandom()) {
|
||||
// Random mode: pick one random regular effect
|
||||
WiredExtraRandom randomExtra = getRandomExtra(ctx.room(), stack);
|
||||
if (regulars.isEmpty()) {
|
||||
toExecute = new ArrayList<>();
|
||||
} else if (randomExtra != null) {
|
||||
toExecute = randomExtra.selectWiredEffects(regulars);
|
||||
debug(ctx.room(), "Random mode: selected {} effect(s), skip window {}", toExecute.size(), randomExtra.getSkipExecutions());
|
||||
} else {
|
||||
int randomIndex = new Random().nextInt(regulars.size());
|
||||
toExecute = Collections.singletonList(regulars.get(randomIndex));
|
||||
debug(ctx.room(), "Random mode: selected effect {}/{}", randomIndex + 1, regulars.size());
|
||||
}
|
||||
} else if (stack.useUnseen()) {
|
||||
// Unseen mode: round-robin among regular effects
|
||||
// Unseen mode: execute in stable order with memory
|
||||
if (regulars.isEmpty()) {
|
||||
toExecute = new ArrayList<>();
|
||||
} else {
|
||||
int index = getNextUnseenIndex(stack, regulars.size());
|
||||
toExecute = Collections.singletonList(regulars.get(index));
|
||||
debug(ctx.room(), "Unseen mode: selected effect {}/{}", index + 1, regulars.size());
|
||||
WiredExtraUnseen unseenExtra = getUnseenExtra(ctx.room(), stack);
|
||||
|
||||
if (unseenExtra != null) {
|
||||
toExecute = unseenExtra.selectWiredEffects(regulars);
|
||||
|
||||
if (!toExecute.isEmpty()) {
|
||||
int selectedIndex = regulars.indexOf(toExecute.get(0));
|
||||
debug(ctx.room(), "Unseen mode: selected effect {}/{}", selectedIndex + 1, regulars.size());
|
||||
} else {
|
||||
debug(ctx.room(), "Unseen mode: no eligible effect found");
|
||||
}
|
||||
} else {
|
||||
int index = getNextUnseenIndex(stack, regulars.size());
|
||||
toExecute = Collections.singletonList(regulars.get(index));
|
||||
debug(ctx.room(), "Unseen mode fallback: selected effect {}/{}", index + 1, regulars.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal mode: regular effects in random order
|
||||
@@ -684,6 +702,40 @@ public final class WiredEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private WiredExtraRandom getRandomExtra(Room room, WiredStack stack) {
|
||||
InteractionWiredExtra extra = getStackExtra(room, stack, WiredExtraRandom.class);
|
||||
|
||||
return (extra instanceof WiredExtraRandom) ? (WiredExtraRandom) extra : null;
|
||||
}
|
||||
|
||||
private WiredExtraUnseen getUnseenExtra(Room room, WiredStack stack) {
|
||||
InteractionWiredExtra extra = getStackExtra(room, stack, WiredExtraUnseen.class);
|
||||
|
||||
return (extra instanceof WiredExtraUnseen) ? (WiredExtraUnseen) extra : null;
|
||||
}
|
||||
|
||||
private <T extends InteractionWiredExtra> InteractionWiredExtra getStackExtra(Room room, WiredStack stack, Class<T> extraClass) {
|
||||
if (room == null || stack == null || stack.triggerItem() == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(
|
||||
stack.triggerItem().getX(),
|
||||
stack.triggerItem().getY());
|
||||
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extraClass.isInstance(extra)) {
|
||||
return extra;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services used by this engine.
|
||||
* @return the wired services
|
||||
|
||||
@@ -36,8 +36,6 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
/**
|
||||
* Manager class for the new wired engine system.
|
||||
@@ -83,7 +81,6 @@ public final class WiredManager {
|
||||
private static final boolean DEFAULT_ENABLED = false;
|
||||
private static final boolean DEFAULT_EXCLUSIVE = false;
|
||||
private static final int DEFAULT_MAX_STEPS = 100;
|
||||
private static final long FURNI_CLICK_TRIGGER_DELAY_MS = 400L;
|
||||
|
||||
/** The singleton engine instance */
|
||||
private static volatile WiredEngine engine;
|
||||
@@ -93,8 +90,6 @@ public final class WiredManager {
|
||||
|
||||
/** Whether the engine is initialized */
|
||||
private static volatile boolean initialized = false;
|
||||
private static final ConcurrentHashMap<String, ScheduledFuture<?>> pendingFurniClickTriggers = new ConcurrentHashMap<>();
|
||||
|
||||
private WiredManager() {
|
||||
// Static utility class
|
||||
}
|
||||
@@ -168,13 +163,6 @@ public final class WiredManager {
|
||||
engine.clearUnseenCache();
|
||||
}
|
||||
|
||||
pendingFurniClickTriggers.values().forEach(future -> {
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
});
|
||||
pendingFurniClickTriggers.clear();
|
||||
|
||||
initialized = false;
|
||||
LOGGER.info("Wired Manager shutdown complete");
|
||||
}
|
||||
@@ -267,34 +255,11 @@ public final class WiredManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String clickKey = getPendingFurniClickKey(room, user, item);
|
||||
|
||||
cancelPendingUserClicksFurni(room, user, item);
|
||||
|
||||
ScheduledFuture<?> future = Emulator.getThreading().run(() -> {
|
||||
pendingFurniClickTriggers.remove(clickKey);
|
||||
triggerUserClicksFurni(room, user, item);
|
||||
}, FURNI_CLICK_TRIGGER_DELAY_MS);
|
||||
|
||||
if (future != null) {
|
||||
pendingFurniClickTriggers.put(clickKey, future);
|
||||
}
|
||||
triggerUserClicksFurni(room, user, item);
|
||||
}
|
||||
|
||||
public static void cancelPendingUserClicksFurni(Room room, RoomUnit user, HabboItem item) {
|
||||
if (room == null || user == null || item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScheduledFuture<?> future = pendingFurniClickTriggers.remove(getPendingFurniClickKey(room, user, item));
|
||||
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPendingFurniClickKey(Room room, RoomUnit user, HabboItem item) {
|
||||
return room.getId() + ":" + user.getId() + ":" + item.getId();
|
||||
// Click furni triggers are now executed immediately.
|
||||
}
|
||||
/**
|
||||
* Trigger when a user clicks invisible click tile furniture.
|
||||
|
||||
+12
@@ -10,9 +10,12 @@ import com.eu.habbo.habbohotel.rooms.BedProfile;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboInfo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectMoveRotateUser;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitOnRollerComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.plugin.events.users.UserIdleEvent;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
@@ -109,10 +112,19 @@ public class RoomUserWalkEvent extends MessageHandler {
|
||||
|
||||
// This is where we set the end location and begin finding a path
|
||||
if (tile.isWalkable() || room.canSitOrLayAt(tile.x, tile.y)) {
|
||||
if (WiredEffectMoveRotateUser.handleWalkWhileActive(room, roomUnit, tile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomUnit.getMoveBlockingTask() != null) {
|
||||
roomUnit.getMoveBlockingTask().get();
|
||||
}
|
||||
|
||||
if (WiredUserMovementHelper.shouldSuppressStatusComposer(roomUnit)) {
|
||||
WiredUserMovementHelper.clearStatusComposerSuppression(roomUnit);
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
}
|
||||
|
||||
roomUnit.setGoalLocation(tile);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user