package com.eu.habbo.habbohotel.catalog; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.achievements.AchievementManager; import com.eu.habbo.habbohotel.bots.Bot; import com.eu.habbo.habbohotel.campaign.calendar.CalendarRewardObject; import com.eu.habbo.habbohotel.catalog.layouts.*; import com.eu.habbo.habbohotel.gameclients.GameClient; import com.eu.habbo.habbohotel.guilds.Guild; import com.eu.habbo.habbohotel.items.FurnitureType; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.SoundTrack; import com.eu.habbo.habbohotel.items.interactions.*; import com.eu.habbo.habbohotel.modtool.ScripterManager; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboBadge; import com.eu.habbo.habbohotel.users.HabboGender; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.messages.outgoing.catalog.*; import com.eu.habbo.messages.outgoing.events.calendar.AdventCalendarProductComposer; import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer; import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys; import com.eu.habbo.messages.outgoing.inventory.AddBotComposer; import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer; import com.eu.habbo.messages.outgoing.inventory.AddPetComposer; import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer; import com.eu.habbo.messages.outgoing.modtool.ModToolIssueHandledComposer; import com.eu.habbo.messages.outgoing.users.AddUserBadgeComposer; import com.eu.habbo.messages.outgoing.users.UserCreditsComposer; import com.eu.habbo.messages.outgoing.users.UserPointsComposer; import com.eu.habbo.plugin.events.emulator.EmulatorLoadCatalogManagerEvent; import com.eu.habbo.plugin.events.users.catalog.UserCatalogFurnitureBoughtEvent; import com.eu.habbo.plugin.events.users.catalog.UserCatalogItemPurchasedEvent; import gnu.trove.TCollections; import gnu.trove.iterator.TIntObjectIterator; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.THashMap; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; import java.util.*; import java.util.stream.Collectors; public class CatalogManager { private static final Logger LOGGER = LoggerFactory.getLogger(CatalogManager.class); public static final THashMap> pageDefinitions = new THashMap>(CatalogPageLayouts.values().length) { { for (CatalogPageLayouts layout : CatalogPageLayouts.values()) { switch (layout) { case frontpage: this.put(layout.name().toLowerCase(), FrontpageLayout.class); break; case badge_display: this.put(layout.name().toLowerCase(), BadgeDisplayLayout.class); break; case spaces_new: this.put(layout.name().toLowerCase(), SpacesLayout.class); break; case trophies: this.put(layout.name().toLowerCase(), TrophiesLayout.class); break; case bots: this.put(layout.name().toLowerCase(), BotsLayout.class); break; case club_buy: this.put(layout.name().toLowerCase(), ClubBuyLayout.class); break; case club_gift: this.put(layout.name().toLowerCase(), ClubGiftsLayout.class); break; case sold_ltd_items: this.put(layout.name().toLowerCase(), SoldLTDItemsLayout.class); break; case single_bundle: this.put(layout.name().toLowerCase(), SingleBundle.class); break; case roomads: this.put(layout.name().toLowerCase(), RoomAdsLayout.class); break; case recycler: if (Emulator.getConfig().getBoolean("hotel.ecotron.enabled")) this.put(layout.name().toLowerCase(), RecyclerLayout.class); break; case recycler_info: if (Emulator.getConfig().getBoolean("hotel.ecotron.enabled")) this.put(layout.name().toLowerCase(), RecyclerInfoLayout.class); case recycler_prizes: if (Emulator.getConfig().getBoolean("hotel.ecotron.enabled")) this.put(layout.name().toLowerCase(), RecyclerPrizesLayout.class); break; case marketplace: if (Emulator.getConfig().getBoolean("hotel.marketplace.enabled")) this.put(layout.name().toLowerCase(), MarketplaceLayout.class); break; case marketplace_own_items: if (Emulator.getConfig().getBoolean("hotel.marketplace.enabled")) this.put(layout.name().toLowerCase(), MarketplaceOwnItems.class); break; case info_duckets: this.put(layout.name().toLowerCase(), InfoDucketsLayout.class); break; case info_pets: this.put(layout.name().toLowerCase(), InfoPetsLayout.class); break; case info_rentables: this.put(layout.name().toLowerCase(), InfoRentablesLayout.class); break; case info_loyalty: this.put(layout.name().toLowerCase(), InfoLoyaltyLayout.class); break; case loyalty_vip_buy: this.put(layout.name().toLowerCase(), LoyaltyVipBuyLayout.class); break; case guilds: this.put(layout.name().toLowerCase(), GuildFrontpageLayout.class); break; case guild_furni: this.put(layout.name().toLowerCase(), GuildFurnitureLayout.class); break; case guild_forum: this.put(layout.name().toLowerCase(), GuildForumLayout.class); break; case pets: this.put(layout.name().toLowerCase(), PetsLayout.class); break; case pets2: this.put(layout.name().toLowerCase(), Pets2Layout.class); break; case pets3: this.put(layout.name().toLowerCase(), Pets3Layout.class); break; case soundmachine: this.put(layout.name().toLowerCase(), TraxLayout.class); break; case default_3x3_color_grouping: this.put(layout.name().toLowerCase(), ColorGroupingLayout.class); break; case recent_purchases: this.put(layout.name().toLowerCase(), RecentPurchasesLayout.class); break; case room_bundle: this.put(layout.name().toLowerCase(), RoomBundleLayout.class); break; case petcustomization: this.put(layout.name().toLowerCase(), PetCustomizationLayout.class); break; case vip_buy: this.put(layout.name().toLowerCase(), VipBuyLayout.class); break; case frontpage_featured: this.put(layout.name().toLowerCase(), FrontPageFeaturedLayout.class); break; case builders_club_addons: this.put(layout.name().toLowerCase(), BuildersClubAddonsLayout.class); break; case builders_club_frontpage: this.put(layout.name().toLowerCase(), BuildersClubFrontPageLayout.class); break; case builders_club_loyalty: this.put(layout.name().toLowerCase(), BuildersClubLoyaltyLayout.class); break; case default_3x3: default: this.put("default_3x3", Default_3x3Layout.class); break; } } } }; public static int catalogItemAmount; public static int PURCHASE_COOLDOWN = 1; public static boolean SORT_USING_ORDERNUM = false; public final TIntObjectMap catalogPages; public final TIntObjectMap catalogFeaturedPages; public final THashMap> prizes; public final THashMap giftWrappers; public final THashMap giftFurnis; public final THashSet clubItems; public final THashMap clubOffers; public final THashMap targetOffers; public final THashMap clothing; public final TIntIntHashMap offerDefs; public final Item ecotronItem; public final THashMap limitedNumbers; private final List vouchers; public CatalogManager() { long millis = System.currentTimeMillis(); this.catalogPages = TCollections.synchronizedMap(new TIntObjectHashMap<>()); this.catalogFeaturedPages = new TIntObjectHashMap<>(); this.prizes = new THashMap<>(); this.giftWrappers = new THashMap<>(); this.giftFurnis = new THashMap<>(); this.clubItems = new THashSet<>(); this.clubOffers = new THashMap<>(); this.targetOffers = new THashMap<>(); this.clothing = new THashMap<>(); this.offerDefs = new TIntIntHashMap(); this.vouchers = new ArrayList<>(); this.limitedNumbers = new THashMap<>(); this.initialize(); this.ecotronItem = Emulator.getGameEnvironment().getItemManager().getItem("ecotron_box"); LOGGER.info("Catalog Manager -> Loaded! (" + (System.currentTimeMillis() - millis) + " MS)"); } public synchronized void initialize() { Emulator.getPluginManager().fireEvent(new EmulatorLoadCatalogManagerEvent()); this.loadLimitedNumbers(); this.loadCatalogPages(); this.loadCatalogFeaturedPages(); this.loadCatalogItems(); this.loadClubOffers(); this.loadTargetOffers(); this.loadVouchers(); this.loadClothing(); this.loadRecycler(); this.loadGiftWrappers(); } private synchronized void loadLimitedNumbers() { this.limitedNumbers.clear(); THashMap> limiteds = new THashMap<>(); TIntIntHashMap totals = new TIntIntHashMap(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM catalog_items_limited")) { try (ResultSet set = statement.executeQuery()) { while (set.next()) { if (!limiteds.containsKey(set.getInt("catalog_item_id"))) { limiteds.put(set.getInt("catalog_item_id"), new LinkedList<>()); } totals.adjustOrPutValue(set.getInt("catalog_item_id"), 1, 1); if (set.getInt("user_id") == 0) { limiteds.get(set.getInt("catalog_item_id")).push(set.getInt("number")); } } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } for (Map.Entry> set : limiteds.entrySet()) { this.limitedNumbers.put(set.getKey(), new CatalogLimitedConfiguration(set.getKey(), set.getValue(), totals.get(set.getKey()))); } } private synchronized void loadCatalogPages() { this.catalogPages.clear(); final THashMap pages = new THashMap<>(); pages.put(-1, new CatalogRootLayout()); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM catalog_pages ORDER BY parent_id, id")) { try (ResultSet set = statement.executeQuery()) { while (set.next()) { Class pageClazz = pageDefinitions.get(set.getString("page_layout")); if (pageClazz == null) { LOGGER.info("Unknown 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 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("Parent Page not found for " + object.getPageName() + " (ID: " + object.id + ", parent_id: " + object.parentId + ")"); } } return true; }); this.catalogPages.putAll(pages); LOGGER.info("Loaded " + this.catalogPages.size() + " Catalog Pages!"); } private synchronized void loadCatalogFeaturedPages() { this.catalogFeaturedPages.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM catalog_featured_pages ORDER BY slot_id ASC")) { while (set.next()) { this.catalogFeaturedPages.put(set.getInt("slot_id"), new CatalogFeaturedPage( set.getInt("slot_id"), set.getString("caption"), set.getString("image"), CatalogFeaturedPage.Type.valueOf(set.getString("type").toUpperCase()), set.getInt("expire_timestamp"), set.getString("page_name"), set.getInt("page_id"), set.getString("product_name") )); } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } private synchronized void loadCatalogItems() { this.clubItems.clear(); catalogItemAmount = 0; try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM catalog_items")) { CatalogItem item; while (set.next()) { if (set.getString("item_ids").equals("0")) continue; if (set.getString("catalog_name").contains("HABBO_CLUB_")) { this.clubItems.add(new CatalogItem(set)); continue; } CatalogPage page = this.catalogPages.get(set.getInt("page_id")); if (page == null) continue; item = page.getCatalogItem(set.getInt("id")); if (item == null) { catalogItemAmount++; item = new CatalogItem(set); page.addItem(item); if (item.getOfferId() != -1) { page.addOfferId(item.getOfferId()); this.offerDefs.put(item.getOfferId(), item.getId()); } } else item.update(set); if (item.isLimited()) { this.createOrUpdateLimitedConfig(item); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } for (CatalogPage page : this.catalogPages.valueCollection()) { for (Integer id : page.getIncluded()) { CatalogPage p = this.catalogPages.get(id); if (p != null) { page.getCatalogItems().putAll(p.getCatalogItems()); } } } } private void loadClubOffers() { this.clubOffers.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM catalog_club_offers WHERE enabled = ?")) { statement.setString(1, "1"); try (ResultSet set = statement.executeQuery()) { while (set.next()) { this.clubOffers.put(set.getInt("id"), new ClubOffer(set)); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } private void loadTargetOffers() { synchronized (this.targetOffers) { this.targetOffers.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM catalog_target_offers WHERE end_timestamp > ?")) { statement.setInt(1, Emulator.getIntUnixTimestamp()); try (ResultSet set = statement.executeQuery()) { while (set.next()) { this.targetOffers.put(set.getInt("id"), new TargetOffer(set)); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } private void loadVouchers() { synchronized (this.vouchers) { this.vouchers.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM vouchers")) { while (set.next()) { this.vouchers.add(new Voucher(set)); } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } public void loadRecycler() { synchronized (this.prizes) { this.prizes.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM recycler_prizes")) { while (set.next()) { Item item = Emulator.getGameEnvironment().getItemManager().getItem(set.getInt("item_id")); if (item != null) { if (this.prizes.get(set.getInt("rarity")) == null) { this.prizes.put(set.getInt("rarity"), new THashSet<>()); } this.prizes.get(set.getInt("rarity")).add(item); } else { LOGGER.error("Cannot load item with ID: {} as recycler reward!", set.getInt("item_id")); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } public void loadGiftWrappers() { synchronized (this.giftWrappers) { synchronized (this.giftFurnis) { this.giftWrappers.clear(); this.giftFurnis.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM gift_wrappers ORDER BY sprite_id DESC")) { while (set.next()) { switch (set.getString("type")) { case "wrapper": this.giftWrappers.put(set.getInt("sprite_id"), set.getInt("item_id")); break; case "gift": this.giftFurnis.put(set.getInt("sprite_id"), set.getInt("item_id")); break; } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } } private void loadClothing() { synchronized (this.clothing) { this.clothing.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM catalog_clothing")) { while (set.next()) { this.clothing.put(set.getInt("id"), new ClothItem(set)); } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } public ClothItem getClothing(String name) { for (ClothItem item : this.clothing.values()) { if (item.name.equalsIgnoreCase(name)) { return item; } } return null; } public Voucher getVoucher(String code) { synchronized (this.vouchers) { for (Voucher voucher : this.vouchers) { if (voucher.code.equals(code)) { return voucher; } } } return null; } public void redeemVoucher(GameClient client, String voucherCode) { Habbo habbo = client.getHabbo(); if (habbo == null) return; Voucher voucher = Emulator.getGameEnvironment().getCatalogManager().getVoucher(voucherCode); if (voucher == null) { client.sendResponse(new RedeemVoucherErrorComposer(RedeemVoucherErrorComposer.INVALID_CODE)); return; } if (voucher.isExhausted()) { client.sendResponse(new RedeemVoucherErrorComposer(Emulator.getGameEnvironment().getCatalogManager().deleteVoucher(voucher) ? RedeemVoucherErrorComposer.INVALID_CODE : RedeemVoucherErrorComposer.TECHNICAL_ERROR)); return; } if (voucher.hasUserExhausted(habbo.getHabboInfo().getId())) { client.sendResponse(new ModToolIssueHandledComposer("You have exceeded the limit for redeeming this voucher.")); return; } voucher.addHistoryEntry(habbo.getHabboInfo().getId()); if (voucher.points > 0) { client.getHabbo().getHabboInfo().addCurrencyAmount(voucher.pointsType, voucher.points); client.sendResponse(new UserPointsComposer(client.getHabbo().getHabboInfo().getCurrencyAmount(voucher.pointsType), voucher.points, voucher.pointsType)); } if (voucher.credits > 0) { client.getHabbo().getHabboInfo().addCredits(voucher.credits); client.sendResponse(new UserCreditsComposer(client.getHabbo())); } if (voucher.catalogItemId > 0) { CatalogItem item = this.getCatalogItem(voucher.catalogItemId); if (item != null) { this.purchaseItem(null, item, client.getHabbo(), 1, "", true); } } client.sendResponse(new RedeemVoucherOKComposer()); } public boolean deleteVoucher(Voucher voucher) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("DELETE FROM vouchers WHERE code = ?")) { statement.setString(1, voucher.code); synchronized (this.vouchers) { this.vouchers.remove(voucher); } return statement.executeUpdate() >= 1; } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } return false; } public CatalogPage getCatalogPage(int pageId) { return this.catalogPages.get(pageId); } public CatalogPage getCatalogPage(String captionSafe) { return this.catalogPages.valueCollection().stream() .filter(p -> p != null && p.getPageName() != null && p.getPageName().equalsIgnoreCase(captionSafe)) .findAny().orElse(null); } public CatalogPage getCatalogPageByLayout(String layoutName) { return this.catalogPages.valueCollection().stream() .filter(p -> p != null && p.isVisible() && p.isEnabled() && p.getRank() < 2 && p.getLayout() != null && p.getLayout().equalsIgnoreCase(layoutName) ) .findAny().orElse(null); } public CatalogItem getCatalogItem(int id) { final CatalogItem[] item = {null}; synchronized (this.catalogPages) { this.catalogPages.forEachValue(new TObjectProcedure() { @Override public boolean execute(CatalogPage object) { item[0] = object.getCatalogItem(id); return item[0] == null; } }); } return item[0]; } public List getCatalogPages(int parentId, final Habbo habbo) { final List pages = new ArrayList<>(); this.catalogPages.get(parentId).childPages.forEachValue(new TObjectProcedure() { @Override public boolean execute(CatalogPage object) { boolean isVisiblePage = object.visible; boolean hasRightRank = object.getRank() <= habbo.getHabboInfo().getRank().getId(); boolean clubRightsOkay = true; if(object.isClubOnly() && !habbo.getHabboInfo().getHabboStats().hasActiveClub()) { clubRightsOkay = false; } if (isVisiblePage && hasRightRank && clubRightsOkay) { pages.add(object); } return true; } }); Collections.sort(pages); return pages; } public TIntObjectMap getCatalogFeaturedPages() { return this.catalogFeaturedPages; } public CatalogItem getClubItem(int itemId) { synchronized (this.clubItems) { for (CatalogItem item : this.clubItems) { if (item.getId() == itemId) return item; } } return null; } public boolean moveCatalogItem(CatalogItem item, int pageId) { CatalogPage page = this.getCatalogPage(item.getPageId()); if (page == null) return false; page.getCatalogItems().remove(item.getId()); page = this.getCatalogPage(pageId); page.getCatalogItems().put(item.getId(), item); item.setPageId(pageId); item.run(); return true; } public Item getRandomRecyclerPrize() { int level = 1; if (Emulator.getRandom().nextInt(Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.5")) + 1 == Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.5")) { level = 5; } else if (Emulator.getRandom().nextInt(Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.4")) + 1 == Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.4")) { level = 4; } else if (Emulator.getRandom().nextInt(Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.3")) + 1 == Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.3")) { level = 3; } else if (Emulator.getRandom().nextInt(Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.2")) + 1 == Emulator.getConfig().getInt("hotel.ecotron.rarity.chance.2")) { level = 2; } if (this.prizes.containsKey(level) && !this.prizes.get(level).isEmpty()) { return (Item) this.prizes.get(level).toArray()[Emulator.getRandom().nextInt(this.prizes.get(level).size())]; } else { LOGGER.error("No rewards specified for rarity level {}", level); } return null; } public CatalogPage createCatalogPage(String caption, String captionSave, int roomId, int icon, CatalogPageLayouts layout, int minRank, int parentId) { 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)) { 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); statement.execute(); try (ResultSet set = statement.getGeneratedKeys()) { if (set.next()) { try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM catalog_pages WHERE id = ?")) { stmt.setInt(1, set.getInt(1)); try (ResultSet page = stmt.executeQuery()) { if (page.next()) { Class pageClazz = pageDefinitions.get(page.getString("page_layout")); if (pageClazz != null) { try { catalogPage = pageClazz.getConstructor(ResultSet.class).newInstance(page); } catch (Exception e) { LOGGER.error("Caught exception", e); } } else { LOGGER.error("Unknown page layout: {}", page.getString("page_layout")); } } } } } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } if (catalogPage != null) { this.catalogPages.put(catalogPage.getId(), catalogPage); } return catalogPage; } public CatalogLimitedConfiguration getLimitedConfig(CatalogItem item) { synchronized (this.limitedNumbers) { return this.limitedNumbers.get(item.getId()); } } public CatalogLimitedConfiguration createOrUpdateLimitedConfig(CatalogItem item) { if (item.isLimited()) { CatalogLimitedConfiguration limitedConfiguration = this.limitedNumbers.get(item.getId()); if (limitedConfiguration == null) { limitedConfiguration = new CatalogLimitedConfiguration(item.getId(), new LinkedList<>(), 0); limitedConfiguration.generateNumbers(1, item.limitedStack); this.limitedNumbers.put(item.getId(), limitedConfiguration); } else { if (limitedConfiguration.getTotalSet() != item.limitedStack) { if (limitedConfiguration.getTotalSet() == 0) { limitedConfiguration.setTotalSet(item.limitedStack); } else if (item.limitedStack > limitedConfiguration.getTotalSet()) { limitedConfiguration.generateNumbers(item.limitedStack + 1, item.limitedStack - limitedConfiguration.getTotalSet()); } else { item.limitedStack = limitedConfiguration.getTotalSet(); } } } return limitedConfiguration; } return null; } public void dispose() { TIntObjectIterator pageIterator = this.catalogPages.iterator(); while (pageIterator.hasNext()) { pageIterator.advance(); for (CatalogItem item : pageIterator.value().getCatalogItems().valueCollection()) { item.run(); if (item.isLimited()) { this.limitedNumbers.get(item.getId()).run(); } } } LOGGER.info("Catalog Manager -> Disposed!"); } public void purchaseItem(CatalogPage page, CatalogItem item, Habbo habbo, int amount, String extradata, boolean free) { Item cBaseItem = null; if (item == null || habbo.getHabboStats().isPurchasingFurniture) { habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR).compose()); return; } habbo.getHabboStats().isPurchasingFurniture = true; try { if (item.isClubOnly() && !habbo.getClient().getHabbo().getHabboStats().hasActiveClub()) { habbo.getClient().sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.REQUIRES_CLUB)); return; } if (amount <= 0) { habbo.getClient().sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL)); return; } try { CatalogLimitedConfiguration limitedConfiguration = null; int limitedStack = 0; int limitedNumber = 0; if (item.isLimited()) { amount = 1; if (this.getLimitedConfig(item).available() == 0) { habbo.getClient().sendResponse(new AlertLimitedSoldOutComposer()); return; } if (Emulator.getConfig().getBoolean("hotel.catalog.ltd.limit.enabled")) { int ltdLimit = Emulator.getConfig().getInt("hotel.purchase.ltd.limit.daily.total"); if (habbo.getHabboStats().totalLtds() >= ltdLimit) { habbo.alert(Emulator.getTexts().getValue("error.catalog.buy.limited.daily.total").replace("%itemname%", item.getBaseItems().iterator().next().getFullName()).replace("%limit%", ltdLimit + "")); return; } ltdLimit = Emulator.getConfig().getInt("hotel.purchase.ltd.limit.daily.item"); if (habbo.getHabboStats().totalLtds(item.id) >= ltdLimit) { habbo.alert(Emulator.getTexts().getValue("error.catalog.buy.limited.daily.item").replace("%itemname%", item.getBaseItems().iterator().next().getFullName()).replace("%limit%", ltdLimit + "")); return; } } } if (amount > 1) { if (amount == item.getAmount()) { amount = 1; } else { if (amount * item.getAmount() > 100) { habbo.alert("Whoops! You tried to buy this " + (amount * item.getAmount()) + " times. This must've been a mistake."); habbo.getClient().sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL)); return; } } } THashSet itemsList = new THashSet<>(); if (amount > 1 && !CatalogItem.haveOffer(item)) { String message = Emulator.getTexts().getValue("scripter.warning.catalog.amount").replace("%username%", habbo.getHabboInfo().getUsername()).replace("%itemname%", item.getName()).replace("%pagename%", page.getCaption()); ScripterManager.scripterDetected(habbo.getClient(), message); LOGGER.info(message); habbo.getClient().sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL)); return; } if (item.isLimited()) { limitedConfiguration = this.getLimitedConfig(item); if (limitedConfiguration == null) { limitedConfiguration = this.createOrUpdateLimitedConfig(item); } limitedNumber = limitedConfiguration.getNumber(); limitedStack = limitedConfiguration.getTotalSet(); } int totalCredits = free ? 0 : this.calculateDiscountedPrice(item.getCredits(), amount, item); int totalPoints = free ? 0 : this.calculateDiscountedPrice(item.getPoints(), amount, item); if (totalCredits > 0 && habbo.getHabboInfo().getCredits() - totalCredits < 0) return; if (totalPoints > 0 && habbo.getHabboInfo().getCurrencyAmount(item.getPointsType()) - totalPoints < 0) return; List badges = new ArrayList<>(); Map> unseenItems = new HashMap<>(); boolean badgeFound = false; for (int i = 0; i < amount; i++) { habbo.getHabboStats().addLtdLog(item.getId(), Emulator.getIntUnixTimestamp()); for (Item baseItem : item.getBaseItems()) { for (int k = 0; k < item.getItemAmount(baseItem.getId()); k++) { if (baseItem.getName().startsWith("rentable_bot_") || baseItem.getName().startsWith("bot_")) { String type = item.getName().replace("rentable_bot_", ""); type = type.replace("bot_", ""); type = type.replace("visitor_logger", "visitor_log"); THashMap data = new THashMap<>(); for (String s : item.getExtradata().split(";")) { if (s.contains(":")) { data.put(s.split(":")[0], s.split(":")[1]); } } Bot bot = Emulator.getGameEnvironment().getBotManager().createBot(data, type); if (bot != null) { bot.setOwnerId(habbo.getClient().getHabbo().getHabboInfo().getId()); bot.setOwnerName(habbo.getClient().getHabbo().getHabboInfo().getUsername()); bot.needsUpdate(true); Emulator.getThreading().run(bot); habbo.getClient().getHabbo().getInventory().getBotsComponent().addBot(bot); habbo.getClient().sendResponse(new AddBotComposer(bot)); if (!unseenItems.containsKey(AddHabboItemComposer.AddHabboItemCategory.BOT)) { unseenItems.put(AddHabboItemComposer.AddHabboItemCategory.BOT, new ArrayList<>()); } unseenItems.get(AddHabboItemComposer.AddHabboItemCategory.BOT).add(bot.getId()); } else { throw new Exception("Failed to create bot of type: " + type); } } else if (baseItem.getType() == FurnitureType.EFFECT) { int effectId = baseItem.getEffectM(); if (habbo.getHabboInfo().getGender().equals(HabboGender.F)) { effectId = baseItem.getEffectF(); } if (effectId > 0) { habbo.getInventory().getEffectsComponent().createEffect(effectId); } } else if (Item.isPet(baseItem)) { String[] data = extradata.split("\n"); if (data.length < 3) { habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); return; } Pet pet = null; try { pet = Emulator.getGameEnvironment().getPetManager().createPet(baseItem, data[0], data[1], data[2], habbo.getClient()); } catch (Exception e) { LOGGER.error("Caught exception", e); habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); } if (pet == null) { habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); return; } habbo.getClient().getHabbo().getInventory().getPetsComponent().addPet(pet); habbo.getClient().sendResponse(new AddPetComposer(pet)); habbo.getClient().sendResponse(new PetBoughtNotificationComposer(pet, false)); AchievementManager.progressAchievement(habbo.getClient().getHabbo(), Emulator.getGameEnvironment().getAchievementManager().getAchievement("PetLover")); if (!unseenItems.containsKey(AddHabboItemComposer.AddHabboItemCategory.PET)) { unseenItems.put(AddHabboItemComposer.AddHabboItemCategory.PET, new ArrayList<>()); } unseenItems.get(AddHabboItemComposer.AddHabboItemCategory.PET).add(pet.getId()); } else if (baseItem.getType() == FurnitureType.BADGE) { if (!habbo.getInventory().getBadgesComponent().hasBadge(baseItem.getName())) { if (!badges.contains(baseItem.getName())) { badges.add(baseItem.getName()); } } else { badgeFound = true; } } else { if (baseItem.getInteractionType().getType() == InteractionTrophy.class || baseItem.getInteractionType().getType() == InteractionBadgeDisplay.class) { if (baseItem.getInteractionType().getType() == InteractionBadgeDisplay.class && !habbo.getClient().getHabbo().getInventory().getBadgesComponent().hasBadge(extradata)) { ScripterManager.scripterDetected(habbo.getClient(), Emulator.getTexts().getValue("scripter.warning.catalog.badge_display").replace("%username%", habbo.getClient().getHabbo().getHabboInfo().getUsername()).replace("%badge%", extradata)); extradata = "UMAD"; } 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); } if (InteractionTeleport.class.isAssignableFrom(baseItem.getInteractionType().getType())) { HabboItem teleportOne = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, extradata); HabboItem teleportTwo = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, extradata); Emulator.getGameEnvironment().getItemManager().insertTeleportPair(teleportOne.getId(), teleportTwo.getId()); itemsList.add(teleportOne); itemsList.add(teleportTwo); } else if (baseItem.getInteractionType().getType() == InteractionHopper.class) { HabboItem hopper = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, extradata); Emulator.getGameEnvironment().getItemManager().insertHopper(hopper); itemsList.add(hopper); } else if (baseItem.getInteractionType().getType() == InteractionGuildFurni.class || baseItem.getInteractionType().getType() == InteractionGuildGate.class) { int guildId; try { guildId = Integer.parseInt(extradata); } catch (Exception e) { LOGGER.error("Caught exception", e); habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); return; } Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId); if (guild != null && Emulator.getGameEnvironment().getGuildManager().getGuildMember(guild, habbo) != null) { InteractionGuildFurni habboItem = (InteractionGuildFurni) Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, extradata); habboItem.setExtradata(""); habboItem.needsUpdate(true); Emulator.getThreading().run(habboItem); Emulator.getGameEnvironment().getGuildManager().setGuild(habboItem, guildId); itemsList.add(habboItem); if (baseItem.getName().equals("guild_forum")) { guild.setForum(true); guild.needsUpdate = true; guild.run(); } } } else if (baseItem.getInteractionType().getType() == InteractionMusicDisc.class) { SoundTrack track = Emulator.getGameEnvironment().getItemManager().getSoundTrack(item.getExtradata()); if (track == null) { habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); return; } InteractionMusicDisc habboItem = (InteractionMusicDisc) Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, habbo.getClient().getHabbo().getHabboInfo().getUsername() + "\n" + Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "\n" + (Calendar.getInstance().get(Calendar.MONTH) + 1) + "\n" + Calendar.getInstance().get(Calendar.YEAR) + "\n" + track.getLength() + "\n" + track.getName() + "\n" + track.getId()); habboItem.needsUpdate(true); Emulator.getThreading().run(habboItem); itemsList.add(habboItem); AchievementManager.progressAchievement(habbo, Emulator.getGameEnvironment().getAchievementManager().getAchievement("MusicCollector")); } else { HabboItem habboItem = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getClient().getHabbo().getHabboInfo().getId(), baseItem, limitedStack, limitedNumber, extradata); itemsList.add(habboItem); } } } } } if (badgeFound && item.getBaseItems().size() == 1) { habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.ALREADY_HAVE_BADGE)); return; } UserCatalogItemPurchasedEvent purchasedEvent = new UserCatalogItemPurchasedEvent(habbo, item, itemsList, totalCredits, totalPoints, badges); Emulator.getPluginManager().fireEvent(purchasedEvent); if (!free && !habbo.getClient().getHabbo().hasPermission(Permission.ACC_INFINITE_CREDITS)) { if (purchasedEvent.totalCredits > 0) { habbo.getClient().getHabbo().getHabboInfo().addCredits(-purchasedEvent.totalCredits); habbo.getClient().sendResponse(new UserCreditsComposer(habbo.getClient().getHabbo())); } } if (!free && !habbo.getClient().getHabbo().hasPermission(Permission.ACC_INFINITE_POINTS)) { if (purchasedEvent.totalPoints > 0) { habbo.getClient().getHabbo().getHabboInfo().addCurrencyAmount(item.getPointsType(), -purchasedEvent.totalPoints); habbo.getClient().sendResponse(new UserPointsComposer(habbo.getClient().getHabbo().getHabboInfo().getCurrencyAmount(item.getPointsType()), -purchasedEvent.totalPoints, item.getPointsType())); } } if (purchasedEvent.itemsList != null && !purchasedEvent.itemsList.isEmpty()) { habbo.getClient().getHabbo().getInventory().getItemsComponent().addItems(purchasedEvent.itemsList); unseenItems.put(AddHabboItemComposer.AddHabboItemCategory.OWNED_FURNI, purchasedEvent.itemsList.stream().map(HabboItem::getId).collect(Collectors.toList())); Emulator.getPluginManager().fireEvent(new UserCatalogFurnitureBoughtEvent(habbo, item, purchasedEvent.itemsList)); if (limitedConfiguration != null) { for (HabboItem itm : purchasedEvent.itemsList) { limitedConfiguration.limitedSold(item.getId(), habbo, itm); } } } if (!purchasedEvent.badges.isEmpty() && !unseenItems.containsKey(AddHabboItemComposer.AddHabboItemCategory.BADGE)) { unseenItems.put(AddHabboItemComposer.AddHabboItemCategory.BADGE, new ArrayList<>()); } for (String b : purchasedEvent.badges) { HabboBadge badge = new HabboBadge(0, b, 0, habbo); Emulator.getThreading().run(badge); habbo.getInventory().getBadgesComponent().addBadge(badge); habbo.getClient().sendResponse(new AddUserBadgeComposer(badge)); THashMap keys = new THashMap<>(); keys.put("display", "BUBBLE"); keys.put("image", "${image.library.url}album1584/" + badge.getCode() + ".gif"); keys.put("message", Emulator.getTexts().getValue("commands.generic.cmd_badge.received")); habbo.getClient().sendResponse(new BubbleAlertComposer(BubbleAlertKeys.RECEIVED_BADGE.key, keys)); unseenItems.get(AddHabboItemComposer.AddHabboItemCategory.BADGE).add(badge.getId()); } habbo.getClient().getHabbo().getHabboStats().addPurchase(purchasedEvent.catalogItem); habbo.getClient().sendResponse(new AddHabboItemComposer(unseenItems)); habbo.getClient().sendResponse(new PurchaseOKComposer(purchasedEvent.catalogItem)); habbo.getClient().sendResponse(new InventoryRefreshComposer()); THashSet itemIds = new THashSet<>(); for(HabboItem ix : purchasedEvent.itemsList) { itemIds.add(ix.getId() + ""); } if(!free) { Emulator.getThreading().run(new CatalogPurchaseLogEntry( Emulator.getIntUnixTimestamp(), purchasedEvent.habbo.getHabboInfo().getId(), purchasedEvent.catalogItem != null ? purchasedEvent.catalogItem.getId() : 0, String.join(";", itemIds), purchasedEvent.catalogItem != null ? purchasedEvent.catalogItem.getName() : "", purchasedEvent.totalCredits, purchasedEvent.totalPoints, item != null ? item.getPointsType() : 0, amount )); } } catch (Exception e) { LOGGER.error("Exception caught", e); habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); } } finally { habbo.getHabboStats().isPurchasingFurniture = false; } } public List getClubOffers() { List offers = new ArrayList<>(); for (Map.Entry entry : this.clubOffers.entrySet()) { if (!entry.getValue().isDeal()) { offers.add(entry.getValue()); } } return offers; } public TargetOffer getTargetOffer(int offerId) { return this.targetOffers.get(offerId); } private int calculateDiscountedPrice(int originalPrice, int amount, CatalogItem item) { if (!CatalogItem.haveOffer(item)) return originalPrice * amount; int basicDiscount = amount / DiscountComposer.DISCOUNT_BATCH_SIZE; int bonusDiscount = 0; if (basicDiscount >= DiscountComposer.MINIMUM_DISCOUNTS_FOR_BONUS) { if (amount % DiscountComposer.DISCOUNT_BATCH_SIZE == DiscountComposer.DISCOUNT_BATCH_SIZE - 1) { bonusDiscount = 1; } bonusDiscount += basicDiscount - DiscountComposer.MINIMUM_DISCOUNTS_FOR_BONUS; } int additionalDiscounts = 0; for (int threshold : DiscountComposer.ADDITIONAL_DISCOUNT_THRESHOLDS) { if (amount >= threshold) additionalDiscounts++; } int totalDiscountedItems = (basicDiscount * DiscountComposer.DISCOUNT_AMOUNT_PER_BATCH) + bonusDiscount + additionalDiscounts; return Math.max(0, originalPrice * (amount - totalDiscountedItems)); } }