From 75151accb57c56dfa12c27d8b76262526ccbff8d Mon Sep 17 00:00:00 2001 From: Lorenzune Date: Thu, 19 Mar 2026 00:01:59 +0100 Subject: [PATCH] feat(wired): add upcounter clock controls and checks --- .../habbo/habbohotel/items/ItemManager.java | 6 + .../games/InteractionGameTimer.java | 85 ++++-- .../games/InteractionGameUpCounter.java | 214 ++++++++++++++ .../WiredConditionCounterTimeMatches.java | 270 ++++++++++++++++++ .../wired/effects/WiredEffectAdjustClock.java | 252 ++++++++++++++++ .../effects/WiredEffectControlClock.java | 244 ++++++++++++++++ .../triggers/WiredTriggerClockCounter.java | 229 +++++++++++++++ .../com/eu/habbo/habbohotel/rooms/Room.java | 7 +- .../eu/habbo/habbohotel/users/HabboItem.java | 5 +- .../habbohotel/wired/WiredConditionType.java | 3 +- .../habbohotel/wired/WiredEffectType.java | 4 +- .../habbohotel/wired/WiredTriggerType.java | 1 + .../habbohotel/wired/core/WiredEvent.java | 3 + .../habbohotel/wired/core/WiredManager.java | 9 + .../habbohotel/wired/migrate/WiredEvents.java | 6 + .../runnables/games/GameUpCounter.java | 46 +++ 16 files changed, 1356 insertions(+), 28 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java index 51845de6..4e645d4b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java @@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.items; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.*; import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTimer; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiPuck; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiSphere; import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiTeleporter; @@ -206,6 +207,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("room_invisible_click_tile", InteractionDefault.class)); this.interactionsList.add(new ItemInteraction("game_timer", InteractionGameTimer.class)); + this.interactionsList.add(new ItemInteraction("game_upcounter", InteractionGameUpCounter.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_on_furni", WiredTriggerHabboWalkOnFurni.class)); this.interactionsList.add(new ItemInteraction("wf_trg_walks_off_furni", WiredTriggerHabboWalkOffFurni.class)); @@ -216,6 +218,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_trg_enter_room", WiredTriggerHabboEntersRoom.class)); this.interactionsList.add(new ItemInteraction("wf_trg_leave_room", WiredTriggerHabboLeavesRoom.class)); this.interactionsList.add(new ItemInteraction("wf_trg_says_something", WiredTriggerHabboSaysKeyword.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_clock_counter", WiredTriggerClockCounter.class)); this.interactionsList.add(new ItemInteraction("wf_trg_periodically", WiredTriggerRepeater.class)); this.interactionsList.add(new ItemInteraction("wf_trg_period_short", WiredTriggerRepeaterShort.class)); this.interactionsList.add(new ItemInteraction("wf_trg_period_long", WiredTriggerRepeaterLong.class)); @@ -271,6 +274,8 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_act_furni_to_furni", WiredEffectFurniToFurni.class)); this.interactionsList.add(new ItemInteraction("wf_act_set_altitude", WiredEffectSetAltitude.class)); this.interactionsList.add(new ItemInteraction("wf_act_rel_mov", WiredEffectRelativeMove.class)); + this.interactionsList.add(new ItemInteraction("wf_act_control_clock", WiredEffectControlClock.class)); + this.interactionsList.add(new ItemInteraction("wf_act_adjust_clock", WiredEffectAdjustClock.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_area", WiredEffectFurniArea.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_neighborhood", WiredEffectFurniNeighborhood.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_bytype", WiredEffectFurniByType.class)); @@ -303,6 +308,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_cnd_has_handitem", WiredConditionHabboHasHandItem.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_date_rng_active", WiredConditionDateRangeActive.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_valid_moves", WiredConditionMovementValidation.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java index e93d5308..62160f59 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameTimer.java @@ -24,13 +24,13 @@ import java.util.Arrays; public class InteractionGameTimer extends HabboItem { private static final Logger LOGGER = LoggerFactory.getLogger(InteractionGameTimer.class); - private int[] TIMER_INTERVAL_STEPS = new int[] { 30, 60, 120, 180, 300, 600 }; + protected int[] TIMER_INTERVAL_STEPS = new int[] { 30, 60, 120, 180, 300, 600 }; - private int baseTime = 0; - private int timeNow = 0; - private boolean isRunning = false; - private boolean isPaused = false; - private boolean threadActive = false; + protected int baseTime = 0; + protected int timeNow = 0; + protected boolean isRunning = false; + protected boolean isPaused = false; + protected boolean threadActive = false; public enum InteractionGameTimerAction { START_STOP(1), @@ -83,7 +83,7 @@ public class InteractionGameTimer extends HabboItem { parseCustomParams(item); } - private void parseCustomParams(Item baseItem) { + protected void parseCustomParams(Item baseItem) { try { TIMER_INTERVAL_STEPS = Arrays.stream(baseItem.getCustomParams().split(",")) .mapToInt(s -> { @@ -114,7 +114,7 @@ public class InteractionGameTimer extends HabboItem { } } - private void createNewGame(Room room) { + protected void createNewGame(Room room) { for(Class gameClass : Emulator.getGameEnvironment().getRoomManager().getGameTypes()) { Game existingGame = room.getGame(gameClass); @@ -132,13 +132,13 @@ public class InteractionGameTimer extends HabboItem { } } - private void pause(Room room) { + protected void pause(Room room) { for (Game game : room.getGames()) { game.pause(); } } - private void unpause(Room room) { + protected void unpause(Room room) { for (Game game : room.getGames()) { game.unpause(); } @@ -155,7 +155,8 @@ public class InteractionGameTimer extends HabboItem { public void onPickUp(Room room) { this.endGame(room); - this.setExtradata(this.baseTime + "\t" + this.baseTime); + this.timeNow = this.getInitialTimeValue(); + this.setExtradata(this.timeNow + "\t" + this.baseTime); this.needsUpdate(true); } @@ -165,7 +166,7 @@ public class InteractionGameTimer extends HabboItem { this.baseTime = this.TIMER_INTERVAL_STEPS[0]; } - this.timeNow = this.baseTime; + this.timeNow = this.getInitialTimeValue(); this.setExtradata(this.timeNow + "\t" + this.baseTime); room.updateItem(this); @@ -212,7 +213,7 @@ public class InteractionGameTimer extends HabboItem { this.createNewGame(room); - this.timeNow = this.baseTime; + this.resetTimeForStart(); this.isRunning = true; this.isPaused = false; @@ -221,7 +222,7 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } else if (client != null) { if (!(room.hasRights(client.getHabbo()) || client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER))) @@ -244,13 +245,13 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this)); + this.scheduleTimerRunnable(this.getTimerResumeDelayMs()); } } } else { this.isPaused = false; this.isRunning = true; - this.timeNow = this.baseTime; + this.resetTimeForStart(); room.updateItem(this); this.createNewGame(room); @@ -258,7 +259,7 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } @@ -290,15 +291,15 @@ public class InteractionGameTimer extends HabboItem { if (!isRunning) { isRunning = true; isPaused = false; - if(timeNow <= 0) { - timeNow = baseTime; + if (this.shouldResetTimeOnStart()) { + this.resetTimeForStart(); room.updateItem(this); } this.createNewGame(room); WiredManager.triggerGameStarts(room); if (!threadActive) { threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerStartDelayMs()); } } } @@ -322,12 +323,12 @@ public class InteractionGameTimer extends HabboItem { if (!this.threadActive) { this.threadActive = true; - Emulator.getThreading().run(new GameTimer(this), 1000); + this.scheduleTimerRunnable(this.getTimerResumeDelayMs()); } } } - private void increaseTimer(Room room) { + protected void increaseTimer(Room room) { if (this.isRunning) return; @@ -347,13 +348,45 @@ public class InteractionGameTimer extends HabboItem { } this.baseTime = baseTime; + this.timeNow = this.getInitialTimeValue(); this.setExtradata(this.timeNow + "\t" + this.baseTime); - - this.timeNow = this.baseTime; room.updateItem(this); this.needsUpdate(true); } + protected int getInitialTimeValue() { + return this.baseTime; + } + + protected boolean shouldResetTimeOnStart() { + return this.timeNow <= 0; + } + + protected void resetTimeForStart() { + this.timeNow = this.baseTime; + } + + protected Runnable createTimerRunnable() { + return new GameTimer(this); + } + + protected long getTimerStartDelayMs() { + return 1000L; + } + + protected long getTimerResumeDelayMs() { + return 0L; + } + + protected void scheduleTimerRunnable(long delayMs) { + if (delayMs <= 0) { + Emulator.getThreading().run(this.createTimerRunnable()); + return; + } + + Emulator.getThreading().run(this.createTimerRunnable(), delayMs); + } + @Override public String getDatabaseExtraData() { return this.getExtradata(); @@ -391,4 +424,8 @@ public class InteractionGameTimer extends HabboItem { public void setTimeNow(int timeNow) { this.timeNow = timeNow; } + + public int getBaseTime() { + return this.baseTime; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java new file mode 100644 index 00000000..41e05a7a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/InteractionGameUpCounter.java @@ -0,0 +1,214 @@ +package com.eu.habbo.habbohotel.items.interactions.games; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.threading.runnables.games.GameUpCounter; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class InteractionGameUpCounter extends InteractionGameTimer { + private static final int ONE_SECOND_MS = 1000; + private static final int HALF_SECOND_MS = 500; + private static final int MAX_UPCOUNTER_TIME = ((99 * 60) + 59); + private int subSecondOffsetMs = 0; + + public InteractionGameUpCounter(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.normalizeCounterState(); + } + + public InteractionGameUpCounter(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.normalizeCounterState(); + } + + @Override + protected void parseCustomParams(Item baseItem) { + this.TIMER_INTERVAL_STEPS = new int[] { MAX_UPCOUNTER_TIME }; + } + + private void normalizeCounterState() { + this.baseTime = MAX_UPCOUNTER_TIME; + this.setCurrentTimeInMs(this.parseStoredTime() * ONE_SECOND_MS); + this.setExtradata(this.timeNow + "\t" + this.baseTime); + } + + private int parseStoredTime() { + try { + String[] data = this.getExtradata().split("\t"); + + if (data.length > 0) { + int storedTime = Integer.parseInt(data[0]); + return Math.max(0, Math.min(storedTime, this.baseTime)); + } + } catch (Exception ignored) { + } + + return Math.max(0, Math.min(this.timeNow, this.baseTime)); + } + + @Override + protected int getInitialTimeValue() { + return 0; + } + + @Override + protected boolean shouldResetTimeOnStart() { + return this.timeNow >= this.baseTime; + } + + @Override + protected void resetTimeForStart() { + this.setCurrentTimeInMs(0); + } + + @Override + protected void increaseTimer(Room room) { + if (this.isRunning && !this.isPaused) { + return; + } + + if (this.isRunning) { + this.endGame(room); + WiredManager.triggerGameEnds(room); + } + + this.baseTime = MAX_UPCOUNTER_TIME; + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + } + + @Override + protected Runnable createTimerRunnable() { + return new GameUpCounter(this); + } + + @Override + protected long getTimerStartDelayMs() { + return this.getNextTickDelayMs(); + } + + @Override + protected long getTimerResumeDelayMs() { + return this.getNextTickDelayMs(); + } + + public int getCurrentTimeInMs() { + return (this.timeNow * ONE_SECOND_MS) + this.subSecondOffsetMs; + } + + public int getMaximumTimeInMs() { + return this.baseTime * ONE_SECOND_MS; + } + + public long getNextTickDelayMs() { + return (this.subSecondOffsetMs > 0) ? HALF_SECOND_MS : ONE_SECOND_MS; + } + + public void setCurrentTimeInMs(int totalMs) { + int clamped = Math.max(0, Math.min(totalMs, this.getMaximumTimeInMs())); + int remainder = clamped % ONE_SECOND_MS; + + this.timeNow = (clamped / ONE_SECOND_MS); + this.subSecondOffsetMs = (remainder >= HALF_SECOND_MS) ? HALF_SECOND_MS : 0; + } + + public void advanceCounterInMs(int deltaMs) { + this.setCurrentTimeInMs(this.getCurrentTimeInMs() + deltaMs); + } + + private void applyCounterState(Room room, boolean updateRoom) { + this.setExtradata(this.timeNow + "\t" + this.baseTime); + + if (updateRoom && room != null) { + room.updateItem(this); + } + + this.needsUpdate(true); + } + + public void restartFromZero(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + if (wasActive) { + this.endGame(room); + WiredManager.triggerGameEnds(room); + } + + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + + this.startTimer(room); + } + + public void stopCounter(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + this.endGame(room); + this.applyCounterState(room, true); + + if (wasActive) { + WiredManager.triggerGameEnds(room); + } + } + + public void resetCounter(Room room) { + boolean wasActive = this.isRunning || this.isPaused; + + this.endGame(room); + this.setCurrentTimeInMs(0); + this.applyCounterState(room, true); + + if (wasActive) { + WiredManager.triggerGameEnds(room); + } + } + + public void pauseCounter(Room room) { + if (!this.isRunning || this.isPaused) { + return; + } + + this.pauseTimer(room); + this.applyCounterState(room, true); + } + + public void resumeCounter(Room room) { + if (!this.isPaused) { + return; + } + + this.resumeTimer(room); + this.applyCounterState(room, true); + } + + public void adjustCounter(Room room, int operator, int minutes, int halfSecondSteps) { + int deltaMs = (Math.max(0, minutes) * 60000) + (Math.max(0, halfSecondSteps) * HALF_SECOND_MS); + int nextTimeMs = this.getCurrentTimeInMs(); + + switch (operator) { + case 0: + nextTimeMs += deltaMs; + break; + case 1: + nextTimeMs -= deltaMs; + break; + case 2: + default: + nextTimeMs = deltaMs; + break; + } + + this.setCurrentTimeInMs(nextTimeMs); + this.applyCounterState(room, true); + } + + public void resetOnRoomUnload(Room room) { + this.endGame(room); + this.setThreadActive(false); + this.setCurrentTimeInMs(0); + this.applyCounterState(null, false); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java new file mode 100644 index 00000000..639eb38c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java @@ -0,0 +1,270 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredConditionCounterTimeMatches extends InteractionWiredCondition { + private static final int COMPARISON_LESS = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER = 2; + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredConditionType type = WiredConditionType.COUNTER_TIME_MATCHES; + + private final THashSet items; + private int comparison = COMPARISON_EQUAL; + private int minutes = 0; + private int halfSecondSteps = 0; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredConditionCounterTimeMatches(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredConditionCounterTimeMatches(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean evaluate(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return false; + } + + this.refresh(room); + + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (targets.isEmpty()) { + return false; + } + + int targetTimeInMs = this.getTargetTimeInMs(); + + for (HabboItem item : targets) { + if (!(item instanceof InteractionGameUpCounter)) { + return false; + } + + int currentTimeInMs = ((InteractionGameUpCounter) item).getCurrentTimeInMs(); + + switch (this.comparison) { + case COMPARISON_LESS: + if (currentTimeInMs >= targetTimeInMs) { + return false; + } + break; + case COMPARISON_GREATER: + if (currentTimeInMs <= targetTimeInMs) { + return false; + } + break; + default: + if (currentTimeInMs != targetTimeInMs) { + return false; + } + break; + } + } + + return true; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.minutes, + this.halfSecondSteps, + this.furniSource, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + this.furniSource = data.furniSource; + + if (data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.comparison); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(this.furniSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL; + this.minutes = (params.length > 1) ? this.normalizeMinutes(params[1]) : 0; + this.halfSecondSteps = (params.length > 2) ? this.normalizeHalfSecondSteps(params[2]) : 0; + this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER; + + this.items.clear(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + + return true; + } + + private void refresh(Room room) { + THashSet remove = new THashSet<>(); + + for (HabboItem item : this.items) { + HabboItem roomItem = room.getHabboItem(item.getId()); + if (!(roomItem instanceof InteractionGameUpCounter)) { + remove.add(item); + } + } + + for (HabboItem item : remove) { + this.items.remove(item); + } + } + + private int getTargetTimeInMs() { + return (this.minutes * 60_000) + (this.halfSecondSteps * 500); + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + return value; + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + static class JsonData { + int comparison; + int minutes; + int halfSecondSteps; + int furniSource; + List itemIds; + + public JsonData(int comparison, int minutes, int halfSecondSteps, int furniSource, List itemIds) { + this.comparison = comparison; + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + this.furniSource = furniSource; + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java new file mode 100644 index 00000000..183eec7d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectAdjustClock.java @@ -0,0 +1,252 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.effects; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredEffectAdjustClock extends InteractionWiredEffect { + private static final int OPERATOR_INCREASE = 0; + private static final int OPERATOR_DECREASE = 1; + private static final int OPERATOR_SET = 2; + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredEffectType type = WiredEffectType.ADJUST_CLOCK; + + private final List items = new ArrayList<>(); + private int operator = OPERATOR_SET; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int minutes = 0; + private int halfSecondSteps = 0; + + public WiredEffectAdjustClock(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectAdjustClock(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + + if (room == null) { + return; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + for (HabboItem item : effectiveItems) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + ((InteractionGameUpCounter) item).adjustCounter(room, this.operator, this.minutes, this.halfSecondSteps); + } + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.operator, + this.furniSource, + this.minutes, + this.halfSecondSteps + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + String wiredData = set.getString("wired_data"); + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.setDelay(data.delay); + this.operator = this.normalizeOperator(data.operator); + this.furniSource = data.furniSource; + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.operator = OPERATOR_SET; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.minutes = 0; + this.halfSecondSteps = 0; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.operator = OPERATOR_SET; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.minutes = 0; + this.halfSecondSteps = 0; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List itemsSnapshot = new ArrayList<>(this.items); + itemsSnapshot.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + + this.items.clear(); + this.items.addAll(itemsSnapshot); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(itemsSnapshot.size()); + for (HabboItem item : itemsSnapshot) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.operator); + message.appendInt(this.furniSource); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + int[] params = settings.getIntParams(); + + if (params.length < 4) { + throw new WiredSaveException("Invalid data"); + } + + this.operator = this.normalizeOperator(params[0]); + this.furniSource = params[1]; + this.minutes = this.normalizeMinutes(params[2]); + this.halfSecondSteps = this.normalizeHalfSecondSteps(params[3]); + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + throw new WiredSaveException(String.format("Item %s not found", itemId)); + } + + if (!(item instanceof InteractionGameUpCounter)) { + throw new WiredSaveException("wiredfurni.error.require_counter_furni"); + } + + newItems.add(item); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int normalizeOperator(int value) { + if (value < OPERATOR_INCREASE || value > OPERATOR_SET) { + return OPERATOR_SET; + } + + return value; + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + static class JsonData { + int delay; + List itemIds; + int operator; + int furniSource; + int minutes; + int halfSecondSteps; + + public JsonData(int delay, List itemIds, int operator, int furniSource, int minutes, int halfSecondSteps) { + this.delay = delay; + this.itemIds = itemIds; + this.operator = operator; + this.furniSource = furniSource; + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java new file mode 100644 index 00000000..b4f55831 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectControlClock.java @@ -0,0 +1,244 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.effects; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredEffectControlClock extends InteractionWiredEffect { + private static final int ACTION_START = 0; + private static final int ACTION_STOP = 1; + private static final int ACTION_RESET = 2; + private static final int ACTION_PAUSE = 3; + private static final int ACTION_RESUME = 4; + + public static final WiredEffectType type = WiredEffectType.CONTROL_CLOCK; + + private final List items = new ArrayList<>(); + private int action = ACTION_START; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectControlClock(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectControlClock(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + + if (room == null) { + return; + } + + List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.items.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + } + + for (HabboItem item : effectiveItems) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + InteractionGameUpCounter counter = (InteractionGameUpCounter) item; + + switch (this.action) { + case ACTION_START: + counter.restartFromZero(room); + break; + case ACTION_STOP: + counter.stopCounter(room); + break; + case ACTION_RESET: + counter.resetCounter(room); + break; + case ACTION_PAUSE: + counter.pauseCounter(room); + break; + case ACTION_RESUME: + counter.resumeCounter(room); + break; + } + } + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.action, + this.furniSource + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + String wiredData = set.getString("wired_data"); + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.setDelay(data.delay); + this.action = this.normalizeAction(data.action); + this.furniSource = data.furniSource; + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + return; + } + + this.action = ACTION_START; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public void onPickUp() { + this.items.clear(); + this.action = ACTION_START; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List itemsSnapshot = new ArrayList<>(this.items); + itemsSnapshot.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + + this.items.clear(); + this.items.addAll(itemsSnapshot); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(itemsSnapshot.size()); + for (HabboItem item : itemsSnapshot) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.action); + message.appendInt(this.furniSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + int[] params = settings.getIntParams(); + + if (params.length < 2) { + throw new WiredSaveException("Invalid data"); + } + + this.action = this.normalizeAction(params[0]); + this.furniSource = params[1]; + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + throw new WiredSaveException(String.format("Item %s not found", itemId)); + } + + if (!(item instanceof InteractionGameUpCounter)) { + throw new WiredSaveException("wiredfurni.error.require_counter_furni"); + } + + newItems.add(item); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private int normalizeAction(int value) { + if (value < ACTION_START || value > ACTION_RESUME) { + return ACTION_START; + } + + return value; + } + + static class JsonData { + int delay; + List itemIds; + int action; + int furniSource; + + public JsonData(int delay, List itemIds, int action, int furniSource) { + this.delay = delay; + this.itemIds = itemIds; + this.action = action; + this.furniSource = furniSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java new file mode 100644 index 00000000..2e6c9d78 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java @@ -0,0 +1,229 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +public class WiredTriggerClockCounter extends InteractionWiredTrigger { + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + public static final WiredTriggerType type = WiredTriggerType.CLOCK_COUNTER; + + private final THashSet items; + private int minutes = 0; + private int halfSecondSteps = 0; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredTriggerClockCounter(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredTriggerClockCounter(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + this.items = new THashSet<>(); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + HabboItem sourceItem = event.getSourceItem().orElse(null); + + if (!(sourceItem instanceof InteractionGameUpCounter)) { + return false; + } + + if (((InteractionGameUpCounter) sourceItem).getCurrentTimeInMs() != this.getTargetTimeInMs()) { + return false; + } + + return (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) || this.items.contains(sourceItem); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public WiredTriggerType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(this.furniSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.minutes = (params.length > 0) ? this.normalizeMinutes(params[0]) : 0; + this.halfSecondSteps = (params.length > 1) ? this.normalizeHalfSecondSteps(params[1]) : 0; + this.furniSource = (params.length > 2) ? this.normalizeFurniSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER; + + this.items.clear(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.minutes, + this.halfSecondSteps, + this.furniSource, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (!wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.minutes = this.normalizeMinutes(data.minutes); + this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps); + this.furniSource = this.normalizeFurniSource(data.furniSource); + + if (data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item instanceof InteractionGameUpCounter) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + private void refresh(Room room) { + THashSet remove = new THashSet<>(); + + for (HabboItem item : this.items) { + HabboItem roomItem = room.getHabboItem(item.getId()); + if (!(roomItem instanceof InteractionGameUpCounter)) { + remove.add(item); + } + } + + for (HabboItem item : remove) { + this.items.remove(item); + } + } + + private int getTargetTimeInMs() { + return (this.minutes * 60_000) + (this.halfSecondSteps * 500); + } + + private int normalizeMinutes(int value) { + return Math.max(0, Math.min(MAX_MINUTES, value)); + } + + private int normalizeHalfSecondSteps(int value) { + return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value)); + } + + private int normalizeFurniSource(int value) { + return (value == WiredSourceUtil.SOURCE_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER; + } + + static class JsonData { + int minutes; + int halfSecondSteps; + int furniSource; + List itemIds; + + public JsonData(int minutes, int halfSecondSteps, int furniSource, List itemIds) { + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + this.furniSource = furniSource; + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java index 323abce6..7b27a858 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java @@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.items.FurnitureType; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.*; import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTimer; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.pets.PetManager; @@ -898,7 +899,11 @@ public class Room implements Comparable, ISerialize, Runnable { } for (InteractionGameTimer timer : this.getRoomSpecialTypes().getGameTimers().values()) { - timer.setRunning(false); + if (timer instanceof InteractionGameUpCounter) { + ((InteractionGameUpCounter) timer).resetOnRoomUnload(this); + } else { + timer.setRunning(false); + } } for (Game game : this.games) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java index ee85190c..7f37a1ae 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboItem.java @@ -294,7 +294,10 @@ public abstract class HabboItem implements Runnable, IEventTriggers { } } - if ((this.getBaseItem().getStateCount() > 1 && !(this instanceof InteractionDice)) || Arrays.asList(HabboItem.TOGGLING_INTERACTIONS).contains(this.getClass()) || (objects != null && objects.length == 1 && objects[0].equals("TOGGLE_OVERRIDE"))) { + boolean isTogglingInteraction = Arrays.stream(HabboItem.TOGGLING_INTERACTIONS) + .anyMatch(type -> type.isAssignableFrom(this.getClass())); + + if ((this.getBaseItem().getStateCount() > 1 && !(this instanceof InteractionDice)) || isTogglingInteraction || (objects != null && objects.length == 1 && objects[0].equals("TOGGLE_OVERRIDE"))) { WiredManager.triggerFurniStateChanged(room, client.getHabbo().getRoomUnit(), this); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java index e620b4e0..b2f435d0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java @@ -25,7 +25,8 @@ public enum WiredConditionType { NOT_ACTOR_WEARS_EFFECT(23), DATE_RANGE(24), ACTOR_HAS_HANDITEM(25), - MOVEMENT_VALIDATION(26); // i dont know what type it is but its needed + MOVEMENT_VALIDATION(26), // i dont know what type it is but its needed + COUNTER_TIME_MATCHES(27); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index 8ece3410..332cbe7f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -39,7 +39,9 @@ public enum WiredEffectType { USER_TO_FURNI(37), FURNI_TO_FURNI(38), SET_ALTITUDE(39), - RELATIVE_MOVE(40); + RELATIVE_MOVE(40), + CONTROL_CLOCK(41), + ADJUST_CLOCK(42); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java index 2672758b..b9d17bf6 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java @@ -21,6 +21,7 @@ public enum WiredTriggerType { CLICKS_TILE(19), CLICKS_USER(20), USER_PERFORMS_ACTION(21), + CLOCK_COUNTER(22), SAY_COMMAND(0), IDLES(11), UNIDLES(11), diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java index 369d7fba..fb194c2c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java @@ -63,6 +63,9 @@ public final class WiredEvent { /** Timer fires periodically/repeatedly */ TIMER_REPEAT(WiredTriggerType.PERIODICALLY), + + /** Up-counter reaches a configured elapsed time */ + CLOCK_COUNTER_REACHED(WiredTriggerType.CLOCK_COUNTER), /** Long timer repeat */ TIMER_REPEAT_LONG(WiredTriggerType.PERIODICALLY_LONG), diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java index ea712884..cfe08f6b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -355,6 +355,15 @@ public final class WiredManager { return handleEvent(event); } + public static boolean triggerClockCounter(Room room, HabboItem counterItem) { + if (!isEnabled() || room == null || counterItem == null) { + return false; + } + + WiredEvent event = WiredEvents.clockCounter(room, counterItem); + return handleEvent(event); + } + /** * Trigger a long periodic timer. */ diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java index e93072b2..165f0c1d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java @@ -218,6 +218,12 @@ public final class WiredEvents { .build(); } + public static WiredEvent clockCounter(Room room, HabboItem counterItem) { + return WiredEvent.builder(WiredEvent.Type.CLOCK_COUNTER_REACHED, room) + .sourceItem(counterItem) + .build(); + } + /** * Create an event for a long periodic timer. * @param room the room diff --git a/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java b/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java new file mode 100644 index 00000000..19e45f5c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/threading/runnables/games/GameUpCounter.java @@ -0,0 +1,46 @@ +package com.eu.habbo.threading.runnables.games; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.wired.core.WiredManager; + +public class GameUpCounter implements Runnable { + private final InteractionGameUpCounter timer; + + public GameUpCounter(InteractionGameUpCounter timer) { + this.timer = timer; + } + + @Override + public void run() { + if (timer.getRoomId() == 0) { + timer.setRunning(false); + timer.setThreadActive(false); + return; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(timer.getRoomId()); + + if (room == null || !timer.isRunning() || timer.isPaused()) { + timer.setThreadActive(false); + return; + } + + int tickDelayMs = (int) timer.getNextTickDelayMs(); + timer.advanceCounterInMs(tickDelayMs); + WiredManager.triggerClockCounter(room, timer); + + if (timer.getCurrentTimeInMs() < timer.getMaximumTimeInMs()) { + timer.setThreadActive(true); + Emulator.getThreading().run(this, timer.getNextTickDelayMs()); + } else { + timer.setThreadActive(false); + timer.setCurrentTimeInMs(timer.getMaximumTimeInMs()); + timer.endGame(room); + WiredManager.triggerGameEnds(room); + } + + room.updateItem(timer); + } +}