{
+ T map(ResultSet rs) throws SQLException;
+ }
+
+ @FunctionalInterface
+ public interface RowConsumer {
+ void accept(ResultSet rs) throws SQLException;
+ }
+
+ @FunctionalInterface
+ public interface ParameterBinder {
+ void bind(PreparedStatement ps, P value) throws SQLException;
+ }
+
+ public static class DataAccessException extends RuntimeException {
+ public DataAccessException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public static List query(String sql, RowMapper mapper, Object... params) {
+ try (Connection c = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement ps = c.prepareStatement(sql)) {
+ bindAll(ps, params);
+ try (ResultSet rs = ps.executeQuery()) {
+ List out = new ArrayList<>();
+ while (rs.next()) {
+ out.add(mapper.map(rs));
+ }
+ return out;
+ }
+ } catch (SQLException e) {
+ throw new DataAccessException("query failed: " + sql, e);
+ }
+ }
+
+ public static Optional queryOne(String sql, RowMapper mapper, Object... params) {
+ try (Connection c = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement ps = c.prepareStatement(sql)) {
+ bindAll(ps, params);
+ try (ResultSet rs = ps.executeQuery()) {
+ return rs.next() ? Optional.ofNullable(mapper.map(rs)) : Optional.empty();
+ }
+ } catch (SQLException e) {
+ throw new DataAccessException("queryOne failed: " + sql, e);
+ }
+ }
+
+ public static void forEach(String sql, RowConsumer consumer, Object... params) {
+ try (Connection c = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement ps = c.prepareStatement(sql)) {
+ bindAll(ps, params);
+ try (ResultSet rs = ps.executeQuery()) {
+ while (rs.next()) {
+ consumer.accept(rs);
+ }
+ }
+ } catch (SQLException e) {
+ throw new DataAccessException("forEach failed: " + sql, e);
+ }
+ }
+
+ public static int update(String sql, Object... params) {
+ try (Connection c = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement ps = c.prepareStatement(sql)) {
+ bindAll(ps, params);
+ return ps.executeUpdate();
+ } catch (SQLException e) {
+ throw new DataAccessException("update failed: " + sql, e);
+ }
+ }
+
+ public static int[] batchUpdate(String sql, Collection extends P> items, ParameterBinder
binder) {
+ try (Connection c = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement ps = c.prepareStatement(sql)) {
+ for (P item : items) {
+ binder.bind(ps, item);
+ ps.addBatch();
+ }
+ return ps.executeBatch();
+ } catch (SQLException e) {
+ throw new DataAccessException("batchUpdate failed: " + sql, e);
+ }
+ }
+
+ private static void bindAll(PreparedStatement ps, Object[] params) throws SQLException {
+ if (params == null) {
+ return;
+ }
+ for (int i = 0; i < params.length; i++) {
+ ps.setObject(i + 1, params[i]);
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/achievements/AchievementManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/achievements/AchievementManager.java
index ff3700c4..36712b0e 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/achievements/AchievementManager.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/achievements/AchievementManager.java
@@ -1,6 +1,7 @@
package com.eu.habbo.habbohotel.achievements;
import com.eu.habbo.Emulator;
+import com.eu.habbo.database.SqlQueries;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboBadge;
@@ -49,16 +50,12 @@ public class AchievementManager {
if (habbo != null) {
progressAchievement(habbo, achievement, amount);
} else {
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
- PreparedStatement statement = connection.prepareStatement("" +
- "INSERT INTO users_achievements_queue (user_id, achievement_id, amount) VALUES (?, ?, ?) " +
- "ON DUPLICATE KEY UPDATE amount = amount + ?")) {
- statement.setInt(1, habboId);
- statement.setInt(2, achievement.id);
- statement.setInt(3, amount);
- statement.setInt(4, amount);
- statement.execute();
- } catch (SQLException e) {
+ try {
+ SqlQueries.update(
+ "INSERT INTO users_achievements_queue (user_id, achievement_id, amount) VALUES (?, ?, ?) "
+ + "ON DUPLICATE KEY UPDATE amount = amount + ?",
+ habboId, achievement.id, amount, amount);
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
}
}
@@ -203,44 +200,41 @@ public class AchievementManager {
}
public static void createUserEntry(Habbo habbo, Achievement achievement) {
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_achievements (user_id, achievement_name, progress) VALUES (?, ?, ?)")) {
- statement.setInt(1, habbo.getHabboInfo().getId());
- statement.setString(2, achievement.name);
- statement.setInt(3, 1);
- statement.execute();
- } catch (SQLException e) {
+ try {
+ SqlQueries.update(
+ "INSERT INTO users_achievements (user_id, achievement_name, progress) VALUES (?, ?, ?)",
+ habbo.getHabboInfo().getId(), achievement.name, 1);
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
}
}
public static void saveAchievements(Habbo habbo) {
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("UPDATE users_achievements SET progress = ? WHERE achievement_name = ? AND user_id = ? LIMIT 1")) {
- statement.setInt(3, habbo.getHabboInfo().getId());
- for (Map.Entry map : habbo.getHabboStats().getAchievementProgress().entrySet()) {
- statement.setInt(1, map.getValue());
- statement.setString(2, map.getKey().name);
- statement.addBatch();
- }
- statement.executeBatch();
- } catch (SQLException e) {
+ int userId = habbo.getHabboInfo().getId();
+ try {
+ SqlQueries.batchUpdate(
+ "UPDATE users_achievements SET progress = ? WHERE achievement_name = ? AND user_id = ? LIMIT 1",
+ habbo.getHabboStats().getAchievementProgress().entrySet(),
+ (ps, entry) -> {
+ ps.setInt(1, entry.getValue());
+ ps.setString(2, entry.getKey().name);
+ ps.setInt(3, userId);
+ });
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
}
}
public static int getAchievementProgressForHabbo(int userId, Achievement achievement) {
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT progress FROM users_achievements WHERE user_id = ? AND achievement_name = ? LIMIT 1")) {
- statement.setInt(1, userId);
- statement.setString(2, achievement.name);
- try (ResultSet set = statement.executeQuery()) {
- if (set.next()) {
- return set.getInt("progress");
- }
- }
- } catch (SQLException e) {
+ try {
+ return SqlQueries.queryOne(
+ "SELECT progress FROM users_achievements WHERE user_id = ? AND achievement_name = ? LIMIT 1",
+ rs -> rs.getInt("progress"),
+ userId, achievement.name).orElse(0);
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
+ return 0;
}
-
- return 0;
}
public void reload() {
@@ -393,4 +387,4 @@ public class AchievementManager {
public TalentTrackLevel getTalentTrackLevel(TalentTrackType type, int level) {
return this.talentTrackLevels.get(type).get(level);
}
-}
\ No newline at end of file
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/campaign/calendar/CalendarManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/campaign/calendar/CalendarManager.java
index 1336f88f..99c238c3 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/campaign/calendar/CalendarManager.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/campaign/calendar/CalendarManager.java
@@ -1,6 +1,7 @@
package com.eu.habbo.habbohotel.campaign.calendar;
import com.eu.habbo.Emulator;
+import com.eu.habbo.database.SqlQueries;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.outgoing.events.calendar.AdventCalendarProductComposer;
import com.eu.habbo.plugin.events.users.calendar.UserClaimRewardEvent;
@@ -33,27 +34,27 @@ public class CalendarManager {
public boolean reload() {
this.dispose();
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM calendar_campaigns WHERE enabled = 1")) {
- try (ResultSet set = statement.executeQuery()) {
- while (set.next()) {
- calendarCampaigns.put(set.getInt("id"), new CalendarCampaign(set));
- }
- }
- } catch (SQLException e) {
+ try {
+ SqlQueries.query(
+ "SELECT * FROM calendar_campaigns WHERE enabled = 1",
+ CalendarCampaign::new)
+ .forEach(c -> calendarCampaigns.put(c.getId(), c));
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
return false;
}
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM calendar_rewards")) {
- try (ResultSet set = statement.executeQuery()) {
- while (set.next()) {
- CalendarCampaign campaign = calendarCampaigns.get(set.getInt("campaign_id"));
- if(campaign != null){
- campaign.addReward(new CalendarRewardObject(set));
- }
- }
- }
- } catch (SQLException e) {
+ try {
+ SqlQueries.query(
+ "SELECT * FROM calendar_rewards",
+ rs -> Map.entry(rs.getInt("campaign_id"), new CalendarRewardObject(rs)))
+ .forEach(entry -> {
+ CalendarCampaign campaign = calendarCampaigns.get(entry.getKey());
+ if (campaign != null) {
+ campaign.addReward(entry.getValue());
+ }
+ });
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
return false;
}
@@ -94,14 +95,12 @@ public class CalendarManager {
public boolean deleteCampaign(CalendarCampaign campaign) {
calendarCampaigns.remove(campaign.getId());
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("DELETE FROM calendar_campaigns WHERE id = ? LIMIT 1")) {
- statement.setInt(1, campaign.getId());
- return statement.execute();
- } catch (SQLException e) {
+ try {
+ return SqlQueries.update("DELETE FROM calendar_campaigns WHERE id = ? LIMIT 1", campaign.getId()) > 0;
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
+ return false;
}
-
- return false;
}
public CalendarCampaign getCalendarCampaign(String campaignName) {
@@ -136,14 +135,15 @@ public class CalendarManager {
habbo.getHabboStats().calendarRewardsClaimed.add(new CalendarRewardClaimed(habbo.getHabboInfo().getId(), campaign.getId(), day, object.getId(), new Timestamp(System.currentTimeMillis())));
habbo.getClient().sendResponse(new AdventCalendarProductComposer(true, object, habbo));
object.give(habbo);
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO calendar_rewards_claimed (user_id, campaign_id, day, reward_id, timestamp) VALUES (?, ?, ?, ?, ?)")) {
- statement.setInt(1, habbo.getHabboInfo().getId());
- statement.setInt(2, campaign.getId());
- statement.setInt(3, day);
- statement.setInt(4, object.getId());
- statement.setInt(5, Emulator.getIntUnixTimestamp());
- statement.execute();
- } catch (SQLException e) {
+ try {
+ SqlQueries.update(
+ "INSERT INTO calendar_rewards_claimed (user_id, campaign_id, day, reward_id, timestamp) VALUES (?, ?, ?, ?, ?)",
+ habbo.getHabboInfo().getId(),
+ campaign.getId(),
+ day,
+ object.getId(),
+ Emulator.getIntUnixTimestamp());
+ } catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
}
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogItem.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogItem.java
index 53a979ec..7bfa22f8 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogItem.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogItem.java
@@ -338,6 +338,8 @@ public class CatalogItem implements ISerialize, Runnable, Comparable catalogPages;
+ public final TIntObjectMap buildersClubCatalogPages;
public final TIntObjectMap catalogFeaturedPages;
public final THashMap> prizes;
public final THashMap giftWrappers;
@@ -194,6 +198,7 @@ public class CatalogManager {
public final THashMap targetOffers;
public final THashMap clothing;
public final TIntIntHashMap offerDefs;
+ public final TIntIntHashMap buildersClubOfferDefs;
public final Item ecotronItem;
public final THashMap limitedNumbers;
private final List vouchers;
@@ -201,6 +206,7 @@ public class CatalogManager {
public CatalogManager() {
long millis = System.currentTimeMillis();
this.catalogPages = TCollections.synchronizedMap(new TIntObjectHashMap<>());
+ this.buildersClubCatalogPages = TCollections.synchronizedMap(new TIntObjectHashMap<>());
this.catalogFeaturedPages = new TIntObjectHashMap<>();
this.prizes = new THashMap<>();
this.giftWrappers = new THashMap<>();
@@ -210,6 +216,7 @@ public class CatalogManager {
this.targetOffers = new THashMap<>();
this.clothing = new THashMap<>();
this.offerDefs = new TIntIntHashMap();
+ this.buildersClubOfferDefs = new TIntIntHashMap();
this.vouchers = new ArrayList<>();
this.limitedNumbers = new THashMap<>();
@@ -226,8 +233,10 @@ public class CatalogManager {
this.loadLimitedNumbers();
this.loadCatalogPages();
+ this.loadBuildersClubCatalogPages();
this.loadCatalogFeaturedPages();
this.loadCatalogItems();
+ this.loadBuildersClubCatalogItems();
this.loadClubOffers();
this.loadTargetOffers();
this.loadVouchers();
@@ -312,6 +321,57 @@ public class CatalogManager {
LOGGER.info("Loaded {} Catalog Pages!", this.catalogPages.size());
}
+ private synchronized void loadBuildersClubCatalogPages() {
+ this.buildersClubCatalogPages.clear();
+
+ final THashMap pages = new THashMap<>();
+ pages.put(-1, new CatalogRootLayout());
+
+ String query = "SELECT id, parent_id, caption, caption AS caption_save, page_layout, icon_color, icon_image, 1 AS min_rank, order_num, visible, enabled, '0' AS club_only, 'BUILDERS_CLUB' AS catalog_mode, page_headline, page_teaser, page_special, page_text1, page_text2, page_text_details, page_text_teaser, '' AS includes FROM catalog_pages_bc ORDER BY parent_id, id";
+
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement statement = connection.prepareStatement(query)) {
+ try (ResultSet set = statement.executeQuery()) {
+ while (set.next()) {
+ Class extends CatalogPage> pageClazz = pageDefinitions.get(set.getString("page_layout"));
+
+ if (pageClazz == null) {
+ LOGGER.info("Unknown Builders Club Page Layout: {}", set.getString("page_layout"));
+ continue;
+ }
+
+ try {
+ CatalogPage page = pageClazz.getConstructor(ResultSet.class).newInstance(set);
+ pages.put(page.getId(), page);
+ } catch (Exception e) {
+ LOGGER.error("Failed to load Builders Club layout: {}", set.getString("page_layout"));
+ }
+ }
+ }
+ } catch (SQLException e) {
+ LOGGER.error("Caught SQL exception", e);
+ }
+
+ pages.forEachValue((object) -> {
+ CatalogPage page = pages.get(object.parentId);
+
+ if (page != null) {
+ if (page.id != object.id) {
+ page.addChildPage(object);
+ }
+ } else {
+ if (object.parentId != -2) {
+ LOGGER.info("Builders Club parent page not found for {} (ID: {}, parent_id: {})", object.getPageName(), object.id, object.parentId);
+ }
+ }
+ return true;
+ });
+
+ this.buildersClubCatalogPages.putAll(pages);
+
+ LOGGER.info("Loaded {} Builders Club Catalog Pages!", this.buildersClubCatalogPages.size());
+ }
+
private synchronized void loadCatalogFeaturedPages() {
this.catalogFeaturedPages.clear();
@@ -388,6 +448,53 @@ public class CatalogManager {
}
}
+ private synchronized void loadBuildersClubCatalogItems() {
+ this.buildersClubOfferDefs.clear();
+
+ String query = "SELECT id, item_ids, page_id, catalog_name, 0 AS cost_credits, 0 AS cost_points, 0 AS points_type, 1 AS amount, 0 AS limited_stack, 0 AS limited_sells, extradata, '0' AS club_only, '1' AS have_offer, id AS offer_id, order_number FROM catalog_items_bc";
+
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ Statement statement = connection.createStatement();
+ ResultSet set = statement.executeQuery(query)) {
+ CatalogItem item;
+
+ while (set.next()) {
+ if (set.getString("item_ids").equals("0")) {
+ continue;
+ }
+
+ CatalogPage page = this.buildersClubCatalogPages.get(set.getInt("page_id"));
+
+ if (page == null) {
+ continue;
+ }
+
+ item = page.getCatalogItem(set.getInt("id"));
+
+ if (item == null) {
+ item = new CatalogItem(set);
+ page.addItem(item);
+ page.addOfferId(item.getOfferId());
+ this.buildersClubOfferDefs.put(item.getOfferId(), item.getId());
+ } else {
+ item.update(set);
+ }
+ }
+ } catch (SQLException e) {
+ LOGGER.error("Caught SQL exception", e);
+ }
+
+ for (CatalogPage page : this.buildersClubCatalogPages.valueCollection()) {
+ for (Integer id : page.getIncluded()) {
+ CatalogPage includedPage = this.buildersClubCatalogPages.get(id);
+
+ if (includedPage != null) {
+ page.getCatalogItems().putAll(includedPage.getCatalogItems());
+ }
+ }
+ }
+ }
+
private void loadClubOffers() {
this.clubOffers.clear();
@@ -582,6 +689,10 @@ public class CatalogManager {
return this.catalogPages.get(pageId);
}
+ public CatalogPage getCatalogPage(int pageId, CatalogPageType pageType) {
+ return this.getCatalogPagesMap(pageType).get(pageId);
+ }
+
public CatalogPage getCatalogPage(String captionSafe) {
return this.catalogPages.valueCollection().stream()
.filter(p -> p != null && p.getPageName() != null && p.getPageName().equalsIgnoreCase(captionSafe))
@@ -600,9 +711,15 @@ public class CatalogManager {
}
public CatalogItem getCatalogItem(int id) {
+ return this.getCatalogItem(id, CatalogPageType.NORMAL);
+ }
+
+ public CatalogItem getCatalogItem(int id, CatalogPageType pageType) {
final CatalogItem[] item = {null};
- synchronized (this.catalogPages) {
- this.catalogPages.forEachValue(new TObjectProcedure() {
+ final TIntObjectMap pagesMap = this.getCatalogPagesMap(pageType);
+
+ synchronized (pagesMap) {
+ pagesMap.forEachValue(new TObjectProcedure() {
@Override
public boolean execute(CatalogPage object) {
item[0] = object.getCatalogItem(id);
@@ -617,17 +734,28 @@ public class CatalogManager {
public List getCatalogPages(int parentId, final Habbo habbo) {
- final List pages = new ArrayList<>();
+ return this.getCatalogPages(parentId, habbo, CatalogPageType.NORMAL);
+ }
- this.catalogPages.get(parentId).childPages.forEachValue(new TObjectProcedure() {
+ public List getCatalogPages(int parentId, final Habbo habbo, final CatalogPageType pageType) {
+ final List pages = new ArrayList<>();
+ final TIntObjectMap pagesMap = this.getCatalogPagesMap(pageType);
+ CatalogPage parentPage = pagesMap.get(parentId);
+
+ if (parentPage == null) {
+ return pages;
+ }
+
+ parentPage.childPages.forEachValue(new TObjectProcedure() {
@Override
public boolean execute(CatalogPage object) {
boolean isVisiblePage = object.visible;
boolean hasRightRank = object.getRank() <= habbo.getHabboInfo().getRank().getId();
boolean clubRightsOkay = !object.isClubOnly() || habbo.getHabboInfo().getHabboStats().hasActiveClub();
+ boolean pageTypeMatches = (pageType == CatalogPageType.BUILDER) || object.getCatalogPageType().matches(pageType);
- if (isVisiblePage && hasRightRank && clubRightsOkay) {
+ if (isVisiblePage && hasRightRank && clubRightsOkay && pageTypeMatches) {
pages.add(object);
}
return true;
@@ -701,22 +829,42 @@ public class CatalogManager {
}
- public CatalogPage createCatalogPage(String caption, String captionSave, int roomId, int icon, CatalogPageLayouts layout, int minRank, int parentId) {
+ public CatalogPage createCatalogPage(String caption, String captionSave, int roomId, int icon, CatalogPageLayouts layout, int minRank, int parentId, CatalogPageType pageType, CatalogPageType catalogMode) {
CatalogPage catalogPage = null;
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO catalog_pages (parent_id, caption, caption_save, icon_image, visible, enabled, min_rank, page_layout, room_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
+ boolean buildersClubPage = (pageType == CatalogPageType.BUILDER);
+ String insertQuery = buildersClubPage
+ ? "INSERT INTO catalog_pages_bc (parent_id, caption, page_layout, icon_color, icon_image, order_num, visible, enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
+ : "INSERT INTO catalog_pages (parent_id, caption, caption_save, icon_image, visible, enabled, min_rank, page_layout, room_id, catalog_mode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ String selectQuery = buildersClubPage
+ ? "SELECT id, parent_id, caption, caption AS caption_save, page_layout, icon_color, icon_image, 1 AS min_rank, order_num, visible, enabled, '0' AS club_only, 'BUILDERS_CLUB' AS catalog_mode, page_headline, page_teaser, page_special, page_text1, page_text2, page_text_details, page_text_teaser, '' AS includes FROM catalog_pages_bc WHERE id = ?"
+ : "SELECT * FROM catalog_pages WHERE id = ?";
+
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement statement = connection.prepareStatement(insertQuery, Statement.RETURN_GENERATED_KEYS)) {
statement.setInt(1, parentId);
statement.setString(2, caption);
- statement.setString(3, captionSave);
- statement.setInt(4, icon);
- statement.setString(5, "1");
- statement.setString(6, "1");
- statement.setInt(7, minRank);
- statement.setString(8, layout.name());
- statement.setInt(9, roomId);
+
+ if (buildersClubPage) {
+ statement.setString(3, layout.name());
+ statement.setInt(4, 1);
+ statement.setInt(5, icon);
+ statement.setInt(6, 1);
+ statement.setString(7, "1");
+ statement.setString(8, "1");
+ } else {
+ statement.setString(3, captionSave);
+ statement.setInt(4, icon);
+ statement.setString(5, "1");
+ statement.setString(6, "1");
+ statement.setInt(7, minRank);
+ statement.setString(8, layout.name());
+ statement.setInt(9, roomId);
+ statement.setString(10, catalogMode.name());
+ }
statement.execute();
try (ResultSet set = statement.getGeneratedKeys()) {
if (set.next()) {
- try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM catalog_pages WHERE id = ?")) {
+ try (PreparedStatement stmt = connection.prepareStatement(selectQuery)) {
stmt.setInt(1, set.getInt(1));
try (ResultSet page = stmt.executeQuery()) {
if (page.next()) {
@@ -741,7 +889,7 @@ public class CatalogManager {
}
if (catalogPage != null) {
- this.catalogPages.put(catalogPage.getId(), catalogPage);
+ this.getCatalogPagesMap(pageType).put(catalogPage.getId(), catalogPage);
}
return catalogPage;
@@ -988,7 +1136,7 @@ public class CatalogManager {
if (extradata.length() > Emulator.getConfig().getInt("hotel.trophies.length.max", 300)) {
extradata = extradata.substring(0, Emulator.getConfig().getInt("hotel.trophies.length.max", 300));
}
-
+
extradata = habbo.getClient().getHabbo().getHabboInfo().getUsername() + (char) 9 + Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "-" + (Calendar.getInstance().get(Calendar.MONTH) + 1) + "-" + Calendar.getInstance().get(Calendar.YEAR) + (char) 9 + Emulator.getGameEnvironment().getWordFilter().filter(extradata.replace(((char) 9) + "", ""), habbo);
}
@@ -1057,7 +1205,7 @@ public class CatalogManager {
if (badgeFound && item.getBaseItems().size() == 1) {
habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.ALREADY_HAVE_BADGE));
- return;
+ return;
}
UserCatalogItemPurchasedEvent purchasedEvent = new UserCatalogItemPurchasedEvent(habbo, item, itemsList, totalCredits, totalPoints, badges);
@@ -1141,14 +1289,23 @@ public class CatalogManager {
}
public List getClubOffers() {
+ return this.getClubOffers(ClubOffer.WINDOW_HABBO_CLUB);
+ }
+
+ public TIntObjectMap getCatalogPagesMap(CatalogPageType pageType) {
+ return (pageType == CatalogPageType.BUILDER) ? this.buildersClubCatalogPages : this.catalogPages;
+ }
+
+ public List getClubOffers(int windowId) {
List offers = new ArrayList<>();
for (Map.Entry entry : this.clubOffers.entrySet()) {
- if (!entry.getValue().isDeal()) {
+ if (!entry.getValue().isDeal() && entry.getValue().belongsToWindow(windowId)) {
offers.add(entry.getValue());
}
}
+ offers.sort(Comparator.comparingInt(ClubOffer::getId));
return offers;
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPage.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPage.java
index 6d36c279..0f136688 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPage.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPage.java
@@ -32,6 +32,7 @@ public abstract class CatalogPage implements Comparable, ISerialize
protected boolean visible;
protected boolean enabled;
protected boolean clubOnly;
+ protected CatalogPageType catalogPageType = CatalogPageType.NORMAL;
protected String layout;
protected String headerImage;
protected String teaserImage;
@@ -59,6 +60,11 @@ public abstract class CatalogPage implements Comparable, ISerialize
this.visible = set.getBoolean("visible");
this.enabled = set.getBoolean("enabled");
this.clubOnly = set.getBoolean("club_only");
+ try {
+ this.catalogPageType = CatalogPageType.fromString(set.getString("catalog_mode"));
+ } catch (SQLException ignored) {
+ this.catalogPageType = CatalogPageType.NORMAL;
+ }
this.layout = set.getString("page_layout");
this.headerImage = set.getString("page_headline");
this.teaserImage = set.getString("page_teaser");
@@ -68,8 +74,9 @@ public abstract class CatalogPage implements Comparable, ISerialize
this.textDetails = set.getString("page_text_details");
this.textTeaser = set.getString("page_text_teaser");
- if (!set.getString("includes").isEmpty()) {
- for (String id : set.getString("includes").split(";")) {
+ String includes = set.getString("includes");
+ if (includes != null && !includes.isEmpty()) {
+ for (String id : includes.split(";")) {
try {
this.included.add(Integer.valueOf(id));
} catch (Exception e) {
@@ -128,6 +135,10 @@ public abstract class CatalogPage implements Comparable, ISerialize
return this.clubOnly;
}
+ public CatalogPageType getCatalogPageType() {
+ return this.catalogPageType;
+ }
+
public String getLayout() {
return this.layout;
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageLayouts.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageLayouts.java
index ff6d01aa..f0456c1c 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageLayouts.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageLayouts.java
@@ -43,5 +43,6 @@ public enum CatalogPageLayouts {
builders_club_loyalty,
monkey,
niko,
- mad_money
+ mad_money,
+ custom_prefix
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageType.java
index d270395f..993ea34e 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageType.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPageType.java
@@ -4,6 +4,33 @@ public enum CatalogPageType {
NORMAL,
+ BUILDER,
- BUILDER
+ BOTH;
+
+ public static CatalogPageType fromString(String value) {
+ if (value == null || value.isEmpty()) {
+ return NORMAL;
+ }
+
+ switch (value.trim().toUpperCase()) {
+ case "BUILDERS_CLUB":
+ case "BUILDER":
+ case "BC":
+ return BUILDER;
+ case "BOTH":
+ return BOTH;
+ case "NORMAL":
+ default:
+ return NORMAL;
+ }
+ }
+
+ public boolean matches(CatalogPageType requestedType) {
+ if (this == BOTH || requestedType == BOTH) {
+ return true;
+ }
+
+ return this == requestedType;
+ }
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java
index b7654416..3f357713 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java
@@ -10,6 +10,30 @@ import java.util.Calendar;
import java.util.TimeZone;
public class ClubOffer implements ISerialize {
+ public static final int WINDOW_HABBO_CLUB = 1;
+ public static final int WINDOW_BUILDERS_CLUB = 2;
+ public static final int WINDOW_BUILDERS_CLUB_ADDONS = 3;
+
+ public enum OfferType {
+ HC,
+ VIP,
+ BUILDERS_CLUB,
+ BUILDERS_CLUB_ADDON;
+
+ public static OfferType fromDatabase(String value) {
+ if (value == null) {
+ return HC;
+ }
+
+ for (OfferType type : OfferType.values()) {
+ if (type.name().equalsIgnoreCase(value)) {
+ return type;
+ }
+ }
+
+ return HC;
+ }
+ }
private final int id;
@@ -28,12 +52,16 @@ public class ClubOffer implements ISerialize {
private final int pointsType;
+ private final OfferType type;
+
private final boolean vip;
private final boolean deal;
+ private final boolean giftable;
+
public ClubOffer(ResultSet set) throws SQLException {
this.id = set.getInt("id");
this.name = set.getString("name");
@@ -41,8 +69,10 @@ public class ClubOffer implements ISerialize {
this.credits = set.getInt("credits");
this.points = set.getInt("points");
this.pointsType = set.getInt("points_type");
- this.vip = set.getString("type").equalsIgnoreCase("vip");
+ this.type = OfferType.fromDatabase(set.getString("type"));
+ this.vip = this.type == OfferType.VIP;
this.deal = set.getString("deal").equals("1");
+ this.giftable = set.getString("giftable").equals("1");
}
public int getId() {
@@ -69,6 +99,10 @@ public class ClubOffer implements ISerialize {
return this.pointsType;
}
+ public OfferType getType() {
+ return this.type;
+ }
+
public boolean isVip() {
return this.vip;
}
@@ -77,13 +111,49 @@ public class ClubOffer implements ISerialize {
return this.deal;
}
+ public boolean isGiftable() {
+ return this.giftable;
+ }
+
+ public boolean isBuildersClubSubscription() {
+ return this.type == OfferType.BUILDERS_CLUB;
+ }
+
+ public boolean isBuildersClubAddon() {
+ return this.type == OfferType.BUILDERS_CLUB_ADDON;
+ }
+
+ public boolean isHabboClubOffer() {
+ return this.type == OfferType.HC || this.type == OfferType.VIP;
+ }
+
+ public boolean isSubscriptionOffer() {
+ return !this.isBuildersClubAddon();
+ }
+
+ public int getWindowId() {
+ if (this.isBuildersClubAddon()) {
+ return WINDOW_BUILDERS_CLUB_ADDONS;
+ }
+
+ if (this.isBuildersClubSubscription()) {
+ return WINDOW_BUILDERS_CLUB;
+ }
+
+ return WINDOW_HABBO_CLUB;
+ }
+
+ public boolean belongsToWindow(int windowId) {
+ return this.getWindowId() == windowId;
+ }
+
@Override
public void serialize(ServerMessage message) {
serialize(message, Emulator.getIntUnixTimestamp());
}
- public void serialize(ServerMessage message, int hcExpireTimestamp) {
- hcExpireTimestamp = Math.max(Emulator.getIntUnixTimestamp(), hcExpireTimestamp);
+ public void serialize(ServerMessage message, int expireTimestamp) {
+ expireTimestamp = Math.max(Emulator.getIntUnixTimestamp(), expireTimestamp);
message.appendInt(this.id);
message.appendString(this.name);
message.appendBoolean(false); //unused
@@ -96,27 +166,29 @@ public class ClubOffer implements ISerialize {
long secondsTotal = seconds;
- int totalYears = (int) Math.floor((int) seconds / (86400.0 * 31 * 12));
+ int totalYears = (int) Math.floor(seconds / (86400.0 * 31 * 12));
seconds -= totalYears * (86400 * 31 * 12);
- int totalMonths = (int) Math.floor((int) seconds / (86400.0 * 31));
+ int totalMonths = (int) Math.floor(seconds / (86400.0 * 31));
seconds -= totalMonths * (86400 * 31);
- int totalDays = (int) Math.floor((int) seconds / 86400.0);
+ int totalDays = (int) Math.floor(seconds / 86400.0);
seconds -= totalDays * 86400L;
- message.appendInt((int) secondsTotal / 86400 / 31);
- message.appendInt((int) seconds);
- message.appendBoolean(false); //giftable
- message.appendInt((int) seconds);
+ message.appendInt(totalMonths);
+ message.appendInt(totalDays);
+ message.appendBoolean(this.giftable);
+ message.appendInt(totalDays);
- hcExpireTimestamp += secondsTotal;
+ if (this.isSubscriptionOffer()) {
+ expireTimestamp += secondsTotal;
+ }
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
- cal.setTimeInMillis(hcExpireTimestamp * 1000L);
+ cal.setTimeInMillis(expireTimestamp * 1000L);
message.appendInt(cal.get(Calendar.YEAR));
message.appendInt(cal.get(Calendar.MONTH) + 1);
message.appendInt(cal.get(Calendar.DAY_OF_MONTH));
}
-}
\ No newline at end of file
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/layouts/CustomPrefixLayout.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/layouts/CustomPrefixLayout.java
new file mode 100644
index 00000000..8152b89c
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/layouts/CustomPrefixLayout.java
@@ -0,0 +1,27 @@
+package com.eu.habbo.habbohotel.catalog.layouts;
+
+import com.eu.habbo.habbohotel.catalog.CatalogPage;
+import com.eu.habbo.messages.ServerMessage;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class CustomPrefixLayout extends CatalogPage {
+
+ public CustomPrefixLayout(ResultSet set) throws SQLException {
+ super(set);
+ }
+
+ @Override
+ public void serialize(ServerMessage message) {
+ message.appendString("custom_prefix");
+ message.appendInt(3);
+ message.appendString(super.getHeaderImage());
+ message.appendString(super.getTeaserImage());
+ message.appendString(super.getSpecialImage());
+ message.appendInt(3);
+ message.appendString(super.getTextOne());
+ message.appendString(super.getTextDetails());
+ message.appendString(super.getTextTeaser());
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java
index 45a7be0a..edbcd4a3 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java
@@ -75,6 +75,12 @@ public class MarketPlace {
return;
}
+ ownerSet.first();
+ if (ownerSet.getInt("user_id") != habbo.getHabboInfo().getId()) {
+ LOGGER.warn("User {} attempted to take back marketplace offer {} owned by user {}", habbo.getHabboInfo().getId(), offer.getOfferId(), ownerSet.getInt("user_id"));
+ return;
+ }
+
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM marketplace_items WHERE id = ? AND state != 2")) {
statement.setInt(1, offer.getOfferId());
int count = statement.executeUpdate();
@@ -115,10 +121,10 @@ public class MarketPlace {
List offers = new ArrayList<>(10);
String query = "SELECT B.* FROM marketplace_items a INNER JOIN (SELECT b.item_id AS base_item_id, b.limited_data AS ltd_data, marketplace_items.*, AVG(price) as avg, MIN(marketplace_items.price) as minPrice, MAX(marketplace_items.price) as maxPrice, COUNT(*) as number, (SELECT COUNT(*) FROM marketplace_items c INNER JOIN items as items_b ON c.item_id = items_b.id WHERE state = 2 AND items_b.item_id = base_item_id AND DATE(from_unixtime(sold_timestamp)) = CURDATE()) as sold_count_today FROM marketplace_items INNER JOIN items b ON marketplace_items.item_id = b.id INNER JOIN items_base bi ON b.item_id = bi.id INNER JOIN catalog_items ci ON bi.id = ci.item_ids WHERE price = (SELECT MIN(e.price) FROM marketplace_items e, items d WHERE e.item_id = d.id AND d.item_id = b.item_id AND e.state = 1 AND e.timestamp > ? GROUP BY d.item_id) AND state = 1 AND timestamp > ?";
if (minPrice > 0) {
- query += " AND CEIL(price + (price / 100)) >= " + minPrice;
+ query += " AND CEIL(price + (price / 100)) >= ?";
}
if (maxPrice > 0 && maxPrice > minPrice) {
- query += " AND CEIL(price + (price / 100)) <= " + maxPrice;
+ query += " AND CEIL(price + (price / 100)) <= ?";
}
if (!search.isEmpty()) {
query += " AND ( bi.public_name LIKE ? OR ci.catalog_name LIKE ? ) ";
@@ -155,11 +161,18 @@ public class MarketPlace {
query += " LIMIT 250";
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(query)) {
- statement.setInt(1, Emulator.getIntUnixTimestamp() - 172800);
- statement.setInt(2, Emulator.getIntUnixTimestamp() - 172800);
+ int paramIndex = 1;
+ statement.setInt(paramIndex++, Emulator.getIntUnixTimestamp() - 172800);
+ statement.setInt(paramIndex++, Emulator.getIntUnixTimestamp() - 172800);
+ if (minPrice > 0) {
+ statement.setInt(paramIndex++, minPrice);
+ }
+ if (maxPrice > 0 && maxPrice > minPrice) {
+ statement.setInt(paramIndex++, maxPrice);
+ }
if (!search.isEmpty()) {
- statement.setString(3, "%" + search + "%");
- statement.setString(4, "%" + search + "%");
+ statement.setString(paramIndex++, "%" + search + "%");
+ statement.setString(paramIndex++, "%" + search + "%");
}
try (ResultSet set = statement.executeQuery()) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/AboutCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/AboutCommand.java
index 9f8e473e..3f5059f4 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/AboutCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/AboutCommand.java
@@ -15,7 +15,7 @@ public class AboutCommand extends Command {
}
public static String credits = "Arcturus Morningstar is an opensource project based on Arcturus By TheGeneral \n" +
"The Following people have all contributed to this emulator:\n" +
- "TheGeneral\n Beny\n Alejandro\n Capheus\n Skeletor\n Harmonic\n Mike\n Remco\n zGrav \n Quadral \n Harmony\n Swirny\n ArpyAge\n Mikkel\n Rodolfo\n Rasmus\n Kitt Mustang\n Snaiker\n nttzx\n necmi\n Dome\n Jose Flores\n Cam\n Oliver\n Narzo\n Tenshie\n MartenM\n Ridge\n SenpaiDipper\n Snaiker\n Thijmen\n DuckieTM\n simoleo89\n Medievalshell\n Lorenzune";
+ "TheGeneral\n Beny\n Alejandro\n Capheus\n Skeletor\n Harmonic\n Mike\n Remco\n zGrav \n Quadral \n Harmony\n Swirny\n ArpyAge\n Mikkel\n Rodolfo\n Rasmus\n Kitt Mustang\n Snaiker\n nttzx\n necmi\n Dome\n Jose Flores\n Cam\n Oliver\n Narzo\n Tenshie\n MartenM\n Ridge\n SenpaiDipper\n Snaiker\n Thijmen\n DuckieTM\n simoleo89\n Medievalshell\n Lorenzo (the wired master)";
@Override
public boolean handle(GameClient gameClient, String[] params) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/BadgeCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/BadgeCommand.java
index 1328ed19..8abf0b25 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/BadgeCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/BadgeCommand.java
@@ -36,7 +36,8 @@ public class BadgeCommand extends Command {
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(params[1]);
if (habbo != null) {
- if (habbo.addBadge(params[2])) {
+ String senderName = gameClient.getHabbo().getHabboInfo().getUsername();
+ if (habbo.addBadge(params[2], senderName)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_badge.given").replace("%user%", params[1]).replace("%badge%", params[2]), RoomChatMessageBubbles.ALERT);
} else {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_badge.already_owned").replace("%user%", params[1]).replace("%badge%", params[2]), RoomChatMessageBubbles.ALERT);
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
index 956b0430..3f26738b 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
@@ -297,7 +297,11 @@ public class CommandHandler {
addCommand(new SoftKickCommand());
addCommand(new SubscriptionCommand());
addCommand(new UpdateChatBubblesCommand());
-
+ addCommand(new GivePrefixCommand());
+ addCommand(new ListPrefixesCommand());
+ addCommand(new RemovePrefixCommand());
+ addCommand(new PrefixBlacklistCommand());
+ addCommand(new WiredCommand());
addCommand(new TestCommand());
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/GivePrefixCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/GivePrefixCommand.java
new file mode 100644
index 00000000..eba65376
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/GivePrefixCommand.java
@@ -0,0 +1,66 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
+import com.eu.habbo.habbohotel.users.Habbo;
+import com.eu.habbo.habbohotel.users.UserPrefix;
+import com.eu.habbo.messages.outgoing.inventory.prefixes.PrefixReceivedComposer;
+import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer;
+
+public class GivePrefixCommand extends Command {
+ public GivePrefixCommand() {
+ super("cmd_give_prefix", Emulator.getTexts().getValue("commands.keys.cmd_give_prefix").split(";"));
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ if (params.length < 4) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_prefix.usage"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ String targetName = params[1];
+ String text = params[2];
+ String color = params[3];
+ String icon = params.length > 4 ? params[4] : "";
+ String effect = params.length > 5 ? params[5] : "";
+
+ // Validate color
+ String[] colorParts = color.split(",");
+ for (String part : colorParts) {
+ if (!part.matches("^#[0-9A-Fa-f]{6}$")) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_prefix.invalid_color"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+ }
+
+ if (text.length() > 15) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_prefix.too_long"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetName);
+
+ if (target == null) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_prefix.user_not_found"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ UserPrefix prefix = new UserPrefix(target.getHabboInfo().getId(), text, color, icon, effect);
+ prefix.run();
+ target.getInventory().getPrefixesComponent().addPrefix(prefix);
+
+ target.getClient().sendResponse(new PrefixReceivedComposer(prefix));
+ target.getClient().sendResponse(new UserPrefixesComposer(target));
+
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_give_prefix")
+ .replace("%user%", targetName)
+ .replace("%prefix%", text),
+ RoomChatMessageBubbles.ALERT
+ );
+
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/ListPrefixesCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/ListPrefixesCommand.java
new file mode 100644
index 00000000..caa78b6e
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/ListPrefixesCommand.java
@@ -0,0 +1,59 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
+import com.eu.habbo.habbohotel.users.Habbo;
+import com.eu.habbo.habbohotel.users.UserPrefix;
+
+import java.util.List;
+
+public class ListPrefixesCommand extends Command {
+ public ListPrefixesCommand() {
+ super("cmd_list_prefixes", Emulator.getTexts().getValue("commands.keys.cmd_list_prefixes").split(";"));
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ if (params.length < 2) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_list_prefixes.usage"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ String targetName = params[1];
+
+ Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetName);
+
+ if (target == null) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_list_prefixes.user_not_found"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ List prefixes = target.getInventory().getPrefixesComponent().getPrefixes();
+
+ if (prefixes.isEmpty()) {
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_list_prefixes.empty").replace("%user%", targetName),
+ RoomChatMessageBubbles.ALERT
+ );
+ return true;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(Emulator.getTexts().getValue("commands.succes.cmd_list_prefixes.header").replace("%user%", targetName)).append("\r");
+
+ for (UserPrefix prefix : prefixes) {
+ sb.append("ID: ").append(prefix.getId())
+ .append(" | {").append(prefix.getText()).append("}")
+ .append(" | Color: ").append(prefix.getColor())
+ .append(prefix.getIcon().isEmpty() ? "" : " | Icon: " + prefix.getIcon())
+ .append(prefix.getEffect().isEmpty() ? "" : " | Effect: " + prefix.getEffect())
+ .append(prefix.isActive() ? " [ACTIVE]" : "")
+ .append("\r");
+ }
+
+ gameClient.getHabbo().whisper(sb.toString(), RoomChatMessageBubbles.ALERT);
+
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PrefixBlacklistCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PrefixBlacklistCommand.java
new file mode 100644
index 00000000..dc8bbd69
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PrefixBlacklistCommand.java
@@ -0,0 +1,98 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class PrefixBlacklistCommand extends Command {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PrefixBlacklistCommand.class);
+
+ public PrefixBlacklistCommand() {
+ super("cmd_prefix_blacklist", Emulator.getTexts().getValue("commands.keys.cmd_prefix_blacklist").split(";"));
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ if (params.length < 2) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ String action = params[1].toLowerCase();
+
+ if (action.equals("list")) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.header")).append("\r");
+
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement statement = connection.prepareStatement("SELECT word FROM custom_prefix_blacklist ORDER BY word")) {
+ try (ResultSet set = statement.executeQuery()) {
+ int count = 0;
+ while (set.next()) {
+ sb.append("- ").append(set.getString("word")).append("\r");
+ count++;
+ }
+ if (count == 0) {
+ sb.append(Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.empty"));
+ }
+ }
+ } catch (SQLException e) {
+ LOGGER.error("Error listing prefix blacklist", e);
+ }
+
+ gameClient.getHabbo().whisper(sb.toString(), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ if (params.length < 3) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ String word = params[2].toLowerCase().trim();
+
+ if (word.isEmpty()) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.empty_word"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ if (action.equals("add")) {
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement statement = connection.prepareStatement("INSERT INTO custom_prefix_blacklist (word) VALUES (?)")) {
+ statement.setString(1, word);
+ statement.execute();
+ } catch (SQLException e) {
+ LOGGER.error("Error adding prefix blacklist word", e);
+ }
+
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.added").replace("%word%", word),
+ RoomChatMessageBubbles.ALERT
+ );
+ } else if (action.equals("remove")) {
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
+ PreparedStatement statement = connection.prepareStatement("DELETE FROM custom_prefix_blacklist WHERE word = ?")) {
+ statement.setString(1, word);
+ statement.execute();
+ } catch (SQLException e) {
+ LOGGER.error("Error removing prefix blacklist word", e);
+ }
+
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.removed").replace("%word%", word),
+ RoomChatMessageBubbles.ALERT
+ );
+ } else {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
+ }
+
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RemovePrefixCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RemovePrefixCommand.java
new file mode 100644
index 00000000..9396d18d
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RemovePrefixCommand.java
@@ -0,0 +1,81 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
+import com.eu.habbo.habbohotel.users.Habbo;
+import com.eu.habbo.habbohotel.users.UserPrefix;
+import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer;
+
+import java.util.List;
+
+public class RemovePrefixCommand extends Command {
+ public RemovePrefixCommand() {
+ super("cmd_remove_prefix", Emulator.getTexts().getValue("commands.keys.cmd_remove_prefix").split(";"));
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ if (params.length < 3) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_remove_prefix.usage"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ String targetName = params[1];
+ String prefixIdStr = params[2];
+
+ Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetName);
+
+ if (target == null) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_remove_prefix.user_not_found"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ if (prefixIdStr.equalsIgnoreCase("all")) {
+ List prefixes = target.getInventory().getPrefixesComponent().getPrefixes();
+ for (UserPrefix prefix : prefixes) {
+ prefix.needsDelete(true);
+ Emulator.getThreading().run(prefix);
+ }
+ // Clear in-memory
+ for (UserPrefix prefix : prefixes) {
+ target.getInventory().getPrefixesComponent().removePrefix(prefix);
+ }
+
+ target.getClient().sendResponse(new UserPrefixesComposer(target));
+
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_remove_prefix.all").replace("%user%", targetName),
+ RoomChatMessageBubbles.ALERT
+ );
+ } else {
+ int prefixId;
+ try {
+ prefixId = Integer.parseInt(prefixIdStr);
+ } catch (NumberFormatException e) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_remove_prefix.invalid_id"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ UserPrefix prefix = target.getInventory().getPrefixesComponent().getPrefix(prefixId);
+
+ if (prefix == null) {
+ gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_remove_prefix.not_found"), RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ target.getInventory().getPrefixesComponent().removePrefix(prefix);
+ prefix.needsDelete(true);
+ Emulator.getThreading().run(prefix);
+
+ target.getClient().sendResponse(new UserPrefixesComposer(target));
+
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_remove_prefix").replace("%user%", targetName).replace("%id%", String.valueOf(prefixId)),
+ RoomChatMessageBubbles.ALERT
+ );
+ }
+
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RoomBundleCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RoomBundleCommand.java
index e1992e98..e2776daa 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RoomBundleCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RoomBundleCommand.java
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.catalog.CatalogItem;
import com.eu.habbo.habbohotel.catalog.CatalogPage;
import com.eu.habbo.habbohotel.catalog.CatalogPageLayouts;
+import com.eu.habbo.habbohotel.catalog.CatalogPageType;
import com.eu.habbo.habbohotel.catalog.layouts.RoomBundleLayout;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
@@ -41,7 +42,7 @@ public class RoomBundleCommand extends Command {
points = Integer.parseInt(params[3]);
pointsType = Integer.parseInt(params[4]);
- CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().createCatalogPage("Room Bundle: " + gameClient.getHabbo().getHabboInfo().getCurrentRoom().getName(), "room_bundle_" + gameClient.getHabbo().getHabboInfo().getCurrentRoom().getId(), gameClient.getHabbo().getHabboInfo().getCurrentRoom().getId(), 0, CatalogPageLayouts.room_bundle, gameClient.getHabbo().getHabboInfo().getRank().getId(), parentId);
+ CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().createCatalogPage("Room Bundle: " + gameClient.getHabbo().getHabboInfo().getCurrentRoom().getName(), "room_bundle_" + gameClient.getHabbo().getHabboInfo().getCurrentRoom().getId(), gameClient.getHabbo().getHabboInfo().getCurrentRoom().getId(), 0, CatalogPageLayouts.room_bundle, gameClient.getHabbo().getHabboInfo().getRank().getId(), parentId, CatalogPageType.NORMAL, CatalogPageType.NORMAL);
if (page instanceof RoomBundleLayout) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO catalog_items (page_id, item_ids, catalog_name, cost_credits, cost_points, points_type ) VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java
index 376f8b94..7cd922e9 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UnmuteCommand.java
@@ -29,7 +29,7 @@ public class UnmuteCommand extends Command {
}
if (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo)) {
- habbo.getHabboInfo().getCurrentRoom().muteHabbo(habbo, 1);
+ habbo.getHabboInfo().getCurrentRoom().unmuteHabbo(habbo);
}
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_unmute").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT);
@@ -41,4 +41,4 @@ public class UnmuteCommand extends Command {
return true;
}
-}
\ No newline at end of file
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UserInfoCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UserInfoCommand.java
index b4a3ca78..c2a244cc 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UserInfoCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/UserInfoCommand.java
@@ -84,7 +84,7 @@ public class UserInfoCommand extends Command {
if (onlineHabbo != null) {
message.append("\r" + "Other accounts (");
- ArrayList users = Emulator.getGameEnvironment().getHabboManager().getCloneAccounts(onlineHabbo, 10);
+ List users = Emulator.getGameEnvironment().getHabboManager().getCloneAccounts(onlineHabbo, 10);
users.sort(new Comparator() {
@Override
public int compare(HabboInfo o1, HabboInfo o2) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java
new file mode 100644
index 00000000..59ab1a4b
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/WiredCommand.java
@@ -0,0 +1,30 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
+import com.eu.habbo.messages.outgoing.users.InClientLinkComposer;
+
+public class WiredCommand extends Command {
+ public WiredCommand() {
+ super(null, new String[]{"wired"});
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ Room room = gameClient.getHabbo().getHabboInfo().getCurrentRoom();
+
+ if (room == null) {
+ gameClient.getHabbo().whisper("You need to be inside a room to use :wired.", RoomChatMessageBubbles.ALERT);
+ return true;
+ }
+
+ if (!room.canInspectWired(gameClient.getHabbo())) {
+ gameClient.sendResponse(new InClientLinkComposer("wired-tools/invalid"));
+ return true;
+ }
+
+ gameClient.sendResponse(new InClientLinkComposer("wired-tools/show"));
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
index 61c219cc..f526c15b 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
@@ -26,6 +26,7 @@ public class GameClient {
private Habbo habbo;
private boolean handshakeFinished;
private String machineId = "";
+ private String ssoTicket = "";
public final ConcurrentHashMap incomingPacketCounter = new ConcurrentHashMap<>(25);
public final ConcurrentHashMap, Long> messageTimestamps = new ConcurrentHashMap<>();
@@ -82,6 +83,14 @@ public class GameClient {
this.machineId = machineId;
}
+ public String getSsoTicket() {
+ return this.ssoTicket;
+ }
+
+ public void setSsoTicket(String ssoTicket) {
+ this.ssoTicket = ssoTicket != null ? ssoTicket : "";
+ }
+
public void sendResponse(MessageComposer composer) {
this.sendResponse(composer.compose());
}
@@ -145,8 +154,15 @@ public class GameClient {
if (this.habbo != null) {
if (this.habbo.isOnline()) {
- this.habbo.getHabboInfo().setOnline(false);
- this.habbo.disconnect();
+ // Try to park the habbo in the grace period instead of immediate disconnect
+ boolean parked = SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket);
+
+ if (!parked) {
+ // No grace period configured — immediate disconnect as before
+ this.habbo.getHabboInfo().setOnline(false);
+ this.habbo.disconnect();
+ }
+ // If parked, do NOT call disconnect() — the habbo stays in the room
}
this.habbo = null;
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java
index 68366a0a..cd0602cb 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java
@@ -116,6 +116,22 @@ public class GameClientManager {
}
+ /**
+ * Find an existing GameClient that authenticated with the given SSO ticket.
+ * Used to detect reconnections where the old connection hasn't been closed yet.
+ */
+ public GameClient findClientBySsoTicket(String ssoTicket) {
+ if (ssoTicket == null || ssoTicket.isEmpty()) return null;
+
+ for (GameClient client : this.clients.values()) {
+ if (ssoTicket.equals(client.getSsoTicket()) && client.getHabbo() != null) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+
public List getHabbosWithMachineId(String machineId) {
List habbos = new ArrayList<>();
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java
new file mode 100644
index 00000000..f2724578
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java
@@ -0,0 +1,173 @@
+package com.eu.habbo.habbohotel.gameclients;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.users.Habbo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+
+/**
+ * Manages a grace period for disconnected users. Instead of immediately
+ * disposing a Habbo when their WebSocket drops, the Habbo is held in
+ * a "ghost" state for a configurable number of seconds. If the same
+ * user reconnects (via SSO ticket) within the grace window, their
+ * existing Habbo object is resumed on the new connection — keeping
+ * them in their room, preserving inventory state, etc.
+ *
+ * Config key: session.reconnect.grace.seconds (default: 30)
+ */
+public class SessionResumeManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SessionResumeManager.class);
+
+ private static SessionResumeManager instance;
+
+ private final ConcurrentHashMap ghostSessions = new ConcurrentHashMap<>();
+
+ public static SessionResumeManager getInstance() {
+ if (instance == null) {
+ instance = new SessionResumeManager();
+ }
+ return instance;
+ }
+
+ public int getGracePeriodSeconds() {
+ return Emulator.getConfig().getInt("session.reconnect.grace.seconds", 30);
+ }
+
+ /**
+ * Park a disconnected Habbo in ghost mode. Their room presence is
+ * preserved, but the old GameClient channel is closed.
+ *
+ * @return true if the habbo was parked (grace period > 0), false if immediate dispose should happen
+ */
+ public boolean parkHabbo(Habbo habbo, String ssoTicket) {
+ int graceSeconds = getGracePeriodSeconds();
+ if (graceSeconds <= 0) {
+ return false;
+ }
+
+ int userId = habbo.getHabboInfo().getId();
+
+ // Cancel any existing ghost session for this user
+ GhostSession existing = ghostSessions.remove(userId);
+ if (existing != null && existing.disposeFuture != null) {
+ existing.disposeFuture.cancel(false);
+ }
+
+ LOGGER.info("[SessionResume] Parking {} (id={}) for {}s grace period",
+ habbo.getHabboInfo().getUsername(), userId, graceSeconds);
+
+ // Restore the SSO ticket so the client can reconnect with the same ticket
+ if (ssoTicket != null && !ssoTicket.isEmpty()) {
+ restoreSsoTicket(userId, ssoTicket);
+ }
+
+ // Schedule the final disconnect after the grace period
+ ScheduledFuture> future = Emulator.getThreading().run(() -> {
+ GhostSession ghost = ghostSessions.remove(userId);
+ if (ghost != null) {
+ LOGGER.info("[SessionResume] Grace period expired for {} (id={}) - performing full disconnect",
+ ghost.habbo.getHabboInfo().getUsername(), userId);
+ performFullDisconnect(ghost.habbo);
+ }
+ }, graceSeconds * 1000);
+
+ ghostSessions.put(userId, new GhostSession(habbo, ssoTicket, future));
+ return true;
+ }
+
+ /**
+ * Try to resume a ghost session for the given user ID.
+ *
+ * @return the parked Habbo if found within grace period, null otherwise
+ */
+ public Habbo resumeSession(int userId) {
+ GhostSession ghost = ghostSessions.remove(userId);
+ if (ghost == null) {
+ return null;
+ }
+
+ // Cancel the scheduled dispose
+ if (ghost.disposeFuture != null) {
+ ghost.disposeFuture.cancel(false);
+ }
+
+ LOGGER.info("[SessionResume] Resuming session for {} (id={})",
+ ghost.habbo.getHabboInfo().getUsername(), userId);
+
+ return ghost.habbo;
+ }
+
+ /**
+ * Check if a user has a ghost session (is in grace period).
+ */
+ public boolean hasGhostSession(int userId) {
+ return ghostSessions.containsKey(userId);
+ }
+
+ /**
+ * Immediately expire all ghost sessions (e.g. on emulator shutdown).
+ */
+ public void disposeAll() {
+ for (GhostSession ghost : ghostSessions.values()) {
+ if (ghost.disposeFuture != null) {
+ ghost.disposeFuture.cancel(false);
+ }
+ performFullDisconnect(ghost.habbo);
+ }
+ ghostSessions.clear();
+ }
+
+ /**
+ * Perform the actual full disconnect that normally happens in Habbo.disconnect().
+ */
+ private void performFullDisconnect(Habbo habbo) {
+ try {
+ habbo.getHabboInfo().setOnline(false);
+ habbo.disconnect();
+ } catch (Exception e) {
+ LOGGER.error("[SessionResume] Error during deferred disconnect", e);
+ }
+
+ // Clear the SSO ticket now that the grace period is truly over
+ clearSsoTicket(habbo.getHabboInfo().getId());
+ }
+
+ private void restoreSsoTicket(int userId, String ssoTicket) {
+ try (var connection = Emulator.getDatabase().getDataSource().getConnection();
+ var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
+ statement.setString(1, ssoTicket);
+ statement.setInt(2, userId);
+ statement.execute();
+ LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId);
+ } catch (Exception e) {
+ LOGGER.error("[SessionResume] Failed to restore SSO ticket for user " + userId, e);
+ }
+ }
+
+ private void clearSsoTicket(int userId) {
+ try (var connection = Emulator.getDatabase().getDataSource().getConnection();
+ var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
+ statement.setString(1, "");
+ statement.setInt(2, userId);
+ statement.execute();
+ } catch (Exception e) {
+ LOGGER.error("[SessionResume] Failed to clear SSO ticket for user " + userId, e);
+ }
+ }
+
+ private static class GhostSession {
+ final Habbo habbo;
+ final String ssoTicket;
+ final ScheduledFuture> disposeFuture;
+
+ GhostSession(Habbo habbo, String ssoTicket, ScheduledFuture> disposeFuture) {
+ this.habbo = habbo;
+ this.ssoTicket = ssoTicket;
+ this.disposeFuture = disposeFuture;
+ }
+ }
+}
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/guilds/forums/ForumThread.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/guilds/forums/ForumThread.java
index a82a2d01..0bc0c265 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/guilds/forums/ForumThread.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/guilds/forums/ForumThread.java
@@ -135,16 +135,10 @@ public class ForumThread implements Runnable, ISerialize {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT A.*, B.`id` AS `last_comment_id` " +
"FROM guilds_forums_threads A " +
- "JOIN (" +
- "SELECT * " +
+ "LEFT JOIN (" +
+ "SELECT `thread_id`, MAX(`id`) AS `id`, MAX(`created_at`) AS `created_at` " +
"FROM `guilds_forums_comments` " +
- "WHERE `id` IN (" +
- "SELECT MAX(id) " +
- "FROM `guilds_forums_comments` B " +
- "GROUP BY `thread_id` AND B.`id` " +
- "ORDER BY B.`id` " +
- ") " +
- "ORDER BY `id` DESC " +
+ "GROUP BY `thread_id`" +
") B ON A.`id` = B.`thread_id` " +
"WHERE A.`guild_id` = ? " +
"ORDER BY A.`pinned` DESC, B.`created_at` DESC "
@@ -176,16 +170,10 @@ public class ForumThread implements Runnable, ISerialize {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
"SELECT A.*, B.`id` AS `last_comment_id` " +
"FROM guilds_forums_threads A " +
- "JOIN (" +
- "SELECT * " +
+ "LEFT JOIN (" +
+ "SELECT `thread_id`, MAX(`id`) AS `id`, MAX(`created_at`) AS `created_at` " +
"FROM `guilds_forums_comments` " +
- "WHERE `id` IN (" +
- "SELECT MAX(id) " +
- "FROM `guilds_forums_comments` B " +
- "GROUP BY `thread_id` AND b.`id`" +
- "ORDER BY B.`id` " +
- ") " +
- "ORDER BY `id` DESC " +
+ "GROUP BY `thread_id`" +
") B ON A.`id` = B.`thread_id` " +
"WHERE A.`id` = ? " +
"ORDER BY A.`pinned` DESC, B.`created_at` DESC " +
@@ -222,6 +210,19 @@ public class ForumThread implements Runnable, ISerialize {
guildThreads.add(thread);
}
+ public static void clearCacheForGuild(int guildId) {
+ synchronized (guildThreadsCache) {
+ THashSet threads = guildThreadsCache.remove(guildId);
+ if (threads != null) {
+ synchronized (forumThreadsCache) {
+ for (ForumThread thread : threads) {
+ forumThreadsCache.remove(thread.threadId);
+ }
+ }
+ }
+ }
+ }
+
public static void clearCache() {
for (THashSet threads : guildThreadsCache.values()) {
for (ForumThread thread : threads) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/Item.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/Item.java
index 4e0a69fe..0ca0afbe 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/Item.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/Item.java
@@ -91,7 +91,23 @@ public class Item implements ISerialize {
this.allowGift = set.getBoolean("allow_gift");
this.allowInventoryStack = set.getBoolean("allow_inventory_stack");
- this.interactionType = Emulator.getGameEnvironment().getItemManager().getItemInteraction(set.getString("interaction_type").toLowerCase());
+ String interactionTypeName = set.getString("interaction_type");
+ if (interactionTypeName == null) {
+ interactionTypeName = "default";
+ }
+
+ this.interactionType = Emulator.getGameEnvironment().getItemManager().getItemInteraction(interactionTypeName.toLowerCase());
+
+ if ((this.interactionType != null)
+ && "default".equalsIgnoreCase(this.interactionType.getName())
+ && (this.fullName != null)
+ && this.fullName.toLowerCase().startsWith("wf_")) {
+ ItemInteraction fallbackInteraction = Emulator.getGameEnvironment().getItemManager().getItemInteraction(this.fullName.toLowerCase());
+
+ if ((fallbackInteraction != null) && !"default".equalsIgnoreCase(fallbackInteraction.getName())) {
+ this.interactionType = fallbackInteraction;
+ }
+ }
this.stateCount = set.getShort("interaction_modes_count");
this.effectM = set.getShort("effect_id_male");
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 50928f05..9280c25d 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;
@@ -48,15 +49,33 @@ import com.eu.habbo.habbohotel.items.interactions.totems.InteractionTotemLegs;
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.selector.WiredEffectFurniArea;
-import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersArea;
-import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersNeighborhood;
-import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniNeighborhood;
-import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniByType;
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.WiredExtraFilterFurniByVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFurniVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.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.WiredExtraRoomVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputFurniName;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextInputVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraContextVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUserVariable;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableEcho;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableReference;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableLevelUpSystem;
+import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableTextConnector;
+import com.eu.habbo.habbohotel.items.interactions.wired.selector.*;
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.*;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
@@ -150,6 +169,7 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("monsterplant_seed", InteractionMonsterPlantSeed.class));
this.interactionsList.add(new ItemInteraction("gift", InteractionGift.class));
this.interactionsList.add(new ItemInteraction("stack_helper", InteractionStackHelper.class));
+ this.interactionsList.add(new ItemInteraction("stack_walk_helper", InteractionStackWalkHelper.class));
this.interactionsList.add(new ItemInteraction("puzzle_box", InteractionPuzzleBox.class));
this.interactionsList.add(new ItemInteraction("hopper", InteractionHopper.class));
this.interactionsList.add(new ItemInteraction("costume_hopper", InteractionCostumeHopper.class));
@@ -179,6 +199,13 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("youtube", InteractionYoutubeTV.class));
this.interactionsList.add(new ItemInteraction("jukebox", InteractionJukeBox.class));
this.interactionsList.add(new ItemInteraction("switch", InteractionSwitch.class));
+ this.interactionsList.add(new ItemInteraction("conf_invis_control", InteractionConfInvisControl.class));
+ this.interactionsList.add(new ItemInteraction("wf_conf_invis_control", InteractionConfInvisControl.class));
+ this.interactionsList.add(new ItemInteraction("wf_conf_area_hide", InteractionAreaHideControl.class));
+ this.interactionsList.add(new ItemInteraction("conf_area_hide", InteractionAreaHideControl.class));
+ this.interactionsList.add(new ItemInteraction("wf_conf_handitem_block", InteractionHanditemBlockControl.class));
+ this.interactionsList.add(new ItemInteraction("wf_conf_queue_speed", InteractionQueueSpeedControl.class));
+ this.interactionsList.add(new ItemInteraction("wf_conf_wired_disable", InteractionWiredDisableControl.class));
this.interactionsList.add(new ItemInteraction("switch_remote_control", InteractionSwitchRemoteControl.class));
this.interactionsList.add(new ItemInteraction("fx_box", InteractionFXBox.class));
this.interactionsList.add(new ItemInteraction("blackhole", InteractionBlackHole.class));
@@ -202,16 +229,28 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("random_state", InteractionRandomState.class));
this.interactionsList.add(new ItemInteraction("vendingmachine_no_sides", InteractionNoSidesVendingMachine.class));
this.interactionsList.add(new ItemInteraction("tile_walkmagic", InteractionTileWalkMagic.class));
+ this.interactionsList.add(new ItemInteraction("antenna", InteractionDefault.class));
+ 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));
+ this.interactionsList.add(new ItemInteraction("wf_trg_click_furni", WiredTriggerHabboClicksFurni.class));
+ this.interactionsList.add(new ItemInteraction("wf_trg_click_tile", WiredTriggerHabboClicksTile.class));
+ this.interactionsList.add(new ItemInteraction("wf_trg_click_user", WiredTriggerHabboClicksUser.class));
+ this.interactionsList.add(new ItemInteraction("wf_trg_user_performs_action", WiredTriggerHabboPerformsAction.class));
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_var_changed", WiredTriggerVariableChanged.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));
this.interactionsList.add(new ItemInteraction("wf_trg_state_changed", WiredTriggerFurniStateToggled.class));
+ this.interactionsList.add(new ItemInteraction("wf_trg_stuff_state", WiredTriggerFurniStateToggled.class));
this.interactionsList.add(new ItemInteraction("wf_trg_at_given_time", WiredTriggerAtSetTime.class));
this.interactionsList.add(new ItemInteraction("wf_trg_at_time_long", WiredTriggerAtTimeLong.class));
this.interactionsList.add(new ItemInteraction("wf_trg_collision", WiredTriggerCollision.class));
@@ -242,6 +281,8 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_act_move_furni_to", WiredEffectMoveFurniTo.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_reward", WiredEffectGiveReward.class));
this.interactionsList.add(new ItemInteraction("wf_act_call_stacks", WiredEffectTriggerStacks.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_neg_call_stack", WiredEffectNegativeTriggerStacks.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_neg_call_stacks", WiredEffectNegativeTriggerStacks.class));
this.interactionsList.add(new ItemInteraction("wf_act_kick_user", WiredEffectKickHabbo.class));
this.interactionsList.add(new ItemInteraction("wf_act_mute_triggerer", WiredEffectMuteHabbo.class));
this.interactionsList.add(new ItemInteraction("wf_act_bot_teleport", WiredEffectBotTeleport.class));
@@ -255,12 +296,40 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_act_alert", WiredEffectAlert.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_handitem", WiredEffectGiveHandItem.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_effect", WiredEffectGiveEffect.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_freeze", WiredEffectFreeze.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_unfreeze", WiredEffectUnfreeze.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_furni_to_user", WiredEffectFurniToUser.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_user_to_furni", WiredEffectUserToFurni.class));
+ 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_slc_furni_with_var", WiredEffectFurniWithVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_slc_users_with_var", WiredEffectUsersWithVariable.class));
this.interactionsList.add(new ItemInteraction("wf_act_send_signal", WiredEffectSendSignal.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_neg_send_signal", WiredEffectNegativeSendSignal.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_give_var", WiredEffectGiveVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_remove_var", WiredEffectRemoveVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_act_change_var_val", WiredEffectChangeVariableValue.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_has_furni_on", WiredConditionFurniHaveFurni.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_furnis_hv_avtrs", WiredConditionFurniHaveHabbo.class));
@@ -285,14 +354,54 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_cnd_actor_in_team", WiredConditionTeamMember.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_trggrer_on_frn", WiredConditionTriggerOnFurni.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_has_handitem", WiredConditionHabboHasHandItem.class));
+ 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_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));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_triggerer_match", WiredConditionTriggererMatch.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_not_triggerer_match", WiredConditionNotTriggererMatch.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_score", WiredConditionTeamHasScore.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_rank", WiredConditionTeamHasRank.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_has_var", WiredConditionHasVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_neg_has_var", WiredConditionNotHasVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_var_val_match", WiredConditionVariableValueMatch.class));
+ this.interactionsList.add(new ItemInteraction("wf_cnd_var_age_match", WiredConditionVariableAgeMatch.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class));
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_filter_furni_by_var", WiredExtraFilterFurniByVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_filter_users_by_var", WiredExtraFilterUsersByVariable.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_xtra_exec_in_order", WiredExtraExecuteInOrder.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_execution_limit", WiredExtraExecutionLimit.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_username", WiredExtraTextOutputUsername.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_furni_name", WiredExtraTextOutputFurniName.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_variable", WiredExtraTextOutputVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_text_input_variable", WiredExtraTextInputVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_var_text_connector", WiredExtraVariableTextConnector.class));
+ this.interactionsList.add(new ItemInteraction("wf_xtra_var_lvlup_system", WiredExtraVariableLevelUpSystem.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_user", WiredExtraUserVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_furni", WiredExtraFurniVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_room", WiredExtraRoomVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_context", WiredExtraContextVariable.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_reference", WiredExtraVariableReference.class));
+ this.interactionsList.add(new ItemInteraction("wf_var_echo", WiredExtraVariableEcho.class));
this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class));
@@ -574,7 +683,7 @@ public class ItemManager {
statement.execute();
try (ResultSet set = statement.getGeneratedKeys()) {
- try (PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO items_presents VALUES (?, ?)")) {
+ try (PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO items_presents (item_id, base_item_reward) VALUES (?, ?)")) {
while (set.next() && item == null) {
preparedStatement.setInt(1, set.getInt(1));
preparedStatement.setInt(2, Integer.parseInt(itemId));
@@ -635,7 +744,7 @@ public class ItemManager {
}
public void insertTeleportPair(int itemOneId, int itemTwoId) {
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items_teleports VALUES (?, ?)")) {
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items_teleports (teleport_one_id, teleport_two_id) VALUES (?, ?)")) {
statement.setInt(1, itemOneId);
statement.setInt(2, itemTwoId);
statement.execute();
@@ -655,20 +764,28 @@ public class ItemManager {
}
public int[] getTargetTeleportRoomId(HabboItem item) {
- int[] a = new int[]{};
+ int[] target = new int[]{};
- try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT items.id, items.room_id FROM items_teleports INNER JOIN items ON items_teleports.teleport_one_id = items.id OR items_teleports.teleport_two_id = items.id WHERE items.id != ? AND items.room_id > 0 LIMIT 1")) {
+ try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT items_teleports.*, A.room_id as a_room_id, A.id as a_id, B.room_id as b_room_id, B.id as b_id FROM items_teleports INNER JOIN items AS A ON items_teleports.teleport_one_id = A.id INNER JOIN items AS B ON items_teleports.teleport_two_id = B.id WHERE (teleport_one_id = ? OR teleport_two_id = ?) LIMIT 1")) {
statement.setInt(1, item.getId());
+ statement.setInt(2, item.getId());
+
try (ResultSet set = statement.executeQuery()) {
if (set.next()) {
- a = new int[]{set.getInt("room_id"), set.getInt("id")};
+ final boolean useA = (set.getInt("a_id") != item.getId());
+ final int targetRoomId = useA ? set.getInt("a_room_id") : set.getInt("b_room_id");
+ final int targetItemId = useA ? set.getInt("a_id") : set.getInt("b_id");
+
+ if (targetRoomId > 0 && targetItemId > 0) {
+ target = new int[]{targetRoomId, targetItemId};
+ }
}
}
} catch (SQLException e) {
LOGGER.error("Caught SQL exception", e);
}
- return a;
+ return target;
}
public HabboItem loadHabboItem(int itemId) {
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/YoutubeManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/YoutubeManager.java
index 17e2f1cf..040a13f1 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/YoutubeManager.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/YoutubeManager.java
@@ -76,7 +76,11 @@ public class YoutubeManager {
private final THashMap> playlists = new THashMap<>();
private final THashMap playlistCache = new THashMap<>();
- private final String apiKey = Emulator.getConfig().getValue("youtube.apikey");
+
+ private String getApiKey() {
+ String key = Emulator.getConfig().getValue("youtube.apikey");
+ return key != null ? key : "";
+ }
public void load() {
this.playlists.clear();
@@ -89,11 +93,19 @@ public class YoutubeManager {
LOGGER.info("YouTube Manager -> Loading...");
+ if (getApiKey().isEmpty()) {
+ LOGGER.warn("YouTube Manager -> No API key configured (youtube.apikey). YouTube TVs will not work!");
+ }
+
+ int dbEntryCount = 0;
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM youtube_playlists")) {
try (ResultSet set = statement.executeQuery()) {
while (set.next()) {
final int itemId = set.getInt("item_id");
final String playlistId = set.getString("playlist_id");
+ dbEntryCount++;
+
+ LOGGER.info("YouTube Manager -> Loading playlist {} for base item #{}", playlistId, itemId);
youtubeDataLoaderPool.submit(() -> {
YoutubePlaylist playlist;
@@ -101,6 +113,9 @@ public class YoutubeManager {
playlist = this.getPlaylistDataById(playlistId);
if (playlist != null) {
this.addPlaylistToItem(itemId, playlist);
+ LOGGER.info("YouTube Manager -> Successfully loaded playlist {} for base item #{}", playlistId, itemId);
+ } else {
+ LOGGER.error("YouTube Manager -> Failed to load playlist {} for base item #{} (returned null - check API key and playlist ID)", playlistId, itemId);
}
} catch (IOException e) {
LOGGER.error("Failed to load YouTube playlist {} ERROR: {}", playlistId, e);
@@ -112,6 +127,10 @@ public class YoutubeManager {
LOGGER.error("Caught SQL exception", e);
}
+ if (dbEntryCount == 0) {
+ LOGGER.warn("YouTube Manager -> No entries found in youtube_playlists table!");
+ }
+
youtubeDataLoaderPool.shutdown();
try {
youtubeDataLoaderPool.awaitTermination(60, TimeUnit.SECONDS);
@@ -125,7 +144,12 @@ public class YoutubeManager {
public YoutubePlaylist getPlaylistDataById(String playlistId) throws IOException {
if (this.playlistCache.containsKey(playlistId)) return this.playlistCache.get(playlistId);
- if(apiKey.isEmpty()) return null;
+
+ String apiKey = getApiKey();
+ if(apiKey.isEmpty()) {
+ LOGGER.error("YouTube API key is not configured! Set 'youtube.apikey' in emulator_settings to enable YouTube TV.");
+ return null;
+ }
YoutubePlaylist playlist;
URL playlistInfo = URI.create("https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&id=" + playlistId + "&maxResults=1&key=" + apiKey).toURL();
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionAreaHideControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionAreaHideControl.java
new file mode 100644
index 00000000..5c7e975b
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionAreaHideControl.java
@@ -0,0 +1,172 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomAreaHideSupport;
+import com.eu.habbo.habbohotel.rooms.RoomLayout;
+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.messages.ServerMessage;
+import gnu.trove.map.hash.THashMap;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionAreaHideControl extends InteractionCustomValues {
+ public static final THashMap defaultValues = new THashMap() {
+ {
+ this.put("state", "0");
+ }
+ {
+ this.put("rootX", "0");
+ }
+ {
+ this.put("rootY", "0");
+ }
+ {
+ this.put("width", "0");
+ }
+ {
+ this.put("length", "0");
+ }
+ {
+ this.put("invisibility", "0");
+ }
+ {
+ this.put("wallItems", "0");
+ }
+ {
+ this.put("invert", "0");
+ }
+ };
+
+ public InteractionAreaHideControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem, defaultValues);
+ this.normalizeValues();
+ }
+
+ public InteractionAreaHideControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells, defaultValues);
+ this.normalizeValues();
+ }
+
+ @Override
+ public void serializeExtradata(ServerMessage serverMessage) {
+ this.normalizeValues();
+
+ serverMessage.appendInt(5 + (this.isLimited() ? 256 : 0));
+ serverMessage.appendInt(8);
+ serverMessage.appendInt(RoomAreaHideSupport.getState(this));
+ serverMessage.appendInt(RoomAreaHideSupport.getRootX(this));
+ serverMessage.appendInt(RoomAreaHideSupport.getRootY(this));
+ serverMessage.appendInt(RoomAreaHideSupport.getWidth(this));
+ serverMessage.appendInt(RoomAreaHideSupport.getLength(this));
+ serverMessage.appendInt(RoomAreaHideSupport.isInvisibilityEnabled(this) ? 1 : 0);
+ serverMessage.appendInt(RoomAreaHideSupport.includesWallItems(this) ? 1 : 0);
+ serverMessage.appendInt(RoomAreaHideSupport.isInverted(this) ? 1 : 0);
+
+ if (this.isLimited()) {
+ serverMessage.appendInt(this.getLimitedSells());
+ serverMessage.appendInt(this.getLimitedStack());
+ }
+ }
+
+ @Override
+ public boolean isUsable() {
+ return true;
+ }
+
+ @Override
+ public boolean allowWiredResetState() {
+ return true;
+ }
+
+ @Override
+ public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
+ if (room == null) {
+ return;
+ }
+
+ boolean wiredToggle = objects != null
+ && objects.length >= 2
+ && objects[1] instanceof WiredEffectType;
+
+ if (!wiredToggle) {
+ if (client == null || !this.canToggle(client.getHabbo(), room)) {
+ return;
+ }
+ }
+
+ this.values.put("state", (RoomAreaHideSupport.getState(this) == 1) ? "0" : "1");
+ this.normalizeValues();
+ this.needsUpdate(true);
+ Emulator.getThreading().run(this);
+ room.updateItem(this);
+ }
+
+ @Override
+ public void onCustomValuesSaved(Room room, GameClient client, THashMap oldValues) {
+ this.normalizeValues();
+ }
+
+ public boolean canToggle(Habbo habbo, Room room) {
+ if (habbo == null || room == null) {
+ return false;
+ }
+
+ if (room.hasRights(habbo)) {
+ return true;
+ }
+
+ if (!habbo.getHabboStats().isRentingSpace()) {
+ return false;
+ }
+
+ HabboItem rentedItem = room.getHabboItem(habbo.getHabboStats().rentedItemId);
+
+ return room.getLayout() != null
+ && rentedItem != null
+ && RoomLayout.squareInSquare(
+ RoomLayout.getRectangle(
+ rentedItem.getX(),
+ rentedItem.getY(),
+ rentedItem.getBaseItem().getWidth(),
+ rentedItem.getBaseItem().getLength(),
+ rentedItem.getRotation()
+ ),
+ RoomLayout.getRectangle(
+ this.getX(),
+ this.getY(),
+ this.getBaseItem().getWidth(),
+ this.getBaseItem().getLength(),
+ this.getRotation()
+ )
+ );
+ }
+
+ private void normalizeValues() {
+ this.values.put("state", booleanFlag(this.values.get("state")));
+ this.values.put("rootX", Integer.toString(nonNegative(this.values.get("rootX"))));
+ this.values.put("rootY", Integer.toString(nonNegative(this.values.get("rootY"))));
+ this.values.put("width", Integer.toString(nonNegative(this.values.get("width"))));
+ this.values.put("length", Integer.toString(nonNegative(this.values.get("length"))));
+ this.values.put("invisibility", booleanFlag(this.values.get("invisibility")));
+ this.values.put("wallItems", booleanFlag(this.values.get("wallItems")));
+ this.values.put("invert", booleanFlag(this.values.get("invert")));
+ }
+
+ private static int nonNegative(String value) {
+ try {
+ return Math.max(0, Integer.parseInt(value));
+ } catch (Exception ignored) {
+ return 0;
+ }
+ }
+
+ private static String booleanFlag(String value) {
+ return ("1".equals(value) || "true".equalsIgnoreCase(value)) ? "1" : "0";
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionConfInvisControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionConfInvisControl.java
new file mode 100644
index 00000000..fe132642
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionConfInvisControl.java
@@ -0,0 +1,16 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.habbohotel.items.Item;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionConfInvisControl extends InteractionRemoteSwitchControl {
+ public InteractionConfInvisControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionConfInvisControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionHanditemBlockControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionHanditemBlockControl.java
new file mode 100644
index 00000000..59362e23
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionHanditemBlockControl.java
@@ -0,0 +1,16 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.habbohotel.items.Item;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionHanditemBlockControl extends InteractionRemoteSwitchControl {
+ public InteractionHanditemBlockControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionHanditemBlockControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionQueueSpeedControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionQueueSpeedControl.java
new file mode 100644
index 00000000..6707af8d
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionQueueSpeedControl.java
@@ -0,0 +1,186 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.rooms.Room;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionQueueSpeedControl extends InteractionRemoteSwitchControl {
+ private static final int[] MODE_STATES = new int[]{0, 3, 6, 9};
+ private static final int MODE_FRAME_COUNT = 3;
+ private static final int BASE_FRAME_DURATION_MS = 500;
+
+ private transient volatile int animationRevision = 0;
+ private transient volatile int animationRoomId = 0;
+ private transient volatile int animationModeState = Integer.MIN_VALUE;
+
+ public InteractionQueueSpeedControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionQueueSpeedControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ public static int toModeState(String extradata) {
+ int state = 0;
+
+ try {
+ state = Integer.parseInt(extradata);
+ } catch (NumberFormatException ignored) {
+ }
+
+ if (state >= 9) {
+ return 9;
+ }
+
+ if (state >= 6) {
+ return 6;
+ }
+
+ if (state >= 3) {
+ return 3;
+ }
+
+ return 0;
+ }
+
+ public static int toRollerSpeed(String extradata) {
+ int modeState = toModeState(extradata);
+
+ if (modeState >= 9) {
+ return 3;
+ }
+
+ if (modeState >= 6) {
+ return 2;
+ }
+
+ if (modeState >= 3) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ public static int toRollerIntervalMs(String extradata) {
+ return BASE_FRAME_DURATION_MS * (toRollerSpeed(extradata) + 1);
+ }
+
+ @Override
+ public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
+ if (room == null) {
+ return;
+ }
+
+ boolean wiredToggle = objects != null
+ && objects.length >= 2
+ && objects[1] instanceof com.eu.habbo.habbohotel.wired.WiredEffectType;
+
+ if (!wiredToggle) {
+ if (client == null) {
+ return;
+ }
+
+ if (!this.canToggle(client.getHabbo(), room)) {
+ super.onClick(client, room, new Object[]{"QUEUE_SPEED_USE"});
+ return;
+ }
+ }
+
+ int nextModeState = getNextModeState(this.getExtradata());
+ applyModeState(room, nextModeState, true);
+
+ if (client != null) {
+ super.onClick(client, room, new Object[]{"TOGGLE_OVERRIDE"});
+ }
+ }
+
+ @Override
+ public void onPlace(Room room) {
+ super.onPlace(room);
+ this.ensureAnimationLoop(room);
+ }
+
+ @Override
+ public void onPickUp(Room room) {
+ this.animationRevision++;
+ this.animationRoomId = 0;
+ this.animationModeState = Integer.MIN_VALUE;
+ super.onPickUp(room);
+ }
+
+ public void ensureAnimationLoop(Room room) {
+ if (room == null || !room.isLoaded() || this.getRoomId() != room.getId()) {
+ return;
+ }
+
+ int modeState = toModeState(this.getExtradata());
+
+ if (this.animationRoomId == room.getId() && this.animationModeState == modeState) {
+ return;
+ }
+
+ applyModeState(room, modeState, false);
+ }
+
+ private void applyModeState(Room room, int modeState, boolean persistSelection) {
+ if (room == null) {
+ return;
+ }
+
+ this.animationRevision++;
+ this.animationRoomId = room.getId();
+ this.animationModeState = modeState;
+
+ this.setExtradata(Integer.toString(modeState));
+ if (persistSelection) {
+ this.needsUpdate(true);
+ }
+ room.updateItemState(this);
+
+ int revision = this.animationRevision;
+ int nextFrame = modeState + 1;
+ long delay = toRollerIntervalMs(Integer.toString(modeState));
+
+ Emulator.getThreading().run(() -> this.animateNextFrame(room, modeState, nextFrame, revision), delay);
+ }
+
+ private void animateNextFrame(Room room, int modeState, int nextFrame, int revision) {
+ if (room == null || !room.isLoaded() || this.getRoomId() != room.getId()) {
+ return;
+ }
+
+ if (revision != this.animationRevision || modeState != this.animationModeState) {
+ return;
+ }
+
+ int maxFrame = modeState + (MODE_FRAME_COUNT - 1);
+ int frame = (nextFrame > maxFrame) ? modeState : nextFrame;
+
+ this.setExtradata(Integer.toString(frame));
+ room.updateItemState(this);
+
+ int followingFrame = (frame >= maxFrame) ? modeState : (frame + 1);
+ long delay = toRollerIntervalMs(Integer.toString(modeState));
+
+ Emulator.getThreading().run(() -> this.animateNextFrame(room, modeState, followingFrame, revision), delay);
+ }
+
+ private static int getNextModeState(String extradata) {
+ int currentModeState = toModeState(extradata);
+
+ for (int index = 0; index < MODE_STATES.length; index++) {
+ if (MODE_STATES[index] != currentModeState) {
+ continue;
+ }
+
+ return MODE_STATES[(index + 1) % MODE_STATES.length];
+ }
+
+ return MODE_STATES[0];
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionRemoteSwitchControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionRemoteSwitchControl.java
new file mode 100644
index 00000000..aeb7f8f0
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionRemoteSwitchControl.java
@@ -0,0 +1,21 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.habbohotel.items.Item;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionRemoteSwitchControl extends InteractionDefault {
+ public InteractionRemoteSwitchControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionRemoteSwitchControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean isUsable() {
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionStackWalkHelper.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionStackWalkHelper.java
new file mode 100644
index 00000000..d6102f98
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionStackWalkHelper.java
@@ -0,0 +1,48 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.users.HabboItem;
+import com.eu.habbo.messages.ServerMessage;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionStackWalkHelper extends HabboItem {
+ public InteractionStackWalkHelper(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionStackWalkHelper(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
+ return false;
+ }
+
+ @Override
+ public boolean isWalkable() {
+ return false;
+ }
+
+ @Override
+ public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
+
+ }
+
+ @Override
+ public void serializeExtradata(ServerMessage serverMessage) {
+ serverMessage.appendInt((this.isLimited() ? 256 : 0));
+ serverMessage.appendString(this.getExtradata());
+
+ super.serializeExtradata(serverMessage);
+ }
+
+ @Override
+ public boolean isUsable() {
+ return true;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredCondition.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredCondition.java
index 97018e8b..8af6b046 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredCondition.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredCondition.java
@@ -43,7 +43,7 @@ public abstract class InteractionWiredCondition extends InteractionWired impleme
@Override
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
if (client != null) {
- if (room.hasRights(client.getHabbo())) {
+ if (room.canInspectWired(client.getHabbo())) {
client.sendResponse(new WiredConditionDataComposer(this, room));
this.activateBox(room);
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredDisableControl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredDisableControl.java
new file mode 100644
index 00000000..2c69ff86
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredDisableControl.java
@@ -0,0 +1,16 @@
+package com.eu.habbo.habbohotel.items.interactions;
+
+import com.eu.habbo.habbohotel.items.Item;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class InteractionWiredDisableControl extends InteractionRemoteSwitchControl {
+ public InteractionWiredDisableControl(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public InteractionWiredDisableControl(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+}
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..a71e2820 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;
/**
@@ -78,7 +80,7 @@ public abstract class InteractionWiredEffect extends InteractionWired implements
@Override
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
if (client != null) {
- if (room.hasRights(client.getHabbo())) {
+ if (room.canInspectWired(client.getHabbo())) {
client.sendResponse(new WiredEffectDataComposer(this, room));
this.activateBox(room);
}
@@ -198,4 +200,66 @@ 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) {
+ return this.getSelectableFloorItems(room, null);
+ }
+
+ protected LinkedHashSet getSelectableFloorItems(Room room, WiredContext ctx) {
+ LinkedHashSet result = new LinkedHashSet<>();
+ if (room == null) {
+ return result;
+ }
+
+ boolean includeWiredItems = this.includeWiredTargets(ctx);
+
+ room.getFloorItems().forEach(item -> {
+ if (item != null && (includeWiredItems || !(item instanceof InteractionWired))) {
+ result.add(item);
+ }
+ });
+
+ return result;
+ }
+
+ protected boolean includeWiredTargets(WiredContext ctx) {
+ return ctx != null && ctx.includeWiredSelectorItems();
+ }
+
+ 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..18ba973f 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;
@@ -20,7 +23,10 @@ public abstract class InteractionWiredExtra extends InteractionWired {
@Override
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
if (client != null) {
- if (room.hasRights(client.getHabbo())) {
+ if (room.canInspectWired(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/InteractionWiredHighscore.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredHighscore.java
index c02b251a..e7a79606 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredHighscore.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionWiredHighscore.java
@@ -90,6 +90,7 @@ public class InteractionWiredHighscore extends HabboItem {
try {
int state = Integer.parseInt(this.getExtradata());
this.setExtradata(Math.abs(state - 1) + "");
+ this.needsUpdate(true);
room.updateItem(this);
} catch (Exception e) {
LOGGER.error("Caught exception", e);
@@ -150,4 +151,4 @@ public class InteractionWiredHighscore extends HabboItem {
public void reloadData() {
this.data = Emulator.getGameEnvironment().getItemManager().getHighscoreManager().getHighscoreRowsForItem(this.getId(), this.clearType, this.scoreType);
}
-}
+}
\ No newline at end of file
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..24c67767 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
@@ -45,7 +45,7 @@ public abstract class InteractionWiredTrigger extends InteractionWired implement
@Override
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
if (client != null) {
- if (room.hasRights(client.getHabbo())) {
+ if (room.canInspectWired(client.getHabbo())) {
client.sendResponse(new WiredTriggerDataComposer(this, room));
this.activateBox(room);
}
@@ -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/InteractionYoutubeTV.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionYoutubeTV.java
index 9ed58fe0..2d1a0573 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionYoutubeTV.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionYoutubeTV.java
@@ -56,7 +56,7 @@ public class InteractionYoutubeTV extends HabboItem {
if (this.currentVideo == null) {
serverMessage.appendString("");
} else {
- serverMessage.appendString(Emulator.getConfig().getValue("imager.url.youtube").replace("%video%", this.currentVideo.getId()));
+ serverMessage.appendString("https://img.youtube.com/vi/" + this.currentVideo.getId() + "/hqdefault.jpg");
}
super.serializeExtradata(serverMessage);
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 extends Game> 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..a9670b2e
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionActorDir.java
@@ -0,0 +1,193 @@
+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) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : 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/WiredConditionFurniHaveFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveFurni.java
index 1e96dfc3..01eae2ad 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveFurni.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionFurniHaveFurni.java
@@ -177,6 +177,10 @@ public class WiredConditionFurniHaveFurni extends InteractionWiredCondition {
int count = settings.getFurniIds().length;
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
+
this.items.clear();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
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..4fb60898 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,12 @@ 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);
+
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
this.items.clear();
@@ -179,6 +186,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 +219,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..02d2a0c9 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,55 @@ 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;
+ }
+
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
+
+ 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 +282,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..441a5989 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,63 @@ 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) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : 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 1a064793..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,15 +121,58 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
public void onPickUp() {
this.handItem = 0;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ }
+
+ protected int getHandItem() {
+ return this.handItem;
+ }
+
+ protected int getUserSource() {
+ 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/WiredConditionHasAltitude.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java
new file mode 100644
index 00000000..43a71b8f
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasAltitude.java
@@ -0,0 +1,289 @@
+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.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.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class WiredConditionHasAltitude 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;
+
+ public static final WiredConditionType type = WiredConditionType.HAS_ALTITUDE;
+
+ private final THashSet items;
+ private int comparison = COMPARISON_EQUAL;
+ private double altitude = 0.0D;
+ private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int quantifier = QUANTIFIER_ALL;
+
+ public WiredConditionHasAltitude(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ this.items = new THashSet<>();
+ }
+
+ public WiredConditionHasAltitude(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;
+ }
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ return targets.stream().anyMatch(this::matchesAltitude);
+ }
+
+ return targets.stream().allMatch(this::matchesAltitude);
+ }
+
+ @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.formatAltitude(this.altitude),
+ 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.altitude = 0.0D;
+ this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+
+ 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.furniSource = this.normalizeFurniSource(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 != null) {
+ this.items.add(item);
+ }
+ }
+ }
+
+ @Override
+ public void onPickUp() {
+ this.items.clear();
+ this.comparison = COMPARISON_EQUAL;
+ this.altitude = 0.0D;
+ 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(this.formatAltitude(this.altitude));
+ message.appendInt(3);
+ message.appendInt(this.comparison);
+ 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.furniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL;
+ this.altitude = this.parseAltitudeOrDefault(settings.getStringParam());
+
+ int count = settings.getFurniIds().length;
+ if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
+ return false;
+ }
+
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
+
+ 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);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ 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 void refresh(Room room) {
+ THashSet remove = new THashSet<>();
+
+ for (HabboItem item : this.items) {
+ if (room.getHabboItem(item.getId()) == null) {
+ remove.add(item);
+ }
+ }
+
+ for (HabboItem item : remove) {
+ this.items.remove(item);
+ }
+ }
+
+ private int normalizeComparison(int value) {
+ if (value < COMPARISON_LESS || value > COMPARISON_GREATER) {
+ return COMPARISON_EQUAL;
+ }
+
+ return value;
+ }
+
+ private int normalizeQuantifier(int value) {
+ return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
+ }
+
+ 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();
+ }
+
+ 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;
+ int furniSource;
+ int quantifier;
+ List itemIds;
+
+ public JsonData(int comparison, String altitude, int furniSource, int quantifier, List itemIds) {
+ this.comparison = comparison;
+ this.altitude = altitude;
+ this.furniSource = furniSource;
+ this.quantifier = quantifier;
+ this.itemIds = itemIds;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasVariable.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasVariable.java
new file mode 100644
index 00000000..65349024
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionHasVariable.java
@@ -0,0 +1,506 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.bots.Bot;
+import com.eu.habbo.habbohotel.items.FurnitureType;
+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.pets.Pet;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomRightLevels;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
+import com.eu.habbo.habbohotel.users.DanceType;
+import com.eu.habbo.habbohotel.users.Habbo;
+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.WiredContextVariableSupport;
+import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
+import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class WiredConditionHasVariable extends InteractionWiredCondition {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WiredConditionHasVariable.class);
+
+ protected static final int TARGET_USER = 0;
+ protected static final int TARGET_FURNI = 1;
+ protected static final int TARGET_CONTEXT = 2;
+ protected static final int TARGET_ROOM = 3;
+ protected static final int QUANTIFIER_ALL = 0;
+ protected static final int QUANTIFIER_ANY = 1;
+ private static final String CUSTOM_TOKEN_PREFIX = "custom:";
+ private static final String INTERNAL_TOKEN_PREFIX = "internal:";
+
+ public static final WiredConditionType type = WiredConditionType.HAS_VAR;
+
+ protected final THashSet selectedItems = new THashSet<>();
+ protected int targetType = TARGET_USER;
+ protected int userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ protected int quantifier = QUANTIFIER_ALL;
+ protected String variableToken = "";
+ protected int variableItemId = 0;
+
+ public WiredConditionHasVariable(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionHasVariable(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) {
+ this.refresh();
+
+ List serializedItems = new ArrayList<>();
+ if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
+ serializedItems.addAll(this.selectedItems);
+ }
+
+ message.appendBoolean(false);
+ message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
+ message.appendInt(serializedItems.size());
+
+ for (HabboItem item : serializedItems) {
+ message.appendInt(item.getId());
+ }
+
+ message.appendInt(this.getBaseItem().getSpriteId());
+ message.appendInt(this.getId());
+ message.appendString(this.variableToken == null ? "" : this.variableToken);
+ message.appendInt(4);
+ message.appendInt(this.targetType);
+ message.appendInt(this.userSource);
+ 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();
+ Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
+
+ this.targetType = (params.length > 0) ? normalizeTargetType(params[0]) : TARGET_USER;
+ this.userSource = (params.length > 1) ? normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
+ this.furniSource = (params.length > 2) ? normalizeFurniSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = (params.length > 3) ? normalizeQuantifier(params[3]) : QUANTIFIER_ALL;
+ this.setVariableToken(normalizeVariableToken(settings.getStringParam()));
+
+ if (this.variableToken.isEmpty()) {
+ return false;
+ }
+
+ this.selectedItems.clear();
+
+ if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
+ int[] furniIds = settings.getFurniIds();
+ if (furniIds.length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
+ return false;
+ }
+
+ for (int furniId : furniIds) {
+ HabboItem item = room.getHabboItem(furniId);
+
+ if (item != null) {
+ this.selectedItems.add(item);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ return this.evaluateWithNegation(ctx, false);
+ }
+
+ protected boolean evaluateWithNegation(WiredContext ctx, boolean negative) {
+ Room room = ctx.room();
+
+ if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
+ return false;
+ }
+
+ return switch (this.targetType) {
+ case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room, negative);
+ case TARGET_CONTEXT -> {
+ boolean contextMatch = this.matchesContext(ctx, room);
+ yield negative ? !contextMatch : contextMatch;
+ }
+ case TARGET_ROOM -> {
+ boolean roomMatch = this.matchesRoom(room);
+ yield negative ? !roomMatch : roomMatch;
+ }
+ default -> this.evaluateUserTargets(ctx, room, negative);
+ };
+ }
+
+ private boolean evaluateUserTargets(WiredContext ctx, Room room, boolean negative) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
+ if (targets.isEmpty()) return false;
+
+ boolean match = (this.quantifier == QUANTIFIER_ANY)
+ ? this.matchesAnyUser(room, targets)
+ : this.matchesAllUsers(room, targets);
+
+ return negative ? !match : match;
+ }
+
+ private boolean evaluateFurniTargets(WiredContext ctx, Room room, boolean negative) {
+ this.refresh();
+
+ List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
+ if (targets.isEmpty()) return false;
+
+ boolean match = (this.quantifier == QUANTIFIER_ANY)
+ ? this.matchesAnyFurni(room, targets)
+ : this.matchesAllFurni(room, targets);
+
+ return negative ? !match : match;
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ this.refresh();
+
+ List itemIds = new ArrayList<>();
+ for (HabboItem item : this.selectedItems) {
+ if (item != null) itemIds.add(item.getId());
+ }
+
+ return WiredManager.getGson().toJson(new JsonData(
+ itemIds,
+ this.targetType,
+ this.variableToken,
+ this.variableItemId,
+ this.userSource,
+ this.furniSource,
+ 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;
+
+ try {
+ if (wiredData.startsWith("{")) {
+ JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
+
+ if (data == null) return;
+
+ this.targetType = normalizeTargetType(data.targetType);
+ this.userSource = normalizeUserSource(data.userSource);
+ this.furniSource = normalizeFurniSource(data.furniSource);
+ this.quantifier = normalizeQuantifier(data.quantifier);
+ this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
+
+ if (room != null && data.itemIds != null) {
+ for (Integer itemId : data.itemIds) {
+ if (itemId == null || itemId <= 0) continue;
+
+ HabboItem item = room.getHabboItem(itemId);
+ if (item != null) this.selectedItems.add(item);
+ }
+ }
+
+ return;
+ }
+
+ this.setVariableToken(normalizeVariableToken(wiredData));
+ } catch (Exception e) {
+ LOGGER.error("Failed to load wired variable condition data for item {}", this.getId(), e);
+ this.onPickUp();
+ }
+ }
+
+ @Override
+ public void onPickUp() {
+ this.targetType = TARGET_USER;
+ this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ this.selectedItems.clear();
+ this.setVariableToken("");
+ }
+
+ protected boolean matchesAnyUser(Room room, List targets) {
+ for (RoomUnit roomUnit : targets) {
+ if (this.matchesUser(room, roomUnit)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean matchesAllUsers(Room room, List targets) {
+ for (RoomUnit roomUnit : targets) {
+ if (!this.matchesUser(room, roomUnit)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected boolean matchesAnyFurni(Room room, List targets) {
+ for (HabboItem item : targets) {
+ if (this.matchesFurni(room, item)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean matchesAllFurni(Room room, List targets) {
+ for (HabboItem item : targets) {
+ if (!this.matchesFurni(room, item)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected boolean matchesUser(Room room, RoomUnit roomUnit) {
+ if (room == null || roomUnit == null) {
+ return false;
+ }
+
+ if (isCustomVariableToken(this.variableToken)) {
+ Habbo habbo = room.getHabbo(roomUnit);
+
+ return habbo != null && room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId);
+ }
+
+ if (isInternalVariableToken(this.variableToken)) {
+ return this.hasUserInternalVariable(room, roomUnit, getInternalVariableKey(this.variableToken));
+ }
+
+ return false;
+ }
+
+ protected boolean matchesFurni(Room room, HabboItem item) {
+ if (room == null || item == null) {
+ return false;
+ }
+
+ if (isCustomVariableToken(this.variableToken)) {
+ return room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId);
+ }
+
+ if (isInternalVariableToken(this.variableToken)) {
+ return this.hasFurniInternalVariable(item, getInternalVariableKey(this.variableToken));
+ }
+
+ return false;
+ }
+
+ protected boolean matchesContext(WiredContext ctx, Room room) {
+ if (ctx == null || room == null) {
+ return false;
+ }
+
+ if (isCustomVariableToken(this.variableToken)) {
+ return WiredContextVariableSupport.hasVariable(ctx, this.variableItemId);
+ }
+
+ if (isInternalVariableToken(this.variableToken)) {
+ return WiredInternalVariableSupport.readContextValue(ctx, getInternalVariableKey(this.variableToken)) != null;
+ }
+
+ return false;
+ }
+
+ protected boolean matchesRoom(Room room) {
+ if (room == null) {
+ return false;
+ }
+
+ if (isCustomVariableToken(this.variableToken)) {
+ return room.getRoomVariableManager().hasVariable(this.variableItemId);
+ }
+
+ if (isInternalVariableToken(this.variableToken)) {
+ return this.hasRoomInternalVariable(getInternalVariableKey(this.variableToken));
+ }
+
+ return false;
+ }
+
+ protected boolean hasUserInternalVariable(Room room, RoomUnit roomUnit, String key) {
+ return WiredInternalVariableSupport.hasUserValue(room, roomUnit, key);
+ }
+
+ protected boolean hasFurniInternalVariable(HabboItem item, String key) {
+ return WiredInternalVariableSupport.hasFurniValue(item, key);
+ }
+
+ protected boolean hasRoomInternalVariable(String key) {
+ return WiredInternalVariableSupport.hasRoomValue(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()), key);
+ }
+
+ protected void refresh() {
+ THashSet staleItems = new THashSet<>();
+ Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
+
+ if (room == null) {
+ staleItems.addAll(this.selectedItems);
+ } else {
+ for (HabboItem item : this.selectedItems) {
+ if (item == null || item.getRoomId() != room.getId()) {
+ staleItems.add(item);
+ }
+ }
+ }
+
+ this.selectedItems.removeAll(staleItems);
+ }
+
+ protected void setVariableToken(String token) {
+ this.variableToken = normalizeVariableToken(token);
+ this.variableItemId = getCustomItemId(this.variableToken);
+ }
+
+ protected boolean hasRoomEntryMethod(Habbo habbo) {
+ if (habbo == null) return false;
+
+ String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
+
+ return roomEntryMethod != null && !roomEntryMethod.trim().isEmpty() && !"unknown".equalsIgnoreCase(roomEntryMethod);
+ }
+
+ protected TeamEffectData getTeamEffectData(int effectValue) {
+ if (effectValue <= 0) return null;
+
+ if (effectValue >= 223 && effectValue <= 226) return new TeamEffectData(effectValue - 222, 0);
+ if (effectValue >= 33 && effectValue <= 36) return new TeamEffectData(effectValue - 32, 1);
+ if (effectValue >= 40 && effectValue <= 43) return new TeamEffectData(effectValue - 39, 2);
+
+ return null;
+ }
+
+ protected static int normalizeTargetType(int value) {
+ return switch (value) {
+ case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
+ default -> TARGET_USER;
+ };
+ }
+
+ protected static int normalizeQuantifier(int value) {
+ return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
+ }
+
+ protected static int normalizeUserSource(int value) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+
+ protected static int normalizeFurniSource(int value) {
+ return switch (value) {
+ case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
+ default -> WiredSourceUtil.SOURCE_TRIGGER;
+ };
+ }
+
+ protected static boolean isCustomVariableToken(String token) {
+ return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
+ }
+
+ protected static boolean isInternalVariableToken(String token) {
+ return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
+ }
+
+ protected static int getCustomItemId(String token) {
+ if (!isCustomVariableToken(token)) return 0;
+
+ try {
+ return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ protected static String getInternalVariableKey(String token) {
+ return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
+ }
+
+ protected static String normalizeVariableToken(String token) {
+ if (token == null) return "";
+
+ String normalized = token.trim();
+ if (normalized.isEmpty()) return "";
+ if (isCustomVariableToken(normalized)) return normalized;
+ if (isInternalVariableToken(normalized)) return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
+
+ try {
+ int parsed = Integer.parseInt(normalized);
+ return (parsed > 0) ? (CUSTOM_TOKEN_PREFIX + parsed) : "";
+ } catch (NumberFormatException e) {
+ return "";
+ }
+ }
+
+ protected static class JsonData {
+ List itemIds;
+ int targetType;
+ String variableToken;
+ int variableItemId;
+ int userSource;
+ int furniSource;
+ int quantifier;
+
+ public JsonData(List itemIds, int targetType, String variableToken, int variableItemId, int userSource, int furniSource, int quantifier) {
+ this.itemIds = itemIds;
+ this.targetType = targetType;
+ this.variableToken = variableToken;
+ this.variableItemId = variableItemId;
+ this.userSource = userSource;
+ this.furniSource = furniSource;
+ this.quantifier = quantifier;
+ }
+ }
+
+ protected static class TeamEffectData {
+ final int colorId;
+ final int typeId;
+
+ protected TeamEffectData(int colorId, int typeId) {
+ this.colorId = colorId;
+ this.typeId = typeId;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java
new file mode 100644
index 00000000..3574480b
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchDate.java
@@ -0,0 +1,253 @@
+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.messages.ServerMessage;
+import com.eu.habbo.util.HotelDateTimeUtil;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalDate;
+
+public class WiredConditionMatchDate extends InteractionWiredCondition {
+ private static final int MODE_SKIP = 0;
+ private static final int MODE_EXACT = 1;
+ private static final int MODE_RANGE = 2;
+ private static final int ALL_WEEKDAYS_MASK = createMask(1, 7);
+ private static final int ALL_MONTHS_MASK = createMask(1, 12);
+
+ public static final WiredConditionType type = WiredConditionType.MATCH_DATE;
+
+ private int weekdayMask = ALL_WEEKDAYS_MASK;
+ private int dayMode = MODE_SKIP;
+ private int dayFrom = 1;
+ private int dayTo = 31;
+ private int monthMask = ALL_MONTHS_MASK;
+ private int yearMode = MODE_SKIP;
+ private int yearFrom = HotelDateTimeUtil.localDateNow().getYear();
+ private int yearTo = HotelDateTimeUtil.localDateNow().getYear();
+
+ public WiredConditionMatchDate(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionMatchDate(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(8);
+ message.appendInt(this.weekdayMask);
+ message.appendInt(this.dayMode);
+ message.appendInt(this.dayFrom);
+ message.appendInt(this.dayTo);
+ message.appendInt(this.monthMask);
+ message.appendInt(this.yearMode);
+ message.appendInt(this.yearFrom);
+ message.appendInt(this.yearTo);
+ 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.weekdayMask = (params.length > 0) ? this.normalizeWeekdayMask(params[0]) : ALL_WEEKDAYS_MASK;
+ this.dayMode = (params.length > 1) ? this.normalizeMode(params[1]) : MODE_SKIP;
+ this.dayFrom = (params.length > 2) ? this.normalizeDay(params[2]) : 1;
+ this.dayTo = (params.length > 3) ? this.normalizeDay(params[3]) : this.dayFrom;
+ this.monthMask = (params.length > 4) ? this.normalizeMonthMask(params[4]) : ALL_MONTHS_MASK;
+ this.yearMode = (params.length > 5) ? this.normalizeMode(params[5]) : MODE_SKIP;
+ this.yearFrom = (params.length > 6) ? this.normalizeYear(params[6]) : HotelDateTimeUtil.localDateNow().getYear();
+ this.yearTo = (params.length > 7) ? this.normalizeYear(params[7]) : this.yearFrom;
+
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ LocalDate now = HotelDateTimeUtil.localDateNow();
+
+ return this.matchesMask(now.getDayOfWeek().getValue(), this.weekdayMask)
+ && this.matchesMask(now.getMonthValue(), this.monthMask)
+ && this.matchesDatePart(now.getDayOfMonth(), this.dayMode, this.dayFrom, this.dayTo)
+ && this.matchesDatePart(now.getYear(), this.yearMode, this.yearFrom, this.yearTo);
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.weekdayMask,
+ this.dayMode,
+ this.dayFrom,
+ this.dayTo,
+ this.monthMask,
+ this.yearMode,
+ this.yearFrom,
+ this.yearTo
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.reset();
+
+ 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.weekdayMask = this.normalizeWeekdayMask(data.weekdayMask);
+ this.dayMode = this.normalizeMode(data.dayMode);
+ this.dayFrom = this.normalizeDay(data.dayFrom);
+ this.dayTo = this.normalizeDay(data.dayTo);
+ this.monthMask = this.normalizeMonthMask(data.monthMask);
+ this.yearMode = this.normalizeMode(data.yearMode);
+ this.yearFrom = this.normalizeYear(data.yearFrom);
+ this.yearTo = this.normalizeYear(data.yearTo);
+ return;
+ }
+
+ String[] data = wiredData.split("\t");
+ if (data.length != 8) {
+ return;
+ }
+
+ try {
+ this.weekdayMask = this.normalizeWeekdayMask(Integer.parseInt(data[0]));
+ this.dayMode = this.normalizeMode(Integer.parseInt(data[1]));
+ this.dayFrom = this.normalizeDay(Integer.parseInt(data[2]));
+ this.dayTo = this.normalizeDay(Integer.parseInt(data[3]));
+ this.monthMask = this.normalizeMonthMask(Integer.parseInt(data[4]));
+ this.yearMode = this.normalizeMode(Integer.parseInt(data[5]));
+ this.yearFrom = this.normalizeYear(Integer.parseInt(data[6]));
+ this.yearTo = this.normalizeYear(Integer.parseInt(data[7]));
+ } catch (NumberFormatException ignored) {
+ this.reset();
+ }
+ }
+
+ @Override
+ public void onPickUp() {
+ this.reset();
+ }
+
+ private void reset() {
+ int currentYear = HotelDateTimeUtil.localDateNow().getYear();
+
+ this.weekdayMask = ALL_WEEKDAYS_MASK;
+ this.dayMode = MODE_SKIP;
+ this.dayFrom = 1;
+ this.dayTo = 31;
+ this.monthMask = ALL_MONTHS_MASK;
+ this.yearMode = MODE_SKIP;
+ this.yearFrom = currentYear;
+ this.yearTo = currentYear;
+ }
+
+ private boolean matchesMask(int value, int mask) {
+ return (mask & (1 << value)) != 0;
+ }
+
+ private boolean matchesDatePart(int currentValue, int mode, int fromValue, int toValue) {
+ switch (mode) {
+ case MODE_EXACT:
+ return currentValue == fromValue;
+ case MODE_RANGE:
+ return currentValue >= Math.min(fromValue, toValue) && currentValue <= Math.max(fromValue, toValue);
+ default:
+ return true;
+ }
+ }
+
+ private int normalizeMode(int value) {
+ if (value < MODE_SKIP || value > MODE_RANGE) {
+ return MODE_SKIP;
+ }
+
+ return value;
+ }
+
+ private int normalizeDay(int value) {
+ return Math.max(1, Math.min(31, value));
+ }
+
+ private int normalizeYear(int value) {
+ return Math.max(1, Math.min(9999, value));
+ }
+
+ private int normalizeWeekdayMask(int value) {
+ int normalized = value & ALL_WEEKDAYS_MASK;
+ return (normalized == 0) ? ALL_WEEKDAYS_MASK : normalized;
+ }
+
+ private int normalizeMonthMask(int value) {
+ int normalized = value & ALL_MONTHS_MASK;
+ return (normalized == 0) ? ALL_MONTHS_MASK : normalized;
+ }
+
+ private static int createMask(int startValue, int endValue) {
+ int mask = 0;
+
+ for (int value = startValue; value <= endValue; value++) {
+ mask |= (1 << value);
+ }
+
+ return mask;
+ }
+
+ static class JsonData {
+ int weekdayMask;
+ int dayMode;
+ int dayFrom;
+ int dayTo;
+ int monthMask;
+ int yearMode;
+ int yearFrom;
+ int yearTo;
+
+ public JsonData(int weekdayMask, int dayMode, int dayFrom, int dayTo, int monthMask, int yearMode, int yearFrom, int yearTo) {
+ this.weekdayMask = weekdayMask;
+ this.dayMode = dayMode;
+ this.dayFrom = dayFrom;
+ this.dayTo = dayTo;
+ this.monthMask = monthMask;
+ this.yearMode = yearMode;
+ this.yearFrom = yearFrom;
+ this.yearTo = yearTo;
+ }
+ }
+}
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 bc965875..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
@@ -19,11 +19,13 @@ import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
+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;
@@ -31,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);
@@ -62,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);
@@ -80,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());
@@ -92,14 +100,12 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.settings.clear();
- if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
- for (int i = 0; i < count; i++) {
- int itemId = settings.getFurniIds()[i];
- HabboItem item = room.getHabboItem(itemId);
+ for (int i = 0; i < count; i++) {
+ int itemId = settings.getFurniIds()[i];
+ HabboItem item = room.getHabboItem(itemId);
- if (item != null)
- this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY()));
- }
+ if (item != null)
+ this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ()));
}
return true;
@@ -107,66 +113,119 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
@Override
public boolean evaluate(WiredContext ctx) {
- Room room = ctx.room();
+ this.refresh();
+
if (this.settings.isEmpty())
return true;
- List targets = null;
- Set targetIds = null;
+ 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) {
- targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null);
+ List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null);
if (targets.isEmpty()) return false;
- targetIds = new HashSet<>();
- for (HabboItem item : targets) {
- if (item != null) targetIds.add(item.getId());
- }
- if (targetIds.isEmpty()) return false;
- }
- THashSet toRemove = new THashSet<>();
- Set settingsIds = new HashSet<>();
+ for (HabboItem item : targets) {
+ if (item == null) return false;
+
+ WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item);
+ if (setting == null) {
+ return false;
+ }
+
+ if (!this.matchesSetting(item, setting)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
for (WiredMatchFurniSetting setting : this.settings) {
- if (targetIds != null && !targetIds.contains(setting.item_id)) {
- continue;
- }
HabboItem item = room.getHabboItem(setting.item_id);
-
- if (item != null) {
- settingsIds.add(setting.item_id);
- if (this.state) {
- if (!item.getExtradata().equals(setting.state))
- return false;
- }
-
- if (this.position) {
- if (!(setting.x == item.getX() && setting.y == item.getY()))
- return false;
- }
-
- if (this.direction) {
- if (setting.rotation != item.getRotation())
- return false;
- }
- } else {
- toRemove.add(setting);
- }
- }
-
- if (targetIds != null && !settingsIds.containsAll(targetIds)) {
- return false;
- }
-
- if (!toRemove.isEmpty()) {
- for (WiredMatchFurniSetting setting : toRemove) {
- this.settings.remove(setting);
- }
+ if (item == null) continue;
+ if (!this.matchesSetting(item, setting))
+ return false;
}
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;
+
+ for (WiredMatchFurniSetting setting : this.settings) {
+ HabboItem sourceItem = room.getHabboItem(setting.item_id);
+ if (sourceItem == null) continue;
+ if (sourceItem.getBaseItem().getId() != target.getBaseItem().getId()) continue;
+
+ if (setting.state.equals(target.getExtradata())) {
+ return setting;
+ }
+
+ if (fallback == null) {
+ fallback = setting;
+ }
+ }
+
+ return fallback;
+ }
+
+ private boolean matchesSetting(HabboItem item, WiredMatchFurniSetting setting) {
+ if (this.state && !item.getExtradata().equals(setting.state))
+ return false;
+
+ 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();
+ }
+
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
@@ -179,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
));
}
@@ -193,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(":");
@@ -205,17 +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;
- }
- if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.settings.isEmpty()) {
- this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ this.quantifier = QUANTIFIER_ALL;
}
}
@@ -225,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) {
@@ -267,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/WiredConditionMatchTime.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java
new file mode 100644
index 00000000..982b56ec
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionMatchTime.java
@@ -0,0 +1,237 @@
+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.messages.ServerMessage;
+import com.eu.habbo.util.HotelDateTimeUtil;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalTime;
+
+public class WiredConditionMatchTime extends InteractionWiredCondition {
+ private static final int MODE_SKIP = 0;
+ private static final int MODE_EXACT = 1;
+ private static final int MODE_RANGE = 2;
+
+ public static final WiredConditionType type = WiredConditionType.MATCH_TIME;
+
+ private int hourMode = MODE_SKIP;
+ private int hourFrom = 0;
+ private int hourTo = 0;
+ private int minuteMode = MODE_SKIP;
+ private int minuteFrom = 0;
+ private int minuteTo = 0;
+ private int secondMode = MODE_SKIP;
+ private int secondFrom = 0;
+ private int secondTo = 0;
+
+ public WiredConditionMatchTime(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionMatchTime(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(9);
+ message.appendInt(this.hourMode);
+ message.appendInt(this.hourFrom);
+ message.appendInt(this.hourTo);
+ message.appendInt(this.minuteMode);
+ message.appendInt(this.minuteFrom);
+ message.appendInt(this.minuteTo);
+ message.appendInt(this.secondMode);
+ message.appendInt(this.secondFrom);
+ message.appendInt(this.secondTo);
+ 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.hourMode = (params.length > 0) ? this.normalizeMode(params[0]) : MODE_SKIP;
+ this.hourFrom = (params.length > 1) ? this.normalizeHour(params[1]) : 0;
+ this.hourTo = (params.length > 2) ? this.normalizeHour(params[2]) : this.hourFrom;
+ this.minuteMode = (params.length > 3) ? this.normalizeMode(params[3]) : MODE_SKIP;
+ this.minuteFrom = (params.length > 4) ? this.normalizeMinuteOrSecond(params[4]) : 0;
+ this.minuteTo = (params.length > 5) ? this.normalizeMinuteOrSecond(params[5]) : this.minuteFrom;
+ this.secondMode = (params.length > 6) ? this.normalizeMode(params[6]) : MODE_SKIP;
+ this.secondFrom = (params.length > 7) ? this.normalizeMinuteOrSecond(params[7]) : 0;
+ this.secondTo = (params.length > 8) ? this.normalizeMinuteOrSecond(params[8]) : this.secondFrom;
+
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ LocalTime now = HotelDateTimeUtil.localTimeNow();
+
+ return this.matchesTimePart(now.getHour(), this.hourMode, this.hourFrom, this.hourTo)
+ && this.matchesTimePart(now.getMinute(), this.minuteMode, this.minuteFrom, this.minuteTo)
+ && this.matchesTimePart(now.getSecond(), this.secondMode, this.secondFrom, this.secondTo);
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.hourMode,
+ this.hourFrom,
+ this.hourTo,
+ this.minuteMode,
+ this.minuteFrom,
+ this.minuteTo,
+ this.secondMode,
+ this.secondFrom,
+ this.secondTo
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.reset();
+
+ 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.hourMode = this.normalizeMode(data.hourMode);
+ this.hourFrom = this.normalizeHour(data.hourFrom);
+ this.hourTo = this.normalizeHour(data.hourTo);
+ this.minuteMode = this.normalizeMode(data.minuteMode);
+ this.minuteFrom = this.normalizeMinuteOrSecond(data.minuteFrom);
+ this.minuteTo = this.normalizeMinuteOrSecond(data.minuteTo);
+ this.secondMode = this.normalizeMode(data.secondMode);
+ this.secondFrom = this.normalizeMinuteOrSecond(data.secondFrom);
+ this.secondTo = this.normalizeMinuteOrSecond(data.secondTo);
+ return;
+ }
+
+ String[] data = wiredData.split("\t");
+ if (data.length != 9) {
+ return;
+ }
+
+ try {
+ this.hourMode = this.normalizeMode(Integer.parseInt(data[0]));
+ this.hourFrom = this.normalizeHour(Integer.parseInt(data[1]));
+ this.hourTo = this.normalizeHour(Integer.parseInt(data[2]));
+ this.minuteMode = this.normalizeMode(Integer.parseInt(data[3]));
+ this.minuteFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[4]));
+ this.minuteTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[5]));
+ this.secondMode = this.normalizeMode(Integer.parseInt(data[6]));
+ this.secondFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[7]));
+ this.secondTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[8]));
+ } catch (NumberFormatException ignored) {
+ this.reset();
+ }
+ }
+
+ @Override
+ public void onPickUp() {
+ this.reset();
+ }
+
+ private void reset() {
+ this.hourMode = MODE_SKIP;
+ this.hourFrom = 0;
+ this.hourTo = 0;
+ this.minuteMode = MODE_SKIP;
+ this.minuteFrom = 0;
+ this.minuteTo = 0;
+ this.secondMode = MODE_SKIP;
+ this.secondFrom = 0;
+ this.secondTo = 0;
+ }
+
+ private boolean matchesTimePart(int currentValue, int mode, int fromValue, int toValue) {
+ switch (mode) {
+ case MODE_EXACT:
+ return currentValue == fromValue;
+ case MODE_RANGE:
+ if (fromValue <= toValue) {
+ return currentValue >= fromValue && currentValue <= toValue;
+ }
+
+ return currentValue >= fromValue || currentValue <= toValue;
+ default:
+ return true;
+ }
+ }
+
+ private int normalizeMode(int value) {
+ if (value < MODE_SKIP || value > MODE_RANGE) {
+ return MODE_SKIP;
+ }
+
+ return value;
+ }
+
+ private int normalizeHour(int value) {
+ return Math.max(0, Math.min(23, value));
+ }
+
+ private int normalizeMinuteOrSecond(int value) {
+ return Math.max(0, Math.min(59, value));
+ }
+
+ static class JsonData {
+ int hourMode;
+ int hourFrom;
+ int hourTo;
+ int minuteMode;
+ int minuteFrom;
+ int minuteTo;
+ int secondMode;
+ int secondFrom;
+ int secondTo;
+
+ public JsonData(int hourMode, int hourFrom, int hourTo, int minuteMode, int minuteFrom, int minuteTo, int secondMode, int secondFrom, int secondTo) {
+ this.hourMode = hourMode;
+ this.hourFrom = hourFrom;
+ this.hourTo = hourTo;
+ this.minuteMode = minuteMode;
+ this.minuteFrom = minuteFrom;
+ this.minuteTo = minuteTo;
+ this.secondMode = secondMode;
+ this.secondFrom = secondFrom;
+ this.secondTo = secondTo;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveFurni.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveFurni.java
index c4540b26..41f72077 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveFurni.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotFurniHaveFurni.java
@@ -177,6 +177,10 @@ public class WiredConditionNotFurniHaveFurni extends InteractionWiredCondition {
int count = settings.getFurniIds().length;
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
+
this.items.clear();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
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..a7d85525 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,12 @@ 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);
+
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
this.items.clear();
@@ -178,6 +185,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 +218,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
new file mode 100644
index 00000000..060b6089
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHabboHasHandItem.java
@@ -0,0 +1,40 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.items.Item;
+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.WiredSourceUtil;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+public class WiredConditionNotHabboHasHandItem extends WiredConditionHabboHasHandItem {
+ public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_HAS_HANDITEM;
+
+ public WiredConditionNotHabboHasHandItem(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionNotHabboHasHandItem(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource());
+ if (targets.isEmpty()) return false;
+
+ if (this.getQuantifier() == QUANTIFIER_ANY) {
+ return !this.matchesAnyTarget(targets);
+ }
+
+ return !this.matchesAllTargets(targets);
+ }
+
+ @Override
+ public WiredConditionType getType() {
+ return type;
+ }
+}
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/WiredConditionNotHasVariable.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHasVariable.java
new file mode 100644
index 00000000..c0dcc43f
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotHasVariable.java
@@ -0,0 +1,30 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.wired.WiredConditionType;
+import com.eu.habbo.habbohotel.wired.core.WiredContext;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class WiredConditionNotHasVariable extends WiredConditionHasVariable {
+ public static final WiredConditionType type = WiredConditionType.NOT_HAS_VAR;
+
+ public WiredConditionNotHasVariable(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionNotHasVariable(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 boolean evaluate(WiredContext ctx) {
+ return this.evaluateWithNegation(ctx, true);
+ }
+}
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..06055f4d 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,59 @@ 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) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : 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/WiredConditionNotTriggererMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java
new file mode 100644
index 00000000..3e945329
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotTriggererMatch.java
@@ -0,0 +1,31 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.wired.WiredConditionType;
+import com.eu.habbo.habbohotel.wired.core.WiredContext;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class WiredConditionNotTriggererMatch extends WiredConditionTriggererMatch {
+ public static final WiredConditionType type = WiredConditionType.NOT_TRIGGERER_MATCH;
+
+ public WiredConditionNotTriggererMatch(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionNotTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ MatchResult result = this.evaluateMatch(ctx);
+ return result.valid && !result.matched;
+ }
+
+ @Override
+ public WiredConditionType getType() {
+ return type;
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java
new file mode 100644
index 00000000..d4ff4f38
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionNotUserPerformsAction.java
@@ -0,0 +1,46 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.wired.WiredConditionType;
+import com.eu.habbo.habbohotel.wired.core.WiredContext;
+import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+public class WiredConditionNotUserPerformsAction extends WiredConditionUserPerformsAction {
+ private static final int QUANTIFIER_ANY_NOT_MATCH = 0;
+ private static final int QUANTIFIER_NONE_MATCH = 1;
+
+ public static final WiredConditionType type = WiredConditionType.NOT_USER_PERFORMS_ACTION;
+
+ public WiredConditionNotUserPerformsAction(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionNotUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource());
+ if (targets.isEmpty()) {
+ return false;
+ }
+
+ if (this.getQuantifier() == QUANTIFIER_NONE_MATCH) {
+ return targets.stream().noneMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
+ }
+
+ return targets.stream().anyMatch(roomUnit -> !this.matchesAction(ctx, roomUnit));
+ }
+
+ @Override
+ public WiredConditionType getType() {
+ return type;
+ }
+}
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..590ea9d8
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionSelectionQuantity.java
@@ -0,0 +1,217 @@
+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) {
+ if (group == SOURCE_GROUP_USERS) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+
+ 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/WiredConditionTeamGameBase.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java
new file mode 100644
index 00000000..1b5dd1d9
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamGameBase.java
@@ -0,0 +1,190 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.games.Game;
+import com.eu.habbo.habbohotel.games.GameState;
+import com.eu.habbo.habbohotel.games.GameTeam;
+import com.eu.habbo.habbohotel.games.GameTeamColors;
+import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
+import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.users.Habbo;
+import com.eu.habbo.habbohotel.wired.core.WiredContext;
+import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+abstract class WiredConditionTeamGameBase extends InteractionWiredCondition {
+ protected static final int QUANTIFIER_ALL = 0;
+ protected static final int QUANTIFIER_ANY = 1;
+ protected static final int COMPARISON_LOWER = 0;
+ protected static final int COMPARISON_EQUAL = 1;
+ protected static final int COMPARISON_HIGHER = 2;
+ protected static final int TEAM_TRIGGERER = 0;
+
+ private static final GameTeamColors[] SUPPORTED_TEAM_COLORS = new GameTeamColors[] {
+ GameTeamColors.RED,
+ GameTeamColors.GREEN,
+ GameTeamColors.BLUE,
+ GameTeamColors.YELLOW
+ };
+
+ protected WiredConditionTeamGameBase(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ protected WiredConditionTeamGameBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ protected List resolveUsers(WiredContext ctx, int userSource) {
+ Map deduplicated = new LinkedHashMap<>();
+
+ for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, userSource)) {
+ if (roomUnit != null) {
+ deduplicated.putIfAbsent(roomUnit.getId(), roomUnit);
+ }
+ }
+
+ return new ArrayList<>(deduplicated.values());
+ }
+
+ protected boolean matchesQuantifier(List users, int quantifier, Predicate predicate) {
+ if (users.isEmpty()) {
+ return false;
+ }
+
+ if (quantifier == QUANTIFIER_ANY) {
+ return users.stream().anyMatch(predicate);
+ }
+
+ return users.stream().allMatch(predicate);
+ }
+
+ protected int normalizeQuantifier(int value) {
+ return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
+ }
+
+ protected int normalizeComparison(int value) {
+ switch (value) {
+ case COMPARISON_LOWER:
+ case COMPARISON_HIGHER:
+ return value;
+ default:
+ return COMPARISON_EQUAL;
+ }
+ }
+
+ protected int normalizeUserSource(int value) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+
+ protected int normalizePlacement(int value) {
+ if (value < 1 || value > 4) {
+ return 1;
+ }
+
+ return value;
+ }
+
+ protected int normalizeScore(int value) {
+ return Math.max(0, value);
+ }
+
+ protected int normalizeExplicitTeamType(int value) {
+ GameTeamColors color = GameTeamColors.fromType(value);
+ return (color.type >= GameTeamColors.RED.type && color.type <= GameTeamColors.YELLOW.type)
+ ? color.type
+ : GameTeamColors.RED.type;
+ }
+
+ protected int normalizeRankTeamType(int value) {
+ if (value == TEAM_TRIGGERER) {
+ return TEAM_TRIGGERER;
+ }
+
+ return this.normalizeExplicitTeamType(value);
+ }
+
+ protected GameTeamColors resolveConfiguredTeamColor(int value) {
+ return GameTeamColors.fromType(this.normalizeExplicitTeamType(value));
+ }
+
+ protected boolean compareValue(int actual, int expected, int comparison) {
+ switch (comparison) {
+ case COMPARISON_LOWER:
+ return actual < expected;
+ case COMPARISON_HIGHER:
+ return actual > expected;
+ default:
+ return actual == expected;
+ }
+ }
+
+ protected UserGameContext resolveUserGameContext(Room room, RoomUnit roomUnit) {
+ if (room == null || roomUnit == null) {
+ return null;
+ }
+
+ Habbo habbo = room.getHabbo(roomUnit);
+ if (habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getCurrentGame() == null) {
+ return null;
+ }
+
+ Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
+ if (!this.isSupportedGame(game)) {
+ return null;
+ }
+
+ GameTeam team = game.getTeamForHabbo(habbo);
+ if (team == null) {
+ return null;
+ }
+
+ return new UserGameContext(habbo, game, team);
+ }
+
+ protected int getTeamRank(Game game, GameTeam team) {
+ if (game == null || team == null) {
+ return Integer.MAX_VALUE;
+ }
+
+ int rank = 1;
+ int targetScore = team.getTotalScore();
+
+ for (GameTeamColors teamColor : SUPPORTED_TEAM_COLORS) {
+ GameTeam otherTeam = game.getTeam(teamColor);
+ if (otherTeam != null && otherTeam != team && otherTeam.getTotalScore() > targetScore) {
+ rank++;
+ }
+ }
+
+ return rank;
+ }
+
+ private boolean isSupportedGame(Game game) {
+ return game != null
+ && game.getState() != GameState.IDLE
+ && (game instanceof FreezeGame || game instanceof BattleBanzaiGame);
+ }
+
+ protected static class UserGameContext {
+ protected final Habbo habbo;
+ protected final Game game;
+ protected final GameTeam team;
+
+ protected UserGameContext(Habbo habbo, Game game, GameTeam team) {
+ this.habbo = habbo;
+ this.game = game;
+ this.team = team;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java
new file mode 100644
index 00000000..58272197
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasRank.java
@@ -0,0 +1,175 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.games.GameTeam;
+import com.eu.habbo.habbohotel.games.GameTeamColors;
+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.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 WiredConditionTeamHasRank extends WiredConditionTeamGameBase {
+ public static final WiredConditionType type = WiredConditionType.TEAM_HAS_RANK;
+
+ private int teamType = GameTeamColors.RED.type;
+ private int placement = 1;
+ private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int quantifier = QUANTIFIER_ALL;
+
+ public WiredConditionTeamHasRank(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionTeamHasRank(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ Room room = ctx.room();
+ List users = this.resolveUsers(ctx, this.userSource);
+
+ return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(ctx, room, roomUnit));
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.teamType,
+ this.placement,
+ this.userSource,
+ this.quantifier
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.resetSettings();
+
+ 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.normalizeRankTeamType(data.teamType);
+ this.placement = this.normalizePlacement(data.placement);
+ this.userSource = this.normalizeUserSource(data.userSource);
+ this.quantifier = this.normalizeQuantifier(data.quantifier);
+ }
+
+ @Override
+ public void onPickUp() {
+ this.resetSettings();
+ }
+
+ @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.teamType);
+ message.appendInt(this.placement);
+ 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.resetSettings();
+
+ if (params.length > 0) this.teamType = this.normalizeRankTeamType(params[0]);
+ if (params.length > 1) this.placement = this.normalizePlacement(params[1]);
+ if (params.length > 2) this.userSource = this.normalizeUserSource(params[2]);
+ if (params.length > 3) this.quantifier = this.normalizeQuantifier(params[3]);
+
+ return true;
+ }
+
+ private boolean matchesUser(WiredContext ctx, Room room, RoomUnit roomUnit) {
+ UserGameContext context = this.resolveUserGameContext(room, roomUnit);
+ if (context == null) {
+ return false;
+ }
+
+ GameTeamColors requiredTeam = this.resolveRequiredTeamColor(ctx, room, context.game);
+ if (requiredTeam == GameTeamColors.NONE || context.team.teamColor != requiredTeam) {
+ return false;
+ }
+
+ GameTeam team = context.game.getTeam(requiredTeam);
+ if (team == null) {
+ return false;
+ }
+
+ return this.getTeamRank(context.game, team) == this.placement;
+ }
+
+ private GameTeamColors resolveRequiredTeamColor(WiredContext ctx, Room room, com.eu.habbo.habbohotel.games.Game game) {
+ if (this.teamType == TEAM_TRIGGERER) {
+ RoomUnit actor = ctx.actor().orElse(null);
+ UserGameContext triggererContext = this.resolveUserGameContext(room, actor);
+
+ if (triggererContext == null || triggererContext.game != game) {
+ return GameTeamColors.NONE;
+ }
+
+ return triggererContext.team.teamColor;
+ }
+
+ return this.resolveConfiguredTeamColor(this.teamType);
+ }
+
+ private void resetSettings() {
+ this.teamType = GameTeamColors.RED.type;
+ this.placement = 1;
+ this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ }
+
+ static class JsonData {
+ int teamType;
+ int placement;
+ int userSource;
+ int quantifier;
+
+ public JsonData(int teamType, int placement, int userSource, int quantifier) {
+ this.teamType = teamType;
+ this.placement = placement;
+ this.userSource = userSource;
+ this.quantifier = quantifier;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java
new file mode 100644
index 00000000..e85741a6
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTeamHasScore.java
@@ -0,0 +1,162 @@
+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.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 WiredConditionTeamHasScore extends WiredConditionTeamGameBase {
+ public static final WiredConditionType type = WiredConditionType.TEAM_HAS_SCORE;
+
+ private int teamType = GameTeamColors.RED.type;
+ private int comparison = COMPARISON_EQUAL;
+ private int score = 0;
+ private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int quantifier = QUANTIFIER_ALL;
+
+ public WiredConditionTeamHasScore(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionTeamHasScore(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ Room room = ctx.room();
+ List users = this.resolveUsers(ctx, this.userSource);
+
+ return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(room, roomUnit));
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.teamType,
+ this.comparison,
+ this.score,
+ this.userSource,
+ this.quantifier
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.resetSettings();
+
+ 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.normalizeExplicitTeamType(data.teamType);
+ this.comparison = this.normalizeComparison(data.comparison);
+ this.score = this.normalizeScore(data.score);
+ this.userSource = this.normalizeUserSource(data.userSource);
+ this.quantifier = this.normalizeQuantifier(data.quantifier);
+ }
+
+ @Override
+ public void onPickUp() {
+ this.resetSettings();
+ }
+
+ @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(5);
+ message.appendInt(this.teamType);
+ message.appendInt(this.comparison);
+ message.appendInt(this.score);
+ 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.resetSettings();
+
+ if (params.length > 0) this.teamType = this.normalizeExplicitTeamType(params[0]);
+ if (params.length > 1) this.comparison = this.normalizeComparison(params[1]);
+ if (params.length > 2) this.score = this.normalizeScore(params[2]);
+ if (params.length > 3) this.userSource = this.normalizeUserSource(params[3]);
+ if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]);
+
+ return true;
+ }
+
+ private boolean matchesUser(Room room, RoomUnit roomUnit) {
+ UserGameContext context = this.resolveUserGameContext(room, roomUnit);
+ if (context == null) {
+ return false;
+ }
+
+ GameTeamColors requiredTeam = this.resolveConfiguredTeamColor(this.teamType);
+ if (context.team.teamColor != requiredTeam) {
+ return false;
+ }
+
+ return this.compareValue(context.team.getTotalScore(), this.score, this.comparison);
+ }
+
+ private void resetSettings() {
+ this.teamType = GameTeamColors.RED.type;
+ this.comparison = COMPARISON_EQUAL;
+ this.score = 0;
+ this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ }
+
+ static class JsonData {
+ int teamType;
+ int comparison;
+ int score;
+ int userSource;
+ int quantifier;
+
+ public JsonData(int teamType, int comparison, int score, int userSource, int quantifier) {
+ this.teamType = teamType;
+ this.comparison = comparison;
+ this.score = score;
+ this.userSource = userSource;
+ this.quantifier = quantifier;
+ }
+ }
+}
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..82054911 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,11 @@ 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;
+
+ if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
+ this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
+ }
this.items.clear();
@@ -194,6 +225,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 +242,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/conditions/WiredConditionTriggererMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java
new file mode 100644
index 00000000..e7f01dcb
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionTriggererMatch.java
@@ -0,0 +1,359 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.habbohotel.bots.Bot;
+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.pets.Pet;
+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.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;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class WiredConditionTriggererMatch extends InteractionWiredCondition {
+ protected static final int ENTITY_HABBO = 1;
+ protected static final int ENTITY_PET = 2;
+ protected static final int ENTITY_BOT = 4;
+ protected static final int AVATAR_MODE_ANY = 0;
+ protected static final int AVATAR_MODE_CERTAIN = 1;
+ protected static final int QUANTIFIER_ALL = 0;
+ protected static final int QUANTIFIER_ANY = 1;
+ protected static final int SOURCE_SPECIFIED_USERNAME = 101;
+
+ public static final WiredConditionType type = WiredConditionType.TRIGGERER_MATCH;
+
+ private int entityType = ENTITY_HABBO;
+ private int avatarMode = AVATAR_MODE_ANY;
+ private int matchUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int compareUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int quantifier = QUANTIFIER_ALL;
+ private String username = "";
+
+ public WiredConditionTriggererMatch(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ MatchResult result = this.evaluateMatch(ctx);
+ return result.valid && result.matched;
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.entityType,
+ this.avatarMode,
+ this.matchUserSource,
+ this.compareUserSource,
+ this.quantifier,
+ this.username
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.resetSettings();
+
+ 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.avatarMode = this.normalizeAvatarMode(data.avatarMode);
+ this.matchUserSource = this.normalizePrimaryUserSource(data.matchUserSource);
+ this.compareUserSource = this.normalizeCompareUserSource(data.compareUserSource);
+ this.quantifier = this.normalizeQuantifier(data.quantifier);
+ this.username = this.normalizeUsername(data.username);
+ }
+
+ @Override
+ public void onPickUp() {
+ this.resetSettings();
+ }
+
+ @Override
+ public WiredConditionType getType() {
+ return type;
+ }
+
+ @Override
+ public void serializeWiredData(ServerMessage message, Room room) {
+ message.appendBoolean(true);
+ message.appendInt(5);
+ message.appendInt(0);
+ message.appendInt(this.getBaseItem().getSpriteId());
+ message.appendInt(this.getId());
+ message.appendString(this.username);
+ message.appendInt(5);
+ message.appendInt(this.entityType);
+ message.appendInt(this.avatarMode);
+ message.appendInt(this.matchUserSource);
+ message.appendInt(this.compareUserSource);
+ 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.resetSettings();
+
+ if (params.length > 0) this.entityType = this.normalizeEntityType(params[0]);
+ if (params.length > 1) this.avatarMode = this.normalizeAvatarMode(params[1]);
+ if (params.length > 2) this.matchUserSource = this.normalizePrimaryUserSource(params[2]);
+ if (params.length > 3) this.compareUserSource = this.normalizeCompareUserSource(params[3]);
+ if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]);
+
+ this.username = this.normalizeUsername(settings.getStringParam());
+
+ return true;
+ }
+
+ protected MatchResult evaluateMatch(WiredContext ctx) {
+ List matchUsers = this.resolvePrimaryUsers(ctx);
+ if (matchUsers.isEmpty()) {
+ return MatchResult.invalid();
+ }
+
+ List compareUsers = this.resolveCompareUsers(ctx);
+ if (compareUsers.isEmpty()) {
+ return MatchResult.valid(false);
+ }
+
+ Set compareUserIds = compareUsers.stream()
+ .filter(this::matchesEntityType)
+ .map(RoomUnit::getId)
+ .collect(Collectors.toSet());
+
+ if (compareUserIds.isEmpty()) {
+ return MatchResult.valid(false);
+ }
+
+ boolean matched;
+ if (this.quantifier == QUANTIFIER_ANY) {
+ matched = matchUsers.stream().anyMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds));
+ } else {
+ matched = matchUsers.stream().allMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds));
+ }
+
+ return MatchResult.valid(matched);
+ }
+
+ protected int getQuantifier() {
+ return this.quantifier;
+ }
+
+ private void resetSettings() {
+ this.entityType = ENTITY_HABBO;
+ this.avatarMode = AVATAR_MODE_ANY;
+ this.matchUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.compareUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ this.username = "";
+ }
+
+ private List resolvePrimaryUsers(WiredContext ctx) {
+ return this.deduplicate(WiredSourceUtil.resolveUsers(ctx, this.matchUserSource));
+ }
+
+ private List resolveCompareUsers(WiredContext ctx) {
+ List resolved;
+
+ if (this.compareUserSource == SOURCE_SPECIFIED_USERNAME) {
+ resolved = this.resolveUsersByName(ctx.room(), this.username);
+ } else {
+ resolved = WiredSourceUtil.resolveUsers(ctx, this.compareUserSource);
+ }
+
+ if (this.avatarMode == AVATAR_MODE_CERTAIN) {
+ String normalizedName = this.normalizeUsername(this.username);
+ if (normalizedName.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ resolved = resolved.stream()
+ .filter(roomUnit -> normalizedName.equalsIgnoreCase(this.getRoomUnitName(ctx.room(), roomUnit)))
+ .collect(Collectors.toList());
+ }
+
+ return this.deduplicate(resolved);
+ }
+
+ private List resolveUsersByName(Room room, String username) {
+ List result = new ArrayList<>();
+ String normalizedName = this.normalizeUsername(username);
+ if (room == null || normalizedName.isEmpty()) {
+ return result;
+ }
+
+ Habbo habbo = room.getHabbo(normalizedName);
+ if (habbo != null && habbo.getRoomUnit() != null) {
+ result.add(habbo.getRoomUnit());
+ }
+
+ for (Bot bot : room.getBots(normalizedName)) {
+ if (bot != null && bot.getRoomUnit() != null) {
+ result.add(bot.getRoomUnit());
+ }
+ }
+
+ for (Pet pet : room.getUnitManager().getPets()) {
+ if (pet != null && pet.getRoomUnit() != null && normalizedName.equalsIgnoreCase(pet.getName())) {
+ result.add(pet.getRoomUnit());
+ }
+ }
+
+ return result;
+ }
+
+ private List deduplicate(List users) {
+ Map deduplicated = new LinkedHashMap<>();
+
+ for (RoomUnit user : users) {
+ if (user != null) {
+ deduplicated.putIfAbsent(user.getId(), user);
+ }
+ }
+
+ return new ArrayList<>(deduplicated.values());
+ }
+
+ private boolean matchesCandidate(RoomUnit roomUnit, Set compareUserIds) {
+ return roomUnit != null && this.matchesEntityType(roomUnit) && compareUserIds.contains(roomUnit.getId());
+ }
+
+ private boolean matchesEntityType(RoomUnit roomUnit) {
+ return roomUnit != null && roomUnit.getRoomUnitType().getTypeId() == this.entityType;
+ }
+
+ private String getRoomUnitName(Room room, RoomUnit roomUnit) {
+ if (room == null || roomUnit == null) {
+ return "";
+ }
+
+ if (roomUnit.getRoomUnitType() == RoomUnitType.USER) {
+ Habbo habbo = room.getHabbo(roomUnit);
+ return (habbo != null && habbo.getHabboInfo() != null) ? habbo.getHabboInfo().getUsername() : "";
+ }
+
+ if (roomUnit.getRoomUnitType() == RoomUnitType.BOT) {
+ Bot bot = room.getBot(roomUnit);
+ return (bot != null) ? bot.getName() : "";
+ }
+
+ if (roomUnit.getRoomUnitType() == RoomUnitType.PET) {
+ Pet pet = room.getPet(roomUnit);
+ return (pet != null) ? pet.getName() : "";
+ }
+
+ return "";
+ }
+
+ private int normalizeEntityType(int value) {
+ switch (value) {
+ case ENTITY_HABBO:
+ case ENTITY_PET:
+ case ENTITY_BOT:
+ return value;
+ default:
+ return ENTITY_HABBO;
+ }
+ }
+
+ private int normalizeAvatarMode(int value) {
+ return (value == AVATAR_MODE_CERTAIN) ? AVATAR_MODE_CERTAIN : AVATAR_MODE_ANY;
+ }
+
+ private int normalizeQuantifier(int value) {
+ return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
+ }
+
+ private int normalizePrimaryUserSource(int value) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+
+ private int normalizeCompareUserSource(int value) {
+ switch (value) {
+ case WiredSourceUtil.SOURCE_CLICKED_USER:
+ case SOURCE_SPECIFIED_USERNAME:
+ return value;
+ default:
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+ }
+
+ private String normalizeUsername(String value) {
+ return (value == null) ? "" : value.trim();
+ }
+
+ protected static class MatchResult {
+ protected final boolean valid;
+ protected final boolean matched;
+
+ private MatchResult(boolean valid, boolean matched) {
+ this.valid = valid;
+ this.matched = matched;
+ }
+
+ private static MatchResult invalid() {
+ return new MatchResult(false, false);
+ }
+
+ private static MatchResult valid(boolean matched) {
+ return new MatchResult(true, matched);
+ }
+ }
+
+ static class JsonData {
+ int entityType;
+ int avatarMode;
+ int matchUserSource;
+ int compareUserSource;
+ int quantifier;
+ String username;
+
+ public JsonData(int entityType, int avatarMode, int matchUserSource, int compareUserSource, int quantifier, String username) {
+ this.entityType = entityType;
+ this.avatarMode = avatarMode;
+ this.matchUserSource = matchUserSource;
+ this.compareUserSource = compareUserSource;
+ this.quantifier = quantifier;
+ this.username = username;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java
new file mode 100644
index 00000000..5e49a6a1
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionUserPerformsAction.java
@@ -0,0 +1,339 @@
+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.rooms.RoomUnitStatus;
+import com.eu.habbo.habbohotel.wired.WiredConditionType;
+import com.eu.habbo.habbohotel.wired.WiredUserActionType;
+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 WiredConditionUserPerformsAction extends InteractionWiredCondition {
+ 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;
+ protected static final int DEFAULT_ACTION = WiredUserActionType.WAVE;
+ protected static final int QUANTIFIER_ALL = 0;
+ protected static final int QUANTIFIER_ANY = 1;
+
+ public static final WiredConditionType type = WiredConditionType.USER_PERFORMS_ACTION;
+
+ private int selectedAction = DEFAULT_ACTION;
+ private boolean signFilterEnabled = false;
+ private int signId = 0;
+ private boolean danceFilterEnabled = false;
+ private int danceId = 1;
+ private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ private int quantifier = QUANTIFIER_ALL;
+
+ public WiredConditionUserPerformsAction(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
+ super(id, userId, item, extradata, limitedStack, limitedSells);
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
+ if (targets.isEmpty()) {
+ return false;
+ }
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ return targets.stream().anyMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
+ }
+
+ return targets.stream().allMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ return WiredManager.getGson().toJson(new JsonData(
+ this.selectedAction,
+ this.signFilterEnabled,
+ this.signId,
+ this.danceFilterEnabled,
+ this.danceId,
+ this.userSource,
+ this.quantifier
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.resetSettings();
+
+ 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 = normalizeAction(data.selectedAction);
+ this.signFilterEnabled = data.signFilterEnabled;
+ this.signId = normalizeSignId(data.signId);
+ this.danceFilterEnabled = data.danceFilterEnabled;
+ this.danceId = normalizeDanceId(data.danceId);
+ this.userSource = this.normalizeUserSource(data.userSource);
+ this.quantifier = normalizeQuantifier(data.quantifier);
+ }
+
+ @Override
+ public void onPickUp() {
+ this.resetSettings();
+ }
+
+ @Override
+ public WiredConditionType getType() {
+ return type;
+ }
+
+ @Override
+ public void serializeWiredData(ServerMessage message, Room room) {
+ message.appendBoolean(true);
+ message.appendInt(5);
+ 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.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[] intParams = settings.getIntParams();
+
+ this.resetSettings();
+
+ if (intParams.length > 0) this.selectedAction = normalizeAction(intParams[0]);
+ if (intParams.length > 1) this.signFilterEnabled = (intParams[1] == 1);
+ if (intParams.length > 2) this.signId = normalizeSignId(intParams[2]);
+ if (intParams.length > 3) this.danceFilterEnabled = (intParams[3] == 1);
+ if (intParams.length > 4) this.danceId = normalizeDanceId(intParams[4]);
+ if (intParams.length > 5) this.userSource = this.normalizeUserSource(intParams[5]);
+ if (intParams.length > 6) this.quantifier = normalizeQuantifier(intParams[6]);
+
+ return true;
+ }
+
+ protected void resetSettings() {
+ this.selectedAction = DEFAULT_ACTION;
+ this.signFilterEnabled = false;
+ this.signId = 0;
+ this.danceFilterEnabled = false;
+ this.danceId = 1;
+ this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = QUANTIFIER_ALL;
+ }
+
+ protected 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;
+ }
+ }
+
+ protected int normalizeQuantifier(int value) {
+ return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
+ }
+
+ protected int normalizeUserSource(int value) {
+ return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
+ }
+
+ protected int normalizeSignId(int value) {
+ return (value < 0 || value > 17) ? 0 : value;
+ }
+
+ protected int normalizeDanceId(int value) {
+ return (value < 1 || value > 4) ? 1 : value;
+ }
+
+ protected 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);
+ }
+
+ protected boolean matchesEventAction(WiredContext ctx, RoomUnit roomUnit) {
+ RoomUnit actor = ctx.actor().orElse(null);
+
+ if (actor == null || actor.getId() != roomUnit.getId()) {
+ return false;
+ }
+
+ if (ctx.eventType() != com.eu.habbo.habbohotel.wired.core.WiredEvent.Type.USER_PERFORMS_ACTION) {
+ return false;
+ }
+
+ return this.matchesConfiguredAction(ctx.event().getActionId(), ctx.event().getActionParameter());
+ }
+
+ protected 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;
+ }
+ }
+
+ protected 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);
+ }
+
+ protected 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;
+ }
+
+ protected 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;
+ }
+ }
+
+ protected boolean matchesDanceState(RoomUnit roomUnit) {
+ int currentDance = roomUnit.getDanceType().getType();
+ if (currentDance <= 0) {
+ return false;
+ }
+
+ if (!this.danceFilterEnabled) {
+ return true;
+ }
+
+ return currentDance == this.danceId;
+ }
+
+ protected int getUserSource() {
+ return this.userSource;
+ }
+
+ protected int getQuantifier() {
+ return this.quantifier;
+ }
+
+ static class JsonData {
+ int selectedAction;
+ boolean signFilterEnabled;
+ int signId;
+ boolean danceFilterEnabled;
+ int danceId;
+ int userSource;
+ int quantifier;
+
+ public JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId, int userSource, int quantifier) {
+ this.selectedAction = selectedAction;
+ this.signFilterEnabled = signFilterEnabled;
+ this.signId = signId;
+ this.danceFilterEnabled = danceFilterEnabled;
+ this.danceId = danceId;
+ this.userSource = userSource;
+ this.quantifier = quantifier;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableAgeMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableAgeMatch.java
new file mode 100644
index 00000000..18456674
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableAgeMatch.java
@@ -0,0 +1,420 @@
+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.wired.WiredSettings;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
+import com.eu.habbo.habbohotel.users.Habbo;
+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.WiredContextVariableSupport;
+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.ArrayList;
+import java.util.List;
+
+public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
+ public static final WiredConditionType type = WiredConditionType.VAR_AGE_MATCH;
+
+ private static final int TARGET_CONTEXT = 2;
+ private static final int COMPARE_VALUE_CREATED = 0;
+ private static final int COMPARE_VALUE_UPDATED = 1;
+ private static final int COMPARISON_LOWER_THAN = 0;
+ private static final int COMPARISON_HIGHER_THAN = 2;
+ private static final int DURATION_UNIT_MILLISECONDS = 0;
+ private static final int DURATION_UNIT_SECONDS = 1;
+ private static final int DURATION_UNIT_MINUTES = 2;
+ private static final int DURATION_UNIT_HOURS = 3;
+ private static final int DURATION_UNIT_DAYS = 4;
+ private static final int DURATION_UNIT_WEEKS = 5;
+ private static final int DURATION_UNIT_MONTHS = 6;
+ private static final int DURATION_UNIT_YEARS = 7;
+
+ protected int compareValue = COMPARE_VALUE_CREATED;
+ protected int comparison = COMPARISON_LOWER_THAN;
+ protected int durationAmount = 0;
+ protected int durationUnit = DURATION_UNIT_SECONDS;
+
+ public WiredConditionVariableAgeMatch(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionVariableAgeMatch(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) {
+ this.refresh();
+
+ List serializedItems = new ArrayList<>();
+ if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
+ serializedItems.addAll(this.selectedItems);
+ }
+
+ message.appendBoolean(false);
+ message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
+ message.appendInt(serializedItems.size());
+
+ for (HabboItem item : serializedItems) {
+ message.appendInt(item.getId());
+ }
+
+ message.appendInt(this.getBaseItem().getSpriteId());
+ message.appendInt(this.getId());
+ message.appendString(this.variableToken == null ? "" : this.variableToken);
+ message.appendInt(8);
+ message.appendInt(this.targetType);
+ message.appendInt(this.compareValue);
+ message.appendInt(this.comparison);
+ message.appendInt(this.durationAmount);
+ message.appendInt(this.durationUnit);
+ message.appendInt(this.userSource);
+ 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();
+ Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
+
+ this.targetType = (params.length > 0) ? normalizeTargetTypeExtended(params[0]) : TARGET_USER;
+ this.compareValue = (params.length > 1) ? normalizeCompareValue(params[1]) : COMPARE_VALUE_CREATED;
+ this.comparison = (params.length > 2) ? normalizeComparison(params[2]) : COMPARISON_LOWER_THAN;
+ this.durationAmount = Math.max(0, (params.length > 3) ? params[3] : 0);
+ this.durationUnit = (params.length > 4) ? normalizeDurationUnit(params[4]) : DURATION_UNIT_SECONDS;
+ this.userSource = (params.length > 5) ? normalizeUserSource(params[5]) : WiredSourceUtil.SOURCE_TRIGGER;
+ this.furniSource = (params.length > 6) ? normalizeFurniSource(params[6]) : WiredSourceUtil.SOURCE_TRIGGER;
+ this.quantifier = (params.length > 7) ? normalizeQuantifier(params[7]) : QUANTIFIER_ALL;
+ this.setVariableToken(normalizeVariableToken(settings.getStringParam()));
+
+ if (!this.isValidSource(room)) {
+ return false;
+ }
+
+ this.selectedItems.clear();
+
+ if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
+ int[] furniIds = settings.getFurniIds();
+ if (furniIds.length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
+ return false;
+ }
+
+ for (int furniId : furniIds) {
+ HabboItem item = room.getHabboItem(furniId);
+
+ if (item != null) {
+ this.selectedItems.add(item);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ Room room = ctx.room();
+
+ if (room == null || this.variableToken == null || this.variableToken.isEmpty() || !isCustomVariableToken(this.variableToken)) {
+ return false;
+ }
+
+ long thresholdMs = durationToMillis(this.durationAmount, this.durationUnit);
+
+ return switch (this.targetType) {
+ case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room, thresholdMs);
+ case TARGET_ROOM -> this.evaluateRoomTarget(room, thresholdMs);
+ case TARGET_CONTEXT -> this.evaluateContextTarget(ctx, room, thresholdMs);
+ default -> this.evaluateUserTargets(ctx, room, thresholdMs);
+ };
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ this.refresh();
+
+ List itemIds = new ArrayList<>();
+ for (HabboItem item : this.selectedItems) {
+ if (item != null) itemIds.add(item.getId());
+ }
+
+ return WiredManager.getGson().toJson(new JsonData(
+ itemIds,
+ this.targetType,
+ this.variableToken,
+ this.variableItemId,
+ this.compareValue,
+ this.comparison,
+ this.durationAmount,
+ this.durationUnit,
+ this.userSource,
+ this.furniSource,
+ 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;
+
+ try {
+ if (wiredData.startsWith("{")) {
+ JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
+
+ if (data == null) return;
+
+ this.targetType = normalizeTargetTypeExtended(data.targetType);
+ this.compareValue = normalizeCompareValue(data.compareValue);
+ this.comparison = normalizeComparison(data.comparison);
+ this.durationAmount = Math.max(0, data.durationAmount);
+ this.durationUnit = normalizeDurationUnit(data.durationUnit);
+ this.userSource = normalizeUserSource(data.userSource);
+ this.furniSource = normalizeFurniSource(data.furniSource);
+ this.quantifier = normalizeQuantifier(data.quantifier);
+ this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
+
+ if (room != null && data.itemIds != null) {
+ for (Integer itemId : data.itemIds) {
+ if (itemId == null || itemId <= 0) continue;
+
+ HabboItem item = room.getHabboItem(itemId);
+ if (item != null) this.selectedItems.add(item);
+ }
+ }
+
+ return;
+ }
+
+ this.setVariableToken(normalizeVariableToken(wiredData));
+ } catch (Exception e) {
+ this.onPickUp();
+ }
+ }
+
+ @Override
+ public void onPickUp() {
+ super.onPickUp();
+ this.compareValue = COMPARE_VALUE_CREATED;
+ this.comparison = COMPARISON_LOWER_THAN;
+ this.durationAmount = 0;
+ this.durationUnit = DURATION_UNIT_SECONDS;
+ }
+
+ private boolean evaluateUserTargets(WiredContext ctx, Room room, long thresholdMs) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
+ if (targets.isEmpty()) return false;
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ for (RoomUnit roomUnit : targets) {
+ if (this.matchesAge(this.readUserAgeMs(room, roomUnit), thresholdMs)) return true;
+ }
+
+ return false;
+ }
+
+ for (RoomUnit roomUnit : targets) {
+ if (!this.matchesAge(this.readUserAgeMs(room, roomUnit), thresholdMs)) return false;
+ }
+
+ return true;
+ }
+
+ private boolean evaluateFurniTargets(WiredContext ctx, Room room, long thresholdMs) {
+ this.refresh();
+
+ List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
+ if (targets.isEmpty()) return false;
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ for (HabboItem item : targets) {
+ if (this.matchesAge(this.readFurniAgeMs(room, item), thresholdMs)) return true;
+ }
+
+ return false;
+ }
+
+ for (HabboItem item : targets) {
+ if (!this.matchesAge(this.readFurniAgeMs(room, item), thresholdMs)) return false;
+ }
+
+ return true;
+ }
+
+ private boolean evaluateRoomTarget(Room room, long thresholdMs) {
+ return this.matchesAge(this.readRoomAgeMs(room), thresholdMs);
+ }
+
+ private boolean evaluateContextTarget(WiredContext ctx, Room room, long thresholdMs) {
+ return this.matchesAge(this.readContextAgeMs(ctx, room), thresholdMs);
+ }
+
+ private Long readUserAgeMs(Room room, RoomUnit roomUnit) {
+ if (room == null || roomUnit == null) return null;
+
+ Habbo habbo = room.getHabbo(roomUnit);
+ if (habbo == null || !room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId)) return null;
+
+ int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
+ ? room.getUserVariableManager().getUpdatedAt(habbo.getHabboInfo().getId(), this.variableItemId)
+ : room.getUserVariableManager().getCreatedAt(habbo.getHabboInfo().getId(), this.variableItemId);
+
+ return timestampToAgeMs(timestamp);
+ }
+
+ private Long readFurniAgeMs(Room room, HabboItem item) {
+ if (room == null || item == null || !room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId)) return null;
+
+ int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
+ ? room.getFurniVariableManager().getUpdatedAt(item.getId(), this.variableItemId)
+ : room.getFurniVariableManager().getCreatedAt(item.getId(), this.variableItemId);
+
+ return timestampToAgeMs(timestamp);
+ }
+
+ private Long readRoomAgeMs(Room room) {
+ if (room == null) return null;
+ if (this.compareValue == COMPARE_VALUE_CREATED) return null;
+
+ int timestamp = room.getRoomVariableManager().getUpdatedAt(this.variableItemId);
+ return timestampToAgeMs(timestamp);
+ }
+
+ private Long readContextAgeMs(WiredContext ctx, Room room) {
+ if (ctx == null || room == null || !WiredContextVariableSupport.hasVariable(ctx, this.variableItemId)) return null;
+
+ int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
+ ? WiredContextVariableSupport.getUpdatedAt(ctx, this.variableItemId)
+ : WiredContextVariableSupport.getCreatedAt(ctx, this.variableItemId);
+
+ return timestampToAgeMs(timestamp);
+ }
+
+ private boolean matchesAge(Long ageMs, long thresholdMs) {
+ if (ageMs == null) return false;
+
+ return switch (this.comparison) {
+ case COMPARISON_HIGHER_THAN -> ageMs > thresholdMs;
+ default -> ageMs < thresholdMs;
+ };
+ }
+
+ private boolean isValidSource(Room room) {
+ if (room == null || !isCustomVariableToken(this.variableToken)) return false;
+
+ return switch (this.targetType) {
+ case TARGET_FURNI -> room.getFurniVariableManager().getDefinitionInfo(this.variableItemId) != null;
+ case TARGET_CONTEXT -> WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId) != null;
+ case TARGET_ROOM -> {
+ WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.variableItemId);
+ yield this.compareValue == COMPARE_VALUE_UPDATED && definition != null;
+ }
+ default -> room.getUserVariableManager().getDefinitionInfo(this.variableItemId) != null;
+ };
+ }
+
+ private static Long timestampToAgeMs(int timestampSeconds) {
+ if (timestampSeconds <= 0) return null;
+
+ long timestampMs = (timestampSeconds * 1000L);
+ return Math.max(0L, System.currentTimeMillis() - timestampMs);
+ }
+
+ private static long durationToMillis(int amount, int unit) {
+ long normalizedAmount = Math.max(0L, amount);
+
+ return switch (unit) {
+ case DURATION_UNIT_MILLISECONDS -> normalizedAmount;
+ case DURATION_UNIT_MINUTES -> safeMultiply(normalizedAmount, 60_000L);
+ case DURATION_UNIT_HOURS -> safeMultiply(normalizedAmount, 3_600_000L);
+ case DURATION_UNIT_DAYS -> safeMultiply(normalizedAmount, 86_400_000L);
+ case DURATION_UNIT_WEEKS -> safeMultiply(normalizedAmount, 604_800_000L);
+ case DURATION_UNIT_MONTHS -> safeMultiply(normalizedAmount, 2_592_000_000L);
+ case DURATION_UNIT_YEARS -> safeMultiply(normalizedAmount, 31_536_000_000L);
+ default -> safeMultiply(normalizedAmount, 1_000L);
+ };
+ }
+
+ private static long safeMultiply(long left, long right) {
+ if (left <= 0 || right <= 0) return 0L;
+ if (left > (Long.MAX_VALUE / right)) return Long.MAX_VALUE;
+
+ return left * right;
+ }
+
+ private static int normalizeTargetTypeExtended(int value) {
+ return switch (value) {
+ case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
+ default -> TARGET_USER;
+ };
+ }
+
+ private static int normalizeCompareValue(int value) {
+ return (value == COMPARE_VALUE_UPDATED) ? COMPARE_VALUE_UPDATED : COMPARE_VALUE_CREATED;
+ }
+
+ private static int normalizeComparison(int value) {
+ return (value == COMPARISON_HIGHER_THAN) ? COMPARISON_HIGHER_THAN : COMPARISON_LOWER_THAN;
+ }
+
+ private static int normalizeDurationUnit(int value) {
+ return switch (value) {
+ case DURATION_UNIT_MILLISECONDS, DURATION_UNIT_SECONDS, DURATION_UNIT_MINUTES, DURATION_UNIT_HOURS,
+ DURATION_UNIT_DAYS, DURATION_UNIT_WEEKS, DURATION_UNIT_MONTHS, DURATION_UNIT_YEARS -> value;
+ default -> DURATION_UNIT_SECONDS;
+ };
+ }
+
+ protected static class JsonData {
+ List itemIds;
+ int targetType;
+ String variableToken;
+ int variableItemId;
+ int compareValue;
+ int comparison;
+ int durationAmount;
+ int durationUnit;
+ int userSource;
+ int furniSource;
+ int quantifier;
+
+ JsonData(List itemIds, int targetType, String variableToken, int variableItemId, int compareValue, int comparison, int durationAmount, int durationUnit, int userSource, int furniSource, int quantifier) {
+ this.itemIds = itemIds;
+ this.targetType = targetType;
+ this.variableToken = variableToken;
+ this.variableItemId = variableItemId;
+ this.compareValue = compareValue;
+ this.comparison = comparison;
+ this.durationAmount = durationAmount;
+ this.durationUnit = durationUnit;
+ this.userSource = userSource;
+ this.furniSource = furniSource;
+ this.quantifier = quantifier;
+ }
+ }
+}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableValueMatch.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableValueMatch.java
new file mode 100644
index 00000000..13f79233
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/conditions/WiredConditionVariableValueMatch.java
@@ -0,0 +1,814 @@
+package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
+
+import com.eu.habbo.Emulator;
+import com.eu.habbo.habbohotel.bots.Bot;
+import com.eu.habbo.habbohotel.games.Game;
+import com.eu.habbo.habbohotel.games.GamePlayer;
+import com.eu.habbo.habbohotel.games.GameTeam;
+import com.eu.habbo.habbohotel.games.GameTeamColors;
+import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
+import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
+import com.eu.habbo.habbohotel.games.wired.WiredGame;
+import com.eu.habbo.habbohotel.items.Item;
+import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
+import com.eu.habbo.habbohotel.pets.Pet;
+import com.eu.habbo.habbohotel.rooms.Room;
+import com.eu.habbo.habbohotel.rooms.RoomUnit;
+import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
+import com.eu.habbo.habbohotel.users.Habbo;
+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.WiredContextVariableSupport;
+import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
+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.util.HotelDateTimeUtil;
+import gnu.trove.set.hash.THashSet;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.ZonedDateTime;
+import java.time.temporal.WeekFields;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+
+public class WiredConditionVariableValueMatch extends WiredConditionHasVariable {
+ public static final WiredConditionType type = WiredConditionType.VAR_VAL_MATCH;
+
+ private static final int TARGET_CONTEXT = 2;
+ private static final int SOURCE_SECONDARY_SELECTED = 101;
+ private static final int REFERENCE_CONSTANT = 0;
+ private static final int REFERENCE_VARIABLE = 1;
+ private static final int COMPARISON_GREATER_THAN = 0;
+ private static final int COMPARISON_GREATER_THAN_OR_EQUAL = 1;
+ private static final int COMPARISON_EQUAL = 2;
+ private static final int COMPARISON_LESS_THAN_OR_EQUAL = 3;
+ private static final int COMPARISON_LESS_THAN = 4;
+ private static final int COMPARISON_NOT_EQUAL = 5;
+ private static final String DELIM = "\t";
+ private static final String FURNI_DELIM = ";";
+
+ protected int comparison = COMPARISON_EQUAL;
+ protected int referenceMode = REFERENCE_CONSTANT;
+ protected int referenceConstantValue = 0;
+ protected int referenceTargetType = TARGET_USER;
+ protected int referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ protected int referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ protected String referenceVariableToken = "";
+ protected int referenceVariableItemId = 0;
+ protected final THashSet referenceSelectedItems = new THashSet<>();
+
+ public WiredConditionVariableValueMatch(ResultSet set, Item baseItem) throws SQLException {
+ super(set, baseItem);
+ }
+
+ public WiredConditionVariableValueMatch(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) {
+ this.refresh();
+ this.refreshReferenceItems();
+
+ List serializedItems = new ArrayList<>();
+ if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
+ serializedItems.addAll(this.selectedItems);
+ }
+
+ message.appendBoolean(false);
+ message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
+ message.appendInt(serializedItems.size());
+
+ for (HabboItem item : serializedItems) {
+ message.appendInt(item.getId());
+ }
+
+ message.appendInt(this.getBaseItem().getSpriteId());
+ message.appendInt(this.getId());
+ message.appendString(this.serializeStringData());
+ message.appendInt(10);
+ message.appendInt(this.targetType);
+ message.appendInt(this.comparison);
+ message.appendInt(this.referenceMode);
+ message.appendInt(this.referenceConstantValue);
+ message.appendInt(this.referenceTargetType);
+ message.appendInt(this.userSource);
+ message.appendInt(this.furniSource);
+ message.appendInt(this.referenceUserSource);
+ message.appendInt(this.referenceFurniSource);
+ message.appendInt(this.quantifier);
+ message.appendInt(0);
+ message.appendInt(this.getType().code);
+ message.appendInt(0);
+ message.appendInt(0);
+ }
+
+ @Override
+ public boolean saveData(WiredSettings settings) {
+ Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
+ if (room == null) return false;
+
+ int[] params = settings.getIntParams();
+ String[] stringParts = this.parseStringData(settings.getStringParam());
+ int nextTargetType = normalizeTargetTypeExtended(param(params, 0, TARGET_USER));
+ int nextComparison = normalizeComparison(param(params, 1, COMPARISON_EQUAL));
+ int nextReferenceMode = normalizeReferenceMode(param(params, 2, REFERENCE_CONSTANT));
+ int nextReferenceConstantValue = param(params, 3, 0);
+ int nextReferenceTargetType = normalizeTargetTypeExtended(param(params, 4, TARGET_USER));
+ int nextUserSource = normalizeUserSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
+ int nextFurniSource = normalizeFurniSource(param(params, 6, WiredSourceUtil.SOURCE_TRIGGER));
+ int nextReferenceUserSource = normalizeUserSource(param(params, 7, WiredSourceUtil.SOURCE_TRIGGER));
+ int nextReferenceFurniSource = normalizeReferenceFurniSource(param(params, 8, WiredSourceUtil.SOURCE_TRIGGER));
+ int nextQuantifier = normalizeQuantifier(param(params, 9, QUANTIFIER_ALL));
+ String nextVariableToken = normalizeVariableToken((stringParts.length > 0) ? stringParts[0] : settings.getStringParam());
+ String nextReferenceVariableToken = normalizeVariableToken((stringParts.length > 1) ? stringParts[1] : "");
+
+ if (!this.isValidSource(room, nextTargetType, nextVariableToken)) return false;
+ if (nextReferenceMode == REFERENCE_VARIABLE && !this.isValidReference(room, nextReferenceTargetType, nextReferenceVariableToken)) return false;
+
+ int selectionLimit = Emulator.getConfig().getInt("hotel.wired.furni.selection.count");
+ List nextSelectedItems = (nextTargetType == TARGET_FURNI && nextFurniSource == WiredSourceUtil.SOURCE_SELECTED)
+ ? this.parseItems(settings.getFurniIds(), room)
+ : new ArrayList<>();
+ List nextReferenceItems = (nextReferenceMode == REFERENCE_VARIABLE && nextReferenceTargetType == TARGET_FURNI && nextReferenceFurniSource == SOURCE_SECONDARY_SELECTED)
+ ? this.parseItems((stringParts.length > 2) ? stringParts[2] : "", room)
+ : new ArrayList<>();
+
+ if (nextSelectedItems.size() > selectionLimit || nextReferenceItems.size() > selectionLimit) return false;
+
+ this.selectedItems.clear();
+ this.selectedItems.addAll(nextSelectedItems);
+ this.referenceSelectedItems.clear();
+ this.referenceSelectedItems.addAll(nextReferenceItems);
+ this.targetType = nextTargetType;
+ this.comparison = nextComparison;
+ this.referenceMode = nextReferenceMode;
+ this.referenceConstantValue = nextReferenceConstantValue;
+ this.referenceTargetType = nextReferenceTargetType;
+ this.userSource = nextUserSource;
+ this.furniSource = nextFurniSource;
+ this.referenceUserSource = nextReferenceUserSource;
+ this.referenceFurniSource = nextReferenceFurniSource;
+ this.quantifier = nextQuantifier;
+ this.setVariableToken(nextVariableToken);
+ this.setReferenceVariableToken(nextReferenceVariableToken);
+
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(WiredContext ctx) {
+ Room room = ctx.room();
+
+ if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
+ return false;
+ }
+
+ return switch (this.targetType) {
+ case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room);
+ case TARGET_ROOM -> this.evaluateRoomTarget(ctx, room);
+ case TARGET_CONTEXT -> this.evaluateContextTarget(ctx, room);
+ default -> this.evaluateUserTargets(ctx, room);
+ };
+ }
+
+ @Deprecated
+ @Override
+ public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
+ return false;
+ }
+
+ @Override
+ public String getWiredData() {
+ this.refresh();
+ this.refreshReferenceItems();
+
+ return WiredManager.getGson().toJson(new JsonData(
+ this.targetType,
+ this.variableToken,
+ this.variableItemId,
+ this.comparison,
+ this.referenceMode,
+ this.referenceConstantValue,
+ this.referenceTargetType,
+ this.referenceVariableToken,
+ this.referenceVariableItemId,
+ this.userSource,
+ this.furniSource,
+ this.referenceUserSource,
+ this.referenceFurniSource,
+ this.quantifier,
+ this.toIds(this.selectedItems),
+ this.toIds(this.referenceSelectedItems)
+ ));
+ }
+
+ @Override
+ public void loadWiredData(ResultSet set, Room room) throws SQLException {
+ this.onPickUp();
+
+ 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.targetType = normalizeTargetTypeExtended(data.targetType);
+ this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
+ this.comparison = normalizeComparison(data.comparison);
+ this.referenceMode = normalizeReferenceMode(data.referenceMode);
+ this.referenceConstantValue = data.referenceConstantValue;
+ this.referenceTargetType = normalizeTargetTypeExtended(data.referenceTargetType);
+ this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
+ this.userSource = normalizeUserSource(data.userSource);
+ this.furniSource = normalizeFurniSource(data.furniSource);
+ this.referenceUserSource = normalizeUserSource(data.referenceUserSource);
+ this.referenceFurniSource = normalizeReferenceFurniSource(data.referenceFurniSource);
+ this.quantifier = normalizeQuantifier(data.quantifier);
+
+ if (room == null) return;
+
+ this.selectedItems.addAll(this.parseItems(data.selectedItemIds, room));
+ this.referenceSelectedItems.addAll(this.parseItems(data.referenceSelectedItemIds, room));
+ }
+
+ @Override
+ public void onPickUp() {
+ super.onPickUp();
+ this.comparison = COMPARISON_EQUAL;
+ this.referenceMode = REFERENCE_CONSTANT;
+ this.referenceConstantValue = 0;
+ this.referenceTargetType = TARGET_USER;
+ this.referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
+ this.referenceSelectedItems.clear();
+ this.setReferenceVariableToken("");
+ }
+
+ public boolean requiresTriggeringUser() {
+ return (this.targetType == TARGET_USER && this.userSource == WiredSourceUtil.SOURCE_TRIGGER)
+ || (this.referenceMode == REFERENCE_VARIABLE && this.referenceTargetType == TARGET_USER && this.referenceUserSource == WiredSourceUtil.SOURCE_TRIGGER);
+ }
+
+ private boolean evaluateUserTargets(WiredContext ctx, Room room) {
+ List targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
+ if (targets.isEmpty()) return false;
+
+ ReferenceSnapshot references = this.resolveReferences(ctx, room);
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ int index = 0;
+ for (RoomUnit roomUnit : targets) {
+ Integer currentValue = this.readUserValue(room, roomUnit);
+ Integer referenceValue = this.referenceFor(references, roomUnit != null ? roomUnit.getId() : 0, TARGET_USER, index++);
+
+ if (this.matchesComparison(currentValue, referenceValue)) return true;
+ }
+
+ return false;
+ }
+
+ int index = 0;
+ for (RoomUnit roomUnit : targets) {
+ Integer currentValue = this.readUserValue(room, roomUnit);
+ Integer referenceValue = this.referenceFor(references, roomUnit != null ? roomUnit.getId() : 0, TARGET_USER, index++);
+
+ if (!this.matchesComparison(currentValue, referenceValue)) return false;
+ }
+
+ return true;
+ }
+
+ private boolean evaluateFurniTargets(WiredContext ctx, Room room) {
+ this.refresh();
+
+ List targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
+ if (targets.isEmpty()) return false;
+
+ ReferenceSnapshot references = this.resolveReferences(ctx, room);
+
+ if (this.quantifier == QUANTIFIER_ANY) {
+ int index = 0;
+ for (HabboItem item : targets) {
+ Integer currentValue = this.readFurniValue(room, item);
+ Integer referenceValue = this.referenceFor(references, item != null ? item.getId() : 0, TARGET_FURNI, index++);
+
+ if (this.matchesComparison(currentValue, referenceValue)) return true;
+ }
+
+ return false;
+ }
+
+ int index = 0;
+ for (HabboItem item : targets) {
+ Integer currentValue = this.readFurniValue(room, item);
+ Integer referenceValue = this.referenceFor(references, item != null ? item.getId() : 0, TARGET_FURNI, index++);
+
+ if (!this.matchesComparison(currentValue, referenceValue)) return false;
+ }
+
+ return true;
+ }
+
+ private boolean evaluateRoomTarget(WiredContext ctx, Room room) {
+ Integer currentValue = this.readRoomValue(room);
+ Integer referenceValue = this.referenceFor(this.resolveReferences(ctx, room), room.getId(), TARGET_ROOM, 0);
+
+ return this.matchesComparison(currentValue, referenceValue);
+ }
+
+ private boolean evaluateContextTarget(WiredContext ctx, Room room) {
+ Integer currentValue = this.readContextTargetValue(ctx, room);
+ Integer referenceValue = this.referenceFor(this.resolveReferences(ctx, room), this.variableItemId, TARGET_CONTEXT, 0);
+
+ return this.matchesComparison(currentValue, referenceValue);
+ }
+
+ private ReferenceSnapshot resolveReferences(WiredContext ctx, Room room) {
+ if (this.referenceMode != REFERENCE_VARIABLE) return null;
+
+ return switch (this.referenceTargetType) {
+ case TARGET_USER -> this.userReferences(ctx, room);
+ case TARGET_FURNI -> this.furniReferences(ctx, room);
+ case TARGET_CONTEXT -> this.contextReferences(ctx, room);
+ case TARGET_ROOM -> this.roomReferences(room);
+ default -> null;
+ };
+ }
+
+ private ReferenceSnapshot userReferences(WiredContext ctx, Room room) {
+ ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_USER);
+
+ if (isInternalVariableToken(this.referenceVariableToken)) {
+ String key = getInternalVariableKey(this.referenceVariableToken);
+ if (!canUseUserInternalReference(key)) return null;
+
+ for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
+ Integer value = this.readUserInternalValue(room, roomUnit, key);
+ if (value != null && roomUnit != null) snapshot.add(roomUnit.getId(), value);
+ }
+
+ return snapshot.isEmpty() ? null : snapshot;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.referenceVariableItemId);
+ if (definition == null || !definition.hasValue()) return null;
+
+ for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
+ if (roomUnit == null) continue;
+
+ Habbo habbo = room.getHabbo(roomUnit);
+ if (habbo != null) snapshot.add(roomUnit.getId(), room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.referenceVariableItemId));
+ }
+
+ return snapshot.isEmpty() ? null : snapshot;
+ }
+
+ private ReferenceSnapshot furniReferences(WiredContext ctx, Room room) {
+ int source = (this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.referenceFurniSource;
+ if (source == WiredSourceUtil.SOURCE_SELECTED) this.refreshReferenceItems();
+
+ ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_FURNI);
+
+ if (isInternalVariableToken(this.referenceVariableToken)) {
+ String key = getInternalVariableKey(this.referenceVariableToken);
+ if (!canUseFurniInternalReference(key)) return null;
+
+ for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
+ Integer value = this.readFurniInternalValue(room, item, key);
+ if (value != null && item != null) snapshot.add(item.getId(), value);
+ }
+
+ return snapshot.isEmpty() ? null : snapshot;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.referenceVariableItemId);
+ if (definition == null || !definition.hasValue()) return null;
+
+ for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
+ if (item != null) snapshot.add(item.getId(), room.getFurniVariableManager().getCurrentValue(item.getId(), this.referenceVariableItemId));
+ }
+
+ return snapshot.isEmpty() ? null : snapshot;
+ }
+
+ private ReferenceSnapshot roomReferences(Room room) {
+ ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_ROOM);
+
+ if (isInternalVariableToken(this.referenceVariableToken)) {
+ String key = getInternalVariableKey(this.referenceVariableToken);
+ if (!canUseRoomInternalReference(key)) return null;
+
+ Integer value = this.readRoomInternalValue(room, key);
+ if (value == null) return null;
+
+ snapshot.add(room.getId(), value);
+ return snapshot;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.referenceVariableItemId);
+ if (definition == null || !definition.hasValue()) return null;
+
+ snapshot.add(room.getId(), room.getRoomVariableManager().getCurrentValue(this.referenceVariableItemId));
+ return snapshot;
+ }
+
+ private ReferenceSnapshot contextReferences(WiredContext ctx, Room room) {
+ ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_CONTEXT);
+
+ if (isInternalVariableToken(this.referenceVariableToken)) {
+ String key = getInternalVariableKey(this.referenceVariableToken);
+ if (!canUseContextInternalReference(key)) return null;
+
+ Integer value = WiredInternalVariableSupport.readContextValue(ctx, key);
+ if (value == null) return null;
+
+ snapshot.add(this.referenceVariableItemId > 0 ? this.referenceVariableItemId : (room != null ? room.getId() : 0), value);
+ return snapshot;
+ }
+
+ WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.referenceVariableItemId);
+ if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.referenceVariableItemId)) return null;
+
+ Integer value = WiredContextVariableSupport.getCurrentValue(ctx, this.referenceVariableItemId);
+ if (value == null) return null;
+
+ snapshot.add(this.referenceVariableItemId, value);
+ return snapshot;
+ }
+
+ private Integer readUserValue(Room room, RoomUnit roomUnit) {
+ if (room == null || roomUnit == null) return null;
+
+ if (isInternalVariableToken(this.variableToken)) {
+ String key = getInternalVariableKey(this.variableToken);
+ return canUseUserInternalReference(key) ? this.readUserInternalValue(room, roomUnit, key) : null;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
+ if (definition == null || !definition.hasValue()) return null;
+
+ Habbo habbo = room.getHabbo(roomUnit);
+ return (habbo != null) ? room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.variableItemId) : null;
+ }
+
+ private Integer readFurniValue(Room room, HabboItem item) {
+ if (room == null || item == null) return null;
+
+ if (isInternalVariableToken(this.variableToken)) {
+ String key = getInternalVariableKey(this.variableToken);
+ return canUseFurniInternalReference(key) ? this.readFurniInternalValue(room, item, key) : null;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
+ return (definition != null && definition.hasValue()) ? room.getFurniVariableManager().getCurrentValue(item.getId(), this.variableItemId) : null;
+ }
+
+ private Integer readRoomValue(Room room) {
+ if (room == null) return null;
+
+ if (isInternalVariableToken(this.variableToken)) {
+ String key = getInternalVariableKey(this.variableToken);
+ return canUseRoomInternalReference(key) ? this.readRoomInternalValue(room, key) : null;
+ }
+
+ WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.variableItemId);
+ return (definition != null && definition.hasValue()) ? room.getRoomVariableManager().getCurrentValue(this.variableItemId) : null;
+ }
+
+ private Integer readContextTargetValue(WiredContext ctx, Room room) {
+ if (ctx == null || room == null) return null;
+
+ if (isInternalVariableToken(this.variableToken)) {
+ String key = getInternalVariableKey(this.variableToken);
+ return canUseContextInternalReference(key) ? WiredInternalVariableSupport.readContextValue(ctx, key) : null;
+ }
+
+ WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId);
+ if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.variableItemId)) return null;
+
+ return WiredContextVariableSupport.getCurrentValue(ctx, this.variableItemId);
+ }
+
+ private Integer referenceFor(ReferenceSnapshot snapshot, int destinationEntityId, int destinationTarget, int destinationIndex) {
+ if (this.referenceMode != REFERENCE_VARIABLE) return this.referenceConstantValue;
+ if (snapshot == null || snapshot.isEmpty()) return null;
+ if (snapshot.targetType == destinationTarget && snapshot.values.containsKey(destinationEntityId)) return snapshot.values.get(destinationEntityId);
+ if (destinationIndex >= 0 && destinationIndex < snapshot.values.size()) return new ArrayList<>(snapshot.values.values()).get(destinationIndex);
+ return new ArrayList<>(snapshot.values.values()).get(0);
+ }
+
+ private boolean matchesComparison(Integer currentValue, Integer referenceValue) {
+ if (currentValue == null || referenceValue == null) return false;
+
+ return switch (this.comparison) {
+ case COMPARISON_GREATER_THAN -> currentValue > referenceValue;
+ case COMPARISON_GREATER_THAN_OR_EQUAL -> currentValue >= referenceValue;
+ case COMPARISON_LESS_THAN_OR_EQUAL -> currentValue <= referenceValue;
+ case COMPARISON_LESS_THAN -> currentValue < referenceValue;
+ case COMPARISON_NOT_EQUAL -> !currentValue.equals(referenceValue);
+ default -> currentValue.equals(referenceValue);
+ };
+ }
+
+ private boolean isValidSource(Room room, int targetType, String variableToken) {
+ if (variableToken == null || variableToken.isEmpty()) return false;
+
+ return switch (targetType) {
+ case TARGET_USER -> isInternalVariableToken(variableToken)
+ ? canUseUserInternalReference(getInternalVariableKey(variableToken))
+ : this.isValidUserCustomValue(room, getCustomItemId(variableToken));
+ case TARGET_FURNI -> isInternalVariableToken(variableToken)
+ ? canUseFurniInternalReference(getInternalVariableKey(variableToken))
+ : this.isValidFurniCustomValue(room, getCustomItemId(variableToken));
+ case TARGET_CONTEXT -> isInternalVariableToken(variableToken)
+ ? canUseContextInternalReference(getInternalVariableKey(variableToken))
+ : this.isValidContextCustomValue(room, getCustomItemId(variableToken));
+ case TARGET_ROOM -> isInternalVariableToken(variableToken)
+ ? canUseRoomInternalReference(getInternalVariableKey(variableToken))
+ : this.isValidRoomCustomValue(room, getCustomItemId(variableToken));
+ default -> false;
+ };
+ }
+
+ private boolean isValidReference(Room room, int targetType, String variableToken) {
+ return this.isValidSource(room, targetType, variableToken);
+ }
+
+ private boolean isValidUserCustomValue(Room room, int variableItemId) {
+ WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(variableItemId) : null;
+ return definition != null && definition.hasValue();
+ }
+
+ private boolean isValidFurniCustomValue(Room room, int variableItemId) {
+ WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(variableItemId) : null;
+ return definition != null && definition.hasValue();
+ }
+
+ private boolean isValidRoomCustomValue(Room room, int variableItemId) {
+ WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(variableItemId) : null;
+ return definition != null && definition.hasValue();
+ }
+
+ private boolean isValidContextCustomValue(Room room, int variableItemId) {
+ WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
+ return definition != null && definition.hasValue();
+ }
+
+ private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
+ return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
+ }
+
+ private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
+ return WiredInternalVariableSupport.readFurniValue(room, item, key);
+ }
+
+ private Integer readRoomInternalValue(Room room, String key) {
+ return WiredInternalVariableSupport.readRoomValue(room, key);
+ }
+
+ private Integer getUserTeamScore(Room room, Habbo habbo) {
+ if (room == null || habbo == null || habbo.getHabboInfo().getGamePlayer() == null) return null;
+
+ Game game = this.resolveTeamGame(room, habbo);
+ GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
+
+ if (game == null || gamePlayer.getTeamColor() == null) return gamePlayer.getScore();
+
+ GameTeam team = game.getTeam(gamePlayer.getTeamColor());
+ return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
+ }
+
+ private Integer getTeamColorId(int effectId) {
+ TeamEffectData data = this.getTeamEffectData(effectId);
+ return data == null ? null : data.colorId;
+ }
+
+ private Integer getTeamTypeId(int effectId) {
+ TeamEffectData data = this.getTeamEffectData(effectId);
+ return data == null ? null : data.typeId;
+ }
+
+ private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
+ Game game = this.resolveTeamGame(room, null);
+ if (game == null || color == null) return 0;
+
+ GameTeam team = game.getTeam(color);
+ if (team == null) return 0;
+
+ return score ? team.getTotalScore() : team.getMembers().size();
+ }
+
+ private Game resolveTeamGame(Room room, Habbo habbo) {
+ if (room == null) return null;
+
+ if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
+ Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
+ if (game != null) return game;
+ }
+
+ Game wiredGame = room.getGame(WiredGame.class);
+ if (wiredGame != null) return wiredGame;
+
+ Game freezeGame = room.getGame(FreezeGame.class);
+ if (freezeGame != null) return freezeGame;
+
+ return room.getGame(BattleBanzaiGame.class);
+ }
+
+ private List parseItems(int[] ids, Room room) {
+ List items = new ArrayList<>();
+ if (ids == null || room == null) return items;
+
+ for (int id : ids) {
+ HabboItem item = room.getHabboItem(id);
+ if (item != null) items.add(item);
+ }
+
+ return items;
+ }
+
+ private List