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:
Ceikry 2022-04-12 08:46:21 +00:00 committed by Ryan
parent f9aea18497
commit e958b8d255
202 changed files with 1910 additions and 2385 deletions

View file

@ -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);
}
/**

View file

@ -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);

View file

@ -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));
}

View file

@ -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));

View file

@ -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!");
}

View file

@ -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 };
}
}
*/

View file

@ -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

View file

@ -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)
)
}
}
}

View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;
}
});

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}
}
*/

View file

@ -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)

View file

@ -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;

View file

@ -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
}
}
}

View file

@ -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;
}

View file

@ -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() {

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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));
}
/**

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}
}
}*/
}

View file

@ -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

View file

@ -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)

View file

@ -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);
}
/**

View file

@ -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;
}
/**

View file

@ -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){

View file

@ -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);

View 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()
}

View file

@ -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()
}

View file

@ -0,0 +1,7 @@
package api
interface ContentInterface {
/**
* A dummy/shell interface that allows us to more easily load content via class scanning
*/
}

View 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
}
}

View file

@ -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.

View file

@ -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.

View 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) {}
}

View file

@ -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.

View file

@ -1,6 +0,0 @@
package api
interface PersistWorld {
fun saveWorld()
fun parseWorld()
}

View file

@ -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.
*/

View file

@ -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.
*/

View file

@ -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.

View file

@ -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 -> {

View file

@ -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 ->

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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) }

View file

@ -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()
}
}

View file

@ -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) {

View file

@ -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)

View file

@ -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() {

View file

@ -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) {

View file

@ -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

View file

@ -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() {

View file

@ -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()
}

View file

@ -20,7 +20,7 @@ import rs09.game.system.config.ItemConfigParser
* @author Ceikry
* @author Woah
*/
class EquipHandler : InteractionListener() {
class EquipHandler : InteractionListener {
override fun defineListeners() {

View 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>()
}
}

View 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)
}
}
}

View file

@ -48,7 +48,7 @@ class GiftRollPlugin : XPGainPlugin() {
}
}
class XMASMboxHandler : InteractionListener() {
class XMASMboxHandler : InteractionListener {
val MBOX = Items.MYSTERY_BOX_6199
override fun defineListeners() {

View file

@ -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)

View file

@ -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

View file

@ -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 ->

View file

@ -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

View file

@ -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
}
}

View file

@ -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")
}
}
}

View file

@ -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
}
}

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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)
}
}
}

View file

@ -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()

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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))

View file

@ -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))

View file

@ -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))

View file

@ -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.")

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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() {

View file

@ -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

View file

@ -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

View file

@ -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