diff --git a/Server/src/main/content/global/skill/construction/BuildHotspot.java b/Server/src/main/content/global/skill/construction/BuildHotspot.java index c5ca6c8e7..2ec669970 100644 --- a/Server/src/main/content/global/skill/construction/BuildHotspot.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspot.java @@ -65,14 +65,16 @@ public enum BuildHotspot { * Low-level Work shop hotspots. */ REPAIR(15448, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.REPAIR_BENCH, Decoration.WHETSTONE, Decoration.ARMOUR_STAND), - WORKBENCH(15439, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKBENCH_WOODEN, Decoration.WORKBENCH_OAK,Decoration.WORKBENCH_STEEL_FRAME, Decoration.WORKBENCH_WITH_VICE,Decoration.WORKBENCH_WITH_LATHE), - CRAFTING(15441, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRAFTING_TABLE_1, Decoration.CRAFTING_TABLE_2,Decoration.CRAFTING_TABLE_3, Decoration.CRAFTING_TABLE_4), + WORKBENCH(15439, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKBENCH_WOODEN, Decoration.WORKBENCH_OAK,Decoration.WORKBENCH_STEEL_FRAME, Decoration.WORKBENCH_WITH_VICE, Decoration.WORKBENCH_WITH_LATHE), + CRAFTING(15441, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.CRAFTING_TABLE_1, Decoration.CRAFTING_TABLE_2,Decoration.CRAFTING_TABLE_3, Decoration.CRAFTING_TABLE_4), TOOL1(15443, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2, Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4, Decoration.TOOL_STORE_5), TOOL2(15444, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), TOOL3(15445, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), TOOL4(15446, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), TOOL5(15447, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5), HERALDRY(15450, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PLUMING_STAND, Decoration.SHIELD_EASEL,Decoration.BANNER_EASEL), + WORKBENCH_STOOL(15440, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKSHOP_STOOL, Decoration.WORKSHOP_STOOL,Decoration.WORKSHOP_STOOL_OAK, Decoration.WORKSHOP_STOOL_OAK,Decoration.WORKSHOP_STOOL_OAK), + CRAFTING_STOOL(15442, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKSHOP_STOOL, Decoration.WORKSHOP_STOOL,Decoration.WORKSHOP_STOOL, Decoration.WORKSHOP_STOOL), /** * Bedroom hotspots. @@ -386,6 +388,8 @@ public enum BuildHotspot { linkedHotspots.add(new BuildHotspot[] { DUNGEON_DOOR_LEFT2, DUNGEON_DOOR_RIGHT2 }); linkedHotspots.add(new BuildHotspot[] { SMALL_PLANT_1, SMALL_PLANT1 }); linkedHotspots.add(new BuildHotspot[] { SHELVES, SHELVES_2 }); + linkedHotspots.add(new BuildHotspot[] { WORKBENCH, WORKBENCH_STOOL }); + linkedHotspots.add(new BuildHotspot[] { CRAFTING, CRAFTING_STOOL }); } /** diff --git a/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java index 69b0b1483..7c26c967c 100644 --- a/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java @@ -9,13 +9,14 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; +import core.game.world.map.RegionManager; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.Log; -import core.tools.SystemLogger; import core.plugin.ClassScanner; import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.setAttribute; /** * The build option handling plugin. @@ -48,7 +49,7 @@ public final class BuildOptionPlugin extends OptionHandler { player.getDialogueInterpreter().open("con:removedec", object); return true; } - player.setAttribute("con:hsobject", node); + setAttribute(player, ConstructionInterface.ATTRIBUTE_HOTSPOT_OBJ, node); if (BuildingUtils.isDoorHotspot(object)) { int[] pos = BuildingUtils.roomExists(player, object); if (pos != null) { @@ -71,7 +72,27 @@ public final class BuildOptionPlugin extends OptionHandler { return true; } - player.setAttribute("con:hotspot", hotspot); + BuildHotspot[] linked = BuildHotspot.getLinkedHotspots(hotspot.getHotspot()); + if (linked != null && linked[0] != hotspot.getHotspot()) { + Room room = player.getHouseManager().getRoom(object.getLocation()); + if (room == null) return true; + for (Hotspot h : room.getHotspots()) { + if (h.getHotspot() == linked[0]) { + int dx = h.getCurrentX() - object.getLocation().getChunkOffsetX(); + int dy = h.getCurrentY() - object.getLocation().getChunkOffsetY(); + Scenery primaryObj = RegionManager.getObject(object.getLocation().transform(dx, dy, 0)); + if (primaryObj != null) { + setAttribute(player, ConstructionInterface.ATTRIBUTE_HOTSPOT_OBJ, primaryObj); + setAttribute(player, ConstructionInterface.ATTRIBUTE_HOTSPOT, h); + BuildingUtils.openBuildInterface(player, linked[0]); + } + return true; + } + } + return true; + } + + setAttribute(player, ConstructionInterface.ATTRIBUTE_HOTSPOT, hotspot); BuildingUtils.openBuildInterface(player, hotspot.getHotspot()); return true; } diff --git a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java index b74e99cb3..d3d10b797 100644 --- a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java +++ b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java @@ -18,6 +18,8 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.getAttribute; + /** * Handles the building a room dialogue. * @author Emperor @@ -105,7 +107,7 @@ public final class BuildRoomDialogue extends DialoguePlugin { stage = 2; return true; } - this.door = (Scenery) player.getAttribute("con:hsobject"); + this.door = (Scenery) getAttribute(player, ConstructionInterface.ATTRIBUTE_HOTSPOT_OBJ, null); int[] pos = BuildingUtils.getRoomPosition(player, door); roomX = pos[0]; roomY = pos[1]; diff --git a/Server/src/main/content/global/skill/construction/BuildingUtils.java b/Server/src/main/content/global/skill/construction/BuildingUtils.java index 2ee636337..e85eec085 100644 --- a/Server/src/main/content/global/skill/construction/BuildingUtils.java +++ b/Server/src/main/content/global/skill/construction/BuildingUtils.java @@ -139,9 +139,13 @@ public final class BuildingUtils { * @param object The object. */ public static void buildDecoration(final Player player, final Hotspot hotspot, final Decoration deco, final Scenery object) { + buildDecoration(player, hotspot, deco, object, false, false); + } + + public static void buildDecoration(final Player player, final Hotspot hotspot, final Decoration deco, final Scenery object, final boolean bypassCost, final boolean bypassXp) { final int nailAmount = deco.getNailAmount(); final NailType type = nailAmount > 0 ? NailType.get(player, nailAmount) : null; - if (nailAmount > 0 && type == null) { + if (nailAmount > 0 && type == null && !bypassCost) { player.getPacketDispatch().sendMessage("You don't have the right materials."); return; } @@ -165,7 +169,7 @@ public final class BuildingUtils { NailType nail = type; @Override public boolean pulse() { - if (nails > 0) { + if (nails > 0 && !bypassCost) { if (!type.isBend()) { player.getPacketDispatch().sendMessage("You use a nail."); nails--; @@ -189,12 +193,14 @@ public final class BuildingUtils { } } } - if (player.getInventory().remove(deco.getItems()) || player.isAdmin()) { + if (player.getInventory().remove(deco.getItems()) || bypassCost || player.isAdmin()) { setDecoration(player, r, room, hotspot, object, deco); - player.getSkills().addExperience(Skills.CONSTRUCTION, deco.getExperience(), true); + if (!bypassXp) { + player.getSkills().addExperience(Skills.CONSTRUCTION, deco.getExperience(), true); - if (getObjectIdsThatGiveFarmingExperience().contains(deco.getObjectId())) { - player.getSkills().addExperience(Skills.FARMING, deco.getExperience(), true); + if (getObjectIdsThatGiveFarmingExperience().contains(deco.getObjectId())) { + player.getSkills().addExperience(Skills.FARMING, deco.getExperience(), true); + } } player.unlock(); } @@ -439,7 +445,7 @@ public final class BuildingUtils { for (int y = 0; y < 8; y++) { for (BuildHotspot bh : linkedHotspots) { Hotspot h = room.getHotspot(bh, x, y); - if (h != null) { + if (h != null && h.getDecorationIndex() >= 0) { int objectId = bh.getDecorations()[h.getDecorationIndex()].getObjectId(style); Scenery o = chunk.get(x, y, chunk.getIndex(x, y, objectId)); h.setDecorationIndex(-1); diff --git a/Server/src/main/content/global/skill/construction/ConstructionInterface.java b/Server/src/main/content/global/skill/construction/ConstructionInterface.java deleted file mode 100644 index a36b0329b..000000000 --- a/Server/src/main/content/global/skill/construction/ConstructionInterface.java +++ /dev/null @@ -1,120 +0,0 @@ -package content.global.skill.construction; - - -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.Skills; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.Log; -import core.tools.SystemLogger; - -import static core.api.ContentAPIKt.log; - -/** - * Handles the creating of a decoration object. - * @author Emperor - * - */ -@Initializable -public final class ConstructionInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) { - ComponentDefinition.put(396, this); - ComponentDefinition.put(398, this); - ComponentDefinition.put(402, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 396: - switch (button) { - case 132: - player.getInterfaceManager().close(); - Hotspot hotspot = player.getAttribute("con:hotspot"); - Scenery object = player.getAttribute("con:hsobject"); - if (hotspot == null || object == null) { - log(this.getClass(), Log.ERR, "Failed building decoration " + hotspot + " : " + object); - break; - } - slot = ((slot % 2 != 0) ? 4 : 0) + (slot >> 1); - if (slot >= hotspot.getHotspot().getDecorations().length) { - log(this.getClass(), Log.ERR, "Failed building decoration " + slot + "/" + hotspot.getHotspot().getDecorations().length); - break; - } - boolean debug = player.isStaff(); - Decoration deco = hotspot.getHotspot().getDecorations()[slot]; - if (!debug) { - if (player.getSkills().getLevel(Skills.CONSTRUCTION) < deco.getLevel()) { - player.sendMessage("You need to have a Construction level of " + deco.getLevel() + " to build that."); - return true; - } - if (!player.getInventory().containsItems(deco.getItems())) { - player.sendMessage("You don't have the right materials."); - return true; - } - for (int tool : deco.getTools()) { - if (tool == BuildingUtils.WATERING_CAN) { - boolean hasWateringCan = false; - for (int i = 0; i < 8; i++) { - if (player.getInventory().contains(tool - i, 1)) { - hasWateringCan = true; - break; - } - } - if (!hasWateringCan) { - player.sendMessage("You need a watering can to plant this."); - return true; - } - continue; - } - if (!player.getInventory().contains(tool, 1)) { - player.sendMessage("You need a " + ItemDefinition.forId(tool).getName() + " to build this."); - return true; - } - } - } - BuildingUtils.buildDecoration(player, hotspot, deco, object); - return true; - } - break; - case 398: - switch (button) { - case 14: - player.getHouseManager().toggleBuildingMode(player, true); - return true; - case 1: - player.getHouseManager().toggleBuildingMode(player, false); - return true; - case 15: - player.getHouseManager().expelGuests(player); - return true; - case 13: - if (!player.getHouseManager().isInHouse(player)) { - player.sendMessage("You can't do this outside of your house."); - return true; - } - HouseManager.leave(player); - return true; - } - break; - case 402: - int index = button - 160; - log(this.getClass(), Log.FINE, "BuildRoom Interface Index: " + index); - if (index > -1 && index < RoomProperties.values().length) { - player.getDialogueInterpreter().open("con:room", RoomProperties.values()[index]); - return true; - } - break; - } - return false; - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/ConstructionInterface.kt b/Server/src/main/content/global/skill/construction/ConstructionInterface.kt new file mode 100644 index 000000000..3f897cfcb --- /dev/null +++ b/Server/src/main/content/global/skill/construction/ConstructionInterface.kt @@ -0,0 +1,161 @@ +package content.global.skill.construction + +import content.global.skill.construction.decoration.workshop.WorkbenchListeners +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.interaction.InterfaceListener +import core.game.node.entity.skill.Skills +import core.game.node.scenery.Scenery +import core.tools.Log +import org.rs09.consts.Components + +/** + * Handles three interfaces related to the Construction skill, namely those which allow a player to build decorations, + * toggle building mode/expel guests, and select a room to add to the POH. + * @author Bishop + */ + +class ConstructionInterface : InterfaceListener { + + companion object { + const val ATTRIBUTE_HOTSPOT = "con:hotspot" + const val ATTRIBUTE_HOTSPOT_OBJ = "con:hsobject" + private const val DECO_BUTTON_BUILD = 132 + private const val HOUSE_BUTTON_BUILDMODE_ON = 14 + private const val HOUSE_BUTTON_BUILDMODE_OFF = 1 + private const val HOUSE_BUTTON_EXPEL_GUESTS = 15 + private const val HOUSE_BUTTON_LEAVE_HOUSE = 13 + + // Register furniture here when it is only obtainable by upgrading, to send the correct rejection message. + private val upgradeOnly = arrayOf( + Decoration.WORKBENCH_WITH_VICE, + Decoration.WORKBENCH_WITH_LATHE, + ) + } + + override fun defineInterfaceListeners() { + /** + * Handles the interface for selecting a decoration to build. + */ + on(Components.POH_DECORATION_396) { player, _, _, buttonID, slot, _ -> + when (buttonID) { + DECO_BUTTON_BUILD -> { + val hotspot = getAttribute(player, ATTRIBUTE_HOTSPOT, null) + val hotspotObj = getAttribute(player, ATTRIBUTE_HOTSPOT_OBJ, null) + val flatpackMode = getAttribute(player, WorkbenchListeners.ATTRIBUTE_FLATPACK_MODE, false) + if (!flatpackMode) { + closeInterface(player) + } + if ((hotspot == null || hotspotObj == null) && !flatpackMode) { + log(this.javaClass, Log.ERR, "Failed building decoration $hotspot : $hotspotObj") + return@on false + } + val decoIndex = (if (slot % 2 != 0) 4 else 0) + (slot shr 1) + val buildHotspot = if (flatpackMode) { + getAttribute(player, WorkbenchListeners.ATTRIBUTE_WORKBENCH_SELECTION, null)?: return@on false + } else { + hotspot!!.hotspot + } + if (decoIndex >= buildHotspot.decorations.size) { + log(this.javaClass, Log.ERR, "Failed building decoration $decoIndex/${buildHotspot.decorations.size}") + return@on false + } + val deco = buildHotspot.decorations[decoIndex] + if (!player.isAdmin) { + if (getDynLevel(player, Skills.CONSTRUCTION) < deco.level) { + closeInterface(player) + sendMessage(player, "You need to have a Construction level of ${deco.level} to build that.") + return@on true + } + if (deco in upgradeOnly) { + closeInterface(player) + sendMessage(player, "That can only be built by upgrading the previous piece of furniture.") + return@on true + } + if (!player.inventory.containsItems(*deco.items)) { // No clean ContentAPI substitute for this + closeInterface(player) + sendMessage(player, "You don't have the right materials.") + return@on true + } + for (tool in deco.tools) { + if (tool == BuildingUtils.WATERING_CAN) { + var hasWateringCan = false + var i = 0 + while (i < 8) { + if (inInventory(player, tool - i)) { + hasWateringCan = true + break + } + i++ + } + if (!hasWateringCan) { + sendMessage(player, "You need a watering can to plant this.") + return@on true + } + continue + } + if (!inInventory(player, tool)) { + closeInterface(player) + sendMessage(player, "You need a ${ItemDefinition.forId(tool).name} to build this.") + return@on true + } + } + if (flatpackMode && WorkbenchListeners.getBenchLevel(getAttribute(player, WorkbenchListeners.ATTRIBUTE_WORKBENCH_ID, 20)) < deco.level) { + closeInterface(player) + sendMessage(player, "You cannot make this with this workbench.") // Placeholder + return@on true + } + } + if (flatpackMode) { + WorkbenchListeners.produceFlatpack(player, deco) + } else { + BuildingUtils.buildDecoration(player, hotspot, deco, hotspotObj) + } + return@on true + } + else -> return@on false + } + } + + /** + * Handles the interface for managing building mode and POH guests. + */ + on(Components.POH_HOUSE_OPTIONS_398) { player, _, _, buttonID, _, _ -> + when (buttonID) { + HOUSE_BUTTON_BUILDMODE_ON -> { + player.houseManager.toggleBuildingMode(player, true) + return@on true + } + HOUSE_BUTTON_BUILDMODE_OFF -> { + player.houseManager.toggleBuildingMode(player, false) + return@on true + } + HOUSE_BUTTON_EXPEL_GUESTS -> { + player.houseManager.expelGuests(player) + return@on true + } + HOUSE_BUTTON_LEAVE_HOUSE -> { + if (!player.houseManager.isInHouse(player)) { + sendMessage(player, "You can't do this outside of your house.") + return@on true + } + HouseManager.leave(player) + return@on true + } + else -> return@on false + } + } + + /** + * Handles the interface for selecting a new room to add to a POH. + */ + on(Components.POH_ROOMS_402) { player, _, _, buttonID, _, _ -> + val index = buttonID - 160 + log(this.javaClass, Log.FINE, "BuildRoom Interface Index: $index") + if (index > -1 && index < RoomProperties.values().size) { + player.dialogueInterpreter.open("con:room", RoomProperties.values()[index]) // ContentAPI impl won't work for this + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/Decoration.java b/Server/src/main/content/global/skill/construction/Decoration.java index 4191cb1a9..6b48243ea 100644 --- a/Server/src/main/content/global/skill/construction/Decoration.java +++ b/Server/src/main/content/global/skill/construction/Decoration.java @@ -74,13 +74,13 @@ public enum Decoration { /** * Parlour chair spot */ - CRUDE_CHAIR (13581, 8309, 1, 58, new Item[] { new Item(Items.PLANK_960, 2) }), - WOODEN_CHAIR (13582, 8310, 8, 87, new Item[] { new Item(Items.PLANK_960, 3) }), - ROCKING_CHAIR (13583, 8311, 14, 87, new Item[] { new Item(Items.PLANK_960, 3) }), - OAK_CHAIR (13584, 8312, 19, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - OAK_ARMCHAIR (13585, 8313, 26, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), - TEAK_ARMCHAIR (13586, 8314, 35, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), - MAHOGANY_ARMCHAIR(13587, 8315, 50, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + CRUDE_CHAIR (13581, 8309, 1, 58, new Item[] { new Item(Items.PLANK_960, 2) }, Items.CRUDE_WOODEN_CHAIR_8496), + WOODEN_CHAIR (13582, 8310, 8, 87, new Item[] { new Item(Items.PLANK_960, 3) }, Items.WOODEN_CHAIR_8498), + ROCKING_CHAIR (13583, 8311, 14, 87, new Item[] { new Item(Items.PLANK_960, 3) }, Items.ROCKING_CHAIR_8500), + OAK_CHAIR (13584, 8312, 19, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }, Items.OAK_CHAIR_8502), + OAK_ARMCHAIR (13585, 8313, 26, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }, Items.OAK_ARMCHAIR_8504), + TEAK_ARMCHAIR (13586, 8314, 35, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }, Items.TEAK_ARMCHAIR_8506), + MAHOGANY_ARMCHAIR(13587, 8315, 50, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }, Items.MAHOGANY_ARMCHAIR_8508), /** * Rugs rugs rugs @@ -112,9 +112,9 @@ public enum Decoration { /** * Bookcases */ - WOODEN_BOOKCASE (13597, 8319, 4, 115, new Item[] { new Item(Items.PLANK_960, 4) }), - OAK_BOOKCASE (13598, 8320, 29, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), - MAHOGANY_BOOKCASE(13599, 8321, 40, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), + WOODEN_BOOKCASE (13597, 8319, 4, 115, new Item[] { new Item(Items.PLANK_960, 4) }, Items.WOODEN_BOOKCASE_8510), + OAK_BOOKCASE (13598, 8320, 29, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }, Items.OAK_BOOKCASE_8512), + MAHOGANY_BOOKCASE(13599, 8321, 40, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }, Items.MAHOGANY_BKCASE_8514), /** * Kitchen Beer Barrels @@ -122,19 +122,19 @@ public enum Decoration { * Basic: 1, Cider: 14, Asgarnian: 24, Greenman's: 29, D.Bitter: 39, Chef's: 54 * */ - BASIC_BEER_BARREL (13568, 8239, 7, 87, new Item[] { new Item(Items.PLANK_960, 3) }), - CIDER_BARREL (13569, 8240, 12, 91, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.CIDER_5763, 8) }), - ASGARNIAN_ALE_BARREL(13570, 8241, 18, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.ASGARNIAN_ALE_1905, 8) }), - GREENMANS_ALE_BARREL(13571, 8242, 26, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.GREENMANS_ALE_1909, 8) }), - DRAGON_BITTER_BARREL(13572, 8243, 36, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.DRAGON_BITTER_1911, 8), new Item(Items.STEEL_BAR_2353, 2) }), - CHEFS_DELIGHT_BARREL(13573, 8244, 48, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.CHEFS_DELIGHT_5755, 8), new Item(Items.STEEL_BAR_2353, 2) }), + BASIC_BEER_BARREL (13568, 8239, 7, 87, new Item[] { new Item(Items.PLANK_960, 3) }, Items.BEER_BARREL_8516), + CIDER_BARREL (13569, 8240, 12, 91, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.CIDER_5763, 8) }, Items.CIDER_BARREL_8518), + ASGARNIAN_ALE_BARREL(13570, 8241, 18, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.ASGARNIAN_ALE_1905, 8) }, Items.ASGARNIAN_ALE_8520), + GREENMANS_ALE_BARREL(13571, 8242, 26, 184, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.GREENMANS_ALE_1909, 8) }, Items.GREENMANS_ALE_8522), + DRAGON_BITTER_BARREL(13572, 8243, 36, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.DRAGON_BITTER_1911, 8), new Item(Items.STEEL_BAR_2353, 2) }, Items.DRAGON_BITTER_8524), + CHEFS_DELIGHT_BARREL(13573, 8244, 48, 224, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.CHEFS_DELIGHT_5755, 8), new Item(Items.STEEL_BAR_2353, 2) }, Items.CHEFS_DELIGHT_8526), /** * Kitchen Tables! */ - KITCHEN_WOODEN_TABLE(13577, 8246, 12, 87, new Item[] { new Item(Items.PLANK_960, 3) }), - KITCHEN_OAK_TABLE (13578, 8247, 32, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), - KITCHEN_TEAK_TABLE (13579, 8248, 52, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), + KITCHEN_WOODEN_TABLE(13577, 8246, 12, 87, new Item[] { new Item(Items.PLANK_960, 3) }, Items.WOOD_KITCHEN_TABLE_8528), + KITCHEN_OAK_TABLE (13578, 8247, 32, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }, Items.OAK_KITCHEN_TABLE_8530), + KITCHEN_TEAK_TABLE (13579, 8248, 52, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }, Items.TEAK_KITCHEN_TABLE_8532), /** * Kitchen Stoves @@ -182,24 +182,24 @@ public enum Decoration { /** * Dining room tables */ - DINING_TABLE_WOOD (13293, 8246, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }), - DINING_TABLE_OAK (13294, 8247, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), - DINING_TABLE_CARVED_OAK (13295, 8247, 31, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }), - DINING_TABLE_TEAK (13296, 8248, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), - DINING_TABLE_CARVED_TEAK(13297, 8248, 45, 600, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }), - DINING_TABLE_MAHOGANY (13298, 8120, 52, 840, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }), - DINING_TABLE_OPULENT (13299, 8121, 72, 3100, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 4), new Item(Items.MARBLE_BLOCK_8786, 2) }), + DINING_TABLE_WOOD (13293, 8246, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }, Items.WOOD_DINING_TABLE_8548), + DINING_TABLE_OAK (13294, 8247, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }, Items.OAK_DINING_TABLE_8550), + DINING_TABLE_CARVED_OAK (13295, 8247, 31, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }, Items.CARVED_OAK_TABLE_8552), + DINING_TABLE_TEAK (13296, 8248, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }, Items.TEAK_TABLE_8554), + DINING_TABLE_CARVED_TEAK(13297, 8248, 45, 600, new Item[] { new Item(Items.TEAK_PLANK_8780, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4) }, Items.CARVED_TEAK_TABLE_8556), + DINING_TABLE_MAHOGANY (13298, 8120, 52, 840, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }, Items.MAHOGANY_TABLE_8558), + DINING_TABLE_OPULENT (13299, 8121, 72, 3100, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6), new Item(Items.BOLT_OF_CLOTH_8790, 4), new Item(Items.GOLD_LEAF_8784, 4), new Item(Items.MARBLE_BLOCK_8786, 2) }, Items.OPULENT_TABLE_8560), /** * Dining room benches */ - BENCH_WOODEN (13300, 8108, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }), - BENCH_OAK (13301, 8109, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), - BENCH_CARVED_OAK (13302, 8110, 31, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), - BENCH_TEAK (13303, 8111, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), - BENCH_CARVED_TEAK(13304, 8112, 44, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), - BENCH_MAHOGANY (13305, 8113, 52, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }), - BENCH_GILDED (13306, 8114, 61, 1760, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4) }), + BENCH_WOODEN (13300, 8108, 10, 115, new Item[] { new Item(Items.PLANK_960, 4) }, Items.WOODEN_BENCH_8562), + BENCH_OAK (13301, 8109, 22, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }, Items.OAK_BENCH_8564), + BENCH_CARVED_OAK (13302, 8110, 31, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }, Items.CARVED_OAK_BENCH_8566), + BENCH_TEAK (13303, 8111, 38, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }, Items.TEAK_DINING_BENCH_8568), + BENCH_CARVED_TEAK(13304, 8112, 44, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }, Items.CARVED_TEAK_BENCH_8570), + BENCH_MAHOGANY (13305, 8113, 52, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 6) }, Items.MAHOGANY_BENCH_8572), + BENCH_GILDED (13306, 8114, 61, 1760, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784, 4) }, Items.GILDED_BENCH_8574), /** * Dining room bell-pulls @@ -209,7 +209,7 @@ public enum Decoration { FANCY_BELL_PULL(13309, 8101, 33, 58, new Item[] { new Item(Items.TEAK_PLANK_8780), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784) }), /** - * Workshop workbench + * Workshop workbenches */ WORKBENCH_WOODEN (13704, 8375, 17, 143, new Item[] { new Item(Items.PLANK_960, 5) }), WORKBENCH_OAK (13705, 8376, 32, 300, new Item[] { new Item(Items.OAK_PLANK_8778, 5) }), @@ -217,6 +217,12 @@ public enum Decoration { WORKBENCH_WITH_VICE (13707, 8378, 62, 750, new Item[] { new Item(Items.STEEL_FRAMED_BENCH_8377), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), WORKBENCH_WITH_LATHE (13708, 8379, 77, 1000, new Item[] { new Item(Items.OAK_WORKBENCH_8376), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), + /** + * Workshop stools (linked to workbench/crafting table hotspots) + */ + WORKSHOP_STOOL (13719, -1, 1, 0), + WORKSHOP_STOOL_OAK(13720, -1, 1, 0), + /** * Workshop repair benches/stands */ @@ -425,30 +431,30 @@ public enum Decoration { /** * Bedroom decorations. */ - WOODEN_BED (13148, 8031, 20, 117, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - OAK_BED (13149, 8032, 30, 210, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - LARGE_OAK_BED (13150, 8033, 34, 330, new Item[] { new Item(Items.OAK_PLANK_8778, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - TEAK_BED (13151, 8034, 40, 300, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - LARGE_TEAK_BED (13152, 8035, 45, 480, new Item[] { new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - FOUR_POSTER (13153, 8036, 53, 450, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }), - GILDED_FOUR_POSTER(13154, 8037, 60, 1330, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 2) }), - OAK_CLOCK (13169, 8052, 25, 142, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.CLOCKWORK_8792) }), - TEAK_CLOCK (13170, 8053, 55, 202, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CLOCKWORK_8792) }), - GILDED_CLOCK (13171, 8054, 85, 602, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.CLOCKWORK_8792), new Item(Items.GOLD_LEAF_8784) }), - SHAVING_STAND (13162, 8045, 21, 30, new Item[] { new Item(Items.PLANK_960), new Item(Items.MOLTEN_GLASS_1775) }), - OAK_SHAVING_STAND (13163, 8046, 29, 61, new Item[] { new Item(Items.OAK_PLANK_8778), new Item(Items.MOLTEN_GLASS_1775) }), - OAK_DRESSER (13164, 8047, 37, 121, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775) }), - TEAK_DRESSER (13165, 8048, 46, 181, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775) }), - FANCY_TEAK_DRESSER(13166, 8049, 56, 182, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2) }), - MAHOGANY_DRESSER (13167, 8050, 64, 281, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775) }), - GILDED_DRESSER (13168, 8051, 74, 582, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.GOLD_LEAF_8784) }), - SHOE_BOX (13155, 8038, 20, 58, new Item[] { new Item(Items.PLANK_960, 2) }), - OAK_DRAWERS (13156, 8039, 27, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - OAK_WARDROBE (13157, 8040, 39, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), - TEAK_DRAWERS (13158, 8041, 51, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), - TEAK_WARDROBE (13159, 8042, 63, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), - MAHOGANY_WARDROBE (13160, 8043, 75, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), - GILDED_WARDROBE (13161, 8044, 87, 720, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784) }), + WOODEN_BED (13148, 8031, 20, 117, new Item[] { new Item(Items.PLANK_960, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.WOODEN_BED_8576), + OAK_BED (13149, 8032, 30, 210, new Item[] { new Item(Items.OAK_PLANK_8778, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.OAK_BED_8578), + LARGE_OAK_BED (13150, 8033, 34, 330, new Item[] { new Item(Items.OAK_PLANK_8778, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.LARGE_OAK_BED_8580), + TEAK_BED (13151, 8034, 40, 300, new Item[] { new Item(Items.TEAK_PLANK_8780, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.TEAK_BED_8582), + LARGE_TEAK_BED (13152, 8035, 45, 480, new Item[] { new Item(Items.TEAK_PLANK_8780, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.LARGE_TEAK_BED_8584), + FOUR_POSTER (13153, 8036, 53, 450, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3), new Item(Items.BOLT_OF_CLOTH_8790, 2) }, Items.FOUR_POSTER_8586), + GILDED_FOUR_POSTER(13154, 8037, 60, 1330, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 5), new Item(Items.BOLT_OF_CLOTH_8790, 2), new Item(Items.GOLD_LEAF_8784, 2) }, Items.GILDED_4_POSTER_8588), + OAK_CLOCK (13169, 8052, 25, 142, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.CLOCKWORK_8792) }, Items.OAK_CLOCK_8590), + TEAK_CLOCK (13170, 8053, 55, 202, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.CLOCKWORK_8792) }, Items.TEAK_CLOCK_8592), + GILDED_CLOCK (13171, 8054, 85, 602, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.CLOCKWORK_8792), new Item(Items.GOLD_LEAF_8784) }, Items.GILDED_CLOCK_8594), + SHAVING_STAND (13162, 8045, 21, 30, new Item[] { new Item(Items.PLANK_960), new Item(Items.MOLTEN_GLASS_1775) }, Items.SHAVING_STAND_8596), + OAK_SHAVING_STAND (13163, 8046, 29, 61, new Item[] { new Item(Items.OAK_PLANK_8778), new Item(Items.MOLTEN_GLASS_1775) }, Items.OAK_SHAVING_STAND_8598), + OAK_DRESSER (13164, 8047, 37, 121, new Item[] { new Item(Items.OAK_PLANK_8778, 2), new Item(Items.MOLTEN_GLASS_1775) }, Items.OAK_DRESSER_8600), + TEAK_DRESSER (13165, 8048, 46, 181, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775) }, Items.TEAK_DRESSER_8602), + FANCY_TEAK_DRESSER(13166, 8049, 56, 182, new Item[] { new Item(Items.TEAK_PLANK_8780, 2), new Item(Items.MOLTEN_GLASS_1775, 2) }, Items.FANCY_TEAK_DRESSER_8604), + MAHOGANY_DRESSER (13167, 8050, 64, 281, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775) }, Items.MAHOGANY_DRESSER_8606), + GILDED_DRESSER (13168, 8051, 74, 582, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.MOLTEN_GLASS_1775, 2), new Item(Items.GOLD_LEAF_8784) }, Items.GILDED_DRESSER_8608), + SHOE_BOX (13155, 8038, 20, 58, new Item[] { new Item(Items.PLANK_960, 2) }, Items.SHOE_BOX_8610), + OAK_DRAWERS (13156, 8039, 27, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }, Items.OAK_DRAWERS_8612), + OAK_WARDROBE (13157, 8040, 39, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }, Items.OAK_WARDROBE_8614), + TEAK_DRAWERS (13158, 8041, 51, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }, Items.TEAK_DRAWERS_8616), + TEAK_WARDROBE (13159, 8042, 63, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }, Items.TEAK_WARDROBE_8618), + MAHOGANY_WARDROBE (13160, 8043, 75, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }, Items.MAHOGANY_DROBE_8620), + GILDED_WARDROBE (13161, 8044, 87, 720, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2), new Item(Items.GOLD_LEAF_8784) }, Items.GILDED_WARDROBE_8622), /** * Quest hall decorations. @@ -502,31 +508,31 @@ public enum Decoration { /** * Costume room decorations. */ - OAK_TREASURE_CHEST (18804, 9839, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - TEAK_TREASURE_CHEST (18806, 9840, 66, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), - MAHOGANY_TREASURE_CHEST(18808, 9841, 84, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), - OAK_ARMOUR_CASE (18778, 9826, 46, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }), - TEAK_ARMOUR_CASE (18780, 9827, 64, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }), - MGANY_ARMOUR_CASE (18782, 9828, 82, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }), - OAK_MAGIC_WARDROBE (18784, 9829, 42, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), - C_OAK_MAGIC_WARDROBE (18786, 9830, 51, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }), - TEAK_MAGIC_WARDROBE (18788, 9831, 60, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), - C_TEAK_MAGIC_WARDROBE (18790, 9832, 69, 540, new Item[] { new Item(Items.TEAK_PLANK_8780, 6) }), - MGANY_MAGIC_WARDROBE (18792, 9833, 78, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }), - GILDED_MAGIC_WARDROBE (18794, 9834, 87, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }), - MARBLE_MAGIC_WARDROBE (18796, 9835, 96, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), - OAK_CAPE_RACK (18766, 9817, 54, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }), - TEAK_CAPE_RACK (18767, 9818, 63, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }), - MGANY_CAPE_RACK (18768, 9819, 72, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }), - GILDED_CAPE_RACK (18769, 9820, 81, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }), - MARBLE_CAPE_RACK (18770, 9821, 90, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }), - MAGIC_CAPE_RACK (18771, 9822, 99, 1000, new Item[] { new Item(Items.MAGIC_STONE_8788) }), - OAK_TOY_BOX (18798, 9836, 50, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - TEAK_TOY_BOX (18800, 9837, 68, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), - MAHOGANY_TOY_BOX (18802, 9838, 86, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), - OAK_COSTUME_BOX (18772, 9823, 44, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }), - TEAK_COSTUME_BOX (18774, 9824, 62, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }), - MAHOGANY_COSTUME_BOX (18776, 9825, 80, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }), + OAK_TREASURE_CHEST (18804, 9839, 48, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }, Items.OAK_TREASURE_CHEST_9862), + TEAK_TREASURE_CHEST (18806, 9840, 66, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }, Items.TEAK_TREAS_CHEST_9863), + MAHOGANY_TREASURE_CHEST(18808, 9841, 84, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }, Items.MGANY_TREAS_CHEST_9864), + OAK_ARMOUR_CASE (18778, 9826, 46, 180, new Item[] { new Item(Items.OAK_PLANK_8778, 3) }, Items.OAK_ARMOUR_CASE_9859), + TEAK_ARMOUR_CASE (18780, 9827, 64, 270, new Item[] { new Item(Items.TEAK_PLANK_8780, 3) }, Items.TEAK_ARMOUR_CASE_9860), + MGANY_ARMOUR_CASE (18782, 9828, 82, 420, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 3) }, Items.MGANY_ARMR_CASE_9861), + OAK_MAGIC_WARDROBE (18784, 9829, 42, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }, Items.OAK_MAGIC_WARDROBE_9852), + C_OAK_MAGIC_WARDROBE (18786, 9830, 51, 360, new Item[] { new Item(Items.OAK_PLANK_8778, 6) }, Items.CARVED_OAK_MAGIC_WARDROBE_9853), + TEAK_MAGIC_WARDROBE (18788, 9831, 60, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }, Items.TEAK_MAGIC_WARDROBE_9854), + C_TEAK_MAGIC_WARDROBE (18790, 9832, 69, 540, new Item[] { new Item(Items.TEAK_PLANK_8780, 6) }, Items.CARVED_TEAK_MAGIC_WARDROBE_9855), + MGANY_MAGIC_WARDROBE (18792, 9833, 78, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }, Items.MAHOGANY_MAGIC_WARDROBE_9856), + GILDED_MAGIC_WARDROBE (18794, 9834, 87, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }, Items.GILDED_MAGIC_WARDROBE_9857), + MARBLE_MAGIC_WARDROBE (18796, 9835, 96, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }, Items.MARBLE_MAGIC_WARDROBE_9858), + OAK_CAPE_RACK (18766, 9817, 54, 240, new Item[] { new Item(Items.OAK_PLANK_8778, 4) }, Items.OAK_CAPE_RACK_9843), + TEAK_CAPE_RACK (18767, 9818, 63, 360, new Item[] { new Item(Items.TEAK_PLANK_8780, 4) }, Items.TEAK_CAPE_RACK_9844), + MGANY_CAPE_RACK (18768, 9819, 72, 560, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4) }, Items.MGANY_CAPE_RACK_9845), + GILDED_CAPE_RACK (18769, 9820, 81, 860, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 4), new Item(Items.GOLD_LEAF_8784) }, Items.GILDED_CAPE_RACK_9846), + MARBLE_CAPE_RACK (18770, 9821, 90, 500, new Item[] { new Item(Items.MARBLE_BLOCK_8786) }, Items.MARBLE_CAPE_RACK_9847), + MAGIC_CAPE_RACK (18771, 9822, 99, 1000, new Item[] { new Item(Items.MAGIC_STONE_8788) }, Items.MAGICAL_CAPE_RACK_9848), + OAK_TOY_BOX (18798, 9836, 50, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }, Items.OAK_TOY_BOX_9849), + TEAK_TOY_BOX (18800, 9837, 68, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }, Items.TEAK_TOY_BOX_9850), + MAHOGANY_TOY_BOX (18802, 9838, 86, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }, Items.MAHOGANY_TOY_BOX_9851), + OAK_COSTUME_BOX (18772, 9823, 44, 120, new Item[] { new Item(Items.OAK_PLANK_8778, 2) }, Items.OAK_COSTUME_BOX_9865), + TEAK_COSTUME_BOX (18774, 9824, 62, 180, new Item[] { new Item(Items.TEAK_PLANK_8780, 2) }, Items.TEAK_COSTUME_BOX_9866), + MAHOGANY_COSTUME_BOX (18776, 9825, 80, 280, new Item[] { new Item(Items.MAHOGANY_PLANK_8782, 2) }, Items.MAHOGANY_COS_BOX_9867), /** * Chapel decorations. @@ -684,6 +690,11 @@ public enum Decoration { FREMENNIK_WINDOW (13112, -1, 1, 0), TROPICAL_WOOD_WINDOW (10816, -1, 1, 0), FANCY_STONE_WINDOW (13117, -1, 1, 0), + + /** + * Flatpack deco for filling the flatpack hotspot. + */ + FLATPACK(-1, -1, 1, 0), ; /** @@ -716,6 +727,11 @@ public enum Decoration { */ private final Item[] refundItems; + /** + * The item id of the flatpack. + */ + private final int flatpackItemId; + /** * The tools required. */ @@ -774,6 +790,11 @@ public enum Decoration { this.reqsText = generateDefaultReqsText(items, getNailAmount()); } + Decoration(int objectId, int interfaceItem, int level, int experience, Item[] items, int flatpackItemId) { + this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, new Item[] {}, new String[] {}, flatpackItemId); + this.reqsText = generateDefaultReqsText(items, getNailAmount()); + } + /** * Constructs a new object, default refund items, default requirements text. * @param objectId The object id. @@ -827,6 +848,10 @@ public enum Decoration { * @param reqsText The requirements text to be shown in the interface. */ Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems, String[] reqsText) { + this(objectId, interfaceItem, level, experience, tools, items, refundItems, reqsText, -1); + } + + Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems, String[] reqsText, int flatpackItemId) { this.objectId = objectId; this.objectIds = null; this.interfaceItem = interfaceItem; @@ -835,6 +860,7 @@ public enum Decoration { this.tools = tools; this.items = items; this.refundItems = refundItems; + this.flatpackItemId = flatpackItemId; if (reqsText.length > 0) { System.arraycopy(reqsText, 0, this.reqsText, 0, reqsText.length); } @@ -881,6 +907,7 @@ public enum Decoration { this.tools = tools; this.items = items; this.refundItems = refundItems; + this.flatpackItemId = -1; } /** @@ -931,6 +958,15 @@ public enum Decoration { return null; } + public static Decoration forFlatpackItemId(int flatpackId) { + for (Decoration d : Decoration.values()) { + if (d.getFlatpackItemID() == flatpackId) { + return d; + } + } + return null; + } + /** * Gets the amount of nails required for this hotspot. * @return The amount of nails. @@ -1020,6 +1056,14 @@ public enum Decoration { return objectIds; } + /** + * Gets the flatpack item ID if it exists. + * @return the int ID. + */ + public int getFlatpackItemID() { + return flatpackItemId; + } + /** * If this node should be invisible to user build options * @return true if so. diff --git a/Server/src/main/content/global/skill/construction/FlatpackListener.kt b/Server/src/main/content/global/skill/construction/FlatpackListener.kt new file mode 100644 index 000000000..d502415d7 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/FlatpackListener.kt @@ -0,0 +1,48 @@ +package content.global.skill.construction + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import org.rs09.consts.Items + +/** + * Handles building furniture using flatpacks + * @author Roderik + * @author Bishop + */ + +class FlatpackListener : InteractionListener { + val flatpacks = Decoration.values().map { it.flatpackItemID }.filter { it != -1 }.toIntArray() + val hotspots = BuildHotspot.values().map { it.objectId }.toIntArray() + + override fun defineListeners() { + for (hotspot in hotspots) { + onUseWith(IntType.SCENERY, flatpacks, hotspot) { player, used, with -> + return@onUseWith buildFlatpackOnHotspot(player, used.asItem(), with as Scenery) + } + } + } + + private fun buildFlatpackOnHotspot(player: Player, used: Item, with: Scenery): Boolean { + val hotspotUsed = player.houseManager.getHotspot(with) + val decorationUsed = Decoration.forFlatpackItemId(used.id) + + if (!hotspotUsed.hotspot.decorations.contains(decorationUsed)) { + sendMessage(player, "You can't build that here.") + return false + } + if (!inInventory(player, Items.HAMMER_2347) || !inInventory(player, Items.SAW_8794)) { + sendMessage(player, "You need a hammer and a saw to build this.") + return false + } + if (removeItem(player, used)) { + BuildingUtils.buildDecoration(player, hotspotUsed, decorationUsed, with.asScenery(), true, true) + return true + } else { + return false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/HouseZone.java b/Server/src/main/content/global/skill/construction/HouseZone.java index 31a57f6f8..6b2ce9656 100644 --- a/Server/src/main/content/global/skill/construction/HouseZone.java +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -1,8 +1,12 @@ package content.global.skill.construction; +import content.global.skill.construction.decoration.workshop.WorkbenchListeners; import core.api.Container; import core.game.world.map.Location; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import org.rs09.consts.Items; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; @@ -80,6 +84,23 @@ public final class HouseZone extends MapZone { return super.enter(e); } + @Override + public boolean move(Entity e, Location from, Location to) { + if (e instanceof Player) { + Player p = (Player) e; + if (getAttribute(p, WorkbenchListeners.ATTRIBUTE_WORKBENCH_SEATED, false)) { + p.getPulseManager().clear(); + p.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(p)); + if (!getAttribute(p, WorkbenchListeners.ATTRIBUTE_WORKBENCH_STANDING_UP, false)) { + WorkbenchListeners.standUpFromWorkbench(p); + } + return false; + } + } + return super.move(e, from, to); + } + @Override public boolean death(Entity e, Entity killer) { if (e instanceof Player) { diff --git a/Server/src/main/content/global/skill/construction/RoomProperties.java b/Server/src/main/content/global/skill/construction/RoomProperties.java index 80bc6e6ed..70236bdd4 100644 --- a/Server/src/main/content/global/skill/construction/RoomProperties.java +++ b/Server/src/main/content/global/skill/construction/RoomProperties.java @@ -135,7 +135,9 @@ public enum RoomProperties { new Hotspot(BuildHotspot.TOOL2, 6, 0), new Hotspot(BuildHotspot.TOOL1, 1, 0), new Hotspot(BuildHotspot.TOOL3, 0, 1), - new Hotspot(BuildHotspot.TOOL5, 0, 6)), + new Hotspot(BuildHotspot.TOOL5, 0, 6), + new Hotspot(BuildHotspot.CRAFTING_STOOL, 1, 3), + new Hotspot(BuildHotspot.WORKBENCH_STOOL, 3, 3)), /** * Bedroom. diff --git a/Server/src/main/content/global/skill/construction/decoration/StaySeated.kt b/Server/src/main/content/global/skill/construction/decoration/StaySeated.kt new file mode 100644 index 000000000..bbc4cbea3 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/StaySeated.kt @@ -0,0 +1,51 @@ +package content.global.skill.construction.decoration + +import core.api.animate +import core.api.getScenery +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import org.rs09.consts.Animations +import org.rs09.consts.Scenery +import kotlin.collections.get + +/** + * Makes a player stay seated by reapplying the current animation occasionally. + * An alternative to some features of ChairBenchPlugin. + * Written with workbench stools in mind. + * @author Bishop + */ + +class StaySeated : PersistTimer(10 /*ticks*/, "con:sitting", isSoft = true) { + lateinit var player: Player + + private val standAnim = 808 // Same as AppearanceCache.STAND_ANIM + + // Register new chairs and animations here + private val sittables = mapOf( + Scenery.STOOL_13719 to Animations.HUMAN_SEATED_POH_STOOL_4107, + Scenery.STOOL_13720 to Animations.HUMAN_SEATED_POH_OAK_STOOL_4108, + ) + + override fun run(entity: Entity): Boolean { + player = entity as Player + val localObj = getScenery(player.location)?.id + // Don't interrupt a player doing a different animation unless it's the idle animation + val currentAnimId = player.animator.animation?.id ?: -1 + if (localObj in sittables) { + if (currentAnimId != sittables[localObj] && + currentAnimId != standAnim && + currentAnimId != -1) { + return true + } + animate(player, sittables[localObj]) + return true + } + return false + } + + override fun getTimer(vararg args: Any): RSTimer { + return StaySeated() + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/WorkbenchListeners.kt b/Server/src/main/content/global/skill/construction/decoration/workshop/WorkbenchListeners.kt new file mode 100644 index 000000000..08d386f91 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/WorkbenchListeners.kt @@ -0,0 +1,228 @@ +package content.global.skill.construction.decoration.workshop + +import content.global.skill.construction.* +import content.global.skill.construction.decoration.StaySeated +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.map.Direction +import core.tools.ticksToCycles +import org.rs09.consts.Animations +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +/** + * Handles usage of the workbench, which allows construction of flatpacks. + * @author Bishop + */ + +class WorkbenchListeners : InteractionListener, InterfaceListener { + + companion object { + const val ATTRIBUTE_FLATPACK_MODE = "con:workbench:active" + const val ATTRIBUTE_WORKBENCH_ID = "con:workbench:id" + const val ATTRIBUTE_WORKBENCH_SELECTION = "con:workbench:hotspot" + const val ATTRIBUTE_WORKBENCH_SEATED = "con:workbench:sitting" + const val ATTRIBUTE_WORKBENCH_STANDING_UP = "con:workbench:standing" + private const val ATTRIBUTE_DIRECTIONS = "con:workbench:directions" + + private val workbenches = intArrayOf( + Scenery.WORKBENCH_13704, // lv20, wooden + Scenery.WORKBENCH_13705, // lv40, oak + Scenery.WORKBENCH_13706, // lv60, steel framed + Scenery.WORKBENCH_13707, // lv80, with vice + Scenery.WORKBENCH_13708, // lv99, with lathe + ) + + private val stools = intArrayOf( + Scenery.STOOL_13719, // wooden stool, bottom 2 tiers + Scenery.STOOL_13720, // oak stool, top 3 tiers + ) + + private val decoCategories = arrayOf( + BuildHotspot.CHAIRS_1, // ButtonID 111 + BuildHotspot.BOOKCASE, // ButtonID 112 + BuildHotspot.BARRELS, // ButtonID 113 + BuildHotspot.KITCHEN_TABLE, // ButtonID 114 + BuildHotspot.DINING_TABLE, // ButtonID 115 + BuildHotspot.DINING_BENCH_1, // ButtonID 116 + BuildHotspot.BED, // ButtonID 117 + BuildHotspot.DRESSER, // ButtonID 118 + BuildHotspot.DRAWERS, // ButtonID 119 + BuildHotspot.CLOCK, // ButtonID 120 + BuildHotspot.CAPE_RACK, // ButtonID 121 + BuildHotspot.MAGIC_WARDROBE, // ButtonID 122 + BuildHotspot.ARMOUR_CASE, // ButtonID 123 + BuildHotspot.TREASURE_CHEST, // ButtonID 124 + BuildHotspot.COSTUME_BOX, // ButtonID 125 + BuildHotspot.TOY_BOX, // ButtonID 126 + ) + + /** + * Returns the maximum construction level of decoration that can be built at a given workbench by id. + */ + fun getBenchLevel(workbenchId: Int): Int { + return ((workbenches.indexOf(workbenchId) + 1) * 20) + } + + /** + * Animates the player crafting a flatpack, and exchanges its materials for the flatpack item. + * Flatpacks are free to produce for players with admin rights. + */ + fun produceFlatpack(player: Player, deco: Decoration) { + val reward = deco.flatpackItemID + val animation = if (getScenery(player.location)?.id == Scenery.STOOL_13720) { Animations.HUMAN_CRAFT_POH_OAK_STOOL_4110 } else { Animations.HUMAN_CRAFT_POH_STOOL_4109 } + if (reward == -1) return + if (removeItemsIfPlayerHasEnough(player, *deco.items) || player.isAdmin) { + closeInterface(player) + lock(player, getAnimation(animation).duration) + animate(player, animation) + rewardXP(player, Skills.CONSTRUCTION, deco.experience.toDouble()) + addItemOrDrop(player, reward) + } + } + + /** + * Animates the player getting up from the workbench's stool. + * This occurs on the player's first attempt at movement off of the workbench. + */ + @JvmStatic + fun standUpFromWorkbench(player: Player) { + val directions = getAttribute(player, ATTRIBUTE_DIRECTIONS, arrayOf(Direction.WEST, Direction.NORTH, Direction.EAST)) + val animation = if (getScenery(player.location)?.id == Scenery.STOOL_13720) { Animations.HUMAN_SEATED_POH_OAK_STOOL_4108 } else { Animations.HUMAN_SEATED_POH_STOOL_4107 } + setAttribute(player, ATTRIBUTE_WORKBENCH_STANDING_UP, true) + lock(player, 1) + animate(player, animation) + queueScript(player, 0, QueueStrength.SOFT) { + forceMove(player, player.location, player.location.transform(directions[2], 1), + 0, ticksToCycles(1), directions[2], Animations.HUMAN_STAND_POH_STOOL_4105) { + removeAttributes(player, ATTRIBUTE_DIRECTIONS, ATTRIBUTE_WORKBENCH_SEATED, ATTRIBUTE_WORKBENCH_STANDING_UP) + removeTimer(player) + } + return@queueScript stopExecuting(player) + } + } + } + + override fun defineListeners() { + /** + * Animates the player sitting down at the workbench, and opens the workbench interface. + */ + on(workbenches, IntType.SCENERY, "work-at") { player, node -> + setAttribute(player, ATTRIBUTE_WORKBENCH_ID, node.id) + // Skips the animation if the player is already sitting down. + if (getAttribute(player, ATTRIBUTE_WORKBENCH_SEATED, false)) { + val animation = if (getScenery(player.location)?.id == Scenery.STOOL_13720) { Animations.HUMAN_SEATED_POH_OAK_STOOL_4108 } else { Animations.HUMAN_SEATED_POH_STOOL_4107 } + animate(player, animation) + openInterface(player, Components.POH_WORKBENCH_397) + return@on true + } + // Ascertains the room rotation from where the stool is, to coordinate animations + val baseX = node.location.x - node.location.chunkOffsetX + val baseY = node.location.y - node.location.chunkOffsetY + for (x in 3..4) { + for (y in 3..4) { + val obj = getScenery(baseX + x, baseY + y, node.location.z) ?: continue + if (obj.id in stools) { + val directions = when (Pair(x,y)) { + Pair(3,3) -> arrayOf(Direction.WEST, Direction.NORTH, Direction.EAST) + Pair(3,4) -> arrayOf(Direction.NORTH, Direction.EAST, Direction.SOUTH) + Pair(4,3) -> arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH) + else -> arrayOf(Direction.EAST, Direction.SOUTH, Direction.WEST) + } + setAttribute(player, ATTRIBUTE_DIRECTIONS, directions) + forceMove(player, player.location, player.location.transform(directions[0], 1), + 0, ticksToCycles(1), directions[1], Animations.HUMAN_SIT_POH_STOOL_4103) { + val animation = if (obj.id == Scenery.STOOL_13720) { Animations.HUMAN_SEATED_POH_OAK_STOOL_4108 } else { Animations.HUMAN_SEATED_POH_STOOL_4107 } + animate(player, animation) + setAttribute(player, ATTRIBUTE_WORKBENCH_SEATED, true) + registerTimer(player, StaySeated()) + openInterface(player, Components.POH_WORKBENCH_397) + } + return@on true + } + } + } + return@on true + } + + /** + * Handles upgrading a workbench with the minimenu option. + */ + on(workbenches, IntType.SCENERY, "upgrade") { player, node -> + // I could not find any video of how this looks or what the messages are, so all messages in this listener are placeholders. + val hotspot = Hotspot(BuildHotspot.WORKBENCH, 3, 4, 4, 4) + val nodeObj = node as core.game.node.scenery.Scenery + val resultDeco = Decoration.forObjectId(node.id + 1) + if (!player.houseManager.isBuildingMode) { + sendMessage(player, "You have to be in building mode to do this.") // I don't actually know if this is true but better safe than sorry. + return@on true + } + if (getDynLevel(player, Skills.CONSTRUCTION) < resultDeco.level) { + sendMessage(player, "You need a Construction level of ${resultDeco.level} to upgrade this.") + return@on true + } + if (!inInventory(player, Items.OAK_PLANK_8778, 2) || !inInventory(player, Items.STEEL_BAR_2353)) { + sendMessage(player, "You need two oak planks and a steel bar to upgrade this.") + return@on true + } + if (!inInventory(player, Items.HAMMER_2347) || !inInventory(player, Items.SAW_8794)) { + sendMessage(player, "You need a hammer and a saw to upgrade this.") + return@on true + } + if (removeItemsIfPlayerHasEnough(player, Item(Items.OAK_PLANK_8778, 2), Item(Items.STEEL_BAR_2353))) { + queueScript(player, 0, QueueStrength.SOFT) { stage -> + when (stage) { + 0 -> { + lock(player, BuildingUtils.BUILD_MID_ANIM.duration) + // Replaces the workbench with the upgraded form in HouseManager but not immediately in the instance + BuildingUtils.buildDecoration(player, hotspot, resultDeco, nodeObj, true, false) + return@queueScript delayScript(player, BuildingUtils.BUILD_MID_ANIM.duration) + } + 1 -> { + // Temporary replacement until POH can rebuild on next entrance with the correct workbench + replaceScenery(nodeObj, resultDeco.objectId, -1) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + return@on true + } + } + + override fun defineInterfaceListeners() { + /** + * Directs the player to the appropriate decoration building interface. + */ + on(Components.POH_WORKBENCH_397) { player, _, _, buttonID, _, _ -> + val index = buttonID - 111 + if (index < 0 || index >= decoCategories.size) return@on false + val hotspot = decoCategories[index] + setAttribute(player, ATTRIBUTE_WORKBENCH_SELECTION, hotspot) + setAttribute(player, ATTRIBUTE_FLATPACK_MODE, true) + BuildingUtils.openBuildInterface(player, hotspot) + return@on true + } + + onClose(Components.POH_WORKBENCH_397) { player, _ -> + if (!getAttribute(player, ATTRIBUTE_FLATPACK_MODE, false)) { + removeAttributes(player, ATTRIBUTE_FLATPACK_MODE, ATTRIBUTE_WORKBENCH_SELECTION, ATTRIBUTE_WORKBENCH_ID) + } + return@onClose true + } + + onClose(Components.POH_DECORATION_396) { player, _ -> + removeAttributes(player, ATTRIBUTE_FLATPACK_MODE, ATTRIBUTE_WORKBENCH_SELECTION, ATTRIBUTE_WORKBENCH_ID) + return@onClose true + } + } + +} \ No newline at end of file