Merge branch 'imp' into 'master'

Fixed Imp in a Box (hunter item)

Closes #709

See merge request 2009scape/2009scape!2479
This commit is contained in:
oftheshire 2026-07-31 16:00:56 +00:00
commit 87fb2cd74b
2 changed files with 218 additions and 174 deletions

View file

@ -0,0 +1,218 @@
package content.global.skill.hunter
import core.api.*
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.DialogueOption
import core.game.dialogue.FacialExpression
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.interaction.InterfaceListener
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.IronmanMode
import core.game.node.item.Item
import core.game.system.config.ItemConfigParser
import org.rs09.consts.Items
import org.rs09.consts.NPCs
/**
* The Imp-in-a-box that can bank items.
*/
class ImpBoxListener : InteractionListener, InterfaceListener {
// Great source: https://www.youtube.com/watch?v=k0LhRhcQ0g8
// wiki: https://runescape.wiki/w/Imp-in-a-box, https://runescape.wiki/w/Magic_box?oldid=1743149
// uses 701.cs2
companion object {
val RELEASE_IFACE = 478
private val BOXES = intArrayOf(Items.IMP_IN_A_BOX2_10027, Items.IMP_IN_A_BOX1_10028)
}
override fun defineListeners() {
// use an item with the imp box
onUseAnyWith(IntType.ITEM,*BOXES) { player, used, _ ->
bankItem(player, used.asItem())
return@onUseAnyWith true
}
// open the banking interface
on(BOXES, IntType.ITEM, "bank") {player, _ ->
openInterface(player, RELEASE_IFACE)
return@on true
}
// talk to the imp
on(BOXES, IntType.ITEM, "talk-to") {player, node ->
openDialogue(player, ImpBoxDialogueFile(node.id), NPC(NPCs.IMP_1531))
return@on true
}
}
override fun defineInterfaceListeners() {
// send the interface settings that display the deposit and examine options that get set by the cs2 file
onOpen(RELEASE_IFACE) { player, _ ->
val settings = IfaceSettingsBuilder()
.enableOptions(0, 1) // option 0 = "Deposit" (CS2 slot 1), option 1 = "Examine" (CS2 slot 2)
.build()
player.packetDispatch.sendIfaceSettings(settings, 14, RELEASE_IFACE, 0, 28)
return@onOpen true
}
on(RELEASE_IFACE) { player, _, opcode, buttonID, slot, _ ->
val clickItemButton = 14
val depositOpcode = 155
val examineOpcode = 196
val item = player.inventory[slot]
when(buttonID) {
clickItemButton -> {
when(opcode) {
depositOpcode -> bankItem(player, item)
examineOpcode -> sendMessage(player, "${item.definition.examine}")
}
}
}
return@on true
}
}
// banks the item
private fun bankItem(player: Player, item: Item) {
if (item != null) {
// check for non-bankable conditions (non-bankable items, banking the imp, ult. ironman, or in wildy)
if (!item.definition.getConfiguration(ItemConfigParser.BANKABLE, true)
|| item.id in BOXES
|| player.ironmanManager.mode == IronmanMode.ULTIMATE
|| player.skullManager.level > 20
) {
sendMessage(player, "A magical force prevents you from banking this item.")
return
}
// can't do in combat
if (player.inCombat()) {
sendMessage(player, "You can't use this while in combat.")
return
}
// attempt to add to bank
if (addItem(player, item.id, item.amount, Container.BANK)
&& removeItem(player, item)
&& decrementBox(player)
) {
closeInterface(player)
sendMessage(player, "The imp teleports away, taking the item to your bank account.")
} else {
sendMessage(player, "You cannot add this item to your bank.")
}
}
}
// decrements the box
private fun decrementBox(player: Player) : Boolean {
if (amountInInventory(player, Items.IMP_IN_A_BOX1_10028) > 0) {
// replace a 1-charge box with a 0-charge
if (removeItem(player, Items.IMP_IN_A_BOX1_10028)) {
addItem(player, Items.MAGIC_BOX_10025)
return true
}
} else if (amountInInventory(player, Items.IMP_IN_A_BOX2_10027) > 0) {
// replace a 2-charge box with a 1-charge
if (removeItem(player, Items.IMP_IN_A_BOX2_10027)) {
addItem(player, Items.IMP_IN_A_BOX1_10028)
return true
}
} else {
sendMessage(player, "ERROR: No valid imp boxes found. Please report this.")
return false
}
return true
}
}
// dialogue for the imp in a box
class ImpBoxDialogueFile(val box: Int) : DialogueLabeller() {
override fun addConversation() {
exec { player, _ ->
if (box == Items.IMP_IN_A_BOX2_10027) {
loadLabel(player, "two start")
} else {
loadLabel(player, "one start")
}
}
label("one start")
player("Hey imp, are you still there?")
npc(FacialExpression.OLD_DEFAULT, "An who's fault is that, eh? EH? I got betta fings to do dan sit ere waitin' for ya. 'Ave ya made up ya mind what else I'm luggin about?")
options(
DialogueOption("ok", "Yes I have."),
DialogueOption("no escape", "No, I'm afraid you're going to have to wait a bit longer.")
)
label("no escape")
npc(FacialExpression.OLD_DEFAULT, "Dat's alright guv'nor. I'll get back to me luvverly dream about beads. Big shiny beads. Love it!")
goto("end")
label("two start")
player("Hey imp, can you hear me?")
npc(FacialExpression.OLD_DEFAULT, "Of course I can hear ya, ya great big ape. You know,", "'veres not even enuf space to swing Bob about in 'ere.", "How about a breather? You know, stretch me pins for a", "bit?")
options(
DialogueOption("no", "No, I'm going to keep you in there.", "No, I'm going to keep you in there. I might keep you as a pet."),
DialogueOption("not bad", "It's not that bad.", "It's not that bad. You've got four big windows, charming company...er..."),
DialogueOption("wish", "Don't I get three wishes?")
)
label("no")
npc(FacialExpression.OLD_DEFAULT, "Pet!! Nah mate. I fink you'd find dat you'd be my pet!! We is not makin good pets.")
player("Really? Why not?")
npc(FacialExpression.OLD_DEFAULT, "Coz...errr...")
npc(FacialExpression.OLD_DEFAULT, "We bite! Yeah we is biting and...and...er...")
npc(FacialExpression.OLD_DEFAULT, "We is fire risk! Yeah dat's it! We be burning down your housey and stealin' all ya shiny gems. Oh, and da beads!! Mmmm, beads.")
player("Fire risk? How does that work?")
npc(FacialExpression.OLD_DEFAULT, "Is those wizzies. Dey don't like de imps so dey make us go BOOOM!!")
goto("end")
label("not bad")
npc(FacialExpression.OLD_DEFAULT, "Yeah, we's love tiny, crampt space. It be magical. But I is a busy imp, innit? Dragons needin' ticklin', shiny relics needin' stealin', you know how it goes.")
npc(FacialExpression.OLD_DEFAULT, "So, if you's know whas good for ya, you'd be lettin' me go, right?")
options(
DialogueOption("no", "No, I'm going to keep you in there.", "No, I'm going to keep you in there. I might keep you as a pet."),
DialogueOption("wish", "Don't I get three wishes?")
)
label("wish")
npc(FacialExpression.OLD_DEFAULT, "Nah, mate. Dunno what you're chirpin' about.")
player("Well, you're a magical creature aren't you? Surely I", "get some wishes for capturing you, or releasing you, or", "something?")
npc(FacialExpression.OLD_DEFAULT, "I'm finking dat you be a bit confoosed. I is an imp, not", "some namby-pamby genie or some kinda fairy. Ye can", "tell by the horns.")
npc(FacialExpression.OLD_DEFAULT, "Sayin' dat, I don't fancy being cooped up like one of", "my uncle's pigeons. Tell you what, is there anything", "you need deliverin' to the bank? I may not be no", "cunjerer or sommink like dat, but I can get about nice")
npc(FacialExpression.OLD_DEFAULT, "an quick like. If you let me scarper, I'll take a couple of", "fings to the bank for ya. You game?")
options(
DialogueOption("ok", "Okay, that sounds fair."),
DialogueOption("three", "Surely it should be three items?", "Surely it should be three items? Then it's one item per wish."),
DialogueOption("nothing", "I've got nothing I need banking right now.")
)
label("ok")
exec { player, _ ->
openInterface(player, ImpBoxListener.RELEASE_IFACE)
end()
}
label("three")
npc(FacialExpression.OLD_DEFAULT, "I've already told ya, I ain't no bloomin' fairy. Besides, you know wot dey say, three's a crowd innit? I don't fink I can hop about carryin' more dan 2 fings.")
options(
DialogueOption("ok", "Okay, that sounds fair."),
DialogueOption("nothing", "I've got nothing I need banking right now.")
)
label("nothing")
npc(FacialExpression.OLD_DEFAULT, "Great, just blinkin great, dat is. I'll just sit about", "countin' zombie sheep then. One...two...two and a", "bit...three and a bit more... I don't fink sheep 'ave dat", "many legs...")
goto("end")
}
}

View file

@ -1,174 +0,0 @@
package content.global.skill.hunter;
import core.cache.def.impl.ItemDefinition;
import core.game.component.Component;
import core.game.component.ComponentDefinition;
import core.game.component.ComponentPlugin;
import core.plugin.Initializable;
import core.game.dialogue.DialogueInterpreter;
import core.game.dialogue.DialoguePlugin;
import core.game.dialogue.FacialExpression;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.net.packet.PacketRepository;
import core.net.packet.context.ContainerContext;
import core.net.packet.out.ContainerPacket;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import core.tools.RandomFunction;
/**
* Handles the imp box.
* @author Taylor
*/
@Initializable
public class ImpBoxPlugin extends OptionHandler {
/**
* The item ids.
*/
private static final int[] IDS = new int[] { 10028, 10027 };
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ClassScanner.definePlugin(new ImpInterfaceHandler(null));
for (int id : IDS) {
ItemDefinition.forId(id).getHandlers().put("option:bank", this);
ItemDefinition.forId(id).getHandlers().put("option:talk-to", this);
}
ClassScanner.definePlugin(new ImpBoxDialogue());
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
switch (option) {
case "bank":
Component component = new Component(478);
component.setPlugin(new ImpInterfaceHandler((Item) node));
player.getInterfaceManager().open(component);
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 478, 61, 91, player.getInventory(), true));
break;
case "talk-to":
player.getDialogueInterpreter().open("imp-box");
break;
}
return true;
}
@Override
public boolean isWalk() {
return false;
}
/**
* Handles talk-to dialogue on the imp box.
* @author Taylor
*/
public static class ImpBoxDialogue extends DialoguePlugin {
/**
* The messages to send.
*/
private static final String[] MESSAGES = { "Let me outa here!", "Errgghh..", "Well look who it is.", "What are you looking at?" };
/**
* Constructs a new {@code ImpBoxDialogue} {@code Object}.
*/
public ImpBoxDialogue() {
/**
* empty.
*/
}
/**
* Constructs a new {@code ImpBoxDialogue} {@code Object}.
* @param player The player.
*/
public ImpBoxDialogue(Player player) {
super(player);
}
@Override
public DialoguePlugin newInstance(Player player) {
return new ImpBoxDialogue(player);
}
@Override
public boolean open(Object... args) {
interpreter.sendDialogues(708, FacialExpression.FURIOUS, MESSAGES[RandomFunction.getRandom(MESSAGES.length - 1)]);
return true;
}
@Override
public boolean handle(int interfaceId, int buttonId) {
end();
return true;
}
@Override
public int[] getIds() {
return new int[] { DialogueInterpreter.getDialogueKey("imp-box") };
}
}
/**
* Handles the imp interface.
* @author Taylor
*/
public class ImpInterfaceHandler extends ComponentPlugin {
/**
* The message to show when the imp is gone.
*/
private static final String FINISHING_MESSAGE = "The imp teleports away, taking the item to your bank account.";
/**
* The box the player is using.
*/
private Item box;
/**
* Constructs a new {@code ImpInterfaceHandler} {@code Object}.
* @param box The box the player is using.
*/
public ImpInterfaceHandler(Item box) {
this.box = box;
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ComponentDefinition.forId(478).setPlugin(this);
return this;
}
@Override
public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) {
Item item = player.getInventory().get(slot);
if (item != null) {
if (player.getBank().canAdd(item) && item.getId() != box.getId()) {
player.getDialogueInterpreter().close();
player.getInventory().remove(item);
player.getBank().add(item);
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 478, 61, 91, player.getInventory(), true));
if (box.getId() == IDS[1]) {
int boxSlot = player.getInventory().getSlot(box);
player.getInventory().replace((box = new Item(IDS[0])), boxSlot);
} else if (box.getId() == IDS[0]) {
int boxSlot = player.getInventory().getSlot(box);
player.getInventory().replace(new Item(10025), boxSlot);
player.getInterfaceManager().close(component);
player.sendMessage(FINISHING_MESSAGE);
}
}
} else {
player.sendMessage("You cannot add this item to your bank.");
return false;
}
return true;
}
}
}