You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8fe1e3e22 | |||
| be77cdf4aa | |||
| 1ba2e43d4d | |||
| 4f4f581371 | |||
| 9705b3e42a | |||
| e626a7fc50 | |||
| d6ebb632e6 | |||
| 014ca9ca48 | |||
| d189d66f9e | |||
| c272a36cc5 | |||
| 10a2b2b872 | |||
| 458b37dbed |
@@ -49,15 +49,15 @@ INSERT INTO `bot_chat_responses` (`bot_type`, `keys`, `responses`) VALUES
|
|||||||
('frank', 'pills;drugs', 'Drugs are bad, mmkay?');
|
('frank', 'pills;drugs', 'Drugs are bad, mmkay?');
|
||||||
|
|
||||||
INSERT IGNORE INTO `bot_serves` (`keys`, `item`) VALUES
|
INSERT IGNORE INTO `bot_serves` (`keys`, `item`) VALUES
|
||||||
('sunflower', 21),
|
('sunflower', 1002),
|
||||||
('cola;habbo cola', 32),
|
('cola;habbo cola', 19),
|
||||||
('rose', 1000),
|
('rose', 1000),
|
||||||
('book', 20),
|
('book', 1003),
|
||||||
('tea', 6),
|
('tea', 27),
|
||||||
('coffee', 1),
|
('coffee', 8),
|
||||||
('migraine;headache;pills', 34),
|
('migraine;headache;pills', 1015),
|
||||||
('radioactive liquid;radioactive', 36),
|
('radioactive liquid;radioactive', 30),
|
||||||
('turkey;can of turkey', 38);
|
('turkey;can of turkey', 70);
|
||||||
|
|
||||||
-- VERY IMPORTANT !!!!
|
-- VERY IMPORTANT !!!!
|
||||||
-- First check if the items_base ID and catalog_items ID is not in use !
|
-- First check if the items_base ID and catalog_items ID is not in use !
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
-- Soundboard
|
||||||
|
-- The room flag column + sounds table are also created at boot by
|
||||||
|
|
||||||
|
ALTER TABLE `rooms` ADD COLUMN IF NOT EXISTS `soundboard_enabled` TINYINT(1) NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `soundboard_sounds` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(64) NOT NULL DEFAULT '', -- pad label shown in the client
|
||||||
|
`url` VARCHAR(255) NOT NULL DEFAULT '', -- audio url (uploaded via CMS, like custom badges)
|
||||||
|
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
`sort_order` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
|
||||||
|
-- Fortune Wheel
|
||||||
|
-- Tables are also created at boot by WheelManager (CREATE TABLE IF NOT EXISTS),
|
||||||
|
-- so applying this file is only needed to seed prizes + settings.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `wheel_prizes` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`type` VARCHAR(16) NOT NULL DEFAULT 'nothing', -- item | badge | credits | points | spin | nothing
|
||||||
|
`value` VARCHAR(64) NOT NULL DEFAULT '', -- item: base item id ; badge: badge code ; others: unused
|
||||||
|
`amount` INT(11) NOT NULL DEFAULT 1, -- item qty / credits / points / extra spins
|
||||||
|
`points_type` INT(11) NOT NULL DEFAULT 5, -- for type=points (diamond default 5)
|
||||||
|
`weight` INT(11) NOT NULL DEFAULT 1, -- relative probability
|
||||||
|
`label` VARCHAR(64) NOT NULL DEFAULT '', -- slice label override (optional)
|
||||||
|
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
`sort_order` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `wheel_user_state` (
|
||||||
|
`user_id` INT(11) NOT NULL,
|
||||||
|
`free_spins` INT(11) NOT NULL DEFAULT 0, -- remaining free spins for the current day
|
||||||
|
`extra_spins` INT(11) NOT NULL DEFAULT 0, -- bought / won spins
|
||||||
|
`last_reset` INT(11) NOT NULL DEFAULT 0, -- day index of last daily reset (unix / 86400)
|
||||||
|
PRIMARY KEY (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `wheel_recent_wins` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` INT(11) NOT NULL,
|
||||||
|
`username` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`look` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
`prize_label` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`won_at` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_wheel_recent_wins_id` (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
||||||
|
('wheel.free_spins_per_day', '1', 'Fortune wheel: free spins granted each day.')
|
||||||
|
ON DUPLICATE KEY UPDATE `comment` = VALUES(`comment`);
|
||||||
|
INSERT INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
||||||
|
('wheel.spin_cost', '50', 'Fortune wheel: cost of one extra spin.')
|
||||||
|
ON DUPLICATE KEY UPDATE `comment` = VALUES(`comment`);
|
||||||
|
INSERT INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
||||||
|
('wheel.spin_cost_type', '5', 'Fortune wheel: currency type for the spin cost (5 = diamonds; -1 = credits).')
|
||||||
|
ON DUPLICATE KEY UPDATE `comment` = VALUES(`comment`);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `wheel_prizes` (`type`, `amount`, `points_type`, `weight`, `label`, `sort_order`) VALUES
|
||||||
|
('points',25, 5, 20, '25 diamonds',1),
|
||||||
|
('points',50, 5, 12, '50 diamonds',2),
|
||||||
|
('points',200, 5, 3, '200 diamonds',3),
|
||||||
|
('credits',100, 0, 15, '100 credits',4),
|
||||||
|
('spin',1, 0, 15, '1 Extra spin', 5),
|
||||||
|
('spin',2, 0, 6, '2 Extra spins',6),
|
||||||
|
('nothing',0, 0, 29, 'Oh to bad!',7);
|
||||||
|
|
||||||
|
INSERT INTO `permission_definitions`
|
||||||
|
(`permission_key`, `max_value`, `comment`,
|
||||||
|
`rank_1`, `rank_2`, `rank_3`, `rank_4`, `rank_5`, `rank_6`, `rank_7`)
|
||||||
|
VALUES
|
||||||
|
('acc_wheeladmin', 1, 'Required to open the Fortune Wheel settings popup and edit prize rows.',
|
||||||
|
0, 0, 0, 0, 0, 0, 1)
|
||||||
|
ON DUPLICATE KEY UPDATE `comment` = VALUES(`comment`);
|
||||||
+2
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.eu.habbo</groupId>
|
<groupId>com.eu.habbo</groupId>
|
||||||
<artifactId>Habbo</artifactId>
|
<artifactId>Habbo</artifactId>
|
||||||
<version>4.2.23</version>
|
<version>4.2.25</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.eu.habbo.Emulator</mainClass>
|
<mainClass>com.eu.habbo.Emulator</mainClass>
|
||||||
|
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -39,12 +39,23 @@ public final class Emulator {
|
|||||||
private static final String OS_NAME = (System.getProperty("os.name") != null ? System.getProperty("os.name") : "Unknown");
|
private static final String OS_NAME = (System.getProperty("os.name") != null ? System.getProperty("os.name") : "Unknown");
|
||||||
private static final String CLASS_PATH = (System.getProperty("java.class.path") != null ? System.getProperty("java.class.path") : "Unknown");
|
private static final String CLASS_PATH = (System.getProperty("java.class.path") != null ? System.getProperty("java.class.path") : "Unknown");
|
||||||
|
|
||||||
|
// Fallback version, only used when running outside a packaged jar (e.g. from
|
||||||
|
// the IDE). In production the version comes from the jar manifest below.
|
||||||
public final static int MAJOR = 4;
|
public final static int MAJOR = 4;
|
||||||
public final static int MINOR = 1;
|
public final static int MINOR = 1;
|
||||||
public final static int BUILD = 0;
|
public final static int BUILD = 0;
|
||||||
public final static String PREVIEW = "";
|
public final static String PREVIEW = "";
|
||||||
|
|
||||||
public static final String version = "Arcturus Morningstar" + " " + MAJOR + "." + MINOR + "." + BUILD + " " + PREVIEW;
|
// Tied to the Maven project version: read from the jar manifest
|
||||||
|
// (Implementation-Version = ${project.version}, see pom assembly plugin).
|
||||||
|
private static String resolveVersionNumber() {
|
||||||
|
String implementation = Emulator.class.getPackage().getImplementationVersion();
|
||||||
|
if (implementation != null && !implementation.isEmpty()) return implementation;
|
||||||
|
String fallback = MAJOR + "." + MINOR + "." + BUILD;
|
||||||
|
return PREVIEW.isEmpty() ? fallback : fallback + " " + PREVIEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String version = "Arcturus Morningstar Extended " + resolveVersionNumber();
|
||||||
private static final String logo =
|
private static final String logo =
|
||||||
"\n" +
|
"\n" +
|
||||||
"███╗ ███╗ ██████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗████████╗ █████╗ ██████╗ \n" +
|
"███╗ ███╗ ██████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗████████╗ █████╗ ██████╗ \n" +
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import com.eu.habbo.habbohotel.achievements.AchievementManager;
|
|||||||
import com.eu.habbo.habbohotel.bots.BotManager;
|
import com.eu.habbo.habbohotel.bots.BotManager;
|
||||||
import com.eu.habbo.habbohotel.campaign.calendar.CalendarManager;
|
import com.eu.habbo.habbohotel.campaign.calendar.CalendarManager;
|
||||||
import com.eu.habbo.habbohotel.catalog.CatalogManager;
|
import com.eu.habbo.habbohotel.catalog.CatalogManager;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
|
import com.eu.habbo.habbohotel.soundboard.SoundboardManager;
|
||||||
import com.eu.habbo.habbohotel.commands.CommandHandler;
|
import com.eu.habbo.habbohotel.commands.CommandHandler;
|
||||||
import com.eu.habbo.habbohotel.crafting.CraftingManager;
|
import com.eu.habbo.habbohotel.crafting.CraftingManager;
|
||||||
import com.eu.habbo.habbohotel.guides.GuideManager;
|
import com.eu.habbo.habbohotel.guides.GuideManager;
|
||||||
@@ -64,6 +66,8 @@ public class GameEnvironment {
|
|||||||
private GoogleTranslateManager googleTranslateManager;
|
private GoogleTranslateManager googleTranslateManager;
|
||||||
private CustomBadgeManager customBadgeManager;
|
private CustomBadgeManager customBadgeManager;
|
||||||
private InfostandBackgroundManager infostandBackgroundManager;
|
private InfostandBackgroundManager infostandBackgroundManager;
|
||||||
|
private WheelManager wheelManager;
|
||||||
|
private SoundboardManager soundboardManager;
|
||||||
|
|
||||||
public void load() throws Exception {
|
public void load() throws Exception {
|
||||||
LOGGER.info("GameEnvironment -> Loading...");
|
LOGGER.info("GameEnvironment -> Loading...");
|
||||||
@@ -93,6 +97,8 @@ public class GameEnvironment {
|
|||||||
this.googleTranslateManager = new GoogleTranslateManager();
|
this.googleTranslateManager = new GoogleTranslateManager();
|
||||||
this.customBadgeManager = new CustomBadgeManager();
|
this.customBadgeManager = new CustomBadgeManager();
|
||||||
this.infostandBackgroundManager = new InfostandBackgroundManager();
|
this.infostandBackgroundManager = new InfostandBackgroundManager();
|
||||||
|
this.wheelManager = new WheelManager();
|
||||||
|
this.soundboardManager = new SoundboardManager();
|
||||||
|
|
||||||
this.roomManager.loadPublicRooms();
|
this.roomManager.loadPublicRooms();
|
||||||
this.navigatorManager.loadNavigator();
|
this.navigatorManager.loadNavigator();
|
||||||
@@ -156,6 +162,14 @@ public class GameEnvironment {
|
|||||||
return this.catalogManager;
|
return this.catalogManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WheelManager getWheelManager() {
|
||||||
|
return this.wheelManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundboardManager getSoundboardManager() {
|
||||||
|
return this.soundboardManager;
|
||||||
|
}
|
||||||
|
|
||||||
public HotelViewManager getHotelViewManager() {
|
public HotelViewManager getHotelViewManager() {
|
||||||
return this.hotelViewManager;
|
return this.hotelViewManager;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,11 +189,7 @@ public class Bot implements Runnable {
|
|||||||
int timeOut = Emulator.getRandom().nextInt(20) * 2;
|
int timeOut = Emulator.getRandom().nextInt(20) * 2;
|
||||||
this.roomUnit.setWalkTimeOut((timeOut < 10 ? 5 : timeOut) + Emulator.getIntUnixTimestamp());
|
this.roomUnit.setWalkTimeOut((timeOut < 10 ? 5 : timeOut) + Emulator.getIntUnixTimestamp());
|
||||||
}
|
}
|
||||||
}/* else {
|
}
|
||||||
for (RoomTile t : this.room.getLayout().getTilesAround(this.room.getLayout().getTile(this.getRoomUnit().getX(), this.getRoomUnit().getY()))) {
|
|
||||||
WiredManager.handle(WiredTriggerType.BOT_REACHED_STF, this.roomUnit, this.room, this.room.getItemsAt(t).toArray());
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.chatLines.isEmpty() && this.chatTimeOut <= Emulator.getIntUnixTimestamp() && this.chatAuto) {
|
if (!this.chatLines.isEmpty() && this.chatTimeOut <= Emulator.getIntUnixTimestamp() && this.chatAuto) {
|
||||||
@@ -218,7 +214,7 @@ public class Bot implements Runnable {
|
|||||||
} else {
|
} else {
|
||||||
this.lastChatIndex++;
|
this.lastChatIndex++;
|
||||||
if (this.lastChatIndex >= this.chatLines.size()) {
|
if (this.lastChatIndex >= this.chatLines.size()) {
|
||||||
this.lastChatIndex = 0; // start from scratch :-3
|
this.lastChatIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,9 +306,6 @@ public class Bot implements Runnable {
|
|||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.needsUpdate = true;
|
this.needsUpdate = true;
|
||||||
|
|
||||||
//if(this.room != null)
|
|
||||||
//this.room.sendComposer(new ChangeNameUpdatedComposer(this.getRoomUnit(), this.getName()).compose());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMotto() {
|
public String getMotto() {
|
||||||
@@ -539,9 +532,28 @@ public class Bot implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final short[] DEFAULT_OWNER_ACTION_IDS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
private static final short[] DEFAULT_OWNER_ACTION_IDS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11};
|
||||||
|
|
||||||
|
public static final int ACTION_ROTATE = 11;
|
||||||
|
|
||||||
|
private static final long MIN_OWNER_ACTION_INTERVAL_MS = 200L;
|
||||||
|
|
||||||
|
private volatile long lastOwnerActionAt;
|
||||||
|
|
||||||
public short[] getOwnerActionIds() {
|
public short[] getOwnerActionIds() {
|
||||||
return DEFAULT_OWNER_ACTION_IDS;
|
return DEFAULT_OWNER_ACTION_IDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean tryAcquireOwnerActionSlot() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - this.lastOwnerActionAt < MIN_OWNER_ACTION_INTERVAL_MS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.lastOwnerActionAt = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPostOwnerAction(int actionId) {
|
||||||
|
// no-op default
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class FrankBot extends ButlerBot {
|
public class FrankBot extends ButlerBot {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(FrankBot.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(FrankBot.class);
|
||||||
|
|
||||||
public static final String BOT_TYPE = "frank";
|
public static final String BOT_TYPE = "frank";
|
||||||
public static final String PERMISSION_USE = "acc_bot_frank";
|
public static final String PERMISSION_USE = "acc_bot_frank";
|
||||||
private static final String KEY_DOOR_LINES = "__door_lines";
|
private static final String KEY_DOOR_LINES = "__door_lines";
|
||||||
@@ -75,13 +74,20 @@ public class FrankBot extends ButlerBot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final short[] FRANK_OWNER_ACTIONS = new short[0];
|
private static final short[] FRANK_OWNER_ACTIONS = { (short) Bot.ACTION_ROTATE };
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public short[] getOwnerActionIds() {
|
public short[] getOwnerActionIds() {
|
||||||
return FRANK_OWNER_ACTIONS;
|
return FRANK_OWNER_ACTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostOwnerAction(int actionId) {
|
||||||
|
if (actionId == ACTION_ROTATE && this.getRoomUnit() != null) {
|
||||||
|
this.homeRotation = this.getRoomUnit().getBodyRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void initialise() {
|
public static void initialise() {
|
||||||
chatResponses.clear();
|
chatResponses.clear();
|
||||||
doorLines = DEFAULT_DOOR_LINES;
|
doorLines = DEFAULT_DOOR_LINES;
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ public class CatalogManager {
|
|||||||
public final Item ecotronItem;
|
public final Item ecotronItem;
|
||||||
public final THashMap<Integer, CatalogLimitedConfiguration> limitedNumbers;
|
public final THashMap<Integer, CatalogLimitedConfiguration> limitedNumbers;
|
||||||
private final List<Voucher> vouchers;
|
private final List<Voucher> vouchers;
|
||||||
|
// spriteId -> [credits, points, pointsType], derived from catalog_items (see loadFurnitureValues)
|
||||||
|
public final TIntObjectMap<int[]> furnitureValues;
|
||||||
|
|
||||||
public CatalogManager() {
|
public CatalogManager() {
|
||||||
long millis = System.currentTimeMillis();
|
long millis = System.currentTimeMillis();
|
||||||
@@ -219,6 +221,7 @@ public class CatalogManager {
|
|||||||
this.buildersClubOfferDefs = new TIntIntHashMap();
|
this.buildersClubOfferDefs = new TIntIntHashMap();
|
||||||
this.vouchers = new ArrayList<>();
|
this.vouchers = new ArrayList<>();
|
||||||
this.limitedNumbers = new THashMap<>();
|
this.limitedNumbers = new THashMap<>();
|
||||||
|
this.furnitureValues = new TIntObjectHashMap<>();
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
|
||||||
@@ -243,6 +246,56 @@ public class CatalogManager {
|
|||||||
this.loadClothing();
|
this.loadClothing();
|
||||||
this.loadRecycler();
|
this.loadRecycler();
|
||||||
this.loadGiftWrappers();
|
this.loadGiftWrappers();
|
||||||
|
this.loadFurnitureValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds spriteId -> [credits, points, pointsType] from catalog_items so the
|
||||||
|
// client can show a furni's "value" (toolbar price guide + infostand line).
|
||||||
|
// Only single-item, single-amount FLOOR/WALL sales are considered, so bundles
|
||||||
|
// and multi-packs don't pollute the per-rare price. First clean entry wins.
|
||||||
|
private synchronized void loadFurnitureValues() {
|
||||||
|
this.furnitureValues.clear();
|
||||||
|
final int diamondType = Emulator.getConfig().getInt("seasonal.currency.diamond", 5);
|
||||||
|
|
||||||
|
for (CatalogPage page : this.catalogPages.valueCollection()) {
|
||||||
|
for (CatalogItem catalogItem : page.getCatalogItems().valueCollection()) {
|
||||||
|
if (catalogItem.getAmount() != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int credits = catalogItem.getCredits();
|
||||||
|
int points = catalogItem.getPoints();
|
||||||
|
int pointsType = catalogItem.getPointsType();
|
||||||
|
|
||||||
|
// Only diamond-priced items — both the "Valore Rari" panel and the
|
||||||
|
// infostand value line show diamonds only.
|
||||||
|
if (points <= 0 || pointsType != diamondType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
THashSet<Item> baseItems = catalogItem.getBaseItems();
|
||||||
|
|
||||||
|
if (baseItems.size() != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (Item item : baseItems) {
|
||||||
|
FurnitureType type = item.getType();
|
||||||
|
|
||||||
|
if (type != FurnitureType.FLOOR && type != FurnitureType.WALL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int spriteId = item.getSpriteId();
|
||||||
|
|
||||||
|
if (spriteId > 0 && !this.furnitureValues.containsKey(spriteId)) {
|
||||||
|
this.furnitureValues.put(spriteId, new int[]{credits, points, pointsType});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Furniture Values -> Loaded! ({} entries)", this.furnitureValues.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TIntObjectMap<int[]> getFurnitureValues() {
|
||||||
|
return this.furnitureValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void loadLimitedNumbers() {
|
private synchronized void loadLimitedNumbers() {
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
|||||||
private int wiredInspectMask = WIRED_ACCESS_DEFAULT_INSPECT_MASK;
|
private int wiredInspectMask = WIRED_ACCESS_DEFAULT_INSPECT_MASK;
|
||||||
private int wiredModifyMask = WIRED_ACCESS_DEFAULT_MODIFY_MASK;
|
private int wiredModifyMask = WIRED_ACCESS_DEFAULT_MODIFY_MASK;
|
||||||
private boolean youtubeEnabled = false;
|
private boolean youtubeEnabled = false;
|
||||||
|
private boolean soundboardEnabled = false;
|
||||||
private String youtubeCurrentVideo = "";
|
private String youtubeCurrentVideo = "";
|
||||||
private String youtubeSenderName = "";
|
private String youtubeSenderName = "";
|
||||||
private final java.util.List<String> youtubePlaylist = new java.util.concurrent.CopyOnWriteArrayList<>();
|
private final java.util.List<String> youtubePlaylist = new java.util.concurrent.CopyOnWriteArrayList<>();
|
||||||
@@ -204,6 +205,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
|||||||
|
|
||||||
public boolean isYoutubeEnabled() { return this.youtubeEnabled; }
|
public boolean isYoutubeEnabled() { return this.youtubeEnabled; }
|
||||||
public void setYoutubeEnabled(boolean enabled) { this.youtubeEnabled = enabled; }
|
public void setYoutubeEnabled(boolean enabled) { this.youtubeEnabled = enabled; }
|
||||||
|
public boolean isSoundboardEnabled() { return this.soundboardEnabled; }
|
||||||
|
public void setSoundboardEnabled(boolean enabled) { this.soundboardEnabled = enabled; }
|
||||||
public String getYoutubeCurrentVideo() { return this.youtubeCurrentVideo; }
|
public String getYoutubeCurrentVideo() { return this.youtubeCurrentVideo; }
|
||||||
public String getYoutubeSenderName() { return this.youtubeSenderName; }
|
public String getYoutubeSenderName() { return this.youtubeSenderName; }
|
||||||
public java.util.List<String> getYoutubePlaylist() { return this.youtubePlaylist; }
|
public java.util.List<String> getYoutubePlaylist() { return this.youtubePlaylist; }
|
||||||
@@ -250,6 +253,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
|||||||
this.allowWalkthrough = set.getBoolean("allow_walkthrough");
|
this.allowWalkthrough = set.getBoolean("allow_walkthrough");
|
||||||
this.hideWall = set.getBoolean("allow_hidewall");
|
this.hideWall = set.getBoolean("allow_hidewall");
|
||||||
try { this.youtubeEnabled = set.getBoolean("youtube_enabled"); } catch (Exception e) { this.youtubeEnabled = false; }
|
try { this.youtubeEnabled = set.getBoolean("youtube_enabled"); } catch (Exception e) { this.youtubeEnabled = false; }
|
||||||
|
try { this.soundboardEnabled = set.getBoolean("soundboard_enabled"); } catch (Exception e) { this.soundboardEnabled = false; }
|
||||||
this.chatMode = set.getInt("chat_mode");
|
this.chatMode = set.getInt("chat_mode");
|
||||||
this.chatWeight = set.getInt("chat_weight");
|
this.chatWeight = set.getInt("chat_weight");
|
||||||
this.chatSpeed = set.getInt("chat_speed");
|
this.chatSpeed = set.getInt("chat_speed");
|
||||||
|
|||||||
@@ -1020,6 +1020,10 @@ public class RoomManager {
|
|||||||
room.getYoutubeWatchers()).compose());
|
room.getYoutubeWatchers()).compose());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
habbo.getClient().sendResponse(new com.eu.habbo.messages.outgoing.soundboard.SoundboardSettingsComposer(
|
||||||
|
room.isSoundboardEnabled(),
|
||||||
|
Emulator.getGameEnvironment().getSoundboardManager().getSounds()).compose());
|
||||||
|
|
||||||
WiredManager.triggerUserEntersRoom(room, habbo.getRoomUnit());
|
WiredManager.triggerUserEntersRoom(room, habbo.getRoomUnit());
|
||||||
room.habboEntered(habbo);
|
room.habboEntered(habbo);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.eu.habbo.habbohotel.soundboard;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SoundboardManager {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SoundboardManager.class);
|
||||||
|
|
||||||
|
private final List<SoundboardSound> sounds = new ArrayList<>();
|
||||||
|
|
||||||
|
public SoundboardManager() {
|
||||||
|
long millis = System.currentTimeMillis();
|
||||||
|
this.bootstrap();
|
||||||
|
this.reload();
|
||||||
|
LOGGER.info("Soundboard Manager -> Loaded! ({} MS, {} sounds)", System.currentTimeMillis() - millis, this.sounds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self-bootstrap: room flag column + sounds table, so the feature works even
|
||||||
|
// before the manual migration is applied.
|
||||||
|
private void bootstrap() {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
Statement statement = connection.createStatement()) {
|
||||||
|
statement.execute("ALTER TABLE `rooms` ADD COLUMN IF NOT EXISTS `soundboard_enabled` TINYINT(1) NOT NULL DEFAULT 0");
|
||||||
|
statement.execute("CREATE TABLE IF NOT EXISTS `soundboard_sounds` (" +
|
||||||
|
"`id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) NOT NULL DEFAULT '', " +
|
||||||
|
"`url` VARCHAR(255) NOT NULL DEFAULT '', `enabled` TINYINT(1) NOT NULL DEFAULT 1, " +
|
||||||
|
"`sort_order` INT(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to bootstrap soundboard schema", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
this.sounds.clear();
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT id, name, url FROM soundboard_sounds WHERE enabled = 1 ORDER BY sort_order ASC, id ASC");
|
||||||
|
ResultSet set = statement.executeQuery()) {
|
||||||
|
while (set.next()) {
|
||||||
|
this.sounds.add(new SoundboardSound(set));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to load soundboard sounds", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SoundboardSound> getSounds() {
|
||||||
|
return this.sounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundboardSound getSound(int id) {
|
||||||
|
for (SoundboardSound sound : this.sounds) {
|
||||||
|
if (sound.id == id) return sound;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owner toggle — persists the room flag with a dedicated UPDATE (kept out of
|
||||||
|
// the big room-settings save to avoid touching that statement).
|
||||||
|
public void setRoomEnabled(int roomId, boolean enabled) {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("UPDATE rooms SET soundboard_enabled = ? WHERE id = ? LIMIT 1")) {
|
||||||
|
statement.setString(1, enabled ? "1" : "0");
|
||||||
|
statement.setInt(2, roomId);
|
||||||
|
statement.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to set soundboard_enabled for room {}", roomId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.eu.habbo.habbohotel.soundboard;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
// One soundboard pad: a named audio clip served from a URL (uploaded via the CMS).
|
||||||
|
public class SoundboardSound {
|
||||||
|
public final int id;
|
||||||
|
public final String name;
|
||||||
|
public final String url;
|
||||||
|
|
||||||
|
public SoundboardSound(ResultSet set) throws SQLException {
|
||||||
|
this.id = set.getInt("id");
|
||||||
|
this.name = set.getString("name");
|
||||||
|
this.url = set.getString("url");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
package com.eu.habbo.habbohotel.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.items.Item;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||||
|
import gnu.trove.set.hash.THashSet;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class WheelManager {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(WheelManager.class);
|
||||||
|
private static final int RECENT_KEEP = 50;
|
||||||
|
private static final int SECONDS_PER_DAY = 86400;
|
||||||
|
|
||||||
|
public static final Set<String> VALID_PRIZE_TYPES = Set.of(
|
||||||
|
"credits", "points", "spin", "item", "badge", "nothing");
|
||||||
|
public static final int MAX_PRIZES_PER_SAVE = 64;
|
||||||
|
public static final int MAX_STRING_LEN = 64;
|
||||||
|
public static final int MAX_PRIZE_AMOUNT = 1_000_000;
|
||||||
|
public static final int MAX_ITEM_QUANTITY = 100;
|
||||||
|
public static final int MAX_WEIGHT = 1_000_000;
|
||||||
|
public static final int MAX_EXTRA_SPINS = 10_000;
|
||||||
|
private static final long MIN_SPIN_INTERVAL_MS = 1500L;
|
||||||
|
|
||||||
|
private final List<WheelPrize> prizes = new ArrayList<>();
|
||||||
|
private int totalWeight = 0;
|
||||||
|
private int freeSpinsPerDay = 1;
|
||||||
|
private int spinCost = 50;
|
||||||
|
private int spinCostType = 5;
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Integer, Long> lastSpinAt = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, WheelUserState> userStateCache = new ConcurrentHashMap<>();
|
||||||
|
private final java.util.concurrent.CopyOnWriteArrayList<WheelRecentWin> recentWinsCache = new java.util.concurrent.CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public WheelManager() {
|
||||||
|
long millis = System.currentTimeMillis();
|
||||||
|
this.reload();
|
||||||
|
LOGGER.info("Wheel Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
this.loadSettings();
|
||||||
|
this.loadPrizes();
|
||||||
|
this.loadRecentWins();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSettings() {
|
||||||
|
this.freeSpinsPerDay = Emulator.getConfig().getInt("wheel.free_spins_per_day", 1);
|
||||||
|
this.spinCost = Emulator.getConfig().getInt("wheel.spin_cost", 50);
|
||||||
|
this.spinCostType = Emulator.getConfig().getInt("wheel.spin_cost_type", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPrizes() {
|
||||||
|
this.prizes.clear();
|
||||||
|
this.totalWeight = 0;
|
||||||
|
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT * FROM wheel_prizes WHERE enabled = 1 ORDER BY sort_order ASC, id ASC");
|
||||||
|
ResultSet set = statement.executeQuery()) {
|
||||||
|
while (set.next()) {
|
||||||
|
WheelPrize prize = new WheelPrize(set);
|
||||||
|
this.prizes.add(prize);
|
||||||
|
this.totalWeight += prize.weight;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to load fortune wheel prizes", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WheelPrize> getPrizes() {
|
||||||
|
return this.prizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpinCost() {
|
||||||
|
return this.spinCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpinCostType() {
|
||||||
|
return this.spinCostType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int today() {
|
||||||
|
return Emulator.getIntUnixTimestamp() / SECONDS_PER_DAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized WheelUserState getUserState(int userId) {
|
||||||
|
int today = this.today();
|
||||||
|
WheelUserState cached = this.userStateCache.get(userId);
|
||||||
|
|
||||||
|
if (cached != null) {
|
||||||
|
if (cached.lastReset != today) {
|
||||||
|
cached.freeSpins = this.freeSpinsPerDay;
|
||||||
|
cached.lastReset = today;
|
||||||
|
this.persistUserState(userId, cached);
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
WheelUserState state = new WheelUserState();
|
||||||
|
boolean exists = false;
|
||||||
|
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT free_spins, extra_spins, last_reset FROM wheel_user_state WHERE user_id = ?")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
|
if (set.next()) {
|
||||||
|
state.freeSpins = set.getInt("free_spins");
|
||||||
|
state.extraSpins = set.getInt("extra_spins");
|
||||||
|
state.lastReset = set.getInt("last_reset");
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to read wheel state for user {}", userId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
state.freeSpins = this.freeSpinsPerDay;
|
||||||
|
state.extraSpins = 0;
|
||||||
|
state.lastReset = today;
|
||||||
|
this.persistUserState(userId, state);
|
||||||
|
} else if (state.lastReset != today) {
|
||||||
|
state.freeSpins = this.freeSpinsPerDay;
|
||||||
|
state.lastReset = today;
|
||||||
|
this.persistUserState(userId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userStateCache.put(userId, state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistUserState(int userId, WheelUserState state) {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"INSERT INTO wheel_user_state (user_id, free_spins, extra_spins, last_reset) VALUES (?, ?, ?, ?) " +
|
||||||
|
"ON DUPLICATE KEY UPDATE free_spins = VALUES(free_spins), extra_spins = VALUES(extra_spins), last_reset = VALUES(last_reset)")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
statement.setInt(2, state.freeSpins);
|
||||||
|
statement.setInt(3, state.extraSpins);
|
||||||
|
statement.setInt(4, state.lastReset);
|
||||||
|
statement.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to persist wheel state for user {}", userId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized WheelPrize spin(Habbo habbo) {
|
||||||
|
int userId = habbo.getHabboInfo().getId();
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Long last = this.lastSpinAt.get(userId);
|
||||||
|
if (last != null && (now - last) < MIN_SPIN_INTERVAL_MS) return null;
|
||||||
|
this.lastSpinAt.put(userId, now);
|
||||||
|
|
||||||
|
WheelUserState state = this.getUserState(userId);
|
||||||
|
|
||||||
|
boolean usedFree;
|
||||||
|
if (state.freeSpins > 0) {
|
||||||
|
state.freeSpins--;
|
||||||
|
usedFree = true;
|
||||||
|
} else if (state.extraSpins > 0) {
|
||||||
|
state.extraSpins--;
|
||||||
|
usedFree = false;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
WheelPrize prize = this.pickWeighted();
|
||||||
|
if (prize == null) {
|
||||||
|
if (usedFree) state.freeSpins++; else state.extraSpins++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.giveReward(habbo, prize, state);
|
||||||
|
this.persistUserState(userId, state);
|
||||||
|
this.recordWin(habbo, prize);
|
||||||
|
|
||||||
|
return prize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WheelPrize pickWeighted() {
|
||||||
|
if (this.prizes.isEmpty() || this.totalWeight <= 0) return null;
|
||||||
|
|
||||||
|
int roll = ThreadLocalRandom.current().nextInt(this.totalWeight);
|
||||||
|
int acc = 0;
|
||||||
|
for (WheelPrize prize : this.prizes) {
|
||||||
|
acc += prize.weight;
|
||||||
|
if (roll < acc) return prize;
|
||||||
|
}
|
||||||
|
return this.prizes.get(this.prizes.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void giveReward(Habbo habbo, WheelPrize prize, WheelUserState state) {
|
||||||
|
int amount = Math.max(0, Math.min(prize.amount, MAX_PRIZE_AMOUNT));
|
||||||
|
|
||||||
|
switch (prize.type) {
|
||||||
|
case "credits":
|
||||||
|
if (amount > 0) habbo.giveCredits(amount);
|
||||||
|
break;
|
||||||
|
case "points":
|
||||||
|
if (amount > 0) habbo.givePoints(prize.pointsType, amount);
|
||||||
|
break;
|
||||||
|
case "spin":
|
||||||
|
int room = Math.max(0, MAX_EXTRA_SPINS - state.extraSpins);
|
||||||
|
state.extraSpins += Math.min(amount, room);
|
||||||
|
break;
|
||||||
|
case "item":
|
||||||
|
this.giveItem(habbo, prize, Math.min(amount, MAX_ITEM_QUANTITY));
|
||||||
|
break;
|
||||||
|
case "badge":
|
||||||
|
if (prize.value != null && !prize.value.isEmpty()) {
|
||||||
|
habbo.addBadge(prize.value, "Fortune Wheel");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "nothing":
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void giveItem(Habbo habbo, WheelPrize prize, int quantity) {
|
||||||
|
if (quantity <= 0 || prize.value == null) return;
|
||||||
|
|
||||||
|
int baseId;
|
||||||
|
try {
|
||||||
|
baseId = Integer.parseInt(prize.value.trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item base = Emulator.getGameEnvironment().getItemManager().getItem(baseId);
|
||||||
|
if (base == null) return;
|
||||||
|
|
||||||
|
THashSet<HabboItem> items = new THashSet<>();
|
||||||
|
for (int i = 0; i < quantity; i++) {
|
||||||
|
HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), base, 0, 0, "");
|
||||||
|
if (item != null) items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!items.isEmpty()) {
|
||||||
|
habbo.addFurniture(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordWin(Habbo habbo, WheelPrize prize) {
|
||||||
|
WheelRecentWin win = new WheelRecentWin(
|
||||||
|
habbo.getHabboInfo().getUsername(),
|
||||||
|
habbo.getHabboInfo().getLook(),
|
||||||
|
prize.label);
|
||||||
|
|
||||||
|
this.recentWinsCache.add(0, win);
|
||||||
|
while (this.recentWinsCache.size() > RECENT_KEEP) {
|
||||||
|
this.recentWinsCache.remove(this.recentWinsCache.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||||
|
try (PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"INSERT INTO wheel_recent_wins (user_id, username, look, prize_label, won_at) VALUES (?, ?, ?, ?, ?)")) {
|
||||||
|
statement.setInt(1, habbo.getHabboInfo().getId());
|
||||||
|
statement.setString(2, habbo.getHabboInfo().getUsername());
|
||||||
|
statement.setString(3, habbo.getHabboInfo().getLook());
|
||||||
|
statement.setString(4, prize.label);
|
||||||
|
statement.setInt(5, Emulator.getIntUnixTimestamp());
|
||||||
|
statement.executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (PreparedStatement trim = connection.prepareStatement(
|
||||||
|
"DELETE FROM wheel_recent_wins WHERE id < (SELECT id FROM (SELECT id FROM wheel_recent_wins ORDER BY id DESC LIMIT 1 OFFSET ?) t)")) {
|
||||||
|
trim.setInt(1, RECENT_KEEP - 1);
|
||||||
|
trim.executeUpdate();
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to record wheel win", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WheelRecentWin> getRecentWins(int limit) {
|
||||||
|
if (limit <= 0) return new ArrayList<>();
|
||||||
|
int size = this.recentWinsCache.size();
|
||||||
|
if (size == 0) return new ArrayList<>();
|
||||||
|
int take = Math.min(limit, size);
|
||||||
|
return new ArrayList<>(this.recentWinsCache.subList(0, take));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRecentWins() {
|
||||||
|
this.recentWinsCache.clear();
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement("SELECT username, look, prize_label FROM wheel_recent_wins ORDER BY id DESC LIMIT ?")) {
|
||||||
|
statement.setInt(1, RECENT_KEEP);
|
||||||
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
|
while (set.next()) {
|
||||||
|
this.recentWinsCache.add(new WheelRecentWin(
|
||||||
|
set.getString("username"),
|
||||||
|
set.getString("look"),
|
||||||
|
set.getString("prize_label")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to load wheel recent wins", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean buySpin(Habbo habbo) {
|
||||||
|
if (this.spinCost <= 0) return false;
|
||||||
|
|
||||||
|
int userId = habbo.getHabboInfo().getId();
|
||||||
|
WheelUserState state = this.getUserState(userId);
|
||||||
|
if (state.extraSpins >= MAX_EXTRA_SPINS) return false;
|
||||||
|
|
||||||
|
if (this.spinCostType == -1) {
|
||||||
|
if (habbo.getHabboInfo().getCredits() < this.spinCost) return false;
|
||||||
|
habbo.giveCredits(-this.spinCost);
|
||||||
|
} else {
|
||||||
|
if (habbo.getHabboInfo().getCurrencyAmount(this.spinCostType) < this.spinCost) return false;
|
||||||
|
habbo.givePoints(this.spinCostType, -this.spinCost);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.extraSpins++;
|
||||||
|
this.persistUserState(userId, state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void savePrize(int id, String type, String value, int amount, int pointsType, int weight, String label) {
|
||||||
|
String safeType = (type != null && VALID_PRIZE_TYPES.contains(type)) ? type : "nothing";
|
||||||
|
String safeValue = truncate(value, MAX_STRING_LEN);
|
||||||
|
String safeLabel = truncate(label, MAX_STRING_LEN);
|
||||||
|
int safeAmount = clamp(amount, 0, MAX_PRIZE_AMOUNT);
|
||||||
|
int safeWeight = clamp(weight, 0, MAX_WEIGHT);
|
||||||
|
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"UPDATE wheel_prizes SET type = ?, value = ?, amount = ?, points_type = ?, weight = ?, label = ? WHERE id = ?")) {
|
||||||
|
statement.setString(1, safeType);
|
||||||
|
statement.setString(2, safeValue);
|
||||||
|
statement.setInt(3, safeAmount);
|
||||||
|
statement.setInt(4, pointsType);
|
||||||
|
statement.setInt(5, safeWeight);
|
||||||
|
statement.setString(6, safeLabel);
|
||||||
|
statement.setInt(7, id);
|
||||||
|
statement.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to save wheel prize {}", id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String truncate(String s, int max) {
|
||||||
|
if (s == null) return "";
|
||||||
|
return s.length() <= max ? s : s.substring(0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int clamp(int value, int min, int max) {
|
||||||
|
if (value < min) return min;
|
||||||
|
if (value > max) return max;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.eu.habbo.habbohotel.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.items.Item;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
// One slice of the wheel. type = item | badge | credits | points | spin | nothing.
|
||||||
|
public class WheelPrize {
|
||||||
|
public final int id;
|
||||||
|
public final String type;
|
||||||
|
public final String value; // item: base item id ; badge: badge code ; others: unused
|
||||||
|
public final int amount; // item qty / credits / points / extra spins
|
||||||
|
public final int pointsType; // for type=points
|
||||||
|
public final int weight;
|
||||||
|
public final String label;
|
||||||
|
public final int spriteId; // resolved for item prizes so the client can render the furni icon
|
||||||
|
|
||||||
|
public WheelPrize(ResultSet set) throws SQLException {
|
||||||
|
this.id = set.getInt("id");
|
||||||
|
this.type = set.getString("type");
|
||||||
|
this.value = set.getString("value");
|
||||||
|
this.amount = set.getInt("amount");
|
||||||
|
this.pointsType = set.getInt("points_type");
|
||||||
|
this.weight = Math.max(0, set.getInt("weight"));
|
||||||
|
this.label = set.getString("label");
|
||||||
|
this.spriteId = resolveSpriteId(this.type, this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int resolveSpriteId(String type, String value) {
|
||||||
|
if (!"item".equals(type) || value == null) return 0;
|
||||||
|
try {
|
||||||
|
Item item = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(value.trim()));
|
||||||
|
return item != null ? item.getSpriteId() : 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String badgeCode() {
|
||||||
|
return "badge".equals(this.type) && this.value != null ? this.value : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.eu.habbo.habbohotel.wheel;
|
||||||
|
|
||||||
|
// A row in the "latest winners" panel. Denormalized (username/look stored at win time).
|
||||||
|
public class WheelRecentWin {
|
||||||
|
public final String username;
|
||||||
|
public final String look;
|
||||||
|
public final String prizeLabel;
|
||||||
|
|
||||||
|
public WheelRecentWin(String username, String look, String prizeLabel) {
|
||||||
|
this.username = username != null ? username : "";
|
||||||
|
this.look = look != null ? look : "";
|
||||||
|
this.prizeLabel = prizeLabel != null ? prizeLabel : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.eu.habbo.habbohotel.wheel;
|
||||||
|
|
||||||
|
// Per-user spin balance. freeSpins resets daily (lazy, on access); extraSpins persist.
|
||||||
|
public class WheelUserState {
|
||||||
|
public int freeSpins;
|
||||||
|
public int extraSpins;
|
||||||
|
public int lastReset; // day index (unix / 86400) of the last daily reset
|
||||||
|
|
||||||
|
public int totalSpins() {
|
||||||
|
return this.freeSpins + this.extraSpins;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -745,5 +745,16 @@ public class PacketManager {
|
|||||||
this.registerHandler(Incoming.HousekeepingSendHotelAlertEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingSendHotelAlertEvent.class);
|
this.registerHandler(Incoming.HousekeepingSendHotelAlertEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingSendHotelAlertEvent.class);
|
||||||
this.registerHandler(Incoming.HousekeepingGetDashboardEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingGetDashboardEvent.class);
|
this.registerHandler(Incoming.HousekeepingGetDashboardEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingGetDashboardEvent.class);
|
||||||
this.registerHandler(Incoming.HousekeepingListActionLogEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingListActionLogEvent.class);
|
this.registerHandler(Incoming.HousekeepingListActionLogEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingListActionLogEvent.class);
|
||||||
|
|
||||||
|
this.registerHandler(Incoming.RequestRareValuesEvent, com.eu.habbo.messages.incoming.rarevalues.RequestRareValuesEvent.class);
|
||||||
|
|
||||||
|
this.registerHandler(Incoming.WheelOpenEvent, com.eu.habbo.messages.incoming.wheel.WheelOpenEvent.class);
|
||||||
|
this.registerHandler(Incoming.WheelSpinEvent, com.eu.habbo.messages.incoming.wheel.WheelSpinEvent.class);
|
||||||
|
this.registerHandler(Incoming.WheelBuySpinEvent, com.eu.habbo.messages.incoming.wheel.WheelBuySpinEvent.class);
|
||||||
|
this.registerHandler(Incoming.WheelAdminGetPrizesEvent, com.eu.habbo.messages.incoming.wheel.WheelAdminGetPrizesEvent.class);
|
||||||
|
this.registerHandler(Incoming.WheelAdminSavePrizesEvent, com.eu.habbo.messages.incoming.wheel.WheelAdminSavePrizesEvent.class);
|
||||||
|
|
||||||
|
this.registerHandler(Incoming.SoundboardPlayEvent, com.eu.habbo.messages.incoming.soundboard.SoundboardPlayEvent.class);
|
||||||
|
this.registerHandler(Incoming.SoundboardSetEnabledEvent, com.eu.habbo.messages.incoming.soundboard.SoundboardSetEnabledEvent.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,4 +486,14 @@ public class Incoming {
|
|||||||
public static final int HousekeepingSendHotelAlertEvent = 9121;
|
public static final int HousekeepingSendHotelAlertEvent = 9121;
|
||||||
public static final int HousekeepingGetDashboardEvent = 9122;
|
public static final int HousekeepingGetDashboardEvent = 9122;
|
||||||
public static final int HousekeepingListActionLogEvent = 9123;
|
public static final int HousekeepingListActionLogEvent = 9123;
|
||||||
|
|
||||||
|
// Custom features — IDs 9300+ reserved
|
||||||
|
public static final int RequestRareValuesEvent = 9300;
|
||||||
|
public static final int WheelOpenEvent = 9301;
|
||||||
|
public static final int WheelSpinEvent = 9302;
|
||||||
|
public static final int WheelBuySpinEvent = 9303;
|
||||||
|
public static final int WheelAdminGetPrizesEvent = 9304;
|
||||||
|
public static final int WheelAdminSavePrizesEvent = 9305;
|
||||||
|
public static final int SoundboardPlayEvent = 9306;
|
||||||
|
public static final int SoundboardSetEnabledEvent = 9307;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.rarevalues;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.rarevalues.RareValuesComposer;
|
||||||
|
|
||||||
|
// Client requests the furni value map once on load. Public info (catalog prices),
|
||||||
|
// no permission gate. Rate limited since the payload is large.
|
||||||
|
public class RequestRareValuesEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
this.client.sendResponse(new RareValuesComposer(
|
||||||
|
Emulator.getGameEnvironment().getCatalogManager().getFurnitureValues()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-6
@@ -5,11 +5,13 @@ import com.eu.habbo.habbohotel.bots.Bot;
|
|||||||
import com.eu.habbo.habbohotel.bots.BotManager;
|
import com.eu.habbo.habbohotel.bots.BotManager;
|
||||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||||
import com.eu.habbo.habbohotel.rooms.Room;
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||||
import com.eu.habbo.habbohotel.users.DanceType;
|
import com.eu.habbo.habbohotel.users.DanceType;
|
||||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
import com.eu.habbo.messages.outgoing.generic.alerts.BotErrorComposer;
|
import com.eu.habbo.messages.outgoing.generic.alerts.BotErrorComposer;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDanceComposer;
|
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDanceComposer;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserNameChangedComposer;
|
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserNameChangedComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUsersComposer;
|
import com.eu.habbo.messages.outgoing.rooms.users.RoomUsersComposer;
|
||||||
import com.eu.habbo.plugin.events.bots.BotSavedChatEvent;
|
import com.eu.habbo.plugin.events.bots.BotSavedChatEvent;
|
||||||
import com.eu.habbo.plugin.events.bots.BotSavedLookEvent;
|
import com.eu.habbo.plugin.events.bots.BotSavedLookEvent;
|
||||||
@@ -28,16 +30,20 @@ public class BotSaveSettingsEvent extends MessageHandler {
|
|||||||
|
|
||||||
if (room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER)) {
|
if (room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER)) {
|
||||||
int botId = this.packet.readInt();
|
int botId = this.packet.readInt();
|
||||||
|
|
||||||
Bot bot = room.getBot(Math.abs(botId));
|
Bot bot = room.getBot(Math.abs(botId));
|
||||||
|
|
||||||
if (bot == null)
|
if (bot == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (bot.getOwnerActionIds().length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int settingId = this.packet.readInt();
|
int settingId = this.packet.readInt();
|
||||||
|
boolean allowed = false;
|
||||||
|
for (short a : bot.getOwnerActionIds()) {
|
||||||
|
if (a == settingId) {
|
||||||
|
allowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allowed) return;
|
||||||
|
|
||||||
|
if (!bot.tryAcquireOwnerActionSlot()) return;
|
||||||
|
|
||||||
switch (settingId) {
|
switch (settingId) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -163,8 +169,18 @@ public class BotSaveSettingsEvent extends MessageHandler {
|
|||||||
bot.needsUpdate(true);
|
bot.needsUpdate(true);
|
||||||
room.sendComposer(new RoomUsersComposer(bot).compose());
|
room.sendComposer(new RoomUsersComposer(bot).compose());
|
||||||
break;
|
break;
|
||||||
|
case Bot.ACTION_ROTATE:
|
||||||
|
if (bot.getRoomUnit() == null) break;
|
||||||
|
int next = (bot.getRoomUnit().getBodyRotation().getValue() + 2) % 8;
|
||||||
|
RoomUserRotation rotation = RoomUserRotation.fromValue(next);
|
||||||
|
bot.getRoomUnit().setRotation(rotation);
|
||||||
|
bot.needsUpdate(true);
|
||||||
|
room.sendComposer(new RoomUserStatusComposer(bot.getRoomUnit()).compose());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bot.onPostOwnerAction(settingId);
|
||||||
|
|
||||||
if (bot.needsUpdate()) {
|
if (bot.needsUpdate()) {
|
||||||
Emulator.getThreading().run(bot);
|
Emulator.getThreading().run(bot);
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.soundboard;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.soundboard.SoundboardSound;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.soundboard.SoundboardPlayComposer;
|
||||||
|
|
||||||
|
public class SoundboardPlayEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 250;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
Habbo habbo = this.client.getHabbo();
|
||||||
|
if (habbo == null) return;
|
||||||
|
|
||||||
|
Room room = this.currentRoom();
|
||||||
|
if (room == null || !room.isSoundboardEnabled()) return;
|
||||||
|
|
||||||
|
int soundId = this.packet.readInt();
|
||||||
|
SoundboardSound sound = Emulator.getGameEnvironment().getSoundboardManager().getSound(soundId);
|
||||||
|
if (sound == null) return;
|
||||||
|
|
||||||
|
// Broadcast to everyone in the room.
|
||||||
|
room.sendComposer(new SoundboardPlayComposer(sound.id, sound.url, habbo.getHabboInfo().getUsername()).compose());
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.soundboard;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.soundboard.SoundboardSettingsComposer;
|
||||||
|
|
||||||
|
public class SoundboardSetEnabledEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
Habbo habbo = this.client.getHabbo();
|
||||||
|
if (habbo == null) return;
|
||||||
|
|
||||||
|
Room room = this.currentRoom();
|
||||||
|
if (room == null) return;
|
||||||
|
|
||||||
|
// Only the room owner (or staff) may toggle the soundboard for the room.
|
||||||
|
boolean isOwner = room.getOwnerId() == habbo.getHabboInfo().getId();
|
||||||
|
if (!isOwner && !habbo.hasPermission(Permission.ACC_SUPPORTTOOL)) return;
|
||||||
|
|
||||||
|
boolean enabled = this.packet.readInt() == 1;
|
||||||
|
|
||||||
|
room.setSoundboardEnabled(enabled);
|
||||||
|
Emulator.getGameEnvironment().getSoundboardManager().setRoomEnabled(room.getId(), enabled);
|
||||||
|
|
||||||
|
// Push the refreshed settings (flag + sound list) to everyone in the room
|
||||||
|
// so the toolbar icon appears/disappears live.
|
||||||
|
room.sendComposer(new SoundboardSettingsComposer(enabled, Emulator.getGameEnvironment().getSoundboardManager().getSounds()).compose());
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelAdminPrizesComposer;
|
||||||
|
|
||||||
|
public class WheelAdminGetPrizesEvent extends MessageHandler {
|
||||||
|
public static final String PERMISSION_KEY = "acc_wheeladmin";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
if (this.client.getHabbo() == null || !this.client.getHabbo().hasPermission(PERMISSION_KEY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.sendResponse(new WheelAdminPrizesComposer(
|
||||||
|
Emulator.getGameEnvironment().getWheelManager().getPrizes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelAdminPrizesComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelDataComposer;
|
||||||
|
|
||||||
|
public class WheelAdminSavePrizesEvent extends MessageHandler {
|
||||||
|
public static final String PERMISSION_KEY = "acc_wheeladmin";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
if (this.client.getHabbo() == null || !this.client.getHabbo().hasPermission(PERMISSION_KEY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WheelManager wheel = Emulator.getGameEnvironment().getWheelManager();
|
||||||
|
|
||||||
|
int count = this.packet.readInt();
|
||||||
|
if (count <= 0 || count > WheelManager.MAX_PRIZES_PER_SAVE) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int id = this.packet.readInt();
|
||||||
|
String type = this.packet.readString();
|
||||||
|
String value = this.packet.readString();
|
||||||
|
int amount = this.packet.readInt();
|
||||||
|
int pointsType = this.packet.readInt();
|
||||||
|
int weight = this.packet.readInt();
|
||||||
|
String label = this.packet.readString();
|
||||||
|
wheel.savePrize(id, type, value, amount, pointsType, weight, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
wheel.reload();
|
||||||
|
|
||||||
|
this.client.sendResponse(new WheelAdminPrizesComposer(wheel.getPrizes()));
|
||||||
|
this.client.sendResponse(new WheelDataComposer(
|
||||||
|
wheel.getUserState(this.client.getHabbo().getHabboInfo().getId()),
|
||||||
|
wheel.getSpinCost(), wheel.getSpinCostType(), wheel.getPrizes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelDataComposer;
|
||||||
|
|
||||||
|
public class WheelBuySpinEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
Habbo habbo = this.client.getHabbo();
|
||||||
|
if (habbo == null) return;
|
||||||
|
|
||||||
|
WheelManager wheel = Emulator.getGameEnvironment().getWheelManager();
|
||||||
|
wheel.buySpin(habbo); // whether or not it succeeds, resend the balance
|
||||||
|
|
||||||
|
this.client.sendResponse(new WheelDataComposer(
|
||||||
|
wheel.getUserState(habbo.getHabboInfo().getId()),
|
||||||
|
wheel.getSpinCost(), wheel.getSpinCostType(), wheel.getPrizes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelDataComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelRecentWinsComposer;
|
||||||
|
|
||||||
|
public class WheelOpenEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
Habbo habbo = this.client.getHabbo();
|
||||||
|
if (habbo == null) return;
|
||||||
|
|
||||||
|
WheelManager wheel = Emulator.getGameEnvironment().getWheelManager();
|
||||||
|
this.client.sendResponse(new WheelDataComposer(
|
||||||
|
wheel.getUserState(habbo.getHabboInfo().getId()),
|
||||||
|
wheel.getSpinCost(), wheel.getSpinCostType(), wheel.getPrizes()));
|
||||||
|
this.client.sendResponse(new WheelRecentWinsComposer(wheel.getRecentWins(50)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelPrize;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelDataComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelRecentWinsComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.wheel.WheelResultComposer;
|
||||||
|
|
||||||
|
public class WheelSpinEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public int getRatelimit() {
|
||||||
|
return 1500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
Habbo habbo = this.client.getHabbo();
|
||||||
|
if (habbo == null) return;
|
||||||
|
|
||||||
|
WheelManager wheel = Emulator.getGameEnvironment().getWheelManager();
|
||||||
|
WheelPrize prize = wheel.spin(habbo);
|
||||||
|
|
||||||
|
if (prize != null) {
|
||||||
|
this.client.sendResponse(new WheelResultComposer(prize.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the balance either way so the client unlocks the wheel.
|
||||||
|
this.client.sendResponse(new WheelDataComposer(
|
||||||
|
wheel.getUserState(habbo.getHabboInfo().getId()),
|
||||||
|
wheel.getSpinCost(), wheel.getSpinCostType(), wheel.getPrizes()));
|
||||||
|
|
||||||
|
if (prize != null) {
|
||||||
|
this.client.sendResponse(new WheelRecentWinsComposer(wheel.getRecentWins(50)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -594,4 +594,13 @@ public class Outgoing {
|
|||||||
public static final int HousekeepingDashboardComposer = 9204;
|
public static final int HousekeepingDashboardComposer = 9204;
|
||||||
public static final int HousekeepingActionLogComposer = 9205;
|
public static final int HousekeepingActionLogComposer = 9205;
|
||||||
|
|
||||||
|
// Custom features — IDs 9400+ reserved
|
||||||
|
public static final int RareValuesComposer = 9400;
|
||||||
|
public static final int WheelDataComposer = 9401;
|
||||||
|
public static final int WheelResultComposer = 9402;
|
||||||
|
public static final int WheelRecentWinsComposer = 9403;
|
||||||
|
public static final int WheelAdminPrizesComposer = 9404;
|
||||||
|
public static final int SoundboardSettingsComposer = 9405;
|
||||||
|
public static final int SoundboardPlayComposer = 9406;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.rarevalues;
|
||||||
|
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
import gnu.trove.iterator.TIntObjectIterator;
|
||||||
|
import gnu.trove.map.TIntObjectMap;
|
||||||
|
|
||||||
|
// Sends the full spriteId -> value map to the client. Consumed by the toolbar
|
||||||
|
// price guide and the furni infostand "value" line. See CatalogManager#loadFurnitureValues.
|
||||||
|
public class RareValuesComposer extends MessageComposer {
|
||||||
|
private final TIntObjectMap<int[]> values;
|
||||||
|
|
||||||
|
public RareValuesComposer(TIntObjectMap<int[]> values) {
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.RareValuesComposer);
|
||||||
|
this.response.appendInt(this.values.size());
|
||||||
|
|
||||||
|
TIntObjectIterator<int[]> iterator = this.values.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.advance();
|
||||||
|
int[] value = iterator.value();
|
||||||
|
this.response.appendInt(iterator.key()); // spriteId
|
||||||
|
this.response.appendInt(value[0]); // credits
|
||||||
|
this.response.appendInt(value[1]); // points
|
||||||
|
this.response.appendInt(value[2]); // pointsType
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.soundboard;
|
||||||
|
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
// Broadcast to everyone in the room when a pad is pressed — they all play the clip.
|
||||||
|
public class SoundboardPlayComposer extends MessageComposer {
|
||||||
|
private final int soundId;
|
||||||
|
private final String url;
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
public SoundboardPlayComposer(int soundId, String url, String username) {
|
||||||
|
this.soundId = soundId;
|
||||||
|
this.url = url != null ? url : "";
|
||||||
|
this.username = username != null ? username : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.SoundboardPlayComposer);
|
||||||
|
this.response.appendInt(this.soundId);
|
||||||
|
this.response.appendString(this.url);
|
||||||
|
this.response.appendString(this.username);
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.soundboard;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.soundboard.SoundboardSound;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// Sent on room enter (and on toggle): whether the soundboard is active in this
|
||||||
|
// room + the available pads. The client shows the toolbar icon only if enabled.
|
||||||
|
public class SoundboardSettingsComposer extends MessageComposer {
|
||||||
|
private final boolean enabled;
|
||||||
|
private final List<SoundboardSound> sounds;
|
||||||
|
|
||||||
|
public SoundboardSettingsComposer(boolean enabled, List<SoundboardSound> sounds) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
this.sounds = sounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.SoundboardSettingsComposer);
|
||||||
|
this.response.appendBoolean(this.enabled);
|
||||||
|
this.response.appendInt(this.sounds.size());
|
||||||
|
for (SoundboardSound sound : this.sounds) {
|
||||||
|
this.response.appendInt(sound.id);
|
||||||
|
this.response.appendString(sound.name);
|
||||||
|
this.response.appendString(sound.url);
|
||||||
|
}
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelPrize;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// Raw editable prize list for the in-client admin editor (sends value/amount/
|
||||||
|
// pointsType as stored, unlike WheelDataComposer which resolves icons for players).
|
||||||
|
public class WheelAdminPrizesComposer extends MessageComposer {
|
||||||
|
private final List<WheelPrize> prizes;
|
||||||
|
|
||||||
|
public WheelAdminPrizesComposer(List<WheelPrize> prizes) {
|
||||||
|
this.prizes = prizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.WheelAdminPrizesComposer);
|
||||||
|
this.response.appendInt(this.prizes.size());
|
||||||
|
for (WheelPrize prize : this.prizes) {
|
||||||
|
this.response.appendInt(prize.id);
|
||||||
|
this.response.appendString(prize.type);
|
||||||
|
this.response.appendString(prize.value == null ? "" : prize.value);
|
||||||
|
this.response.appendInt(prize.amount);
|
||||||
|
this.response.appendInt(prize.pointsType);
|
||||||
|
this.response.appendInt(prize.weight);
|
||||||
|
this.response.appendString(prize.label == null ? "" : prize.label);
|
||||||
|
}
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelPrize;
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelUserState;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// User spin balance + cost + the full prize list (one entry per slice).
|
||||||
|
public class WheelDataComposer extends MessageComposer {
|
||||||
|
private final WheelUserState state;
|
||||||
|
private final int spinCost;
|
||||||
|
private final int spinCostType;
|
||||||
|
private final List<WheelPrize> prizes;
|
||||||
|
|
||||||
|
public WheelDataComposer(WheelUserState state, int spinCost, int spinCostType, List<WheelPrize> prizes) {
|
||||||
|
this.state = state;
|
||||||
|
this.spinCost = spinCost;
|
||||||
|
this.spinCostType = spinCostType;
|
||||||
|
this.prizes = prizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.WheelDataComposer);
|
||||||
|
this.response.appendInt(this.state.freeSpins);
|
||||||
|
this.response.appendInt(this.state.extraSpins);
|
||||||
|
this.response.appendInt(this.spinCost);
|
||||||
|
this.response.appendInt(this.spinCostType);
|
||||||
|
|
||||||
|
this.response.appendInt(this.prizes.size());
|
||||||
|
for (WheelPrize prize : this.prizes) {
|
||||||
|
this.response.appendInt(prize.id);
|
||||||
|
this.response.appendString(prize.type);
|
||||||
|
this.response.appendInt(prize.spriteId); // item only, else 0
|
||||||
|
this.response.appendString(prize.badgeCode()); // badge only, else ""
|
||||||
|
this.response.appendInt(prize.amount);
|
||||||
|
this.response.appendInt(prize.pointsType);
|
||||||
|
this.response.appendString(prize.label == null ? "" : prize.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.wheel.WheelRecentWin;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// "Latest winners" list: username + look (for the headshot) + prize label.
|
||||||
|
public class WheelRecentWinsComposer extends MessageComposer {
|
||||||
|
private final List<WheelRecentWin> wins;
|
||||||
|
|
||||||
|
public WheelRecentWinsComposer(List<WheelRecentWin> wins) {
|
||||||
|
this.wins = wins;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.WheelRecentWinsComposer);
|
||||||
|
this.response.appendInt(this.wins.size());
|
||||||
|
for (WheelRecentWin win : this.wins) {
|
||||||
|
this.response.appendString(win.username);
|
||||||
|
this.response.appendString(win.look);
|
||||||
|
this.response.appendString(win.prizeLabel);
|
||||||
|
}
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.wheel;
|
||||||
|
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
// The winning prize id. The client animates the wheel to that slice; the reward
|
||||||
|
// was already granted server-side.
|
||||||
|
public class WheelResultComposer extends MessageComposer {
|
||||||
|
private final int prizeId;
|
||||||
|
|
||||||
|
public WheelResultComposer(int prizeId) {
|
||||||
|
this.prizeId = prizeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.WheelResultComposer);
|
||||||
|
this.response.appendInt(this.prizeId);
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user