diff --git a/Database Updates/21022026_user_prefixes.sql b/Database Updates/21022026_user_prefixes.sql new file mode 100644 index 00000000..e0ee239f --- /dev/null +++ b/Database Updates/21022026_user_prefixes.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `user_prefixes` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `user_id` INT(11) NOT NULL, + `text` VARCHAR(50) NOT NULL, + `color` VARCHAR(255) NOT NULL DEFAULT '#FFFFFF', + `icon` VARCHAR(50) NOT NULL DEFAULT '', + `effect` VARCHAR(50) NOT NULL DEFAULT '', + `active` TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + INDEX `idx_user_id` (`user_id`), + INDEX `idx_user_active` (`user_id`, `active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/Emulator/.idea/misc.xml b/Emulator/.idea/misc.xml index 5ddb3b31..6568344f 100644 --- a/Emulator/.idea/misc.xml +++ b/Emulator/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/Emulator/plugins/NitroWebsockets-3.1.jar b/Emulator/NitroWebsockets-3.1.jar similarity index 100% rename from Emulator/plugins/NitroWebsockets-3.1.jar rename to Emulator/NitroWebsockets-3.1.jar diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 11f29086..edca543c 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -56,6 +56,8 @@ public final class Emulator { public static String build = ""; public static long buildTimestamp = -1L; + + public static boolean isReady = false; public static boolean isShuttingDown = false; public static boolean stopped = false; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java index a76f01dd..114aa86d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/GamePlayer.java @@ -36,8 +36,16 @@ public class GamePlayer { if (this.score < 0) this.score = 0; - if(isWired && this.score > 0) { + if(isWired) { this.wiredScore += amount; + + if (this.wiredScore < 0) { + this.wiredScore = 0; + } + + if (this.wiredScore > this.score) { + this.wiredScore = this.score; + } } WiredManager.triggerScoreAchieved(this.habbo.getHabboInfo().getCurrentRoom(), this.habbo.getRoomUnit(), this.score, amount); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java index 80fe874d..631345e2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/games/wired/WiredGame.java @@ -6,6 +6,10 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.Habbo; public class WiredGame extends Game { + public static final int RED_EFFECT_ID = 223; + public static final int BLUE_EFFECT_ID = 224; + public static final int YELLOW_EFFECT_ID = 225; + public static final int GREEN_EFFECT_ID = 226; public GameState state = GameState.RUNNING; public WiredGame(Room room) { @@ -28,7 +32,7 @@ public class WiredGame extends Game { @Override public boolean addHabbo(Habbo habbo, GameTeamColors teamColor) { - this.room.giveEffect(habbo, FreezeGame.effectId + teamColor.type, -1); + this.room.giveEffect(habbo, this.getEffectId(teamColor), -1); return super.addHabbo(habbo, teamColor); } @@ -47,4 +51,19 @@ public class WiredGame extends Game { public GameState getState() { return GameState.RUNNING; } -} \ No newline at end of file + + private int getEffectId(GameTeamColors teamColor) { + switch (teamColor) { + case RED: + return RED_EFFECT_ID; + case BLUE: + return BLUE_EFFECT_ID; + case YELLOW: + return YELLOW_EFFECT_ID; + case GREEN: + return GREEN_EFFECT_ID; + default: + return FreezeGame.effectId + teamColor.type; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java index ac2eab14..d4ac19c7 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; @@ -49,6 +50,12 @@ import com.eu.habbo.habbohotel.items.interactions.totems.InteractionTotemPlanet; import com.eu.habbo.habbohotel.items.interactions.wired.conditions.*; import com.eu.habbo.habbohotel.items.interactions.wired.effects.*; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimationTime; +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.WiredExtraMoveCarryUsers; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhysics; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen; @@ -202,6 +209,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)); @@ -212,6 +220,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)); @@ -267,11 +276,26 @@ 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_act_move_rotate_user", WiredEffectMoveRotateUser.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_area", WiredEffectFurniArea.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_neighborhood", WiredEffectFurniNeighborhood.class)); this.interactionsList.add(new ItemInteraction("wf_slc_furni_bytype", WiredEffectFurniByType.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_altitude", WiredEffectFurniAltitude.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_onfurni", WiredEffectFurniOnFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_picks", WiredEffectFurniPicks.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_furni_signal", WiredEffectFurniSignal.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_area", WiredEffectUsersArea.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_neighborhood", WiredEffectUsersNeighborhood.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_signal", WiredEffectUsersSignal.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_bytype", WiredEffectUsersByType.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_team", WiredEffectUsersTeam.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_byaction", WiredEffectUsersByAction.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_byname", WiredEffectUsersByName.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_handitem", WiredEffectUsersHandItem.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_onfurni", WiredEffectUsersOnFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_slc_users_group", WiredEffectUsersGroup.class)); this.interactionsList.add(new ItemInteraction("wf_act_send_signal", WiredEffectSendSignal.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_furni_on", WiredConditionFurniHaveFurni.class)); @@ -300,9 +324,11 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_cnd_not_has_handitem", WiredConditionNotHabboHasHandItem.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_date_rng_active", WiredConditionDateRangeActive.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_valid_moves", WiredConditionMovementValidation.class)); - // this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_match_time", WiredConditionMatchTime.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_match_date", WiredConditionMatchDate.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_actor_dir", WiredConditionActorDir.class)); + this.interactionsList.add(new ItemInteraction("wf_cnd_slc_quantity", WiredConditionSelectionQuantity.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_user_performs_action", WiredConditionUserPerformsAction.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_not_user_performs_action", WiredConditionNotUserPerformsAction.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_altitude", WiredConditionHasAltitude.class)); @@ -316,6 +342,13 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_xtra_unseen", WiredExtraUnseen.class)); this.interactionsList.add(new ItemInteraction("wf_blob", WiredBlob.class)); this.interactionsList.add(new ItemInteraction("wf_xtra_or_eval", WiredExtraOrEval.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_filter_furni", WiredExtraFilterFurni.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_filter_user", WiredExtraFilterUser.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_filter_users", WiredExtraFilterUser.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_carry_users", WiredExtraMoveCarryUsers.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_no_animation", WiredExtraMoveNoAnimation.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_anim_time", WiredExtraAnimationTime.class)); + this.interactionsList.add(new ItemInteraction("wf_xtra_mov_physics", WiredExtraMovePhysics.class)); this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java index d303291d..84c7453a 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredEffect.java @@ -16,6 +16,8 @@ import com.eu.habbo.messages.outgoing.wired.WiredEffectDataComposer; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.function.Predicate; /** @@ -198,4 +200,56 @@ public abstract class InteractionWiredEffect extends InteractionWired implements || additionalRemoveCondition.test(item)); return sizeBefore - items.size(); } + + protected LinkedHashSet applySelectorModifiers(Iterable matchedTargets, + Iterable availableTargets, + Iterable existingTargets, + boolean filterExisting, + boolean invert) { + LinkedHashSet matched = toLinkedHashSet(matchedTargets); + LinkedHashSet base = filterExisting + ? toLinkedHashSet(existingTargets) + : toLinkedHashSet(availableTargets); + + if (invert) { + base.removeAll(matched); + return base; + } + + if (filterExisting) { + matched.retainAll(base); + } + + return matched; + } + + protected LinkedHashSet getSelectableFloorItems(Room room) { + LinkedHashSet result = new LinkedHashSet<>(); + if (room == null) { + return result; + } + + room.getFloorItems().forEach(item -> { + if (item != null && !(item instanceof InteractionWired)) { + result.add(item); + } + }); + + return result; + } + + protected LinkedHashSet toLinkedHashSet(Iterable values) { + LinkedHashSet result = new LinkedHashSet<>(); + if (values == null) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + return result; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java index b946d7d9..727684f8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredExtra.java @@ -2,8 +2,11 @@ package com.eu.habbo.habbohotel.items.interactions; import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; +import com.eu.habbo.messages.outgoing.wired.WiredExtraDataComposer; import java.sql.ResultSet; import java.sql.SQLException; @@ -21,6 +24,9 @@ public abstract class InteractionWiredExtra extends InteractionWired { public void onClick(GameClient client, Room room, Object[] objects) throws Exception { if (client != null) { if (room.hasRights(client.getHabbo())) { + if (this.hasConfiguration()) { + client.sendResponse(new WiredExtraDataComposer(this, room)); + } this.activateBox(room); } } @@ -35,4 +41,12 @@ public abstract class InteractionWiredExtra extends InteractionWired { public boolean isWalkable() { return true; } -} \ No newline at end of file + + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + return true; + } + + public boolean hasConfiguration() { + return false; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java index e6410040..16e5b4c8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredTrigger.java @@ -56,6 +56,10 @@ public abstract class InteractionWiredTrigger extends InteractionWired implement public abstract boolean saveData(WiredSettings settings); + public boolean saveData(WiredSettings settings, GameClient gameClient) { + return this.saveData(settings); + } + protected int getDelay() { return this.delay; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/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/WiredConditionActorDir.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java new file mode 100644 index 00000000..8c0e79af --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java @@ -0,0 +1,200 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionActorDir extends InteractionWiredCondition { + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + private static final int ALL_DIRECTIONS_MASK = createDirectionMask(); + + public static final WiredConditionType type = WiredConditionType.ACTOR_DIR; + + private int directionMask = 0; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; + + public WiredConditionActorDir(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionActorDir(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(5); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.directionMask); + message.appendInt(this.userSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.directionMask = (params.length > 0) ? this.normalizeDirectionMask(params[0]) : 0; + this.userSource = (params.length > 1) ? this.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + if (this.directionMask == 0) { + return false; + } + + List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); + if (targets.isEmpty()) { + return false; + } + + if (this.quantifier == QUANTIFIER_ANY) { + return targets.stream().anyMatch(this::matchesDirection); + } + + return targets.stream().allMatch(this::matchesDirection); + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.directionMask, + this.userSource, + this.quantifier + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data == null) { + return; + } + + this.directionMask = this.normalizeDirectionMask(data.directionMask); + this.userSource = this.normalizeUserSource(data.userSource); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; + } + + String[] parts = wiredData.split("\t"); + + try { + if (parts.length > 0) { + this.directionMask = this.normalizeDirectionMask(Integer.parseInt(parts[0])); + } + if (parts.length > 1) { + this.userSource = this.normalizeUserSource(Integer.parseInt(parts[1])); + } + if (parts.length > 2) { + this.quantifier = this.normalizeQuantifier(Integer.parseInt(parts[2])); + } + } catch (NumberFormatException ignored) { + this.onPickUp(); + } + } + + @Override + public void onPickUp() { + this.directionMask = 0; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + } + + private boolean matchesDirection(RoomUnit roomUnit) { + if (roomUnit == null || roomUnit.getBodyRotation() == null) { + return false; + } + + int direction = roomUnit.getBodyRotation().getValue(); + + return (this.directionMask & (1 << direction)) != 0; + } + + private int normalizeDirectionMask(int value) { + return value & ALL_DIRECTIONS_MASK; + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + private static int createDirectionMask() { + int mask = 0; + + for (int direction = 0; direction < 8; direction++) { + mask |= (1 << direction); + } + + return mask; + } + + static class JsonData { + int directionMask; + int userSource; + int quantifier; + + public JsonData(int directionMask, int userSource, int quantifier) { + this.directionMask = directionMask; + this.userSource = userSource; + this.quantifier = quantifier; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java new file mode 100644 index 00000000..a28ea00b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionCounterTimeMatches.java @@ -0,0 +1,296 @@ +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 QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + private static final int MAX_MINUTES = 99; + private static final int MAX_HALF_SECOND_STEPS = 119; + + 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; + private int quantifier = QUANTIFIER_ALL; + + 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(); + + if (this.quantifier == QUANTIFIER_ANY) { + for (HabboItem item : targets) { + if (!(item instanceof InteractionGameUpCounter)) { + continue; + } + + if (this.matchesCounter((InteractionGameUpCounter) item, targetTimeInMs)) { + return true; + } + } + + return false; + } + + for (HabboItem item : targets) { + if (!(item instanceof InteractionGameUpCounter)) { + return false; + } + + if (!this.matchesCounter((InteractionGameUpCounter) item, targetTimeInMs)) { + return false; + } + } + + 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.quantifier, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + this.comparison = COMPARISON_EQUAL; + this.minutes = 0; + this.halfSecondSteps = 0; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) { + 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; + this.quantifier = this.normalizeQuantifier(data.quantifier); + + 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; + this.quantifier = QUANTIFIER_ALL; + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(5); + message.appendInt(this.comparison); + message.appendInt(this.minutes); + message.appendInt(this.halfSecondSteps); + message.appendInt(this.furniSource); + message.appendInt(this.quantifier); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + 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.quantifier = (params.length > 4) ? this.normalizeQuantifier(params[4]) : QUANTIFIER_ALL; + + 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 boolean matchesCounter(InteractionGameUpCounter counter, int targetTimeInMs) { + int currentTimeInMs = counter.getCurrentTimeInMs(); + + switch (this.comparison) { + case COMPARISON_LESS: + return currentTimeInMs < targetTimeInMs; + case COMPARISON_GREATER: + return currentTimeInMs > targetTimeInMs; + default: + return currentTimeInMs == targetTimeInMs; + } + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + 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)); + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + static class JsonData { + int comparison; + int minutes; + int halfSecondSteps; + int furniSource; + int quantifier; + List itemIds; + + public JsonData(int comparison, int minutes, int halfSecondSteps, int furniSource, int quantifier, List itemIds) { + this.comparison = comparison; + this.minutes = minutes; + this.halfSecondSteps = halfSecondSteps; + this.furniSource = furniSource; + this.quantifier = quantifier; + this.itemIds = itemIds; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java index 1863c07c..a12c1874 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveHabbo.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { public static final WiredConditionType type = WiredConditionType.FURNI_HAVE_HABBO; protected THashSet items; + protected boolean all; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredConditionFurniHaveHabbo(ResultSet set, Item baseItem) throws SQLException { @@ -42,6 +43,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.all = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -62,15 +64,11 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { Collection bots = room.getCurrentBots().valueCollection(); Collection pets = room.getCurrentPets().valueCollection(); - return targets.stream().filter(item -> item != null).allMatch(item -> { - RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); - if (baseTile == null) return false; - - THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - return habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())); - }); + if (this.all) { + return targets.stream().filter(item -> item != null).allMatch(item -> this.hasAvatarOnItem(item, room, habbos, bots, pets)); + } + + return targets.stream().filter(item -> item != null).anyMatch(item -> this.hasAvatarOnItem(item, room, habbos, bots, pets)); } @Deprecated @@ -84,7 +82,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.furniSource, + this.all )); } @@ -96,6 +95,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.furniSource = data.furniSource; + this.all = data.all; for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -119,6 +119,7 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { } } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.all = false; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -144,7 +145,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.all ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -159,7 +161,8 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.all = (params.length > 0) && (params[0] == 1); + this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER); this.items.clear(); @@ -179,6 +182,18 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { return true; } + protected boolean hasAvatarOnItem(HabboItem item, Room room, Collection habbos, Collection bots, Collection pets) { + RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); + if (baseTile == null) return false; + + THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + return occupiedTiles != null && ( + habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) + ); + } + private void refresh() { THashSet items = new THashSet<>(); @@ -200,10 +215,12 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition { static class JsonData { List itemIds; int furniSource; + boolean all; - public JsonData(List itemIds, int furniSource) { + public JsonData(List itemIds, int furniSource, boolean all) { this.itemIds = itemIds; this.furniSource = furniSource; + this.all = all; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java index 72552354..0b91d791 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniTypeMatch.java @@ -8,22 +8,30 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { + protected static final int SOURCE_SECONDARY_SELECTED = 101; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.STUFF_IS; - private THashSet items = new THashSet<>(); - private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected THashSet items = new THashSet<>(); + protected THashSet secondaryItems = new THashSet<>(); + protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int compareFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionFurniTypeMatch(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,19 +44,90 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.secondaryItems.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.compareFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override public boolean evaluate(WiredContext ctx) { + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyMatches(ctx); + } + + return this.evaluateAllMatches(ctx); + } + + protected boolean evaluateAllMatches(WiredContext ctx) { + List matchTargets = this.resolveMatchTargets(ctx); + if (matchTargets.isEmpty()) { + return false; + } + + THashSet compareTypeIds = this.resolveCompareTypeIds(ctx); + if (compareTypeIds.isEmpty()) { + return false; + } + + for (HabboItem item : matchTargets) { + if (!this.matchesType(item, compareTypeIds)) { + return false; + } + } + + return true; + } + + protected boolean evaluateAnyMatches(WiredContext ctx) { + List matchTargets = this.resolveMatchTargets(ctx); + if (matchTargets.isEmpty()) { + return false; + } + + THashSet compareTypeIds = this.resolveCompareTypeIds(ctx); + if (compareTypeIds.isEmpty()) { + return false; + } + + for (HabboItem item : matchTargets) { + if (this.matchesType(item, compareTypeIds)) { + return true; + } + } + + return false; + } + + protected List resolveMatchTargets(WiredContext ctx) { + this.refresh(); + return this.resolveConfiguredItems(ctx, this.furniSource); + } + + protected THashSet resolveCompareTypeIds(WiredContext ctx) { this.refresh(); - if(items.isEmpty()) - return false; + THashSet compareTypeIds = new THashSet<>(); - List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); - if (targets.isEmpty()) return false; - return targets.stream().anyMatch(this.items::contains); + for (HabboItem item : this.resolveConfiguredItems(ctx, this.compareFurniSource)) { + if (item != null && item.getBaseItem() != null) { + compareTypeIds.add(item.getBaseItem().getId()); + } + } + + return compareTypeIds; + } + + protected boolean matchesType(HabboItem item, THashSet compareTypeIds) { + return item != null && item.getBaseItem() != null && compareTypeIds.contains(item.getBaseItem().getId()); + } + + protected List resolveConfiguredItems(WiredContext ctx, int sourceType) { + if (sourceType == SOURCE_SECONDARY_SELECTED) { + return new ArrayList<>(this.secondaryItems); + } + + return WiredSourceUtil.resolveItems(ctx, sourceType, this.items); } @Deprecated @@ -62,41 +141,53 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.secondaryItems.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.furniSource, + this.compareFurniSource, + this.quantifier )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { - this.items.clear(); + this.onPickUp(); + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.furniSource = data.furniSource; - - for(int id : data.itemIds) { - HabboItem item = room.getHabboItem(id); - - if (item != null) { - this.items.add(item); - } + if (data == null) { + return; } - } else { - String[] data = wiredData.split(";"); - for (String s : data) { - HabboItem item = room.getHabboItem(Integer.parseInt(s)); + List primaryIds = (data.primaryItemIds != null) ? data.primaryItemIds : new ArrayList<>(); + List compareIds = (data.secondaryItemIds != null) ? data.secondaryItemIds : ((data.itemIds != null) ? data.itemIds : new ArrayList<>()); - if (item != null) { - this.items.add(item); - } + this.furniSource = this.normalizeFurniSource((data.furniSource != null) ? data.furniSource : WiredSourceUtil.SOURCE_TRIGGER); + this.compareFurniSource = this.normalizeFurniSource((data.compareFurniSource != null) ? data.compareFurniSource : (compareIds.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED)); + this.quantifier = this.normalizeQuantifier((data.quantifier != null) ? data.quantifier : QUANTIFIER_ANY); + + this.loadItems(room, primaryIds, this.items); + this.loadItems(room, compareIds, this.secondaryItems); + return; + } + + String[] data = wiredData.split(";"); + List compareIds = new ArrayList<>(); + + for (String value : data) { + try { + compareIds.add(Integer.parseInt(value)); + } catch (NumberFormatException ignored) { } - this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; - } - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; } + + this.loadItems(room, compareIds, this.secondaryItems); + this.compareFurniSource = this.secondaryItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -112,14 +203,17 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); message.appendInt(this.items.size()); - for (HabboItem item : this.items) + for (HabboItem item : this.items) { message.appendInt(item.getId()); + } message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); - message.appendString(""); - message.appendInt(1); + message.appendString(this.serializeIds(this.secondaryItems)); + message.appendInt(3); message.appendInt(this.furniSource); + message.appendInt(this.compareFurniSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -132,16 +226,51 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + String stringParam = (settings.getStringParam() != null) ? settings.getStringParam().trim() : ""; + boolean legacyData = (params.length <= 1) && stringParam.isEmpty(); - this.items.clear(); + this.onPickUp(); + + if (legacyData) { + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; + } else { + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.compareFurniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } + + if (legacyData) { + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.secondaryItems.add(item); + } + } + + this.compareFurniSource = this.secondaryItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED; + return true; + } if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.items.add(item); + } + } + } - if (room != null) { - for (int i = 0; i < count; i++) { - this.items.add(room.getHabboItem(settings.getFurniIds()[i])); + if (this.compareFurniSource == SOURCE_SECONDARY_SELECTED) { + for (Integer itemId : this.parseIds(stringParam)) { + HabboItem item = room.getHabboItem(itemId); + if (item != null) { + this.secondaryItems.add(item); } } } @@ -149,31 +278,110 @@ public class WiredConditionFurniTypeMatch extends InteractionWiredCondition { return true; } - private void refresh() { - THashSet items = new THashSet<>(); + protected int getQuantifier() { + return this.quantifier; + } + + protected void refresh() { + this.refreshSelection(this.items); + this.refreshSelection(this.secondaryItems); + } + + private void refreshSelection(THashSet selection) { + THashSet remove = new THashSet<>(); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room == null) { - items.addAll(this.items); + remove.addAll(selection); } else { - for (HabboItem item : this.items) { - if (room.getHabboItem(item.getId()) == null) - items.add(item); + for (HabboItem item : selection) { + if (room.getHabboItem(item.getId()) == null) { + remove.add(item); + } } } - for (HabboItem item : items) { - this.items.remove(item); + for (HabboItem item : remove) { + selection.remove(item); } } + private void loadItems(Room room, List itemIds, THashSet target) { + if (itemIds == null) { + return; + } + + for (Integer id : itemIds) { + if (id == null) { + continue; + } + + HabboItem item = room.getHabboItem(id); + if (item != null) { + target.add(item); + } + } + } + + private String serializeIds(THashSet source) { + return source.stream() + .map(HabboItem::getId) + .filter(id -> id > 0) + .map(String::valueOf) + .collect(Collectors.joining(";")); + } + + private List parseIds(String value) { + List result = new ArrayList<>(); + if (value == null || value.isEmpty()) { + return result; + } + + for (String part : value.split("[;,\\t]")) { + if (part == null || part.trim().isEmpty()) { + continue; + } + + try { + result.add(Integer.parseInt(part.trim())); + } catch (NumberFormatException ignored) { + } + } + + return result; + } + + protected int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTED: + case SOURCE_SECONDARY_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { + List primaryItemIds; + List secondaryItemIds; List itemIds; - int furniSource; + Integer furniSource; + Integer compareFurniSource; + Integer quantifier; - public JsonData(List itemIds, int furniSource) { - this.itemIds = itemIds; + public JsonData(List primaryItemIds, List secondaryItemIds, int furniSource, int compareFurniSource, int quantifier) { + this.primaryItemIds = primaryItemIds; + this.secondaryItemIds = secondaryItemIds; this.furniSource = furniSource; + this.compareFurniSource = compareFurniSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java index aa9631b5..d442add5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionGroupMember.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredConditionType; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; @@ -16,8 +17,16 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionGroupMember extends InteractionWiredCondition { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_IN_GROUP; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private int quantifier = QUANTIFIER_ALL; public WiredConditionGroupMember(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,19 +39,32 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { Room room = ctx.room(); - if (room.getGuildId() == 0) + int targetGroupId = this.resolveTargetGroupId(room); + if (targetGroupId == 0) return false; List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ANY) { + for (RoomUnit roomUnit : targets) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo != null && habbo.getHabboStats().hasGuild(targetGroupId)) { + return true; + } + } + + return false; + } + for (RoomUnit roomUnit : targets) { Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboStats().hasGuild(room.getGuildId())) { - return true; + if (habbo == null || !habbo.getHabboStats().hasGuild(targetGroupId)) { + return false; } } - return false; + + return true; } @Deprecated @@ -53,26 +75,45 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public String getWiredData() { - return String.valueOf(this.userSource); + return WiredManager.getGson().toJson(new JsonData( + this.userSource, + this.groupType, + this.selectedGroupId, + this.quantifier + )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + String wiredData = set.getString("wired_data"); - if (wiredData != null && !wiredData.isEmpty()) { - try { - this.userSource = Integer.parseInt(wiredData); - } catch (NumberFormatException ignored) { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + try { + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.userSource = this.normalizeUserSource(data.userSource); + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = this.normalizeSelectedGroupId(data.selectedGroupId); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; } - } else { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = this.normalizeUserSource(Integer.parseInt(wiredData)); + } catch (Exception ignored) { + this.resetSettings(); } } @Override public void onPickUp() { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.resetSettings(); } @Override @@ -88,8 +129,11 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(4); message.appendInt(this.userSource); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -99,7 +143,70 @@ public class WiredConditionGroupMember extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 0) ? this.normalizeUserSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = (params.length > 1) ? this.normalizeGroupType(params[1]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 2) ? this.normalizeSelectedGroupId(params[2]) : 0; + this.quantifier = (params.length > 3) ? this.normalizeQuantifier(params[3]) : QUANTIFIER_ALL; return true; } + + private void resetSettings() { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.quantifier = QUANTIFIER_ALL; + } + + private int resolveTargetGroupId(Room room) { + if (room == null) { + return 0; + } + + if (this.groupType == GROUP_SELECTED) { + return this.selectedGroupId; + } + + return room.getGuildId(); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + private int normalizeSelectedGroupId(int value) { + return Math.max(0, value); + } + + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + static class JsonData { + int userSource; + int groupType; + int selectedGroupId; + int quantifier; + + public JsonData(int userSource, int groupType, int selectedGroupId, int quantifier) { + this.userSource = userSource; + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.quantifier = quantifier; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java index a184beaf..2075513d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasEffect.java @@ -16,10 +16,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionHabboHasEffect extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_EFFECT; protected int effectId = 0; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ANY; public WiredConditionHabboHasEffect(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -33,14 +37,38 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + + if (this.quantifier == QUANTIFIER_ALL) { + return this.matchesAllTargets(targets); + } + + return this.matchesAnyTarget(targets); + } + + protected boolean matchesAllTargets(List targets) { for (RoomUnit roomUnit : targets) { - if (roomUnit != null && roomUnit.getEffectId() == this.effectId) { + if (!this.matchesEffect(roomUnit)) { + return false; + } + } + + return true; + } + + protected boolean matchesAnyTarget(List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesEffect(roomUnit)) { return true; } } + return false; } + protected boolean matchesEffect(RoomUnit roomUnit) { + return roomUnit != null && roomUnit.getEffectId() == this.effectId; + } + @Deprecated @Override public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { @@ -51,7 +79,8 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.effectId, - this.userSource + this.userSource, + this.quantifier )); } @@ -63,9 +92,11 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.effectId = data.effectId; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { this.effectId = Integer.parseInt(wiredData); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } @@ -73,6 +104,7 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { public void onPickUp() { this.effectId = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -88,9 +120,10 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.effectId); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -100,20 +133,35 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.effectId = settings.getIntParams()[0]; int[] params = settings.getIntParams(); + this.effectId = params[0]; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ANY) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { int effectId; int userSource; + Integer quantifier; - public JsonData(int effectId, int userSource) { + public JsonData(int effectId, int userSource, int quantifier) { this.effectId = effectId; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java index 64aade52..d949e4bd 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboHasHandItem.java @@ -19,11 +19,14 @@ import java.util.List; public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { private static final Logger LOGGER = LoggerFactory.getLogger(WiredConditionHabboHasHandItem.class); + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; public static final WiredConditionType type = WiredConditionType.ACTOR_HAS_HANDITEM; private int handItem; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; public WiredConditionHabboHasHandItem(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -46,9 +49,10 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.handItem); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -58,9 +62,10 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.handItem = settings.getIntParams()[0]; + this.handItem = this.normalizeHandItem(settings.getIntParams()[0]); int[] params = settings.getIntParams(); this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; return true; } @@ -69,12 +74,12 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit != null && roomUnit.getHandItem() == this.handItem) { - return true; - } + + if (this.quantifier == QUANTIFIER_ANY) { + return this.matchesAnyTarget(targets); } - return false; + + return this.matchesAllTargets(targets); } @Deprecated @@ -87,7 +92,8 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.handItem, - this.userSource + this.userSource, + this.quantifier )); } @@ -98,11 +104,13 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.handItem = data.handItemId; + this.handItem = this.normalizeHandItem(data.handItemId); this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); } else { - this.handItem = Integer.parseInt(wiredData); + this.handItem = this.normalizeHandItem(Integer.parseInt(wiredData)); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } } catch (Exception e) { LOGGER.error("Caught exception", e); @@ -113,6 +121,7 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { public void onPickUp() { this.handItem = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } protected int getHandItem() { @@ -123,13 +132,47 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition { return this.userSource; } + protected int getQuantifier() { + return this.quantifier; + } + + protected boolean matchesAnyTarget(List targets) { + for (RoomUnit roomUnit : targets) { + if (roomUnit != null && roomUnit.getHandItem() == this.handItem) { + return true; + } + } + + return false; + } + + protected boolean matchesAllTargets(List targets) { + for (RoomUnit roomUnit : targets) { + if (roomUnit == null || roomUnit.getHandItem() != this.handItem) { + return false; + } + } + + return true; + } + + protected int normalizeHandItem(int value) { + return Math.max(0, value); + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { int handItemId; int userSource; + int quantifier; - public JsonData(int handItemId, int userSource) { + public JsonData(int handItemId, int userSource, int quantifier) { this.handItemId = handItemId; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java index 844ef89c..83c85a8f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHabboWearsBadge.java @@ -18,10 +18,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_BADGE; protected String badge = ""; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ANY; public WiredConditionHabboWearsBadge(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,18 +41,47 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ALL) { + return this.matchesAllTargets(room, targets); + } + + return this.matchesAnyTarget(room, targets); + } + + protected boolean matchesAllTargets(Room room, List targets) { for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null) { - synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { - for (HabboBadge badge : habbo.getInventory().getBadgesComponent().getWearingBadges()) { - if (badge.getCode().equalsIgnoreCase(this.badge)) { - return true; - } - } + if (!this.matchesBadge(room, roomUnit)) { + return false; + } + } + + return true; + } + + protected boolean matchesAnyTarget(Room room, List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesBadge(room, roomUnit)) { + return true; + } + } + + return false; + } + + protected boolean matchesBadge(Room room, RoomUnit roomUnit) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null) { + return false; + } + + synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { + for (HabboBadge badge : habbo.getInventory().getBadgesComponent().getWearingBadges()) { + if (badge.getCode().equalsIgnoreCase(this.badge)) { + return true; } } } + return false; } @@ -62,7 +95,8 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.badge, - this.userSource + this.userSource, + this.quantifier )); } @@ -74,9 +108,11 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.badge = data.badge; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { this.badge = wiredData; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } @@ -84,6 +120,7 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { public void onPickUp() { this.badge = ""; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } @Override @@ -99,8 +136,9 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.badge); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -112,17 +150,32 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition { this.badge = settings.getStringParam(); int[] params = settings.getIntParams(); this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 1) ? this.normalizeQuantifier(params[1], QUANTIFIER_ANY) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { String badge; int userSource; + Integer quantifier; - public JsonData(String badge, int userSource) { + public JsonData(String badge, int userSource, int quantifier) { this.badge = badge; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java index 7966d563..ddc0477e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchStatePosition.java @@ -20,8 +20,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.math.BigDecimal; public class WiredConditionMatchStatePosition extends InteractionWiredCondition implements InteractionWiredMatchFurniSettings { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.MATCH_SSHOT; private THashSet settings; @@ -29,7 +33,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition private boolean state; private boolean position; private boolean direction; + private boolean altitude; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int quantifier = QUANTIFIER_ALL; public WiredConditionMatchStatePosition(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -60,11 +66,13 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(6); message.appendInt(this.state ? 1 : 0); message.appendInt(this.direction ? 1 : 0); message.appendInt(this.position ? 1 : 0); + message.appendInt(this.altitude ? 1 : 0); message.appendInt(this.furniSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -78,7 +86,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state = params[0] == 1; this.direction = params[1] == 1; this.position = params[2] == 1; - this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER; + this.altitude = (params.length > 3) && (params[3] == 1); + this.furniSource = (params.length > 4) ? params[4] : ((params.length > 3 && params[3] > 1) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER); + this.quantifier = (params.length > 5) ? this.normalizeQuantifier(params[5]) : QUANTIFIER_ALL; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -95,7 +105,7 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition HabboItem item = room.getHabboItem(itemId); if (item != null) - this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY())); + this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ())); } return true; @@ -103,12 +113,21 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition @Override public boolean evaluate(WiredContext ctx) { - Room room = ctx.room(); this.refresh(); if (this.settings.isEmpty()) return true; + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyTargetMatches(ctx); + } + + return this.evaluateAllTargetsMatch(ctx); + } + + protected boolean evaluateAllTargetsMatch(WiredContext ctx) { + Room room = ctx.room(); + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); if (targets.isEmpty()) return false; @@ -139,6 +158,41 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition return true; } + protected boolean evaluateAnyTargetMatches(WiredContext ctx) { + Room room = ctx.room(); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null); + if (targets.isEmpty()) return false; + + for (HabboItem item : targets) { + if (item == null) continue; + + WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); + if (setting != null && this.matchesSetting(item, setting)) { + return true; + } + } + + return false; + } + + for (WiredMatchFurniSetting setting : this.settings) { + HabboItem item = room.getHabboItem(setting.item_id); + if (item == null) continue; + + if (this.matchesSetting(item, setting)) { + return true; + } + } + + return false; + } + + protected int getQuantifier() { + return this.quantifier; + } + private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) { WiredMatchFurniSetting fallback = null; @@ -166,6 +220,9 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition if (this.position && !(setting.x == item.getX() && setting.y == item.getY())) return false; + if (this.altitude && BigDecimal.valueOf(item.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0) + return false; + return !this.direction || setting.rotation == item.getRotation(); } @@ -181,8 +238,10 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state, this.position, this.direction, + this.altitude, new ArrayList<>(this.settings), - this.furniSource + this.furniSource, + this.quantifier )); } @@ -195,8 +254,12 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.state = data.state; this.position = data.position; this.direction = data.direction; - this.settings.addAll(data.settings); + this.altitude = data.altitude; + if (data.settings != null) { + this.settings.addAll(data.settings); + } this.furniSource = data.furniSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); } else { String[] data = wiredData.split(":"); @@ -207,14 +270,18 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition for (int i = 0; i < itemCount; i++) { String[] stuff = items[i].split("-"); - if (stuff.length >= 5) + if (stuff.length >= 6) + this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]), Double.parseDouble(stuff[5]))); + else if (stuff.length >= 5) this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]))); } this.state = data[2].equals("1"); this.direction = data[3].equals("1"); this.position = data[4].equals("1"); + this.altitude = false; this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.quantifier = QUANTIFIER_ALL; } } @@ -224,10 +291,16 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition this.direction = false; this.position = false; this.state = false; + this.altitude = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } - private void refresh() { + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + protected void refresh() { Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room != null) { @@ -266,19 +339,28 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition return this.position; } + @Override + public boolean shouldMatchAltitude() { + return this.altitude; + } + static class JsonData { boolean state; boolean position; boolean direction; + boolean altitude; List settings; int furniSource; + int quantifier; - public JsonData(boolean state, boolean position, boolean direction, List settings, int furniSource) { + public JsonData(boolean state, boolean position, boolean direction, boolean altitude, List settings, int furniSource, int quantifier) { this.state = state; this.position = position; this.direction = direction; + this.altitude = altitude; this.settings = settings; this.furniSource = furniSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java index dff23b8a..3aec0268 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveHabbo.java @@ -28,6 +28,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { public static final WiredConditionType type = WiredConditionType.NOT_FURNI_HAVE_HABBO; protected THashSet items; + protected boolean all; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredConditionNotFurniHaveHabbo(ResultSet set, Item baseItem) throws SQLException { @@ -43,6 +44,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { @Override public void onPickUp() { this.items.clear(); + this.all = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -63,15 +65,11 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { Collection bots = room.getCurrentBots().valueCollection(); Collection pets = room.getCurrentPets().valueCollection(); - return targets.stream().filter(item -> item != null).noneMatch(item -> { - RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); - if (baseTile == null) return false; - - THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - return habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || - pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())); - }); + if (this.all) { + return targets.stream().filter(item -> item != null).allMatch(item -> !this.hasAvatarOnItem(item, room, habbos, bots, pets)); + } + + return targets.stream().filter(item -> item != null).anyMatch(item -> !this.hasAvatarOnItem(item, room, habbos, bots, pets)); } @Deprecated @@ -85,7 +83,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource + this.furniSource, + this.all )); } @@ -97,6 +96,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { if (wiredData.startsWith("{")) { WiredConditionFurniHaveHabbo.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionFurniHaveHabbo.JsonData.class); this.furniSource = data.furniSource; + this.all = data.all; for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -119,6 +119,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { } } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.all = false; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -144,7 +145,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.all ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -158,7 +160,8 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.all = (params.length > 0) && (params[0] == 1); + this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER); this.items.clear(); @@ -178,6 +181,18 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { return true; } + protected boolean hasAvatarOnItem(HabboItem item, Room room, Collection habbos, Collection bots, Collection pets) { + RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY()); + if (baseTile == null) return false; + + THashSet occupiedTiles = room.getLayout().getTilesAt(baseTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + return occupiedTiles != null && ( + habbos.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + bots.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) || + pets.stream().anyMatch(character -> character.getRoomUnit() != null && occupiedTiles.contains(character.getRoomUnit().getCurrentLocation())) + ); + } + private void refresh() { THashSet items = new THashSet<>(); @@ -199,10 +214,12 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition { static class JsonData { List itemIds; int furniSource; + boolean all; - public JsonData(List itemIds, int furniSource) { + public JsonData(List itemIds, int furniSource, boolean all) { this.itemIds = itemIds; this.furniSource = furniSource; + this.all = all; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java index b9aaf539..49dc6775 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniTypeMatch.java @@ -1,30 +1,15 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; -import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; -import com.eu.habbo.habbohotel.rooms.Room; -import com.eu.habbo.habbohotel.rooms.RoomUnit; -import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; -import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; -import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.List; -import java.util.stream.Collectors; -public class WiredConditionNotFurniTypeMatch extends InteractionWiredCondition { +public class WiredConditionNotFurniTypeMatch extends WiredConditionFurniTypeMatch { public static final WiredConditionType type = WiredConditionType.NOT_STUFF_IS; - private THashSet items = new THashSet<>(); - private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotFurniTypeMatch(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -35,145 +20,15 @@ public class WiredConditionNotFurniTypeMatch extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { - this.refresh(); - - if(items.isEmpty()) - return true; - - List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); - if (targets.isEmpty()) return true; - return targets.stream().noneMatch(this.items::contains); - } - - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - this.refresh(); - return WiredManager.getGson().toJson(new JsonData( - this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), - this.furniSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - this.items.clear(); - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - WiredConditionFurniTypeMatch.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionFurniTypeMatch.JsonData.class); - this.furniSource = data.furniSource; - - for(int id : data.itemIds) { - HabboItem item = room.getHabboItem(id); - - if (item != null) { - this.items.add(item); - } - } - } else { - String[] data = set.getString("wired_data").split(";"); - - for (String s : data) { - HabboItem item = room.getHabboItem(Integer.parseInt(s)); - - if (item != null) { - this.items.add(item); - } - } - this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.evaluateAllMatches(ctx); } - if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { - this.furniSource = WiredSourceUtil.SOURCE_SELECTED; - } - } - @Override - public void onPickUp() { - this.items.clear(); - this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.evaluateAnyMatches(ctx); } @Override public WiredConditionType getType() { return type; } - - @Override - public void serializeWiredData(ServerMessage message, Room room) { - this.refresh(); - - message.appendBoolean(false); - message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); - message.appendInt(this.items.size()); - - for (HabboItem item : this.items) - message.appendInt(item.getId()); - - message.appendInt(this.getBaseItem().getSpriteId()); - message.appendInt(this.getId()); - message.appendString(""); - message.appendInt(1); - message.appendInt(this.furniSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - int count = settings.getFurniIds().length; - if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false; - - int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - - this.items.clear(); - - if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); - - if (room != null) { - for (int i = 0; i < count; i++) { - this.items.add(room.getHabboItem(settings.getFurniIds()[i])); - } - } - } - - return true; - } - - private void refresh() { - THashSet items = new THashSet<>(); - - Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); - if (room == null) { - items.addAll(this.items); - } else { - for (HabboItem item : this.items) { - if (room.getHabboItem(item.getId()) == null) - items.add(item); - } - } - - for (HabboItem item : items) { - this.items.remove(item); - } - } - - static class JsonData { - List itemIds; - int furniSource; - - public JsonData(List itemIds, int furniSource) { - this.itemIds = itemIds; - this.furniSource = furniSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java index d061c0a8..b7c72361 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasEffect.java @@ -1,26 +1,18 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; -import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotHabboHasEffect extends InteractionWiredCondition { +public class WiredConditionNotHabboHasEffect extends WiredConditionHabboHasEffect { private static final WiredConditionType type = WiredConditionType.NOT_ACTOR_WEARS_EFFECT; - protected int effectId; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotHabboHasEffect(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -33,88 +25,16 @@ public class WiredConditionNotHabboHasEffect extends InteractionWiredCondition { public boolean evaluate(WiredContext ctx) { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit == null) return false; - if (roomUnit.getEffectId() == this.effectId) { - return false; - } + + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.matchesAllTargets(targets); } - return true; - } - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData( - this.effectId, - this.userSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.effectId = data.effectId; - this.userSource = data.userSource; - } else { - this.effectId = Integer.parseInt(wiredData); - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.effectId = 0; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.matchesAnyTarget(targets); } @Override public WiredConditionType getType() { return type; } - - @Override - public void serializeWiredData(ServerMessage message, Room room) { - message.appendBoolean(false); - message.appendInt(5); - message.appendInt(0); - message.appendInt(this.getBaseItem().getSpriteId()); - message.appendInt(this.getId()); - message.appendString(this.effectId + ""); - message.appendInt(2); - message.appendInt(this.effectId); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.effectId = settings.getIntParams()[0]; - int[] params = settings.getIntParams(); - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - int effectId; - int userSource; - - public JsonData(int effectId, int userSource) { - this.effectId = effectId; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java index 24421924..060b6089 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java @@ -26,13 +26,11 @@ public class WiredConditionNotHabboHasHandItem extends WiredConditionHabboHasHan List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource()); if (targets.isEmpty()) return false; - for (RoomUnit roomUnit : targets) { - if (roomUnit == null || roomUnit.getHandItem() == this.getHandItem()) { - return false; - } + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.matchesAnyTarget(targets); } - return true; + return !this.matchesAllTargets(targets); } @Override diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java index 5521d5d8..18a6c869 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboWearsBadge.java @@ -1,28 +1,19 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; -import com.eu.habbo.habbohotel.users.Habbo; -import com.eu.habbo.habbohotel.users.HabboBadge; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotHabboWearsBadge extends InteractionWiredCondition { +public class WiredConditionNotHabboWearsBadge extends WiredConditionHabboWearsBadge { public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_WEARS_BADGE; - protected String badge = ""; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotHabboWearsBadge(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -37,91 +28,15 @@ public class WiredConditionNotHabboWearsBadge extends InteractionWiredCondition List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return true; - for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null) { - synchronized (habbo.getInventory().getBadgesComponent().getWearingBadges()) { - for (HabboBadge b : habbo.getInventory().getBadgesComponent().getWearingBadges()) { - if (b.getCode().equalsIgnoreCase(this.badge)) - return false; - } - } - } + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.matchesAllTargets(room, targets); } - return true; - } - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData( - this.badge, - this.userSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.badge = data.badge; - this.userSource = data.userSource; - } else { - this.badge = wiredData; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.badge = ""; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.matchesAnyTarget(room, targets); } @Override public WiredConditionType getType() { return type; } - - @Override - public void serializeWiredData(ServerMessage message, Room room) { - message.appendBoolean(false); - message.appendInt(5); - message.appendInt(0); - message.appendInt(this.getBaseItem().getSpriteId()); - message.appendInt(this.getId()); - message.appendString(this.badge); - message.appendInt(1); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - this.badge = settings.getStringParam(); - int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - String badge; - int userSource; - - public JsonData(String badge, int userSource) { - this.badge = badge; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java index 237be63d..04bbe22c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInGroup.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredConditionType; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; @@ -16,8 +17,16 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionNotInGroup extends InteractionWiredCondition { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + private static final int QUANTIFIER_ALL = 0; + private static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_IN_GROUP; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private int quantifier = QUANTIFIER_ALL; public WiredConditionNotInGroup(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,18 +39,31 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { Room room = ctx.room(); - if (room.getGuildId() == 0) + int targetGroupId = this.resolveTargetGroupId(room); + if (targetGroupId == 0) return false; List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); - if (targets.isEmpty()) return true; + if (targets.isEmpty()) return false; + + if (this.quantifier == QUANTIFIER_ANY) { + for (RoomUnit roomUnit : targets) { + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null || !habbo.getHabboStats().hasGuild(targetGroupId)) { + return true; + } + } + + return false; + } for (RoomUnit roomUnit : targets) { Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboStats().hasGuild(room.getGuildId())) { + if (habbo != null && habbo.getHabboStats().hasGuild(targetGroupId)) { return false; } } + return true; } @@ -53,26 +75,45 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public String getWiredData() { - return String.valueOf(this.userSource); + return WiredManager.getGson().toJson(new JsonData( + this.userSource, + this.groupType, + this.selectedGroupId, + this.quantifier + )); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.resetSettings(); + String wiredData = set.getString("wired_data"); - if (wiredData != null && !wiredData.isEmpty()) { - try { - this.userSource = Integer.parseInt(wiredData); - } catch (NumberFormatException ignored) { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + try { + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.userSource = this.normalizeUserSource(data.userSource); + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = this.normalizeSelectedGroupId(data.selectedGroupId); + this.quantifier = this.normalizeQuantifier(data.quantifier); + return; } - } else { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = this.normalizeUserSource(Integer.parseInt(wiredData)); + } catch (Exception ignored) { + this.resetSettings(); } } @Override public void onPickUp() { - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.resetSettings(); } @Override @@ -88,8 +129,11 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(4); message.appendInt(this.userSource); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -99,7 +143,66 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { int[] params = settings.getIntParams(); - this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 0) ? this.normalizeUserSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = (params.length > 1) ? this.normalizeGroupType(params[1]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 2) ? this.normalizeSelectedGroupId(params[2]) : 0; + this.quantifier = (params.length > 3) ? this.normalizeQuantifier(params[3]) : QUANTIFIER_ALL; return true; } + + private void resetSettings() { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.quantifier = QUANTIFIER_ALL; + } + + private int resolveTargetGroupId(Room room) { + if (room == null) { + return 0; + } + + if (this.groupType == GROUP_SELECTED) { + return this.selectedGroupId; + } + + return room.getGuildId(); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + private int normalizeSelectedGroupId(int value) { + return Math.max(0, value); + } + + private int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + + static class JsonData { + int userSource; + int groupType; + int selectedGroupId; + int quantifier; + + public JsonData(int userSource, int groupType, int selectedGroupId, int quantifier) { + this.userSource = userSource; + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.quantifier = quantifier; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java index 9eead216..d2349c84 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotInTeam.java @@ -1,28 +1,19 @@ package com.eu.habbo.habbohotel.items.interactions.wired.conditions; -import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.items.Item; -import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; -import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; -import com.eu.habbo.habbohotel.users.Habbo; -import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.WiredConditionType; -import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; -import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -public class WiredConditionNotInTeam extends InteractionWiredCondition { +public class WiredConditionNotInTeam extends WiredConditionTeamMember { public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_IN_TEAM; - private GameTeamColors teamColor = GameTeamColors.RED; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - public WiredConditionNotInTeam(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -37,96 +28,15 @@ public class WiredConditionNotInTeam extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return true; - for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboInfo().getGamePlayer() != null) { - if (habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor)) { - return false; - } - } + if (this.getQuantifier() == QUANTIFIER_ALL) { + return !this.evaluateAllTargetsMatch(room, targets); } - return true; - } - @Deprecated - @Override - public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { - return false; - } - - @Override - public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData( - this.teamColor, - this.userSource - )); - } - - @Override - public void loadWiredData(ResultSet set, Room room) throws SQLException { - try { - String wiredData = set.getString("wired_data"); - - if (wiredData.startsWith("{")) { - JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); - this.teamColor = data.teamColor; - this.userSource = data.userSource; - } else { - if (!wiredData.equals("")) - this.teamColor = GameTeamColors.values()[Integer.parseInt(wiredData)]; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } catch (Exception e) { - this.teamColor = GameTeamColors.RED; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; - } - } - - @Override - public void onPickUp() { - this.teamColor = GameTeamColors.RED; - this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + return !this.evaluateAnyTargetMatches(room, targets); } @Override public WiredConditionType getType() { return type; } - - @Override - public void serializeWiredData(ServerMessage message, Room room) { - message.appendBoolean(false); - message.appendInt(5); - message.appendInt(0); - message.appendInt(this.getBaseItem().getSpriteId()); - message.appendInt(this.getId()); - message.appendString(""); - message.appendInt(2); - message.appendInt(this.teamColor.type); - message.appendInt(this.userSource); - message.appendInt(0); - message.appendInt(this.getType().code); - message.appendInt(0); - message.appendInt(0); - } - - @Override - public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.teamColor = GameTeamColors.values()[settings.getIntParams()[0]]; - int[] params = settings.getIntParams(); - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; - - return true; - } - - static class JsonData { - GameTeamColors teamColor; - int userSource; - - public JsonData(GameTeamColors teamColor, int userSource) { - this.teamColor = teamColor; - this.userSource = userSource; - } - } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java index 2121bf97..e52315dc 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotMatchStatePosition.java @@ -22,7 +22,17 @@ public class WiredConditionNotMatchStatePosition extends WiredConditionMatchStat @Override public boolean evaluate(WiredContext ctx) { - return !super.evaluate(ctx); + this.refresh(); + + if (this.getMatchFurniSettings().isEmpty()) { + return false; + } + + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.evaluateAnyTargetMatches(ctx); + } + + return !this.evaluateAllTargetsMatch(ctx); } @Deprecated diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java index 4fe1d474..7b726e80 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggerOnFurni.java @@ -37,7 +37,11 @@ public class WiredConditionNotTriggerOnFurni extends WiredConditionTriggerOnFurn if (itemTargets.isEmpty()) return true; - return !isAnyUserOnFurni(userTargets, itemTargets, room); + if (this.getQuantifier() == QUANTIFIER_ANY) { + return !this.isAnyUserOnFurni(userTargets, itemTargets, room); + } + + return !this.areAllUsersOnFurni(userTargets, itemTargets, room); } @Deprecated diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java new file mode 100644 index 00000000..87e09812 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java @@ -0,0 +1,213 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.conditions; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredConditionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class WiredConditionSelectionQuantity extends InteractionWiredCondition { + private static final int COMPARISON_LESS_THAN = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER_THAN = 2; + + private static final int SOURCE_GROUP_USERS = 0; + private static final int SOURCE_GROUP_FURNI = 1; + + public static final WiredConditionType type = WiredConditionType.SLC_QUANTITY; + + private int comparison = COMPARISON_EQUAL; + private int quantity = 0; + private int sourceGroup = SOURCE_GROUP_USERS; + private int sourceType = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredConditionSelectionQuantity(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredConditionSelectionQuantity(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public WiredConditionType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(5); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.comparison); + message.appendInt(this.quantity); + message.appendInt(this.sourceGroup); + message.appendInt(this.sourceType); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + + this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL; + this.quantity = (params.length > 1) ? this.normalizeQuantity(params[1]) : 0; + this.sourceGroup = (params.length > 2) ? this.normalizeSourceGroup(params[2]) : SOURCE_GROUP_USERS; + this.sourceType = (params.length > 3) ? this.normalizeSourceType(this.sourceGroup, params[3]) : WiredSourceUtil.SOURCE_TRIGGER; + + return true; + } + + @Override + public boolean evaluate(WiredContext ctx) { + int count = this.resolveCount(ctx); + + switch (this.comparison) { + case COMPARISON_LESS_THAN: + return count < this.quantity; + case COMPARISON_GREATER_THAN: + return count > this.quantity; + default: + return count == this.quantity; + } + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.quantity, + this.sourceGroup, + this.sourceType + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.quantity = this.normalizeQuantity(data.quantity); + this.sourceGroup = this.normalizeSourceGroup(data.sourceGroup); + this.sourceType = this.normalizeSourceType(this.sourceGroup, data.sourceType); + return; + } + + String[] parts = wiredData.split("\t"); + + try { + if (parts.length > 0) { + this.comparison = this.normalizeComparison(Integer.parseInt(parts[0])); + } + if (parts.length > 1) { + this.quantity = this.normalizeQuantity(Integer.parseInt(parts[1])); + } + if (parts.length > 2) { + this.sourceGroup = this.normalizeSourceGroup(Integer.parseInt(parts[2])); + } + if (parts.length > 3) { + this.sourceType = this.normalizeSourceType(this.sourceGroup, Integer.parseInt(parts[3])); + } + } catch (NumberFormatException ignored) { + this.onPickUp(); + } + } + + @Override + public void onPickUp() { + this.comparison = COMPARISON_EQUAL; + this.quantity = 0; + this.sourceGroup = SOURCE_GROUP_USERS; + this.sourceType = WiredSourceUtil.SOURCE_TRIGGER; + } + + private int resolveCount(WiredContext ctx) { + if (this.sourceGroup == SOURCE_GROUP_FURNI) { + List items = WiredSourceUtil.resolveItems(ctx, this.sourceType, null); + + return items.size(); + } + + List users = WiredSourceUtil.resolveUsers(ctx, this.sourceType); + + return users.size(); + } + + private int normalizeComparison(int value) { + switch (value) { + case COMPARISON_LESS_THAN: + case COMPARISON_GREATER_THAN: + return value; + default: + return COMPARISON_EQUAL; + } + } + + private int normalizeQuantity(int value) { + return Math.max(0, Math.min(100, value)); + } + + private int normalizeSourceGroup(int value) { + return (value == SOURCE_GROUP_FURNI) ? SOURCE_GROUP_FURNI : SOURCE_GROUP_USERS; + } + + private int normalizeSourceType(int group, int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int comparison; + int quantity; + int sourceGroup; + int sourceType; + + public JsonData(int comparison, int quantity, int sourceGroup, int sourceType) { + this.comparison = comparison; + this.quantity = quantity; + this.sourceGroup = sourceGroup; + this.sourceType = sourceType; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java index 658f8b7d..4ca7b4e1 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamMember.java @@ -18,10 +18,14 @@ import java.sql.SQLException; import java.util.List; public class WiredConditionTeamMember extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.ACTOR_IN_TEAM; private GameTeamColors teamColor = GameTeamColors.RED; - private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionTeamMember(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,17 +41,40 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource); if (targets.isEmpty()) return false; + if (this.quantifier == QUANTIFIER_ANY) { + return this.evaluateAnyTargetMatches(room, targets); + } + + return this.evaluateAllTargetsMatch(room, targets); + } + + protected boolean evaluateAllTargetsMatch(Room room, List targets) { for (RoomUnit roomUnit : targets) { - Habbo habbo = room.getHabbo(roomUnit); - if (habbo != null && habbo.getHabboInfo().getGamePlayer() != null) { - if (habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor)) { - return true; - } + if (!this.matchesTeam(room, roomUnit)) { + return false; } } + + return true; + } + + protected boolean evaluateAnyTargetMatches(Room room, List targets) { + for (RoomUnit roomUnit : targets) { + if (this.matchesTeam(room, roomUnit)) { + return true; + } + } + return false; } + protected boolean matchesTeam(Room room, RoomUnit roomUnit) { + Habbo habbo = room.getHabbo(roomUnit); + return habbo != null + && habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getGamePlayer().getTeamColor().equals(this.teamColor); + } + @Deprecated @Override public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { @@ -58,7 +85,8 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.teamColor, - this.userSource + this.userSource, + this.quantifier )); } @@ -71,14 +99,17 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.teamColor = data.teamColor; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY); } else { if (!wiredData.equals("")) this.teamColor = GameTeamColors.values()[Integer.parseInt(wiredData)]; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ANY; } } catch (Exception e) { this.teamColor = GameTeamColors.RED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } } @@ -86,6 +117,7 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { public void onPickUp() { this.teamColor = GameTeamColors.RED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override @@ -101,9 +133,10 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.teamColor.type); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -113,20 +146,35 @@ public class WiredConditionTeamMember extends InteractionWiredCondition { @Override public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; - this.teamColor = GameTeamColors.values()[settings.getIntParams()[0]]; int[] params = settings.getIntParams(); + this.teamColor = GameTeamColors.values()[params[0]]; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ALL) : QUANTIFIER_ANY; return true; } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(Integer value, int fallback) { + if (value == null) { + return fallback; + } + + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + static class JsonData { GameTeamColors teamColor; int userSource; + Integer quantifier; - public JsonData(GameTeamColors teamColor, int userSource) { + public JsonData(GameTeamColors teamColor, int userSource, int quantifier) { this.teamColor = teamColor; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java index 560ac2d8..bcc3fa5c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggerOnFurni.java @@ -22,11 +22,15 @@ import java.util.List; import java.util.stream.Collectors; public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { + protected static final int QUANTIFIER_ALL = 0; + protected static final int QUANTIFIER_ANY = 1; + public static final WiredConditionType type = WiredConditionType.TRIGGER_ON_FURNI; protected THashSet items = new THashSet<>(); protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; protected int userSource = WiredSourceUtil.SOURCE_TRIGGER; + protected int quantifier = QUANTIFIER_ALL; public WiredConditionTriggerOnFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -38,8 +42,6 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { @Override public boolean evaluate(WiredContext ctx) { - Room room = ctx.room(); - this.refresh(); List userTargets = WiredSourceUtil.resolveUsers(ctx, this.userSource); @@ -50,7 +52,11 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { if (itemTargets.isEmpty()) return false; - return isAnyUserOnFurni(userTargets, itemTargets, room); + if (this.quantifier == QUANTIFIER_ANY) { + return this.isAnyUserOnFurni(userTargets, itemTargets, ctx.room()); + } + + return this.areAllUsersOnFurni(userTargets, itemTargets, ctx.room()); } @Deprecated @@ -70,13 +76,29 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { return false; } + protected boolean areAllUsersOnFurni(Collection users, Collection items, Room room) { + for (RoomUnit roomUnit : users) { + if (roomUnit == null) { + return false; + } + + THashSet itemsAtUser = room.getItemsAt(roomUnit.getCurrentLocation()); + if (itemsAtUser == null || items.stream().noneMatch(itemsAtUser::contains)) { + return false; + } + } + + return true; + } + @Override public String getWiredData() { this.refresh(); return WiredManager.getGson().toJson(new JsonData( this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), this.furniSource, - this.userSource + this.userSource, + this.quantifier )); } @@ -89,6 +111,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.furniSource = data.furniSource; this.userSource = data.userSource; + this.quantifier = this.normalizeQuantifier(data.quantifier); for(int id : data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -109,6 +132,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { this.furniSource = WiredSourceUtil.SOURCE_SELECTED; @@ -120,6 +144,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { this.items.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = QUANTIFIER_ALL; } @Override @@ -141,9 +166,10 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.furniSource); message.appendInt(this.userSource); + message.appendInt(this.quantifier); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -158,6 +184,7 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL; this.items.clear(); @@ -194,6 +221,14 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { this.items.removeAll(items); } + protected int getQuantifier() { + return this.quantifier; + } + + protected int normalizeQuantifier(int value) { + return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL; + } + @Override public WiredConditionOperator operator() { return WiredConditionOperator.AND; @@ -203,11 +238,13 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition { List itemIds; int furniSource; int userSource; + int quantifier; - public JsonData(List itemIds, int furniSource, int userSource) { + public JsonData(List itemIds, int furniSource, int userSource, int quantifier) { this.itemIds = itemIds; this.furniSource = furniSource; this.userSource = userSource; + this.quantifier = quantifier; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/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/WiredEffectBotClothes.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java index 6cc5af2b..345fbb0c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotClothes.java @@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.ServerMessage; @@ -24,6 +25,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { private String botName = ""; private String botLook = ""; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotClothes(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -41,7 +43,8 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + ((char) 9) + "" + this.botLook); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -65,6 +68,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { if (data.length != 2) throw new WiredSaveException("Malformed data string. Invalid data length"); + this.botSource = (settings.getIntParams().length > 0) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[0]) : WiredBotSourceUtil.SOURCE_BOT_NAME; this.botName = data[0].substring(0, Math.min(data[0].length(), Emulator.getConfig().getInt("hotel.wired.message.max_length", 100))); this.botLook = data[1]; this.setDelay(delay); @@ -80,10 +84,9 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() == 1) { - Bot bot = bots.get(0); + for (Bot bot : bots) { bot.setFigure(this.botLook); } } @@ -96,7 +99,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.botLook, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.botLook, this.getDelay(), this.botSource)); } @Override @@ -108,6 +111,9 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.botLook = data.look; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -119,6 +125,7 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { } this.needsUpdate(true); + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -126,9 +133,15 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { public void onPickUp() { this.botLook = ""; this.botName = ""; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + public String getBotName() { return this.botName; } @@ -149,11 +162,13 @@ public class WiredEffectBotClothes extends InteractionWiredEffect { String bot_name; String look; int delay; + Integer botSource; - public JsonData(String bot_name, String look, int delay) { + public JsonData(String bot_name, String look, int delay, int botSource) { this.bot_name = bot_name; this.look = look; this.delay = delay; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java index 6bdb028d..a1a80fe7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotFollowHabbo.java @@ -11,6 +11,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -29,6 +30,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { private String botName = ""; private int mode = 0; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotFollowHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -46,9 +48,10 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.mode); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(1); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -79,6 +82,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { int mode = settings.getIntParams()[0]; this.userSource = settings.getIntParams()[1]; + this.botSource = (settings.getIntParams().length > 2) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[2]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -111,15 +115,15 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { RoomUnit roomUnit = targets.get(0); Habbo habbo = room.getHabbo(roomUnit); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (habbo != null && bots.size() == 1) { - Bot bot = bots.get(0); - - if (this.mode == 1) { - bot.startFollowingHabbo(habbo); - } else { - bot.stopFollowingHabbo(); + if (habbo != null && !bots.isEmpty()) { + for (Bot bot : bots) { + if (this.mode == 1) { + bot.startFollowingHabbo(habbo); + } else { + bot.stopFollowingHabbo(); + } } } } @@ -132,7 +136,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -145,6 +149,9 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.mode = data.mode; this.botName = data.bot_name; this.userSource = data.userSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -157,6 +164,7 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -165,12 +173,13 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { this.botName = ""; this.mode = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource); } static class JsonData { @@ -178,12 +187,14 @@ public class WiredEffectBotFollowHabbo extends InteractionWiredEffect { int mode; int delay; int userSource; + Integer botSource; - public JsonData(String bot_name, int mode, int delay, int userSource) { + public JsonData(String bot_name, int mode, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.mode = mode; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java index c5350555..62fef4e0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotGiveHandItem.java @@ -28,10 +28,12 @@ import java.util.List; public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.BOT_GIVE_HANDITEM; + private static final int BOT_SOURCE_NAME = 100; private String botName = ""; private int itemId; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = BOT_SOURCE_NAME; public WiredEffectBotGiveHandItem(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -49,9 +51,10 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.itemId); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -80,11 +83,9 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 2) throw new WiredSaveException("Missing item id"); - int itemId = settings.getIntParams()[0]; - this.userSource = settings.getIntParams()[1]; - - if(itemId < 0) - itemId = 0; + int itemId = this.normalizeHandItem(settings.getIntParams()[0]); + this.userSource = this.normalizeUserSource(settings.getIntParams()[1]); + this.botSource = (settings.getIntParams().length > 2) ? this.normalizeBotSource(settings.getIntParams()[2]) : BOT_SOURCE_NAME; String botName = settings.getStringParam(); @@ -113,10 +114,9 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { RoomUnit roomUnit = targets.get(0); Habbo habbo = room.getHabbo(roomUnit); - List bots = room.getBots(this.botName); + Bot bot = this.resolveBot(ctx, room); - if (habbo != null && bots.size() == 1) { - Bot bot = bots.get(0); + if (habbo != null && bot != null) { List tasks = new ArrayList<>(); tasks.add(new RoomUnitGiveHanditem(roomUnit, room, this.itemId)); @@ -146,7 +146,7 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.itemId, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.itemId, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -156,21 +156,25 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); - this.itemId = data.item_id; + this.itemId = this.normalizeHandItem(data.item_id); this.botName = data.bot_name; - this.userSource = data.userSource; + this.userSource = this.normalizeUserSource(data.userSource); + this.botSource = ((data.botSource == WiredSourceUtil.SOURCE_TRIGGER) && this.botName != null && !this.botName.isEmpty()) + ? BOT_SOURCE_NAME + : this.normalizeBotSource(data.botSource); } else { String[] data = wiredData.split(((char) 9) + ""); if (data.length == 3) { this.setDelay(Integer.parseInt(data[0])); - this.itemId = Integer.parseInt(data[1]); + this.itemId = this.normalizeHandItem(Integer.parseInt(data[1])); this.botName = data[2]; } this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; } } @@ -179,12 +183,13 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { this.botName = ""; this.itemId = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return (this.userSource == WiredSourceUtil.SOURCE_TRIGGER) || (this.botSource == WiredSourceUtil.SOURCE_TRIGGER); } static class JsonData { @@ -192,12 +197,58 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect { int item_id; int delay; int userSource; + int botSource; - public JsonData(String bot_name, int item_id, int delay, int userSource) { + public JsonData(String bot_name, int item_id, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.item_id = item_id; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } + + private int normalizeHandItem(int value) { + return Math.max(0, value); + } + + private int normalizeUserSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private int normalizeBotSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case BOT_SOURCE_NAME: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return BOT_SOURCE_NAME; + } + } + + private Bot resolveBot(WiredContext ctx, Room room) { + if (this.botSource == BOT_SOURCE_NAME) { + List bots = room.getBots(this.botName); + return (bots.size() == 1) ? bots.get(0) : null; + } + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.botSource)) { + Bot bot = room.getBot(roomUnit); + + if (bot != null) { + return bot; + } + } + + return null; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java index 8301f508..dfb0aa88 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalk.java @@ -10,6 +10,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.messages.ServerMessage; @@ -26,6 +27,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { private int mode; private String botName = ""; private String message = ""; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTalk(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -43,8 +45,9 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + "" + ((char) 9) + "" + this.message); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.mode); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -55,6 +58,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 1) throw new WiredSaveException("Mode is invalid"); int mode = settings.getIntParams()[0]; + this.botSource = (settings.getIntParams().length > 1) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -103,21 +107,20 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { .replace(Emulator.getTexts().getValue("wired.variable.points", "%points%"), habbo.getHabboInfo().getCurrencyAmount(Emulator.getConfig().getInt("seasonal.primary.type")) + "") .replace(Emulator.getTexts().getValue("wired.variable.owner", "%owner%"), room.getOwnerName()) .replace(Emulator.getTexts().getValue("wired.variable.item_count", "%item_count%"), room.itemCount() + "") - .replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), this.botName) .replace(Emulator.getTexts().getValue("wired.variable.roomname", "%roomname%"), room.getName()) .replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + ""); } - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() == 1) { - Bot bot = bots.get(0); + for (Bot bot : bots) { + String botMessage = message.replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), bot.getName()); - if(!WiredManager.triggerUserSays(room, bot.getRoomUnit(), message)) { + if(!WiredManager.triggerUserSays(room, bot.getRoomUnit(), botMessage)) { if (this.mode == 1) { - bot.shout(message); + bot.shout(botMessage); } else { - bot.talk(message); + bot.talk(botMessage); } } } @@ -131,7 +134,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.botSource)); } @Override @@ -144,6 +147,9 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { this.mode = data.mode; this.botName = data.bot_name; this.message = data.message; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -156,6 +162,7 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { } this.needsUpdate(true); + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -164,9 +171,15 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { this.mode = 0; this.botName = ""; this.message = ""; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + public int getMode() { return this.mode; } @@ -201,12 +214,14 @@ public class WiredEffectBotTalk extends InteractionWiredEffect { int mode; String message; int delay; + Integer botSource; - public JsonData(String bot_name, int mode, String message, int delay) { + public JsonData(String bot_name, int mode, String message, int delay, int botSource) { this.bot_name = bot_name; this.mode = mode; this.message = message; this.delay = delay; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java index c7dc2eb5..3532b276 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTalkToHabbo.java @@ -11,6 +11,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -31,6 +32,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { private String botName = ""; private String message = ""; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTalkToHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -48,9 +50,10 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName + "" + ((char) 9) + "" + this.message); - message.appendInt(2); + message.appendInt(3); message.appendInt(this.mode); message.appendInt(this.userSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -80,6 +83,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { if(settings.getIntParams().length < 2) throw new WiredSaveException("Missing mode"); int mode = settings.getIntParams()[0]; this.userSource = settings.getIntParams()[1]; + this.botSource = (settings.getIntParams().length > 2) ? WiredBotSourceUtil.normalizeBotSource(settings.getIntParams()[2]) : WiredBotSourceUtil.SOURCE_BOT_NAME; if(mode != 0 && mode != 1) throw new WiredSaveException("Mode is invalid"); @@ -116,9 +120,8 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); - if (bots.size() != 1) return; - Bot bot = bots.get(0); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); + if (bots.isEmpty()) return; for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) { Habbo habbo = room.getHabbo(roomUnit); @@ -131,15 +134,18 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { .replace(Emulator.getTexts().getValue("wired.variable.points", "%points%"), habbo.getHabboInfo().getCurrencyAmount(Emulator.getConfig().getInt("seasonal.primary.type")) + "") .replace(Emulator.getTexts().getValue("wired.variable.owner", "%owner%"), room.getOwnerName()) .replace(Emulator.getTexts().getValue("wired.variable.item_count", "%item_count%"), room.itemCount() + "") - .replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), this.botName) .replace(Emulator.getTexts().getValue("wired.variable.roomname", "%roomname%"), room.getName()) .replace(Emulator.getTexts().getValue("wired.variable.user_count", "%user_count%"), room.getUserCount() + ""); - if (!WiredManager.triggerUserSays(room, bot.getRoomUnit(), m)) { - if (this.mode == 1) { - bot.whisper(m, habbo); - } else { - bot.talk(habbo.getHabboInfo().getUsername() + ": " + m); + for (Bot bot : bots) { + String botMessage = m.replace(Emulator.getTexts().getValue("wired.variable.name", "%name%"), bot.getName()); + + if (!WiredManager.triggerUserSays(room, bot.getRoomUnit(), botMessage)) { + if (this.mode == 1) { + bot.whisper(botMessage, habbo); + } else { + bot.talk(habbo.getHabboInfo().getUsername() + ": " + botMessage); + } } } } @@ -153,7 +159,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, this.mode, this.message, this.getDelay(), this.userSource, this.botSource)); } @Override @@ -167,6 +173,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.botName = data.bot_name; this.message = data.message; this.userSource = data.userSource; + this.botSource = (data.botSource != null) ? WiredBotSourceUtil.normalizeBotSource(data.botSource) : WiredBotSourceUtil.SOURCE_BOT_NAME; } else { String[] data = wiredData.split(((char) 9) + ""); @@ -180,6 +187,7 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.needsUpdate(true); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -189,12 +197,13 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { this.message = ""; this.mode = 0; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } @Override public boolean requiresTriggeringUser() { - return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER || WiredBotSourceUtil.requiresTriggeringUser(this.botSource); } static class JsonData { @@ -203,13 +212,15 @@ public class WiredEffectBotTalkToHabbo extends InteractionWiredEffect { String message; int delay; int userSource; + Integer botSource; - public JsonData(String bot_name, int mode, String message, int delay, int userSource) { + public JsonData(String bot_name, int mode, String message, int delay, int userSource, int botSource) { this.bot_name = bot_name; this.mode = mode; this.message = message; this.delay = delay; this.userSource = userSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java index 650877d7..582a8f19 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotTeleport.java @@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.rooms.RoomTileState; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -34,6 +35,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { private THashSet items; private String botName = ""; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotTeleport(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -110,8 +112,9 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -123,6 +126,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { String botName = settings.getStringParam(); int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = (params.length > 1) ? WiredBotSourceUtil.normalizeBotSource(params[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; int itemsCount = settings.getFurniIds().length; if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { @@ -177,20 +181,20 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { if (validItems.isEmpty()) return; if (room.getLayout() == null) return; - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); - if (bots.size() != 1) { + if (bots.isEmpty()) { return; } - Bot bot = bots.get(0); + for (Bot bot : bots) { + HabboItem targetItem = validItems.get(Emulator.getRandom().nextInt(validItems.size())); - HabboItem targetItem = validItems.get(Emulator.getRandom().nextInt(validItems.size())); - - if (targetItem.getRoomId() == bot.getRoom().getId()) { - RoomTile tile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); - if (tile != null) { - teleportUnitToTile(bot.getRoomUnit(), tile); + if (targetItem.getRoomId() == bot.getRoom().getId()) { + RoomTile tile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); + if (tile != null) { + teleportUnitToTile(bot.getRoomUnit(), tile); + } } } } @@ -214,7 +218,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { } } - return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource, this.botSource)); } @Override @@ -228,6 +232,9 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.furniSource = data.furniSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; for(int itemId : data.items) { HabboItem item = room.getHabboItem(itemId); @@ -260,6 +267,7 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.needsUpdate(true); this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -268,20 +276,28 @@ public class WiredEffectBotTeleport extends InteractionWiredEffect { this.botName = ""; this.items.clear(); this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + static class JsonData { String bot_name; List items; int delay; int furniSource; + Integer botSource; - public JsonData(String bot_name, List items, int delay, int furniSource) { + public JsonData(String bot_name, List items, int delay, int furniSource, int botSource) { this.bot_name = bot_name; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java index b8a2f059..bb345e12 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectBotWalkToFurni.java @@ -10,6 +10,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredBotSourceUtil; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; @@ -29,6 +30,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { private List items; private String botName = ""; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; public WiredEffectBotWalkToFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -62,8 +64,9 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -75,6 +78,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { String botName = settings.getStringParam(); int[] params = settings.getIntParams(); this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = (params.length > 1) ? WiredBotSourceUtil.normalizeBotSource(params[1]) : WiredBotSourceUtil.SOURCE_BOT_NAME; int itemsCount = settings.getFurniIds().length; if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { @@ -118,32 +122,30 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); - List bots = room.getBots(this.botName); + List bots = WiredBotSourceUtil.resolveBots(ctx, room, this.botSource, this.botName); List effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { this.items.removeIf(item -> item == null || item.getRoomId() != this.getRoomId() || Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null); } - if (effectiveItems.isEmpty() || bots.size() != 1) { + if (effectiveItems.isEmpty() || bots.isEmpty()) { return; } - Bot bot = bots.get(0); + for (Bot bot : bots) { + List possibleItems = effectiveItems.stream() + .filter(item -> !room.getBotsOnItem(item).contains(bot)) + .collect(Collectors.toList()); - // Bots shouldn't walk to the tile they are already standing on - List possibleItems = effectiveItems.stream() - .filter(item -> !room.getBotsOnItem(item).contains(bot)) - .collect(Collectors.toList()); + if (possibleItems.isEmpty()) { + continue; + } - // Get a random tile of possible tiles to walk to - if (possibleItems.size() > 0) { HabboItem item = possibleItems.get(Emulator.getRandom().nextInt(possibleItems.size())); - if (item.getRoomId() != 0 && item.getRoomId() == bot.getRoom().getId()) { - if (room.getLayout() != null) { - bot.getRoomUnit().setGoalLocation(room.getLayout().getTile(item.getX(), item.getY())); - } + if (item.getRoomId() != 0 && item.getRoomId() == bot.getRoom().getId() && room.getLayout() != null) { + bot.getRoomUnit().setGoalLocation(room.getLayout().getTile(item.getX(), item.getY())); } } } @@ -166,7 +168,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { } } - return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.botName, itemIds, this.getDelay(), this.furniSource, this.botSource)); } @Override @@ -180,6 +182,9 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.setDelay(data.delay); this.botName = data.bot_name; this.furniSource = data.furniSource; + this.botSource = (data.botSource != null) + ? WiredBotSourceUtil.normalizeBotSource(data.botSource) + : WiredBotSourceUtil.SOURCE_BOT_NAME; for(int itemId : data.items) { HabboItem item = room.getHabboItem(itemId); @@ -212,6 +217,7 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.needsUpdate(true); this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; } } @@ -220,20 +226,28 @@ public class WiredEffectBotWalkToFurni extends InteractionWiredEffect { this.items.clear(); this.botName = ""; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = WiredBotSourceUtil.SOURCE_BOT_NAME; this.setDelay(0); } + @Override + public boolean requiresTriggeringUser() { + return WiredBotSourceUtil.requiresTriggeringUser(this.botSource); + } + static class JsonData { String bot_name; List items; int delay; int furniSource; + Integer botSource; - public JsonData(String bot_name, List items, int delay, int furniSource) { + public JsonData(String bot_name, List items, int delay, int furniSource, int botSource) { this.bot_name = bot_name; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.botSource = botSource; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java index 529f206b..611b06d1 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectChangeFurniDirection.java @@ -10,10 +10,10 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.*; import com.eu.habbo.habbohotel.wired.core.WiredContext; 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.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; @@ -22,6 +22,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public static final int ACTION_WAIT = 0; @@ -35,9 +36,11 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.MOVE_DIRECTION; private final THashMap items = new THashMap<>(0); + private final ConcurrentHashMap runtimeItems = new ConcurrentHashMap<>(); private RoomUserRotation startRotation = RoomUserRotation.NORTH; private int blockedAction = 0; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean blockOnUserCollision = false; public WiredEffectChangeFurniDirection(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -56,6 +59,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { THashMap effectiveItems; if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + this.runtimeItems.clear(); THashSet toRemove = new THashSet<>(); for (HabboItem item : this.items.keySet()) { if (item == null || Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null) @@ -66,10 +70,13 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { } effectiveItems = this.items; } else { + this.pruneRuntimeItems(room); effectiveItems = new THashMap<>(); for (HabboItem item : resolvedItems) { if (item != null) { - WiredChangeDirectionSetting setting = this.items.get(item); + WiredChangeDirectionSetting setting = this.runtimeItems.computeIfAbsent( + item.getId(), + key -> new WiredChangeDirectionSetting(item.getId(), item.getRotation(), this.startRotation)); if (setting == null) { setting = new WiredChangeDirectionSetting(item.getId(), item.getRotation(), this.startRotation); } @@ -90,7 +97,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { RoomTile targetTile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); int count = 1; - while ((targetTile == null || targetTile.state == RoomTileState.INVALID || room.furnitureFitsAt(targetTile, item, item.getRotation(), false) != FurnitureMovementError.NONE) && count < 8) { + while (this.shouldSearchNextDirection(room, item, targetTile, ctx) && count < 8) { entry.getValue().direction = this.nextRotation(entry.getValue().direction); RoomTile tile = room.getLayout().getTileInFront(itemTile, entry.getValue().direction.getValue()); @@ -114,35 +121,30 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { RoomTile targetTile = room.getLayout().getTileInFront(itemTile, newDirection); if(item.getRotation() != entry.getValue().rotation) { - if(targetTile == null || room.furnitureFitsAt(targetTile, item, entry.getValue().rotation, false) != FurnitureMovementError.NONE) + if (targetTile == null || room.furnitureFitsAt(targetTile, item, entry.getValue().rotation, false) != FurnitureMovementError.NONE) continue; - room.moveFurniTo(entry.getKey(), targetTile, entry.getValue().rotation, null, true); + WiredMoveCarryHelper.moveFurni(room, this, entry.getKey(), targetTile, entry.getValue().rotation, null, true, ctx); } if (targetTile == null) continue; - boolean hasRoomUnits = false; - THashSet newOccupiedTiles = room.getLayout().getTilesAt(targetTile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); - for(RoomTile tile : newOccupiedTiles) { - for (RoomUnit _roomUnit : room.getRoomUnits(tile)) { - hasRoomUnits = true; - if(_roomUnit.getCurrentLocation() == targetTile) { - Emulator.getThreading().run(() -> { - WiredManager.triggerBotCollision(room, _roomUnit); - }); + FurnitureMovementError movementError = WiredMoveCarryHelper.getMovementError(room, this, entry.getKey(), targetTile, item.getRotation(), ctx); + + if (movementError == FurnitureMovementError.TILE_HAS_HABBOS + || movementError == FurnitureMovementError.TILE_HAS_BOTS + || movementError == FurnitureMovementError.TILE_HAS_PETS) { + Emulator.getThreading().run(() -> { + for (RoomUnit roomUnit : room.getRoomUnits(targetTile)) { + WiredManager.triggerBotCollision(room, roomUnit); break; } - } + }); } - if (targetTile.state != RoomTileState.INVALID && room.furnitureFitsAt(targetTile, item, item.getRotation(), false) == FurnitureMovementError.NONE) { - if (!hasRoomUnits) { - RoomTile oldLocation = room.getLayout().getTile(entry.getKey().getX(), entry.getKey().getY()); - double oldZ = entry.getKey().getZ(); - if(oldLocation != null && room.moveFurniTo(entry.getKey(), targetTile, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(entry.getKey(), null, oldLocation, oldZ, targetTile, entry.getKey().getZ(), 0, room).compose()); - } + if (targetTile.state != RoomTileState.INVALID && movementError == FurnitureMovementError.NONE) { + RoomTile oldLocation = room.getLayout().getTile(entry.getKey().getX(), entry.getKey().getY()); + if (oldLocation != null && WiredMoveCarryHelper.moveFurni(room, this, entry.getKey(), targetTile, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } @@ -157,13 +159,14 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { @Override public String getWiredData() { ArrayList settings = new ArrayList<>(this.items.values()); - return WiredManager.getGson().toJson(new JsonData(this.startRotation, this.blockedAction, settings, this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.startRotation, this.blockedAction, settings, this.getDelay(), this.furniSource, this.blockOnUserCollision)); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.runtimeItems.clear(); String wiredData = set.getString("wired_data"); @@ -173,6 +176,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { this.startRotation = data.start_direction; this.blockedAction = data.blocked_action; this.furniSource = data.furniSource; + this.blockOnUserCollision = data.blockOnUserCollision; for(WiredChangeDirectionSetting setting : data.items) { HabboItem item = room.getHabboItem(setting.item_id); @@ -217,6 +221,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { } this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.blockOnUserCollision = false; this.needsUpdate(true); } } @@ -225,9 +230,11 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { public void onPickUp() { this.setDelay(0); this.items.clear(); + this.runtimeItems.clear(); this.blockedAction = 0; this.startRotation = RoomUserRotation.NORTH; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.blockOnUserCollision = false; } @Override @@ -246,10 +253,11 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(3); + message.appendInt(4); message.appendInt(this.startRotation != null ? this.startRotation.getValue() : 0); message.appendInt(this.blockedAction); message.appendInt(this.furniSource); + message.appendInt(this.blockOnUserCollision ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(this.getDelay()); @@ -262,7 +270,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { int startDirectionInt = settings.getIntParams()[0]; - if(startDirectionInt < 0 || startDirectionInt > 7 || (startDirectionInt % 2) != 0) { + if(startDirectionInt < 0 || startDirectionInt > 7) { throw new WiredSaveException("Start direction is invalid"); } @@ -270,6 +278,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { int blockedActionInt = settings.getIntParams()[1]; this.furniSource = settings.getIntParams()[2]; + this.blockOnUserCollision = settings.getIntParams().length > 3 && settings.getIntParams()[3] == 1; if(blockedActionInt < 0 || blockedActionInt > 6) { throw new WiredSaveException("Blocked action is invalid"); @@ -299,6 +308,7 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.items.clear(); + this.runtimeItems.clear(); if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { this.items.putAll(newItems); } @@ -309,6 +319,31 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { return true; } + private void pruneRuntimeItems(Room room) { + if (room == null || this.runtimeItems.isEmpty()) { + return; + } + + this.runtimeItems.entrySet().removeIf(entry -> room.getHabboItem(entry.getKey()) == null); + } + + private boolean shouldSearchNextDirection(Room room, HabboItem item, RoomTile targetTile, WiredContext ctx) { + if (targetTile == null || targetTile.state == RoomTileState.INVALID) { + return true; + } + + if (room.furnitureFitsAt(targetTile, item, item.getRotation(), false) != FurnitureMovementError.NONE) { + return true; + } + + if (this.blockOnUserCollision) { + return false; + } + + FurnitureMovementError unitCollision = WiredMoveCarryHelper.getMovementError(room, this, item, targetTile, item.getRotation(), ctx); + return unitCollision == FurnitureMovementError.TILE_HAS_HABBOS; + } + private RoomUserRotation nextRotation(RoomUserRotation currentRotation) { switch (this.blockedAction) { case ACTION_TURN_BACK: @@ -340,13 +375,15 @@ public class WiredEffectChangeFurniDirection extends InteractionWiredEffect { List items; int delay; int furniSource; + boolean blockOnUserCollision; - public JsonData(RoomUserRotation start_direction, int blocked_action, List items, int delay, int furniSource) { + public JsonData(RoomUserRotation start_direction, int blocked_action, List items, int delay, int furniSource, boolean blockOnUserCollision) { this.start_direction = start_direction; this.blocked_action = blocked_action; this.items = items; this.delay = delay; this.furniSource = furniSource; + this.blockOnUserCollision = blockOnUserCollision; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/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/effects/WiredEffectFurniToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java index bdaa461b..ed8bf0a9 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToFurni.java @@ -12,6 +12,7 @@ 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.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; @@ -64,9 +65,14 @@ public class WiredEffectFurniToFurni extends InteractionWiredEffect { return; } - FurnitureMovementError error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), null, true, false); - if (error != FurnitureMovementError.NONE) { - room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, true, false); + FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, moveItem, targetTile, moveItem.getRotation(), null, false, ctx); + if (error == FurnitureMovementError.NONE) { + return; + } + + error = WiredMoveCarryHelper.moveFurni(room, this, moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, false, ctx); + if (error == FurnitureMovementError.NONE) { + return; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java index 62df4762..9d4fad7d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectFurniToUser.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import java.sql.ResultSet; import java.sql.SQLException; @@ -38,9 +39,16 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { return; } - FurnitureMovementError error = room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false); - if (error != FurnitureMovementError.NONE && item.getBaseItem().getStateCount() > 0) { - room.moveFurniTo(item, targetTile, item.getRotation(), item.getZ(), null, true, false); + FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), null, false, ctx); + if (error == FurnitureMovementError.NONE) { + return; + } + + if (item.getBaseItem().getStateCount() > 0) { + error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), item.getZ(), null, false, ctx); + if (error == FurnitureMovementError.NONE) { + return; + } } } @@ -54,4 +62,5 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase { public WiredEffectType getType() { return type; } + } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java index b8d1ceaa..e249a9ac 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveHandItem.java @@ -20,7 +20,7 @@ public class WiredEffectGiveHandItem extends WiredEffectWhisper { @Override public void execute(WiredContext ctx) { try { - int itemId = Integer.parseInt(this.message); + int itemId = Math.max(0, Integer.parseInt(this.message)); Room room = ctx.room(); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java index b2b07b4d..546f721d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScore.java @@ -16,27 +16,22 @@ import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import gnu.trove.iterator.TObjectIntIterator; -import gnu.trove.map.TObjectIntMap; -import gnu.trove.map.hash.TObjectIntHashMap; import gnu.trove.procedure.TObjectProcedure; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class WiredEffectGiveScore extends InteractionWiredEffect { + private static final int OPERATION_ADD = 0; + private static final int OPERATION_REMOVE = 1; public static final WiredEffectType type = WiredEffectType.GIVE_SCORE; private int score; - private int count; + private int operation = OPERATION_ADD; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; - private TObjectIntMap> data = new TObjectIntHashMap<>(); - public WiredEffectGiveScore(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); } @@ -58,45 +53,8 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if (game == null) continue; - int gameStartTime = game.getStartTime(); - - TObjectIntMap> dataClone = new TObjectIntHashMap<>(this.data); - - TObjectIntIterator> iterator = dataClone.iterator(); - - boolean alreadyCounted = false; - for (int i = dataClone.size(); i-- > 0; ) { - iterator.advance(); - - Map.Entry map = iterator.key(); - - if (map.getValue() == habbo.getHabboInfo().getId()) { - if (map.getKey() == gameStartTime) { - if (iterator.value() < this.count) { - iterator.setValue(iterator.value() + 1); - - habbo.getHabboInfo().getGamePlayer().addScore(this.score, true); - - alreadyCounted = true; - break; - } - } else { - iterator.remove(); - } - } - } - - if (!alreadyCounted) { - try { - this.data.put(new AbstractMap.SimpleEntry<>(gameStartTime, habbo.getHabboInfo().getId()), 1); - } - catch(IllegalArgumentException e) { - - } - - if (habbo.getHabboInfo().getGamePlayer() != null) { - habbo.getHabboInfo().getGamePlayer().addScore(this.score, true); - } + if (habbo.getHabboInfo().getGamePlayer() != null) { + habbo.getHabboInfo().getGamePlayer().addScore(this.getAppliedAmount(), true); } } } @@ -109,7 +67,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.score, this.count, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.score, this.operation, this.getDelay(), this.userSource)); } @Override @@ -119,7 +77,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.score = data.score; - this.count = data.count; + this.operation = this.normalizeOperation(data.operation); this.setDelay(data.delay); this.userSource = data.userSource; } @@ -128,7 +86,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if (data.length == 3) { this.score = Integer.parseInt(data[0]); - this.count = Integer.parseInt(data[1]); + this.operation = OPERATION_ADD; this.setDelay(Integer.parseInt(data[2])); } @@ -140,7 +98,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { @Override public void onPickUp() { this.score = 0; - this.count = 0; + this.operation = OPERATION_ADD; this.setDelay(0); this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -160,7 +118,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { message.appendString(""); message.appendInt(3); message.appendInt(this.score); - message.appendInt(this.count); + message.appendInt(this.operation); message.appendInt(this.userSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -195,10 +153,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { if(score < 1 || score > 100) throw new WiredSaveException("Score is invalid"); - int timesPerGame = settings.getIntParams()[1]; - - if(timesPerGame < 1 || timesPerGame > 10) - throw new WiredSaveException("Times per game is invalid"); + int operation = this.normalizeOperation(settings.getIntParams()[1]); this.userSource = settings.getIntParams()[2]; int delay = settings.getDelay(); @@ -207,7 +162,7 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.score = score; - this.count = timesPerGame; + this.operation = operation; this.setDelay(delay); return true; @@ -218,15 +173,23 @@ public class WiredEffectGiveScore extends InteractionWiredEffect { return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; } + private int normalizeOperation(int value) { + return (value == OPERATION_REMOVE) ? OPERATION_REMOVE : OPERATION_ADD; + } + + private int getAppliedAmount() { + return (this.operation == OPERATION_REMOVE) ? -this.score : this.score; + } + static class JsonData { int score; - int count; + int operation; int delay; int userSource; - public JsonData(int score, int count, int delay, int userSource) { + public JsonData(int score, int operation, int delay, int userSource) { this.score = score; - this.count = count; + this.operation = operation; this.delay = delay; this.userSource = userSource; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java index 7b2c0efd..e9ff423a 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectGiveScoreToTeam.java @@ -16,20 +16,19 @@ import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import gnu.trove.map.hash.TIntIntHashMap; import java.sql.ResultSet; import java.sql.SQLException; public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { + private static final int OPERATION_ADD = 0; + private static final int OPERATION_REMOVE = 1; public static final WiredEffectType type = WiredEffectType.GIVE_SCORE_TEAM; private int points; - private int count; + private int operation = OPERATION_ADD; private GameTeamColors teamColor = GameTeamColors.RED; - private TIntIntHashMap startTimes = new TIntIntHashMap(); - public WiredEffectGiveScoreToTeam(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { super(id, userId, item, extradata, limitedStack, limitedSells); } @@ -43,16 +42,10 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { Room room = ctx.room(); for (Game game : room.getGames()) { if (game != null && game.state.equals(GameState.RUNNING)) { - int c = this.startTimes.get(game.getStartTime()); + GameTeam team = game.getTeam(this.teamColor); - if (c < this.count) { - GameTeam team = game.getTeam(this.teamColor); - - if (team != null) { - team.addTeamScore(this.points); - - this.startTimes.put(game.getStartTime(), c + 1); - } + if (team != null) { + team.addTeamScore(this.getAppliedAmount(team)); } } } @@ -66,7 +59,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.points, this.count, this.teamColor, this.getDelay())); + return WiredManager.getGson().toJson(new JsonData(this.points, this.operation, this.teamColor, this.getDelay())); } @Override @@ -76,7 +69,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if(wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.points = data.score; - this.count = data.count; + this.operation = this.normalizeOperation(data.operation); this.teamColor = data.team; this.setDelay(data.delay); } @@ -85,8 +78,8 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if (data.length == 4) { this.points = Integer.parseInt(data[0]); - this.count = Integer.parseInt(data[1]); - this.teamColor = GameTeamColors.values()[Integer.parseInt(data[2])]; + this.operation = OPERATION_ADD; + this.teamColor = GameTeamColors.fromType(Integer.parseInt(data[2])); this.setDelay(Integer.parseInt(data[3])); } @@ -96,9 +89,8 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { @Override public void onPickUp() { - this.startTimes.clear(); this.points = 0; - this.count = 0; + this.operation = OPERATION_ADD; this.teamColor = GameTeamColors.RED; this.setDelay(0); } @@ -118,7 +110,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { message.appendString(""); message.appendInt(3); message.appendInt(this.points); - message.appendInt(this.count); + message.appendInt(this.operation); message.appendInt(this.teamColor.type); message.appendInt(0); message.appendInt(this.getType().code); @@ -135,10 +127,7 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { if(points < 1 || points > 100) throw new WiredSaveException("Points is invalid"); - int timesPerGame = settings.getIntParams()[1]; - - if(timesPerGame < 1 || timesPerGame > 10) - throw new WiredSaveException("Times per game is invalid"); + int operation = this.normalizeOperation(settings.getIntParams()[1]); int team = settings.getIntParams()[2]; @@ -151,22 +140,34 @@ public class WiredEffectGiveScoreToTeam extends InteractionWiredEffect { throw new WiredSaveException("Delay too long"); this.points = points; - this.count = timesPerGame; - this.teamColor = GameTeamColors.values()[team]; + this.operation = operation; + this.teamColor = GameTeamColors.fromType(team); this.setDelay(delay); return true; } + private int normalizeOperation(int value) { + return (value == OPERATION_REMOVE) ? OPERATION_REMOVE : OPERATION_ADD; + } + + private int getAppliedAmount(GameTeam team) { + if (this.operation != OPERATION_REMOVE) { + return this.points; + } + + return -Math.min(this.points, team.getTeamScore()); + } + static class JsonData { int score; - int count; + int operation; GameTeamColors team; int delay; - public JsonData(int score, int count, GameTeamColors team, int delay) { + public JsonData(int score, int operation, GameTeamColors team, int delay) { this.score = score; - this.count = count; + this.operation = operation; this.team = team; this.delay = delay; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java index 82b94846..525f0fc5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectJoinTeam.java @@ -2,6 +2,8 @@ package com.eu.habbo.habbohotel.items.interactions.wired.effects; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame; +import com.eu.habbo.habbohotel.games.freeze.FreezeGame; import com.eu.habbo.habbohotel.games.Game; import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.games.wired.WiredGame; @@ -26,9 +28,13 @@ import java.util.ArrayList; import java.util.List; public class WiredEffectJoinTeam extends InteractionWiredEffect { + private static final int TEAM_TYPE_WIRED = 0; + private static final int TEAM_TYPE_BANZAI = 1; + private static final int TEAM_TYPE_FREEZE = 2; public static final WiredEffectType type = WiredEffectType.JOIN_TEAM; private GameTeamColors teamColor = GameTeamColors.RED; + private int teamType = TEAM_TYPE_WIRED; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredEffectJoinTeam(ResultSet set, Item baseItem) throws SQLException { @@ -42,20 +48,30 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public void execute(WiredContext ctx) { Room room = ctx.room(); + Class targetGameType = this.resolveGameType(); for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) { Habbo habbo = room.getHabbo(unit); if (habbo == null) continue; - WiredGame game = (WiredGame) room.getGameOrCreate(WiredGame.class); + Game currentGame = null; + if (habbo.getHabboInfo().getCurrentGame() != null) { + currentGame = room.getGame(habbo.getHabboInfo().getCurrentGame()); + } - if (habbo.getHabboInfo().getGamePlayer() != null && habbo.getHabboInfo().getCurrentGame() != null && (habbo.getHabboInfo().getCurrentGame() != WiredGame.class || (habbo.getHabboInfo().getCurrentGame() == WiredGame.class && habbo.getHabboInfo().getGamePlayer().getTeamColor() != this.teamColor))) { - // remove from current game - Game currentGame = room.getGame(habbo.getHabboInfo().getCurrentGame()); + if (habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getCurrentGame() != null + && (habbo.getHabboInfo().getCurrentGame() != targetGameType + || habbo.getHabboInfo().getGamePlayer().getTeamColor() != this.teamColor) + && currentGame != null) { currentGame.removeHabbo(habbo); } if(habbo.getHabboInfo().getGamePlayer() == null) { + Game game = room.getGameOrCreate(targetGameType); + if (game == null) { + continue; + } game.addHabbo(habbo, this.teamColor); } } @@ -69,7 +85,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(this.teamColor, this.getDelay(), this.userSource)); + return WiredManager.getGson().toJson(new JsonData(this.teamColor, this.teamType, this.getDelay(), this.userSource)); } @Override @@ -80,6 +96,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); this.teamColor = data.team; + this.teamType = this.normalizeTeamType(data.teamType); this.userSource = data.userSource; } else { @@ -89,11 +106,12 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { this.setDelay(Integer.parseInt(data[0])); if (data.length >= 2) { - this.teamColor = GameTeamColors.values()[Integer.parseInt(data[1])]; + this.teamColor = GameTeamColors.fromType(Integer.parseInt(data[1])); } } this.needsUpdate(true); + this.teamType = TEAM_TYPE_WIRED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } } @@ -101,6 +119,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { @Override public void onPickUp() { this.teamColor = GameTeamColors.RED; + this.teamType = TEAM_TYPE_WIRED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -118,7 +137,8 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); + message.appendInt(this.teamType); message.appendInt(this.teamColor.type); message.appendInt(this.userSource); message.appendInt(0); @@ -149,8 +169,15 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { if(settings.getIntParams().length < 2) throw new WiredSaveException("invalid data"); - int team = settings.getIntParams()[0]; - this.userSource = settings.getIntParams()[1]; + if (settings.getIntParams().length > 2) { + this.teamType = this.normalizeTeamType(settings.getIntParams()[0]); + this.userSource = settings.getIntParams()[2]; + } else { + this.teamType = TEAM_TYPE_WIRED; + this.userSource = settings.getIntParams()[1]; + } + + int team = (settings.getIntParams().length > 2) ? settings.getIntParams()[1] : settings.getIntParams()[0]; if(team < 1 || team > 4) throw new WiredSaveException("Team is invalid"); @@ -160,7 +187,7 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { if(delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) throw new WiredSaveException("Delay too long"); - this.teamColor = GameTeamColors.values()[team]; + this.teamColor = GameTeamColors.fromType(team); this.setDelay(delay); return true; @@ -171,13 +198,34 @@ public class WiredEffectJoinTeam extends InteractionWiredEffect { return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; } + private int normalizeTeamType(int value) { + if (value == TEAM_TYPE_BANZAI || value == TEAM_TYPE_FREEZE) { + return value; + } + + return TEAM_TYPE_WIRED; + } + + private Class resolveGameType() { + switch (this.teamType) { + case TEAM_TYPE_BANZAI: + return BattleBanzaiGame.class; + case TEAM_TYPE_FREEZE: + return FreezeGame.class; + default: + return WiredGame.class; + } + } + static class JsonData { GameTeamColors team; + int teamType; int delay; int userSource; - public JsonData(GameTeamColors team, int delay, int userSource) { + public JsonData(GameTeamColors team, int teamType, int delay, int userSource) { this.team = team; + this.teamType = teamType; this.delay = delay; this.userSource = userSource; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java index e075f486..dd616217 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMatchFurni.java @@ -12,10 +12,10 @@ import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredMatchFurniSetting; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import java.math.BigDecimal; public class WiredEffectMatchFurni extends InteractionWiredEffect implements InteractionWiredMatchFurniSettings { private static final Logger LOGGER = LoggerFactory.getLogger(WiredEffectMatchFurni.class); @@ -35,6 +36,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int private boolean state = false; private boolean direction = false; private boolean position = false; + private boolean altitude = false; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredEffectMatchFurni(ResultSet set, Item baseItem) throws SQLException { @@ -62,7 +64,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int for (WiredMatchFurniSetting setting : this.settings) { HabboItem item = room.getHabboItem(setting.item_id); if (item != null) { - this.applySetting(room, item, setting); + this.applySetting(room, item, setting, ctx); } } @@ -80,7 +82,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item); if (setting == null) continue; - this.applySetting(room, item, setting); + this.applySetting(room, item, setting, ctx); } } @@ -104,7 +106,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int return fallback; } - private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting) { + private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting, WiredContext ctx) { if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) { if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) { item.setExtradata(setting.state); @@ -115,23 +117,28 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); if (oldLocation == null) return; - double oldZ = item.getZ(); if(this.direction && !this.position) { if(item.getRotation() != setting.rotation && room.furnitureFitsAt(oldLocation, item, setting.rotation, false) == FurnitureMovementError.NONE) { - room.moveFurniTo(item, oldLocation, setting.rotation, null, true); + WiredMoveCarryHelper.moveFurni(room, this, item, oldLocation, setting.rotation, null, true, ctx); + } + } + else if(this.altitude && !this.position) { + int newRotation = this.direction ? setting.rotation : item.getRotation(); + if(BigDecimal.valueOf(item.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0 || newRotation != item.getRotation()) { + WiredMoveCarryHelper.moveFurni(room, this, item, oldLocation, newRotation, setting.z, null, true, ctx); } } else if(this.position) { boolean slideAnimation = !this.direction || item.getRotation() == setting.rotation; RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); int newRotation = this.direction ? setting.rotation : item.getRotation(); + double newZ = this.altitude ? setting.z : item.getZ(); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) && room.furnitureFitsAt(newLocation, item, newRotation, true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(item, newLocation, newRotation, null, !slideAnimation) == FurnitureMovementError.NONE) { - if(slideAnimation) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } + if (newLocation != null && newLocation.state != RoomTileState.INVALID + && (newLocation != oldLocation || newRotation != item.getRotation()) + && WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, newRotation, ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, newRotation, newZ, null, !slideAnimation, ctx) == FurnitureMovementError.NONE) { } } } @@ -146,7 +153,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int @Override public String getWiredData() { this.refresh(); - return WiredManager.getGson().toJson(new JsonData(this.state, this.direction, this.position, new ArrayList(this.settings), this.getDelay(), this.furniSource)); + return WiredManager.getGson().toJson(new JsonData(this.state, this.direction, this.position, this.altitude, new ArrayList(this.settings), this.getDelay(), this.furniSource)); } @Override @@ -159,6 +166,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = data.state; this.direction = data.direction; this.position = data.position; + this.altitude = data.altitude; this.settings.clear(); this.settings.addAll(data.items); this.furniSource = data.furniSource; @@ -175,7 +183,9 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int String[] stuff = items[i].split(Pattern.quote("-")); - if (stuff.length >= 5) { + if (stuff.length >= 6) { + this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]), Double.parseDouble(stuff[5]))); + } else if (stuff.length >= 5) { this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]))); } @@ -187,6 +197,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = data[2].equals("1"); this.direction = data[3].equals("1"); this.position = data[4].equals("1"); + this.altitude = false; this.setDelay(Integer.parseInt(data[5])); this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.needsUpdate(true); @@ -199,6 +210,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = false; this.direction = false; this.position = false; + this.altitude = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -222,10 +234,11 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(5); message.appendInt(this.state ? 1 : 0); message.appendInt(this.direction ? 1 : 0); message.appendInt(this.position ? 1 : 0); + message.appendInt(this.altitude ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -239,7 +252,8 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int boolean setState = settings.getIntParams()[0] == 1; boolean setDirection = settings.getIntParams()[1] == 1; boolean setPosition = settings.getIntParams()[2] == 1; - this.furniSource = settings.getIntParams()[3]; + boolean setAltitude = (settings.getIntParams().length > 4) ? (settings.getIntParams()[3] == 1) : false; + this.furniSource = (settings.getIntParams().length > 4) ? settings.getIntParams()[4] : settings.getIntParams()[3]; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -261,7 +275,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int if(it == null) throw new WiredSaveException(String.format("Item %s not found", itemId)); - newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY())); + newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY(), it.getZ())); } int delay = settings.getDelay(); @@ -272,6 +286,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int this.state = setState; this.direction = setDirection; this.position = setPosition; + this.altitude = setAltitude; this.settings.clear(); this.settings.addAll(newSettings); this.setDelay(delay); @@ -308,18 +323,25 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int return this.position; } + @Override + public boolean shouldMatchAltitude() { + return this.altitude; + } + static class JsonData { boolean state; boolean direction; boolean position; + boolean altitude; List items; int delay; int furniSource; - public JsonData(boolean state, boolean direction, boolean position, List items, int delay, int furniSource) { + public JsonData(boolean state, boolean direction, boolean position, boolean altitude, List items, int delay, int furniSource) { this.state = state; this.direction = direction; this.position = position; + this.altitude = altitude; this.items = items; this.delay = delay; this.furniSource = furniSource; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java index 3cd56464..63d01717 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniAway.java @@ -10,11 +10,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -98,11 +98,10 @@ public class WiredEffectMoveFurniAway extends InteractionWiredEffect { RoomTile newLocation = room.getLayout().getTile((short) (item.getX() + x), (short) (item.getY() + y)); RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - double oldZ = item.getZ(); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && newLocation != oldLocation && room.furnitureFitsAt(newLocation, item, item.getRotation(), true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(item, newLocation, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); + if (newLocation != null && newLocation.state != RoomTileState.INVALID && newLocation != oldLocation + && WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, item.getRotation(), ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java index d2087c9f..ecdc90a7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTo.java @@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.FurnitureMovementError; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; @@ -12,13 +13,12 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; - import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -112,11 +112,6 @@ public class WiredEffectMoveFurniTo extends InteractionWiredEffect { RoomTile objectTile = room.getLayout().getTile(targetItem.getX(), targetItem.getY()); if (objectTile != null) { - RoomTile sourceTile = room.getLayout().getTile(((HabboItem) object).getX(), ((HabboItem) object).getY()); - if (sourceTile == null) continue; - - THashSet refreshTiles = room.getLayout().getTilesAt(sourceTile, ((HabboItem) object).getBaseItem().getWidth(), ((HabboItem) object).getBaseItem().getLength(), ((HabboItem) object).getRotation()); - RoomTile tile = room.getLayout().getTileInFront(objectTile, this.direction, indexOffset); if (tile == null || !tile.getAllowStack()) { indexOffset = 0; @@ -127,13 +122,18 @@ public class WiredEffectMoveFurniTo extends InteractionWiredEffect { continue; } - room.sendComposer(new FloorItemOnRollerComposer((HabboItem) object, null, tile, tile.getStackHeight() - ((HabboItem) object).getZ(), room).compose()); - - RoomTile newSourceTile = room.getLayout().getTile(((HabboItem) object).getX(), ((HabboItem) object).getY()); - if (newSourceTile != null) { - refreshTiles.addAll(room.getLayout().getTilesAt(newSourceTile, ((HabboItem) object).getBaseItem().getWidth(), ((HabboItem) object).getBaseItem().getLength(), ((HabboItem) object).getRotation())); + HabboItem movingItem = (HabboItem) object; + RoomTile oldLocation = room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + if (oldLocation == null) { + continue; } - room.updateTiles(refreshTiles); + + FurnitureMovementError movementError = WiredMoveCarryHelper.moveFurni(room, this, movingItem, tile, movingItem.getRotation(), null, false, ctx); + + if (movementError != FurnitureMovementError.NONE) { + continue; + } + this.indexOffset.put(targetItem.getId(), indexOffset); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java index d01a9fc5..be02d463 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveFurniTowards.java @@ -10,11 +10,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import com.eu.habbo.threading.runnables.WiredCollissionRunnable; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; @@ -53,7 +53,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { this.lastDirections = new THashMap<>(); } - public List getAvailableDirections(HabboItem item, Room room) { + public List getAvailableDirections(HabboItem item, Room room, WiredContext ctx) { List availableDirections = new ArrayList<>(); RoomLayout layout = room.getLayout(); @@ -73,7 +73,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { if (!layout.tileExists(tile.x, tile.y)) continue; - if (room.furnitureFitsAt(tile, item, item.getRotation()) == FurnitureMovementError.INVALID_MOVE) + if (WiredMoveCarryHelper.getMovementError(room, this, item, tile, item.getRotation(), ctx) != FurnitureMovementError.NONE) continue; HabboItem topItem = room.getTopItemAt(tile.x, tile.y); @@ -192,7 +192,7 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { 3+ available - move in random direction, but never the opposite */ - List availableDirections = this.getAvailableDirections(item, room); + List availableDirections = this.getAvailableDirections(item, room, ctx); if (moveDirection != null && !availableDirections.contains(moveDirection)) moveDirection = null; @@ -228,13 +228,11 @@ public class WiredEffectMoveFurniTowards extends InteractionWiredEffect { RoomTile newTile = room.getLayout().getTileInFront(oldLocation, moveDirection.getValue()); - double oldZ = item.getZ(); - if(newTile != null) { lastDirections.put(item.getId(), moveDirection); - if(newTile.state != RoomTileState.INVALID && newTile != oldLocation && room.furnitureFitsAt(newTile, item, item.getRotation(), true) == FurnitureMovementError.NONE) { - if (room.moveFurniTo(item, newTile, item.getRotation(), null, false) == FurnitureMovementError.NONE) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newTile, item.getZ(), 0, room).compose()); + if (newTile.state != RoomTileState.INVALID && newTile != oldLocation + && WiredMoveCarryHelper.getMovementError(room, this, item, newTile, item.getRotation(), ctx) == FurnitureMovementError.NONE) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newTile, item.getRotation(), null, false, ctx) == FurnitureMovementError.NONE) { } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java index 0b4cbb55..26bbe3ab 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateFurni.java @@ -11,11 +11,11 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSimulation; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; -import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -61,7 +61,6 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement int newRotation = this.rotation > 0 ? this.getNewRotation(item) : item.getRotation(); RoomTile newLocation = room.getLayout().getTile(item.getX(), item.getY()); RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY()); - double oldZ = item.getZ(); if(this.direction > 0) { // Use pre-selected direction if available, otherwise pick random @@ -77,13 +76,14 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement boolean slideAnimation = item.getRotation() == newRotation; - FurnitureMovementError furniMoveTest = room.furnitureFitsAt(newLocation, item, newRotation, true); - if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) && (furniMoveTest == FurnitureMovementError.NONE || ((furniMoveTest == FurnitureMovementError.TILE_HAS_BOTS || furniMoveTest == FurnitureMovementError.TILE_HAS_HABBOS || furniMoveTest == FurnitureMovementError.TILE_HAS_PETS) && newLocation == oldLocation))) { - if(room.furnitureFitsAt(newLocation, item, newRotation, false) == FurnitureMovementError.NONE && room.moveFurniTo(item, newLocation, newRotation, null, !slideAnimation) == FurnitureMovementError.NONE) { + FurnitureMovementError furniMoveTest = WiredMoveCarryHelper.getMovementError(room, this, item, newLocation, newRotation, ctx); + if (newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != item.getRotation()) + && (furniMoveTest == FurnitureMovementError.NONE + || ((furniMoveTest == FurnitureMovementError.TILE_HAS_BOTS + || furniMoveTest == FurnitureMovementError.TILE_HAS_HABBOS + || furniMoveTest == FurnitureMovementError.TILE_HAS_PETS) && newLocation == oldLocation))) { + if (WiredMoveCarryHelper.moveFurni(room, this, item, newLocation, newRotation, null, !slideAnimation, ctx) == FurnitureMovementError.NONE) { this.itemCooldowns.add(item); - if(slideAnimation) { - room.sendComposer(new FloorItemOnRollerComposer(item, null, oldLocation, oldZ, newLocation, item.getZ(), 0, room).compose()); - } } } } @@ -352,27 +352,32 @@ public class WiredEffectMoveRotateFurni extends InteractionWiredEffect implement * @return direction */ private RoomUserRotation getMovementDirection() { - RoomUserRotation movemementDirection = RoomUserRotation.NORTH; - if (this.direction == 1) { - movemementDirection = RoomUserRotation.values()[Emulator.getRandom().nextInt(RoomUserRotation.values().length / 2) * 2]; - } else if (this.direction == 2) { - if (Emulator.getRandom().nextInt(2) == 1) { - movemementDirection = RoomUserRotation.EAST; - } else { - movemementDirection = RoomUserRotation.WEST; - } - } else if (this.direction == 3) { - if (Emulator.getRandom().nextInt(2) != 1) { - movemementDirection = RoomUserRotation.SOUTH; - } - } else if (this.direction == 4) { - movemementDirection = RoomUserRotation.SOUTH; - } else if (this.direction == 5) { - movemementDirection = RoomUserRotation.EAST; - } else if (this.direction == 7) { - movemementDirection = RoomUserRotation.WEST; + switch (this.direction) { + case 1: + return RoomUserRotation.values()[Emulator.getRandom().nextInt(RoomUserRotation.values().length / 2) * 2]; + case 2: + return Emulator.getRandom().nextInt(2) == 1 ? RoomUserRotation.EAST : RoomUserRotation.WEST; + case 3: + return Emulator.getRandom().nextInt(2) == 1 ? RoomUserRotation.NORTH : RoomUserRotation.SOUTH; + case 4: + return RoomUserRotation.SOUTH; + case 5: + return RoomUserRotation.EAST; + case 6: + return RoomUserRotation.NORTH; + case 7: + return RoomUserRotation.WEST; + case 8: + return RoomUserRotation.NORTH_EAST; + case 9: + return RoomUserRotation.SOUTH_EAST; + case 10: + return RoomUserRotation.SOUTH_WEST; + case 11: + return RoomUserRotation.NORTH_WEST; + default: + return RoomUserRotation.NORTH; } - return movemementDirection; } @Override diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java new file mode 100644 index 00000000..c08487fb --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectMoveRotateUser.java @@ -0,0 +1,291 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.effects; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomTileState; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUserRotation; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; +import gnu.trove.procedure.TObjectProcedure; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class WiredEffectMoveRotateUser extends InteractionWiredEffect { + private static final int ROTATION_CLOCKWISE = 8; + private static final int ROTATION_COUNTER_CLOCKWISE = 9; + + public static final WiredEffectType type = WiredEffectType.MOVE_ROTATE_USER; + + private int movementDirection = -1; + private int rotationDirection = -1; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredEffectMoveRotateUser(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectMoveRotateUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) { + if (roomUnit == null || roomUnit.getRoom() != room) { + continue; + } + + boolean hasRotation = this.rotationDirection >= 0; + RoomUserRotation targetBodyRotation = hasRotation ? this.getTargetRotation(roomUnit) : roomUnit.getBodyRotation(); + RoomUserRotation targetHeadRotation = hasRotation ? targetBodyRotation : roomUnit.getHeadRotation(); + 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); + + 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); + if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, targetBodyRotation, targetHeadRotation, + animationDuration, noAnimation)) { + if (hasRotation) { + WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation); + } + } + continue; + } + + if (hasRotation) { + WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation); + } + } + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + this.movementDirection, + this.rotationDirection, + this.userSource + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + String wiredData = set.getString("wired_data"); + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.setDelay(data.delay); + this.movementDirection = this.normalizeDirection(data.movementDirection); + this.rotationDirection = this.normalizeRotation(data.rotationDirection); + this.userSource = data.userSource; + return; + } + + this.setDelay(0); + this.movementDirection = -1; + this.rotationDirection = -1; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onPickUp() { + this.setDelay(0); + this.movementDirection = -1; + this.rotationDirection = -1; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(5); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.movementDirection); + message.appendInt(this.rotationDirection); + message.appendInt(this.userSource); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + + if (this.requiresTriggeringUser()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @Override + public boolean execute(InteractionWiredTrigger object) { + if (!object.isTriggeredByRoomUnit()) { + invalidTriggers.add(object.getBaseItem().getSpriteId()); + } + return true; + } + }); + message.appendInt(invalidTriggers.size()); + for (Integer i : invalidTriggers) { + message.appendInt(i); + } + } else { + message.appendInt(0); + } + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + if (settings.getIntParams().length < 3) { + throw new WiredSaveException("Invalid data"); + } + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + this.movementDirection = this.normalizeDirection(settings.getIntParams()[0]); + this.rotationDirection = this.normalizeRotation(settings.getIntParams()[1]); + this.userSource = settings.getIntParams()[2]; + this.setDelay(delay); + + return true; + } + + @Override + public boolean requiresTriggeringUser() { + return this.userSource == WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + protected long requiredCooldown() { + return COOLDOWN_MOVEMENT; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeDirection(int direction) { + return (direction >= 0 && direction <= 7) ? direction : -1; + } + + private int normalizeRotation(int rotation) { + return ((rotation >= 0 && rotation <= 7) || rotation == ROTATION_CLOCKWISE || rotation == ROTATION_COUNTER_CLOCKWISE) ? rotation : -1; + } + + private RoomUserRotation getTargetRotation(RoomUnit roomUnit) { + RoomUserRotation currentRotation = (roomUnit != null && roomUnit.getBodyRotation() != null) ? roomUnit.getBodyRotation() : RoomUserRotation.NORTH; + + if (this.rotationDirection == ROTATION_CLOCKWISE) { + return RoomUserRotation.clockwise(currentRotation); + } + + if (this.rotationDirection == ROTATION_COUNTER_CLOCKWISE) { + return RoomUserRotation.counterClockwise(currentRotation); + } + + return RoomUserRotation.fromValue(this.rotationDirection); + } + + private RoomTile getTargetTile(Room room, RoomUnit roomUnit, int direction) { + RoomTile currentTile = roomUnit.getCurrentLocation(); + + if (currentTile == null) { + return null; + } + + int deltaX = 0; + int deltaY = 0; + + switch (RoomUserRotation.fromValue(direction)) { + case NORTH: + deltaY = 1; + break; + case NORTH_EAST: + deltaX = 1; + deltaY = 1; + break; + case EAST: + deltaX = 1; + break; + case SOUTH_EAST: + deltaX = 1; + deltaY = -1; + break; + case SOUTH: + deltaY = -1; + break; + case SOUTH_WEST: + deltaX = -1; + deltaY = -1; + break; + case WEST: + deltaX = -1; + break; + case NORTH_WEST: + deltaX = -1; + deltaY = 1; + break; + } + + return room.getLayout().getTile((short) (currentTile.x + deltaX), (short) (currentTile.y + deltaY)); + } + + private boolean canMoveTo(Room room, RoomUnit roomUnit, RoomTile targetTile) { + if (targetTile == null || targetTile.state == RoomTileState.INVALID || targetTile.state == RoomTileState.BLOCKED) { + return false; + } + + if (!room.tileWalkable(targetTile)) { + return false; + } + + for (RoomUnit unit : room.getRoomUnitsAt(targetTile)) { + if (unit != null && unit != roomUnit) { + return false; + } + } + + return true; + } + + static class JsonData { + int delay; + int movementDirection; + int rotationDirection; + int userSource; + + public JsonData(int delay, int movementDirection, int rotationDirection, int userSource) { + this.delay = delay; + this.movementDirection = movementDirection; + this.rotationDirection = rotationDirection; + this.userSource = userSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java index e2fcf249..9ba042d5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectRelativeMove.java @@ -12,6 +12,7 @@ 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.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; @@ -81,7 +82,7 @@ public class WiredEffectRelativeMove extends InteractionWiredEffect { continue; } - room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false); + WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), null, true, ctx); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java index e43a4a68..9233daed 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSetAltitude.java @@ -12,6 +12,7 @@ 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.WiredMoveCarryHelper; import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredSaveException; @@ -73,7 +74,7 @@ public class WiredEffectSetAltitude extends InteractionWiredEffect { } double nextAltitude = this.computeAltitude(item.getZ()); - room.moveFurniTo(item, tile, item.getRotation(), nextAltitude, null, true, false); + WiredMoveCarryHelper.moveFurni(room, this, item, tile, item.getRotation(), nextAltitude, null, true, ctx); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java index 03ecbf13..591880ac 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectTeleport.java @@ -39,6 +39,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { public static final WiredEffectType type = WiredEffectType.TELEPORT; protected List items; + private boolean fastTeleport = false; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; private int userSource = WiredSourceUtil.SOURCE_TRIGGER; @@ -53,6 +54,10 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } public static void teleportUnitToTile(RoomUnit roomUnit, RoomTile tile) { + teleportUnitToTile(roomUnit, tile, false); + } + + public static void teleportUnitToTile(RoomUnit roomUnit, RoomTile tile, boolean fastTeleport) { if (roomUnit == null || tile == null || roomUnit.isWiredTeleporting) return; @@ -85,9 +90,10 @@ public class WiredEffectTeleport extends InteractionWiredEffect { // makes a temporary effect + int teleportDelay = getTeleportDelay(fastTeleport); + roomUnit.getRoom().unIdle(roomUnit.getRoom().getHabbo(roomUnit)); - room.sendComposer(new RoomUserEffectComposer(roomUnit, 4).compose()); - Emulator.getThreading().run(new SendRoomUnitEffectComposer(room, roomUnit), WiredManager.TELEPORT_DELAY + 1000); + sendTeleportEffect(room, roomUnit, fastTeleport); if (tile == roomUnit.getCurrentLocation()) { return; @@ -110,8 +116,8 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } } - Emulator.getThreading().run(() -> { roomUnit.isWiredTeleporting = true; }, Math.max(0, WiredManager.TELEPORT_DELAY - 500)); - Emulator.getThreading().run(new RoomUnitTeleport(roomUnit, room, tile.x, tile.y, tile.getStackHeight() + (tile.state == RoomTileState.SIT ? -0.5 : 0), roomUnit.getEffectId()), WiredManager.TELEPORT_DELAY); + Emulator.getThreading().run(() -> { roomUnit.isWiredTeleporting = true; }, Math.max(0, teleportDelay - 500)); + Emulator.getThreading().run(new RoomUnitTeleport(roomUnit, room, tile.x, tile.y, tile.getStackHeight() + (tile.state == RoomTileState.SIT ? -0.5 : 0), roomUnit.getEffectId()), teleportDelay); } @Override @@ -137,7 +143,8 @@ public class WiredEffectTeleport extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(2); + message.appendInt(3); + message.appendInt(this.fastTeleport ? 1 : 0); message.appendInt(this.furniSource); message.appendInt(this.userSource); message.appendInt(0); @@ -166,8 +173,15 @@ public class WiredEffectTeleport extends InteractionWiredEffect { @Override public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; - this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + if (params.length > 2) { + this.fastTeleport = params[0] == 1; + this.furniSource = params[1]; + this.userSource = params[2]; + } else { + this.fastTeleport = false; + this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER; + } int itemsCount = settings.getFurniIds().length; @@ -227,7 +241,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { RoomTile tile = room.getLayout().getTile(item.getX(), item.getY()); if (tile != null) { - teleportUnitToTile(roomUnit, tile); + teleportUnitToTile(roomUnit, tile, this.fastTeleport); } } } @@ -244,6 +258,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { return WiredManager.getGson().toJson(new JsonData( this.getDelay(), itemsSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.fastTeleport, this.furniSource, this.userSource )); @@ -257,6 +272,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); + this.fastTeleport = data.fastTeleport; this.furniSource = data.furniSource; this.userSource = data.userSource; for (Integer id: data.itemIds) { @@ -284,6 +300,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { } } } + this.fastTeleport = false; this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; } @@ -292,6 +309,7 @@ public class WiredEffectTeleport extends InteractionWiredEffect { @Override public void onPickUp() { this.items.clear(); + this.fastTeleport = false; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.userSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); @@ -312,15 +330,30 @@ public class WiredEffectTeleport extends InteractionWiredEffect { return COOLDOWN_DEFAULT; } + private static int getTeleportDelay(boolean fastTeleport) { + return fastTeleport ? Math.max(75, WiredManager.TELEPORT_DELAY / 5) : WiredManager.TELEPORT_DELAY; + } + + private static int getTeleportEffectDuration(boolean fastTeleport) { + return fastTeleport ? Math.max(300, (WiredManager.TELEPORT_DELAY + 1000) / 3) : (WiredManager.TELEPORT_DELAY + 1000); + } + + private static void sendTeleportEffect(Room room, RoomUnit roomUnit, boolean fastTeleport) { + room.sendComposer(new RoomUserEffectComposer(roomUnit, 4).compose()); + Emulator.getThreading().run(new SendRoomUnitEffectComposer(room, roomUnit), getTeleportEffectDuration(fastTeleport)); + } + static class JsonData { int delay; List itemIds; + boolean fastTeleport; int furniSource; int userSource; - public JsonData(int delay, List itemIds, int furniSource, int userSource) { + public JsonData(int delay, List itemIds, boolean fastTeleport, int furniSource, int userSource) { this.delay = delay; this.itemIds = itemIds; + this.fastTeleport = fastTeleport; this.furniSource = furniSource; this.userSource = userSource; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java index 09a14d67..5c4e4489 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectToggleFurni.java @@ -39,10 +39,13 @@ import java.util.stream.Collectors; public class WiredEffectToggleFurni extends InteractionWiredEffect { private static final Logger LOGGER = LoggerFactory.getLogger(WiredEffectToggleFurni.class); + private static final int TOGGLE_TYPE_NEXT = 0; + private static final int TOGGLE_TYPE_PREVIOUS = 1; public static final WiredEffectType type = WiredEffectType.TOGGLE_STATE; private final THashSet items; + private int toggleType = TOGGLE_TYPE_NEXT; private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; private static final List> FORBIDDEN_TYPES = new ArrayList>() { @@ -122,7 +125,8 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); + message.appendInt(this.toggleType); message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); @@ -151,7 +155,13 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { @Override public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { int[] params = settings.getIntParams(); - this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + if (params.length > 1) { + this.toggleType = normalizeToggleType(params[0]); + this.furniSource = params[1]; + } else { + this.toggleType = TOGGLE_TYPE_NEXT; + this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER; + } int itemsCount = settings.getFurniIds().length; @@ -204,15 +214,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { try { if (item.getBaseItem().getStateCount() > 1 || item instanceof InteractionGameTimer) { - int state = 0; - if (!item.getExtradata().isEmpty()) { - try { - state = Integer.parseInt(item.getExtradata()); // assumes that extradata is state, could be something else for trophies etc. - } catch (NumberFormatException ignored) { - - } - } - item.onClick(habbo != null && !(item instanceof InteractionGameTimer) ? habbo.getClient() : null, room, new Object[]{state, this.getType()}); + this.toggleItemState(room, habbo, item); } } catch (Exception e) { LOGGER.error("Caught exception", e); @@ -235,6 +237,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { return WiredManager.getGson().toJson(new JsonData( this.getDelay(), new ArrayList<>(this.items).stream().map(HabboItem::getId).collect(Collectors.toList()), + this.toggleType, this.furniSource )); } @@ -247,6 +250,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.setDelay(data.delay); + this.toggleType = normalizeToggleType(data.toggleType); this.furniSource = data.furniSource; for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); @@ -281,6 +285,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { } } } + this.toggleType = TOGGLE_TYPE_NEXT; this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @@ -288,6 +293,7 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { @Override public void onPickUp() { this.items.clear(); + this.toggleType = TOGGLE_TYPE_NEXT; this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; this.setDelay(0); } @@ -297,14 +303,52 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect { return type; } + private int normalizeToggleType(int value) { + return (value == TOGGLE_TYPE_PREVIOUS) ? TOGGLE_TYPE_PREVIOUS : TOGGLE_TYPE_NEXT; + } + + private void toggleItemState(Room room, Habbo habbo, HabboItem item) throws Exception { + if (item.getBaseItem().getStateCount() <= 1) { + return; + } + + int stateCount = item.getBaseItem().getStateCount(); + int currentState = 0; + + if (!item.getExtradata().isEmpty()) { + try { + currentState = Integer.parseInt(item.getExtradata()); + } catch (NumberFormatException ignored) { + if (this.toggleType == TOGGLE_TYPE_NEXT) { + item.onClick(habbo != null ? habbo.getClient() : null, room, new Object[]{0, this.getType()}); + } + return; + } + } + + int nextState = (this.toggleType == TOGGLE_TYPE_PREVIOUS) + ? ((currentState - 1 + stateCount) % stateCount) + : ((currentState + 1) % stateCount); + + if (currentState == nextState) { + return; + } + + item.setExtradata(Integer.toString(nextState)); + item.needsUpdate(true); + room.updateItemState(item); + } + static class JsonData { int delay; List itemIds; + int toggleType; int furniSource; - public JsonData(int delay, List itemIds, int furniSource) { + public JsonData(int delay, List itemIds, int toggleType, int furniSource) { this.delay = delay; this.itemIds = itemIds; + this.toggleType = toggleType; this.furniSource = furniSource; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java index b5d50cc3..2d2491d5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectUserToFurni.java @@ -1,17 +1,39 @@ package com.eu.habbo.habbohotel.items.interactions.wired.effects; +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; +import gnu.trove.procedure.TObjectProcedure; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { + private static final int WALKMODE_IF_CLOSER = 0; + private static final int WALKMODE_CONTINUE = 1; + private static final int WALKMODE_STOP = 2; + public static final WiredEffectType type = WiredEffectType.USER_TO_FURNI; + private int walkMode = WALKMODE_CONTINUE; public WiredEffectUserToFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -30,8 +52,13 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { return; } + RoomTile targetTile = room.getLayout().getTile(item.getX(), item.getY()); + if (targetTile == null) { + return; + } + for (Habbo habbo : this.resolveHabbos(room, ctx)) { - room.teleportHabboToItem(habbo, item); + this.moveHabboSmooth(room, habbo, item, targetTile); } } @@ -45,4 +72,259 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase { public WiredEffectType getType() { return type; } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.furniSource, + this.userSource, + this.walkMode + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items.clear(); + String wiredData = set.getString("wired_data"); + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.setDelay(data.delay); + this.furniSource = data.furniSource; + this.userSource = data.userSource; + this.walkMode = this.normalizeWalkMode((data.walkMode != null) ? data.walkMode : WALKMODE_CONTINUE); + + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } + + return; + } + + String[] wiredDataOld = wiredData.split("\t"); + + if (wiredDataOld.length >= 1) { + this.setDelay(Integer.parseInt(wiredDataOld[0])); + } + + if (wiredDataOld.length == 2 && wiredDataOld[1].contains(";")) { + for (String s : wiredDataOld[1].split(";")) { + HabboItem item = room.getHabboItem(Integer.parseInt(s)); + + if (item != null) { + this.items.add(item); + } + } + } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + this.walkMode = WALKMODE_CONTINUE; + } + + @Override + public void onPickUp() { + super.onPickUp(); + this.walkMode = WALKMODE_CONTINUE; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List itemsSnapshot = new ArrayList<>(this.items); + itemsSnapshot.removeIf(item -> item == null + || item.getRoomId() != this.getRoomId() + || room.getHabboItem(item.getId()) == null); + this.items.clear(); + this.items.addAll(itemsSnapshot); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(itemsSnapshot.size()); + for (HabboItem item : itemsSnapshot) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.furniSource); + message.appendInt(this.userSource); + message.appendInt(this.walkMode); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + + if (this.requiresTriggeringUser()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @Override + public boolean execute(InteractionWiredTrigger object) { + if (!object.isTriggeredByRoomUnit()) { + invalidTriggers.add(object.getBaseItem().getSpriteId()); + } + return true; + } + }); + message.appendInt(invalidTriggers.size()); + for (Integer i : invalidTriggers) { + message.appendInt(i); + } + } else { + message.appendInt(0); + } + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + this.furniSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER; + this.userSource = (settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER; + this.walkMode = this.normalizeWalkMode((settings.getIntParams().length > 2) ? settings.getIntParams()[2] : WALKMODE_CONTINUE); + + if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + throw new WiredSaveException("Room not found"); + } + + List newItems = new ArrayList<>(); + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item == null) { + throw new WiredSaveException(String.format("Item %s not found", itemId)); + } + + newItems.add(item); + } + } + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + return true; + } + + private void moveHabboSmooth(Room room, Habbo habbo, HabboItem item, RoomTile targetTile) { + if (room == null || habbo == null || item == null || targetTile == null || habbo.getRoomUnit() == null) { + return; + } + + RoomUnit roomUnit = habbo.getRoomUnit(); + RoomTile oldLocation = roomUnit.getCurrentLocation(); + RoomTile previousGoal = roomUnit.getGoal(); + boolean wasWalking = roomUnit.isWalking(); + boolean noAnimation = WiredMoveCarryHelper.hasNoAnimationExtra(room, this); + + if (oldLocation == null) { + return; + } + + double newZ = item.getZ() + Item.getCurrentHeight(item); + int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION); + if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, newZ, + roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), animationDuration, noAnimation)) { + return; + } + + this.applyWalkMode(roomUnit, oldLocation, previousGoal, targetTile, wasWalking, + animationDuration); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); + } + + private void applyWalkMode(RoomUnit roomUnit, RoomTile oldLocation, RoomTile previousGoal, RoomTile targetTile, boolean wasWalking, int delay) { + if (roomUnit == null || targetTile == null) { + return; + } + + Runnable applyGoal = () -> { + if (roomUnit.getCurrentLocation() == null + || roomUnit.isWalking() + || roomUnit.hasStatus(RoomUnitStatus.SIT) + || roomUnit.hasStatus(RoomUnitStatus.LAY) + || roomUnit.getCurrentLocation().x != targetTile.x + || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + if (this.walkMode == WALKMODE_STOP || !wasWalking || previousGoal == null) { + roomUnit.setGoalLocation(targetTile); + return; + } + + if (this.walkMode == WALKMODE_IF_CLOSER && !this.isCloserToGoal(oldLocation, targetTile, previousGoal)) { + roomUnit.setGoalLocation(targetTile); + return; + } + + roomUnit.setGoalLocation(previousGoal); + }; + + if (delay > 0) { + Emulator.getThreading().run(applyGoal, delay); + return; + } + + applyGoal.run(); + } + + private boolean isCloserToGoal(RoomTile oldLocation, RoomTile newLocation, RoomTile goalLocation) { + if (oldLocation == null || newLocation == null || goalLocation == null) { + return false; + } + + return this.distanceSquared(newLocation, goalLocation) < this.distanceSquared(oldLocation, goalLocation); + } + + private int distanceSquared(RoomTile first, RoomTile second) { + int dx = first.x - second.x; + int dy = first.y - second.y; + return (dx * dx) + (dy * dy); + } + + private int normalizeWalkMode(int walkMode) { + if (walkMode < WALKMODE_IF_CLOSER || walkMode > WALKMODE_STOP) { + return WALKMODE_CONTINUE; + } + + return walkMode; + } + + static class JsonData { + int delay; + List itemIds; + int furniSource; + int userSource; + Integer walkMode; + + public JsonData(int delay, List itemIds, int furniSource, int userSource, int walkMode) { + this.delay = delay; + this.itemIds = itemIds; + this.furniSource = furniSource; + this.userSource = userSource; + this.walkMode = walkMode; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java new file mode 100644 index 00000000..2c9a1def --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraAnimationTime.java @@ -0,0 +1,125 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraAnimationTime extends InteractionWiredExtra { + public static final int CODE = 60; + public static final int MIN_DURATION_MS = 50; + public static final int MAX_DURATION_MS = 2000; + + private int durationMs = WiredMovementsComposer.DEFAULT_DURATION; + + public WiredExtraAnimationTime(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraAnimationTime(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int value = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : this.durationMs; + + if (value == this.durationMs && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = this.durationMs; + } + } + + this.durationMs = normalizeDuration(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.durationMs)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(1); + message.appendInt(this.durationMs); + 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.durationMs = normalizeDuration((data != null) ? data.durationMs : WiredMovementsComposer.DEFAULT_DURATION); + return; + } + + try { + this.durationMs = normalizeDuration(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.durationMs = WiredMovementsComposer.DEFAULT_DURATION; + } + } + + @Override + public void onPickUp() { + this.durationMs = WiredMovementsComposer.DEFAULT_DURATION; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getDurationMs() { + return this.durationMs; + } + + private static int normalizeDuration(int value) { + return Math.max(MIN_DURATION_MS, Math.min(MAX_DURATION_MS, value)); + } + + static class JsonData { + int durationMs; + + JsonData(int durationMs) { + this.durationMs = durationMs; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java new file mode 100644 index 00000000..293b5fbf --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterFurni.java @@ -0,0 +1,123 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraFilterFurni extends InteractionWiredExtra { + public static final int CODE = 56; + private static final int MAX_FILTER_AMOUNT = 10000; + + private int amount = 0; + + public WiredExtraFilterFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraFilterFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int value = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : 0; + + if (value == 0 && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = 0; + } + } + + this.amount = normalizeAmount(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.amount)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(1); + message.appendInt(this.amount); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.amount = normalizeAmount((data != null) ? data.amount : 0); + return; + } + + try { + this.amount = normalizeAmount(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.amount = 0; + } + } + + @Override + public void onPickUp() { + this.amount = 0; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getAmount() { + return this.amount; + } + + private static int normalizeAmount(int value) { + return Math.max(0, Math.min(MAX_FILTER_AMOUNT, value)); + } + + static class JsonData { + int amount; + + JsonData(int amount) { + this.amount = amount; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java new file mode 100644 index 00000000..bdc9c6c6 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraFilterUser.java @@ -0,0 +1,123 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraFilterUser extends InteractionWiredExtra { + public static final int CODE = 57; + private static final int MAX_FILTER_AMOUNT = 10000; + + private int amount = 0; + + public WiredExtraFilterUser(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraFilterUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int value = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : 0; + + if (value == 0 && settings.getStringParam() != null && !settings.getStringParam().isEmpty()) { + try { + value = Integer.parseInt(settings.getStringParam()); + } catch (NumberFormatException ignored) { + value = 0; + } + } + + this.amount = normalizeAmount(value); + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.amount)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(1); + message.appendInt(this.amount); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.amount = normalizeAmount((data != null) ? data.amount : 0); + return; + } + + try { + this.amount = normalizeAmount(Integer.parseInt(wiredData)); + } catch (NumberFormatException ignored) { + this.amount = 0; + } + } + + @Override + public void onPickUp() { + this.amount = 0; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getAmount() { + return this.amount; + } + + private static int normalizeAmount(int value) { + return Math.max(0, Math.min(MAX_FILTER_AMOUNT, value)); + } + + static class JsonData { + int amount; + + JsonData(int amount) { + this.amount = amount; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java new file mode 100644 index 00000000..401f5cb4 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveCarryUsers.java @@ -0,0 +1,157 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMoveCarryUsers extends InteractionWiredExtra { + public static final int CODE = 58; + public static final int MODE_DIRECTLY_ON_FURNI = 0; + public static final int MODE_SAME_TILE = 1; + public static final int SOURCE_ALL_ROOM_USERS = 900; + + private int carryMode = MODE_DIRECTLY_ON_FURNI; + private int userSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredExtraMoveCarryUsers(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMoveCarryUsers(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.carryMode = this.normalizeCarryMode((params.length > 0) ? params[0] : MODE_DIRECTLY_ON_FURNI); + this.userSource = this.normalizeUserSource((params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER); + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.carryMode, this.userSource)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.carryMode); + message.appendInt(this.userSource); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data != null) { + this.carryMode = this.normalizeCarryMode(data.carryMode); + this.userSource = this.normalizeUserSource(data.userSource); + } + + return; + } + + String[] legacyData = wiredData.split("\t"); + if (legacyData.length > 0) { + try { + this.carryMode = this.normalizeCarryMode(Integer.parseInt(legacyData[0])); + } catch (NumberFormatException ignored) { + this.carryMode = MODE_DIRECTLY_ON_FURNI; + } + } + + if (legacyData.length > 1) { + try { + this.userSource = this.normalizeUserSource(Integer.parseInt(legacyData[1])); + } catch (NumberFormatException ignored) { + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + } + } + + @Override + public void onPickUp() { + this.carryMode = MODE_DIRECTLY_ON_FURNI; + this.userSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public int getCarryMode() { + return this.carryMode; + } + + public int getUserSource() { + return this.userSource; + } + + private int normalizeCarryMode(int value) { + return (value == MODE_SAME_TILE) ? MODE_SAME_TILE : MODE_DIRECTLY_ON_FURNI; + } + + private int normalizeUserSource(int value) { + switch (value) { + case SOURCE_ALL_ROOM_USERS: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int carryMode; + int userSource; + + JsonData(int carryMode, int userSource) { + this.carryMode = carryMode; + this.userSource = userSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java new file mode 100644 index 00000000..3149aae3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMoveNoAnimation.java @@ -0,0 +1,78 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMoveNoAnimation extends InteractionWiredExtra { + public static final int CODE = 59; + + public WiredExtraMoveNoAnimation(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMoveNoAnimation(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + return true; + } + + @Override + public String getWiredData() { + 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 + public void onPickUp() { + + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + static class JsonData { + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java new file mode 100644 index 00000000..cd890418 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/extra/WiredExtraMovePhysics.java @@ -0,0 +1,229 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.extra; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredExtraMovePhysics extends InteractionWiredExtra { + public static final int CODE = 61; + public static final int SOURCE_ALL_ROOM = 900; + + private boolean keepAltitude = false; + private boolean moveThroughFurni = false; + private boolean moveThroughUsers = false; + private boolean blockByFurni = false; + private int moveThroughFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int moveThroughUsersSource = WiredSourceUtil.SOURCE_TRIGGER; + private int blockByFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + + public WiredExtraMovePhysics(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredExtraMovePhysics(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.keepAltitude = readFlag(params, 0); + this.moveThroughFurni = readFlag(params, 1); + this.moveThroughUsers = readFlag(params, 2); + this.blockByFurni = readFlag(params, 3); + this.moveThroughFurniSource = normalizeSource(readInt(params, 4, WiredSourceUtil.SOURCE_TRIGGER)); + this.blockByFurniSource = normalizeSource(readInt(params, 5, WiredSourceUtil.SOURCE_TRIGGER)); + this.moveThroughUsersSource = normalizeSource(readInt(params, 6, WiredSourceUtil.SOURCE_TRIGGER)); + + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.keepAltitude, + this.moveThroughFurni, + this.moveThroughUsers, + this.blockByFurni, + this.moveThroughFurniSource, + this.blockByFurniSource, + this.moveThroughUsersSource)); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(7); + message.appendInt(this.keepAltitude ? 1 : 0); + message.appendInt(this.moveThroughFurni ? 1 : 0); + message.appendInt(this.moveThroughUsers ? 1 : 0); + message.appendInt(this.blockByFurni ? 1 : 0); + message.appendInt(this.moveThroughFurniSource); + message.appendInt(this.blockByFurniSource); + message.appendInt(this.moveThroughUsersSource); + message.appendInt(0); + message.appendInt(CODE); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + + if (data != null) { + this.keepAltitude = data.keepAltitude; + this.moveThroughFurni = data.moveThroughFurni; + this.moveThroughUsers = data.moveThroughUsers; + this.blockByFurni = data.blockByFurni; + this.moveThroughFurniSource = normalizeSource(data.moveThroughFurniSource); + this.blockByFurniSource = normalizeSource(data.blockByFurniSource); + this.moveThroughUsersSource = normalizeSource(data.moveThroughUsersSource); + } + + return; + } + + String[] legacyData = wiredData.split("\t"); + this.keepAltitude = readLegacyFlag(legacyData, 0); + this.moveThroughFurni = readLegacyFlag(legacyData, 1); + this.moveThroughUsers = readLegacyFlag(legacyData, 2); + this.blockByFurni = readLegacyFlag(legacyData, 3); + this.moveThroughFurniSource = normalizeSource(readLegacyInt(legacyData, 4, WiredSourceUtil.SOURCE_TRIGGER)); + this.blockByFurniSource = normalizeSource(readLegacyInt(legacyData, 5, WiredSourceUtil.SOURCE_TRIGGER)); + this.moveThroughUsersSource = normalizeSource(readLegacyInt(legacyData, 6, WiredSourceUtil.SOURCE_TRIGGER)); + } + + @Override + public void onPickUp() { + this.keepAltitude = false; + this.moveThroughFurni = false; + this.moveThroughUsers = false; + this.blockByFurni = false; + this.moveThroughFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.moveThroughUsersSource = WiredSourceUtil.SOURCE_TRIGGER; + this.blockByFurniSource = WiredSourceUtil.SOURCE_TRIGGER; + } + + @Override + public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception { + + } + + @Override + public boolean hasConfiguration() { + return true; + } + + public boolean isKeepAltitude() { + return this.keepAltitude; + } + + public boolean isMoveThroughFurni() { + return this.moveThroughFurni; + } + + public boolean isMoveThroughUsers() { + return this.moveThroughUsers; + } + + public boolean isBlockByFurni() { + return this.blockByFurni; + } + + public int getMoveThroughFurniSource() { + return this.moveThroughFurniSource; + } + + public int getMoveThroughUsersSource() { + return this.moveThroughUsersSource; + } + + public int getBlockByFurniSource() { + return this.blockByFurniSource; + } + + private static boolean readFlag(int[] params, int index) { + return readInt(params, index, 0) == 1; + } + + private static int readInt(int[] params, int index, int fallback) { + return (params.length > index) ? params[index] : fallback; + } + + private static boolean readLegacyFlag(String[] data, int index) { + return readLegacyInt(data, index, 0) == 1; + } + + private static int readLegacyInt(String[] data, int index, int fallback) { + if (data.length <= index) { + return fallback; + } + + try { + return Integer.parseInt(data[index]); + } catch (NumberFormatException ignored) { + return fallback; + } + } + + private static int normalizeSource(int value) { + switch (value) { + case SOURCE_ALL_ROOM: + case WiredSourceUtil.SOURCE_TRIGGER: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + boolean keepAltitude; + boolean moveThroughFurni; + boolean moveThroughUsers; + boolean blockByFurni; + int moveThroughFurniSource; + int blockByFurniSource; + int moveThroughUsersSource; + + JsonData(boolean keepAltitude, boolean moveThroughFurni, boolean moveThroughUsers, boolean blockByFurni, int moveThroughFurniSource, int blockByFurniSource, int moveThroughUsersSource) { + this.keepAltitude = keepAltitude; + this.moveThroughFurni = moveThroughFurni; + this.moveThroughUsers = moveThroughUsers; + this.blockByFurni = blockByFurni; + this.moveThroughFurniSource = moveThroughFurniSource; + this.blockByFurniSource = blockByFurniSource; + this.moveThroughUsersSource = moveThroughUsersSource; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java index 6db447f7..25bbb7b4 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/interfaces/InteractionWiredMatchFurniSettings.java @@ -8,4 +8,5 @@ public interface InteractionWiredMatchFurniSettings { public boolean shouldMatchState(); public boolean shouldMatchRotation(); public boolean shouldMatchPosition(); + public boolean shouldMatchAltitude(); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java new file mode 100644 index 00000000..a2b6473a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniAltitude.java @@ -0,0 +1,223 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectFurniAltitude extends InteractionWiredEffect { + private static final int COMPARISON_LESS = 0; + private static final int COMPARISON_EQUAL = 1; + private static final int COMPARISON_GREATER = 2; + + public static final WiredEffectType type = WiredEffectType.FURNI_ALTITUDE_SELECTOR; + + private int comparison = COMPARISON_EQUAL; + private double altitude = 0.0D; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniAltitude(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniAltitude(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set matchingItems = new LinkedHashSet<>(); + + room.getFloorItems().forEach(item -> { + if (item == null || item instanceof InteractionWired) { + return; + } + + if (this.matchesAltitude(item)) { + matchingItems.add(item); + } + }); + + Set result = new LinkedHashSet<>(matchingItems); + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + int[] params = settings.getIntParams(); + if (params == null || params.length < 3) { + throw new WiredSaveException("wf_slc_furni_altitude requires 3 int params: comparison, filterExisting, invert"); + } + + this.comparison = this.normalizeComparison(params[0]); + this.filterExisting = params[1] == 1; + this.invert = params[2] == 1; + this.altitude = this.parseAltitudeOrDefault(settings.getStringParam()); + this.setDelay(settings.getDelay()); + + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.comparison, + this.formatAltitude(this.altitude), + this.filterExisting, + this.invert, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.comparison = this.normalizeComparison(data.comparison); + this.altitude = this.parseAltitudeOrDefault(data.altitude); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.comparison = COMPARISON_EQUAL; + this.altitude = 0.0D; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(this.formatAltitude(this.altitude)); + message.appendInt(3); + message.appendInt(this.comparison); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private boolean matchesAltitude(HabboItem item) { + if (item == null) { + return false; + } + + double normalizedAltitude = this.normalizeAltitude(item.getZ()); + + switch (this.comparison) { + case COMPARISON_LESS: + return normalizedAltitude < this.altitude; + case COMPARISON_GREATER: + return normalizedAltitude > this.altitude; + default: + return BigDecimal.valueOf(normalizedAltitude).compareTo(BigDecimal.valueOf(this.altitude)) == 0; + } + } + + private int normalizeComparison(int value) { + if (value < COMPARISON_LESS || value > COMPARISON_GREATER) { + return COMPARISON_EQUAL; + } + + return value; + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + private double parseAltitudeOrDefault(String value) { + if (value == null || value.trim().isEmpty()) { + return 0.0D; + } + + try { + return this.normalizeAltitude(new BigDecimal(value.trim()).doubleValue()); + } catch (NumberFormatException exception) { + return 0.0D; + } + } + + private String formatAltitude(double value) { + BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros(); + return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString(); + } + + static class JsonData { + int comparison; + String altitude; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int comparison, String altitude, boolean filterExisting, boolean invert, int delay) { + this.comparison = comparison; + this.altitude = altitude; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java index 30db5f3d..8b223fe5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniArea.java @@ -28,6 +28,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { private int rootY = 0; private int areaWidth = 0; private int areaHeight = 0; + private boolean filterExisting = false; + private boolean invert = false; public WiredEffectFurniArea(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -43,9 +45,12 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { if (room == null || areaWidth <= 0 || areaHeight <= 0) return; List furniInArea = getFurniInArea(room); - if (!furniInArea.isEmpty()) { - ctx.targets().setItems(furniInArea); - } + ctx.targets().setItems(this.applySelectorModifiers( + furniInArea, + this.getSelectableFloorItems(room), + ctx.targets().items(), + this.filterExisting, + this.invert)); } private List getFurniInArea(Room room) { @@ -78,6 +83,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = params[1]; this.areaWidth = params[2]; this.areaHeight = params[3]; + this.filterExisting = params.length >= 5 && params[4] == 1; + this.invert = params.length >= 6 && params[5] == 1; this.setDelay(settings.getDelay()); return true; @@ -95,7 +102,7 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, getDelay())); + return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, getDelay())); } @Override @@ -108,6 +115,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = data.rootY; this.areaWidth = data.width; this.areaHeight = data.height; + this.filterExisting = data.filterExisting; + this.invert = data.invert; this.setDelay(data.delay); } } @@ -118,6 +127,8 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { this.rootY = 0; this.areaWidth = 0; this.areaHeight = 0; + this.filterExisting = false; + this.invert = false; this.setDelay(0); } @@ -131,11 +142,13 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(4); + message.appendInt(6); message.appendInt(this.rootX); message.appendInt(this.rootY); message.appendInt(this.areaWidth); message.appendInt(this.areaHeight); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); @@ -153,13 +166,17 @@ public class WiredEffectFurniArea extends InteractionWiredEffect { int rootY; int width; int height; + boolean filterExisting; + boolean invert; int delay; - JsonData(int rootX, int rootY, int width, int height, int delay) { + JsonData(int rootX, int rootY, int width, int height, boolean filterExisting, boolean invert, int delay) { this.rootX = rootX; this.rootY = rootY; this.width = width; this.height = height; + this.filterExisting = filterExisting; + this.invert = invert; this.delay = delay; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java index 14bae162..e936fabe 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniByType.java @@ -59,33 +59,19 @@ public class WiredEffectFurniByType extends InteractionWiredEffect { matchKeys.add(key); } - Set result = new LinkedHashSet<>(); + Set matched = new LinkedHashSet<>(); room.getFloorItems().forEach(item -> { if (item instanceof InteractionWired) return; String key = matchState ? item.getBaseItem().getId() + ":" + item.getExtradata() : String.valueOf(item.getBaseItem().getId()); if (matchKeys.contains(key)) { - result.add(item); + matched.add(item); } }); - if (filterExisting) { - result.retainAll(ctx.targets().items()); - } - - if (invert) { - Set all = new LinkedHashSet<>(); - room.getFloorItems().forEach(item -> { - if (!(item instanceof InteractionWired)) all.add(item); - }); - all.removeAll(result); - if (!all.isEmpty()) { - ctx.targets().setItems(all); - } - } else if (!result.isEmpty()) { - ctx.targets().setItems(result); - } + Set result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert); + ctx.targets().setItems(result); } private List resolveSourceFurni(WiredContext ctx, Room room) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java index f3439707..2dd2c5b5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniNeighborhood.java @@ -42,6 +42,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { private int sourceType = SOURCE_USER_TRIGGER; private boolean filterExisting = false; private boolean invert = false; + private int targetOffsetX = 0; + private int targetOffsetY = 0; private List tileOffsets = new ArrayList<>(); private List pickedFurniIds = new ArrayList<>(); @@ -68,8 +70,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { for (int[] src : sourcePositions) { LOGGER.info("[FurniNeighborhood] Source: ({},{}), offsets: {}", src[0], src[1], tileOffsets.size()); for (int[] offset : tileOffsets) { - int tx = src[0] + offset[0]; - int ty = src[1] + offset[1]; + int tx = src[0] + (offset[0] - this.targetOffsetX); + int ty = src[1] + (offset[1] - this.targetOffsetY); for (HabboItem item : room.getItemsAt(tx, ty)) { if (item == null) continue; totalRaw++; @@ -87,18 +89,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { } LOGGER.info("[FurniNeighborhood] Raw={}, wiredSkipped={}, kept={}", totalRaw, wiredSkipped, result.size()); - if (filterExisting) { - result.retainAll(ctx.targets().items()); - } - - if (invert) { - Set all = new LinkedHashSet<>(); - room.getFloorItems().forEach(item -> { - if (!(item instanceof InteractionWired)) all.add(item); - }); - all.removeAll(result); - result = all; - } + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert); // Always set the selector result — even if empty. // An empty result means no items matched the neighborhood, so downstream @@ -158,12 +149,14 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = params[0]; this.filterExisting = params.length > 1 && params[1] == 1; this.invert = params.length > 2 && params[2] == 1; + this.targetOffsetX = params.length > 3 ? params[3] : 0; + this.targetOffsetY = params.length > 4 ? params[4] : 0; this.tileOffsets = new ArrayList<>(); - if (params.length > 3) { - int n = params[3]; + if (params.length > 5) { + int n = params[5]; for (int i = 0; i < n && i < MAX_TILE_OFFSETS; i++) { - int xi = 4 + i * 2; + int xi = 6 + i * 2; if (xi + 1 < params.length) { tileOffsets.add(new int[]{ params[xi], params[xi + 1] }); } @@ -180,8 +173,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.setDelay(settings.getDelay()); - LOGGER.info("[FurniNeighborhood] saveData: sourceType={}, filterExisting={}, invert={}, offsets={}, pickedFurniIds={}", - sourceType, filterExisting, invert, tileOffsets.size(), pickedFurniIds); + LOGGER.info("[FurniNeighborhood] saveData: sourceType={}, filterExisting={}, invert={}, target=({},{}), offsets={}, pickedFurniIds={}", + sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets.size(), pickedFurniIds); for (int[] o : tileOffsets) { LOGGER.info("[FurniNeighborhood] offset: ({}, {})", o[0], o[1]); } @@ -208,11 +201,13 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { message.appendInt(this.getId()); message.appendString(""); - int paramCount = 4 + tileOffsets.size() * 2; + int paramCount = 6 + tileOffsets.size() * 2; message.appendInt(paramCount); message.appendInt(sourceType); message.appendInt(filterExisting ? 1 : 0); message.appendInt(invert ? 1 : 0); + message.appendInt(targetOffsetX); + message.appendInt(targetOffsetY); message.appendInt(tileOffsets.size()); for (int[] offset : tileOffsets) { message.appendInt(offset[0]); @@ -236,7 +231,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { @Override public String getWiredData() { return WiredManager.getGson().toJson( - new JsonData(sourceType, filterExisting, invert, tileOffsets, pickedFurniIds, getDelay())); + new JsonData(sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets, pickedFurniIds, getDelay())); } @Override @@ -247,6 +242,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = data.sourceType; this.filterExisting = data.filterExisting; this.invert = data.invert; + this.targetOffsetX = data.targetOffsetX; + this.targetOffsetY = data.targetOffsetY; this.tileOffsets = data.tileOffsets != null ? data.tileOffsets : new ArrayList<>(); this.pickedFurniIds = data.pickedFurniIds != null ? data.pickedFurniIds : new ArrayList<>(); this.setDelay(data.delay); @@ -258,6 +255,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { this.sourceType = SOURCE_USER_TRIGGER; this.filterExisting = false; this.invert = false; + this.targetOffsetX = 0; + this.targetOffsetY = 0; this.tileOffsets = new ArrayList<>(); this.pickedFurniIds = new ArrayList<>(); this.setDelay(0); @@ -270,15 +269,19 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect { int sourceType; boolean filterExisting; boolean invert; + int targetOffsetX; + int targetOffsetY; List tileOffsets; List pickedFurniIds; int delay; - JsonData(int sourceType, boolean filterExisting, boolean invert, + JsonData(int sourceType, boolean filterExisting, boolean invert, int targetOffsetX, int targetOffsetY, List tileOffsets, List pickedFurniIds, int delay) { this.sourceType = sourceType; this.filterExisting = filterExisting; this.invert = invert; + this.targetOffsetX = targetOffsetX; + this.targetOffsetY = targetOffsetY; this.tileOffsets = tileOffsets; this.pickedFurniIds = pickedFurniIds; this.delay = delay; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java new file mode 100644 index 00000000..0ec8730c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniOnFurni.java @@ -0,0 +1,335 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectFurniOnFurni extends InteractionWiredEffect { + private static final double EPSILON = 0.0001D; + + private static final int SELECT_FURNI_ABOVE = 0; + private static final int SELECT_FURNI_BELOW = 1; + private static final int SELECT_FURNI_SAME_HEIGHT = 2; + private static final int SELECT_ALL_FURNI_ON_TILE = 3; + + public static final WiredEffectType type = WiredEffectType.FURNI_ON_FURNI_SELECTOR; + + private final Set items = new LinkedHashSet<>(); + private int selectionType = SELECT_FURNI_ABOVE; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniOnFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniOnFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null || room.getLayout() == null) { + ctx.targets().setItems(Collections.emptySet()); + return; + } + + this.refresh(room); + + List sourceItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (sourceItems.isEmpty()) { + ctx.targets().setItems(Collections.emptySet()); + return; + } + + Set result = new LinkedHashSet<>(); + + for (HabboItem sourceItem : sourceItems) { + result.addAll(this.resolveRelatedItems(room, sourceItem)); + } + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.selectionType = (params.length > 0) ? this.normalizeSelectionType(params[0]) : SELECT_FURNI_ABOVE; + this.furniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = params.length > 2 && params[2] == 1; + this.invert = params.length > 3 && params[3] == 1; + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + this.items.clear(); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + + if (room == null) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item != null) { + this.items.add(item); + } + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + this.refresh(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId())); + + return WiredManager.getGson().toJson(new JsonData( + this.selectionType, + this.furniSource, + this.filterExisting, + this.invert, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.selectionType = this.normalizeSelectionType(data.selectionType); + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + + if (room == null || data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.selectionType = SELECT_FURNI_ABOVE; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.selectionType); + message.appendInt(this.furniSource); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private Set resolveRelatedItems(Room room, HabboItem sourceItem) { + Set result = new LinkedHashSet<>(); + + if (sourceItem == null) { + return result; + } + + RoomTile baseTile = room.getLayout().getTile(sourceItem.getX(), sourceItem.getY()); + if (baseTile == null) { + return result; + } + + Set occupiedTiles = room.getLayout().getTilesAt(baseTile, sourceItem.getBaseItem().getWidth(), sourceItem.getBaseItem().getLength(), sourceItem.getRotation()); + if (occupiedTiles == null) { + return result; + } + + double sourceBase = this.normalizeAltitude(sourceItem.getZ()); + double sourceTop = this.normalizeAltitude(sourceItem.getZ() + Item.getCurrentHeight(sourceItem)); + + for (RoomTile tile : occupiedTiles) { + if (tile == null) { + continue; + } + + for (HabboItem matchedItem : room.getItemsAt(tile)) { + if (matchedItem == null || matchedItem instanceof InteractionWired) { + continue; + } + + if (matchedItem == sourceItem) { + if (this.selectionType == SELECT_FURNI_SAME_HEIGHT || this.selectionType == SELECT_ALL_FURNI_ON_TILE) { + result.add(matchedItem); + } + continue; + } + + if (this.matchesSelectionType(sourceBase, sourceTop, matchedItem)) { + result.add(matchedItem); + } + } + } + + return result; + } + + private boolean matchesSelectionType(double sourceBase, double sourceTop, HabboItem matchedItem) { + double matchedBase = this.normalizeAltitude(matchedItem.getZ()); + double matchedTop = this.normalizeAltitude(matchedItem.getZ() + Item.getCurrentHeight(matchedItem)); + + switch (this.selectionType) { + case SELECT_FURNI_BELOW: + return matchedTop <= (sourceBase + EPSILON); + case SELECT_FURNI_SAME_HEIGHT: + return BigDecimal.valueOf(matchedBase).compareTo(BigDecimal.valueOf(sourceBase)) == 0; + case SELECT_ALL_FURNI_ON_TILE: + return true; + case SELECT_FURNI_ABOVE: + default: + return matchedBase >= (sourceTop - EPSILON); + } + } + + private void refresh(Room room) { + Set invalidItems = new LinkedHashSet<>(); + + if (room == null) { + invalidItems.addAll(this.items); + } else { + for (HabboItem item : this.items) { + if (room.getHabboItem(item.getId()) == null) { + invalidItems.add(item); + } + } + } + + this.items.removeAll(invalidItems); + } + + private int normalizeSelectionType(int value) { + if (value < SELECT_FURNI_ABOVE || value > SELECT_ALL_FURNI_ON_TILE) { + return SELECT_FURNI_ABOVE; + } + + return value; + } + + private int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + private double normalizeAltitude(double value) { + double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value)); + return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + static class JsonData { + int selectionType; + int furniSource; + boolean filterExisting; + boolean invert; + List itemIds; + int delay; + + JsonData(int selectionType, int furniSource, boolean filterExisting, boolean invert, List itemIds, int delay) { + this.selectionType = selectionType; + this.furniSource = furniSource; + this.filterExisting = filterExisting; + this.invert = invert; + this.itemIds = itemIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java new file mode 100644 index 00000000..53a3c92c --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniPicks.java @@ -0,0 +1,167 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectFurniPicks extends InteractionWiredEffect { + private static final int MAX_PICKED_FURNI = 20; + + public static final WiredEffectType type = WiredEffectType.FURNI_PICKS_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + private List pickedFurniIds = new ArrayList<>(); + + public WiredEffectFurniPicks(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniPicks(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = this.pickedFurniIds.stream() + .map(room::getHabboItem) + .filter(item -> item != null && !(item instanceof InteractionWired)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + + this.pickedFurniIds = new ArrayList<>(); + if (settings.getFurniIds() != null) { + for (int id : settings.getFurniIds()) { + if (this.pickedFurniIds.size() >= MAX_PICKED_FURNI) break; + this.pickedFurniIds.add(id); + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.filterExisting, + this.invert, + this.pickedFurniIds, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.pickedFurniIds = (data.pickedFurniIds != null) ? data.pickedFurniIds : new ArrayList<>(); + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.pickedFurniIds = new ArrayList<>(); + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(true); + message.appendInt(MAX_PICKED_FURNI); + + if (!this.pickedFurniIds.isEmpty()) { + message.appendInt(this.pickedFurniIds.size()); + this.pickedFurniIds.forEach(message::appendInt); + } else { + message.appendInt(0); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + List pickedFurniIds; + int delay; + + JsonData(boolean filterExisting, boolean invert, List pickedFurniIds, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.pickedFurniIds = pickedFurniIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java new file mode 100644 index 00000000..2dc15691 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectFurniSignal.java @@ -0,0 +1,142 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWired; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class WiredEffectFurniSignal extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.FURNI_SIGNAL_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectFurniSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectFurniSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { + List signalItems = WiredSourceUtil.resolveItems(ctx, WiredSourceUtil.SOURCE_SIGNAL, null); + Set matched = signalItems.stream() + .filter(item -> item != null && !(item instanceof InteractionWired)) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + + result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert); + } + + ctx.targets().setItems(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + int delay; + + JsonData(boolean filterExisting, boolean invert, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java index 46984906..b03f4bb7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersArea.java @@ -6,7 +6,6 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; -import com.eu.habbo.habbohotel.rooms.RoomUnitType; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; import com.eu.habbo.habbohotel.wired.core.WiredManager; @@ -29,8 +28,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { private int areaHeight = 0; private boolean filterExisting = false; private boolean invert = false; - private boolean excludeBots = false; - private boolean excludePets = false; public WiredEffectUsersArea(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -50,23 +47,15 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { List usersInArea = new ArrayList<>(); for (RoomUnit unit : room.getRoomUnits()) { - if (excludeBots && unit.getRoomUnitType() == RoomUnitType.BOT) continue; - if (excludePets && unit.getRoomUnitType() == RoomUnitType.PET) continue; int x = unit.getX(); int y = unit.getY(); boolean inArea = x >= rootX && x <= maxX && y >= rootY && y <= maxY; - if (invert ? !inArea : inArea) { + if (inArea) { usersInArea.add(unit); } } - if (filterExisting) { - usersInArea.retainAll(ctx.targets().users()); - } - - if (!usersInArea.isEmpty()) { - ctx.targets().setUsers(usersInArea); - } + ctx.targets().setUsers(this.applySelectorModifiers(usersInArea, room.getRoomUnits(), ctx.targets().users(), filterExisting, invert)); } @Override @@ -82,8 +71,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = params[3]; this.filterExisting = params.length >= 5 && params[4] == 1; this.invert = params.length >= 6 && params[5] == 1; - this.excludeBots = params.length >= 7 && params[6] == 1; - this.excludePets = params.length >= 8 && params[7] == 1; this.setDelay(settings.getDelay()); return true; @@ -101,7 +88,7 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, excludeBots, excludePets, getDelay())); + return WiredManager.getGson().toJson(new JsonData(rootX, rootY, areaWidth, areaHeight, filterExisting, invert, getDelay())); } @Override @@ -116,8 +103,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = data.height; this.filterExisting = data.filterExisting; this.invert = data.invert; - this.excludeBots = data.excludeBots; - this.excludePets = data.excludePets; this.setDelay(data.delay); } } @@ -130,8 +115,6 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { this.areaHeight = 0; this.filterExisting = false; this.invert = false; - this.excludeBots = false; - this.excludePets = false; this.setDelay(0); } @@ -145,15 +128,13 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { message.appendInt(this.getId()); message.appendString(""); - message.appendInt(8); + message.appendInt(6); message.appendInt(this.rootX); message.appendInt(this.rootY); message.appendInt(this.areaWidth); message.appendInt(this.areaHeight); message.appendInt(this.filterExisting ? 1 : 0); message.appendInt(this.invert ? 1 : 0); - message.appendInt(this.excludeBots ? 1 : 0); - message.appendInt(this.excludePets ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); @@ -173,19 +154,15 @@ public class WiredEffectUsersArea extends InteractionWiredEffect { int height; boolean filterExisting; boolean invert; - boolean excludeBots; - boolean excludePets; int delay; - JsonData(int rootX, int rootY, int width, int height, boolean filterExisting, boolean invert, boolean excludeBots, boolean excludePets, int delay) { + JsonData(int rootX, int rootY, int width, int height, boolean filterExisting, boolean invert, int delay) { this.rootX = rootX; this.rootY = rootY; this.width = width; this.height = height; this.filterExisting = filterExisting; this.invert = invert; - this.excludeBots = excludeBots; - this.excludePets = excludePets; this.delay = delay; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java new file mode 100644 index 00000000..3f88d99a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByAction.java @@ -0,0 +1,333 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.WiredUserActionType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersByAction extends InteractionWiredEffect { + private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id"; + private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter"; + private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp"; + private static final long TRANSIENT_ACTION_WINDOW_MS = 5_000L; + private static final int DEFAULT_ACTION = WiredUserActionType.WAVE; + + public static final WiredEffectType type = WiredEffectType.USERS_BY_ACTION_SELECTOR; + + private int selectedAction = DEFAULT_ACTION; + private boolean signFilterEnabled = false; + private int signId = 0; + private boolean danceFilterEnabled = false; + private int danceId = 1; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByAction(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesAction(ctx, roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.onPickUp(); + + if (params.length > 0) this.selectedAction = this.normalizeAction(params[0]); + if (params.length > 1) this.signFilterEnabled = (params[1] == 1); + if (params.length > 2) this.signId = this.normalizeSignId(params[2]); + if (params.length > 3) this.danceFilterEnabled = (params[3] == 1); + if (params.length > 4) this.danceId = this.normalizeDanceId(params[4]); + if (params.length > 5) this.filterExisting = (params[5] == 1); + if (params.length > 6) this.invert = (params[6] == 1); + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData( + this.selectedAction, + this.signFilterEnabled, + this.signId, + this.danceFilterEnabled, + this.danceId, + this.filterExisting, + this.invert, + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.selectedAction = this.normalizeAction(data.selectedAction); + this.signFilterEnabled = data.signFilterEnabled; + this.signId = this.normalizeSignId(data.signId); + this.danceFilterEnabled = data.danceFilterEnabled; + this.danceId = this.normalizeDanceId(data.danceId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.selectedAction = DEFAULT_ACTION; + this.signFilterEnabled = false; + this.signId = 0; + this.danceFilterEnabled = false; + this.danceId = 1; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(7); + message.appendInt(this.selectedAction); + message.appendInt(this.signFilterEnabled ? 1 : 0); + message.appendInt(this.signId); + message.appendInt(this.danceFilterEnabled ? 1 : 0); + message.appendInt(this.danceId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeAction(int action) { + switch (action) { + case WiredUserActionType.WAVE: + case WiredUserActionType.BLOW_KISS: + case WiredUserActionType.LAUGH: + case WiredUserActionType.AWAKE: + case WiredUserActionType.RELAX: + case WiredUserActionType.SIT: + case WiredUserActionType.STAND: + case WiredUserActionType.LAY: + case WiredUserActionType.SIGN: + case WiredUserActionType.DANCE: + case WiredUserActionType.THUMB_UP: + return action; + default: + return DEFAULT_ACTION; + } + } + + private int normalizeSignId(int value) { + return (value < 0 || value > 17) ? 0 : value; + } + + private int normalizeDanceId(int value) { + return (value < 1 || value > 4) ? 1 : value; + } + + private boolean matchesAction(WiredContext ctx, RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + if (this.matchesEventAction(ctx, roomUnit)) { + return true; + } + + if (this.matchesCurrentState(roomUnit)) { + return true; + } + + return this.matchesRecentAction(roomUnit); + } + + private boolean matchesEventAction(WiredContext ctx, RoomUnit roomUnit) { + RoomUnit actor = ctx.actor().orElse(null); + + if (actor == null || actor.getId() != roomUnit.getId()) { + return false; + } + + if (ctx.eventType() != WiredEvent.Type.USER_PERFORMS_ACTION) { + return false; + } + + return this.matchesConfiguredAction(ctx.event().getActionId(), ctx.event().getActionParameter()); + } + + private boolean matchesCurrentState(RoomUnit roomUnit) { + switch (this.selectedAction) { + case WiredUserActionType.SIT: + return roomUnit.hasStatus(RoomUnitStatus.SIT); + case WiredUserActionType.LAY: + return roomUnit.hasStatus(RoomUnitStatus.LAY); + case WiredUserActionType.RELAX: + return roomUnit.isIdle(); + case WiredUserActionType.SIGN: + return this.matchesSignState(roomUnit); + case WiredUserActionType.DANCE: + return this.matchesDanceState(roomUnit); + default: + return false; + } + } + + private boolean matchesRecentAction(RoomUnit roomUnit) { + Object actionValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_ID); + Object parameterValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_PARAMETER); + Object timestampValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_TIMESTAMP); + + if (!(actionValue instanceof Integer) || !(timestampValue instanceof Long)) { + return false; + } + + long timestamp = (Long) timestampValue; + if ((System.currentTimeMillis() - timestamp) > TRANSIENT_ACTION_WINDOW_MS) { + return false; + } + + int actionId = (Integer) actionValue; + int parameter = (parameterValue instanceof Integer) ? (Integer) parameterValue : -1; + + return this.matchesConfiguredAction(actionId, parameter); + } + + private boolean matchesConfiguredAction(int actionId, int actionParameter) { + if (actionId != this.selectedAction) { + return false; + } + + if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) { + return actionParameter == this.signId; + } + + if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) { + return actionParameter == this.danceId; + } + + return true; + } + + private boolean matchesSignState(RoomUnit roomUnit) { + String signStatus = roomUnit.getStatus(RoomUnitStatus.SIGN); + if (signStatus == null) { + return false; + } + + if (!this.signFilterEnabled) { + return true; + } + + try { + return Integer.parseInt(signStatus) == this.signId; + } catch (NumberFormatException ignored) { + return false; + } + } + + private boolean matchesDanceState(RoomUnit roomUnit) { + int currentDance = roomUnit.getDanceType().getType(); + if (currentDance <= 0) { + return false; + } + + if (!this.danceFilterEnabled) { + return true; + } + + return currentDance == this.danceId; + } + + static class JsonData { + int selectedAction; + boolean signFilterEnabled; + int signId; + boolean danceFilterEnabled; + int danceId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId, boolean filterExisting, boolean invert, int delay) { + this.selectedAction = selectedAction; + this.signFilterEnabled = signFilterEnabled; + this.signId = signId; + this.danceFilterEnabled = danceFilterEnabled; + this.danceId = danceId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java new file mode 100644 index 00000000..a8dc2ef8 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByName.java @@ -0,0 +1,202 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersByName extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_BY_NAME_SELECTOR; + + private String namesText = ""; + private Set usernames = new LinkedHashSet<>(); + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByName(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByName(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + for (Habbo habbo : room.getHabbos()) { + if (habbo == null || habbo.getHabboInfo() == null || habbo.getRoomUnit() == null) { + continue; + } + + String username = habbo.getHabboInfo().getUsername(); + if (username == null) { + continue; + } + + if (this.usernames.contains(username.trim().toLowerCase(Locale.ROOT))) { + result.add(habbo.getRoomUnit()); + } + } + + Set availableUsers = room.getHabbos().stream() + .filter(habbo -> habbo != null && habbo.getRoomUnit() != null) + .map(Habbo::getRoomUnit) + .collect(Collectors.toCollection(LinkedHashSet::new)); + result = this.applySelectorModifiers(result, availableUsers, ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.namesText = this.normalizeNamesText(settings.getStringParam()); + this.usernames = this.parseUsernames(this.namesText); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.namesText, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || wiredData.isEmpty()) { + return; + } + + if (wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.namesText = this.normalizeNamesText(data.namesText); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } else { + this.namesText = this.normalizeNamesText(wiredData); + } + + this.usernames = this.parseUsernames(this.namesText); + } + + @Override + public void onPickUp() { + this.namesText = ""; + this.usernames = new LinkedHashSet<>(); + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(this.namesText); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private String normalizeNamesText(String value) { + if (value == null || value.trim().isEmpty()) { + return ""; + } + + Set normalizedLines = new LinkedHashSet<>(); + + for (String line : value.split("\\R")) { + String normalized = line.trim(); + if (!normalized.isEmpty()) { + normalizedLines.add(normalized); + } + } + + return normalizedLines.stream().collect(Collectors.joining("\n")); + } + + private Set parseUsernames(String value) { + Set result = new LinkedHashSet<>(); + + if (value == null || value.trim().isEmpty()) { + return result; + } + + for (String line : value.split("\\R")) { + String normalized = line.trim(); + if (!normalized.isEmpty()) { + result.add(normalized.toLowerCase(Locale.ROOT)); + } + } + + return result; + } + + static class JsonData { + String namesText; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(String namesText, boolean filterExisting, boolean invert, int delay) { + this.namesText = namesText; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java new file mode 100644 index 00000000..b9280099 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersByType.java @@ -0,0 +1,167 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersByType extends InteractionWiredEffect { + private static final int ENTITY_HABBO = 1; + private static final int ENTITY_PET = 2; + private static final int ENTITY_BOT = 4; + + public static final WiredEffectType type = WiredEffectType.USERS_BY_TYPE_SELECTOR; + + private int entityType = ENTITY_HABBO; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersByType(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersByType(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesType(roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.entityType = (params.length > 0) ? this.normalizeEntityType(params[0]) : ENTITY_HABBO; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.entityType, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.entityType = this.normalizeEntityType(data.entityType); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.entityType = ENTITY_HABBO; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.entityType); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeEntityType(int value) { + switch (value) { + case ENTITY_PET: + case ENTITY_BOT: + return value; + default: + return ENTITY_HABBO; + } + } + + private boolean matchesType(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + RoomUnitType roomUnitType = roomUnit.getRoomUnitType(); + return roomUnitType != null && roomUnitType.getTypeId() == this.entityType; + } + + static class JsonData { + int entityType; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int entityType, boolean filterExisting, boolean invert, int delay) { + this.entityType = entityType; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java new file mode 100644 index 00000000..f74de64a --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersGroup.java @@ -0,0 +1,179 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersGroup extends InteractionWiredEffect { + private static final int GROUP_CURRENT_ROOM = 0; + private static final int GROUP_SELECTED = 1; + + public static final WiredEffectType type = WiredEffectType.USERS_GROUP_SELECTOR; + + private int groupType = GROUP_CURRENT_ROOM; + private int selectedGroupId = 0; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersGroup(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersGroup(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + int targetGroupId = this.resolveTargetGroupId(room); + Set result = new LinkedHashSet<>(); + + if (targetGroupId > 0) { + for (Habbo habbo : room.getHabbos()) { + if (habbo == null || habbo.getRoomUnit() == null || habbo.getHabboStats() == null) { + continue; + } + + if (habbo.getHabboStats().hasGuild(targetGroupId)) { + result.add(habbo.getRoomUnit()); + } + } + } + + Set availableUsers = room.getHabbos().stream() + .filter(habbo -> habbo != null && habbo.getRoomUnit() != null) + .map(Habbo::getRoomUnit) + .collect(Collectors.toCollection(LinkedHashSet::new)); + result = this.applySelectorModifiers(result, availableUsers, ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.groupType = (params.length > 0) ? this.normalizeGroupType(params[0]) : GROUP_CURRENT_ROOM; + this.selectedGroupId = (params.length > 1) ? Math.max(0, params[1]) : 0; + this.filterExisting = params.length > 2 && params[2] == 1; + this.invert = params.length > 3 && params[3] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.groupType, this.selectedGroupId, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.groupType = this.normalizeGroupType(data.groupType); + this.selectedGroupId = Math.max(0, data.selectedGroupId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.groupType = GROUP_CURRENT_ROOM; + this.selectedGroupId = 0; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(4); + message.appendInt(this.groupType); + message.appendInt(this.selectedGroupId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int resolveTargetGroupId(Room room) { + if (this.groupType == GROUP_CURRENT_ROOM) { + return room.getGuildId(); + } + + return this.selectedGroupId; + } + + private int normalizeGroupType(int value) { + return (value == GROUP_SELECTED) ? GROUP_SELECTED : GROUP_CURRENT_ROOM; + } + + static class JsonData { + int groupType; + int selectedGroupId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int groupType, int selectedGroupId, boolean filterExisting, boolean invert, int delay) { + this.groupType = groupType; + this.selectedGroupId = selectedGroupId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java new file mode 100644 index 00000000..e34ef8e7 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersHandItem.java @@ -0,0 +1,145 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersHandItem extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_HANDITEM_SELECTOR; + + private int handItemId = 0; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersHandItem(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersHandItem(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (roomUnit != null && roomUnit.getHandItem() == this.handItemId) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.handItemId = (params.length > 0) ? Math.max(0, params[0]) : 0; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.handItemId, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.handItemId = Math.max(0, data.handItemId); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.handItemId = 0; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.handItemId); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + int handItemId; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int handItemId, boolean filterExisting, boolean invert, int delay) { + this.handItemId = handItemId; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java index 7c63b2fb..9c5dd91c 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersNeighborhood.java @@ -6,7 +6,6 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; -import com.eu.habbo.habbohotel.rooms.RoomUnitType; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.core.WiredContext; @@ -43,8 +42,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { private int sourceType = SOURCE_USER_TRIGGER; private boolean filterExisting = false; private boolean invert = false; - private boolean excludeBots = false; - private boolean excludePets = false; + private int targetOffsetX = 0; + private int targetOffsetY = 0; private List tileOffsets = new ArrayList<>(); private List pickedFurniIds = new ArrayList<>(); @@ -77,13 +76,11 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { tileOffsets.stream().map(o -> o[0] + "," + o[1]).collect(Collectors.joining(";")), filterExisting, invert); - // Apply tile offsets relative to each source position. - // The offsets define a neighborhood pattern around the source furni/user. Set targetTiles = new HashSet<>(); for (int[] src : sourcePositions) { for (int[] offset : tileOffsets) { - int tx = src[0] + offset[0]; - int ty = src[1] + offset[1]; + int tx = src[0] + (offset[0] - this.targetOffsetX); + int ty = src[1] + (offset[1] - this.targetOffsetY); targetTiles.add(tx + "," + ty); } } @@ -92,22 +89,17 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { List result = new ArrayList<>(); for (RoomUnit unit : room.getRoomUnits()) { - if (excludeBots && unit.getRoomUnitType() == RoomUnitType.BOT) continue; - if (excludePets && unit.getRoomUnitType() == RoomUnitType.PET) continue; - String pos = unit.getX() + "," + unit.getY(); boolean onTile = targetTiles.contains(pos); LOGGER.debug("[Neighborhood] Unit id={} type={} pos={} onTile={}", unit.getId(), unit.getRoomUnitType(), pos, onTile); - if (invert ? !onTile : onTile) { + if (onTile) { result.add(unit); } } - if (filterExisting) { - result.retainAll(ctx.targets().users()); - } + result = new ArrayList<>(this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), filterExisting, invert)); LOGGER.debug("[Neighborhood] Result: {} users selected", result.size()); @@ -169,8 +161,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = params[0]; this.filterExisting = params.length > 1 && params[1] == 1; this.invert = params.length > 2 && params[2] == 1; - this.excludeBots = params.length > 3 && params[3] == 1; - this.excludePets = params.length > 4 && params[4] == 1; + this.targetOffsetX = params.length > 3 ? params[3] : 0; + this.targetOffsetY = params.length > 4 ? params[4] : 0; this.tileOffsets = new ArrayList<>(); if (params.length > 5) { @@ -218,8 +210,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { message.appendInt(sourceType); message.appendInt(filterExisting ? 1 : 0); message.appendInt(invert ? 1 : 0); - message.appendInt(excludeBots ? 1 : 0); - message.appendInt(excludePets ? 1 : 0); + message.appendInt(targetOffsetX); + message.appendInt(targetOffsetY); message.appendInt(tileOffsets.size()); for (int[] offset : tileOffsets) { message.appendInt(offset[0]); @@ -243,7 +235,7 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { @Override public String getWiredData() { return WiredManager.getGson().toJson( - new JsonData(sourceType, filterExisting, invert, excludeBots, excludePets, tileOffsets, pickedFurniIds, getDelay())); + new JsonData(sourceType, filterExisting, invert, targetOffsetX, targetOffsetY, tileOffsets, pickedFurniIds, getDelay())); } @Override @@ -254,8 +246,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = data.sourceType; this.filterExisting = data.filterExisting; this.invert = data.invert; - this.excludeBots = data.excludeBots; - this.excludePets = data.excludePets; + this.targetOffsetX = data.targetOffsetX; + this.targetOffsetY = data.targetOffsetY; this.tileOffsets = data.tileOffsets != null ? data.tileOffsets : new ArrayList<>(); this.pickedFurniIds = data.pickedFurniIds != null ? data.pickedFurniIds : new ArrayList<>(); this.setDelay(data.delay); @@ -267,8 +259,8 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { this.sourceType = SOURCE_USER_TRIGGER; this.filterExisting = false; this.invert = false; - this.excludeBots = false; - this.excludePets = false; + this.targetOffsetX = 0; + this.targetOffsetY = 0; this.tileOffsets = new ArrayList<>(); this.pickedFurniIds = new ArrayList<>(); this.setDelay(0); @@ -281,20 +273,20 @@ public class WiredEffectUsersNeighborhood extends InteractionWiredEffect { int sourceType; boolean filterExisting; boolean invert; - boolean excludeBots; - boolean excludePets; + int targetOffsetX; + int targetOffsetY; List tileOffsets; List pickedFurniIds; int delay; JsonData(int sourceType, boolean filterExisting, boolean invert, - boolean excludeBots, boolean excludePets, + int targetOffsetX, int targetOffsetY, List tileOffsets, List pickedFurniIds, int delay) { this.sourceType = sourceType; this.filterExisting = filterExisting; this.invert = invert; - this.excludeBots = excludeBots; - this.excludePets = excludePets; + this.targetOffsetX = targetOffsetX; + this.targetOffsetY = targetOffsetY; this.tileOffsets = tileOffsets; this.pickedFurniIds = pickedFurniIds; this.delay = delay; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java new file mode 100644 index 00000000..833d10a4 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersOnFurni.java @@ -0,0 +1,269 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class WiredEffectUsersOnFurni extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_ON_FURNI_SELECTOR; + + private final Set items = new LinkedHashSet<>(); + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersOnFurni(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersOnFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null || room.getLayout() == null) { + ctx.targets().setUsers(Collections.emptySet()); + return; + } + + this.refresh(room); + + List sourceItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items); + if (sourceItems.isEmpty()) { + ctx.targets().setUsers(Collections.emptySet()); + return; + } + + Set result = new LinkedHashSet<>(); + + for (HabboItem sourceItem : sourceItems) { + result.addAll(this.resolveUnitsOnItem(room, sourceItem)); + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.furniSource = (params.length > 0) ? this.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + + int count = settings.getFurniIds().length; + if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + return false; + } + + this.items.clear(); + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) { + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + + if (room == null) { + return false; + } + + for (int itemId : settings.getFurniIds()) { + HabboItem item = room.getHabboItem(itemId); + + if (item != null) { + this.items.add(item); + } + } + } + + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + this.refresh(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId())); + + return WiredManager.getGson().toJson(new JsonData( + this.furniSource, + this.filterExisting, + this.invert, + this.items.stream().map(HabboItem::getId).collect(Collectors.toList()), + this.getDelay() + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + + if (room == null || data.itemIds == null) { + return; + } + + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + + if (item != null) { + this.items.add(item); + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + this.refresh(room); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(this.items.size()); + + for (HabboItem item : this.items) { + message.appendInt(item.getId()); + } + + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.furniSource); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private Set resolveUnitsOnItem(Room room, HabboItem sourceItem) { + Set result = new LinkedHashSet<>(); + + if (sourceItem == null) { + return result; + } + + RoomTile baseTile = room.getLayout().getTile(sourceItem.getX(), sourceItem.getY()); + if (baseTile == null) { + return result; + } + + Set occupiedTiles = room.getLayout().getTilesAt(baseTile, sourceItem.getBaseItem().getWidth(), sourceItem.getBaseItem().getLength(), sourceItem.getRotation()); + if (occupiedTiles == null) { + return result; + } + + for (RoomTile tile : occupiedTiles) { + if (tile == null) { + continue; + } + + result.addAll(room.getUnitManager().getRoomUnitsAt(tile)); + } + + return result; + } + + private void refresh(Room room) { + Set invalidItems = new LinkedHashSet<>(); + + if (room == null) { + invalidItems.addAll(this.items); + } else { + for (HabboItem item : this.items) { + if (room.getHabboItem(item.getId()) == null) { + invalidItems.add(item); + } + } + } + + this.items.removeAll(invalidItems); + } + + private int normalizeFurniSource(int value) { + switch (value) { + case WiredSourceUtil.SOURCE_SELECTED: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + case WiredSourceUtil.SOURCE_TRIGGER: + return value; + default: + return WiredSourceUtil.SOURCE_TRIGGER; + } + } + + static class JsonData { + int furniSource; + boolean filterExisting; + boolean invert; + List itemIds; + int delay; + + JsonData(int furniSource, boolean filterExisting, boolean invert, List itemIds, int delay) { + this.furniSource = furniSource; + this.filterExisting = filterExisting; + this.invert = invert; + this.itemIds = itemIds; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java new file mode 100644 index 00000000..1ce2eaba --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersSignal.java @@ -0,0 +1,138 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class WiredEffectUsersSignal extends InteractionWiredEffect { + public static final WiredEffectType type = WiredEffectType.USERS_SIGNAL_SELECTOR; + + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { + List signalUsers = WiredSourceUtil.resolveUsers(ctx, WiredSourceUtil.SOURCE_SIGNAL); + result.addAll(signalUsers); + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + } + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.filterExisting = params.length > 0 && params[0] == 1; + this.invert = params.length > 1 && params[1] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(2); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + static class JsonData { + boolean filterExisting; + boolean invert; + int delay; + + JsonData(boolean filterExisting, boolean invert, int delay) { + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java new file mode 100644 index 00000000..8b16f73f --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/selector/WiredEffectUsersTeam.java @@ -0,0 +1,177 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.selector; + +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.games.GamePlayer; +import com.eu.habbo.habbohotel.games.GameTeamColors; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.WiredContext; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WiredEffectUsersTeam extends InteractionWiredEffect { + private static final int TEAM_ANY = 0; + + public static final WiredEffectType type = WiredEffectType.USERS_TEAM_SELECTOR; + + private int teamType = TEAM_ANY; + private boolean filterExisting = false; + private boolean invert = false; + + public WiredEffectUsersTeam(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredEffectUsersTeam(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) { + return; + } + + Set result = new LinkedHashSet<>(); + + for (RoomUnit roomUnit : room.getRoomUnits()) { + if (this.matchesTeam(room, roomUnit)) { + result.add(roomUnit); + } + } + + result = this.applySelectorModifiers(result, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert); + + ctx.targets().setUsers(result); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + this.teamType = (params.length > 0) ? this.normalizeTeamType(params[0]) : TEAM_ANY; + this.filterExisting = params.length > 1 && params[1] == 1; + this.invert = params.length > 2 && params[2] == 1; + this.setDelay(settings.getDelay()); + return true; + } + + @Override + public WiredEffectType getType() { + return type; + } + + @Override + public boolean isSelector() { + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(this.teamType, this.filterExisting, this.invert, this.getDelay())); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.onPickUp(); + + String wiredData = set.getString("wired_data"); + if (wiredData == null || !wiredData.startsWith("{")) { + return; + } + + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data == null) { + return; + } + + this.teamType = this.normalizeTeamType(data.teamType); + this.filterExisting = data.filterExisting; + this.invert = data.invert; + this.setDelay(data.delay); + } + + @Override + public void onPickUp() { + this.teamType = TEAM_ANY; + this.filterExisting = false; + this.invert = false; + this.setDelay(0); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(this.teamType); + message.appendInt(this.filterExisting ? 1 : 0); + message.appendInt(this.invert ? 1 : 0); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + message.appendInt(0); + } + + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + private int normalizeTeamType(int value) { + if (value == TEAM_ANY) { + return TEAM_ANY; + } + + GameTeamColors teamColor = GameTeamColors.fromType(value); + return (teamColor.type >= GameTeamColors.RED.type && teamColor.type <= GameTeamColors.YELLOW.type) + ? teamColor.type + : TEAM_ANY; + } + + private boolean matchesTeam(Room room, RoomUnit roomUnit) { + if (room == null || roomUnit == null) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + if (habbo == null || habbo.getHabboInfo() == null) { + return false; + } + + GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer(); + if (gamePlayer == null || gamePlayer.getTeamColor() == null || gamePlayer.getTeamColor() == GameTeamColors.NONE) { + return false; + } + + return (this.teamType == TEAM_ANY) || gamePlayer.getTeamColor().type == this.teamType; + } + + static class JsonData { + int teamType; + boolean filterExisting; + boolean invert; + int delay; + + JsonData(int teamType, boolean filterExisting, boolean invert, int delay) { + this.teamType = teamType; + this.filterExisting = filterExisting; + this.invert = invert; + this.delay = delay; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java index 419dcb90..0bc3cee5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; @@ -11,6 +12,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; @@ -25,11 +28,15 @@ import java.util.stream.Collectors; public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { private static final Logger LOGGER = LoggerFactory.getLogger(WiredTriggerBotReachedFurni.class); + private static final int BOT_SOURCE_NAME = 100; + private static final int BOT_SOURCE_SELECTOR = 200; - public final static WiredTriggerType type = WiredTriggerType.WALKS_ON_FURNI; + public final static WiredTriggerType type = WiredTriggerType.BOT_REACHED_STF; - private THashSet items; + private final THashSet items; private String botName = ""; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; + private int botSource = BOT_SOURCE_NAME; public WiredTriggerBotReachedFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -72,9 +79,11 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); + message.appendInt(2); + message.appendInt(this.furniSource); + message.appendInt(this.botSource); message.appendInt(0); - message.appendInt(0); - message.appendInt(WiredTriggerType.BOT_REACHED_STF.code); + message.appendInt(this.getType().code); if (!this.isTriggeredByRoomUnit()) { List invalidTriggers = new ArrayList<>(); @@ -98,9 +107,22 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.botName = settings.getStringParam(); + int[] params = settings.getIntParams(); + this.furniSource = (params.length > 0) + ? this.normalizeFurniSource(params[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + this.botSource = (params.length > 1) ? this.normalizeBotSource(params[1]) : BOT_SOURCE_NAME; this.items.clear(); + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); @@ -127,20 +149,14 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { return false; } - boolean isCorrectBot = room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); - if (!isCorrectBot) { + if (!this.matchesBotSource(event, roomUnit, room) || !isCorrectBotForName(roomUnit, room)) { return false; } - if (this.items.contains(sourceItem)) { - return true; - } - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -153,6 +169,8 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.botName, + this.furniSource, + this.botSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -165,12 +183,18 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.botName = data.botName; + this.furniSource = this.normalizeFurniSource(data.furniSource); + this.botSource = this.normalizeBotSource(data.botSource); for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { this.items.add(item); } } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } } else { String[] data = wiredData.split(":"); @@ -192,6 +216,9 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; + this.botSource = BOT_SOURCE_NAME; } } @@ -199,15 +226,51 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger { public void onPickUp() { this.items.clear(); this.botName = ""; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; + this.botSource = BOT_SOURCE_NAME; } static class JsonData { String botName; + int furniSource; + int botSource; List itemIds; - public JsonData(String botName, List itemIds) { + public JsonData(String botName, int furniSource, int botSource, List itemIds) { this.botName = botName; + this.furniSource = furniSource; + this.botSource = botSource; this.itemIds = itemIds; } } + + private boolean matchesBotSource(WiredEvent event, RoomUnit roomUnit, Room room) { + if (this.botSource == BOT_SOURCE_SELECTOR) { + return WiredTriggerSourceUtil.containsUser( + WiredTriggerSourceUtil.resolveUsers(this, event, WiredSourceUtil.SOURCE_SELECTOR, null), + roomUnit); + } + + return true; + } + + private boolean isCorrectBotForName(RoomUnit roomUnit, Room room) { + if (this.botSource != BOT_SOURCE_NAME) { + return true; + } + + return room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); + } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + + private int normalizeBotSource(int value) { + return (value == BOT_SOURCE_SELECTOR) ? BOT_SOURCE_SELECTOR : BOT_SOURCE_NAME; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java index 50c39e71..cc671a58 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerBotReachedHabbo.java @@ -9,6 +9,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; @@ -16,8 +18,11 @@ import java.sql.SQLException; public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { public final static WiredTriggerType type = WiredTriggerType.BOT_REACHED_AVTR; + private static final int BOT_SOURCE_NAME = 100; + private static final int BOT_SOURCE_SELECTOR = 200; private String botName = ""; + private int botSource = BOT_SOURCE_NAME; public WiredTriggerBotReachedHabbo(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -40,7 +45,8 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.botName); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.botSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -50,6 +56,7 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { this.botName = settings.getStringParam(); + this.botSource = (settings.getIntParams().length > 0) ? this.normalizeBotSource(settings.getIntParams()[0]) : BOT_SOURCE_NAME; return true; } @@ -58,6 +65,17 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { public boolean matches(HabboItem triggerItem, WiredEvent event) { RoomUnit roomUnit = event.getActor().orElse(null); Room room = event.getRoom(); + + if (roomUnit == null || room == null) { + return false; + } + + if (this.botSource == BOT_SOURCE_SELECTOR) { + return WiredTriggerSourceUtil.containsUser( + WiredTriggerSourceUtil.resolveUsers(this, event, WiredSourceUtil.SOURCE_SELECTOR, null), + roomUnit); + } + return room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit); } @@ -70,7 +88,8 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( - this.botName + this.botName, + this.botSource )); } @@ -81,14 +100,17 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.botName = data.botName; + this.botSource = this.normalizeBotSource(data.botSource); } else { this.botName = wiredData; + this.botSource = BOT_SOURCE_NAME; } } @Override public void onPickUp() { this.botName = ""; + this.botSource = BOT_SOURCE_NAME; } @Override @@ -98,9 +120,15 @@ public class WiredTriggerBotReachedHabbo extends InteractionWiredTrigger { static class JsonData { String botName; + int botSource; - public JsonData(String botName) { + public JsonData(String botName, int botSource) { this.botName = botName; + this.botSource = botSource; } } + + private int normalizeBotSource(int value) { + return (value == BOT_SOURCE_SELECTOR) ? BOT_SOURCE_SELECTOR : BOT_SOURCE_NAME; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java new file mode 100644 index 00000000..dc536b3b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerClockCounter.java @@ -0,0 +1,250 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameUpCounter; +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.habbohotel.wired.core.WiredTriggerSourceUtil; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; +import gnu.trove.set.hash.THashSet; + +import java.sql.ResultSet; +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 WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() + .anyMatch(item -> item != null && item.getId() == sourceItem.getId()); + } + + @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) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { + int[] params = settings.getIntParams(); + + this.minutes = (params.length > 0) ? this.normalizeMinutes(params[0]) : 0; + this.halfSecondSteps = (params.length > 1) ? this.normalizeHalfSecondSteps(params[1]) : 0; + this.furniSource = (params.length > 2) + ? this.normalizeFurniSource(params[2]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : 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)) { + throw new WiredTriggerSaveException("wiredfurni.error.require_counter_furni"); + } + + 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); + } + } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } + } + + @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) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return 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/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java index 6c15531f..0ac5b376 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerFurniStateToggled.java @@ -10,6 +10,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -25,6 +27,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { private THashSet snapshots; private int triggerMode = MODE_ALL_STATES; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerFurniStateToggled(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -48,11 +51,14 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } StateSnapshot snapshot = this.getSnapshot(sourceItem.getId()); - if (snapshot == null) { + if (!this.matchesSourceItem(event, sourceItem)) { return false; } if (this.triggerMode == MODE_SAVED_STATE) { + if (snapshot == null) { + return false; + } return snapshot.state.equals(this.normalizeState(sourceItem.getExtradata())); } @@ -69,6 +75,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( this.triggerMode, + this.furniSource, new ArrayList<>(this.snapshots) )); } @@ -77,11 +84,13 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public void loadWiredData(ResultSet set, Room room) throws SQLException { this.snapshots = new THashSet<>(); this.triggerMode = MODE_ALL_STATES; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.triggerMode = (data != null) ? data.triggerMode : MODE_ALL_STATES; + this.furniSource = (data != null) ? this.normalizeFurniSource(data.furniSource) : WiredSourceUtil.SOURCE_TRIGGER; if (data != null && data.snapshots != null && !data.snapshots.isEmpty()) { for (StateSnapshot snapshot : data.snapshots) { @@ -100,6 +109,10 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } } } + + if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.snapshots.isEmpty()) { + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + } } else { if (wiredData.split(":").length >= 3) { super.setDelay(Integer.parseInt(wiredData.split(":")[0])); @@ -118,6 +131,8 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { } } } + + this.furniSource = this.snapshots.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @@ -125,6 +140,7 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { public void onPickUp() { this.snapshots.clear(); this.triggerMode = MODE_ALL_STATES; + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -157,8 +173,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.triggerMode); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -170,6 +187,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { this.triggerMode = (settings.getIntParams().length > 0 && settings.getIntParams()[0] == MODE_SAVED_STATE) ? MODE_SAVED_STATE : MODE_ALL_STATES; + this.furniSource = (settings.getIntParams().length > 1) + ? this.normalizeFurniSource(settings.getIntParams()[1]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); if (room == null) { @@ -211,8 +231,38 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { return (state == null) ? "" : state; } + private boolean matchesSourceItem(WiredEvent event, HabboItem sourceItem) { + List selectedItems = new ArrayList<>(); + + if (event.getRoom() != null) { + for (StateSnapshot snapshot : this.snapshots) { + HabboItem item = event.getRoom().getHabboItem(snapshot.itemId); + if (item != null) { + selectedItems.add(item); + } + } + } + + for (HabboItem item : WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, selectedItems)) { + if (item != null && item.getId() == sourceItem.getId()) { + return true; + } + } + + return false; + } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + static class JsonData { int triggerMode; + int furniSource; List snapshots; List itemIds; @@ -223,8 +273,9 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger { this.itemIds = itemIds; } - public JsonData(int triggerMode, List snapshots) { + public JsonData(int triggerMode, int furniSource, List snapshots) { this.triggerMode = triggerMode; + this.furniSource = furniSource; this.snapshots = snapshots; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java index 9bf25e62..a801ed88 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,7 +11,10 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; @@ -21,7 +25,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.CLICKS_FURNI; - private THashSet items; + protected final THashSet items; + protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboClicksFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,7 +41,21 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - return sourceItem != null && this.items.contains(sourceItem); + if (sourceItem == null) { + return false; + } + + switch (this.furniSource) { + case WiredSourceUtil.SOURCE_SELECTED: + return this.matchesSourceItem(this.items, sourceItem); + case WiredSourceUtil.SOURCE_SELECTOR: + return this.matchesSourceItem( + WiredTriggerSourceUtil.resolveItems(this, event, WiredSourceUtil.SOURCE_SELECTOR, this.items), + sourceItem); + case WiredSourceUtil.SOURCE_TRIGGER: + default: + return true; + } } @Deprecated @@ -77,7 +96,8 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -86,13 +106,32 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { + if (!this.isSelectableItem(item)) { + throw new WiredTriggerSaveException(this.getInvalidSelectionErrorKey()); + } this.items.add(item); } } @@ -103,6 +142,7 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -110,10 +150,12 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id : data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -141,12 +183,15 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -155,10 +200,42 @@ public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + protected boolean isSelectableItem(HabboItem item) { + return item != null; + } + + protected String getInvalidSelectionErrorKey() { + return "There was an error while saving that trigger"; + } + + protected int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } + + private boolean matchesSourceItem(Iterable candidateItems, HabboItem sourceItem) { + if (candidateItems == null || sourceItem == null) { + return false; + } + + for (HabboItem item : candidateItems) { + if (item != null && item.getId() == sourceItem.getId()) { + return true; + } + } + + return false; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java index 6ddcc503..118c265e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksTile.java @@ -44,4 +44,14 @@ public class WiredTriggerHabboClicksTile extends WiredTriggerHabboClicksFurni { String interaction = item.getBaseItem().getInteractionType().getName(); return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION); } + + @Override + protected boolean isSelectableItem(HabboItem item) { + return this.isClickTileItem(item); + } + + @Override + protected String getInvalidSelectionErrorKey() { + return "wiredfurni.error.require_click_tiles"; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java index f64ca1c0..53a42939 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboClicksUser.java @@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; @@ -15,6 +16,11 @@ import java.sql.SQLException; public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.CLICKS_USER; + private static final String CACHE_BLOCK_MENU_OPEN = "wired.click_user.block_menu_open"; + private static final String CACHE_IGNORE_LOOK_UNTIL = "wired.click_user.ignore_look_until"; + private static final long IGNORE_LOOK_WINDOW_MS = 500L; + private boolean blockMenuOpen = false; + private boolean doNotRotate = false; public WiredTriggerHabboClicksUser(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -37,15 +43,29 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { @Override public String getWiredData() { - return ""; + return WiredManager.getGson().toJson(new JsonData(this.blockMenuOpen, this.doNotRotate)); } @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { + String wiredData = set.getString("wired_data"); + + this.blockMenuOpen = false; + this.doNotRotate = false; + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = com.eu.habbo.habbohotel.wired.core.WiredManager.getGson().fromJson(wiredData, JsonData.class); + if (data != null) { + this.blockMenuOpen = data.blockMenuOpen; + this.doNotRotate = data.doNotRotate; + } + } } @Override public void onPickUp() { + this.blockMenuOpen = false; + this.doNotRotate = false; } @Override @@ -61,7 +81,9 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(2); + message.appendInt(this.blockMenuOpen ? 1 : 0); + message.appendInt(this.doNotRotate ? 1 : 0); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -70,6 +92,9 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.blockMenuOpen = (params.length > 0) && (params[0] == 1); + this.doNotRotate = (params.length > 1) && (params[1] == 1); return true; } @@ -77,4 +102,86 @@ public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger { public boolean isTriggeredByRoomUnit() { return true; } + + public boolean isBlockMenuOpen() { + return this.blockMenuOpen; + } + + public boolean isDoNotRotate() { + return this.doNotRotate; + } + + public static void clearRuntimeFlags(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + roomUnit.getCacheable().remove(CACHE_BLOCK_MENU_OPEN); + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + } + + public static void applyRuntimeOptions(RoomUnit roomUnit, boolean blockMenuOpen, boolean doNotRotate) { + if (roomUnit == null) { + return; + } + + if (blockMenuOpen) { + roomUnit.getCacheable().put(CACHE_BLOCK_MENU_OPEN, Boolean.TRUE); + } + + if (doNotRotate) { + roomUnit.getCacheable().put(CACHE_IGNORE_LOOK_UNTIL, System.currentTimeMillis() + IGNORE_LOOK_WINDOW_MS); + } + } + + public static boolean consumeBlockMenuOpen(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().remove(CACHE_BLOCK_MENU_OPEN); + return Boolean.TRUE.equals(value); + } + + public static boolean consumeIgnoreLook(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().get(CACHE_IGNORE_LOOK_UNTIL); + + if (!(value instanceof Long)) { + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + return false; + } + + long expiresAt = (Long) value; + roomUnit.getCacheable().remove(CACHE_IGNORE_LOOK_UNTIL); + + return System.currentTimeMillis() <= expiresAt; + } + + public static boolean hasPendingIgnoreLook(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Object value = roomUnit.getCacheable().get(CACHE_IGNORE_LOOK_UNTIL); + + if (!(value instanceof Long)) { + return false; + } + + return System.currentTimeMillis() <= (Long) value; + } + + static class JsonData { + boolean blockMenuOpen; + boolean doNotRotate; + + public JsonData(boolean blockMenuOpen, boolean doNotRotate) { + this.blockMenuOpen = blockMenuOpen; + this.doNotRotate = doNotRotate; + } + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java index b05746eb..e2200047 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboSaysKeyword.java @@ -17,9 +17,14 @@ import java.sql.SQLException; public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.SAY_SOMETHING; + private static final int MATCH_CONTAINS = 0; + private static final int MATCH_EXACT = 1; + private static final int MATCH_ALL_WORDS = 2; + private boolean hideMessage = false; private boolean ownerOnly = false; private String key = ""; + private int matchMode = MATCH_CONTAINS; public WiredTriggerHabboSaysKeyword(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -31,16 +36,20 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { - if (this.key.length() > 0) { - String text = event.getText().orElse(null); - if (text != null && text.toLowerCase().contains(this.key.toLowerCase())) { - RoomUnit roomUnit = event.getActor().orElse(null); - Room room = event.getRoom(); - Habbo habbo = room.getHabbo(roomUnit); - return !this.ownerOnly || (habbo != null && room.getOwnerId() == habbo.getHabboInfo().getId()); - } + if (this.key.length() <= 0) { + return false; } - return false; + + String text = event.getText().orElse(null); + RoomUnit roomUnit = event.getActor().orElse(null); + Room room = event.getRoom(); + + if (text == null || roomUnit == null || room == null || !this.matchesText(text)) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + return !this.ownerOnly || (habbo != null && room.getOwnerId() == habbo.getHabboInfo().getId()); } @Deprecated @@ -52,8 +61,10 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.hideMessage, this.ownerOnly, - this.key + this.key, + this.matchMode )); } @@ -64,21 +75,27 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.ownerOnly = data.ownerOnly; + this.hideMessage = data.hideMessage; this.key = data.key; + this.matchMode = this.normalizeMatchMode(data.matchMode); } else { String[] data = wiredData.split("\t"); if (data.length == 2) { this.ownerOnly = data[0].equalsIgnoreCase("1"); this.key = data[1]; + this.hideMessage = false; + this.matchMode = MATCH_CONTAINS; } } } @Override public void onPickUp() { + this.hideMessage = false; this.ownerOnly = false; this.key = ""; + this.matchMode = MATCH_CONTAINS; } @Override @@ -94,8 +111,11 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(this.key); + message.appendInt(3); + message.appendInt(this.matchMode); + message.appendInt(this.hideMessage ? 1 : 0); + message.appendInt(this.ownerOnly ? 1 : 0); message.appendInt(0); - message.appendInt(1); message.appendInt(this.getType().code); message.appendInt(0); message.appendInt(0); @@ -103,8 +123,10 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { - if(settings.getIntParams().length < 1) return false; - this.ownerOnly = settings.getIntParams()[0] == 1; + int[] params = settings.getIntParams(); + this.matchMode = (params.length > 0) ? this.normalizeMatchMode(params[0]) : MATCH_CONTAINS; + this.hideMessage = (params.length > 1) && (params[1] == 1); + this.ownerOnly = (params.length > 2) && (params[2] == 1); this.key = settings.getStringParam(); return true; @@ -115,13 +137,50 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger { return true; } + public boolean isHideMessage() { + return this.hideMessage; + } + + private boolean matchesText(String text) { + String normalizedText = text.toLowerCase().trim(); + String normalizedKey = this.key.toLowerCase().trim(); + + switch (this.matchMode) { + case MATCH_EXACT: + return normalizedText.equals(normalizedKey); + case MATCH_ALL_WORDS: + String[] requiredParts = normalizedKey.split("\\s+"); + for (String part : requiredParts) { + if (!part.isEmpty() && !normalizedText.contains(part)) { + return false; + } + } + return true; + case MATCH_CONTAINS: + default: + return normalizedText.contains(normalizedKey); + } + } + + private int normalizeMatchMode(int value) { + if (value < MATCH_CONTAINS || value > MATCH_ALL_WORDS) { + return MATCH_CONTAINS; + } + + return value; + } + static class JsonData { + boolean hideMessage; boolean ownerOnly; String key; + int matchMode; - public JsonData(boolean ownerOnly, String key) { + public JsonData(boolean hideMessage, boolean ownerOnly, String key, int matchMode) { + this.hideMessage = hideMessage; this.ownerOnly = ownerOnly; this.key = key; + this.matchMode = matchMode; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java index 3e83d923..d08790a8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOffFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,6 +11,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -21,7 +24,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.WALKS_OFF_FURNI; - private THashSet items; + private final THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboWalkOffFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,24 +40,11 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem == null) { - return false; - } - - if (this.items.contains(sourceItem)) { - return true; - } - Room room = event.getRoom(); - if (room != null) { - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - } - - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -64,7 +55,8 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public String getWiredData() { - return WiredManager.getGson().toJson(new WiredTriggerFurniStateToggled.JsonData( + return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -72,10 +64,12 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -101,12 +95,15 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -140,7 +137,8 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -149,12 +147,28 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { this.items.add(item); } @@ -169,10 +183,20 @@ public class WiredTriggerHabboWalkOffFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java index 6f8d7ddc..d6fb8f96 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerHabboWalkOnFurni.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -10,6 +11,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import gnu.trove.set.hash.THashSet; @@ -21,7 +24,8 @@ import java.util.stream.Collectors; public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.WALKS_ON_FURNI; - private THashSet items; + private final THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_TRIGGER; public WiredTriggerHabboWalkOnFurni(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -36,24 +40,11 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public boolean matches(HabboItem triggerItem, WiredEvent event) { HabboItem sourceItem = event.getSourceItem().orElse(null); - if (sourceItem == null) { - return false; - } - - if (this.items.contains(sourceItem)) { - return true; - } - Room room = event.getRoom(); - if (room != null) { - for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { - if (this.items.contains(item)) { - return true; - } - } - } - - return false; + return WiredTriggerSourceUtil.containsItemOrTile( + room, + WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items), + sourceItem); } @Deprecated @@ -93,7 +84,8 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(0); + message.appendInt(1); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -102,12 +94,28 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); + this.furniSource = (settings.getIntParams().length > 0) + ? this.normalizeFurniSource(settings.getIntParams()[0]) + : ((settings.getFurniIds().length > 0) ? WiredSourceUtil.SOURCE_SELECTED : WiredSourceUtil.SOURCE_TRIGGER); + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) { + return true; + } int count = settings.getFurniIds().length; + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + if (room == null) { + return false; + } for (int i = 0; i < count; i++) { - HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]); + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); if (item != null) { this.items.add(item); } @@ -119,6 +127,7 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( + this.furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -126,10 +135,12 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { @Override public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; String wiredData = set.getString("wired_data"); if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.furniSource = this.normalizeFurniSource(data.furniSource); for (Integer id: data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) { @@ -155,12 +166,15 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { } } } + + this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; } } @Override public void onPickUp() { this.items.clear(); + this.furniSource = WiredSourceUtil.SOURCE_TRIGGER; } @Override @@ -169,10 +183,20 @@ public class WiredTriggerHabboWalkOnFurni extends InteractionWiredTrigger { } static class JsonData { + int furniSource; List itemIds; - public JsonData(List itemIds) { + public JsonData(int furniSource, List itemIds) { + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_TRIGGER; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java index 13463c7b..77bf5f29 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java @@ -1,6 +1,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; @@ -11,6 +12,8 @@ import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.core.WiredEvent; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil; +import com.eu.habbo.habbohotel.wired.core.WiredTriggerSourceUtil; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException; import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer; @@ -18,6 +21,7 @@ import gnu.trove.set.hash.THashSet; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -25,11 +29,13 @@ import java.util.stream.Collectors; public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public static final WiredTriggerType type = WiredTriggerType.RECEIVE_SIGNAL; - private static final String ANTENNA_INTERACTION = "antenna"; private static final long ACTIVATION_PULSE_MS = 300L; + private static final String ANTENNA_INTERACTION = "antenna"; + private static final String REQUIRE_ANTENNA_ERROR = "Puoi selezionare solo furni antenna."; private int channel = 0; // signal channel (0-based) private THashSet items; + private int furniSource = WiredSourceUtil.SOURCE_SELECTED; private final AtomicLong activationToken = new AtomicLong(); public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException { @@ -46,15 +52,16 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public boolean matches(HabboItem triggerItem, WiredEvent event) { if (event.getType() != WiredEvent.Type.SIGNAL_RECEIVED) return false; - if (!this.items.isEmpty()) { - int signalChannel = event.getSignalChannel(); - for (HabboItem antenna : this.items) { - if (antenna != null && antenna.getId() == signalChannel) return true; - } - return false; + List resolvedAntennas = WiredTriggerSourceUtil.resolveItems(this, event, this.furniSource, this.items).stream() + .filter(this::isAntennaItem) + .collect(Collectors.toList()); + + if (!resolvedAntennas.isEmpty()) { + return resolvedAntennas.stream() + .anyMatch(item -> item != null && item.getId() == event.getSignalChannel()); } - return event.getSignalChannel() == this.channel; + return this.channel > 0 && event.getSignalChannel() == this.channel; } public int getChannel() { @@ -82,13 +89,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { int senderCount = 0; try { if (room != null && room.getRoomSpecialTypes() != null) { - if (!this.items.isEmpty()) { - for (HabboItem item : this.items) { - senderCount += room.getRoomSpecialTypes().countSendersTargetingReceiver(item.getId()); - } - } else { - senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); - } + senderCount = this.getSenderCount(room); } } catch (Exception e) { } @@ -112,10 +113,11 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(3); + message.appendInt(4); message.appendInt(channel); message.appendInt(senderCount); message.appendInt(RoomSpecialTypes.MAX_SENDERS_PER_RECEIVER); + message.appendInt(this.furniSource); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -124,20 +126,37 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { @Override public boolean saveData(WiredSettings settings) { + return this.saveData(settings, null); + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) { this.items.clear(); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); int count = settings.getFurniIds().length; - for (int i = 0; i < count; i++) { - HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); - if (item == null) continue; - if (!isAntennaItem(item)) throw new WiredTriggerSaveException("wiredfurni.error.require_antenna_furni"); - this.items.add(item); - } - int[] params = settings.getIntParams(); this.channel = params.length > 0 ? params[0] : 0; + this.furniSource = (params.length > 1) + ? this.normalizeFurniSource(params[params.length - 1]) + : WiredSourceUtil.SOURCE_SELECTED; + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) { + for (int i = 0; i < count; i++) { + HabboItem item = room.getHabboItem(settings.getFurniIds()[i]); + if (item == null) continue; + if (!this.isAntennaItem(item)) throw new WiredTriggerSaveException(REQUIRE_ANTENNA_ERROR); + + this.items.add(item); + } + } + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty()) { + HabboItem firstItem = this.items.iterator().next(); + this.channel = (firstItem != null) ? firstItem.getId() : this.channel; + } + return true; } @@ -174,6 +193,7 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( channel, + furniSource, this.items.stream().map(HabboItem::getId).collect(Collectors.toList()) )); } @@ -182,15 +202,24 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public void loadWiredData(ResultSet set, Room room) throws SQLException { this.items = new THashSet<>(); String wiredData = set.getString("wired_data"); + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + if (wiredData != null && wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.channel = data.channel; + this.furniSource = this.normalizeFurniSource(data.furniSource); if (data.itemIds != null) { for (Integer id : data.itemIds) { HabboItem item = room.getHabboItem(id); if (item != null) this.items.add(item); } } + + if (this.furniSource != WiredSourceUtil.SOURCE_SELECTOR) this.furniSource = WiredSourceUtil.SOURCE_SELECTED; + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty() && this.channel <= 0) { + HabboItem firstItem = this.items.iterator().next(); + if (firstItem != null) this.channel = firstItem.getId(); + } } } @@ -198,26 +227,63 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { public void onPickUp() { this.channel = 0; this.items.clear(); - } - - private boolean isAntennaItem(HabboItem item) { - if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false; - String interaction = item.getBaseItem().getInteractionType().getName(); - if (interaction == null) return false; - - String normalized = interaction.toLowerCase(); - return normalized.equals(ANTENNA_INTERACTION); + this.furniSource = WiredSourceUtil.SOURCE_SELECTED; } static class JsonData { int channel; + int furniSource; List itemIds; public JsonData() {} - public JsonData(int channel, List itemIds) { + public JsonData(int channel, int furniSource, List itemIds) { this.channel = channel; + this.furniSource = furniSource; this.itemIds = itemIds; } } + + private int normalizeFurniSource(int value) { + if (value == WiredSourceUtil.SOURCE_SELECTED || value == WiredSourceUtil.SOURCE_SELECTOR) { + return value; + } + + return WiredSourceUtil.SOURCE_SELECTED; + } + + private boolean isAntennaItem(HabboItem item) { + if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) { + return false; + } + + String interaction = item.getBaseItem().getInteractionType().getName(); + return interaction != null && ANTENNA_INTERACTION.equalsIgnoreCase(interaction); + } + + private int getSenderCount(Room room) { + if (room == null || room.getRoomSpecialTypes() == null) { + return 0; + } + + if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && !this.items.isEmpty()) { + List antennaIds = new ArrayList<>(); + + for (HabboItem item : this.items) { + if (this.isAntennaItem(item)) { + antennaIds.add(item.getId()); + } + } + + if (!antennaIds.isEmpty()) { + return room.getRoomSpecialTypes().countSendersTargetingAnyReceiver(antennaIds); + } + } + + if (this.channel > 0) { + return room.getRoomSpecialTypes().countSendersTargetingReceiver(this.channel); + } + + return 0; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java index 179e9597..8bf9c7b2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerScoreAchieved.java @@ -1,10 +1,12 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers; +import com.eu.habbo.habbohotel.games.GameTeamColors; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.WiredTriggerType; @@ -17,6 +19,7 @@ import java.sql.SQLException; public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { private static final WiredTriggerType type = WiredTriggerType.SCORE_ACHIEVED; private int score = 0; + private int teamType = GameTeamColors.NONE.type; public WiredTriggerScoreAchieved(ResultSet set, Item baseItem) throws SQLException { super(set, baseItem); @@ -32,7 +35,23 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { int amountAdded = event.getScoreAdded(); // Check if this score addition crossed the threshold - return points - amountAdded < this.score && points >= this.score; + if (!(points - amountAdded < this.score && points >= this.score)) { + return false; + } + + if (this.teamType == GameTeamColors.NONE.type) { + return true; + } + + if (!event.getActor().isPresent()) { + return false; + } + + Habbo habbo = event.getRoom().getHabbo(event.getActor().get()); + + return habbo != null + && habbo.getHabboInfo().getGamePlayer() != null + && habbo.getHabboInfo().getGamePlayer().getTeamColor().type == this.teamType; } @Deprecated @@ -44,7 +63,8 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { @Override public String getWiredData() { return WiredManager.getGson().toJson(new JsonData( - this.score + this.score, + this.teamType )); } @@ -55,17 +75,20 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { if (wiredData.startsWith("{")) { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); this.score = data.score; + this.teamType = normalizeTeamType(data.teamType); } else { try { this.score = Integer.parseInt(wiredData); } catch (Exception e) { } + this.teamType = GameTeamColors.NONE.type; } } @Override public void onPickUp() { this.score = 0; + this.teamType = GameTeamColors.NONE.type; } @Override @@ -81,8 +104,9 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { message.appendInt(this.getBaseItem().getSpriteId()); message.appendInt(this.getId()); message.appendString(""); - message.appendInt(1); + message.appendInt(2); message.appendInt(this.score); + message.appendInt(this.teamType); message.appendInt(0); message.appendInt(this.getType().code); message.appendInt(0); @@ -93,6 +117,9 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { public boolean saveData(WiredSettings settings) { if(settings.getIntParams().length < 1) return false; this.score = settings.getIntParams()[0]; + this.teamType = (settings.getIntParams().length > 1) + ? normalizeTeamType(settings.getIntParams()[1]) + : GameTeamColors.NONE.type; return true; } @@ -101,11 +128,21 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger { return true; } + private int normalizeTeamType(int value) { + if (value >= GameTeamColors.RED.type && value <= GameTeamColors.YELLOW.type) { + return value; + } + + return GameTeamColors.NONE.type; + } + static class JsonData { int score; + int teamType; - public JsonData(int score) { + public JsonData(int score, int teamType) { this.score = score; + this.teamType = teamType; } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java index 323abce6..989f7f57 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; @@ -17,6 +18,7 @@ import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.WiredUserActionType; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics; import com.eu.habbo.messages.ISerialize; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.guilds.GuildInfoComposer; @@ -898,7 +900,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) { @@ -2435,6 +2441,11 @@ public class Room implements Comparable, ISerialize, Runnable { return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits); } + public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, + boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); + } + public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, Habbo owner) { return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner); @@ -2471,6 +2482,16 @@ public class Room implements Comparable, ISerialize, Runnable { return this.itemManager.moveFurniTo(item, tile, rotation, z, actor, sendUpdates, checkForUnits); } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, actor, sendUpdates, checkForUnits, physics); + } + + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, z, actor, sendUpdates, checkForUnits, physics); + } + public FurnitureMovementError slideFurniTo(HabboItem item, RoomTile tile, int rotation) { return this.itemManager.slideFurniTo(item, tile, rotation); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java index 3f1d8ccf..d3c11dab 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java @@ -23,6 +23,7 @@ import com.eu.habbo.habbohotel.users.HabboInfo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.users.HabboManager; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics; import com.eu.habbo.habbohotel.wired.tick.WiredTickable; import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer; import com.eu.habbo.messages.outgoing.rooms.items.*; @@ -1301,6 +1302,56 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return furnitureFitsAt(tile, item, rotation, checkForUnits); + } + + RoomLayout layout = this.room.getLayout(); + if (!layout.fitsOnMap(tile, item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation)) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic) { + return FurnitureMovementError.NONE; + } + + THashSet occupiedTiles = layout.getTilesAt(tile, item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), rotation); + for (RoomTile t : occupiedTiles) { + if (t.state == RoomTileState.INVALID) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (shouldCheckUnits(item, checkForUnits)) { + FurnitureMovementError unitCollision = this.getPhysicsUnitCollision(t, physics); + if (unitCollision != FurnitureMovementError.NONE) { + return unitCollision; + } + } + } + + if (this.hasBlockingPhysicsFurni(occupiedTiles, item, physics)) { + return FurnitureMovementError.CANT_STACK; + } + + java.util.List>> tileFurniList = new java.util.ArrayList<>(); + for (RoomTile t : occupiedTiles) { + tileFurniList.add(Pair.create(t, this.getPhysicsItemsAt(t, item, physics))); + + HabboItem topItem = this.getTopPhysicsItemAt(t.x, t.y, item, physics); + if (topItem != null && !topItem.getBaseItem().allowStack() && !t.getAllowStack()) { + return FurnitureMovementError.CANT_STACK; + } + } + + if (!item.canStackAt(this.room, tileFurniList)) { + return FurnitureMovementError.CANT_STACK; + } + + return FurnitureMovementError.NONE; + } + /** * Places a floor furniture item at a position. */ @@ -1569,6 +1620,158 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return moveFurniTo(item, tile, rotation, z, actor, sendUpdates, checkForUnits); + } + + if (item == null || tile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + RoomLayout layout = this.room.getLayout(); + RoomTile oldLocation = layout.getTile(item.getX(), item.getY()); + + boolean pluginHelper = false; + + if (Emulator.getPluginManager().isRegistered(FurnitureMovedEvent.class, true)) { + FurnitureMovedEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureMovedEvent(item, actor, oldLocation, tile)); + + if (event.isCancelled()) { + return FurnitureMovementError.CANCEL_PLUGIN_MOVE; + } + + pluginHelper = event.hasPluginHelper(); + } + + rotation %= 8; + + boolean magicTile = + item instanceof InteractionStackHelper || + item instanceof InteractionTileWalkMagic; + + THashSet occupiedTiles = layout.getTilesAt( + tile, + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + rotation + ); + + THashSet oldOccupiedTiles = layout.getTilesAt( + layout.getTile(item.getX(), item.getY()), + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + item.getRotation() + ); + + if (!pluginHelper) { + FurnitureMovementError fits = furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); + if (fits != FurnitureMovementError.NONE) { + return fits; + } + } + + int oldRotation = item.getRotation(); + + if (oldRotation != rotation) { + item.setRotation(rotation); + + if (Emulator.getPluginManager().isRegistered(FurnitureRotatedEvent.class, true)) { + Event rotatedEvent = new FurnitureRotatedEvent(item, actor, oldRotation); + Emulator.getPluginManager().fireEvent(rotatedEvent); + + if (rotatedEvent.isCancelled()) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANCEL_PLUGIN_ROTATE; + } + } + } + + if (z > Room.MAXIMUM_FURNI_HEIGHT) { + return FurnitureMovementError.CANT_STACK; + } + + if (z < layout.getHeightAtSquare(tile.x, tile.y)) { + return FurnitureMovementError.CANT_STACK; + } + + if (Emulator.getPluginManager().isRegistered(FurnitureBuildheightEvent.class, true)) { + FurnitureBuildheightEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureBuildheightEvent(item, actor, 0.00, z)); + + if (event.hasChangedHeight()) { + z = layout.getHeightAtSquare(tile.x, tile.y) + event.getUpdatedHeight(); + } + } + + item.setX(tile.x); + item.setY(tile.y); + item.setZ(z); + + if (magicTile) { + item.setZ(tile.z); + item.setExtradata("" + (item.getZ() * 100)); + } + + if (item.getZ() > Room.MAXIMUM_FURNI_HEIGHT) { + item.setZ(Room.MAXIMUM_FURNI_HEIGHT); + } + + if (oldLocation != null) { + if (item instanceof InteractionWiredTrigger) { + this.room.getRoomSpecialTypes().updateTriggerLocation((InteractionWiredTrigger) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredEffect) { + this.room.getRoomSpecialTypes().updateEffectLocation((InteractionWiredEffect) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredCondition) { + this.room.getRoomSpecialTypes().updateConditionLocation((InteractionWiredCondition) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredExtra) { + this.room.getRoomSpecialTypes().updateExtraLocation((InteractionWiredExtra) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } + } + + item.onMove(this.room, oldLocation, tile); + item.needsUpdate(true); + Emulator.getThreading().run(item); + + if (sendUpdates) { + this.room.sendComposer(new FloorItemUpdateComposer(item).compose()); + } + + occupiedTiles.removeAll(oldOccupiedTiles); + occupiedTiles.addAll(oldOccupiedTiles); + this.room.updateTiles(occupiedTiles); + + for (RoomTile t : occupiedTiles) { + this.room.updateHabbosAt(t.x, t.y, this.room.getHabbosAt(t.x, t.y)); + this.room.updateBotsAt(t.x, t.y); + } + + if (Emulator.getConfig().getBoolean("wired.place.under", false)) { + THashSet newOccupiedTiles = layout.getTilesAt( + tile, + item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), + rotation + ); + + for (RoomTile t : newOccupiedTiles) { + for (Habbo h : this.room.getHabbosAt(t.x, t.y)) { + try { + item.onWalkOn(h.getRoomUnit(), this.room, null); + } catch (Exception ignored) { + } + } + } + } + + return FurnitureMovementError.NONE; + } + /** * Moves furniture to a new position. */ @@ -1791,6 +1994,203 @@ public class RoomItemManager { return FurnitureMovementError.NONE; } + public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + if (physics == null || !physics.isActive()) { + return moveFurniTo(item, tile, rotation, actor, sendUpdates, checkForUnits); + } + + RoomLayout layout = this.room.getLayout(); + RoomTile oldLocation = layout.getTile(item.getX(), item.getY()); + + boolean pluginHelper = false; + if (Emulator.getPluginManager().isRegistered(FurnitureMovedEvent.class, true)) { + FurnitureMovedEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureMovedEvent(item, actor, oldLocation, tile)); + if (event.isCancelled()) { + return FurnitureMovementError.CANCEL_PLUGIN_MOVE; + } + pluginHelper = event.hasPluginHelper(); + } + + boolean magicTile = item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic; + + java.util.Optional stackHelper = this.getItemsAt(tile).stream() + .filter(i -> i instanceof InteractionStackHelper).findAny(); + + THashSet occupiedTiles = layout.getTilesAt(tile, item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), rotation); + THashSet newOccupiedTiles = layout.getTilesAt(tile, + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), rotation); + + HabboItem topItem = this.getTopPhysicsItemAt(occupiedTiles, null, physics); + + if (!stackHelper.isPresent() && !pluginHelper) { + if (oldLocation != tile) { + for (RoomTile t : occupiedTiles) { + HabboItem tileTopItem = this.getTopPhysicsItemAt(t.x, t.y, item, physics); + if (!magicTile && ((tileTopItem != null && tileTopItem != item ? ( + t.state.equals(RoomTileState.INVALID) || !t.getAllowStack() + || !tileTopItem.getBaseItem().allowStack()) + : this.room.calculateTileState(t, item).equals(RoomTileState.INVALID)))) { + return FurnitureMovementError.CANT_STACK; + } + + if (shouldCheckUnits(item, checkForUnits)) { + FurnitureMovementError unitCollision = this.getPhysicsUnitCollision(t, physics); + if (!magicTile && unitCollision != FurnitureMovementError.NONE) { + return unitCollision; + } + } + } + } + + if (this.hasBlockingPhysicsFurni(occupiedTiles, item, physics)) { + return FurnitureMovementError.CANT_STACK; + } + + java.util.List>> tileFurniList = new java.util.ArrayList<>(); + for (RoomTile t : occupiedTiles) { + tileFurniList.add(Pair.create(t, this.getPhysicsItemsAt(t, item, physics))); + } + + if (!magicTile && !item.canStackAt(this.room, tileFurniList)) { + return FurnitureMovementError.CANT_STACK; + } + } + + THashSet oldOccupiedTiles = layout.getTilesAt( + layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), item.getRotation()); + + int oldRotation = item.getRotation(); + + if (oldRotation != rotation) { + item.setRotation(rotation); + if (Emulator.getPluginManager().isRegistered(FurnitureRotatedEvent.class, true)) { + Event furnitureRotatedEvent = new FurnitureRotatedEvent(item, actor, oldRotation); + Emulator.getPluginManager().fireEvent(furnitureRotatedEvent); + + if (furnitureRotatedEvent.isCancelled()) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANCEL_PLUGIN_ROTATE; + } + } + + if ((!stackHelper.isPresent() && topItem != null && topItem != item && !topItem.getBaseItem() + .allowStack()) || (topItem != null && topItem != item + && topItem.getZ() + Item.getCurrentHeight(topItem) + Item.getCurrentHeight(item) + > Room.MAXIMUM_FURNI_HEIGHT)) { + item.setRotation(oldRotation); + return FurnitureMovementError.CANT_STACK; + } + } + + double height; + + if (stackHelper.isPresent()) { + height = stackHelper.get().getExtradata().isEmpty() ? Double.parseDouble("0.0") + : (Double.parseDouble(stackHelper.get().getExtradata()) / 100); + } else if (item == topItem) { + height = item.getZ(); + } else if (magicTile) { + if (topItem == null) { + height = this.getPhysicsStackHeight(tile.x, tile.y, item, physics); + for (RoomTile til : occupiedTiles) { + double sHeight = this.getPhysicsStackHeight(til.x, til.y, item, physics); + if (sHeight > height) { + height = sHeight; + } + } + } else { + height = topItem.getZ() + topItem.getBaseItem().getHeight(); + } + } else { + height = this.getPhysicsStackHeight(tile.x, tile.y, item, physics); + for (RoomTile til : occupiedTiles) { + double sHeight = this.getPhysicsStackHeight(til.x, til.y, item, physics); + if (sHeight > height) { + height = sHeight; + } + } + } + + boolean cantStack = false; + boolean pluginHeight = false; + + if (height > Room.MAXIMUM_FURNI_HEIGHT) { + cantStack = true; + } + if (height < layout.getHeightAtSquare(tile.x, tile.y)) { + cantStack = true; + } + + if (Emulator.getPluginManager().isRegistered(FurnitureBuildheightEvent.class, true)) { + FurnitureBuildheightEvent event = Emulator.getPluginManager() + .fireEvent(new FurnitureBuildheightEvent(item, actor, 0.00, height)); + if (event.hasChangedHeight()) { + height = layout.getHeightAtSquare(tile.x, tile.y) + event.getUpdatedHeight(); + pluginHeight = true; + } + } + + if (!pluginHeight && cantStack) { + return FurnitureMovementError.CANT_STACK; + } + + item.setX(tile.x); + item.setY(tile.y); + item.setZ(height); + if (magicTile) { + item.setZ(tile.z); + item.setExtradata("" + item.getZ() * 100); + } + if (item.getZ() > Room.MAXIMUM_FURNI_HEIGHT) { + item.setZ(Room.MAXIMUM_FURNI_HEIGHT); + } + + if (item instanceof InteractionWiredTrigger) { + this.room.getRoomSpecialTypes().updateTriggerLocation((InteractionWiredTrigger) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredEffect) { + this.room.getRoomSpecialTypes().updateEffectLocation((InteractionWiredEffect) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredCondition) { + this.room.getRoomSpecialTypes().updateConditionLocation((InteractionWiredCondition) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } else if (item instanceof InteractionWiredExtra) { + this.room.getRoomSpecialTypes().updateExtraLocation((InteractionWiredExtra) item, oldLocation.x, oldLocation.y); + WiredManager.invalidateRoom(this.room); + } + + item.onMove(this.room, oldLocation, tile); + item.needsUpdate(true); + Emulator.getThreading().run(item); + + if (sendUpdates) { + this.room.sendComposer(new FloorItemUpdateComposer(item).compose()); + } + + occupiedTiles.removeAll(oldOccupiedTiles); + occupiedTiles.addAll(oldOccupiedTiles); + this.room.updateTiles(occupiedTiles); + + for (RoomTile t : occupiedTiles) { + this.room.updateHabbosAt(t.x, t.y, this.room.getHabbosAt(t.x, t.y)); + this.room.updateBotsAt(t.x, t.y); + } + if (Emulator.getConfig().getBoolean("wired.place.under", false)) { + for (RoomTile t : newOccupiedTiles) { + for (Habbo h : this.room.getHabbosAt(t.x, t.y)) { + try { + item.onWalkOn(h.getRoomUnit(), this.room, null); + } catch (Exception e) { + } + } + } + } + return FurnitureMovementError.NONE; + } + /** * Slides furniture to a new position. */ @@ -1832,4 +2232,153 @@ public class RoomItemManager { } return FurnitureMovementError.NONE; } + + private boolean shouldCheckUnits(HabboItem item, boolean checkForUnits) { + if (!checkForUnits) { + return false; + } + + if (!Emulator.getConfig().getBoolean("wired.place.under", false)) { + return true; + } + + return !item.isWalkable() + && !item.getBaseItem().allowSit() + && !item.getBaseItem().allowLay(); + } + + private FurnitureMovementError getPhysicsUnitCollision(RoomTile tile, WiredMovementPhysics physics) { + for (RoomUnit roomUnit : this.room.getRoomUnits(tile)) { + if (roomUnit == null) { + continue; + } + + switch (roomUnit.getRoomUnitType()) { + case BOT: + return FurnitureMovementError.TILE_HAS_BOTS; + case PET: + return FurnitureMovementError.TILE_HAS_PETS; + case USER: + if (physics == null || !physics.shouldIgnoreUser(roomUnit)) { + return FurnitureMovementError.TILE_HAS_HABBOS; + } + break; + default: + return FurnitureMovementError.TILE_HAS_HABBOS; + } + } + + return FurnitureMovementError.NONE; + } + + private boolean hasBlockingPhysicsFurni(THashSet occupiedTiles, HabboItem exclude, WiredMovementPhysics physics) { + if (physics == null || !physics.hasBlockingFurni()) { + return false; + } + + for (RoomTile tile : occupiedTiles) { + for (HabboItem item : this.getItemsAt(tile)) { + if (item == null || item == exclude) { + continue; + } + + if (physics.isBlockingFurni(item)) { + return true; + } + } + } + + return false; + } + + private THashSet getPhysicsItemsAt(RoomTile tile, HabboItem exclude, WiredMovementPhysics physics) { + THashSet items = new THashSet<>(); + + for (HabboItem item : this.getItemsAt(tile)) { + if (item == null || item == exclude) { + continue; + } + + if (physics != null && physics.shouldIgnoreFurni(item)) { + continue; + } + + items.add(item); + } + + return items; + } + + private HabboItem getTopPhysicsItemAt(int x, int y, HabboItem exclude, WiredMovementPhysics physics) { + RoomTile tile = this.room.getLayout().getTile((short) x, (short) y); + + if (tile == null) { + return null; + } + + HabboItem highestItem = null; + + for (HabboItem item : this.getPhysicsItemsAt(tile, exclude, physics)) { + if (highestItem != null && highestItem.getZ() + Item.getCurrentHeight(highestItem) + > item.getZ() + Item.getCurrentHeight(item)) { + continue; + } + + highestItem = item; + } + + return highestItem; + } + + private HabboItem getTopPhysicsItemAt(THashSet tiles, HabboItem exclude, WiredMovementPhysics physics) { + HabboItem highestItem = null; + + for (RoomTile tile : tiles) { + if (tile == null) { + continue; + } + + HabboItem topItem = this.getTopPhysicsItemAt(tile.x, tile.y, exclude, physics); + if (topItem == null) { + continue; + } + + if (highestItem != null && highestItem.getZ() + Item.getCurrentHeight(highestItem) + > topItem.getZ() + Item.getCurrentHeight(topItem)) { + continue; + } + + highestItem = topItem; + } + + return highestItem; + } + + private double getPhysicsStackHeight(short x, short y, HabboItem exclude, WiredMovementPhysics physics) { + RoomLayout layout = this.room.getLayout(); + + if (x < 0 || y < 0 || layout == null) { + return 0.0; + } + + double height = layout.getHeightAtSquare(x, y); + + RoomTile tile = layout.getTile(x, y); + if (tile == null) { + return height; + } + + for (HabboItem item : this.getPhysicsItemsAt(tile, exclude, physics)) { + if (item instanceof InteractionStackHelper || item instanceof InteractionTileWalkMagic) { + return item.getZ(); + } + } + + HabboItem topItem = this.getTopPhysicsItemAt(x, y, exclude, physics); + if (topItem != null) { + return topItem.getZ() + (topItem.getBaseItem().allowSit() ? 0 : Item.getCurrentHeight(topItem)); + } + + return height; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java index 6d00aafe..9f6e1b5e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java @@ -30,7 +30,9 @@ import gnu.trove.set.hash.THashSet; import java.awt.*; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -376,6 +378,39 @@ public class RoomSpecialTypes { return countSendersTargetingReceiver(receiverItemId, null); } + public int countSendersTargetingAnyReceiver(Collection receiverItemIds, InteractionWiredEffect excludeSender) { + if (receiverItemIds == null || receiverItemIds.isEmpty()) { + return 0; + } + + Set senders = this.wiredEffects.get(WiredEffectType.SEND_SIGNAL); + if (senders == null) { + return 0; + } + + Set uniqueSenderIds = new HashSet<>(); + + for (InteractionWiredEffect effect : senders) { + if (excludeSender != null && effect.getId() == excludeSender.getId()) continue; + if (!(effect instanceof WiredEffectSendSignal)) continue; + + WiredEffectSendSignal sender = (WiredEffectSendSignal) effect; + for (Integer receiverItemId : receiverItemIds) { + if (receiverItemId == null) continue; + if (!sender.hasPickedItem(receiverItemId)) continue; + + uniqueSenderIds.add(effect.getId()); + break; + } + } + + return uniqueSenderIds.size(); + } + + public int countSendersTargetingAnyReceiver(Collection receiverItemIds) { + return countSendersTargetingAnyReceiver(receiverItemIds, null); + } + public void addTrigger(InteractionWiredTrigger trigger) { // Add to type-based index this.wiredTriggers.computeIfAbsent(trigger.getType(), k -> ConcurrentHashMap.newKeySet()) @@ -686,6 +721,15 @@ public class RoomSpecialTypes { return result; } + /** + * Finds a wired extra by its item ID. + * @param itemId The item ID to search for + * @return The extra if found, null otherwise + */ + public InteractionWiredExtra getExtra(int itemId) { + return this.wiredExtras.get(itemId); + } + /** * Gets all wired extras at specific coordinates using spatial index. * @param x The X coordinate diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java index e7a08aea..66d2e5e5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnit.java @@ -9,6 +9,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWaterItem; import com.eu.habbo.habbohotel.items.interactions.interfaces.ConditionalGate; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.pets.RideablePet; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.users.DanceType; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; @@ -614,6 +616,11 @@ public class RoomUnit { public void setStatus(RoomUnitStatus key, String value) { if (key != null && value != null) { + if (key == RoomUnitStatus.MOVE) { + WiredMoveCarryHelper.clearStatusComposerSuppression(this); + WiredUserMovementHelper.clearStatusComposerSuppression(this); + } + this.status.put(key, value); } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java index 22333091..e18b3823 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomUnitManager.java @@ -14,6 +14,8 @@ import com.eu.habbo.habbohotel.users.HabboGender; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil; import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.wired.WiredUserActionType; import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer; import com.eu.habbo.messages.outgoing.inventory.AddPetComposer; @@ -221,6 +223,9 @@ public class RoomUnitManager { if (habbo.getRoomUnit() != null) { WiredManager.triggerUserLeavesRoom(this.room, habbo.getRoomUnit()); + if (WiredFreezeUtil.isFrozen(habbo.getRoomUnit())) { + WiredFreezeUtil.unfreeze(this.room, habbo.getRoomUnit()); + } } if (habbo.getRoomUnit() != null && habbo.getRoomUnit().getCurrentLocation() != null) { @@ -357,6 +362,11 @@ public class RoomUnitManager { continue; } + if (WiredMoveCarryHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit())) { + continue; + } + double z = habbo.getRoomUnit().getCurrentLocation().getStackHeight(); boolean hadLayStatus = habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); @@ -429,9 +439,17 @@ public class RoomUnitManager { if (!habbos.isEmpty()) { THashSet roomUnits = new THashSet<>(); for (Habbo habbo : habbos) { + if (habbo.getRoomUnit() == null + || WiredMoveCarryHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusUpdate(habbo.getRoomUnit())) { + continue; + } roomUnits.add(habbo.getRoomUnit()); } - this.room.sendComposer(new RoomUserStatusComposer(roomUnits, true).compose()); + + if (!roomUnits.isEmpty()) { + this.room.sendComposer(new RoomUserStatusComposer(roomUnits, true).compose()); + } } if (topItem != null && topItem.getBaseItem().allowLay()) { 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 96172f84..63fe4f9e 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredConditionType.java @@ -36,7 +36,9 @@ public enum WiredConditionType { TEAM_HAS_SCORE(34), TEAM_HAS_RANK(35), MATCH_TIME(36), - MATCH_DATE(37); + MATCH_DATE(37), + ACTOR_DIR(38), + SLC_QUANTITY(39); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index 8ece3410..d8755d12 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -39,7 +39,22 @@ 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), + MOVE_ROTATE_USER(43), + FURNI_ALTITUDE_SELECTOR(44), + FURNI_ON_FURNI_SELECTOR(45), + FURNI_PICKS_SELECTOR(46), + FURNI_SIGNAL_SELECTOR(47), + USERS_SIGNAL_SELECTOR(48), + USERS_BY_TYPE_SELECTOR(49), + USERS_TEAM_SELECTOR(50), + USERS_BY_ACTION_SELECTOR(51), + USERS_BY_NAME_SELECTOR(52), + USERS_ON_FURNI_SELECTOR(53), + USERS_GROUP_SELECTOR(54), + USERS_HANDITEM_SELECTOR(55); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java index 7771450b..5179e503 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredMatchFurniSetting.java @@ -6,13 +6,19 @@ public class WiredMatchFurniSetting { public final int rotation; public final int x; public final int y; + public final double z; public WiredMatchFurniSetting(int itemId, String state, int rotation, int x, int y) { + this(itemId, state, rotation, x, y, 0.0D); + } + + public WiredMatchFurniSetting(int itemId, String state, int rotation, int x, int y, double z) { this.item_id = itemId; this.state = state.replace("\t\t\t", " "); this.rotation = rotation; this.x = x; this.y = y; + this.z = z; } @Override @@ -21,7 +27,7 @@ public class WiredMatchFurniSetting { } public String toString(boolean includeState) { - return this.item_id + "-" + (this.state.isEmpty() || !includeState ? " " : this.state) + "-" + this.rotation + "-" + this.x + "-" + this.y; + return this.item_id + "-" + (this.state.isEmpty() || !includeState ? " " : this.state) + "-" + this.rotation + "-" + this.x + "-" + this.y + "-" + this.z; } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/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/RoomWiredStackIndex.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java index 1002a7ce..8aa7b883 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/RoomWiredStackIndex.java @@ -197,7 +197,7 @@ public final class RoomWiredStackIndex implements WiredStackIndex { } List conditions = new ArrayList<>(rawConditions.size()); - for (InteractionWiredCondition condition : rawConditions) { + for (InteractionWiredCondition condition : WiredExecutionOrderUtil.sort(rawConditions)) { conditions.add(condition); } return conditions; @@ -212,7 +212,7 @@ public final class RoomWiredStackIndex implements WiredStackIndex { } List effects = new ArrayList<>(rawEffects.size()); - for (InteractionWiredEffect effect : rawEffects) { + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(rawEffects)) { effects.add(effect); } return effects; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java new file mode 100644 index 00000000..fa40b95e --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredBotSourceUtil.java @@ -0,0 +1,59 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.bots.Bot; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class WiredBotSourceUtil { + public static final int SOURCE_BOT_NAME = 100; + + private WiredBotSourceUtil() { + } + + public static int normalizeBotSource(int value) { + return normalizeBotSource(value, SOURCE_BOT_NAME); + } + + public static int normalizeBotSource(int value, int fallback) { + switch (value) { + case WiredSourceUtil.SOURCE_TRIGGER: + case SOURCE_BOT_NAME: + case WiredSourceUtil.SOURCE_SELECTOR: + case WiredSourceUtil.SOURCE_SIGNAL: + return value; + default: + return fallback; + } + } + + public static List resolveBots(WiredContext ctx, Room room, int botSource, String botName) { + if (ctx == null || room == null) { + return Collections.emptyList(); + } + + if (botSource == SOURCE_BOT_NAME) { + List bots = room.getBots(botName); + return (bots != null) ? new ArrayList<>(bots) : Collections.emptyList(); + } + + List resolved = new ArrayList<>(); + + for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, botSource)) { + Bot bot = room.getBot(roomUnit); + + if (bot != null && !resolved.contains(bot)) { + resolved.add(bot); + } + } + + return resolved; + } + + public static boolean requiresTriggeringUser(int botSource) { + return botSource == WiredSourceUtil.SOURCE_TRIGGER; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java index 0cc66859..beee0014 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEngine.java @@ -4,7 +4,11 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboSaysKeyword; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; @@ -172,6 +176,7 @@ public final class WiredEngine { debug(room, "Processing {} stacks for event type {}", stacks.size(), event.getType()); boolean anyTriggered = false; + boolean suppressSaysOutput = false; long currentTime = System.currentTimeMillis(); for (WiredStack stack : stacks) { @@ -179,6 +184,12 @@ public final class WiredEngine { boolean triggered = processStack(stack, event, currentTime); if (triggered) { anyTriggered = true; + + if ((event.getType() == WiredEvent.Type.USER_SAYS) + && (stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword) + && ((WiredTriggerHabboSaysKeyword) stack.triggerItem()).isHideMessage()) { + suppressSaysOutput = true; + } } } catch (WiredLimitException limitEx) { debug(room, "Stack execution stopped (limit): {}", limitEx.getMessage()); @@ -188,6 +199,10 @@ public final class WiredEngine { } } + if (event.getType() == WiredEvent.Type.USER_SAYS) { + return suppressSaysOutput; + } + return anyTriggered; } @@ -224,6 +239,7 @@ public final class WiredEngine { List executedSelectors = Collections.emptyList(); if (stack.hasEffects()) { executedSelectors = executeSelectors(stack, ctx); + applySelectionFilterExtras(stack, ctx, executedSelectors); } // Evaluate conditions @@ -245,6 +261,16 @@ public final class WiredEngine { return false; } + if ((event.getType() == WiredEvent.Type.USER_CLICKS_USER) + && (stack.triggerItem() instanceof WiredTriggerHabboClicksUser) + && event.getActor().isPresent()) { + WiredTriggerHabboClicksUser clickUserTrigger = (WiredTriggerHabboClicksUser) stack.triggerItem(); + WiredTriggerHabboClicksUser.applyRuntimeOptions( + event.getActor().get(), + clickUserTrigger.isBlockMenuOpen(), + clickUserTrigger.isDoNotRotate()); + } + RoomUnit actor = event.getActor().orElse(null); // Only show the trigger/selector activation when the stack is actually allowed to continue. @@ -462,6 +488,65 @@ public final class WiredEngine { wiredEffect.activateBox(room, actor, currentTime); } } + + private void applySelectionFilterExtras(WiredStack stack, WiredContext ctx, List executedSelectors) { + if (executedSelectors == null || executedSelectors.isEmpty()) { + return; + } + + Room room = ctx.room(); + if (room == null || stack.triggerItem() == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras( + stack.triggerItem().getX(), + stack.triggerItem().getY()); + + if (extras == null || extras.isEmpty()) { + return; + } + + int furniLimit = Integer.MAX_VALUE; + int userLimit = Integer.MAX_VALUE; + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraFilterFurni) { + furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount()); + } else if (extra instanceof WiredExtraFilterUser) { + userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount()); + } + } + + if (ctx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + ctx.targets().setItems(limitIterable(ctx.targets().items(), furniLimit)); + } + + if (ctx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + ctx.targets().setUsers(limitIterable(ctx.targets().users(), userLimit)); + } + } + + private List limitIterable(Iterable values, int limit) { + List result = new ArrayList<>(); + + if (values == null || limit <= 0) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + if (result.size() <= limit) { + return result; + } + + Collections.shuffle(result, Emulator.getRandom()); + return new ArrayList<>(result.subList(0, limit)); + } /** * Schedule a delayed effect execution. diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/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/WiredExecutionOrderUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredExecutionOrderUtil.java new file mode 100644 index 00000000..2b2bf606 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredExecutionOrderUtil.java @@ -0,0 +1,34 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.users.HabboItem; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +public final class WiredExecutionOrderUtil { + private static final Comparator WIRED_STACK_ORDER = Comparator + .comparingDouble(HabboItem::getZ) + .thenComparingInt(HabboItem::getId); + + private WiredExecutionOrderUtil() { + } + + public static List sort(Collection items) { + List sorted = new ArrayList<>(); + + if (items == null || items.isEmpty()) { + return sorted; + } + + for (T item : items) { + if (item != null) { + sorted.add(item); + } + } + + sorted.sort(WIRED_STACK_ORDER); + return sorted; + } +} 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 38bbfd54..a9cb8753 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredManager.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectGiveReward; import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectTriggerStacks; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; @@ -35,6 +36,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; /** * Manager class for the new wired engine system. @@ -80,6 +83,7 @@ public final class WiredManager { private static final boolean DEFAULT_ENABLED = false; private static final boolean DEFAULT_EXCLUSIVE = false; private static final int DEFAULT_MAX_STEPS = 100; + private static final long FURNI_CLICK_TRIGGER_DELAY_MS = 400L; /** The singleton engine instance */ private static volatile WiredEngine engine; @@ -89,6 +93,7 @@ public final class WiredManager { /** Whether the engine is initialized */ private static volatile boolean initialized = false; + private static final ConcurrentHashMap> pendingFurniClickTriggers = new ConcurrentHashMap<>(); private WiredManager() { // Static utility class @@ -163,6 +168,13 @@ public final class WiredManager { engine.clearUnseenCache(); } + pendingFurniClickTriggers.values().forEach(future -> { + if (future != null) { + future.cancel(false); + } + }); + pendingFurniClickTriggers.clear(); + initialized = false; LOGGER.info("Wired Manager shutdown complete"); } @@ -250,6 +262,40 @@ public final class WiredManager { return handleEvent(event); } + public static void queueUserClicksFurni(Room room, RoomUnit user, HabboItem item) { + if (!isEnabled() || room == null || user == null || item == null) { + return; + } + + String clickKey = getPendingFurniClickKey(room, user, item); + + cancelPendingUserClicksFurni(room, user, item); + + ScheduledFuture future = Emulator.getThreading().run(() -> { + pendingFurniClickTriggers.remove(clickKey); + triggerUserClicksFurni(room, user, item); + }, FURNI_CLICK_TRIGGER_DELAY_MS); + + if (future != null) { + pendingFurniClickTriggers.put(clickKey, future); + } + } + + public static void cancelPendingUserClicksFurni(Room room, RoomUnit user, HabboItem item) { + if (room == null || user == null || item == null) { + return; + } + + ScheduledFuture future = pendingFurniClickTriggers.remove(getPendingFurniClickKey(room, user, item)); + + if (future != null) { + future.cancel(false); + } + } + + private static String getPendingFurniClickKey(Room room, RoomUnit user, HabboItem item) { + return room.getId() + ":" + user.getId() + ":" + item.getId(); + } /** * Trigger when a user clicks invisible click tile furniture. */ @@ -270,6 +316,7 @@ public final class WiredManager { return false; } + WiredTriggerHabboClicksUser.clearRuntimeFlags(clickingUser); WiredEvent event = WiredEvents.userClicksUser(room, clickingUser, clickedUser); return handleEvent(event); } @@ -362,6 +409,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/core/WiredMoveCarryHelper.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMoveCarryHelper.java new file mode 100644 index 00000000..40b86acd --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMoveCarryHelper.java @@ -0,0 +1,714 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimationTime; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhysics; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation; +import com.eu.habbo.habbohotel.rooms.FurnitureMovementError; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; +import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; +import gnu.trove.set.hash.THashSet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public final class WiredMoveCarryHelper { + private static final double DIRECT_HEIGHT_TOLERANCE = 0.1D; + private static final int STATUS_SUPPRESSION_GRACE_MS = 250; + private static final ThreadLocal> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>(); + private static final ConcurrentHashMap SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>(); + + private WiredMoveCarryHelper() { + } + + public static FurnitureMovementError getMovementError(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, WiredContext ctx) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (!hasMovementBehaviorExtra(room, stackItem)) { + return room.furnitureFitsAt(targetTile, movingItem, rotation, true); + } + + CarryContext carryContext = prepareCarryContext(room, stackItem, movingItem, ctx); + WiredMovementPhysics movementPhysics = getMovementPhysics(room, stackItem, movingItem, ctx); + FurnitureMovementError movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, false, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + + if (!carryContext.active) { + return room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, true, movementPhysics); + } + + return getBlockingUnitError(room, movingItem, targetTile, rotation, carryContext, movementPhysics); + } + + public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Habbo actor, boolean sendUpdates, WiredContext ctx) { + return moveFurni(room, stackItem, movingItem, targetTile, rotation, null, actor, sendUpdates, ctx); + } + + public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + if (!hasMovementBehaviorExtra(room, stackItem)) { + return moveFurniLegacy(room, movingItem, targetTile, rotation, z, actor, sendUpdates); + } + + RoomTile oldLocation = room.getLayout() == null ? null : room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + double oldZ = movingItem.getZ(); + CarryContext carryContext = prepareCarryContext(room, stackItem, movingItem, ctx); + WiredMovementPhysics movementPhysics = getMovementPhysics(room, stackItem, movingItem, ctx); + FurnitureMovementError movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, false, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + + if (carryContext.active) { + movementError = getBlockingUnitError(room, movingItem, targetTile, rotation, carryContext, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + } else { + movementError = room.furnitureFitsAtWithPhysics(targetTile, movingItem, rotation, true, movementPhysics); + + if (movementError != FurnitureMovementError.NONE) { + return movementError; + } + } + + boolean useWiredMovements = !hasNoAnimationExtra(room, stackItem); + int animationDuration = getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION); + Set previousSuppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + + if (carryContext.active) { + HashSet suppressedRoomUnitIds = previousSuppressedRoomUnitIds == null + ? new HashSet<>() + : new HashSet<>(previousSuppressedRoomUnitIds); + suppressedRoomUnitIds.addAll(carryContext.carriedUserIds); + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(suppressedRoomUnitIds); + } + + FurnitureMovementError result; + Double targetZ = z; + + if (targetZ == null && movementPhysics.isKeepAltitude()) { + targetZ = oldZ; + } + + try { + result = (targetZ == null) + ? room.moveFurniToWithPhysics(movingItem, targetTile, rotation, actor, !useWiredMovements, false, movementPhysics) + : room.moveFurniToWithPhysics(movingItem, targetTile, rotation, targetZ, actor, !useWiredMovements, false, movementPhysics); + } finally { + if (carryContext.active) { + if (previousSuppressedRoomUnitIds == null || previousSuppressedRoomUnitIds.isEmpty()) { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.remove(); + } else { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(previousSuppressedRoomUnitIds); + } + } + } + + if (result == FurnitureMovementError.NONE) { + if (!useWiredMovements) { + applyInstantCarryState(room, movingItem, targetTile, rotation, carryContext); + } else if (oldLocation != null) { + sendAnimatedMove(room, movingItem, oldLocation, oldZ, targetTile, rotation, carryContext, animationDuration); + } + } + + return result; + } + + private static FurnitureMovementError moveFurniLegacy(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates) { + if (room == null || movingItem == null || targetTile == null) { + return FurnitureMovementError.INVALID_MOVE; + } + + RoomTile oldLocation = room.getLayout() == null ? null : room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + double oldZ = movingItem.getZ(); + + FurnitureMovementError result = (z == null) + ? room.moveFurniTo(movingItem, targetTile, rotation, actor, sendUpdates) + : room.moveFurniTo(movingItem, targetTile, rotation, z, actor, sendUpdates, false); + + if (result == FurnitureMovementError.NONE + && !sendUpdates + && oldLocation != null + && (oldLocation.x != targetTile.x || oldLocation.y != targetTile.y || Double.compare(oldZ, movingItem.getZ()) != 0)) { + room.sendComposer(new FloorItemOnRollerComposer(movingItem, null, oldLocation, oldZ, targetTile, movingItem.getZ(), 0, room).compose()); + } + + return result; + } + + public static boolean shouldSuppressStatusUpdate(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Set suppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + return suppressedRoomUnitIds != null && suppressedRoomUnitIds.contains(roomUnit.getId()); + } + + public static boolean shouldSuppressStatusComposer(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Long suppressedUntil = SUPPRESSED_STATUS_COMPOSER_UNTIL.get(roomUnit.getId()); + + if (suppressedUntil == null) { + return false; + } + + if (suppressedUntil <= System.currentTimeMillis()) { + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId(), suppressedUntil); + return false; + } + + return true; + } + + public static void clearStatusComposerSuppression(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId()); + } + + public static boolean hasNoAnimationExtra(Room room, HabboItem stackItem) { + return getNoAnimationExtra(room, stackItem) != null; + } + + public static int getAnimationDuration(Room room, HabboItem stackItem, int fallbackDuration) { + WiredExtraAnimationTime extra = getAnimationTimeExtra(room, stackItem); + return (extra != null) ? extra.getDurationMs() : fallbackDuration; + } + + private static boolean hasMovementBehaviorExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return false; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveCarryUsers + || extra instanceof WiredExtraMoveNoAnimation + || extra instanceof WiredExtraAnimationTime + || extra instanceof WiredExtraMovePhysics) { + return true; + } + } + + return false; + } + + private static CarryContext prepareCarryContext(Room room, HabboItem stackItem, HabboItem movingItem, WiredContext ctx) { + WiredExtraMoveCarryUsers extra = getActiveExtra(room, stackItem); + + if (extra == null || ctx == null || room.getLayout() == null) { + return CarryContext.disabled(); + } + + RoomTile anchorTile = room.getLayout().getTile(movingItem.getX(), movingItem.getY()); + if (anchorTile == null) { + return CarryContext.disabled(); + } + + THashSet occupiedTiles = room.getLayout().getTilesAt( + anchorTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + movingItem.getRotation()); + + if (occupiedTiles == null || occupiedTiles.isEmpty()) { + return CarryContext.disabled(); + } + + Collection targetUsers = resolveUsers(room, ctx, extra.getUserSource()); + if (targetUsers == null || targetUsers.isEmpty()) { + return CarryContext.disabled(); + } + + List carriedUnits = new ArrayList<>(); + HashSet carriedIds = new HashSet<>(); + + for (RoomUnit roomUnit : targetUsers) { + if (!isEligibleUser(room, movingItem, roomUnit, occupiedTiles, extra.getCarryMode())) { + continue; + } + + CarriedRoomUnit carriedRoomUnit = new CarriedRoomUnit( + roomUnit, + roomUnit.getCurrentLocation(), + roomUnit.getZ(), + roomUnit.getZ() - getCarrySurfaceZ(movingItem, roomUnit, roomUnit.getZ()), + roomUnit.getX() - anchorTile.x, + roomUnit.getY() - anchorTile.y); + + carriedUnits.add(carriedRoomUnit); + carriedIds.add(roomUnit.getId()); + } + + if (carriedUnits.isEmpty()) { + return CarryContext.disabled(); + } + + return new CarryContext(true, carriedUnits, carriedIds); + } + + private static Collection resolveUsers(Room room, WiredContext ctx, int userSource) { + if (userSource == WiredExtraMoveCarryUsers.SOURCE_ALL_ROOM_USERS) { + return new ArrayList<>(room.getRoomUnits()); + } + + return WiredSourceUtil.resolveUsers(ctx, userSource); + } + + private static boolean isEligibleUser(Room room, HabboItem movingItem, RoomUnit roomUnit, THashSet occupiedTiles, int carryMode) { + if (roomUnit == null + || roomUnit.getRoomUnitType() != RoomUnitType.USER + || roomUnit.getCurrentLocation() == null + || !roomUnit.isInRoom() + || roomUnit.isWalking()) { + return false; + } + + Habbo habbo = room.getHabbo(roomUnit); + if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getRiding() != null) { + return false; + } + + if (!occupiesMovingFurni(occupiedTiles, roomUnit)) { + return false; + } + + if (carryMode == WiredExtraMoveCarryUsers.MODE_DIRECTLY_ON_FURNI) { + return isDirectlyOnMovingFurni(room, movingItem, roomUnit); + } + + return true; + } + + private static boolean isDirectlyOnMovingFurni(Room room, HabboItem movingItem, RoomUnit roomUnit) { + HabboItem topItem = room.getTopItemAt(roomUnit.getX(), roomUnit.getY()); + if (topItem == movingItem) { + return true; + } + + double carrySurfaceZ = getCarrySurfaceZ(movingItem, roomUnit, roomUnit.getZ()); + return Math.abs(roomUnit.getZ() - carrySurfaceZ) <= DIRECT_HEIGHT_TOLERANCE; + } + + private static boolean occupiesMovingFurni(THashSet occupiedTiles, RoomUnit roomUnit) { + for (RoomTile occupiedTile : occupiedTiles) { + if (occupiedTile != null + && occupiedTile.x == roomUnit.getX() + && occupiedTile.y == roomUnit.getY()) { + return true; + } + } + + return false; + } + + private static FurnitureMovementError getBlockingUnitError(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext, WiredMovementPhysics movementPhysics) { + THashSet occupiedTiles = room.getLayout().getTilesAt( + targetTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + rotation); + + if (occupiedTiles == null || occupiedTiles.isEmpty()) { + return FurnitureMovementError.NONE; + } + + for (RoomTile tile : occupiedTiles) { + for (RoomUnit roomUnit : room.getRoomUnits(tile)) { + if (roomUnit == null || carryContext.carriedUserIds.contains(roomUnit.getId())) { + continue; + } + + if (movementPhysics.shouldIgnoreUser(roomUnit)) { + continue; + } + + switch (roomUnit.getRoomUnitType()) { + case BOT: + return FurnitureMovementError.TILE_HAS_BOTS; + case PET: + return FurnitureMovementError.TILE_HAS_PETS; + case USER: + default: + return FurnitureMovementError.TILE_HAS_HABBOS; + } + } + } + + return FurnitureMovementError.NONE; + } + + private static void sendAnimatedMove(Room room, HabboItem movingItem, RoomTile oldLocation, double oldZ, RoomTile targetTile, int rotation, CarryContext carryContext, int animationDuration) { + List carriedMoves = getCarriedUnitMoves(room, movingItem, targetTile, rotation, carryContext); + List movements = new ArrayList<>(); + movements.add(WiredMovementsComposer.furniMovement( + movingItem.getId(), + oldLocation.x, + oldLocation.y, + targetTile.x, + targetTile.y, + oldZ, + movingItem.getZ(), + movingItem.getRotation(), + animationDuration)); + + for (CarriedUnitMove carriedMove : carriedMoves) { + suppressStatusComposer(carriedMove.roomUnit, animationDuration); + movements.add(WiredMovementsComposer.userSlideMovement( + carriedMove.roomUnit.getId(), + carriedMove.oldLocation.x, + carriedMove.oldLocation.y, + carriedMove.destinationTile.x, + carriedMove.destinationTile.y, + carriedMove.oldZ, + carriedMove.newZ, + carriedMove.roomUnit.getBodyRotation().getValue(), + carriedMove.roomUnit.getHeadRotation().getValue(), + animationDuration)); + } + + room.sendComposer(new WiredMovementsComposer(movements).compose()); + + for (CarriedUnitMove carriedMove : carriedMoves) { + updateCarriedUnitState(carriedMove); + } + } + + private static void applyInstantCarryState(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext) { + if (!carryContext.active || room == null || movingItem == null || targetTile == null) { + return; + } + + List carriedMoves = getCarriedUnitMoves(room, movingItem, targetTile, rotation, carryContext); + + for (CarriedUnitMove carriedMove : carriedMoves) { + updateCarriedUnitStateInstant(carriedMove); + + Habbo habbo = room.getHabbo(carriedMove.roomUnit); + if (habbo != null && shouldRefreshPostureWithTileUpdate(carriedMove.roomUnit)) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(carriedMove.destinationTile.x, carriedMove.destinationTile.y, movedHabbos); + } + + room.sendComposer(new RoomUserStatusComposer(carriedMove.roomUnit).compose()); + } + } + + private static List getCarriedUnitMoves(Room room, HabboItem movingItem, RoomTile targetTile, int rotation, CarryContext carryContext) { + List carriedMoves = new ArrayList<>(); + + if (!carryContext.active) { + return carriedMoves; + } + + THashSet occupiedTiles = room.getLayout().getTilesAt( + targetTile, + movingItem.getBaseItem().getWidth(), + movingItem.getBaseItem().getLength(), + rotation); + + for (CarriedRoomUnit carriedRoomUnit : carryContext.carriedUsers) { + RoomUnit roomUnit = carriedRoomUnit.roomUnit; + + if (roomUnit == null || roomUnit.getCurrentLocation() == null || roomUnit.isWalking()) { + continue; + } + + RoomTile destinationTile = room.getLayout().getTile( + (short) (targetTile.x + carriedRoomUnit.relativeX), + (short) (targetTile.y + carriedRoomUnit.relativeY)); + + if (destinationTile == null || destinationTile.state == null || !occupiedTiles.contains(destinationTile)) { + destinationTile = targetTile; + } + + double carrySurfaceZ = getCarrySurfaceZ(movingItem, roomUnit, carriedRoomUnit.oldZ); + double newZ = carrySurfaceZ + carriedRoomUnit.heightOffset; + carriedMoves.add(new CarriedUnitMove(roomUnit, carriedRoomUnit.oldLocation, carriedRoomUnit.oldZ, destinationTile, newZ)); + } + + return carriedMoves; + } + + private static boolean shouldRefreshPostureWithTileUpdate(RoomUnit roomUnit) { + return roomUnit != null + && (roomUnit.hasStatus(RoomUnitStatus.SIT) || roomUnit.hasStatus(RoomUnitStatus.LAY)); + } + + private static double getCarrySurfaceZ(HabboItem movingItem, RoomUnit roomUnit, double referenceZ) { + if (movingItem == null) { + return referenceZ; + } + + double baseZ = movingItem.getZ(); + double topZ = baseZ + Item.getCurrentHeight(movingItem); + + if (roomUnit != null && (roomUnit.hasStatus(RoomUnitStatus.SIT) || roomUnit.hasStatus(RoomUnitStatus.LAY))) { + return baseZ; + } + + if (movingItem.getBaseItem().allowSit() || movingItem.getBaseItem().allowLay()) { + return (Math.abs(referenceZ - baseZ) <= Math.abs(referenceZ - topZ)) ? baseZ : topZ; + } + + return topZ; + } + + private static void updateCarriedUnitState(CarriedUnitMove carriedMove) { + carriedMove.roomUnit.setLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setZ(carriedMove.newZ); + carriedMove.roomUnit.setLastRollerTime(System.currentTimeMillis()); + carriedMove.roomUnit.setPreviousLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setPreviousLocationZ(carriedMove.newZ); + + if (carriedMove.roomUnit.hasStatus(RoomUnitStatus.SIT)) { + carriedMove.roomUnit.sitUpdate = true; + } + } + + private static void updateCarriedUnitStateInstant(CarriedUnitMove carriedMove) { + carriedMove.roomUnit.setLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setZ(carriedMove.newZ); + carriedMove.roomUnit.setPreviousLocation(carriedMove.destinationTile); + carriedMove.roomUnit.setPreviousLocationZ(carriedMove.newZ); + carriedMove.roomUnit.statusUpdate(false); + + if (carriedMove.roomUnit.hasStatus(RoomUnitStatus.SIT)) { + carriedMove.roomUnit.sitUpdate = true; + } + } + + private static void suppressStatusComposer(RoomUnit roomUnit, int duration) { + if (roomUnit == null) { + return; + } + + long suppressedUntil = System.currentTimeMillis() + Math.max(duration, WiredMovementsComposer.DEFAULT_DURATION) + STATUS_SUPPRESSION_GRACE_MS; + SUPPRESSED_STATUS_COMPOSER_UNTIL.put(roomUnit.getId(), suppressedUntil); + } + + private static WiredExtraMoveCarryUsers getActiveExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveCarryUsers) { + return (WiredExtraMoveCarryUsers) extra; + } + } + + return null; + } + + private static WiredExtraMoveNoAnimation getNoAnimationExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMoveNoAnimation) { + return (WiredExtraMoveNoAnimation) extra; + } + } + + return null; + } + + private static WiredExtraAnimationTime getAnimationTimeExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraAnimationTime) { + return (WiredExtraAnimationTime) extra; + } + } + + return null; + } + + private static WiredMovementPhysics getMovementPhysics(Room room, HabboItem stackItem, HabboItem movingItem, WiredContext ctx) { + WiredExtraMovePhysics extra = getMovementPhysicsExtra(room, stackItem); + if (extra == null) { + return WiredMovementPhysics.NONE; + } + + HashSet passThroughFurniIds = new HashSet<>(); + HashSet passThroughUserIds = new HashSet<>(); + HashSet blockingFurniIds = new HashSet<>(); + + if (extra.isMoveThroughFurni()) { + for (HabboItem item : resolveFurniSources(room, ctx, extra.getMoveThroughFurniSource())) { + if (item != null && item != movingItem) { + passThroughFurniIds.add(item.getId()); + } + } + } + + if (extra.isMoveThroughUsers()) { + for (RoomUnit roomUnit : resolvePhysicsUsers(room, ctx, extra.getMoveThroughUsersSource())) { + if (roomUnit != null && roomUnit.getRoomUnitType() == RoomUnitType.USER) { + passThroughUserIds.add(roomUnit.getId()); + } + } + } + + if (extra.isBlockByFurni()) { + for (HabboItem item : resolveFurniSources(room, ctx, extra.getBlockByFurniSource())) { + if (item != null && item != movingItem) { + blockingFurniIds.add(item.getId()); + } + } + } + + return new WiredMovementPhysics(extra.isKeepAltitude(), passThroughFurniIds, passThroughUserIds, blockingFurniIds); + } + + private static Collection resolveFurniSources(Room room, WiredContext ctx, int sourceType) { + if (room == null) { + return new ArrayList<>(); + } + + if (sourceType == WiredExtraMovePhysics.SOURCE_ALL_ROOM) { + return new ArrayList<>(room.getFloorItems()); + } + + if (ctx == null) { + return new ArrayList<>(); + } + + return WiredSourceUtil.resolveItems(ctx, sourceType, null); + } + + private static Collection resolvePhysicsUsers(Room room, WiredContext ctx, int userSource) { + if (room == null) { + return new ArrayList<>(); + } + + if (userSource == WiredExtraMovePhysics.SOURCE_ALL_ROOM) { + return new ArrayList<>(room.getRoomUnits()); + } + + if (ctx == null) { + return new ArrayList<>(); + } + + return WiredSourceUtil.resolveUsers(ctx, userSource); + } + + private static WiredExtraMovePhysics getMovementPhysicsExtra(Room room, HabboItem stackItem) { + THashSet extras = getMovementExtras(room, stackItem); + if (extras == null || extras.isEmpty()) { + return null; + } + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraMovePhysics) { + return (WiredExtraMovePhysics) extra; + } + } + + return null; + } + + private static THashSet getMovementExtras(Room room, HabboItem stackItem) { + if (room == null || stackItem == null || room.getRoomSpecialTypes() == null) { + return null; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(stackItem.getX(), stackItem.getY()); + if (extras == null || extras.isEmpty()) { + return null; + } + + return extras; + } + + private static final class CarryContext { + private final boolean active; + private final List carriedUsers; + private final HashSet carriedUserIds; + + private CarryContext(boolean active, List carriedUsers, HashSet carriedUserIds) { + this.active = active; + this.carriedUsers = carriedUsers; + this.carriedUserIds = carriedUserIds; + } + + private static CarryContext disabled() { + return new CarryContext(false, new ArrayList<>(), new HashSet<>()); + } + } + + private static final class CarriedRoomUnit { + private final RoomUnit roomUnit; + private final RoomTile oldLocation; + private final double oldZ; + private final double heightOffset; + private final int relativeX; + private final int relativeY; + + private CarriedRoomUnit(RoomUnit roomUnit, RoomTile oldLocation, double oldZ, double heightOffset, int relativeX, int relativeY) { + this.roomUnit = roomUnit; + this.oldLocation = oldLocation; + this.oldZ = oldZ; + this.heightOffset = heightOffset; + this.relativeX = relativeX; + this.relativeY = relativeY; + } + } + + private static final class CarriedUnitMove { + private final RoomUnit roomUnit; + private final RoomTile oldLocation; + private final double oldZ; + private final RoomTile destinationTile; + private final double newZ; + + private CarriedUnitMove(RoomUnit roomUnit, RoomTile oldLocation, double oldZ, RoomTile destinationTile, double newZ) { + this.roomUnit = roomUnit; + this.oldLocation = oldLocation; + this.oldZ = oldZ; + this.destinationTile = destinationTile; + this.newZ = newZ; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java new file mode 100644 index 00000000..538baa2b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredMovementPhysics.java @@ -0,0 +1,56 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitType; +import com.eu.habbo.habbohotel.users.HabboItem; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public final class WiredMovementPhysics { + public static final WiredMovementPhysics NONE = new WiredMovementPhysics(false, Collections.emptySet(), Collections.emptySet(), Collections.emptySet()); + + private final boolean keepAltitude; + private final Set passThroughFurniIds; + private final Set passThroughUserIds; + private final Set blockingFurniIds; + + public WiredMovementPhysics(boolean keepAltitude, Set passThroughFurniIds, Set passThroughUserIds, Set blockingFurniIds) { + this.keepAltitude = keepAltitude; + this.passThroughFurniIds = Collections.unmodifiableSet(new HashSet<>(passThroughFurniIds)); + this.passThroughUserIds = Collections.unmodifiableSet(new HashSet<>(passThroughUserIds)); + this.blockingFurniIds = Collections.unmodifiableSet(new HashSet<>(blockingFurniIds)); + } + + public boolean isKeepAltitude() { + return this.keepAltitude; + } + + public boolean isActive() { + return this.keepAltitude + || !this.passThroughFurniIds.isEmpty() + || !this.passThroughUserIds.isEmpty() + || !this.blockingFurniIds.isEmpty(); + } + + public boolean hasBlockingFurni() { + return !this.blockingFurniIds.isEmpty(); + } + + public boolean shouldIgnoreFurni(HabboItem item) { + return item != null + && this.passThroughFurniIds.contains(item.getId()) + && !this.blockingFurniIds.contains(item.getId()); + } + + public boolean isBlockingFurni(HabboItem item) { + return item != null && this.blockingFurniIds.contains(item.getId()); + } + + public boolean shouldIgnoreUser(RoomUnit roomUnit) { + return roomUnit != null + && roomUnit.getRoomUnitType() == RoomUnitType.USER + && this.passThroughUserIds.contains(roomUnit.getId()); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java index ed65404a..b8ec7046 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredSourceUtil.java @@ -1,7 +1,15 @@ package com.eu.habbo.habbohotel.wired.core; +import com.eu.habbo.Emulator; +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.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.api.IWiredEffect; +import gnu.trove.set.hash.THashSet; import java.util.ArrayList; import java.util.Collection; @@ -24,8 +32,9 @@ public final class WiredSourceUtil { case SOURCE_SELECTED: return (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList(); case SOURCE_SELECTOR: - return ctx.targets().isItemsModifiedBySelector() - ? new ArrayList<>(ctx.targets().items()) + WiredTargets itemTargets = getSelectorTargets(ctx); + return itemTargets.isItemsModifiedBySelector() + ? new ArrayList<>(itemTargets.items()) : Collections.emptyList(); case SOURCE_SIGNAL: if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { @@ -48,8 +57,9 @@ public final class WiredSourceUtil { case SOURCE_SELECTED: return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList(); case SOURCE_SELECTOR: - return ctx.targets().isUsersModifiedBySelector() - ? new ArrayList<>(ctx.targets().users()) + WiredTargets userTargets = getSelectorTargets(ctx); + return userTargets.isUsersModifiedBySelector() + ? new ArrayList<>(userTargets.users()) : Collections.emptyList(); case SOURCE_SIGNAL: if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) { @@ -60,4 +70,147 @@ public final class WiredSourceUtil { return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList()); } } + + private static WiredTargets getSelectorTargets(WiredContext ctx) { + if (ctx == null) { + return new WiredTargets(); + } + + if (ctx.targets().isItemsModifiedBySelector() || ctx.targets().isUsersModifiedBySelector()) { + return ctx.targets(); + } + + WiredContext selectorContext = executeSelectors(ctx); + + if (selectorContext == null) { + return ctx.targets(); + } + + if (selectorContext.targets().isItemsModifiedBySelector()) { + ctx.targets().setItems(selectorContext.targets().items()); + } + + if (selectorContext.targets().isUsersModifiedBySelector()) { + ctx.targets().setUsers(selectorContext.targets().users()); + } + + return ctx.targets(); + } + + private static WiredContext executeSelectors(WiredContext originalCtx) { + if (originalCtx == null) { + return null; + } + + Room room = originalCtx.room(); + HabboItem triggerItem = originalCtx.triggerItem(); + + if (room == null || triggerItem == null || room.getRoomSpecialTypes() == null) { + return null; + } + + WiredContext selectorCtx = new WiredContext( + originalCtx.event(), + triggerItem, + originalCtx.stack(), + originalCtx.services(), + new WiredState(100), + originalCtx.legacySettings() + ); + + List selectorEffects = getOrderedSelectorEffects(originalCtx, room, triggerItem); + + for (InteractionWiredEffect effect : selectorEffects) { + if (effect.requiresActor() && !selectorCtx.hasActor()) { + continue; + } + + try { + selectorCtx.state().step(); + effect.execute(selectorCtx); + } catch (Exception ignored) { + } + } + + applySelectionFilterExtras(room, triggerItem, selectorCtx); + + return selectorCtx; + } + + private static List getOrderedSelectorEffects(WiredContext originalCtx, Room room, HabboItem triggerItem) { + List selectorEffects = new ArrayList<>(); + + if (originalCtx != null && originalCtx.hasStack()) { + for (IWiredEffect effect : originalCtx.stack().effects()) { + if (effect instanceof InteractionWiredEffect && effect.isSelector()) { + selectorEffects.add((InteractionWiredEffect) effect); + } + } + + if (!selectorEffects.isEmpty()) { + return selectorEffects; + } + } + + THashSet roomEffects = room.getRoomSpecialTypes().getEffects(triggerItem.getX(), triggerItem.getY()); + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(roomEffects)) { + if (effect != null && effect.isSelector()) { + selectorEffects.add(effect); + } + } + + return selectorEffects; + } + + private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) { + if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY()); + + if (extras == null || extras.isEmpty()) { + return; + } + + int furniLimit = Integer.MAX_VALUE; + int userLimit = Integer.MAX_VALUE; + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraFilterFurni) { + furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount()); + } else if (extra instanceof WiredExtraFilterUser) { + userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount()); + } + } + + if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit)); + } + + if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit)); + } + } + + private static List limitIterable(Iterable values, int limit) { + List result = new ArrayList<>(); + + if (values == null || limit <= 0) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + if (result.size() <= limit) { + return result; + } + + Collections.shuffle(result, Emulator.getRandom()); + return new ArrayList<>(result.subList(0, limit)); + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java new file mode 100644 index 00000000..17bad58f --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredTriggerSourceUtil.java @@ -0,0 +1,212 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni; +import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import gnu.trove.set.hash.THashSet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class WiredTriggerSourceUtil { + private WiredTriggerSourceUtil() { + } + + public static List resolveItems(InteractionWiredTrigger trigger, + WiredEvent event, + int sourceType, + Collection selectedItems) { + switch (sourceType) { + case WiredSourceUtil.SOURCE_TRIGGER: + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + case WiredSourceUtil.SOURCE_SELECTED: + return (selectedItems != null) ? new ArrayList<>(selectedItems) : Collections.emptyList(); + case WiredSourceUtil.SOURCE_SELECTOR: + return resolveSelectorItems(trigger, event); + case WiredSourceUtil.SOURCE_SIGNAL: + if (event.getType() == WiredEvent.Type.SIGNAL_RECEIVED) { + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + } + return Collections.emptyList(); + default: + return event.getSourceItem().map(Collections::singletonList).orElse(Collections.emptyList()); + } + } + + public static List resolveUsers(InteractionWiredTrigger trigger, + WiredEvent event, + int sourceType, + Collection selectedUsers) { + switch (sourceType) { + case WiredSourceUtil.SOURCE_TRIGGER: + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + case WiredSourceUtil.SOURCE_SELECTED: + return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList(); + case WiredSourceUtil.SOURCE_SELECTOR: + return resolveSelectorUsers(trigger, event); + case WiredSourceUtil.SOURCE_SIGNAL: + if (event.getType() == WiredEvent.Type.SIGNAL_RECEIVED) { + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + } + return Collections.emptyList(); + default: + return event.getActor().map(Collections::singletonList).orElse(Collections.emptyList()); + } + } + + public static boolean containsItemOrTile(Room room, Collection items, HabboItem sourceItem) { + if (room == null || items == null || items.isEmpty() || sourceItem == null) { + return false; + } + + if (items.contains(sourceItem)) { + return true; + } + + for (HabboItem item : room.getItemsAt(sourceItem.getX(), sourceItem.getY())) { + if (items.contains(item)) { + return true; + } + } + + return false; + } + + public static boolean containsUser(Collection users, RoomUnit target) { + if (users == null || users.isEmpty() || target == null) { + return false; + } + + for (RoomUnit user : users) { + if (user != null && user.getId() == target.getId()) { + return true; + } + } + + return false; + } + + private static List resolveSelectorItems(InteractionWiredTrigger trigger, WiredEvent event) { + WiredContext ctx = executeSelectors(trigger, event); + + if (ctx == null || !ctx.targets().isItemsModifiedBySelector()) { + return Collections.emptyList(); + } + + return new ArrayList<>(ctx.targets().items()); + } + + private static List resolveSelectorUsers(InteractionWiredTrigger trigger, WiredEvent event) { + WiredContext ctx = executeSelectors(trigger, event); + + if (ctx == null || !ctx.targets().isUsersModifiedBySelector()) { + return Collections.emptyList(); + } + + return new ArrayList<>(ctx.targets().users()); + } + + private static WiredContext executeSelectors(InteractionWiredTrigger trigger, WiredEvent event) { + if (trigger == null || event == null) { + return null; + } + + Room room = event.getRoom(); + if (room == null || room.getRoomSpecialTypes() == null) { + return null; + } + + WiredContext ctx = new WiredContext(event, trigger, DefaultWiredServices.getInstance(), new WiredState(100)); + + for (InteractionWiredEffect effect : getOrderedSelectorEffects(room, trigger)) { + if (effect.requiresActor() && !ctx.hasActor()) { + continue; + } + + try { + ctx.state().step(); + effect.execute(ctx); + } catch (Exception ignored) { + } + } + + applySelectionFilterExtras(room, trigger, ctx); + + return ctx; + } + + private static List getOrderedSelectorEffects(Room room, InteractionWiredTrigger trigger) { + if (room == null || trigger == null || room.getRoomSpecialTypes() == null) { + return Collections.emptyList(); + } + + THashSet effects = room.getRoomSpecialTypes().getEffects(trigger.getX(), trigger.getY()); + List selectorEffects = new ArrayList<>(); + + for (InteractionWiredEffect effect : WiredExecutionOrderUtil.sort(effects)) { + if (effect != null && effect.isSelector()) { + selectorEffects.add(effect); + } + } + + return selectorEffects; + } + + private static void applySelectionFilterExtras(Room room, HabboItem triggerItem, WiredContext selectorCtx) { + if (room == null || triggerItem == null || selectorCtx == null || room.getRoomSpecialTypes() == null) { + return; + } + + THashSet extras = room.getRoomSpecialTypes().getExtras(triggerItem.getX(), triggerItem.getY()); + + if (extras == null || extras.isEmpty()) { + return; + } + + int furniLimit = Integer.MAX_VALUE; + int userLimit = Integer.MAX_VALUE; + + for (InteractionWiredExtra extra : extras) { + if (extra instanceof WiredExtraFilterFurni) { + furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount()); + } else if (extra instanceof WiredExtraFilterUser) { + userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount()); + } + } + + if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit)); + } + + if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) { + selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit)); + } + } + + private static List limitIterable(Iterable values, int limit) { + List result = new ArrayList<>(); + + if (values == null || limit <= 0) { + return result; + } + + for (T value : values) { + if (value != null) { + result.add(value); + } + } + + if (result.size() <= limit) { + return result; + } + + return new ArrayList<>(result.subList(0, limit)); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java new file mode 100644 index 00000000..9777db22 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredUserMovementHelper.java @@ -0,0 +1,305 @@ +package com.eu.habbo.habbohotel.wired.core; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.interactions.InteractionRoller; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.rooms.RoomUserRotation; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public final class WiredUserMovementHelper { + public static final int DEFAULT_ANIMATION_DURATION = WiredMovementsComposer.DEFAULT_DURATION; + private static final int STATUS_SUPPRESSION_GRACE_MS = 250; + + private static final Logger LOGGER = LoggerFactory.getLogger(WiredUserMovementHelper.class); + private static final ThreadLocal> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>(); + private static final ConcurrentHashMap SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>(); + + private WiredUserMovementHelper() { + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, int duration) { + return moveUser(room, roomUnit, targetTile, targetZ, roomUnit == null ? null : roomUnit.getBodyRotation(), + roomUnit == null ? null : roomUnit.getHeadRotation(), duration, false); + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration) { + return moveUser(room, roomUnit, targetTile, targetZ, bodyRotation, headRotation, duration, false); + } + + public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration, boolean noAnimation) { + if (room == null || roomUnit == null || targetTile == null || room.getLayout() == null) { + return false; + } + + RoomTile oldLocation = roomUnit.getCurrentLocation(); + if (oldLocation == null || hasBlockingUnits(room, roomUnit, targetTile)) { + return false; + } + + 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); + } + + runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> { + roomUnit.setPreviousLocation(oldLocation); + roomUnit.setCurrentLocation(targetTile); + roomUnit.removeStatus(RoomUnitStatus.MOVE); + roomUnit.setZ(targetZ); + roomUnit.setBodyRotation(resolvedBodyRotation); + roomUnit.setHeadRotation(resolvedHeadRotation); + roomUnit.stopWalking(); + roomUnit.resetIdleTimer(); + + if (habbo != null) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos); + } + + roomUnit.statusUpdate(false); + }); + + List movements = new ArrayList<>(); + movements.add(WiredMovementsComposer.userSlideMovement( + roomUnit.getId(), + oldLocation.x, + oldLocation.y, + targetTile.x, + targetTile.y, + oldZ, + roomUnit.getZ(), + resolvedBodyRotation.getValue(), + resolvedHeadRotation.getValue(), + animationDuration)); + suppressStatusComposer(roomUnit, animationDuration); + room.sendComposer(new WiredMovementsComposer(movements).compose()); + + roomUnit.setPreviousLocation(targetTile); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); + + scheduleTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem, animationDuration); + schedulePostureSync(room, roomUnit, targetTile, animationDuration); + return true; + } + + public static boolean updateUserDirection(Room room, RoomUnit roomUnit, RoomUserRotation bodyRotation, RoomUserRotation headRotation) { + if (room == null || roomUnit == null) { + return false; + } + + RoomUserRotation resolvedBodyRotation = bodyRotation == null ? roomUnit.getBodyRotation() : bodyRotation; + RoomUserRotation resolvedHeadRotation = headRotation == null ? roomUnit.getHeadRotation() : headRotation; + + roomUnit.setBodyRotation(resolvedBodyRotation); + roomUnit.setHeadRotation(resolvedHeadRotation); + room.sendComposer(new WiredMovementsComposer(Collections.singletonList( + WiredMovementsComposer.userDirectionUpdate( + roomUnit.getId(), + resolvedHeadRotation.getValue(), + resolvedBodyRotation.getValue()))).compose()); + return true; + } + + public static boolean shouldSuppressStatusUpdate(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Set suppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + return suppressedRoomUnitIds != null && suppressedRoomUnitIds.contains(roomUnit.getId()); + } + + public static boolean shouldSuppressStatusComposer(RoomUnit roomUnit) { + if (roomUnit == null) { + return false; + } + + Long suppressedUntil = SUPPRESSED_STATUS_COMPOSER_UNTIL.get(roomUnit.getId()); + + if (suppressedUntil == null) { + return false; + } + + if (suppressedUntil <= System.currentTimeMillis()) { + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId(), suppressedUntil); + return false; + } + + return true; + } + + public static void clearStatusComposerSuppression(RoomUnit roomUnit) { + if (roomUnit == null) { + return; + } + + SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId()); + } + + private static boolean hasBlockingUnits(Room room, RoomUnit roomUnit, RoomTile targetTile) { + Collection units = room.getRoomUnitsAt(targetTile); + + if (units == null || units.isEmpty()) { + return false; + } + + for (RoomUnit targetUnit : units) { + if (targetUnit != null && targetUnit != roomUnit) { + return true; + } + } + + return false; + } + + 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.setPreviousLocation(oldLocation); + roomUnit.setCurrentLocation(targetTile); + roomUnit.removeStatus(RoomUnitStatus.MOVE); + roomUnit.setZ(targetZ); + roomUnit.setBodyRotation(bodyRotation); + roomUnit.setHeadRotation(headRotation); + roomUnit.stopWalking(); + roomUnit.resetIdleTimer(); + + if (habbo != null) { + THashSet movedHabbos = new THashSet<>(); + movedHabbos.add(habbo); + room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos); + } + + roomUnit.setPreviousLocation(targetTile); + roomUnit.setPreviousLocationZ(roomUnit.getZ()); + roomUnit.statusUpdate(false); + }); + + processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem); + 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)); + } + + private static void processTileCallbacks(Room room, RoomUnit roomUnit, RoomTile oldLocation, RoomTile targetTile, HabboItem oldTopItem, HabboItem newTopItem) { + if (room == null || !room.isLoaded() || roomUnit == null || roomUnit.getCurrentLocation() == null) { + return; + } + + if (roomUnit.getCurrentLocation().x != targetTile.x || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + if (oldTopItem != null && oldTopItem != newTopItem) { + try { + oldTopItem.onWalkOff(roomUnit, room, new Object[]{oldLocation, targetTile}); + } catch (Exception exception) { + LOGGER.error("Failed to process wired user walk off callback", exception); + } + } + + if (newTopItem != null && newTopItem != oldTopItem) { + try { + newTopItem.onWalkOn(roomUnit, room, new Object[]{oldLocation, targetTile}); + } catch (Exception exception) { + LOGGER.error("Failed to process wired user walk on callback", exception); + } + } + } + + private static void schedulePostureSync(Room room, RoomUnit roomUnit, RoomTile targetTile, int delay) { + if (!roomUnit.hasStatus(RoomUnitStatus.SIT) && !roomUnit.hasStatus(RoomUnitStatus.LAY)) { + return; + } + + Emulator.getThreading().run(() -> { + if (room == null || !room.isLoaded() || roomUnit == null || roomUnit.getCurrentLocation() == null) { + return; + } + + if (roomUnit.getCurrentLocation().x != targetTile.x || roomUnit.getCurrentLocation().y != targetTile.y) { + return; + } + + room.sendComposer(new RoomUserStatusComposer(roomUnit).compose()); + }, delay + STATUS_SUPPRESSION_GRACE_MS + 25); + } + + private static void suppressStatusComposer(RoomUnit roomUnit, int duration) { + if (roomUnit == null) { + return; + } + + long suppressedUntil = System.currentTimeMillis() + Math.max(duration, InteractionRoller.DELAY) + STATUS_SUPPRESSION_GRACE_MS; + SUPPRESSED_STATUS_COMPOSER_UNTIL.put(roomUnit.getId(), suppressedUntil); + } + + private static void runWithSuppressedStatusUpdates(Collection roomUnits, Runnable action) { + if (action == null) { + return; + } + + Set previousSuppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get(); + HashSet suppressedRoomUnitIds = previousSuppressedRoomUnitIds == null + ? new HashSet<>() + : new HashSet<>(previousSuppressedRoomUnitIds); + + if (roomUnits != null) { + for (RoomUnit roomUnit : roomUnits) { + if (roomUnit != null) { + suppressedRoomUnitIds.add(roomUnit.getId()); + } + } + } + + if (suppressedRoomUnitIds.isEmpty()) { + action.run(); + return; + } + + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(suppressedRoomUnitIds); + + try { + action.run(); + } finally { + if (previousSuppressedRoomUnitIds == null || previousSuppressedRoomUnitIds.isEmpty()) { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.remove(); + } else { + SUPPRESSED_STATUS_ROOM_UNIT_IDS.set(previousSuppressedRoomUnitIds); + } + } + } +} 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..155001ec 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/migrate/WiredEvents.java @@ -108,8 +108,8 @@ public final class WiredEvents { */ public static WiredEvent userClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) { return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_USER, room) - .actor(clickedUser) - .targetUnit(clickingUser) + .actor(clickingUser) + .targetUnit(clickedUser) .tile(clickedUser.getCurrentLocation()) .build(); } @@ -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/messages/incoming/handshake/SecureLoginEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java index f2afd72c..205f3b39 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java @@ -17,6 +17,7 @@ import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; import com.eu.habbo.messages.NoAuthMessage; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.commands.AvailableCommandsComposer; import com.eu.habbo.messages.outgoing.gamecenter.GameCenterAccountInfoComposer; import com.eu.habbo.messages.outgoing.gamecenter.GameCenterGameListComposer; import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer; @@ -208,6 +209,11 @@ public class SecureLoginEvent extends MessageHandler { messages.add(new UserClothesComposer(this.client.getHabbo()).compose()); messages.add(new NewUserIdentityComposer(habbo).compose()); messages.add(new UserPermissionsComposer(this.client.getHabbo()).compose()); + messages.add(new AvailableCommandsComposer( + Emulator.getGameEnvironment().getCommandHandler().getCommandsForRank( + this.client.getHabbo().getHabboInfo().getRank().getId() + ) + ).compose()); messages.add(new AvailabilityStatusMessageComposer(true, false, true).compose()); messages.add(new PingComposer().compose()); messages.add(new EnableNotificationsComposer(Emulator.getConfig().getBoolean("bubblealerts.enabled", true)).compose()); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java index 6ae99006..640d0ce0 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ClickFurniEvent.java @@ -25,7 +25,7 @@ public class ClickFurniEvent extends MessageHandler { return; } - WiredManager.triggerUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + WiredManager.queueUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); if (isClickTileItem(item)) { WiredManager.triggerUserClicksTile(room, this.client.getHabbo().getRoomUnit(), item); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java index 3cf6eec0..e81a651b 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionMonsterPlantSe import com.eu.habbo.habbohotel.pets.MonsterplantPet; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rooms.items.RemoveFloorItemComposer; import com.eu.habbo.messages.outgoing.rooms.pets.PetPackageComposer; @@ -40,6 +41,8 @@ public class ToggleFloorItemEvent extends MessageHandler { if (item == null || item instanceof InteractionDice) return; + WiredManager.cancelPendingUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + Event furnitureToggleEvent = new FurnitureToggleEvent(item, this.client.getHabbo(), state); Emulator.getPluginManager().fireEvent(furnitureToggleEvent); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java index d4020edf..b24b6f66 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleWallItemEvent.java @@ -3,6 +3,7 @@ package com.eu.habbo.messages.incoming.rooms.items; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.plugin.Event; import com.eu.habbo.plugin.events.furniture.FurnitureToggleEvent; @@ -23,6 +24,8 @@ public class ToggleWallItemEvent extends MessageHandler { if (item == null) return; + WiredManager.cancelPendingUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item); + Event furnitureToggleEvent = new FurnitureToggleEvent(item, this.client.getHabbo(), state); Emulator.getPluginManager().fireEvent(furnitureToggleEvent); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java index 5426e842..d8c0b2f2 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/ClickUserEvent.java @@ -3,8 +3,10 @@ package com.eu.habbo.messages.incoming.rooms.users; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.users.InClientLinkComposer; public class ClickUserEvent extends MessageHandler { @Override @@ -29,5 +31,13 @@ public class ClickUserEvent extends MessageHandler { } WiredManager.triggerUserClicksUser(room, clickingUser, clickedHabbo.getRoomUnit()); + + if (WiredTriggerHabboClicksUser.hasPendingIgnoreLook(clickingUser)) { + this.client.sendResponse(new InClientLinkComposer("avatar-info/block-rotate")); + } + + if (WiredTriggerHabboClicksUser.consumeBlockMenuOpen(clickingUser)) { + this.client.sendResponse(new InClientLinkComposer("avatar-info/block-menu")); + } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java index 473b362c..50fc065d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/users/RoomUserLookAtPoint.java @@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomTile; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.messages.incoming.MessageHandler; @@ -40,6 +41,9 @@ public class RoomUserLookAtPoint extends MessageHandler { if (roomUnit.isIdle()) return; + if (WiredTriggerHabboClicksUser.consumeIgnoreLook(roomUnit)) + return; + int x = this.packet.readInt(); int y = this.packet.readInt(); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java index 1d9266f3..90365132 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredApplySetConditionsEvent.java @@ -14,6 +14,7 @@ import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.math.BigDecimal; public class WiredApplySetConditionsEvent extends MessageHandler { @@ -81,13 +82,21 @@ public class WiredApplySetConditionsEvent extends MessageHandler { room.moveFurniTo(matchItem, oldLocation, setting.rotation, null, true); } } + else if(wired.shouldMatchAltitude() && !wired.shouldMatchPosition()) { + int newRotation = wired.shouldMatchRotation() ? setting.rotation : matchItem.getRotation(); + if (BigDecimal.valueOf(matchItem.getZ()).compareTo(BigDecimal.valueOf(setting.z)) != 0 + || newRotation != matchItem.getRotation()) { + room.moveFurniTo(matchItem, oldLocation, newRotation, setting.z, null, true, false); + } + } else if(wired.shouldMatchPosition()) { boolean slideAnimation = !wired.shouldMatchRotation() || matchItem.getRotation() == setting.rotation; RoomTile newLocation = room.getLayout().getTile((short) setting.x, (short) setting.y); int newRotation = wired.shouldMatchRotation() ? setting.rotation : matchItem.getRotation(); + double newZ = wired.shouldMatchAltitude() ? setting.z : matchItem.getZ(); if(newLocation != null && newLocation.state != RoomTileState.INVALID && (newLocation != oldLocation || newRotation != matchItem.getRotation()) && room.furnitureFitsAt(newLocation, matchItem, newRotation, true) == FurnitureMovementError.NONE) { - if(room.moveFurniTo(matchItem, newLocation, newRotation, null, !slideAnimation) == FurnitureMovementError.NONE) { + if(room.moveFurniTo(matchItem, newLocation, newRotation, newZ, null, !slideAnimation, true) == FurnitureMovementError.NONE) { if(slideAnimation) { room.sendComposer(new FloorItemOnRollerComposer(matchItem, null, oldLocation, oldZ, newLocation, matchItem.getZ(), 0, room).compose()); } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java index aac7ec7a..265a3a07 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredEffectSaveDataEvent.java @@ -3,6 +3,7 @@ package com.eu.habbo.messages.incoming.wired; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionWired; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.rooms.Room; @@ -11,10 +12,6 @@ import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.generic.alerts.UpdateFailedComposer; import com.eu.habbo.messages.outgoing.wired.WiredSavedComposer; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Optional; - public class WiredEffectSaveDataEvent extends MessageHandler { @Override public void handle() throws Exception { @@ -25,40 +22,36 @@ public class WiredEffectSaveDataEvent extends MessageHandler { if (room != null) { if (room.hasRights(this.client.getHabbo()) || room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER) || this.client.getHabbo().hasPermission(Permission.ACC_MOVEROTATE)) { InteractionWiredEffect effect = room.getRoomSpecialTypes().getEffect(itemId); + InteractionWiredExtra extra = room.getRoomSpecialTypes().getExtra(itemId); try { - if (effect == null) - throw new WiredSaveException(String.format("Wired effect with item id %s not found in room", itemId)); + if (effect == null && extra == null) + throw new WiredSaveException(String.format("Wired effect/extra with item id %s not found in room", itemId)); - Optional saveMethod = Arrays.stream(effect.getClass().getMethods()).filter(x -> x.getName().equals("saveData")).findFirst(); + WiredSettings settings = InteractionWired.readSettings(this.packet, true); + boolean saved; - if(saveMethod.isPresent()) { - if(saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { - WiredSettings settings = InteractionWired.readSettings(this.packet, true); - if (effect.saveData(settings, this.client)) { - this.client.sendResponse(new WiredSavedComposer()); - effect.needsUpdate(true); - Emulator.getThreading().run(effect); - - // Invalidate wired cache when effect is saved - WiredManager.invalidateRoom(room); - } - } - else { - if ((boolean) saveMethod.get().invoke(effect, this.packet, this.client)) { - this.client.sendResponse(new WiredSavedComposer()); - effect.needsUpdate(true); - Emulator.getThreading().run(effect); - - // Invalidate wired cache when effect is saved - WiredManager.invalidateRoom(room); - } - } + if (effect != null) { + saved = effect.saveData(settings, this.client); } else { - this.client.sendResponse(new UpdateFailedComposer("Save method was not found")); + saved = extra.saveData(settings, this.client); } + if (saved) { + this.client.sendResponse(new WiredSavedComposer()); + if (effect != null) { + effect.needsUpdate(true); + Emulator.getThreading().run(effect); + } else { + extra.needsUpdate(true); + Emulator.getThreading().run(extra); + } + // Invalidate wired cache when effect is saved + WiredManager.invalidateRoom(room); + } else { + this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that effect")); + } } catch (WiredSaveException e) { this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java index bf27dc0e..dfa480a3 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/wired/WiredTriggerSaveDataEvent.java @@ -11,10 +11,6 @@ import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.generic.alerts.UpdateFailedComposer; import com.eu.habbo.messages.outgoing.wired.WiredSavedComposer; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Optional; - public class WiredTriggerSaveDataEvent extends MessageHandler { @Override public void handle() throws Exception { @@ -27,44 +23,21 @@ public class WiredTriggerSaveDataEvent extends MessageHandler { InteractionWiredTrigger trigger = room.getRoomSpecialTypes().getTrigger(itemId); if (trigger != null) { + WiredSettings settings = InteractionWired.readSettings(this.packet, false); - Optional saveMethod = Arrays.stream(trigger.getClass().getMethods()).filter(x -> x.getName().equals("saveData")).findFirst(); + try { + boolean saved = trigger.saveData(settings, this.client); - if(saveMethod.isPresent()) { - if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { - WiredSettings settings = InteractionWired.readSettings(this.packet, false); - - try { - if (trigger.saveData(settings)) { - this.client.sendResponse(new WiredSavedComposer()); - - trigger.needsUpdate(true); - - Emulator.getThreading().run(trigger); - - // Invalidate wired cache when trigger is saved - WiredManager.invalidateRoom(room); - } else { - this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); - } - } catch (WiredTriggerSaveException e) { - this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); - } + if (saved) { + this.client.sendResponse(new WiredSavedComposer()); + trigger.needsUpdate(true); + Emulator.getThreading().run(trigger); + WiredManager.invalidateRoom(room); } else { - if ((boolean) saveMethod.get().invoke(trigger, this.packet)) { - this.client.sendResponse(new WiredSavedComposer()); - trigger.needsUpdate(true); - Emulator.getThreading().run(trigger); - - // Invalidate wired cache when trigger is saved - WiredManager.invalidateRoom(room); - } else { - this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); - } + this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger")); } - } - else { - this.client.sendResponse(new UpdateFailedComposer("Save method was not found")); + } catch (WiredTriggerSaveException e) { + this.client.sendResponse(new UpdateFailedComposer(e.getMessage())); } } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java index 3305bad8..b558520d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java @@ -41,6 +41,7 @@ public class Outgoing { public final static int HotelViewComposer = 122; public final static int UpdateFriendComposer = 2800; public final static int FloorItemUpdateComposer = 3776; + public final static int WiredMovementsComposer = 3999; public final static int RoomAccessDeniedComposer = 878; public final static int GuildFurniWidgetComposer = 3293; public final static int GiftConfigurationComposer = 2234; @@ -559,5 +560,6 @@ public class Outgoing { public static final int UserPrefixesComposer = 7001; public static final int PrefixReceivedComposer = 7002; public static final int ActivePrefixUpdatedComposer = 7003; + public static final int AvailableCommandsComposer = 4050; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java new file mode 100644 index 00000000..0f8e00f5 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java @@ -0,0 +1,32 @@ +package com.eu.habbo.messages.outgoing.commands; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.commands.Command; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +import java.util.List; + +public class AvailableCommandsComposer extends MessageComposer { + private final List commands; + + public AvailableCommandsComposer(List commands) { + this.commands = commands; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.AvailableCommandsComposer); + this.response.appendInt(this.commands.size()); + + for (Command cmd : this.commands) { + this.response.appendString(cmd.keys[0]); + this.response.appendString( + Emulator.getTexts().getValue("commands.description." + cmd.permission, cmd.permission) + ); + } + + return this.response; + } +} \ No newline at end of file diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java new file mode 100644 index 00000000..07c61622 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/WiredMovementsComposer.java @@ -0,0 +1,206 @@ +package com.eu.habbo.messages.outgoing.rooms; + +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +import java.util.ArrayList; +import java.util.List; + +public class WiredMovementsComposer extends MessageComposer { + public static final int TYPE_USER_MOVE = 0; + public static final int TYPE_FURNI_MOVE = 1; + public static final int TYPE_WALL_ITEM_MOVE = 2; + public static final int TYPE_USER_DIRECTION = 3; + + public static final int USER_MOVEMENT_WALK = 0; + public static final int USER_MOVEMENT_SLIDE = 1; + public static final int DEFAULT_DURATION = 500; + + private final List movements; + + public WiredMovementsComposer(List movements) { + this.movements = movements == null ? new ArrayList<>() : movements; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.WiredMovementsComposer); + this.response.appendInt(this.movements.size()); + + for (MovementData movement : this.movements) { + this.response.appendInt(movement.getType()); + movement.append(this.response); + } + + return this.response; + } + + public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ) { + return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, 0, DEFAULT_DURATION); + } + + public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) { + return new FurniMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration); + } + + public static MovementData userWalkMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int bodyDirection, int headDirection, int duration) { + return new UserMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, USER_MOVEMENT_WALK, bodyDirection, headDirection, duration); + } + + public static MovementData userSlideMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int bodyDirection, int headDirection, int duration) { + return new UserMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, USER_MOVEMENT_SLIDE, bodyDirection, headDirection, duration); + } + + public static MovementData userDirectionUpdate(int id, int headDirection, int bodyDirection) { + return new UserDirectionData(id, headDirection, bodyDirection); + } + + public static MovementData wallItemMovement(int id, boolean enabled, int[] values) { + return new WallItemMovementData(id, enabled, values); + } + + public interface MovementData { + int getType(); + + void append(ServerMessage response); + } + + private abstract static class BaseMovementData implements MovementData { + private final int type; + + private BaseMovementData(int type) { + this.type = type; + } + + @Override + public int getType() { + return this.type; + } + } + + private static final class UserMovementData extends BaseMovementData { + private final int fromX; + private final int fromY; + private final int toX; + private final int toY; + private final double fromZ; + private final double toZ; + private final int id; + private final int movementType; + private final int bodyDirection; + private final int headDirection; + private final int duration; + + private UserMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int movementType, int bodyDirection, int headDirection, int duration) { + super(TYPE_USER_MOVE); + this.id = id; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + this.fromZ = fromZ; + this.toZ = toZ; + this.movementType = movementType; + this.bodyDirection = bodyDirection; + this.headDirection = headDirection; + this.duration = duration; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.fromX); + response.appendInt(this.fromY); + response.appendInt(this.toX); + response.appendInt(this.toY); + response.appendString(Double.toString(this.fromZ)); + response.appendString(Double.toString(this.toZ)); + response.appendInt(this.id); + response.appendInt(this.movementType); + response.appendInt(this.bodyDirection); + response.appendInt(this.headDirection); + response.appendInt(this.duration); + } + } + + private static final class FurniMovementData extends BaseMovementData { + private final int fromX; + private final int fromY; + private final int toX; + private final int toY; + private final double fromZ; + private final double toZ; + private final int id; + private final int rotation; + private final int duration; + + private FurniMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) { + super(TYPE_FURNI_MOVE); + this.id = id; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + this.fromZ = fromZ; + this.toZ = toZ; + this.rotation = rotation; + this.duration = duration; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.fromX); + response.appendInt(this.fromY); + response.appendInt(this.toX); + response.appendInt(this.toY); + response.appendString(Double.toString(this.fromZ)); + response.appendString(Double.toString(this.toZ)); + response.appendInt(this.id); + response.appendInt(this.rotation); + response.appendInt(this.duration); + } + } + + private static final class UserDirectionData extends BaseMovementData { + private final int id; + private final int headDirection; + private final int bodyDirection; + + private UserDirectionData(int id, int headDirection, int bodyDirection) { + super(TYPE_USER_DIRECTION); + this.id = id; + this.headDirection = headDirection; + this.bodyDirection = bodyDirection; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.id); + response.appendInt(this.headDirection); + response.appendInt(this.bodyDirection); + } + } + + private static final class WallItemMovementData extends BaseMovementData { + private final int id; + private final boolean enabled; + private final int[] values; + + private WallItemMovementData(int id, boolean enabled, int[] values) { + super(TYPE_WALL_ITEM_MOVE); + this.id = id; + this.enabled = enabled; + this.values = values == null ? new int[9] : values; + } + + @Override + public void append(ServerMessage response) { + response.appendInt(this.id); + response.appendBoolean(this.enabled); + + for (int index = 0; index < 9; index++) { + response.appendInt(index < this.values.length ? this.values[index] : 0); + } + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java index 1a6d2e96..1e3d2af4 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rooms/users/RoomUserStatusComposer.java @@ -1,5 +1,7 @@ package com.eu.habbo.messages.outgoing.rooms.users; +import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper; +import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnitStatus; import com.eu.habbo.habbohotel.users.Habbo; @@ -8,7 +10,9 @@ import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; import gnu.trove.set.hash.THashSet; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; public class RoomUserStatusComposer extends MessageComposer { @@ -38,8 +42,20 @@ public class RoomUserStatusComposer extends MessageComposer { protected ServerMessage composeInternal() { this.response.init(Outgoing.RoomUserStatusComposer); if (this.roomUnits != null) { - this.response.appendInt(this.roomUnits.size()); + List roomUnits = new ArrayList<>(); + for (RoomUnit roomUnit : this.roomUnits) { + if (roomUnit == null + || WiredMoveCarryHelper.shouldSuppressStatusComposer(roomUnit) + || WiredUserMovementHelper.shouldSuppressStatusComposer(roomUnit)) { + continue; + } + + roomUnits.add(roomUnit); + } + + this.response.appendInt(roomUnits.size()); + for (RoomUnit roomUnit : roomUnits) { this.response.appendInt(roomUnit.getId()); this.response.appendInt(roomUnit.getPreviousLocation().x); this.response.appendInt(roomUnit.getPreviousLocation().y); @@ -59,8 +75,21 @@ public class RoomUserStatusComposer extends MessageComposer { } } else { synchronized (this.habbos) { - this.response.appendInt(this.habbos.size()); + List habbos = new ArrayList<>(); + for (Habbo habbo : this.habbos) { + if (habbo == null + || habbo.getRoomUnit() == null + || WiredMoveCarryHelper.shouldSuppressStatusComposer(habbo.getRoomUnit()) + || WiredUserMovementHelper.shouldSuppressStatusComposer(habbo.getRoomUnit())) { + continue; + } + + habbos.add(habbo); + } + + this.response.appendInt(habbos.size()); + for (Habbo habbo : habbos) { this.response.appendInt(habbo.getRoomUnit().getId()); this.response.appendInt(habbo.getRoomUnit().getPreviousLocation().x); this.response.appendInt(habbo.getRoomUnit().getPreviousLocation().y); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java new file mode 100644 index 00000000..1028a6bb --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/wired/WiredExtraDataComposer.java @@ -0,0 +1,25 @@ +package com.eu.habbo.messages.outgoing.wired; + +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +public class WiredExtraDataComposer extends MessageComposer { + private final InteractionWiredExtra extra; + private final Room room; + + public WiredExtraDataComposer(InteractionWiredExtra extra, Room room) { + this.extra = extra; + this.room = room; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.WiredEffectDataComposer); + this.extra.serializeWiredData(this.response, this.room); + this.extra.needsUpdate(true); + return this.response; + } +} 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); + } +} diff --git a/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar b/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar new file mode 100644 index 00000000..a1fb2c2c Binary files /dev/null and b/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar differ