mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-20 05:20:22 -07:00
Completely rewrote the shop system.
Shop restocking now works properly, with per-item-in-shop granularity as well. (Thanos tool update released that allows defining this) Shops properly restrict ironmen from buying from player stock/overstocked items Fully functioning individualized player stock support (need to set a server config variable to enable, disabled by default. Specifically, set personalized_shops = true in the world section of the config.) The shop pricing formula now scales up/down with the authentic ratios based on stock quantity. Buying or selling, for example, 1000 of an item, now does the proper calculation for each individual item in the purchase/sale rather than exchanging the full 1000 for the initial price at the time. (For example, in the old shops, feather price at full stock was, say, 6gp. You could buy 1000 feathers at once for 6000gp. Authentically, the pricing for each feather should scale as the stock goes down. Now 1000 feathers at once costs a bit over 7k gp, despite the initial price remaining 6gp.) General store player stock is now shared between all general stores
This commit is contained in:
parent
f9aea18497
commit
e958b8d255
202 changed files with 1910 additions and 2385 deletions
|
|
@ -8,7 +8,9 @@ import core.game.node.entity.player.Player;
|
|||
* @date 5/02/2013
|
||||
* @author Stacx
|
||||
* @author Emperor
|
||||
* @deprecated This is a really bad way of doing what this class needs to do. Please use {@link api.IfaceSettingsBuilder}.
|
||||
*/
|
||||
@Deprecated()
|
||||
public final class BitregisterAssembler {
|
||||
|
||||
/**
|
||||
|
|
@ -102,7 +104,7 @@ public final class BitregisterAssembler {
|
|||
if (offset >= length) {
|
||||
throw new RuntimeException("Offset cannot excess length. length = " + length);
|
||||
}
|
||||
player.getPacketDispatch().sendAccessMask(assembler.calculateRegister(), childIndex, interfaceIndex, offset, length);
|
||||
player.getPacketDispatch().sendIfaceSettings(assembler.calculateRegister(), childIndex, interfaceIndex, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -117,11 +117,11 @@ public final class BankContainer extends Container {
|
|||
player.getInventory().getListeners().add(listener);
|
||||
player.getInventory().refresh();
|
||||
player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 73, 762, 0, ServerConstants.BANK_SIZE);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 73, 762, 0, ServerConstants.BANK_SIZE);
|
||||
BitregisterAssembler assembly = new BitregisterAssembler(0, 1, 2, 3, 4, 5);
|
||||
assembly.enableExamineOption();
|
||||
assembly.enableSlotSwitch();
|
||||
player.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 0, 763, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(assembly.calculateRegister(), 0, 763, 0, 27);
|
||||
player.getPacketDispatch().sendRunScript(1451, "");
|
||||
open = true;
|
||||
setTabConfigurations();
|
||||
|
|
@ -148,11 +148,11 @@ public final class BankContainer extends Container {
|
|||
player.getInventory().getListeners().add(player.getBank().listener);
|
||||
player.getInventory().refresh();
|
||||
player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 73, 762, 0, SIZE);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 73, 762, 0, SIZE);
|
||||
BitregisterAssembler assembly = new BitregisterAssembler(0, 1, 2, 3, 4, 5);
|
||||
assembly.enableExamineOption();
|
||||
assembly.enableSlotSwitch();
|
||||
player.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 0, 763, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(assembly.calculateRegister(), 0, 763, 0, 27);
|
||||
player.getPacketDispatch().sendRunScript(1451, "");
|
||||
open = true;
|
||||
this.player.getBank().setTabConfigurations(player);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public class StakeContainer extends Container {
|
|||
public void open() {
|
||||
player.getInterfaceManager().openSingleTab(OVERLAY);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIssssssss", INVY_PARAMS);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 0, 336, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 0, 336, 0, 27);
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, 2, 93, player.getInventory(), false));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ public final class ChestViewer {
|
|||
*/
|
||||
public void update(int type, ContainerEvent event) {
|
||||
if (event == null) {
|
||||
player.getPacketDispatch().sendAccessMask(1278, 27, 647, 0, 10);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 28, 647, 0, 10);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 29, 647, 0, 10);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 27, 647, 0, 10);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 28, 647, 0, 10);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 29, 647, 0, 10);
|
||||
switch (type) {
|
||||
case 0:
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", BEING_DROPPED);
|
||||
|
|
@ -310,14 +310,14 @@ public final class ChestViewer {
|
|||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INV_OPTIONS);
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 90, c.toArray(), 10, false));
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 94, player.getInventory(), false));
|
||||
player.getPacketDispatch().sendAccessMask(1278, 0, 648, 0, 28);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 0, 648, 0, 28);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Container c) {
|
||||
if (c instanceof DepositContainer) {
|
||||
player.getPacketDispatch().sendAccessMask(1278, 0, 648, 0, 28);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 0, 648, 0, 28);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", ACCEPT);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INV_OPTIONS);
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 94, player.getInventory(), false));
|
||||
|
|
|
|||
|
|
@ -119,9 +119,6 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin {
|
|||
}
|
||||
};
|
||||
} else {
|
||||
if (practice) {
|
||||
player.setAttribute("fc_wave", 62);
|
||||
}
|
||||
if (player.getAttribute("fc_wave", 0) == 62) {
|
||||
player.getDialogueInterpreter().sendDialogues(2617, null, "Look out, here comes TzTok-Jad!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
/*
|
||||
package core.game.content.dialogue;
|
||||
|
||||
import core.game.content.global.shop.Shop;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.plugin.Initializable;
|
||||
|
||||
*/
|
||||
/**
|
||||
* Package -> core.game.content.dialogue
|
||||
* Created on -> 9/10/2016 @10:33 PM for 530
|
||||
*
|
||||
* @author Ethan Kyle Millard
|
||||
*/
|
||||
*//*
|
||||
|
||||
@Initializable
|
||||
public class MeleeShopDialoguePlugin extends DialoguePlugin {
|
||||
|
||||
|
|
@ -18,9 +20,11 @@ public class MeleeShopDialoguePlugin extends DialoguePlugin {
|
|||
|
||||
|
||||
public MeleeShopDialoguePlugin() {
|
||||
/**
|
||||
*/
|
||||
/**
|
||||
* Empty
|
||||
*/
|
||||
*//*
|
||||
|
||||
}
|
||||
|
||||
public MeleeShopDialoguePlugin(Player player) {
|
||||
|
|
@ -61,3 +65,4 @@ public class MeleeShopDialoguePlugin extends DialoguePlugin {
|
|||
return new int[] { 5503 };
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import core.game.node.entity.npc.NPC;
|
|||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.plugin.Initializable;
|
||||
import rs09.game.content.global.shops.Shops;
|
||||
import rs09.game.system.config.ShopParser;
|
||||
|
||||
/**
|
||||
|
|
@ -79,7 +80,6 @@ public class SaniDialogue extends DialoguePlugin {
|
|||
break;
|
||||
case 2:
|
||||
end();
|
||||
ShopParser.Companion.getSHOPS().get(4905).open(player);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -136,7 +136,7 @@ public class SaniDialogue extends DialoguePlugin {
|
|||
* @param uid The uid.
|
||||
*/
|
||||
private void openWeaponShop(Player player, int uid) {
|
||||
ShopParser.Companion.openUid(player, uid);
|
||||
Shops.openId(player, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import core.game.node.entity.player.Player
|
|||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.global.shops.Shop
|
||||
import rs09.game.content.global.shops.ShopItem
|
||||
import rs09.game.world.GameWorld
|
||||
import java.lang.Integer.min
|
||||
import kotlin.collections.HashMap
|
||||
|
|
@ -42,7 +44,7 @@ class CulinomancerShop : LoginListener {
|
|||
//Open methods for the shops - should check player's QP and whether they already have a container generated
|
||||
@JvmStatic
|
||||
fun openShop(player: Player, food: Boolean) {
|
||||
getShop(player, food).open(player)
|
||||
getShop(player, food).openFor(player)
|
||||
}
|
||||
|
||||
//Retrieve a player's shop - should generate the shop if it does not exist.
|
||||
|
|
@ -67,24 +69,24 @@ class CulinomancerShop : LoginListener {
|
|||
}
|
||||
|
||||
//Generate default food stock based on an amount of total QP.
|
||||
private fun generateFoodStock(points: Int): Array<Item> {
|
||||
val stock = Array(foodStock.size) { Item(0, 0) }
|
||||
private fun generateFoodStock(points: Int): Array<ShopItem> {
|
||||
val stock = Array(foodStock.size) { ShopItem(0, 0) }
|
||||
val maxQty = when (val qpTier = (points / 18) - 1) {
|
||||
0, 1, 2, 3, 4 -> 1 + qpTier
|
||||
else -> qpTier + (qpTier + (qpTier - 5)) //5 = 10, 6 = 13, 7 = 15, etc
|
||||
}
|
||||
for ((index, item) in foodStock.withIndex()) {
|
||||
stock[index].id = item.id
|
||||
stock[index].itemId = item.id
|
||||
stock[index].amount = if (item.id == Items.PIZZA_BASE_2283) 1 else maxQty
|
||||
}
|
||||
return stock
|
||||
}
|
||||
|
||||
//Generate default gear stock based on an amount of total QP.
|
||||
private fun generateGearStock(points: Int): Array<Item> {
|
||||
val stock = Array(gearStock.size) { Item(0, 0) }
|
||||
private fun generateGearStock(points: Int): Array<ShopItem> {
|
||||
val stock = Array(gearStock.size) { ShopItem(0, 0) }
|
||||
val qpTier = (points / 18)
|
||||
for ((index, item) in stock.withIndex()) item.id = gearStock[index]
|
||||
for ((index, item) in stock.withIndex()) item.itemId = gearStock[index]
|
||||
|
||||
for (i in 0 until min(qpTier, 10)) {
|
||||
stock[i].amount = 30
|
||||
|
|
@ -140,4 +142,4 @@ class CulinomancerShop : LoginListener {
|
|||
Item(Items.BUCKET_1925, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,865 +0,0 @@
|
|||
package core.game.content.global.shop
|
||||
|
||||
import api.amountInInventory
|
||||
import rs09.game.world.GameWorld.ticks
|
||||
import rs09.game.system.SystemLogger.logInfo
|
||||
import core.game.node.entity.player.link.diary.DiaryType
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.game.container.Container
|
||||
import core.game.container.ContainerType
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Items.BLACK_CHAINBODY_1107
|
||||
import org.rs09.consts.Items.BOWL_1923
|
||||
import org.rs09.consts.Items.BUCKET_1925
|
||||
import org.rs09.consts.Items.CAKE_TIN_1887
|
||||
import org.rs09.consts.Items.CANDLE_36
|
||||
import org.rs09.consts.Items.CHAOS_RUNE_562
|
||||
import org.rs09.consts.Items.CHISEL_1755
|
||||
import org.rs09.consts.Items.DEATH_RUNE_560
|
||||
import org.rs09.consts.Items.EMPTY_POT_1931
|
||||
import org.rs09.consts.Items.HAMMER_2347
|
||||
import org.rs09.consts.Items.JUG_1935
|
||||
import org.rs09.consts.Items.NEWCOMER_MAP_550
|
||||
import org.rs09.consts.Items.SECURITY_BOOK_9003
|
||||
import org.rs09.consts.Items.SHEARS_1735
|
||||
import org.rs09.consts.Items.TINDERBOX_590
|
||||
import org.rs09.consts.Items.TOKKUL_6529
|
||||
import rs09.game.system.config.ItemConfigParser
|
||||
import java.lang.IndexOutOfBoundsException
|
||||
import java.util.Arrays
|
||||
import java.util.ArrayList
|
||||
|
||||
/**
|
||||
* A class representing a shop.
|
||||
*
|
||||
* @author 'Vexia
|
||||
* @author Jamix77
|
||||
*/
|
||||
open class Shop @JvmOverloads constructor(
|
||||
/**
|
||||
* Represents the title of the shop.
|
||||
*/
|
||||
val title: String,
|
||||
/**
|
||||
* Represents the items in the store.
|
||||
*/
|
||||
var items: Array<Item>,
|
||||
/**
|
||||
* Represents if it's a general store.
|
||||
*/
|
||||
val isGeneral: Boolean,
|
||||
/**
|
||||
* Represents the currency the shop allows.
|
||||
*/
|
||||
val currency: Int = COINS,
|
||||
/**
|
||||
* If the shop buys for high alch.
|
||||
*/
|
||||
val isHighAlch: Boolean = false
|
||||
) {
|
||||
/**
|
||||
* Gets the containers.
|
||||
*
|
||||
* @return The containers.
|
||||
*/
|
||||
/**
|
||||
* Represents the shop containers.
|
||||
*/
|
||||
val containers = arrayOf(Container(40, ContainerType.SHOP), Container(40, ContainerType.SHOP))
|
||||
/**
|
||||
* Gets the viewers.
|
||||
*
|
||||
* @return The viewers.
|
||||
*/
|
||||
/**
|
||||
* Represents the list of shop viewers.
|
||||
*/
|
||||
val viewers: List<ShopViewer> = ArrayList(20)
|
||||
/**
|
||||
* Gets the title.
|
||||
*
|
||||
* @return The title.
|
||||
*/
|
||||
/**
|
||||
* Gets the items.
|
||||
*
|
||||
* @return The items.
|
||||
*/
|
||||
/**
|
||||
* Gets the general.
|
||||
*
|
||||
* @return The general.
|
||||
*/
|
||||
/**
|
||||
* Gets the currency.
|
||||
*
|
||||
* @return The currency.
|
||||
*/
|
||||
/**
|
||||
* Gets the bhighAlch.
|
||||
*
|
||||
* @return the highAlch
|
||||
*/
|
||||
/**
|
||||
* Gets the SellAllFor value.
|
||||
*
|
||||
* @return the sellAllFor
|
||||
*/
|
||||
/**
|
||||
* Sell price for all shop items, if needed.
|
||||
*/
|
||||
var sellAllFor = 0
|
||||
private set
|
||||
/**
|
||||
* Gets the lastRestock.
|
||||
*
|
||||
* @return the lastRestock
|
||||
*/
|
||||
/**
|
||||
* Sets the balastRestock.
|
||||
*
|
||||
* @param lastRestock the lastRestock to set.
|
||||
*/
|
||||
/**
|
||||
* The last restock.
|
||||
*/
|
||||
var lastRestock = 0
|
||||
/**
|
||||
* Check if shop should restock.
|
||||
*
|
||||
* @return the restock
|
||||
*/
|
||||
/**
|
||||
* Sets the restock.
|
||||
*
|
||||
* @param reStock
|
||||
*/
|
||||
/**
|
||||
* If the shop should restock.
|
||||
*/
|
||||
var isRestock = false
|
||||
/**
|
||||
* Gets the pointShop.
|
||||
*
|
||||
* @return the pointShop
|
||||
*/
|
||||
/**
|
||||
* Sets the pointShop.
|
||||
*
|
||||
* @param pointShop the pointShop to set.
|
||||
*/
|
||||
/**
|
||||
* If it's a point shop.
|
||||
*/
|
||||
var isPointShop = false
|
||||
/**
|
||||
* Gets the npcs.
|
||||
*
|
||||
* @return the npcs
|
||||
*/
|
||||
/**
|
||||
* Sets the banpcs.
|
||||
*
|
||||
* @param npcs the npcs to set.
|
||||
*/
|
||||
/**
|
||||
* The npcs of the shop.
|
||||
*/
|
||||
var npcs: IntArray = intArrayOf()
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param items the items.
|
||||
* @param general the general.
|
||||
* @param currency the currency.
|
||||
* @param highAlch if high alch.
|
||||
* @param restock if restock.
|
||||
*/
|
||||
constructor(
|
||||
title: String,
|
||||
items: Array<Item>,
|
||||
general: Boolean,
|
||||
currency: Int,
|
||||
highAlch: Boolean,
|
||||
restock: Boolean,
|
||||
sellAllFor: Int
|
||||
) : this(title, items, general, currency, highAlch) {
|
||||
this.sellAllFor = sellAllFor
|
||||
isRestock = restock
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`
|
||||
*
|
||||
* @param title the shop title
|
||||
* @param items items the shop can handle
|
||||
* @param general is this a general store
|
||||
* @param currency what currency is used
|
||||
*/
|
||||
constructor(title: String, items: Array<Item>, npcs: IntArray, general: Boolean, currency: Int) : this(
|
||||
title,
|
||||
items,
|
||||
general,
|
||||
currency,
|
||||
false
|
||||
) {
|
||||
this.npcs = npcs
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param general the general.
|
||||
*/
|
||||
constructor(title: String, general: Boolean) : this(title, GENERAL_STORE_ITEMS, general) {}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param general the general.
|
||||
* @param highAlch if highAlch.
|
||||
*/
|
||||
constructor(title: String, general: Boolean, highAlch: Boolean) : this(
|
||||
title,
|
||||
GENERAL_STORE_ITEMS,
|
||||
general,
|
||||
COINS,
|
||||
highAlch
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param general the general.
|
||||
* @param highAlch if highAlch.
|
||||
*/
|
||||
constructor(title: String, general: Boolean, currency: Int, highAlch: Boolean) : this(
|
||||
title,
|
||||
GENERAL_STORE_ITEMS,
|
||||
general,
|
||||
currency,
|
||||
highAlch
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param items the items.
|
||||
* @param npcs the npcs.
|
||||
* @param general the general.
|
||||
*/
|
||||
constructor(title: String, items: Array<Item>, npcs: IntArray, general: Boolean) : this(
|
||||
title,
|
||||
items,
|
||||
general,
|
||||
COINS,
|
||||
false
|
||||
) {
|
||||
this.npcs = npcs
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param npcs the npcs.
|
||||
* @param general the general.
|
||||
*/
|
||||
constructor(title: String, npcs: IntArray, general: Boolean) : this(title, GENERAL_STORE_ITEMS, npcs, general) {
|
||||
this.npcs = npcs
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param npcs the npcs.
|
||||
* @param general the general.
|
||||
* @param highAlch if highAlch.
|
||||
*/
|
||||
constructor(title: String, npcs: IntArray, general: Boolean, highAlch: Boolean) : this(
|
||||
title,
|
||||
GENERAL_STORE_ITEMS,
|
||||
general,
|
||||
995,
|
||||
highAlch
|
||||
) {
|
||||
this.npcs = npcs
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to open the shop.
|
||||
*
|
||||
* @param player the shop.
|
||||
*/
|
||||
fun open(player: Player) {
|
||||
ShopViewer.extend(player, this).open()
|
||||
|
||||
// Browse the Lumbridge General Store
|
||||
if (title.equals("Lumbridge General Store", ignoreCase = true)) {
|
||||
player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 18)
|
||||
}
|
||||
|
||||
// Browse through Oziach's Armour Shop
|
||||
if (title.equals("Oziach's Armour", ignoreCase = true)) {
|
||||
player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 20)
|
||||
}
|
||||
}
|
||||
|
||||
fun give(player: Player, slot: Int, amount: Int, tabIndex: Int) {
|
||||
val container = getContainer(tabIndex)
|
||||
val item = container[slot] ?: return
|
||||
val add = Item(item.id, amount)
|
||||
if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) {
|
||||
player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot get anything.")
|
||||
return
|
||||
}
|
||||
add.amount = getAmount(player, add)
|
||||
if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) {
|
||||
player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot get anything.")
|
||||
return
|
||||
}
|
||||
player.inventory.add(add)
|
||||
update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to buy an item from the shop.
|
||||
*
|
||||
* @param slot the slot.
|
||||
* @param amount the amount.
|
||||
*/
|
||||
fun buy(player: Player, slot: Int, amount: Int, tabIndex: Int) {
|
||||
var amount = amount
|
||||
if (tabIndex == 1 && player.ironmanManager.checkRestriction()) {
|
||||
return
|
||||
}
|
||||
val container = getContainer(tabIndex)
|
||||
val item = container[slot] ?: return
|
||||
if (item.amount == 0) {
|
||||
player.packetDispatch.sendMessage("There is no stock of that item at the moment.")
|
||||
return
|
||||
}
|
||||
if (amount > item.amount && item.amount != -1) {
|
||||
amount = item.amount
|
||||
}
|
||||
val add = Item(item.id, amount)
|
||||
if (player.inventory.getMaximumAdd(add) < amount) {
|
||||
add.amount = player.inventory.getMaximumAdd(add)
|
||||
}
|
||||
if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) {
|
||||
player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot buy anything.")
|
||||
return
|
||||
}
|
||||
add.amount = getAmount(player, add)
|
||||
if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) {
|
||||
player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot buy anything.")
|
||||
return
|
||||
}
|
||||
val price = add.amount * getBuyPrice(item, player)
|
||||
val currency = Item(currency, price)
|
||||
if (!canBuy(player, item, price, currency)) {
|
||||
return
|
||||
}
|
||||
if (handleBuy(player, currency)) {
|
||||
if (isPointShop) {
|
||||
decrementPoints(player, price)
|
||||
}
|
||||
if (tabIndex == 0) {
|
||||
if (container.getAmount(item) != -1) container.replace(
|
||||
Item(
|
||||
item.id,
|
||||
container.getAmount(item) - add.amount
|
||||
), slot, true
|
||||
)
|
||||
} else {
|
||||
container.remove(add)
|
||||
container.shift()
|
||||
}
|
||||
|
||||
// Achievement Diary Handlers
|
||||
if (add.id == BLACK_CHAINBODY_1107 && title.equals("Wayne's Chains", ignoreCase = true) && !player.getAttribute("diary:falador:black-chain-bought", false)) {
|
||||
player.setAttribute("/save:diary:falador:black-chain-bought", true)
|
||||
}
|
||||
if (add.id == 12622 && title.equals("Sarah's Farming Shop", ignoreCase = true)) {
|
||||
player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 0, 0)
|
||||
}
|
||||
if (add.id == CANDLE_36 && title.equals("Candle Shop", ignoreCase = true)) {
|
||||
player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 9)
|
||||
}
|
||||
if (title.equals("Ranging Guild Ticket Exchange", ignoreCase = true)) {
|
||||
player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 8)
|
||||
}
|
||||
player.inventory.add(add)
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to sell an item to the shop.
|
||||
*
|
||||
* @param slot the slot.
|
||||
* @param amount the amount.
|
||||
*/
|
||||
fun sell(player: Player, slot: Int, amount: Int, tabIndex: Int) {
|
||||
var amount = amount
|
||||
var tabIndex = tabIndex
|
||||
val item = player.inventory[slot] ?: return
|
||||
val def = ItemDefinition.forId(item.id)
|
||||
if (!canSell(player, item, def)) {
|
||||
return
|
||||
}
|
||||
val container = getContainer(item)
|
||||
if (amount > player.inventory.getAmount(item)) {
|
||||
amount = player.inventory.getAmount(item)
|
||||
}
|
||||
var add = Item(item.id, amount)
|
||||
if (add.amount > container.getMaximumAdd(add)) {
|
||||
add.amount = container.getMaximumAdd(add)
|
||||
}
|
||||
player.debug("" + add.amount)
|
||||
if (!container.hasSpaceFor(add) || add.amount < 1) {
|
||||
player.packetDispatch.sendMessage("The shop has ran out of space.")
|
||||
return
|
||||
}
|
||||
val currency = Item(currency, getSellingValue(add, player))
|
||||
if (item.definition.isStackable) {
|
||||
if (!player.inventory.hasSpaceFor(currency)) {
|
||||
player.packetDispatch.sendMessage("You don't have enough space for that many " + currency.name.toLowerCase() + ".")
|
||||
return
|
||||
}
|
||||
}
|
||||
player.debug("Selling item")
|
||||
if (player.inventory.remove(add, slot, true)) {
|
||||
if (currency.amount > player.inventory.getMaximumAdd(currency)) {
|
||||
currency.amount = player.inventory.getMaximumAdd(currency)
|
||||
}
|
||||
if (!add.definition.isUnnoted) {
|
||||
add = Item(add.noteChange, add.amount)
|
||||
}
|
||||
if (container.getAmount(add.id) == -1 || container.add(add)) {
|
||||
if (currency.amount > 0) {
|
||||
player.debug("Adding coins to inventory")
|
||||
player.inventory.add(currency)
|
||||
}
|
||||
val viewer = player.getExtension<ShopViewer>(ShopViewer::class.java)
|
||||
tabIndex = if (container === containers[0]) 0 else 1
|
||||
sendStock(player, tabIndex)
|
||||
if (viewer != null) {
|
||||
viewer.tabIndex = tabIndex
|
||||
}
|
||||
update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Values an item.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param viewer the viewer.
|
||||
* @param item the item.
|
||||
* @param sell the sell.
|
||||
*/
|
||||
fun value(player: Player, viewer: ShopViewer, item: Item, sell: Boolean) {
|
||||
if (sell) {
|
||||
if (isPointShop || item.id == viewer.shop.currency || !item.definition.isTradeable || !viewer.shop.itemAllowed(
|
||||
item.id
|
||||
)
|
||||
) {
|
||||
player.packetDispatch.sendMessage("You can't sell this item.")
|
||||
return
|
||||
}
|
||||
val value = viewer.shop.getSellingValue(Item(item.id, 1), player)
|
||||
var currency =
|
||||
if (isPointShop) pointsName else ItemDefinition.forId(viewer.shop.currency).name.toLowerCase()
|
||||
if (value == 1 && currency[currency.length - 1] == 's') {
|
||||
currency = currency.substring(0, currency.length - 1)
|
||||
}
|
||||
player.packetDispatch.sendMessage(item.name + ": shop will buy for " + value + " " + currency + ".")
|
||||
} else {
|
||||
val value = viewer.shop.getBuyPrice(item, player)
|
||||
var name =
|
||||
if (isPointShop) pointsName + "s" else ItemDefinition.forId(viewer.shop.currency).name.toLowerCase()
|
||||
if (value == 1 && name[name.length - 1] == 's') {
|
||||
name = name.substring(0, name.length - 1)
|
||||
}
|
||||
player.packetDispatch.sendMessage("" + item.name + ": currently costs " + value + " " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to send a stock
|
||||
*
|
||||
* @param player
|
||||
* @param tabIndex
|
||||
*/
|
||||
fun sendStock(player: Player, tabIndex: Int) {
|
||||
val main = tabIndex == 0
|
||||
player.packetDispatch.sendInterfaceConfig(620, 23, !main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 24, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 29, !main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 25, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 27, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 26, false)
|
||||
player.packetDispatch.sendAccessMask(1278, if (main) 23 else 24, 620, 0, 40)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to update the viewers.
|
||||
*/
|
||||
fun update() {
|
||||
for (viewer in viewers) {
|
||||
viewer.update()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to restock the shop.
|
||||
*/
|
||||
fun restock() {
|
||||
for (container in containers) {
|
||||
for (i in container.toArray().indices) {
|
||||
val main = container === containers[0]
|
||||
val item = container.toArray()[i] ?: continue
|
||||
var reduce = !main
|
||||
if (main) {
|
||||
if (item.amount < items[i].amount) {
|
||||
item.amount = item.amount + 1
|
||||
}
|
||||
reduce = item.amount > items[i].amount
|
||||
}
|
||||
if (reduce) {
|
||||
val amount = item.amount - 1
|
||||
if (amount < 1 && !main) {
|
||||
container.remove(item)
|
||||
} else {
|
||||
item.amount = amount
|
||||
}
|
||||
if (!main) {
|
||||
container.shift()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player can sell an item to the shop.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param item the item.
|
||||
* @param def the def.
|
||||
* @return `True` if so.
|
||||
*/
|
||||
open fun canSell(player: Player, item: Item, def: ItemDefinition): Boolean {
|
||||
if (isPointShop || item.definition.hasDestroyAction() || !def.isTradeable || !itemAllowed(item.id)) {
|
||||
player.packetDispatch.sendMessage("You can't sell this item to this shop.")
|
||||
return false
|
||||
}
|
||||
if (item.id == currency) {
|
||||
player.packetDispatch.sendMessage("You can't sell " + item.name.toLowerCase() + " to a shop.")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount to buy/sell.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param add the added item.
|
||||
* @return the amount.
|
||||
*/
|
||||
fun getAmount(player: Player?, add: Item): Int {
|
||||
return add.amount
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player can buy the item.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param currency the currency.
|
||||
* @return `True` if so.
|
||||
*/
|
||||
fun handleBuy(player: Player, currency: Item?): Boolean {
|
||||
return isPointShop || player.inventory.remove(currency)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player can buy from the shop.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param item the item.
|
||||
* @param price the price.
|
||||
* @param currency the currency.
|
||||
* @return `True` if they can buy.
|
||||
*/
|
||||
fun canBuy(player: Player, item: Item, price: Int, currency: Item): Boolean {
|
||||
if (!isPointShop && !player.inventory.containsItem(currency)) {
|
||||
player.packetDispatch.sendMessage(
|
||||
"You don't have enough " + ItemDefinition.forId(currency.id).name.toLowerCase() + "."
|
||||
)
|
||||
return false
|
||||
}
|
||||
if (isPointShop && getPoints(player) < price) {
|
||||
player.sendMessage("You don't have enough " + pointsName + "s.")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the points.
|
||||
*
|
||||
* @param player the player.
|
||||
* @return the points.
|
||||
*/
|
||||
fun getPoints(player: Player?): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the points.
|
||||
*
|
||||
* @param player the player.
|
||||
* @param decrement the decrementation.
|
||||
*/
|
||||
fun decrementPoints(player: Player?, decrement: Int) {}
|
||||
|
||||
/**
|
||||
* Gets the points name.
|
||||
*
|
||||
* @return the name.
|
||||
*/
|
||||
val pointsName: String
|
||||
get() = ""
|
||||
|
||||
/**
|
||||
* Gets the value gained for selling this item to a certain shop.
|
||||
*
|
||||
* @param item The item to sell.
|
||||
* @param player the player.
|
||||
* @return The value.
|
||||
*/
|
||||
fun getSellingValue(item: Item, player: Player): Int {
|
||||
var item = item
|
||||
if (!item.definition.isUnnoted) {
|
||||
player.setAttribute("shop:originalId", item.id)
|
||||
item = Item(item.noteChange, item.amount)
|
||||
}
|
||||
var amount = getContainer(1).getAmount(item)
|
||||
if (amount < 1) {
|
||||
amount = getContainer(0).getAmount(item)
|
||||
}
|
||||
return getSellingValue(player, amount, item)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selling value formula based.
|
||||
*
|
||||
* @param amount the amount.
|
||||
* @param item the item.
|
||||
* @return the selling value.
|
||||
*/
|
||||
private fun getSellingValue(player: Player, amount: Int, item: Item): Int {
|
||||
val id = player.getAttribute("shop:originalId", item.id)
|
||||
if (item.amount > amountInInventory(player, id)) {
|
||||
item.amount = amountInInventory(player, id)
|
||||
player.removeAttribute("shop:originalId")
|
||||
}
|
||||
val diff = if (item.definition.isStackable) 0.005 else 0.05
|
||||
var maxMod = 1.0 - amount * diff
|
||||
if (maxMod < 0.25) {
|
||||
maxMod = 0.25
|
||||
}
|
||||
var minMod = maxMod - (item.amount - 1) * diff
|
||||
if (minMod < 0.25) {
|
||||
minMod = 0.25
|
||||
}
|
||||
val mod = (maxMod + minMod) / 2
|
||||
logInfo("" + item.definition.getAlchemyValue(isHighAlch) + " " + mod + " " + item.amount)
|
||||
val baseValue = item.definition.getAlchemyValue(isHighAlch)
|
||||
var value = (baseValue * mod * item.amount).toInt()
|
||||
if (currency == TOKKUL_6529 && item.id == CHAOS_RUNE_562) value = 13 * item.amount
|
||||
if (currency == TOKKUL_6529 && item.id == DEATH_RUNE_560) value = 27 * item.amount
|
||||
if (item.id == 12183) {
|
||||
value = 25 * item.amount
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the buying price.
|
||||
*
|
||||
* @param item the item.
|
||||
* @return the price.
|
||||
*/
|
||||
open fun getBuyPrice(item: Item, player: Player): Int {
|
||||
var item = item
|
||||
item = Item(item.id, 1)
|
||||
var price = item.definition.value
|
||||
val sellVal = getSellingValue(item, player)
|
||||
if (price < sellVal) {
|
||||
price = getSellingValue(player, 0, item) + sellVal - (sellVal - item.definition.maxValue)
|
||||
}
|
||||
if (price < 0) {
|
||||
price = 1
|
||||
}
|
||||
if (currency == TOKKUL) {
|
||||
val tokkul = item.definition.getConfiguration("tokkul_price", -1)
|
||||
if (tokkul > 0) {
|
||||
price = tokkul
|
||||
}
|
||||
if (player.achievementDiaryManager.karamjaGlove != -1) {
|
||||
price = kotlin.math.floor(price * 0.87).toInt()
|
||||
}
|
||||
}
|
||||
if (currency == ARCHERY_TICKET) {
|
||||
val tickets = item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, -1)
|
||||
if (tickets > 0) {
|
||||
price = tickets
|
||||
}
|
||||
}
|
||||
return if (sellAllFor > 0) sellAllFor else price
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is allowed to be sold to the shop.
|
||||
*
|
||||
* @param itemId the item id.
|
||||
* @return `True` if so.
|
||||
*/
|
||||
fun itemAllowed(itemId: Int): Boolean {
|
||||
if (isGeneral) {
|
||||
return true
|
||||
}
|
||||
var noteId = ItemDefinition.forId(itemId).noteId
|
||||
if (!ItemDefinition.forId(itemId).isUnnoted) {
|
||||
noteId = ItemDefinition.forId(noteId).noteId
|
||||
}
|
||||
for (id in items) {
|
||||
if (itemId == id.id || noteId > -1 && noteId == ItemDefinition.forId(id.id).noteId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the container the item should go to.
|
||||
*
|
||||
* @param item the item.
|
||||
* @return the container.
|
||||
*/
|
||||
fun getContainer(item: Item): Container {
|
||||
val itemId = item.id
|
||||
var noteId = ItemDefinition.forId(itemId).noteId
|
||||
if (!ItemDefinition.forId(itemId).isUnnoted) {
|
||||
noteId = ItemDefinition.forId(noteId).noteId
|
||||
}
|
||||
for (i in items) {
|
||||
if (i.id == item.id || noteId > -1 && noteId == ItemDefinition.forId(i.id).noteId) {
|
||||
return getContainer(0)
|
||||
}
|
||||
}
|
||||
return getContainer(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this shop.
|
||||
*
|
||||
* @return the shop.
|
||||
*/
|
||||
fun copy(): Shop {
|
||||
return Shop(title, items, isGeneral, currency, isHighAlch)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the container on the slot.
|
||||
*
|
||||
* @param tabIndex the tab index.
|
||||
* @return the container.
|
||||
*/
|
||||
fun getContainer(tabIndex: Int): Container {
|
||||
if (tabIndex > containers.size) {
|
||||
throw IndexOutOfBoundsException("Error! Shop tab index out of bounds.")
|
||||
}
|
||||
return containers[tabIndex]
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Shop [containers=" + Arrays.toString(containers) + ", viewers=" + viewers + ", title=" + title + ", items=" + Arrays.toString(
|
||||
items
|
||||
) + ", general=" + isGeneral + ", currency=" + currency + ", highAlch=" + isHighAlch + "]"
|
||||
}
|
||||
|
||||
fun setItems(items: ArrayList<Item>) {
|
||||
this.items = items.toTypedArray()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Represents the general store items.
|
||||
*/
|
||||
val GENERAL_STORE_ITEMS = arrayOf(
|
||||
Item(EMPTY_POT_1931, 5),
|
||||
Item(JUG_1935, 5),
|
||||
Item(SHEARS_1735, 2),
|
||||
Item(BUCKET_1925, 3),
|
||||
Item(BOWL_1923, 2),
|
||||
Item(CAKE_TIN_1887, 2),
|
||||
Item(TINDERBOX_590, 2),
|
||||
Item(CHISEL_1755, 2),
|
||||
Item(HAMMER_2347, 5),
|
||||
Item(NEWCOMER_MAP_550, 5),
|
||||
Item(SECURITY_BOOK_9003, 5)
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the coins item.
|
||||
*/
|
||||
private const val COINS = 995
|
||||
|
||||
/**
|
||||
* Represents the tokkul item id.
|
||||
*/
|
||||
private const val TOKKUL = 6529
|
||||
|
||||
/**
|
||||
* Represents the archery ticket item id
|
||||
*/
|
||||
private const val ARCHERY_TICKET = 1464
|
||||
}
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param items the items.
|
||||
* @param isGeneral the general.
|
||||
* @param currency the currency.
|
||||
* @param isHighAlch if high alch.
|
||||
*/
|
||||
/**
|
||||
* Constructs a new `Shop` `Object`.
|
||||
*
|
||||
* @param title the title.
|
||||
* @param items the items.
|
||||
* @param general the general.
|
||||
*/
|
||||
init {
|
||||
this.getContainer(0).add(*items)
|
||||
isRestock = true
|
||||
lastRestock = ticks + 100
|
||||
}
|
||||
}
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
package core.game.content.global.shop;
|
||||
|
||||
import core.game.component.CloseEvent;
|
||||
import core.game.component.Component;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.net.packet.PacketRepository;
|
||||
import core.net.packet.context.ContainerContext;
|
||||
import core.net.packet.out.ContainerPacket;
|
||||
|
||||
/**
|
||||
* Represents the viewing of a shop.
|
||||
* @author 'Vexia
|
||||
*/
|
||||
public final class ShopViewer {
|
||||
|
||||
/**
|
||||
* Represents the main component.
|
||||
*/
|
||||
private static final Component COMPONENT = new Component(620).setCloseEvent(new ShopCloseEvent());
|
||||
|
||||
/**
|
||||
* Represents the single tab component.
|
||||
*/
|
||||
private static final Component SINGLE_TAB = new Component(621);
|
||||
|
||||
/**
|
||||
* Represents the player.
|
||||
*/
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* Represents the shop being viewed.
|
||||
*/
|
||||
private Shop shop;
|
||||
|
||||
/**
|
||||
* Represents the tab index.
|
||||
*/
|
||||
private int tabIndex;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ShopViewer} {@code Object}.
|
||||
* @param player the player.
|
||||
* @param shop the shop.
|
||||
*/
|
||||
public ShopViewer(Player player, Shop shop) {
|
||||
this.player = player;
|
||||
this.shop = shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends a shop a shop viewer to the player instance.
|
||||
* @param player the player.
|
||||
* @param shop the shop.
|
||||
* @return the viewer.
|
||||
*/
|
||||
public static ShopViewer extend(final Player player, Shop shop) {
|
||||
ShopViewer viewer = player.getExtension(ShopViewer.class);
|
||||
if (viewer != null) {
|
||||
return viewer;
|
||||
}
|
||||
player.addExtension(ShopViewer.class, (viewer = new ShopViewer(player, shop)));
|
||||
return viewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to open the shop visual.
|
||||
*/
|
||||
public void open() {
|
||||
update();
|
||||
shop.getViewers().add(this);
|
||||
player.getInterfaceManager().open(COMPONENT);
|
||||
player.getInterfaceManager().openSingleTab(SINGLE_TAB);
|
||||
shop.sendStock(player, tabIndex);
|
||||
player.getPacketDispatch().sendRunScript(25, "vg", 868, 92);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 0, 621, 0, 28);
|
||||
player.getPacketDispatch().sendString(shop.getTitle(), 620, 22);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 92, 620 << 16 | 23);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 31, (620 << 16) | 24);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Sell X", "Sell 10", "Sell 5", "Sell 1", "Value", -1, 0, 7, 4, 93, 621 << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to update the contents on the component.
|
||||
*/
|
||||
public void update() {
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 93, player.getInventory().toArray(), 28, false));
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 92, shop.getContainer(0).toArray(), shop.getContainer(0).itemCount(), false));
|
||||
PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, 64271, 31, shop.getContainer(1).toArray(), shop.getContainer(1).itemCount(), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to show a stock.
|
||||
*/
|
||||
public void showStock(int tabIndex) {
|
||||
this.tabIndex = tabIndex;
|
||||
shop.sendStock(player, tabIndex);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tabIndex.
|
||||
* @param tabIndex The tabIndex to set.
|
||||
*/
|
||||
public void setTabIndex(int tabIndex) {
|
||||
this.tabIndex = tabIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player.
|
||||
* @return The player.
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shop.
|
||||
* @return The shop.
|
||||
*/
|
||||
public Shop getShop() {
|
||||
return shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tabIndex.
|
||||
* @return The tabIndex.
|
||||
*/
|
||||
public int getTabIndex() {
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the close event when a shop is closed.
|
||||
* @author 'Vexia
|
||||
*/
|
||||
public static final class ShopCloseEvent implements CloseEvent {
|
||||
|
||||
@Override
|
||||
public boolean close(Player player, Component c) {
|
||||
final ShopViewer viewer = player.getExtension(ShopViewer.class);
|
||||
if (viewer == null) {
|
||||
return true;
|
||||
}
|
||||
player.removeExtension(ShopViewer.class);
|
||||
viewer.getShop().getViewers().remove(viewer);
|
||||
player.getInterfaceManager().closeSingleTab();
|
||||
player.removeAttribute("spawning_items");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package core.game.content.global.worldevents.shootingstar;
|
||||
|
||||
public class ScoreboardEntry{
|
||||
public String playerName;
|
||||
public int time;
|
||||
public ScoreboardEntry(String player, int time){
|
||||
this.playerName = player;
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package core.game.content.global.worldevents.shootingstar;
|
||||
|
||||
import core.cache.def.impl.SceneryDefinition;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.player.Player;
|
||||
import rs09.game.world.GameWorld;
|
||||
import core.plugin.Plugin;
|
||||
|
||||
public class ScoreboardHandler extends OptionHandler {
|
||||
int index = 0;
|
||||
int ifaceid = ShootingStarScoreboard.iface.getId();
|
||||
@Override
|
||||
public boolean handle(Player player, Node node, String option) {
|
||||
ScoreboardManager.getEntries().forEach(e -> {
|
||||
player.getPacketDispatch().sendString("" + Math.floor(((GameWorld.getTicks() - e.time) / 0.6) / 60) + " minutes ago",ifaceid,index + 6);
|
||||
player.getPacketDispatch().sendString(e.playerName,ifaceid,index + 11);
|
||||
index++;
|
||||
});
|
||||
index = 0;
|
||||
player.getInterfaceManager().open(ShootingStarScoreboard.iface);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
SceneryDefinition.forId(38669).getHandlers().put("option:read",this);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package core.game.content.global.worldevents.shootingstar;
|
||||
|
||||
import core.game.node.entity.player.Player;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ScoreboardManager {
|
||||
public static List<ScoreboardEntry> entries = new ArrayList<>(20);
|
||||
|
||||
public static void submit(Player player){
|
||||
if(entries.size() == 5){
|
||||
entries.remove(0);
|
||||
}
|
||||
entries.add(new ScoreboardEntry(player.getUsername(), GameWorld.getTicks()));
|
||||
}
|
||||
|
||||
public static List<ScoreboardEntry> getEntries(){
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package core.game.content.global.worldevents.shootingstar;
|
||||
|
||||
import core.game.component.Component;
|
||||
import core.game.component.ComponentDefinition;
|
||||
import core.game.component.ComponentPlugin;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.plugin.Plugin;
|
||||
|
||||
public class ShootingStarScoreboard extends ComponentPlugin {
|
||||
public static Component iface = new Component(787);
|
||||
|
||||
@Override
|
||||
public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
ComponentDefinition.put(iface.getId(),this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import core.game.node.entity.player.Player;
|
|||
import core.game.node.entity.player.link.quest.Quest;
|
||||
import core.game.node.item.Item;
|
||||
import core.plugin.Plugin;
|
||||
import rs09.game.content.global.shops.Shops;
|
||||
import rs09.game.system.config.ShopParser;
|
||||
import rs09.plugin.ClassScanner;
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ public final class CandleMakerDialogue extends DialoguePlugin {
|
|||
NPC npc = node.asNpc();
|
||||
Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal");
|
||||
if (quest.getStage(player) > 60) {
|
||||
ShopParser.Companion.openUid(player, 198);
|
||||
Shops.openId(player, 198);
|
||||
} else {
|
||||
npc.openShop(player);
|
||||
}
|
||||
|
|
@ -175,7 +176,7 @@ public final class CandleMakerDialogue extends DialoguePlugin {
|
|||
case 30:
|
||||
end();
|
||||
if (quest.getStage(player) > 60) {
|
||||
ShopParser.Companion.openUid(player, 198);
|
||||
Shops.openId(player, 198);
|
||||
} else {
|
||||
npc.openShop(player);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,7 +289,6 @@ public final class WaterfallPlugin extends OptionHandler {
|
|||
public boolean pulse() {
|
||||
player.getPacketDispatch().sendMessage("You are washed downstream but feel lucky to be alive.");
|
||||
player.teleport(new Location(2527, 3413));
|
||||
player.logoutListeners.remove("waterfall");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ public enum ClueLevel {
|
|||
value += item.getValue();
|
||||
}
|
||||
player.sendMessage("<col=990000>Your clue is worth approximately " + NumberFormat.getInstance().format(value) + " coins!</col>");
|
||||
player.getPacketDispatch().sendAccessMask(1278, 4, 364, 0, 6);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 4, 364, 0, 6);
|
||||
InterfaceContainer.generateItems(player, rewards.toArray(new Item[] {}), new String[] { "" }, 364, 4, 3, 3);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ public final class EquipmentInterface extends ComponentPlugin {
|
|||
boolean skulled = p.getSkullManager().isSkulled();
|
||||
boolean usingProtect = p.getPrayer().get(PrayerType.PROTECT_ITEMS);
|
||||
p.getInterfaceManager().openComponent(102);
|
||||
p.getPacketDispatch().sendAccessMask(211, 0, 2, 6684690, 4);
|
||||
p.getPacketDispatch().sendAccessMask(212, 0, 2, 6684693, 42);
|
||||
p.getPacketDispatch().sendIfaceSettings(211, 0, 2, 6684690, 4);
|
||||
p.getPacketDispatch().sendIfaceSettings(212, 0, 2, 6684693, 42);
|
||||
Container[] itemArray = DeathTask.getContainers(p);
|
||||
Container kept = itemArray[0];
|
||||
int state = 0; // 1=familiar carrying items
|
||||
|
|
@ -166,11 +166,11 @@ public final class EquipmentInterface extends ComponentPlugin {
|
|||
BitregisterAssembler assembly = new BitregisterAssembler(new String[] { "Equip" });
|
||||
assembly.enableExamineOption();
|
||||
assembly.enableSlotSwitch();
|
||||
p.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 0, 670, 0, 27);
|
||||
p.getPacketDispatch().sendIfaceSettings(assembly.calculateRegister(), 0, 670, 0, 27);
|
||||
p.getInventory().getListeners().add(listener);
|
||||
p.getInventory().refresh();
|
||||
ItemDefinition.statsUpdate(p);
|
||||
p.getPacketDispatch().sendAccessMask(1278, 14, 667, 0, 13);
|
||||
p.getPacketDispatch().sendIfaceSettings(1278, 14, 667, 0, 13);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
package core.game.interaction.inter;
|
||||
|
||||
import static api.ContentAPIKt.*;
|
||||
|
|
@ -15,11 +16,13 @@ import core.plugin.Plugin;
|
|||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
*/
|
||||
/**
|
||||
* Represents the plugin used to handle the shopping interface.
|
||||
* @author 'Vexia
|
||||
* @version 1.0
|
||||
*/
|
||||
*//*
|
||||
|
||||
@Initializable
|
||||
public final class ShoppingPlugin extends ComponentPlugin {
|
||||
|
||||
|
|
@ -97,13 +100,15 @@ public final class ShoppingPlugin extends ComponentPlugin {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
/**
|
||||
* Method used to value an item.
|
||||
* @param player the player.
|
||||
* @param viewer the viewer.
|
||||
* @param item the item.
|
||||
* @param sell the sell.
|
||||
*/
|
||||
*//*
|
||||
|
||||
private void value(final Player player, final ShopViewer viewer, final Item item, final boolean sell) {
|
||||
if (item == null) {
|
||||
return;
|
||||
|
|
@ -111,11 +116,13 @@ public final class ShoppingPlugin extends ComponentPlugin {
|
|||
viewer.getShop().value(player, viewer, item, sell);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
/**
|
||||
* Gets the run script for selling an item.
|
||||
* @return the script.
|
||||
* @param slot the slot.
|
||||
*/
|
||||
*//*
|
||||
|
||||
private Function1 getRunScript(final ShopViewer viewer, final int slot, final int componentId) {
|
||||
return (value) -> {
|
||||
switch (componentId){
|
||||
|
|
@ -130,12 +137,15 @@ public final class ShoppingPlugin extends ComponentPlugin {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
/**
|
||||
* Gets the amount by the opcode.
|
||||
* @param opcode the opcode.
|
||||
* @return the amount.
|
||||
*/
|
||||
*//*
|
||||
|
||||
private int getAmount(int opcode) {
|
||||
return opcode == 196 ? 1 : opcode == 124 ? 5 : opcode == 199 ? 10 : -1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import rs09.game.interaction.InteractionListener
|
|||
*
|
||||
* @author Vexia
|
||||
*/
|
||||
class ExplorersRingPlugin : InteractionListener() {
|
||||
class ExplorersRingPlugin : InteractionListener {
|
||||
|
||||
val RINGS = intArrayOf(Items.EXPLORERS_RING_1_13560, Items.EXPLORERS_RING_2_13561, Items.EXPLORERS_RING_3_13562)
|
||||
val CABBAGE_PORT = Location.create(3051, 3291, 0)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import core.game.node.entity.npc.NPC;
|
|||
import core.game.node.entity.player.Player;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
import rs09.game.content.global.shops.Shops;
|
||||
import rs09.game.system.config.ShopParser;
|
||||
|
||||
/**
|
||||
|
|
@ -39,11 +40,11 @@ public final class GabootyTrade extends OptionHandler {
|
|||
return true;
|
||||
|
||||
case "trade-co-op":
|
||||
ShopParser.Companion.openUid(player, 226);
|
||||
Shops.openId(player, 226);
|
||||
return true;
|
||||
|
||||
case "trade-drinks":
|
||||
ShopParser.Companion.openUid(player, 227);
|
||||
Shops.openId(player, 227);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
package core.game.interaction.npc
|
||||
|
||||
import api.*
|
||||
import core.cache.def.impl.NPCDefinition
|
||||
import core.game.component.Component
|
||||
import core.game.content.dialogue.FacialExpression
|
||||
import core.plugin.Initializable
|
||||
import core.game.interaction.OptionHandler
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Plugin
|
||||
import core.game.node.entity.skill.crafting.TanningProduct
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
/**
|
||||
* Represents the plugin used for an npc with the trade option.
|
||||
* @author Ceikry
|
||||
* @version 1.0
|
||||
*/
|
||||
class NPCTradePlugin : InteractionListener() {
|
||||
override fun defineListeners() {
|
||||
on(NPC, "trade", "shop"){player, node ->
|
||||
val npc = node as NPC
|
||||
if (npc.id == 2824) {
|
||||
TanningProduct.open(player, 2824)
|
||||
return@on true
|
||||
}
|
||||
if (npc.id == 7601) {
|
||||
openInterface(player, 732)
|
||||
return@on true
|
||||
}
|
||||
return@on npc.openShop(player)
|
||||
}
|
||||
|
||||
on(NPCs.SIEGFRIED_ERKLE_933, NPC, "trade"){player, node ->
|
||||
val points = getQP(player)
|
||||
if(points < 40){
|
||||
sendNPCDialogue(player, NPCs.SIEGFRIED_ERKLE_933, "I'm sorry, adventurer, but you need 40 quest points to buy from me.")
|
||||
return@on true
|
||||
}
|
||||
node.asNpc().openShop(player)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(NPCs.FUR_TRADER_1316, NPC,"trade") { player, node ->
|
||||
if (!isQuestComplete(player, "Fremennik Trials")) {
|
||||
sendNPCDialogue(player, NPCs.FUR_TRADER_1316, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE }
|
||||
} else {
|
||||
END_DIALOGUE.also { node.asNpc().openShop(player) }
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(NPCs.FISH_MONGER_1315, NPC,"trade") { player, node ->
|
||||
if (!isQuestComplete(player, "Fremennik Trials")) {
|
||||
sendNPCDialogue(player, NPCs.FISH_MONGER_1315, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE }
|
||||
} else {
|
||||
END_DIALOGUE.also { node.asNpc().openShop(player) }
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineDestinationOverrides() {
|
||||
setDest(NPC,"trade","shop"){_,node ->
|
||||
val npc = node as NPC
|
||||
if (npc.getAttribute("facing_booth", false)) {
|
||||
val offsetX = npc.direction.stepX shl 1
|
||||
val offsetY = npc.direction.stepY shl 1
|
||||
return@setDest npc.location.transform(offsetX, offsetY, 0)
|
||||
}
|
||||
return@setDest node.location
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import core.game.node.Node;
|
|||
import core.game.node.entity.player.Player;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
import rs09.game.content.global.shops.Shops;
|
||||
import rs09.game.system.config.ShopParser;
|
||||
|
||||
/**
|
||||
|
|
@ -18,7 +19,7 @@ public final class BuyCrateOptionPlugin extends OptionHandler {
|
|||
|
||||
@Override
|
||||
public boolean handle(Player player, Node node, String option) {
|
||||
ShopParser.Companion.openUid(player, 93);
|
||||
Shops.openId(player, 93);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import rs09.game.interaction.InteractionListener
|
|||
* Handles the culino chest options.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class CulinoChestListener : InteractionListener() {
|
||||
class CulinoChestListener : InteractionListener {
|
||||
val CULINO_CHEST = Scenery.CHEST_12309
|
||||
|
||||
override fun defineListeners() {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import rs09.game.world.GameWorld
|
|||
import rs09.plugin.ClassScanner
|
||||
|
||||
|
||||
class GardenObjectsPlugin : InteractionListener() {
|
||||
class GardenObjectsPlugin : InteractionListener {
|
||||
|
||||
val SQIRK_TREES = intArrayOf(21767, 21768, 21769, 21766)
|
||||
val FOUNTAIN = 21764
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package core.game.node.entity.npc;
|
|||
import api.events.NPCKillEvent;
|
||||
import core.cache.def.impl.NPCDefinition;
|
||||
import core.game.content.dialogue.DialoguePlugin;
|
||||
import core.game.content.global.shop.Shop;
|
||||
import core.game.interaction.Interaction;
|
||||
import core.game.interaction.MovementPulse;
|
||||
import core.game.node.entity.Entity;
|
||||
|
|
@ -33,6 +32,7 @@ import core.game.world.update.flag.npc.NPCForceChat;
|
|||
import core.game.world.update.flag.npc.NPCSwitchId;
|
||||
import core.tools.RandomFunction;
|
||||
import rs09.game.content.global.GlobalKillCounter;
|
||||
import rs09.game.content.global.shops.Shops;
|
||||
import rs09.game.content.jobs.JobManager;
|
||||
import rs09.game.node.entity.combat.CombatSwingHandler;
|
||||
import rs09.game.system.config.NPCConfigParser;
|
||||
|
|
@ -140,11 +140,6 @@ public class NPC extends Entity {
|
|||
* If the npc can never walk.
|
||||
*/
|
||||
private boolean neverWalks;
|
||||
|
||||
/**
|
||||
* The shop of this npc.
|
||||
*/
|
||||
private Shop shop;
|
||||
|
||||
/**
|
||||
* The force talk string.
|
||||
|
|
@ -234,18 +229,6 @@ public class NPC extends Entity {
|
|||
npc.size = size;
|
||||
}
|
||||
}
|
||||
if (definition.hasAction("trade")) {
|
||||
shop = ShopParser.Companion.getSHOPS().get(getId());
|
||||
if (shop != null) {
|
||||
shop = shop.copy();
|
||||
}
|
||||
}
|
||||
if (definition.hasAction("sew")) {
|
||||
shop = ShopParser.Companion.getSHOPS().get(getId());
|
||||
if (shop != null) {
|
||||
shop = shop.copy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -321,23 +304,14 @@ public class NPC extends Entity {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean openShop(Player player) {
|
||||
shop = null;
|
||||
if(shop != null){
|
||||
player.debug("testing new shop for: " + name);
|
||||
}
|
||||
if (shop == null) {
|
||||
shop = ShopParser.Companion.getSHOPS().get(getId());
|
||||
if (shop == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (getName().contains("assistant")) {
|
||||
NPC n = RegionManager.getNpc(this, getId() - 1);
|
||||
if (n != null) {
|
||||
return n.openShop(player);
|
||||
}
|
||||
}
|
||||
shop.open(player);
|
||||
|
||||
Shops.getShopsByNpc().get(id).openFor(player);
|
||||
|
||||
//Fix for issue #11 for shops keeping dialogue open.
|
||||
DialoguePlugin dialogue = player.getDialogueInterpreter().getDialogue();
|
||||
|
|
@ -470,14 +444,6 @@ public class NPC extends Entity {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (shop != null) {
|
||||
if (shop.getLastRestock() < GameWorld.getTicks()) {
|
||||
if (shop.isRestock()) {
|
||||
shop.restock();
|
||||
shop.setLastRestock(GameWorld.getTicks() + 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (forceTalk != null && getAttribute("lastForceTalk", 0) < GameWorld.getTicks()) {
|
||||
sendChat(forceTalk);
|
||||
setAttribute("lastForceTalk", GameWorld.getTicks() + RandomFunction.random(15, 30));
|
||||
|
|
@ -942,22 +908,6 @@ public class NPC extends Entity {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shop.
|
||||
* @return the shop
|
||||
*/
|
||||
public Shop getShop() {
|
||||
return shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bashop.
|
||||
* @param shop the shop to set.
|
||||
*/
|
||||
public void setShop(Shop shop) {
|
||||
this.shop = shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slayerExperience.
|
||||
* @return The slayerExperience.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import core.game.node.entity.player.info.RenderInfo;
|
|||
import core.game.node.entity.player.info.Rights;
|
||||
import core.game.node.entity.player.info.UIDInfo;
|
||||
import core.game.node.entity.player.info.login.LoginConfiguration;
|
||||
import core.game.node.entity.player.info.login.PlayerParser;
|
||||
import core.game.node.entity.player.link.*;
|
||||
import core.game.node.entity.player.link.appearance.Appearance;
|
||||
import core.game.node.entity.player.link.audio.AudioManager;
|
||||
|
|
@ -63,12 +64,14 @@ import core.tools.RandomFunction;
|
|||
import core.tools.StringUtils;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.rs09.consts.Items;
|
||||
import rs09.GlobalStats;
|
||||
import rs09.ServerConstants;
|
||||
import rs09.game.VarpManager;
|
||||
import rs09.game.node.entity.combat.CombatSwingHandler;
|
||||
import rs09.game.node.entity.combat.equipment.EquipmentDegrader;
|
||||
import rs09.game.node.entity.player.info.login.PlayerSaveParser;
|
||||
import rs09.game.node.entity.player.info.login.PlayerSaver;
|
||||
import rs09.game.node.entity.skill.runecrafting.PouchManager;
|
||||
import rs09.game.node.entity.state.newsys.State;
|
||||
|
|
|
|||
|
|
@ -97,14 +97,14 @@ public final class PacketDispatch {
|
|||
|
||||
/**
|
||||
* Send a access mask.
|
||||
* @param id The access mask id.
|
||||
* @param settingsHash The access mask settingsHash.
|
||||
* @param childId The access mask child id.
|
||||
* @param interfaceId The access mask interface Id.
|
||||
* @param offset The access mask off set.
|
||||
* @param length The access mask length.
|
||||
*/
|
||||
public void sendAccessMask(int id, int childId, int interfaceId, int offset, int length) {
|
||||
PacketRepository.send(AccessMask.class, new AccessMaskContext(player, id, childId, interfaceId, offset, length));
|
||||
public void sendIfaceSettings(int settingsHash, int childId, int interfaceId, int offset, int length) {
|
||||
PacketRepository.send(AccessMask.class, new AccessMaskContext(player, settingsHash, childId, interfaceId, offset, length));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import core.net.packet.context.StringContext;
|
|||
import core.net.packet.out.MusicPacket;
|
||||
import core.net.packet.out.StringPacket;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
|
@ -80,7 +79,7 @@ public final class MusicPlayer {
|
|||
for (int i = 0; i < CONFIG_IDS.length; i++) {
|
||||
value |= 2 << i;
|
||||
}
|
||||
player.getPacketDispatch().sendAccessMask(value, 1, 187, 0, CONFIG_IDS.length * 64);
|
||||
player.getPacketDispatch().sendIfaceSettings(value, 1, 187, 0, CONFIG_IDS.length * 64);
|
||||
if (!unlocked.containsKey(TUTORIAL_MUSIC)) {
|
||||
unlock(TUTORIAL_MUSIC, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,10 +272,10 @@ public final class TradeModule implements RequestModule {
|
|||
player.getInterfaceManager().open(MAIN_INTERFACE);
|
||||
player.getInterfaceManager().openSingleTab(OVERLAY_INTERFACE);
|
||||
player.getInventory().refresh();
|
||||
player.getPacketDispatch().sendAccessMask(1278, 30, 335, 0, 27);
|
||||
player.getPacketDispatch().sendAccessMask(1026, 32, 335, 0, 27);
|
||||
player.getPacketDispatch().sendAccessMask(1278, 0, 336, 0, 27);
|
||||
player.getPacketDispatch().sendAccessMask(2360446, 0, 335, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 30, 335, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(1026, 32, 335, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(1278, 0, 336, 0, 27);
|
||||
player.getPacketDispatch().sendIfaceSettings(2360446, 0, 335, 0, 27);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INVENTORY_PARAMS);
|
||||
player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", TRADE_PARAMS);
|
||||
player.getPacketDispatch().sendRunScript(695, "IviiiIsssssssss", PARTENER_PARAMS);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package core.game.node.entity.skill.construction.npc;
|
|||
|
||||
import core.cache.def.impl.ItemDefinition;
|
||||
import core.cache.def.impl.NPCDefinition;
|
||||
import core.game.content.global.shop.Shop;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.player.Player;
|
||||
|
|
@ -16,11 +15,11 @@ import core.plugin.Plugin;
|
|||
*/
|
||||
@Initializable
|
||||
public class StonemasonPlugin extends OptionHandler {
|
||||
|
||||
/**
|
||||
/*
|
||||
*//**
|
||||
* The store that sells supplies.
|
||||
*/
|
||||
private static final SupplyStore STORE = new SupplyStore();
|
||||
*//*
|
||||
private static final SupplyStore STORE = new SupplyStore();*/
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
|
|
@ -34,21 +33,20 @@ public class StonemasonPlugin extends OptionHandler {
|
|||
switch (option) {
|
||||
case "trade":
|
||||
case "talk-to":
|
||||
STORE.open(player);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
*//**
|
||||
* Stonemason's store.
|
||||
* @author Splinter
|
||||
*/
|
||||
*//*
|
||||
public static class SupplyStore extends Shop {
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Constructs a new {@Code SupplyStore} {@Code Object}
|
||||
*/
|
||||
*//*
|
||||
public SupplyStore() {
|
||||
super("Keldagrim Stonemason", new Item[] { new Item(3420, 1000), new Item(8786, 20), new Item(8784, 20), new Item(8788, 10) }, false);
|
||||
}
|
||||
|
|
@ -73,6 +71,6 @@ public class StonemasonPlugin extends OptionHandler {
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ import rs09.game.node.entity.skill.cooking.CookingDialogue
|
|||
* @author Ceikry
|
||||
* @author bushtail - added bear meat for sinew making
|
||||
*/
|
||||
class CookingRewrite : InteractionListener() {
|
||||
class CookingRewrite : InteractionListener {
|
||||
|
||||
val RAW_FOODS: IntArray
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package core.game.node.entity.skill.firemaking
|
|||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class FiremakingListener : InteractionListener()
|
||||
class FiremakingListener : InteractionListener
|
||||
{
|
||||
val logs = intArrayOf(1511, 1521, 1513, 1515, 1517, 1519, 1521, 2862, 3438, 3440, 3442, 3444, 3446, 3448, 6211, 6213, 6332, 6333, 7404, 7405, 7406, 8934, 9067, 10328, 10329, 10808, 10810, 10812, 11035, 12581, 12583, 3125)
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public final class SummoningCreator {
|
|||
public static final void configure(final Player player, final boolean pouch) {
|
||||
player.getInterfaceManager().open(pouch ? SUMMONING_COMPONENT : SCROLL_COMPONENT);
|
||||
player.getPacketDispatch().sendRunScript(pouch ? 757 : 765, pouch ? "Iiissssss" : "Iiisssss", pouch ? POUCH_PARAMS : SCROLL_PARAMS);
|
||||
player.getPacketDispatch().sendAccessMask(pouch ? 190 : 126, 15, pouch ? 669 : 673, 0, 78);
|
||||
player.getPacketDispatch().sendIfaceSettings(pouch ? 190 : 126, 15, pouch ? 669 : 673, 0, 78);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public final class ContainerContext implements Context {
|
|||
/**
|
||||
* The container type.
|
||||
*/
|
||||
private final int type;
|
||||
private final int containerId;
|
||||
|
||||
/**
|
||||
* The items.
|
||||
|
|
@ -73,12 +73,12 @@ public final class ContainerContext implements Context {
|
|||
* @param player The player.
|
||||
* @param interfaceId The interface id.
|
||||
* @param childId The child id.
|
||||
* @param type The container type.
|
||||
* @param containerId The container type.
|
||||
* @param container The container.
|
||||
* @param split If the container should be split.
|
||||
*/
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int type, Container container, boolean split) {
|
||||
this(player, interfaceId, childId, type, container.toArray(), container.toArray().length, split);
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Container container, boolean split) {
|
||||
this(player, interfaceId, childId, containerId, container.toArray(), container.toArray().length, split);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,12 +86,12 @@ public final class ContainerContext implements Context {
|
|||
* @param player The player.
|
||||
* @param interfaceId The interface id.
|
||||
* @param childId The child id.
|
||||
* @param type The container type.
|
||||
* @param containerId The container type.
|
||||
* @param items The items.
|
||||
* @param split If the container should be split.
|
||||
*/
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, boolean split) {
|
||||
this(player, interfaceId, childId, type, items, items.length, split);
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split) {
|
||||
this(player, interfaceId, childId, containerId, items, items.length, split);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,16 +99,16 @@ public final class ContainerContext implements Context {
|
|||
* @param player The player.
|
||||
* @param interfaceId The interface id.
|
||||
* @param childId The child id.
|
||||
* @param type The container type.
|
||||
* @param containerId The container containerId.
|
||||
* @param items The items.
|
||||
* @param length The length.
|
||||
* @param split If the container should be split.
|
||||
*/
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, int length, boolean split) {
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, int length, boolean split) {
|
||||
this.player = player;
|
||||
this.interfaceId = interfaceId;
|
||||
this.childId = childId;
|
||||
this.type = type;
|
||||
this.containerId = containerId;
|
||||
this.items = items;
|
||||
this.length = length;
|
||||
this.split = split;
|
||||
|
|
@ -120,16 +120,16 @@ public final class ContainerContext implements Context {
|
|||
* @param player The player.
|
||||
* @param interfaceId The interface id.
|
||||
* @param childId The child id.
|
||||
* @param type The container type.
|
||||
* @param containerId The container containerId.
|
||||
* @param items The items.
|
||||
* @param split If the container should be split.
|
||||
* @param slots The slots to update.
|
||||
*/
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, boolean split, int... slots) {
|
||||
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split, int... slots) {
|
||||
this.player = player;
|
||||
this.interfaceId = interfaceId;
|
||||
this.childId = childId;
|
||||
this.type = type;
|
||||
this.containerId = containerId;
|
||||
this.items = items;
|
||||
this.length = items.length;
|
||||
this.split = split;
|
||||
|
|
@ -153,8 +153,8 @@ public final class ContainerContext implements Context {
|
|||
* Gets the type.
|
||||
* @return The type.
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
public int getContainerId() {
|
||||
return containerId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ public class RunScriptPacketHandler implements IncomingPacket {
|
|||
} else {
|
||||
value = buffer.getInt();
|
||||
}
|
||||
if(value instanceof Integer)
|
||||
{
|
||||
if((int) value <= 0){
|
||||
player.removeAttribute("runscript");
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
script.invoke(value);
|
||||
} catch (NumberFormatException nfe){
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public final class ContainerPacket implements OutgoingPacket<ContainerContext> {
|
|||
buffer = new IoBuffer(slotBased ? 22 : 105, PacketHeader.SHORT);
|
||||
buffer.putShort(context.getInterfaceId());
|
||||
buffer.putShort(context.getChildId());
|
||||
buffer.putShort(context.getType());
|
||||
buffer.putShort(context.getContainerId());
|
||||
if (slotBased) {
|
||||
for (int slot : context.getSlots()) {
|
||||
buffer.putSmart(slot);
|
||||
|
|
|
|||
49
Server/src/main/kotlin/api/Commands.kt
Normal file
49
Server/src/main/kotlin/api/Commands.kt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package api
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import rs09.game.system.command.Command
|
||||
import rs09.game.system.command.CommandMapping
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.tools.stringtools.colorize
|
||||
|
||||
/**
|
||||
* An interface for writing content that allows the class to define commands.
|
||||
*
|
||||
* Includes methods [reject] and [notify] for notifying the player of a command rejection or some output text respectively.
|
||||
*
|
||||
* Includes the method [define] to define individual commands
|
||||
*
|
||||
* Commands should ideally be defined in the required [defineCommands] method.
|
||||
*/
|
||||
interface Commands : ContentInterface {
|
||||
/**
|
||||
* Glorified player.sendMessage with red text coloring. Please use this
|
||||
* rather than just player.sendMessage for anything that involves informing the player
|
||||
* of proper usage or invalid syntax. Throws an IllegalStateException and ends command execution immediately.
|
||||
*/
|
||||
fun reject(player: Player, vararg message: String){
|
||||
for(msg in message) {
|
||||
player.sendMessage(colorize("-->%R$msg"))
|
||||
}
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Glorified player.sendMessage with black text coloring and an arrow. Use this when you need to
|
||||
* notify/inform a player of some information from within the command without ending execution.
|
||||
*/
|
||||
fun notify(player: Player, message: String){
|
||||
player.sendMessage(colorize("-->$message"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to define commands. define("name",(optional) privilege override){ handle }
|
||||
* @param name the name of the command. Example: ::example would be just "example"
|
||||
* @param privilege the rights level needed to execute the command. Options are [Privilege.STANDARD], [Privilege.MODERATOR], [Privilege.ADMIN]. Defaults to [Privilege.STANDARD]
|
||||
*/
|
||||
fun define(name: String, privilege: Privilege = Privilege.STANDARD, handle: (Player, Array<String>) -> Unit){
|
||||
CommandMapping.register(Command(name, privilege, handle))
|
||||
}
|
||||
|
||||
fun defineCommands()
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import Cutscene
|
||||
import com.moandjiezana.toml.Toml
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.cache.def.impl.SceneryDefinition
|
||||
import core.game.component.Component
|
||||
|
|
@ -45,6 +46,7 @@ import rs09.game.content.global.GlobalKillCounter
|
|||
import rs09.game.interaction.InteractionListeners
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.config.ItemConfigParser
|
||||
import rs09.game.system.config.ServerConfigParser
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.GameWorld.Pulser
|
||||
import rs09.game.world.repository.Repository
|
||||
|
|
@ -714,6 +716,15 @@ fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List<NPC>{
|
|||
return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param regionId the ID of the region
|
||||
* @return a [ZoneBorders] encapsulating the entire region indicated by the provided regionId
|
||||
*/
|
||||
fun getRegionBorders(regionId: Int) : ZoneBorders
|
||||
{
|
||||
return ZoneBorders.forRegion(regionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an attribute key from the Entity's attributes store
|
||||
* @param entity the entity to get the attribute from
|
||||
|
|
@ -1447,4 +1458,9 @@ fun Player.getCutscene(): Cutscene? {
|
|||
|
||||
fun Player.getCutsceneStage(): Int {
|
||||
return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE_STAGE, 0)
|
||||
}
|
||||
|
||||
fun getServerConfig() : Toml
|
||||
{
|
||||
return ServerConfigParser.tomlData ?: Toml()
|
||||
}
|
||||
7
Server/src/main/kotlin/api/ContentInterface.kt
Normal file
7
Server/src/main/kotlin/api/ContentInterface.kt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package api
|
||||
|
||||
interface ContentInterface {
|
||||
/**
|
||||
* A dummy/shell interface that allows us to more easily load content via class scanning
|
||||
*/
|
||||
}
|
||||
151
Server/src/main/kotlin/api/IfaceSettingsBuilder.kt
Normal file
151
Server/src/main/kotlin/api/IfaceSettingsBuilder.kt
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package api
|
||||
|
||||
/**
|
||||
* Used to generate interface settings hashes.
|
||||
* Deprecates [core.game.container.access.BitregisterAssembler]
|
||||
* @author Ceikry
|
||||
*/
|
||||
class IfaceSettingsBuilder {
|
||||
/**
|
||||
* Contains the value which should be sent in access mask packet.
|
||||
*/
|
||||
private var value = 0
|
||||
|
||||
/**
|
||||
* Sets right click option settings. If specified option is not allowed, it
|
||||
* might not appear in the context menu, and if it does, it will throw an error when clicked.
|
||||
* @param optionId The option index.
|
||||
*/
|
||||
fun enableOption(optionId: Int): IfaceSettingsBuilder {
|
||||
require(!(optionId < 0 || optionId > 9)) { "Option index must be 0-9." }
|
||||
value = value or (0x1 shl optionId + 1)
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(vararg ids: Int): IfaceSettingsBuilder {
|
||||
for (i in ids.indices) {
|
||||
enableOption(ids[i])
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(ids: IntRange): IfaceSettingsBuilder {
|
||||
for (i in ids.start..ids.endInclusive) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableAllOptions(): IfaceSettingsBuilder {
|
||||
for (i in 0..9) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(vararg options: String?): IfaceSettingsBuilder {
|
||||
for (i in options.indices) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets use on option settings. If nothing is allowed then 'use' option will
|
||||
* not appear in right click menu.
|
||||
*/
|
||||
fun setUseOnSettings(
|
||||
groundItems: Boolean,
|
||||
npcs: Boolean,
|
||||
objects: Boolean,
|
||||
otherPlayer: Boolean,
|
||||
selfPlayer: Boolean,
|
||||
component: Boolean
|
||||
): IfaceSettingsBuilder {
|
||||
var useFlag = 0
|
||||
if (groundItems) {
|
||||
useFlag = useFlag or 0x1
|
||||
}
|
||||
if (npcs) {
|
||||
useFlag = useFlag or 0x2
|
||||
}
|
||||
if (objects) {
|
||||
useFlag = useFlag or 0x4
|
||||
}
|
||||
if (otherPlayer) {
|
||||
useFlag = useFlag or 0x8
|
||||
}
|
||||
if (selfPlayer) {
|
||||
useFlag = useFlag or 0x10
|
||||
}
|
||||
if (component) {
|
||||
useFlag = useFlag or 0x20
|
||||
}
|
||||
value = value or (useFlag shl 11)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interface events depth. For example, we have inventory interface
|
||||
* which is opened on gameframe interface (548) If depth is 1, then the
|
||||
* clicks in inventory will also invoke click event handler scripts on
|
||||
* gameframe interface. Setting depth to 2 also allows dragged items to
|
||||
* leave the bounds of their container, useful for things such as bank tabs.
|
||||
* @param depth The depth value.
|
||||
*/
|
||||
fun setInterfaceEventsDepth(depth: Int): IfaceSettingsBuilder {
|
||||
require(!(depth < 0 || depth > 7)) { "depth must be 0-7." }
|
||||
value = value and (0x7 shl 18).inv()
|
||||
value = value or (depth shl 18)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows items in this interface container to be switched around
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableSlotSwitch(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 21)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows items in this interface container to have a "Use" option
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableUseOption(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 17)
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableExamine(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 9)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this component to have items used on it
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableUseOnSelf(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 22)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this component to switch item slots with a slot that contains a null (empty slot)
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableNullSlotSwitch(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 23)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* @return The value.
|
||||
*/
|
||||
fun build(): Int {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,12 @@ package api
|
|||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LoginListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute some code when a player logs in.
|
||||
*
|
||||
* Login listeners are called *before* [PersistPlayer] data is parsed.
|
||||
*/
|
||||
interface LoginListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ package api
|
|||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LogoutListener {
|
||||
/**
|
||||
* An interface for writing content that allows code to be executed by the class when a player logs out.
|
||||
*
|
||||
* Logout listeners are called *before* [PersistPlayer] data is saved.
|
||||
*/
|
||||
interface LogoutListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
|
|
|||
18
Server/src/main/kotlin/api/MapArea.kt
Normal file
18
Server/src/main/kotlin/api/MapArea.kt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package api
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneRestriction
|
||||
|
||||
/**
|
||||
* Interface that allows a class to define a map area.
|
||||
* Optionally-overridable methods include [getRestrictions], [areaEnter], [areaLeave] and [entityStep]
|
||||
*/
|
||||
interface MapArea {
|
||||
fun defineAreaBorders() : Array<ZoneBorders>
|
||||
fun getRestrictions() : Array<ZoneRestriction> {return arrayOf()}
|
||||
fun areaEnter(entity: Entity) {}
|
||||
fun areaLeave(entity: Entity, logout: Boolean) {}
|
||||
fun entityStep(entity: Entity, location: Location, lastLocation: Location) {}
|
||||
}
|
||||
|
|
@ -3,7 +3,14 @@ package api
|
|||
import core.game.node.entity.player.Player
|
||||
import org.json.simple.JSONObject
|
||||
|
||||
interface PersistPlayer {
|
||||
/**
|
||||
* An interface for writing content that allows data to be saved and loaded from player saves.
|
||||
*
|
||||
* Parsing is called *after* any [LoginListener] is executed.
|
||||
*
|
||||
* Saving is called *after* any [LogoutListener] is executed.
|
||||
*/
|
||||
interface PersistPlayer : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* You need to fetch a player's specific instance of the data and save from that.
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
package api
|
||||
|
||||
interface PersistWorld {
|
||||
fun saveWorld()
|
||||
fun parseWorld()
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ package api
|
|||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface ShutdownListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code as the server is shutting down
|
||||
*/
|
||||
interface ShutdownListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package api
|
|||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface StartupListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code when the server is started.
|
||||
*/
|
||||
interface StartupListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package api
|
||||
|
||||
interface TickListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to be updated each tick.
|
||||
*/
|
||||
interface TickListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* TickListeners are generally for NON-player, WORLD tick events.
|
||||
|
|
|
|||
|
|
@ -6,17 +6,14 @@ import core.game.world.map.Location
|
|||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.ai.general.GeneralBotCreator
|
||||
import rs09.game.ai.general.ScriptAPI
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStarEvent
|
||||
import rs09.game.interaction.InteractionListener.Companion.SCENERY
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStarPlugin
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InteractionListeners
|
||||
import kotlin.concurrent.timer
|
||||
|
||||
class ShootingStarBot : Script() {
|
||||
private var state = State.FULL_IDLE
|
||||
private var timerCountdown = 0
|
||||
val star = (WorldEvents.get("shooting-stars") as? ShootingStarEvent)!!.star
|
||||
val star = ShootingStarPlugin.getStar()
|
||||
|
||||
override fun tick() {
|
||||
bot.fullRestore()
|
||||
|
|
@ -36,7 +33,7 @@ class ShootingStarBot : Script() {
|
|||
}
|
||||
|
||||
State.MINING -> {
|
||||
InteractionListeners.run(star.starObject.id, SCENERY, "mine", bot, star.starObject)
|
||||
InteractionListeners.run(star.starObject.id, InteractionListener.SCENERY, "mine", bot, star.starObject)
|
||||
}
|
||||
|
||||
State.TELEPORT_BACK -> {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ private val LIGHT_ANIM = Animation(7307)
|
|||
* Handles interactions for beacons
|
||||
* @author Ceikry
|
||||
*/
|
||||
class AFUBeaconListeners : InteractionListener(){
|
||||
class AFUBeaconListeners : InteractionListener{
|
||||
|
||||
override fun defineListeners() {
|
||||
on(SCENERY,"add-logs","light"){ player, node ->
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import java.util.*
|
|||
* Handles repairing and climbing of the 3 beacon shortcuts needed to access them
|
||||
* @author Ceikry
|
||||
*/
|
||||
class AFURepairClimbHandler : InteractionListener() {
|
||||
class AFURepairClimbHandler : InteractionListener {
|
||||
|
||||
val repairIDs = intArrayOf(38480,38470,38494)
|
||||
val climbIDs = intArrayOf(38469,38471,38486,38481,38469)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import rs09.game.interaction.InterfaceListener
|
|||
* @author definitely phil who didn't get help from ceikry at all haha :)
|
||||
* @version 69.0
|
||||
*/
|
||||
class BlastFurnaceInterfaceListener : InterfaceListener() {
|
||||
class BlastFurnaceInterfaceListener : InterfaceListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
|
||||
on(Components.BLAST_FURNACE_BAR_STOCK_28){ player, _, _, buttonID, _, _ ->
|
||||
val bar = BFBars.forId(buttonID) ?: return@on false
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import rs09.game.node.entity.npc.other.BlastFurnaceOre
|
|||
* That lives in OrdanDialogue.kt
|
||||
* @author phil lips*/
|
||||
|
||||
class BlastFurnaceListeners : InteractionListener() {
|
||||
class BlastFurnaceListeners : InteractionListener {
|
||||
|
||||
val disLoc = getScenery(1941, 4963, 0)
|
||||
val brokenPotPipe = 9117
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import rs09.game.interaction.InterfaceListener
|
|||
* Only updates the gauge if people are actually looking at it
|
||||
* @author phil lips*/
|
||||
|
||||
class PhunnyGaugeTempInterfaceListener : InterfaceListener(){
|
||||
class PhunnyGaugeTempInterfaceListener : InterfaceListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen(30) {player, component ->
|
||||
BlastFurnace.gaugeViewList.add(player)
|
||||
return@onOpen true
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import kotlin.math.ceil
|
|||
* Option handler for fishing trawler
|
||||
* @author Ceikry
|
||||
*/
|
||||
class FishingTrawlerInteractionHandler : InteractionListener() {
|
||||
class FishingTrawlerInteractionHandler : InteractionListener {
|
||||
val ENTRANCE_PLANK = 2178
|
||||
val EXIT_PLANK = 2179
|
||||
val HOLE = 2167
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import rs09.game.interaction.InteractionListener
|
|||
* @author bushtail
|
||||
*/
|
||||
|
||||
class CraftingGuildListeners : InteractionListener() {
|
||||
class CraftingGuildListeners : InteractionListener {
|
||||
private val GUILD_DOOR = Scenery.GUILD_DOOR_2647
|
||||
private val APRON = Items.BROWN_APRON_1757
|
||||
private val CAPE = Items.CRAFTING_CAPE_9780
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import rs09.game.interaction.InteractionListener
|
|||
import rs09.game.node.entity.skill.magic.SpellListener
|
||||
import rs09.game.node.entity.skill.magic.spellconsts.Modern
|
||||
|
||||
class MTAListeners : InteractionListener() {
|
||||
class MTAListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.MAZE_GUARDIAN_3102,NPC,"talk-to"){player,node ->
|
||||
player.dialogueInterpreter.open(node.id, node)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package core.game.interaction.item
|
||||
package rs09.game.content.activity.pyramidplunder
|
||||
|
||||
import api.EquipmentSlot
|
||||
import api.openDialogue
|
||||
|
|
@ -21,7 +21,7 @@ import rs09.tools.END_DIALOGUE
|
|||
* Adds functionality to the pharoah's scepter
|
||||
* @author ceik
|
||||
*/
|
||||
class PharoahSceptre : InteractionListener() {
|
||||
class PharaohSceptre : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
val SCEPTRES = intArrayOf(Items.PHARAOHS_SCEPTRE_9044, Items.PHARAOHS_SCEPTRE_9046, Items.PHARAOHS_SCEPTRE_9048, Items.PHARAOHS_SCEPTRE_9050)
|
||||
|
||||
|
|
@ -17,11 +17,12 @@ import org.rs09.consts.Scenery
|
|||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
|
||||
/**
|
||||
* The "controller" class for pyramid plunder. Handles per-tick updates, logout hooks, and defines interaction listeners for the minigame.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class PyramidPlunderMinigame : InteractionListener(), TickListener, LogoutListener {
|
||||
class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener {
|
||||
override fun tick() {
|
||||
val playersToExpel = PlunderUtils.decrementTimeRemaining()
|
||||
playersToExpel.forEach {player -> PlunderUtils.expel(player, false) }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
package rs09.game.content.activity.vinesweeper
|
||||
|
||||
import BlinkinDialogue
|
||||
import WinkinDialogue
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.DEAD_PLANT_OBJ
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.FARMERS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.HOLES
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.NUMBERS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.RABBIT
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.SEED_LOCS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.populateSeeds
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.scheduleNPCs
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.sendUpdatedPoints
|
||||
import api.*
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
|
|
@ -13,164 +27,52 @@ import core.game.node.scenery.SceneryBuilder
|
|||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneBuilder
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
val AVACH_NIMPORTO_LOC = Location.create(1637, 4709)
|
||||
val PORTAL = 29534
|
||||
val SIGNS = intArrayOf(29461, 29462, 29463, 29464)
|
||||
val HOLES = intArrayOf(29476, 29477, 29478)
|
||||
val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455)
|
||||
val DEAD_PLANT_OBJ = 29456
|
||||
val FLAG_OBJ = 29457
|
||||
class Vinesweeper : InteractionListener, InterfaceListener, MapArea {
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(getRegionBorders(6473))
|
||||
}
|
||||
|
||||
val TUTORIAL = 685
|
||||
|
||||
val INSTRUCTION_SIGNS = hashMapOf(
|
||||
29463 to 684,
|
||||
29464 to 687,
|
||||
29462 to 688,
|
||||
29461 to 690
|
||||
)
|
||||
|
||||
val RABBIT = 7125
|
||||
val FARMERS = intArrayOf(7128, 7129, 7130)
|
||||
val FARMER_BLINKIN = 7131
|
||||
val MRS_WINKIN = 7132
|
||||
|
||||
val MAX_SEEDS = 300
|
||||
val FARMER_CLEAR_RADIUS = 3
|
||||
val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735)
|
||||
|
||||
fun sendUpdatedPoints(player: Player) {
|
||||
val points = player.getAttribute("vinesweeper:points", 0);
|
||||
player.varpManager.get(1195).setVarbit(6, points).send(player)
|
||||
}
|
||||
|
||||
data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other is SeedDestination) {
|
||||
return loc.equals(other.loc)
|
||||
} else {
|
||||
return false
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if(entity is Player)
|
||||
{
|
||||
openOverlay(entity, Components.RABBIT_OVERLAY_689)
|
||||
sendUpdatedPoints(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
var SEED_LOCS: HashSet<Location> = HashSet()
|
||||
|
||||
fun isSeed(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && SEED_LOCS.contains(scenery.location)
|
||||
}
|
||||
|
||||
fun populateSeeds() {
|
||||
while(SEED_LOCS.size < MAX_SEEDS) {
|
||||
val loc = VINESWEEPER_BORDERS.getRandomLoc()
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null && HOLES.contains(scenery.id)) {
|
||||
SEED_LOCS.add(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isHole(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && HOLES.contains(scenery.id)
|
||||
}
|
||||
|
||||
fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) {
|
||||
val dest = SeedDestination(player, loc, alive)
|
||||
val ids = if(rabbit) { intArrayOf(RABBIT, *FARMERS) } else { FARMERS }
|
||||
for(npc in findLocalNPCs(player, ids, 30)) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.add(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VinesweeperTeleport {
|
||||
@JvmStatic
|
||||
fun teleport(npc: NPC, player: Player) {
|
||||
npc.animate(Animation(437))
|
||||
npc.faceTemporary(player, 1)
|
||||
npc.graphics(Graphics(108))
|
||||
player.lock()
|
||||
player.audioManager.send(125)
|
||||
Projectile.create(npc, player, 109).send()
|
||||
npc.sendChat("Avach nimporto!")
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
2 -> {
|
||||
player.savedData.globalData.essenceTeleporter = npc.id
|
||||
player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x)
|
||||
player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y)
|
||||
player.properties.teleportLocation = AVACH_NIMPORTO_LOC
|
||||
}
|
||||
3 -> {
|
||||
player.graphics(Graphics(110))
|
||||
player.unlock()
|
||||
return true
|
||||
}
|
||||
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||
if(entity is Player) {
|
||||
entity.interfaceManager.closeOverlay()
|
||||
if(!logout) {
|
||||
entity.sendMessage("Winkin's Farm thanks you for your visit.")
|
||||
entity.sendMessage("Leftover ogleroots and flags have been returned to the establishment.")
|
||||
entity.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.")
|
||||
val flags = entity.inventory.getAmount(Item(Items.FLAG_12625))
|
||||
if(flags > 0) {
|
||||
entity.setAttribute("/save:vinesweeper:stored-flags", flags)
|
||||
entity.inventory.remove(Item(Items.FLAG_12625, flags))
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperListener : InteractionListener() {
|
||||
fun dig(player: Player, loc: Location) {
|
||||
if(isSeed(loc)) {
|
||||
val oldPoints = player.getAttribute("vinesweeper:points", 0)
|
||||
player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0))
|
||||
sendUpdatedPoints(player)
|
||||
player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.");
|
||||
scheduleNPCs(player, loc, false, false)
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(DEAD_PLANT_OBJ))
|
||||
}
|
||||
} else {
|
||||
player.incrementAttribute("/save:vinesweeper:points", 1)
|
||||
sendUpdatedPoints(player)
|
||||
var count = 0
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
if(isSeed(loc.transform(dx, dy, 0))) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count]))
|
||||
}
|
||||
if(count == 0) {
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
val newLoc = loc.transform(dx, dy, 0)
|
||||
if(isHole(newLoc))
|
||||
dig(player, newLoc)
|
||||
}
|
||||
val roots = entity.inventory.getAmount(Item(Items.OGLEROOT_12624))
|
||||
if(roots > 0) {
|
||||
entity.inventory.remove(Item(Items.OGLEROOT_12624, roots))
|
||||
entity.inventory.add(Item(Items.COINS_995, roots * 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
ZoneBuilder.configure(VinesweeperZone())
|
||||
populateSeeds()
|
||||
on(PORTAL, SCENERY, "enter") { player, _ ->
|
||||
val x = player.getAttribute("vinesweeper:return-tele:x", 3052)
|
||||
|
|
@ -227,7 +129,7 @@ class VinesweeperListener : InteractionListener() {
|
|||
return@on true
|
||||
}
|
||||
on(MRS_WINKIN, NPC, "talk-to") { player, npc ->
|
||||
openDialogue(player, WinkinDialogue(), npc)
|
||||
openDialogue(player, WinkinDialogue(), npc)
|
||||
return@on true
|
||||
}
|
||||
on(MRS_WINKIN, NPC, "trade") { player, _ ->
|
||||
|
|
@ -264,7 +166,7 @@ class VinesweeperListener : InteractionListener() {
|
|||
}
|
||||
on(FARMER_BLINKIN, NPC, "talk-to") { player, npc ->
|
||||
//player.interfaceManager.open(Component(TUTORIAL))
|
||||
openDialogue(player, BlinkinDialogue(), npc)
|
||||
openDialogue(player, BlinkinDialogue(), npc)
|
||||
return@on true
|
||||
}
|
||||
on(FARMER_BLINKIN, NPC, "buy-flags") { player, npc ->
|
||||
|
|
@ -280,240 +182,8 @@ class VinesweeperListener : InteractionListener() {
|
|||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Initializable
|
||||
class VinesweeperNPC : AbstractNPC {
|
||||
fun compareDistance(a: SeedDestination, b: SeedDestination): Int {
|
||||
val da = a.loc.getDistance(location).toInt()
|
||||
val db = b.loc.getDistance(location).toInt()
|
||||
return db - da
|
||||
}
|
||||
var seedDestinations: ArrayList<SeedDestination> = ArrayList();
|
||||
constructor() : super(RABBIT, null, true) {}
|
||||
private constructor(id: Int, location: Location) : super(id, location) {}
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
|
||||
return VinesweeperNPC(id, location)
|
||||
}
|
||||
|
||||
init {
|
||||
walkRadius = 22
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(RABBIT, *FARMERS)
|
||||
}
|
||||
|
||||
override fun handleTickActions() {
|
||||
val dest = seedDestinations.find { sd -> sd.loc == location }
|
||||
if(dest != null) {
|
||||
for(npc in RegionManager.getRegionPlane(location).npcs) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.remove(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(dest.loc)
|
||||
if(scenery != null) {
|
||||
if(id == RABBIT) {
|
||||
val replacement = if(SEED_LOCS.contains(dest.loc)) { DEAD_PLANT_OBJ } else { HOLES[0] }
|
||||
SceneryBuilder.replace(scenery, scenery.transform(replacement))
|
||||
scheduleNPCs(dest.player, dest.loc, false, false)
|
||||
} else {
|
||||
if(dest.alive) {
|
||||
handleFarmerFlag(scenery, dest)
|
||||
} else {
|
||||
sendChat("Hmm. Looks like there's a plant here.")
|
||||
lock(3)
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Gracious me! This one's dead")
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
seedDestinations.remove(dest)
|
||||
}
|
||||
super.handleTickActions()
|
||||
}
|
||||
|
||||
override fun getMovementDestination(): Location? {
|
||||
if(seedDestinations.size > 0) {
|
||||
seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() }
|
||||
return seedDestinations.first().loc
|
||||
} else {
|
||||
return super.getMovementDestination()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleFarmerFlag(scenery: Scenery, dest: SeedDestination) {
|
||||
sendChat("Ah, another flag to clear. Let's see what's there.")
|
||||
lock(3)
|
||||
animate(Animation(451))
|
||||
if(SEED_LOCS.contains(dest.loc)) {
|
||||
val npc = this
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Ah! A seed. Points for everyone near me!")
|
||||
val level = dest.player.skills.getStaticLevel(Skills.FARMING)
|
||||
val points = RandomFunction.random(level, 4 * level)
|
||||
dest.player.incrementAttribute("/save:vinesweeper:points", points)
|
||||
dest.player.inventory.add(Item(Items.FLAG_12625, 1))
|
||||
sendUpdatedPoints(dest.player)
|
||||
for(neighbor in RegionManager.getLocalPlayers(npc)) {
|
||||
if(neighbor != dest.player) {
|
||||
neighbor.incrementAttribute("/save:vinesweeper:points", points / 2)
|
||||
sendUpdatedPoints(neighbor)
|
||||
}
|
||||
}
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
var i = 0
|
||||
val lines = arrayOf("Hmm, no seeds planted here, I'm afraid.", "I'll have to keep this 'ere flag. Sorry.")
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat(lines[i++])
|
||||
return i >= lines.size
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
fun farmerClear(dest: SeedDestination) {
|
||||
for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
val toClear = getScenery(dest.loc.transform(dx, dy, 0))
|
||||
if(toClear != null && intArrayOf(DEAD_PLANT_OBJ, *NUMBERS).contains(toClear.id)) {
|
||||
SceneryBuilder.replace(toClear, toClear.transform(HOLES[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
SEED_LOCS.remove(dest.loc)
|
||||
populateSeeds()
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperZone : MapZone("Vinesweeper", true) {
|
||||
override fun enter(e: Entity): Boolean {
|
||||
if(e is Player) {
|
||||
e.interfaceManager.openOverlay(Component(689))
|
||||
sendUpdatedPoints(e)
|
||||
}
|
||||
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun leave(e: Entity, logout: Boolean): Boolean {
|
||||
if(e is Player) {
|
||||
e.interfaceManager.closeOverlay()
|
||||
if(!logout) {
|
||||
e.sendMessage("Winkin's Farm thanks you for your visit.")
|
||||
e.sendMessage("Leftover ogleroots and flags have been returned to the establishment.")
|
||||
e.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.")
|
||||
val flags = e.inventory.getAmount(Item(Items.FLAG_12625))
|
||||
if(flags > 0) {
|
||||
e.setAttribute("/save:vinesweeper:stored-flags", flags)
|
||||
e.inventory.remove(Item(Items.FLAG_12625, flags))
|
||||
}
|
||||
val roots = e.inventory.getAmount(Item(Items.OGLEROOT_12624))
|
||||
if(roots > 0) {
|
||||
e.inventory.remove(Item(Items.OGLEROOT_12624, roots))
|
||||
e.inventory.add(Item(Items.COINS_995, roots * 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.leave(e, logout)
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
super.registerRegion(6473)
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperRewards : InterfaceListener() {
|
||||
val IFACE = 686
|
||||
val TRADE_FOR_XP_BUTTON = 53
|
||||
val XP_CONFIRM = 72
|
||||
val XP_DENY = 73
|
||||
|
||||
enum class Opcode(val value: Int) {
|
||||
VALUE(155),
|
||||
BUY1(196),
|
||||
BUY5(124),
|
||||
BUY10(199),
|
||||
BUYX(234),
|
||||
}
|
||||
|
||||
data class Reward(val itemID: Int, val points: Int) {}
|
||||
|
||||
val REWARDS = hashMapOf(
|
||||
18 to Reward(Items.TOMATO_SEED_5322, 10),
|
||||
19 to Reward(Items.SWEETCORN_SEED_5320, 150),
|
||||
20 to Reward(Items.STRAWBERRY_SEED_5323, 165),
|
||||
21 to Reward(Items.WATERMELON_SEED_5321, 680),
|
||||
22 to Reward(Items.GUAM_SEED_5291, 10),
|
||||
23 to Reward(Items.MARRENTILL_SEED_5292, 10),
|
||||
24 to Reward(Items.RANARR_SEED_5295, 4000),
|
||||
25 to Reward(Items.KWUARM_SEED_5299, 1000),
|
||||
26 to Reward(Items.TARROMIN_SEED_5293, 10),
|
||||
27 to Reward(Items.NASTURTIUM_SEED_5098, 10),
|
||||
|
||||
28 to Reward(Items.WOAD_SEED_5099, 30),
|
||||
29 to Reward(Items.LIMPWURT_SEED_5100, 70),
|
||||
30 to Reward(Items.ASGARNIAN_SEED_5308, 5),
|
||||
31 to Reward(Items.KRANDORIAN_SEED_5310, 20),
|
||||
32 to Reward(Items.REDBERRY_SEED_5101, 5),
|
||||
33 to Reward(Items.CADAVABERRY_SEED_5102, 5),
|
||||
34 to Reward(Items.DWELLBERRY_SEED_5103, 5),
|
||||
35 to Reward(Items.JANGERBERRY_SEED_5104, 10),
|
||||
36 to Reward(Items.WHITEBERRY_SEED_5105, 25),
|
||||
|
||||
37 to Reward(Items.POISON_IVY_SEED_5106, 30),
|
||||
38 to Reward(Items.ACORN_5312, 100),
|
||||
39 to Reward(Items.WILLOW_SEED_5313, 1800),
|
||||
40 to Reward(Items.MAPLE_SEED_5314, 12000),
|
||||
41 to Reward(Items.PINEAPPLE_SEED_5287, 10000),
|
||||
42 to Reward(Items.YEW_SEED_5315, 29000),
|
||||
43 to Reward(Items.PALM_TREE_SEED_5289, 35000),
|
||||
44 to Reward(Items.SPIRIT_SEED_5317, 55000),
|
||||
45 to Reward(Items.COMPOST_POTION4_6470, 5000),
|
||||
46 to Reward(Items.FLAG_12625, 50),
|
||||
)
|
||||
|
||||
fun buy(player: Player, buttonID: Int, amount: Int) {
|
||||
val reward = REWARDS[buttonID] ?: return
|
||||
val cost = amount * reward.points
|
||||
val points = player.getAttribute("vinesweeper:points", 0)
|
||||
if(cost in 1 until points) {
|
||||
val item = Item(reward.itemID, amount)
|
||||
if(!player.inventory.add(item)) {
|
||||
GroundItemManager.create(item, player)
|
||||
}
|
||||
player.incrementAttribute("/save:vinesweeper:points", -cost)
|
||||
sendUpdatedPoints(player)
|
||||
} else {
|
||||
// TODO (crash): authenticity
|
||||
player.sendMessage("You don't have enough points for that.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
onOpen(IFACE) { _, _ ->
|
||||
/*for((buttonID, reward) in REWARDS) {
|
||||
sendItemOnInterface(player, IFACE, buttonID, reward.itemID, 5)
|
||||
}*/
|
||||
//player.packetDispatch.sendRunScript(2003, "")
|
||||
return@onOpen true
|
||||
}
|
||||
override fun defineInterfaceListeners() {
|
||||
on(IFACE) { player, _, opcode, buttonID, _, _ ->
|
||||
when(opcode) {
|
||||
Opcode.VALUE.value -> {
|
||||
|
|
@ -566,6 +236,338 @@ class VinesweeperRewards : InterfaceListener() {
|
|||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if(other is SeedDestination) {
|
||||
loc == other.loc
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return loc.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
companion object
|
||||
{
|
||||
val AVACH_NIMPORTO_LOC = Location.create(1637, 4709)
|
||||
val PORTAL = 29534
|
||||
val SIGNS = intArrayOf(29461, 29462, 29463, 29464)
|
||||
val HOLES = intArrayOf(29476, 29477, 29478)
|
||||
val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455)
|
||||
val DEAD_PLANT_OBJ = 29456
|
||||
val FLAG_OBJ = 29457
|
||||
|
||||
val IFACE = 686
|
||||
val TRADE_FOR_XP_BUTTON = 53
|
||||
val XP_CONFIRM = 72
|
||||
val XP_DENY = 73
|
||||
|
||||
enum class Opcode(val value: Int) {
|
||||
VALUE(155),
|
||||
BUY1(196),
|
||||
BUY5(124),
|
||||
BUY10(199),
|
||||
BUYX(234),
|
||||
}
|
||||
|
||||
data class Reward(val itemID: Int, val points: Int) {}
|
||||
|
||||
val REWARDS = hashMapOf(
|
||||
18 to Reward(Items.TOMATO_SEED_5322, 10),
|
||||
19 to Reward(Items.SWEETCORN_SEED_5320, 150),
|
||||
20 to Reward(Items.STRAWBERRY_SEED_5323, 165),
|
||||
21 to Reward(Items.WATERMELON_SEED_5321, 680),
|
||||
22 to Reward(Items.GUAM_SEED_5291, 10),
|
||||
23 to Reward(Items.MARRENTILL_SEED_5292, 10),
|
||||
24 to Reward(Items.RANARR_SEED_5295, 4000),
|
||||
25 to Reward(Items.KWUARM_SEED_5299, 1000),
|
||||
26 to Reward(Items.TARROMIN_SEED_5293, 10),
|
||||
27 to Reward(Items.NASTURTIUM_SEED_5098, 10),
|
||||
|
||||
28 to Reward(Items.WOAD_SEED_5099, 30),
|
||||
29 to Reward(Items.LIMPWURT_SEED_5100, 70),
|
||||
30 to Reward(Items.ASGARNIAN_SEED_5308, 5),
|
||||
31 to Reward(Items.KRANDORIAN_SEED_5310, 20),
|
||||
32 to Reward(Items.REDBERRY_SEED_5101, 5),
|
||||
33 to Reward(Items.CADAVABERRY_SEED_5102, 5),
|
||||
34 to Reward(Items.DWELLBERRY_SEED_5103, 5),
|
||||
35 to Reward(Items.JANGERBERRY_SEED_5104, 10),
|
||||
36 to Reward(Items.WHITEBERRY_SEED_5105, 25),
|
||||
|
||||
37 to Reward(Items.POISON_IVY_SEED_5106, 30),
|
||||
38 to Reward(Items.ACORN_5312, 100),
|
||||
39 to Reward(Items.WILLOW_SEED_5313, 1800),
|
||||
40 to Reward(Items.MAPLE_SEED_5314, 12000),
|
||||
41 to Reward(Items.PINEAPPLE_SEED_5287, 10000),
|
||||
42 to Reward(Items.YEW_SEED_5315, 29000),
|
||||
43 to Reward(Items.PALM_TREE_SEED_5289, 35000),
|
||||
44 to Reward(Items.SPIRIT_SEED_5317, 55000),
|
||||
45 to Reward(Items.COMPOST_POTION4_6470, 5000),
|
||||
46 to Reward(Items.FLAG_12625, 50),
|
||||
)
|
||||
|
||||
fun buy(player: Player, buttonID: Int, amount: Int) {
|
||||
val reward = REWARDS[buttonID] ?: return
|
||||
val cost = amount * reward.points
|
||||
val points = player.getAttribute("vinesweeper:points", 0)
|
||||
if(cost in 1 until points) {
|
||||
val item = Item(reward.itemID, amount)
|
||||
if(!player.inventory.add(item)) {
|
||||
GroundItemManager.create(item, player)
|
||||
}
|
||||
player.incrementAttribute("/save:vinesweeper:points", -cost)
|
||||
sendUpdatedPoints(player)
|
||||
} else {
|
||||
// TODO (crash): authenticity
|
||||
player.sendMessage("You don't have enough points for that.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val TUTORIAL = 685
|
||||
|
||||
val INSTRUCTION_SIGNS = hashMapOf(
|
||||
29463 to 684,
|
||||
29464 to 687,
|
||||
29462 to 688,
|
||||
29461 to 690
|
||||
)
|
||||
|
||||
val RABBIT = 7125
|
||||
val FARMERS = intArrayOf(7128, 7129, 7130)
|
||||
val FARMER_BLINKIN = 7131
|
||||
val MRS_WINKIN = 7132
|
||||
|
||||
val MAX_SEEDS = 300
|
||||
val FARMER_CLEAR_RADIUS = 3
|
||||
val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735)
|
||||
|
||||
fun sendUpdatedPoints(player: Player) {
|
||||
val points = player.getAttribute("vinesweeper:points", 0);
|
||||
player.varpManager.get(1195).setVarbit(6, points).send(player)
|
||||
}
|
||||
|
||||
var SEED_LOCS: HashSet<Location> = HashSet()
|
||||
|
||||
fun isSeed(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && SEED_LOCS.contains(scenery.location)
|
||||
}
|
||||
|
||||
fun populateSeeds() {
|
||||
while(SEED_LOCS.size < MAX_SEEDS) {
|
||||
val loc = VINESWEEPER_BORDERS.getRandomLoc()
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null && HOLES.contains(scenery.id)) {
|
||||
SEED_LOCS.add(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dig(player: Player, loc: Location) {
|
||||
if(isSeed(loc)) {
|
||||
val oldPoints = player.getAttribute("vinesweeper:points", 0)
|
||||
player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0))
|
||||
sendUpdatedPoints(player)
|
||||
player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.");
|
||||
scheduleNPCs(player, loc, false, false)
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(DEAD_PLANT_OBJ))
|
||||
}
|
||||
} else {
|
||||
player.incrementAttribute("/save:vinesweeper:points", 1)
|
||||
sendUpdatedPoints(player)
|
||||
var count = 0
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
if(isSeed(loc.transform(dx, dy, 0))) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count]))
|
||||
}
|
||||
if(count == 0) {
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
val newLoc = loc.transform(dx, dy, 0)
|
||||
if(isHole(newLoc))
|
||||
dig(player, newLoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isHole(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && HOLES.contains(scenery.id)
|
||||
}
|
||||
|
||||
fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) {
|
||||
val dest = SeedDestination(player, loc, alive)
|
||||
val ids = if(rabbit) { intArrayOf(RABBIT, *FARMERS) } else { FARMERS }
|
||||
for(npc in findLocalNPCs(player, ids, 30)) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.add(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VinesweeperTeleport {
|
||||
@JvmStatic
|
||||
fun teleport(npc: NPC, player: Player) {
|
||||
npc.animate(Animation(437))
|
||||
npc.faceTemporary(player, 1)
|
||||
npc.graphics(Graphics(108))
|
||||
player.lock()
|
||||
player.audioManager.send(125)
|
||||
Projectile.create(npc, player, 109).send()
|
||||
npc.sendChat("Avach nimporto!")
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
2 -> {
|
||||
player.savedData.globalData.essenceTeleporter = npc.id
|
||||
player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x)
|
||||
player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y)
|
||||
player.properties.teleportLocation = AVACH_NIMPORTO_LOC
|
||||
}
|
||||
3 -> {
|
||||
player.graphics(Graphics(110))
|
||||
player.unlock()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Initializable
|
||||
class VinesweeperNPC : AbstractNPC {
|
||||
var seedDestinations: ArrayList<Vinesweeper.SeedDestination> = ArrayList();
|
||||
constructor() : super(RABBIT, null, true) {}
|
||||
private constructor(id: Int, location: Location) : super(id, location) {}
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
|
||||
return VinesweeperNPC(id, location)
|
||||
}
|
||||
|
||||
init {
|
||||
walkRadius = 22
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(RABBIT, *FARMERS)
|
||||
}
|
||||
|
||||
override fun handleTickActions() {
|
||||
val dest = seedDestinations.find { sd -> sd.loc == location }
|
||||
if(dest != null) {
|
||||
for(npc in RegionManager.getRegionPlane(location).npcs) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.remove(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(dest.loc)
|
||||
if(scenery != null) {
|
||||
if(id == RABBIT) {
|
||||
val replacement = if(SEED_LOCS.contains(dest.loc)) { DEAD_PLANT_OBJ } else { HOLES[0] }
|
||||
SceneryBuilder.replace(scenery, scenery.transform(replacement))
|
||||
scheduleNPCs(dest.player, dest.loc, alive = false, rabbit = false)
|
||||
} else {
|
||||
if(dest.alive) {
|
||||
handleFarmerFlag(scenery, dest)
|
||||
} else {
|
||||
sendChat("Hmm. Looks like there's a plant here.")
|
||||
lock(3)
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Gracious me! This one's dead")
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
seedDestinations.remove(dest)
|
||||
}
|
||||
super.handleTickActions()
|
||||
}
|
||||
|
||||
override fun getMovementDestination(): Location? {
|
||||
if(seedDestinations.size > 0) {
|
||||
seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() }
|
||||
return seedDestinations.first().loc
|
||||
} else {
|
||||
return super.getMovementDestination()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleFarmerFlag(scenery: Scenery, dest: Vinesweeper.SeedDestination) {
|
||||
sendChat("Ah, another flag to clear. Let's see what's there.")
|
||||
lock(3)
|
||||
animate(Animation(451))
|
||||
if(SEED_LOCS.contains(dest.loc)) {
|
||||
val npc = this
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Ah! A seed. Points for everyone near me!")
|
||||
val level = dest.player.skills.getStaticLevel(Skills.FARMING)
|
||||
val points = RandomFunction.random(level, 4 * level)
|
||||
dest.player.incrementAttribute("/save:vinesweeper:points", points)
|
||||
dest.player.inventory.add(Item(Items.FLAG_12625, 1))
|
||||
sendUpdatedPoints(dest.player)
|
||||
for(neighbor in RegionManager.getLocalPlayers(npc)) {
|
||||
if(neighbor != dest.player) {
|
||||
neighbor.incrementAttribute("/save:vinesweeper:points", points / 2)
|
||||
sendUpdatedPoints(neighbor)
|
||||
}
|
||||
}
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
var i = 0
|
||||
val lines = arrayOf("Hmm, no seeds planted here, I'm afraid.", "I'll have to keep this 'ere flag. Sorry.")
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat(lines[i++])
|
||||
return i >= lines.size
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
fun farmerClear(dest: Vinesweeper.SeedDestination) {
|
||||
for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
val toClear = getScenery(dest.loc.transform(dx, dy, 0))
|
||||
if(toClear != null && intArrayOf(DEAD_PLANT_OBJ, *NUMBERS).contains(toClear.id)) {
|
||||
SceneryBuilder.replace(toClear, toClear.transform(HOLES[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
SEED_LOCS.remove(dest.loc)
|
||||
populateSeeds()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import core.game.node.item.Item
|
|||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class WGuildListeners : InteractionListener() {
|
||||
class WGuildListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onEquip(Items.DEFENSIVE_SHIELD_8856){player, node ->
|
||||
if (node is Item) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import org.rs09.consts.Items
|
|||
import rs09.game.content.ame.RandomEventManager
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class CerterEventInterface : InterfaceListener() {
|
||||
class CerterEventInterface : InterfaceListener {
|
||||
val CERTER_INTERFACE = 184
|
||||
val OPTION_A_CHILD = 1
|
||||
val OPTION_B_CHILD = 2
|
||||
|
|
@ -31,7 +31,7 @@ class CerterEventInterface : InterfaceListener() {
|
|||
Items.NULL_6198 to "A necklace."
|
||||
)
|
||||
val falseOptions = arrayOf("An axe.", "An arrow.", "A pair of boots.", "A pair of gloves.", "A staff.", "A bow.", "A feather.", "The disenfrachaised youth of 1940's Columbia.")
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(CERTER_INTERFACE) { player, _, _, buttonID, _, _ ->
|
||||
val answer = buttonID - 7
|
||||
val correctAnswer = player.getAttribute("certer:correctIndex", 0)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import core.game.system.task.Pulse
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class DrillDemonListeners : InteractionListener() {
|
||||
class DrillDemonListeners : InteractionListener {
|
||||
val MATS = intArrayOf(10076,10077,10078,10079)
|
||||
override fun defineListeners() {
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import org.rs09.consts.Items
|
|||
import rs09.game.content.ame.RandomEventManager
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class SandwichLadyInterface : InterfaceListener(){
|
||||
class SandwichLadyInterface : InterfaceListener{
|
||||
|
||||
val SANDWICH_INTERFACE = 297
|
||||
val baguette = Items.BAGUETTE_6961
|
||||
|
|
@ -16,7 +16,7 @@ class SandwichLadyInterface : InterfaceListener(){
|
|||
val kebab = Items.KEBAB_1971
|
||||
val chocobar = Items.CHOCOLATE_BAR_1973
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(SANDWICH_INTERFACE){player, _, _, buttonID, _, _ ->
|
||||
val item =
|
||||
when(buttonID) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import org.rs09.consts.Components
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class SEPatternInterface : InterfaceListener() {
|
||||
class SEPatternInterface : InterfaceListener {
|
||||
|
||||
val COMPONENT = Components.PATTERN_NEXT_103
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
|
||||
on(COMPONENT){player, component, opcode, buttonID, slot, itemID ->
|
||||
val index = buttonID - 10
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import org.rs09.consts.NPCs
|
|||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.inter.ExperienceInterface
|
||||
|
||||
class SupriseExamListeners : InteractionListener() {
|
||||
class SupriseExamListeners : InteractionListener {
|
||||
val MORDAUT = NPCs.MR_MORDAUT_6117
|
||||
val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640
|
||||
override fun defineListeners() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import core.game.content.dialogue.FacialExpression
|
|||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import rs09.game.content.global.shops.Shops
|
||||
import rs09.game.system.config.ShopParser
|
||||
|
||||
/**
|
||||
|
|
@ -116,8 +117,8 @@ class GabootyDialogue(player: Player? = null) : DialoguePlugin(player){
|
|||
"you know, a little more of this, a little less of that, it all",
|
||||
"adds up and makes for an interesting tipple!").also { stage = 23 }
|
||||
|
||||
90 -> end().also { ShopParser.Companion.openUid(player,226) }
|
||||
91 -> end().also { ShopParser.Companion.openUid(player,227) }
|
||||
90 -> end().also { Shops.openId(player,226) }
|
||||
91 -> end().also { Shops.openId(player,227) }
|
||||
99 -> end()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import rs09.game.system.config.ItemConfigParser
|
|||
* @author Ceikry
|
||||
* @author Woah
|
||||
*/
|
||||
class EquipHandler : InteractionListener() {
|
||||
class EquipHandler : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
|
|
|
|||
393
Server/src/main/kotlin/rs09/game/content/global/shops/Shop.kt
Normal file
393
Server/src/main/kotlin/rs09/game/content/global/shops/Shop.kt
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
package rs09.game.content.global.shops
|
||||
|
||||
import api.*
|
||||
import core.game.component.Component
|
||||
import core.game.container.Container
|
||||
import core.game.container.ContainerEvent
|
||||
import core.game.container.ContainerListener
|
||||
import core.game.container.ContainerType
|
||||
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 org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.content.global.shops.Shops.Companion.logShop
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
import java.lang.Integer.max
|
||||
import java.lang.Integer.min
|
||||
import kotlin.math.ceil
|
||||
|
||||
data class ShopItem(var itemId: Int, var amount: Int, val restockRate: Int = 100)
|
||||
|
||||
class ShopListener(val player: Player) : ContainerListener
|
||||
{
|
||||
var enabled = false
|
||||
override fun update(c: Container?, event: ContainerEvent?) {
|
||||
PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1, 92, event!!.items, false, *event.slots))
|
||||
}
|
||||
|
||||
override fun refresh(c: Container?) {
|
||||
PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1, 92, c!!.toArray(), c.capacity(), false))
|
||||
}
|
||||
}
|
||||
|
||||
class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean = false, val currency: Int = Items.COINS_995, val highAlch: Boolean = false)
|
||||
{
|
||||
val stockInstances = HashMap<Int, Container>()
|
||||
val playerStock = if (general) generalPlayerStock else Container(40, ContainerType.SHOP)
|
||||
private val needsUpdate = HashMap<Int, Boolean>()
|
||||
private val restockRates = HashMap<Int,Int>()
|
||||
|
||||
init {
|
||||
if(!getServerConfig().getBoolean(Shops.personalizedShops, false))
|
||||
stockInstances[ServerConstants.SERVER_NAME.hashCode()] = generateStockContainer()
|
||||
}
|
||||
|
||||
fun openFor(player: Player)
|
||||
{
|
||||
val cont = getContainer(player)
|
||||
setInterfaceText(player, title, 620, 22)
|
||||
setAttribute(player, "shop", this)
|
||||
setAttribute(player, "shop-cont", cont)
|
||||
openInterface(player, Components.SHOP_TEMPLATE_620)
|
||||
player.interfaceManager.openSingleTab(Component(Components.SHOP_TEMPLATE_SIDE_621))
|
||||
showTab(player, true)
|
||||
logShop("Opening shop [Title: $title, Player: ${player.username}]")
|
||||
}
|
||||
|
||||
fun showTab(player: Player, main: Boolean)
|
||||
{
|
||||
val cont = if (main) getAttribute<Container?>(player, "shop-cont", null) ?: return else playerStock
|
||||
|
||||
if(!main)
|
||||
{
|
||||
cont.listeners.remove(listenerInstances[player.username.hashCode()])
|
||||
playerStock.listeners.add(listenerInstances[player.username.hashCode()])
|
||||
}
|
||||
else
|
||||
{
|
||||
playerStock.listeners.remove(listenerInstances[player.username.hashCode()])
|
||||
cont.listeners.add(listenerInstances[player.username.hashCode()])
|
||||
}
|
||||
|
||||
val settings = IfaceSettingsBuilder()
|
||||
.enableOptions(0..9)
|
||||
.build()
|
||||
|
||||
player.packetDispatch.sendIfaceSettings(settings, if (main) 23 else 24, Components.SHOP_TEMPLATE_620, 0, cont.capacity())
|
||||
player.packetDispatch.sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 92, (620 shl 16) or if (main) 23 else 24)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 23, !main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 24, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 29, !main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 25, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 27, main)
|
||||
player.packetDispatch.sendInterfaceConfig(620, 26, false)
|
||||
|
||||
if (!main) playerStock.refresh()
|
||||
else cont.refresh()
|
||||
|
||||
setAttribute(player, "shop-main", main)
|
||||
}
|
||||
|
||||
private fun getContainer(player: Player) : Container
|
||||
{
|
||||
val container = if(getServerConfig().getBoolean(Shops.personalizedShops, false))
|
||||
stockInstances[player.username.hashCode()] ?: generateStockContainer().also { stockInstances[player.username.hashCode()] = it }
|
||||
else
|
||||
stockInstances[ServerConstants.SERVER_NAME.hashCode()]!!
|
||||
|
||||
val listener = listenerInstances[player.username.hashCode()]
|
||||
|
||||
if(listener != null && listener.player != player)
|
||||
{
|
||||
container.listeners.remove(listener)
|
||||
}
|
||||
|
||||
if(listener == null || listener.player != player)
|
||||
{
|
||||
listenerInstances[player.username.hashCode()] = ShopListener(player)
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
private fun generateStockContainer(): Container
|
||||
{
|
||||
val container = Container(40, ContainerType.SHOP)
|
||||
for(item in stock) {
|
||||
container.add(Item(item.itemId,item.amount))
|
||||
restockRates[item.itemId] = item.restockRate
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
fun restock()
|
||||
{
|
||||
stockInstances.filter { needsUpdate[it.key] == true }.forEach{ (player,cont) ->
|
||||
for(i in 0 until cont.capacity())
|
||||
{
|
||||
if(stock.size < i + 1) break
|
||||
if(GameWorld.ticks % stock[i].restockRate != 0) continue
|
||||
|
||||
if(cont[i].amount < stock[i].amount){
|
||||
cont[i].amount++
|
||||
cont.event.flag(i, cont[i])
|
||||
}
|
||||
else if(cont[i].amount > stock[i].amount){
|
||||
cont[i].amount--
|
||||
cont.event.flag(i, cont[i])
|
||||
}
|
||||
if(cont[i].amount != stock[i].amount) needsUpdate[player] = true
|
||||
}
|
||||
cont.update()
|
||||
}
|
||||
}
|
||||
|
||||
fun getBuyPrice(player: Player, slot: Int): Item
|
||||
{
|
||||
val isMainStock = getAttribute(player, "shop-main", true)
|
||||
val cont = if (isMainStock) getAttribute<Container?>(player, "shop-cont", null) ?: return Item(-1,-1) else playerStock
|
||||
val item = cont[slot]
|
||||
val price = when(currency)
|
||||
{
|
||||
Items.TOKKUL_6529 -> item.definition.getConfiguration("tokkul_price", 1)
|
||||
Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration("archery_ticket_price", 1)
|
||||
else -> getGPCost(Item(item.id, 1), if (isMainStock) stock[item.slot].amount else playerStock[slot].amount, if (isMainStock) item.amount else playerStock[slot].amount)
|
||||
}
|
||||
|
||||
return Item(currency, price)
|
||||
}
|
||||
|
||||
fun getSellPrice(player: Player, slot: Int): Pair<Container?,Item>
|
||||
{
|
||||
val shopCont = getAttribute<Container?>(player, "shop-cont", null) ?: return Pair(null, Item(-1,-1))
|
||||
val item = player.inventory[slot]
|
||||
var (isPlayerStock, shopSlot) = getStockSlot(item.id)
|
||||
|
||||
val stockAmt =
|
||||
if(isPlayerStock)
|
||||
0
|
||||
else{
|
||||
if(shopSlot != -1) stock[shopSlot].amount
|
||||
else 0
|
||||
}
|
||||
val currentAmt =
|
||||
if(isPlayerStock) playerStock.getAmount(item.id)
|
||||
else {
|
||||
if(shopSlot != -1) shopCont[shopSlot].amount
|
||||
else {
|
||||
isPlayerStock = true
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
val price = when(currency)
|
||||
{
|
||||
Items.TOKKUL_6529 -> item.definition.getConfiguration("tokkul_price", 1)
|
||||
Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration("archery_ticket_price", 1)
|
||||
else -> getGPSell(Item(item.id, 1), stockAmt, currentAmt)
|
||||
}
|
||||
|
||||
if(!general && stockAmt == 0 && shopSlot == -1)
|
||||
{
|
||||
return Pair(null, Item(-1,-1))
|
||||
}
|
||||
|
||||
return Pair(if (isPlayerStock) playerStock else shopCont, Item(currency, price))
|
||||
}
|
||||
|
||||
private fun getGPCost(item: Item, stockAmount: Int, currentAmt: Int): Int{
|
||||
var mod: Int
|
||||
mod = if(stockAmount == 0) 100
|
||||
else if(currentAmt == 0) 130
|
||||
else if(currentAmt >= stockAmount) 100
|
||||
else 130 - (130 - 100) * currentAmt / stockAmount
|
||||
if(mod < 1) mod = 1
|
||||
mod = max(100, min(130, mod))
|
||||
|
||||
|
||||
val price: Int = ceil(item.definition.value * mod.toDouble() / 100.0).toInt()
|
||||
/* if(player.getVarp(532) == 6529){
|
||||
price = 3 * price / 2
|
||||
}*/
|
||||
return max(price, 1)
|
||||
}
|
||||
|
||||
private fun getGPSell(item: Item, stockAmount: Int, currentAmt: Int): Int{
|
||||
if(!item.definition.isUnnoted)
|
||||
item.id = item.noteChange
|
||||
var mod: Int
|
||||
mod = if(stockAmount == 0) 70
|
||||
else if(currentAmt == 0) 100
|
||||
else if(currentAmt >= stockAmount) 70
|
||||
else 100 - (100 - 70) * currentAmt / stockAmount
|
||||
if(mod < 1) mod = 1
|
||||
mod = max(70, min(100, mod))
|
||||
|
||||
var base = if (highAlch) item.definition.getAlchemyValue(true) else item.definition.value
|
||||
base = max(base, item.definition.value)
|
||||
|
||||
val price: Int = ceil(base * mod.toDouble() / 100.0).toInt()
|
||||
return max(price, 1)
|
||||
}
|
||||
|
||||
fun buy(player: Player, slot: Int, amount: Int)
|
||||
{
|
||||
if(amount !in 1..Integer.MAX_VALUE) return
|
||||
val isMainStock = getAttribute(player, "shop-main", false)
|
||||
if(!isMainStock && player.ironmanManager.isIronman)
|
||||
{
|
||||
sendDialogue(player, "As an ironman, you cannot buy from player stock in shops.")
|
||||
return
|
||||
}
|
||||
val cont = if (isMainStock) getAttribute<Container?>(player, "shop-cont", null) ?: return else playerStock
|
||||
val inStock = cont[slot]
|
||||
val item = Item(inStock.id, amount)
|
||||
if(inStock.amount < amount)
|
||||
item.amount = inStock.amount
|
||||
|
||||
if(inStock.amount > stock[slot].amount && !getServerConfig().getBoolean(Shops.personalizedShops, false))
|
||||
{
|
||||
sendDialogue(player, "As an ironman, you cannot buy overstocked items from shops.")
|
||||
return
|
||||
}
|
||||
|
||||
val cost = getBuyPrice(player, slot)
|
||||
if(cost.id == -1) sendMessage(player, "This shop cannot sell that item.").also { return }
|
||||
|
||||
if(currency == Items.COINS_995){
|
||||
var amt = item.amount
|
||||
var inStockAmt = inStock.amount
|
||||
while(amt-- > 1)
|
||||
cost.amount += getGPCost(Item(item.id, 1), if (isMainStock) stock[slot].amount else playerStock[slot].amount, --inStockAmt)
|
||||
} else {
|
||||
cost.amount = cost.amount * item.amount
|
||||
}
|
||||
|
||||
if(inInventory(player, cost.id, cost.amount))
|
||||
{
|
||||
if(removeItem(player, cost))
|
||||
{
|
||||
if(!hasSpaceFor(player, item)) {
|
||||
addItem(player, cost.id, cost.amount)
|
||||
sendMessage(player, "You don't have enough inventory space to buy that many.")
|
||||
return
|
||||
}
|
||||
|
||||
if(!isMainStock && cont[slot].amount - item.amount == 0)
|
||||
{
|
||||
cont.remove(cont[slot], false)
|
||||
cont.refresh()
|
||||
}
|
||||
else {
|
||||
cont[slot].amount -= item.amount
|
||||
cont.event.flag(slot, cont[slot])
|
||||
cont.update()
|
||||
}
|
||||
|
||||
addItem(player, item.id, item.amount)
|
||||
|
||||
if(getServerConfig().getBoolean(Shops.personalizedShops, false)){
|
||||
needsUpdate[player.username.hashCode()] = true
|
||||
} else {
|
||||
needsUpdate[ServerConstants.SERVER_NAME.hashCode()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sendMessage(player, "You don't have enough ${cost.name.toLowerCase()} to buy that many.")
|
||||
}
|
||||
}
|
||||
|
||||
fun sell(player: Player, slot: Int, amount: Int)
|
||||
{
|
||||
if(amount !in 1..Integer.MAX_VALUE) return
|
||||
val playerInventory = player.inventory[slot]
|
||||
if(playerInventory.id in intArrayOf(Items.COINS_995, Items.TOKKUL_6529, Items.ARCHERY_TICKET_1464))
|
||||
{
|
||||
sendMessage(player, "You can't sell currency to a shop.")
|
||||
return
|
||||
}
|
||||
val item = Item(playerInventory.id, amount)
|
||||
val (container,profit) = getSellPrice(player, slot)
|
||||
if(profit.amount == -1) sendMessage(player, "This item can't be sold to this shop.").also { return }
|
||||
if(amount > player.inventory.getAmount(item.id))
|
||||
item.amount = player.inventory.getAmount(item.id)
|
||||
|
||||
if(currency == Items.COINS_995 && item.amount > 1){
|
||||
val id = if(!item.definition.isUnnoted) item.noteChange else item.id
|
||||
val (isPlayerStock, shopSlot) = getStockSlot(id)
|
||||
var amt = item.amount
|
||||
var inStockAmt = container!![shopSlot]?.amount ?: playerStock.getAmount(id)
|
||||
while(amt-- > 1)
|
||||
profit.amount += getGPSell(Item(item.id, 1), if (isPlayerStock) 0 else stock[shopSlot].amount, ++inStockAmt)
|
||||
} else {
|
||||
profit.amount = profit.amount * item.amount
|
||||
}
|
||||
|
||||
if(removeItem(player, item))
|
||||
{
|
||||
if(!hasSpaceFor(player, profit)){
|
||||
sendMessage(player, "You don't have enough space to do that.")
|
||||
addItem(player, item.id, item.amount)
|
||||
return
|
||||
}
|
||||
if(container == playerStock && getAttribute(player, "shop-main", false)){
|
||||
showTab(player, false)
|
||||
}
|
||||
else if(!getAttribute(player, "shop-main", false) && container != playerStock)
|
||||
{
|
||||
showTab(player, true)
|
||||
}
|
||||
addItem(player, profit.id, profit.amount)
|
||||
if(!item.definition.isUnnoted)
|
||||
{
|
||||
item.id = item.noteChange
|
||||
}
|
||||
container?.add(item)
|
||||
container?.refresh()
|
||||
if(getServerConfig().getBoolean(Shops.personalizedShops, false)){
|
||||
needsUpdate[player.username.hashCode()] = true
|
||||
} else {
|
||||
needsUpdate[ServerConstants.SERVER_NAME.hashCode()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getStockSlot(itemId: Int): Pair<Boolean, Int>
|
||||
{
|
||||
var shopSlot: Int = -1
|
||||
var isPlayerStock = false
|
||||
val notechange = itemDefinition(itemId).noteId
|
||||
for((stockSlot, shopItem) in stock.withIndex())
|
||||
{
|
||||
if(shopItem.itemId == itemId || shopItem.itemId == notechange)
|
||||
shopSlot = stockSlot
|
||||
}
|
||||
if(shopSlot == -1)
|
||||
{
|
||||
for((stockSlot, playerStockItem) in playerStock.toArray().withIndex())
|
||||
{
|
||||
if(playerStockItem == null) continue
|
||||
if(playerStockItem.id == itemId || playerStockItem.id == notechange) {
|
||||
shopSlot = stockSlot
|
||||
isPlayerStock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(isPlayerStock, shopSlot)
|
||||
}
|
||||
|
||||
companion object {
|
||||
//General stores globally share player stock (weird quirk, right?)
|
||||
val generalPlayerStock = Container(40, ContainerType.SHOP)
|
||||
val listenerInstances = HashMap<Int, ShopListener>()
|
||||
}
|
||||
}
|
||||
256
Server/src/main/kotlin/rs09/game/content/global/shops/Shops.kt
Normal file
256
Server/src/main/kotlin/rs09/game/content/global/shops/Shops.kt
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
package rs09.game.content.global.shops
|
||||
|
||||
import api.*
|
||||
import core.game.content.dialogue.FacialExpression
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.crafting.TanningProduct
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.json.simple.parser.JSONParser
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.tools.END_DIALOGUE
|
||||
import java.io.FileReader
|
||||
|
||||
/**
|
||||
* The "controller" class for shops. Handles opening shops from various NPC interactions and updating stock, etc.
|
||||
* Note: If you wish to enable personalized shops, add and set the personalized_shops entry to true in the world section of the server config.
|
||||
* ex:
|
||||
* ```toml
|
||||
* [world]
|
||||
* personalized_shops = true
|
||||
* ```
|
||||
*/
|
||||
class Shops : StartupListener, TickListener, InteractionListener, InterfaceListener, Commands {
|
||||
companion object {
|
||||
@JvmStatic val personalizedShops = "world.personalized_shops"
|
||||
@JvmStatic val shopsById = HashMap<Int,Shop>()
|
||||
@JvmStatic val shopsByNpc = HashMap<Int,Shop>()
|
||||
|
||||
@JvmStatic fun openId(player: Player, id: Int)
|
||||
{
|
||||
shopsById[id]?.openFor(player)
|
||||
}
|
||||
|
||||
fun logShop(msg: String)
|
||||
{
|
||||
SystemLogger.logInfo("[SHOPS] $msg")
|
||||
}
|
||||
}
|
||||
|
||||
override fun startup() {
|
||||
val path = ServerConstants.CONFIG_PATH + "shops.json"
|
||||
var shopCount = 0
|
||||
logShop("Using JSON path: $path")
|
||||
|
||||
val reader = FileReader(path)
|
||||
val data = JSONParser().parse(reader) as JSONArray
|
||||
|
||||
fun parseStock(stock: String, id: Int): ArrayList<ShopItem>{
|
||||
val items = ArrayList<ShopItem>()
|
||||
val idsInStock = HashMap<Int, Boolean>()
|
||||
if(stock.isEmpty()){
|
||||
return items
|
||||
}
|
||||
stock.split('-').map {
|
||||
val tokens = it.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray()
|
||||
var amount = tokens[1].trim()
|
||||
if(amount == "inf")
|
||||
amount = "-1"
|
||||
val item = tokens[0].toInt()
|
||||
if(idsInStock[item] != null)
|
||||
SystemLogger.logWarn("[SHOPS] MALFORMED STOCK IN SHOP ID $id FOR ITEM $item")
|
||||
else
|
||||
items.add(ShopItem(item, amount.toInt(), tokens.getOrNull(2)?.toIntOrNull() ?: 100))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
for(rawShop in data)
|
||||
{
|
||||
var shop: Shop?
|
||||
val shopData = rawShop as JSONObject
|
||||
val id = shopData["id"].toString().toInt()
|
||||
val title = shopData["title"].toString()
|
||||
val general = shopData["general_store"].toString().toBoolean()
|
||||
val stock = parseStock(shopData["stock"].toString(), id).toTypedArray()
|
||||
val npcs = if(shopData["npcs"].toString().isNotBlank()) shopData["npcs"].toString().split(",").map { it.toInt() }.toIntArray() else intArrayOf()
|
||||
val currency = shopData["currency"].toString().toInt()
|
||||
val highAlch = shopData["high_alch"].toString() == "1"
|
||||
shop = Shop(title, stock, general, currency, highAlch)
|
||||
|
||||
npcs.map { shopsByNpc[it] = shop }
|
||||
shopsById[id] = shop
|
||||
++shopCount
|
||||
}
|
||||
|
||||
logShop("Parsed $shopCount shops.")
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
shopsById.values.forEach(Shop::restock)
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(NPC, "trade", "shop"){player, node ->
|
||||
val npc = node as NPC
|
||||
if (npc.id == 2824) {
|
||||
TanningProduct.open(player, 2824)
|
||||
return@on true
|
||||
}
|
||||
if (npc.id == 7601) {
|
||||
openInterface(player, 732)
|
||||
return@on true
|
||||
}
|
||||
shopsByNpc[npc.id]?.openFor(player) ?: return@on false
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(NPCs.SIEGFRIED_ERKLE_933, NPC, "trade"){ player, node ->
|
||||
val points = getQP(player)
|
||||
if(points < 40){
|
||||
sendNPCDialogue(player, NPCs.SIEGFRIED_ERKLE_933, "I'm sorry, adventurer, but you need 40 quest points to buy from me.")
|
||||
return@on true
|
||||
}
|
||||
shopsByNpc[node.id]?.openFor(player)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(NPCs.FUR_TRADER_1316, NPC,"trade") { player, node ->
|
||||
if (!isQuestComplete(player, "Fremennik Trials")) {
|
||||
sendNPCDialogue(player, NPCs.FUR_TRADER_1316, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE }
|
||||
} else {
|
||||
shopsByNpc[node.id]?.openFor(player)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(NPCs.FISH_MONGER_1315, NPC,"trade") { player, node ->
|
||||
if (!isQuestComplete(player, "Fremennik Trials")) {
|
||||
sendNPCDialogue(player, NPCs.FISH_MONGER_1315, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE }
|
||||
} else {
|
||||
shopsByNpc[node.id]?.openFor(player)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineInterfaceListeners() {
|
||||
on(Components.SHOP_TEMPLATE_620){player, _, opcode, buttonID, slot, _ ->
|
||||
val OP_VALUE = 155
|
||||
val OP_BUY_1 = 196
|
||||
val OP_BUY_5 = 124
|
||||
val OP_BUY_10 = 199
|
||||
val OP_BUY_X = 234
|
||||
val OP_EXAMINE = 9
|
||||
|
||||
val shop = getAttribute<Shop?>(player, "shop", null) ?: return@on false
|
||||
val isMainStock = getAttribute(player, "shop-main", true)
|
||||
|
||||
when(buttonID)
|
||||
{
|
||||
26 -> shop.showTab(player, false).also { return@on true }
|
||||
25 -> shop.showTab(player, true).also { return@on true }
|
||||
27,29 -> return@on true
|
||||
}
|
||||
|
||||
val price = shop.getBuyPrice(player, slot)
|
||||
|
||||
when(opcode)
|
||||
{
|
||||
OP_VALUE -> sendMessage(player, "${getItemName(if (isMainStock) shop.stock[slot].itemId else shop.playerStock[slot].id)}: This item currently costs ${price.amount} ${price.name.toLowerCase()}.")
|
||||
OP_BUY_1 -> shop.buy(player, slot, 1)
|
||||
OP_BUY_5 -> shop.buy(player, slot, 5)
|
||||
OP_BUY_10 -> shop.buy(player, slot, 10)
|
||||
OP_BUY_X -> sendInputDialogue(player, true, "Enter the amount to buy:"){value ->
|
||||
val amt = value as Int
|
||||
shop.buy(player, slot, amt)
|
||||
}
|
||||
OP_EXAMINE -> sendMessage(player, itemDefinition(if (isMainStock) shop.stock[slot].itemId else shop.playerStock[slot].id).examine)
|
||||
}
|
||||
|
||||
return@on true
|
||||
}
|
||||
|
||||
onOpen(Components.SHOP_TEMPLATE_SIDE_621) {player, _ ->
|
||||
val settings = IfaceSettingsBuilder()
|
||||
.enableOptions(0 until 9)
|
||||
.build()
|
||||
player.packetDispatch.sendIfaceSettings(settings, 0, 621, 0, 28)
|
||||
player.packetDispatch.sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Sell X", "Sell 10", "Sell 5", "Sell 1", "Value", -1, 0, 7, 4, 93, 621 shl 16);
|
||||
return@onOpen true
|
||||
}
|
||||
|
||||
onClose(Components.SHOP_TEMPLATE_620) { player, _ ->
|
||||
val shop = getAttribute<Shop?>(player, "shop", null) ?: return@onClose true
|
||||
val listener = Shop.listenerInstances[player.username.hashCode()] ?: return@onClose true
|
||||
|
||||
if(getServerConfig().getBoolean(personalizedShops, false))
|
||||
shop.stockInstances[player.username.hashCode()]?.listeners?.remove(listener)
|
||||
else
|
||||
shop.stockInstances[ServerConstants.SERVER_NAME.hashCode()]!!.listeners.remove(listener)
|
||||
|
||||
shop.playerStock.listeners.remove(listener)
|
||||
player.interfaceManager.closeSingleTab()
|
||||
return@onClose true
|
||||
}
|
||||
|
||||
on(Components.SHOP_TEMPLATE_SIDE_621){player, component, opcode, buttonID, slot, itemID ->
|
||||
val OP_VALUE = 155
|
||||
val OP_SELL_1 = 196
|
||||
val OP_SELL_5 = 124
|
||||
val OP_SELL_10 = 199
|
||||
val OP_SELL_X = 234
|
||||
|
||||
val shop = getAttribute<Shop?>(player, "shop", null) ?: return@on false
|
||||
|
||||
val (_,price) = shop.getSellPrice(player, slot)
|
||||
|
||||
val valueMsg = if(price.amount == -1) "This shop will not buy that item." else "${player.inventory[slot].name}: This shop will buy this item for ${price.amount} ${price.name.toLowerCase()}."
|
||||
|
||||
when(opcode)
|
||||
{
|
||||
OP_VALUE -> sendMessage(player, valueMsg)
|
||||
OP_SELL_1 -> shop.sell(player, slot, 1)
|
||||
OP_SELL_5 -> shop.sell(player, slot, 5)
|
||||
OP_SELL_10 -> shop.sell(player, slot, 10)
|
||||
OP_SELL_X -> sendInputDialogue(player, true, "Enter the amount to sell:"){value ->
|
||||
val amt = value as Int
|
||||
shop.sell(player, slot, amt)
|
||||
}
|
||||
}
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineDestinationOverrides() {
|
||||
setDest(NPC,"trade","shop"){_,node ->
|
||||
val npc = node as NPC
|
||||
if (npc.getAttribute("facing_booth", false)) {
|
||||
val offsetX = npc.direction.stepX shl 1
|
||||
val offsetY = npc.direction.stepY shl 1
|
||||
return@setDest npc.location.transform(offsetX, offsetY, 0)
|
||||
}
|
||||
return@setDest node.location
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineCommands() {
|
||||
define("openshop") { player, args ->
|
||||
if(args.size < 2) reject(player, "Usage: ::openshop shopId")
|
||||
val shopId = args[1].toInt()
|
||||
shopsById[shopId]?.openFor(player)
|
||||
}
|
||||
|
||||
define("shopscript") { player, args ->
|
||||
val arg1 = args[1].toInt()
|
||||
player.packetDispatch.sendRunScript(25, "vg", arg1, 92) //Run CS2 script 25, with args 868? and 92(our container id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ class GiftRollPlugin : XPGainPlugin() {
|
|||
}
|
||||
}
|
||||
|
||||
class XMASMboxHandler : InteractionListener() {
|
||||
class XMASMboxHandler : InteractionListener {
|
||||
val MBOX = Items.MYSTERY_BOX_6199
|
||||
|
||||
override fun defineListeners() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import org.rs09.consts.Items
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class EasterEventListeners : InteractionListener() {
|
||||
class EasterEventListeners : InteractionListener {
|
||||
|
||||
val EGG_ATTRIBUTE = "/save:easter:eggs"
|
||||
val eggs = intArrayOf(Items.EASTER_EGG_11027, Items.EASTER_EGG_11028, Items.EASTER_EGG_11029, Items.EASTER_EGG_11030)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import rs09.game.interaction.InteractionListener
|
|||
import rs09.game.world.GameWorld
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
class TrickOrTreatHandler : InteractionListener() {
|
||||
class TrickOrTreatHandler : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPC, "trick-or-treat"){player, node ->
|
||||
val hasDone5 = getDailyTrickOrTreats(player) == 5
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import core.game.content.quest.PluginInteraction
|
|||
import core.game.content.quest.PluginInteractionManager
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class PenguinSpyingHandler : InteractionListener(){
|
||||
class PenguinSpyingHandler : InteractionListener{
|
||||
|
||||
override fun defineListeners() {
|
||||
on(PENGUINS, NPC, "spy-on"){player, node ->
|
||||
|
|
|
|||
|
|
@ -71,15 +71,15 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
|
|||
starSprite.location = starObject.location
|
||||
starSprite.init()
|
||||
spriteSpawned = true
|
||||
ShootingStarEvent.getStoreFile().clear()
|
||||
ShootingStarPlugin.getStoreFile().clear()
|
||||
return
|
||||
}
|
||||
level = getNextType()
|
||||
maxDust = level.totalStardust
|
||||
dustLeft = level.totalStardust
|
||||
|
||||
ShootingStarEvent.getStoreFile()["level"] = level.ordinal
|
||||
ShootingStarEvent.getStoreFile()["isDiscovered"] = isDiscovered
|
||||
ShootingStarPlugin.getStoreFile()["level"] = level.ordinal
|
||||
ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered
|
||||
|
||||
val newStar = Scenery(level.objectId, starObject.location)
|
||||
SceneryBuilder.replace(starObject, newStar)
|
||||
|
|
@ -114,10 +114,10 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
|
|||
* Rebuilds some of the variables with new information.
|
||||
*/
|
||||
fun rebuildVars(){
|
||||
if(firstStar && ShootingStarEvent.getStoreFile().isNotEmpty()){
|
||||
level = ShootingStarType.values()[ShootingStarEvent.getStoreFile().getInt("level")]
|
||||
location = ShootingStarEvent.getStoreFile().getString("location")
|
||||
isDiscovered = ShootingStarEvent.getStoreFile().getBoolean("isDiscovered")
|
||||
if(firstStar && ShootingStarPlugin.getStoreFile().isNotEmpty()){
|
||||
level = ShootingStarType.values()[ShootingStarPlugin.getStoreFile().getInt("level")]
|
||||
location = ShootingStarPlugin.getStoreFile().getString("location")
|
||||
isDiscovered = ShootingStarPlugin.getStoreFile().getBoolean("isDiscovered")
|
||||
} else {
|
||||
level = ShootingStarType.values().random()
|
||||
location = crash_locations.entries.random().key
|
||||
|
|
@ -128,9 +128,9 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
|
|||
dustLeft = level.totalStardust
|
||||
starObject = Scenery(level.objectId, crash_locations.get(location))
|
||||
|
||||
ShootingStarEvent.getStoreFile()["level"] = level.ordinal
|
||||
ShootingStarEvent.getStoreFile()["location"] = location
|
||||
ShootingStarEvent.getStoreFile()["isDiscovered"] = false
|
||||
ShootingStarPlugin.getStoreFile()["level"] = level.ordinal
|
||||
ShootingStarPlugin.getStoreFile()["location"] = location
|
||||
ShootingStarPlugin.getStoreFile()["isDiscovered"] = false
|
||||
|
||||
ticks = 0
|
||||
firstStar = false
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.system.command.CommandSet
|
||||
import core.plugin.Plugin
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.system.command.CommandPlugin
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* A few assorted commands for shooting stars.
|
||||
*/
|
||||
class ShootingStarCommands : CommandPlugin(){
|
||||
override fun newInstance(arg: Any?): Plugin<Any?>{
|
||||
link(CommandSet.DEVELOPER)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun parse(player: Player?, name: String?, args: Array<String?>?): Boolean {
|
||||
val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star
|
||||
when (name) {
|
||||
"tostar" -> {
|
||||
player!!.teleport(star.starObject.location.transform(1, 1, 0))
|
||||
}
|
||||
"submit" -> {
|
||||
star.fire()
|
||||
}
|
||||
"resettime" -> player!!.savedData.globalData.starSpriteDelay = 0L
|
||||
"stardust" -> {
|
||||
val dust = 8
|
||||
println("Cosmic Runes: " + 0.76 * dust)
|
||||
println("Astral runes: " + 0.26 * dust)
|
||||
println("Gold ores: " + 0.1 * dust)
|
||||
println("GP: " + 250.1 * dust)
|
||||
}
|
||||
"setsprite" -> player!!.savedData.globalData.starSpriteDelay = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import core.game.content.global.worldevents.shootingstar.ScoreboardHandler
|
||||
import core.game.content.global.worldevents.shootingstar.ShootingStarScoreboard
|
||||
import core.game.content.global.worldevents.shootingstar.StarChartPlugin
|
||||
import core.game.system.task.Pulse
|
||||
import org.json.simple.JSONObject
|
||||
import rs09.ServerStore
|
||||
import rs09.game.content.global.worldevents.PluginSet
|
||||
import rs09.game.content.global.worldevents.WorldEvent
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.world.GameWorld
|
||||
|
||||
|
||||
/**
|
||||
* The world event class for shooting stars. Keeps track of event-related data like the ShootingStar instance.
|
||||
* Also handles spawning new shooting stars based on time.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class ShootingStarEvent : WorldEvent("shooting-stars") {
|
||||
val star = ShootingStar()
|
||||
val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000
|
||||
|
||||
|
||||
override fun initialize() {
|
||||
plugins = PluginSet(
|
||||
ScoreboardHandler(),
|
||||
ShootingStarScoreboard(),
|
||||
StarChartPlugin(),
|
||||
ShootingStarCommands(),
|
||||
StarSpriteDialogue(),
|
||||
ShootingStarLogin()
|
||||
)
|
||||
super.initialize()
|
||||
GameWorld.Pulser.submit(StarPulse())
|
||||
log("Shooting Star event has been initialized.")
|
||||
}
|
||||
|
||||
override fun checkTrigger(): Boolean {
|
||||
/**
|
||||
* Spawn a new star only if: star's ticks are greater than the tickDelay and star sprite is not spawned OR,
|
||||
* neither the star or the sprite are spawned.
|
||||
*/
|
||||
star.ticks += 10
|
||||
if ((star.ticks >= tickDelay && !star.spriteSpawned) || (!star.isSpawned && !star.spriteSpawned)) {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the sprite when we have passed the tickDelay + (1/3) of the tickDelay.
|
||||
* This gives players a little extra time before the star respawns to run to the bank and grab
|
||||
* stardust or whatever. Once the sprite is cleared a new star will be allowed to spawn.
|
||||
*/
|
||||
val maxDelay = tickDelay + (tickDelay / 3)
|
||||
if(star.ticks > maxDelay && star.spriteSpawned){
|
||||
star.clearSprite()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun checkActive(): Boolean {
|
||||
return true //this event is always active.
|
||||
}
|
||||
|
||||
override fun fireEvent() {
|
||||
log("Fired new shooting star event.")
|
||||
star.fire()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles checking star status and spawning new ones if necessary.
|
||||
*/
|
||||
class StarPulse : Pulse(10){
|
||||
override fun pulse(): Boolean {
|
||||
val event = WorldEvents.get("shooting-stars")
|
||||
event ?: return true
|
||||
|
||||
if(event.checkTrigger()){
|
||||
event.fireEvent()
|
||||
}
|
||||
|
||||
return false //always returns false because it needs to run forever.
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getStoreFile() : JSONObject {
|
||||
return ServerStore.getArchive("shooting-star")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Plugin
|
||||
import core.plugin.PluginManifest
|
||||
import core.plugin.PluginType
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
|
||||
/**
|
||||
* A plugin that handles the message a player receives on login pertaining to shooting stars.
|
||||
*/
|
||||
@PluginManifest(type = PluginType.LOGIN)
|
||||
class ShootingStarLogin : Plugin<Player?> {
|
||||
@Throws(Throwable::class)
|
||||
override fun newInstance(arg: Player?): Plugin<Player?>? {
|
||||
if (arg is Player) {
|
||||
val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star
|
||||
if (star.isSpawned) {
|
||||
arg.sendMessage("<img=12><col=CC6600>News: A shooting star (Level " + (star.level.ordinal + 1).toString() + ") has just crashed near the " + star.location + "!")
|
||||
}
|
||||
return this
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun fireEvent(identifier: String, vararg args: Any): Any {
|
||||
return Unit
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import api.*
|
||||
import core.game.content.global.worldevents.shootingstar.ScoreboardManager
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.SkillPulse
|
||||
|
|
@ -50,7 +49,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
|
|||
player.incrementAttribute("/save:shooting-star:bonus-xp", bonusXp)
|
||||
Repository.sendNews(player.username + " is the discoverer of the crashed star near " + star.location + "!")
|
||||
player.sendMessage("You have ${player.skills.experienceMutiplier * player.getAttribute("shooting-star:bonus-xp", 0).toDouble()} bonus xp towards mining stardust.")
|
||||
ScoreboardManager.submit(player)
|
||||
ShootingStarPlugin.submitScoreBoard(player)
|
||||
star.isDiscovered = true
|
||||
return player.skills.getLevel(Skills.MINING) >= star.miningLevel
|
||||
}
|
||||
|
|
@ -63,7 +62,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
|
|||
player.packetDispatch.sendMessage("You do not have a pickaxe to use.")
|
||||
return false
|
||||
}
|
||||
if (player.inventory.freeSlots() < 1 && !player.inventory.contains(ShootingStarOptionHandler.STAR_DUST, 1)) {
|
||||
if (player.inventory.freeSlots() < 1 && !player.inventory.contains(ShootingStarPlugin.STAR_DUST, 1)) {
|
||||
player.dialogueInterpreter.sendDialogue("Your inventory is too full to hold any more stardust.")
|
||||
return false
|
||||
}
|
||||
|
|
@ -98,8 +97,8 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
|
|||
}
|
||||
|
||||
player.skills.addExperience(Skills.MINING, xp)
|
||||
if (ShootingStarOptionHandler.getStarDust(player) < 200) {
|
||||
player.inventory.add(Item(ShootingStarOptionHandler.STAR_DUST, 1))
|
||||
if (ShootingStarPlugin.getStarDust(player) < 200) {
|
||||
player.inventory.add(Item(ShootingStarPlugin.STAR_DUST, 1))
|
||||
}
|
||||
if(!inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !inBank(player, Items.ANCIENT_BLUEPRINT_14651)){
|
||||
rollBlueprint(player)
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
/**
|
||||
* Option handlers for the various shooting star objects.
|
||||
*/
|
||||
class ShootingStarOptionHandler : InteractionListener() {
|
||||
|
||||
val SHOOTING_STARS = ShootingStarType.values().map(ShootingStarType::objectId).toIntArray()
|
||||
|
||||
override fun defineListeners() {
|
||||
on(SHOOTING_STARS,SCENERY,"mine"){ player, _ ->
|
||||
val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star
|
||||
star.mine(player)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(SHOOTING_STARS,SCENERY,"prospect"){ player, _ ->
|
||||
val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star
|
||||
star.prospect(player)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STAR_DUST = 13727
|
||||
/**
|
||||
* Gets the star dust amount for a player.
|
||||
* @param player The player.
|
||||
* @return The stardust amount.
|
||||
*/
|
||||
fun getStarDust(player: Player): Int {
|
||||
return player.inventory.getAmount(STAR_DUST) + player.bank.getAmount(STAR_DUST)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
package rs09.game.content.global.worldevents.shootingstar
|
||||
|
||||
import api.*
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.TeleportManager
|
||||
import core.game.node.entity.skill.Skills
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Scenery
|
||||
import rs09.ServerStore
|
||||
import rs09.ServerStore.Companion.getBoolean
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.tools.secondsToTicks
|
||||
|
||||
class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Commands, StartupListener {
|
||||
override fun login(player: Player) {
|
||||
if(star.isSpawned && !star.spriteSpawned)
|
||||
sendMessage(player, "<img=12><col=CC6600>News: A shooting star (Level ${star.level.ordinal + 1}) has just crashed near the ${star.location}!")
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
++star.ticks
|
||||
|
||||
val maxDelay = tickDelay + (tickDelay / 3)
|
||||
if(star.ticks > maxDelay && star.spriteSpawned){
|
||||
star.clearSprite()
|
||||
}
|
||||
|
||||
if ((star.ticks >= tickDelay && !star.spriteSpawned) || (!star.isSpawned && !star.spriteSpawned)) {
|
||||
star.fire()
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Scenery.SHOOTING_STAR_NOTICEBOARD_38669, SCENERY, "read") { player, _ ->
|
||||
var index = 0
|
||||
scoreboardEntries.forEach { entry ->
|
||||
val timeElapsed = secondsToTicks(GameWorld.ticks - entry.time) / 60
|
||||
setInterfaceText(player, "$timeElapsed minutes ago", scoreboardIface, index + 6)
|
||||
setInterfaceText(player, entry.player, scoreboardIface, index + 11)
|
||||
++index
|
||||
}
|
||||
openInterface(player, scoreboardIface)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(SHOOTING_STARS,SCENERY,"mine"){ player, _ ->
|
||||
star.mine(player)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(SHOOTING_STARS,SCENERY,"prospect"){ player, _ ->
|
||||
star.prospect(player)
|
||||
return@on true
|
||||
}
|
||||
|
||||
on(RING, ITEM, "rub", "operate"){player, node ->
|
||||
if(getRingStoreFile().getBoolean(player.username.toLowerCase())){
|
||||
sendDialogue(player, "The ring is still recharging.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
val condition: (Player) -> Boolean = when(star.location.toLowerCase()){
|
||||
"canifis bank" -> { p -> p.questRepository.isComplete("Priest in Peril")}
|
||||
"crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40) }
|
||||
"south crandor mining site" -> {p -> p.questRepository.isComplete("Dragon Slayer")}
|
||||
else -> {_ -> true}
|
||||
}
|
||||
|
||||
if(!condition.invoke(player) || player.skullManager.isWilderness){
|
||||
sendDialogue(player, "Magical forces prevent your teleportation.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
val shouldWarn = when(star.location){
|
||||
"North Edgeville mining site",
|
||||
"Southern wilderness mine",
|
||||
"Pirates' Hideout mine",
|
||||
"Lava Maze mining site",
|
||||
"Mage Arena bank" -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
openDialogue(player, RingDialogue(shouldWarn, star))
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineCommands() {
|
||||
define("tostar", Privilege.ADMIN) { player, _ ->
|
||||
teleport(player, star.starObject.location.transform(1,1,0))
|
||||
}
|
||||
|
||||
define("submit", Privilege.ADMIN) { _, _ ->
|
||||
star.fire()
|
||||
}
|
||||
|
||||
define("resetsprite", Privilege.ADMIN) { player, _ ->
|
||||
player.savedData.globalData.starSpriteDelay = 0L
|
||||
}
|
||||
}
|
||||
|
||||
override fun startup() {
|
||||
SystemLogger.logInfo("Shooting Stars initialized.")
|
||||
}
|
||||
|
||||
private data class ScoreboardEntry(val player: String, val time: Int)
|
||||
|
||||
private class RingDialogue(val shouldWarn: Boolean, val star: ShootingStar) : DialogueFile(){
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
if(shouldWarn){
|
||||
when(stage) {
|
||||
0 -> dialogue("WARNING: That mining site is located in the wilderness.").also { stage++ }
|
||||
1 -> player!!.dialogueInterpreter.sendOptions("Continue?","Yes","No").also { stage++ }
|
||||
2 -> when(buttonID){
|
||||
1 -> teleport(player!!, star).also { end() }
|
||||
2 -> end()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when(stage){
|
||||
0 -> player!!.dialogueInterpreter.sendOptions("Teleport to the Star?", "Yes", "No").also { stage++ }
|
||||
1 -> when(buttonID){
|
||||
1 -> teleport(player!!, star).also { end() }
|
||||
2 -> end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun teleport(player: Player, star: ShootingStar){
|
||||
teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME)
|
||||
getRingStoreFile()[player.username.toLowerCase()] = true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val star = ShootingStar()
|
||||
private val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000
|
||||
private val scoreboardEntries = ArrayList<ScoreboardEntry>()
|
||||
private val scoreboardIface = 787
|
||||
val SHOOTING_STARS = ShootingStarType.values().map(ShootingStarType::objectId).toIntArray()
|
||||
val STAR_DUST = Items.STARDUST_13727
|
||||
val RING = Items.RING_OF_THE_STAR_SPRITE_14652
|
||||
|
||||
|
||||
@JvmStatic fun submitScoreBoard(player: Player)
|
||||
{
|
||||
if(scoreboardEntries.size == 5)
|
||||
scoreboardEntries.removeAt(0)
|
||||
scoreboardEntries.add(ScoreboardEntry(player.username, GameWorld.ticks))
|
||||
}
|
||||
|
||||
@JvmStatic fun getStar(): ShootingStar
|
||||
{
|
||||
return star
|
||||
}
|
||||
|
||||
@JvmStatic fun getStoreFile() : JSONObject {
|
||||
return ServerStore.getArchive("shooting-star")
|
||||
}
|
||||
|
||||
@JvmStatic fun getRingStoreFile() : JSONObject {
|
||||
return ServerStore.getArchive("daily-star-ring")
|
||||
}
|
||||
|
||||
fun getStarDust(player: Player): Int {
|
||||
return player.inventory.getAmount(STAR_DUST) + player.bank.getAmount(STAR_DUST)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import core.game.content.dialogue.FacialExpression
|
|||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Initializable
|
||||
import core.tools.RandomFunction
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
|
|
@ -21,6 +22,7 @@ import java.util.concurrent.TimeUnit
|
|||
/**
|
||||
* Dialogue for the star sprite.
|
||||
*/
|
||||
@Initializable
|
||||
class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
/**
|
||||
|
|
@ -70,7 +72,7 @@ class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
|||
} else if (inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && getAttribute(player, "star-ring:bp-shown", false)) {
|
||||
playerl(FacialExpression.HALF_ASKING, "So about those rings...")
|
||||
stage = 2000
|
||||
} else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarOptionHandler.STAR_DUST, 1)) {
|
||||
} else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarPlugin.STAR_DUST, 1)) {
|
||||
npc("Hello, strange creature.")
|
||||
stage = 0
|
||||
} else {
|
||||
|
|
@ -185,8 +187,8 @@ class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
|||
41 -> end()
|
||||
50 -> {
|
||||
val wearingRing = inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)
|
||||
val dust = if (player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST)
|
||||
if (player.getInventory().remove(Item(ShootingStarOptionHandler.STAR_DUST, dust))) {
|
||||
val dust = if (player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST)
|
||||
if (player.getInventory().remove(Item(ShootingStarPlugin.STAR_DUST, dust))) {
|
||||
val cosmicRunes = (Math.ceil(0.76 * dust) * AMPLIFIER).toInt()
|
||||
val astralRunes = (Math.ceil(0.26 * dust) * AMPLIFIER).toInt()
|
||||
val goldOre = (Math.ceil(0.1 * dust) * AMPLIFIER).toInt()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit
|
|||
* Handles the work-for actions for the NPCs
|
||||
* @author Ceikry
|
||||
*/
|
||||
class WorkForInteractionListener : InteractionListener(), LoginListener {
|
||||
class WorkForInteractionListener : InteractionListener, LoginListener {
|
||||
val possibleWeaponLooks = arrayListOf(
|
||||
Items.BRONZE_SCIMITAR_1321,
|
||||
Items.STEEL_SCIMITAR_1325,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import api.*
|
|||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class DSEquipListeners : InteractionListener() {
|
||||
class DSEquipListeners : InteractionListener {
|
||||
|
||||
private val restrictedItems = intArrayOf(
|
||||
Items.RUNE_PLATEBODY_1127,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import org.rs09.consts.Items
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class JohnathonAntiPosionInteraction: InteractionListener() {
|
||||
class JohnathonAntiPosionInteraction: InteractionListener {
|
||||
override fun defineListeners() {
|
||||
val poisons = intArrayOf(Items.ANTIPOISON4_2446, Items.ANTIPOISON3_175, Items.ANTIPOISON2_177, Items.ANTIPOISON1_179)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ fun doDoor(player: Player, scenery: Scenery) {
|
|||
|
||||
}
|
||||
|
||||
class WitchavenLeverInteraction : InteractionListener() {
|
||||
class WitchavenLeverInteraction : InteractionListener {
|
||||
val DOWN_ANIMATION = Animation(2140)
|
||||
val UP_ANIMATION = Animation(2139)
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class BrotherCedricDialogue : DialogueFile() {
|
|||
* Handles BrotherCedricListener to launch the dialogue
|
||||
* @author Kya
|
||||
*/
|
||||
class BrotherCedricListener : InteractionListener() {
|
||||
class BrotherCedricListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.BROTHER_CEDRIC_280, NPC, "talk-to"){ player, _ ->
|
||||
player.dialogueInterpreter.open(BrotherCedricDialogue(), NPC(NPCs.BROTHER_CEDRIC_280))
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ class BrotherOmadDialogue : DialogueFile() {
|
|||
* Handles BrotherCedricListener to launch the dialogue
|
||||
* @author Kya
|
||||
*/
|
||||
class BrotherOmadListener : InteractionListener() {
|
||||
class BrotherOmadListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.BROTHER_OMAD_279, NPC, "talk-to"){ player, _ ->
|
||||
player.dialogueInterpreter.open(BrotherOmadDialogue(), NPC(NPCs.BROTHER_OMAD_279))
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class MonasteryMonkDialogue : DialogueFile() {
|
|||
* Handles BrotherCedricListener to launch the dialogue
|
||||
* @author Kya
|
||||
*/
|
||||
class MonasteryMonkListener : InteractionListener() {
|
||||
class MonasteryMonkListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.MONK_281, NPC, "talk-to"){ player, _ ->
|
||||
player.dialogueInterpreter.open(MonasteryMonkDialogue(), NPC(NPCs.MONK_281))
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ import org.rs09.consts.Items
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.content.global.action.PickupHandler
|
||||
import rs09.game.content.global.shops.Shops
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.node.entity.npc.other.MortMyreGhastNPC
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.config.ShopParser
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
class NSListeners : InteractionListener() {
|
||||
class NSListeners : InteractionListener {
|
||||
|
||||
val GROTTO_TREE = 3517
|
||||
val GROTTO_ENTRANCE = 3516
|
||||
|
|
@ -101,7 +102,7 @@ class NSListeners : InteractionListener() {
|
|||
|
||||
on(WISHING_WELL, SCENERY, "make-wish"){player, node ->
|
||||
if(player.questRepository.isComplete("Nature Spirit") && player.questRepository.isComplete("Wolf Whistle"))
|
||||
ShopParser.openUid(player, 241)
|
||||
Shops.openId(player, 241)
|
||||
else
|
||||
sendDialogue(player, "You can't do that yet.")
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import rs09.game.world.GameWorld.Pulser
|
|||
import kotlin.math.abs
|
||||
import kotlin.math.atan2
|
||||
|
||||
class HunterTalismanListener : InteractionListener() {
|
||||
class HunterTalismanListener : InteractionListener {
|
||||
|
||||
val TALISMAN = Items.HUNTERS_TALISMAN_3696
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import api.sendMessage
|
|||
import core.game.container.access.BitregisterAssembler
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class SeerLockInterfaceListener : InterfaceListener() {
|
||||
class SeerLockInterfaceListener : InterfaceListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
val LETTERONEBACK = 39
|
||||
val LETTERONEFORWARD = 40
|
||||
val LETTERTWOBACK = 35
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import rs09.game.content.dialogue.DialogueFile
|
|||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.tools.stringtools.RED
|
||||
|
||||
class SeersHouseListeners : InteractionListener() {
|
||||
class SeersHouseListeners : InteractionListener {
|
||||
|
||||
val WESTDOOR = 4165
|
||||
val EASTDOOR = 4166
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import rs09.game.system.config.ItemConfigParser
|
|||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.GameWorld.Pulser
|
||||
|
||||
class TFTInteractionListeners : InteractionListener(){
|
||||
class TFTInteractionListeners : InteractionListener{
|
||||
|
||||
val BEER = Items.BEER_1917
|
||||
val WORKER = NPCs.COUNCIL_WORKMAN_1287
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import core.game.node.entity.skill.gather.SkillingTool
|
|||
import core.game.world.map.Location
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class DramenTreeListener : InteractionListener() {
|
||||
class DramenTreeListener : InteractionListener {
|
||||
|
||||
val DRAMEN_TREE = 1292
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import rs09.game.world.GameWorld
|
|||
* handles pickpocketing sigmund during the lost tribe quest
|
||||
* @author Ceikry
|
||||
*/
|
||||
class PickpocketSigmund : InteractionListener(){
|
||||
class PickpocketSigmund : InteractionListener{
|
||||
val SIGMUND = NPCs.SIGMUND_2082
|
||||
|
||||
override fun defineListeners() {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import api.setInterfaceText
|
|||
import core.game.world.map.Location
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class TTDoorCodeInterfaceListener : InterfaceListener() {
|
||||
class TTDoorCodeInterfaceListener : InterfaceListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
val LETTERONEBACK = 10
|
||||
val LETTERONEFORWARD = 11
|
||||
val LETTERTWOBACK = 12
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import core.game.world.update.flag.context.Animation
|
|||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class TribalTotemListeners : InteractionListener(){
|
||||
class TribalTotemListeners : InteractionListener{
|
||||
|
||||
val frontDoor = 2706
|
||||
val wizCrate = 2707
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import core.game.world.map.Location
|
|||
import org.rs09.consts.NPCs
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class WaterfallListeners : InteractionListener(){
|
||||
class WaterfallListeners : InteractionListener{
|
||||
|
||||
val HUDON = NPCs.HUDON_305
|
||||
override fun defineListeners() {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue