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:
@@ -0,0 +1,49 @@
|
||||
package api
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import rs09.game.system.command.Command
|
||||
import rs09.game.system.command.CommandMapping
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.tools.stringtools.colorize
|
||||
|
||||
/**
|
||||
* An interface for writing content that allows the class to define commands.
|
||||
*
|
||||
* Includes methods [reject] and [notify] for notifying the player of a command rejection or some output text respectively.
|
||||
*
|
||||
* Includes the method [define] to define individual commands
|
||||
*
|
||||
* Commands should ideally be defined in the required [defineCommands] method.
|
||||
*/
|
||||
interface Commands : ContentInterface {
|
||||
/**
|
||||
* Glorified player.sendMessage with red text coloring. Please use this
|
||||
* rather than just player.sendMessage for anything that involves informing the player
|
||||
* of proper usage or invalid syntax. Throws an IllegalStateException and ends command execution immediately.
|
||||
*/
|
||||
fun reject(player: Player, vararg message: String){
|
||||
for(msg in message) {
|
||||
player.sendMessage(colorize("-->%R$msg"))
|
||||
}
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Glorified player.sendMessage with black text coloring and an arrow. Use this when you need to
|
||||
* notify/inform a player of some information from within the command without ending execution.
|
||||
*/
|
||||
fun notify(player: Player, message: String){
|
||||
player.sendMessage(colorize("-->$message"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to define commands. define("name",(optional) privilege override){ handle }
|
||||
* @param name the name of the command. Example: ::example would be just "example"
|
||||
* @param privilege the rights level needed to execute the command. Options are [Privilege.STANDARD], [Privilege.MODERATOR], [Privilege.ADMIN]. Defaults to [Privilege.STANDARD]
|
||||
*/
|
||||
fun define(name: String, privilege: Privilege = Privilege.STANDARD, handle: (Player, Array<String>) -> Unit){
|
||||
CommandMapping.register(Command(name, privilege, handle))
|
||||
}
|
||||
|
||||
fun defineCommands()
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import Cutscene
|
||||
import com.moandjiezana.toml.Toml
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.cache.def.impl.SceneryDefinition
|
||||
import core.game.component.Component
|
||||
@@ -45,6 +46,7 @@ import rs09.game.content.global.GlobalKillCounter
|
||||
import rs09.game.interaction.InteractionListeners
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.config.ItemConfigParser
|
||||
import rs09.game.system.config.ServerConfigParser
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.GameWorld.Pulser
|
||||
import rs09.game.world.repository.Repository
|
||||
@@ -714,6 +716,15 @@ fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List<NPC>{
|
||||
return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param regionId the ID of the region
|
||||
* @return a [ZoneBorders] encapsulating the entire region indicated by the provided regionId
|
||||
*/
|
||||
fun getRegionBorders(regionId: Int) : ZoneBorders
|
||||
{
|
||||
return ZoneBorders.forRegion(regionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an attribute key from the Entity's attributes store
|
||||
* @param entity the entity to get the attribute from
|
||||
@@ -1447,4 +1458,9 @@ fun Player.getCutscene(): Cutscene? {
|
||||
|
||||
fun Player.getCutsceneStage(): Int {
|
||||
return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE_STAGE, 0)
|
||||
}
|
||||
|
||||
fun getServerConfig() : Toml
|
||||
{
|
||||
return ServerConfigParser.tomlData ?: Toml()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package api
|
||||
|
||||
interface ContentInterface {
|
||||
/**
|
||||
* A dummy/shell interface that allows us to more easily load content via class scanning
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package api
|
||||
|
||||
/**
|
||||
* Used to generate interface settings hashes.
|
||||
* Deprecates [core.game.container.access.BitregisterAssembler]
|
||||
* @author Ceikry
|
||||
*/
|
||||
class IfaceSettingsBuilder {
|
||||
/**
|
||||
* Contains the value which should be sent in access mask packet.
|
||||
*/
|
||||
private var value = 0
|
||||
|
||||
/**
|
||||
* Sets right click option settings. If specified option is not allowed, it
|
||||
* might not appear in the context menu, and if it does, it will throw an error when clicked.
|
||||
* @param optionId The option index.
|
||||
*/
|
||||
fun enableOption(optionId: Int): IfaceSettingsBuilder {
|
||||
require(!(optionId < 0 || optionId > 9)) { "Option index must be 0-9." }
|
||||
value = value or (0x1 shl optionId + 1)
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(vararg ids: Int): IfaceSettingsBuilder {
|
||||
for (i in ids.indices) {
|
||||
enableOption(ids[i])
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(ids: IntRange): IfaceSettingsBuilder {
|
||||
for (i in ids.start..ids.endInclusive) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableAllOptions(): IfaceSettingsBuilder {
|
||||
for (i in 0..9) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableOptions(vararg options: String?): IfaceSettingsBuilder {
|
||||
for (i in options.indices) {
|
||||
enableOption(i)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets use on option settings. If nothing is allowed then 'use' option will
|
||||
* not appear in right click menu.
|
||||
*/
|
||||
fun setUseOnSettings(
|
||||
groundItems: Boolean,
|
||||
npcs: Boolean,
|
||||
objects: Boolean,
|
||||
otherPlayer: Boolean,
|
||||
selfPlayer: Boolean,
|
||||
component: Boolean
|
||||
): IfaceSettingsBuilder {
|
||||
var useFlag = 0
|
||||
if (groundItems) {
|
||||
useFlag = useFlag or 0x1
|
||||
}
|
||||
if (npcs) {
|
||||
useFlag = useFlag or 0x2
|
||||
}
|
||||
if (objects) {
|
||||
useFlag = useFlag or 0x4
|
||||
}
|
||||
if (otherPlayer) {
|
||||
useFlag = useFlag or 0x8
|
||||
}
|
||||
if (selfPlayer) {
|
||||
useFlag = useFlag or 0x10
|
||||
}
|
||||
if (component) {
|
||||
useFlag = useFlag or 0x20
|
||||
}
|
||||
value = value or (useFlag shl 11)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interface events depth. For example, we have inventory interface
|
||||
* which is opened on gameframe interface (548) If depth is 1, then the
|
||||
* clicks in inventory will also invoke click event handler scripts on
|
||||
* gameframe interface. Setting depth to 2 also allows dragged items to
|
||||
* leave the bounds of their container, useful for things such as bank tabs.
|
||||
* @param depth The depth value.
|
||||
*/
|
||||
fun setInterfaceEventsDepth(depth: Int): IfaceSettingsBuilder {
|
||||
require(!(depth < 0 || depth > 7)) { "depth must be 0-7." }
|
||||
value = value and (0x7 shl 18).inv()
|
||||
value = value or (depth shl 18)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows items in this interface container to be switched around
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableSlotSwitch(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 21)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows items in this interface container to have a "Use" option
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableUseOption(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 17)
|
||||
return this
|
||||
}
|
||||
|
||||
fun enableExamine(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 9)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this component to have items used on it
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableUseOnSelf(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 22)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this component to switch item slots with a slot that contains a null (empty slot)
|
||||
* @return this builder
|
||||
*/
|
||||
fun enableNullSlotSwitch(): IfaceSettingsBuilder {
|
||||
value = value or (1 shl 23)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* @return The value.
|
||||
*/
|
||||
fun build(): Int {
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,12 @@ package api
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LoginListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute some code when a player logs in.
|
||||
*
|
||||
* Login listeners are called *before* [PersistPlayer] data is parsed.
|
||||
*/
|
||||
interface LoginListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
||||
@@ -2,7 +2,12 @@ package api
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LogoutListener {
|
||||
/**
|
||||
* An interface for writing content that allows code to be executed by the class when a player logs out.
|
||||
*
|
||||
* Logout listeners are called *before* [PersistPlayer] data is saved.
|
||||
*/
|
||||
interface LogoutListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package api
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneRestriction
|
||||
|
||||
/**
|
||||
* Interface that allows a class to define a map area.
|
||||
* Optionally-overridable methods include [getRestrictions], [areaEnter], [areaLeave] and [entityStep]
|
||||
*/
|
||||
interface MapArea {
|
||||
fun defineAreaBorders() : Array<ZoneBorders>
|
||||
fun getRestrictions() : Array<ZoneRestriction> {return arrayOf()}
|
||||
fun areaEnter(entity: Entity) {}
|
||||
fun areaLeave(entity: Entity, logout: Boolean) {}
|
||||
fun entityStep(entity: Entity, location: Location, lastLocation: Location) {}
|
||||
}
|
||||
@@ -3,7 +3,14 @@ package api
|
||||
import core.game.node.entity.player.Player
|
||||
import org.json.simple.JSONObject
|
||||
|
||||
interface PersistPlayer {
|
||||
/**
|
||||
* An interface for writing content that allows data to be saved and loaded from player saves.
|
||||
*
|
||||
* Parsing is called *after* any [LoginListener] is executed.
|
||||
*
|
||||
* Saving is called *after* any [LogoutListener] is executed.
|
||||
*/
|
||||
interface PersistPlayer : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* You need to fetch a player's specific instance of the data and save from that.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package api
|
||||
|
||||
interface PersistWorld {
|
||||
fun saveWorld()
|
||||
fun parseWorld()
|
||||
}
|
||||
@@ -2,7 +2,10 @@ package api
|
||||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface ShutdownListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code as the server is shutting down
|
||||
*/
|
||||
interface ShutdownListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,10 @@ package api
|
||||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface StartupListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code when the server is started.
|
||||
*/
|
||||
interface StartupListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package api
|
||||
|
||||
interface TickListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to be updated each tick.
|
||||
*/
|
||||
interface TickListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* TickListeners are generally for NON-player, WORLD tick events.
|
||||
|
||||
@@ -6,17 +6,14 @@ import core.game.world.map.Location
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.ai.general.GeneralBotCreator
|
||||
import rs09.game.ai.general.ScriptAPI
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStarEvent
|
||||
import rs09.game.interaction.InteractionListener.Companion.SCENERY
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStarPlugin
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InteractionListeners
|
||||
import kotlin.concurrent.timer
|
||||
|
||||
class ShootingStarBot : Script() {
|
||||
private var state = State.FULL_IDLE
|
||||
private var timerCountdown = 0
|
||||
val star = (WorldEvents.get("shooting-stars") as? ShootingStarEvent)!!.star
|
||||
val star = ShootingStarPlugin.getStar()
|
||||
|
||||
override fun tick() {
|
||||
bot.fullRestore()
|
||||
@@ -36,7 +33,7 @@ class ShootingStarBot : Script() {
|
||||
}
|
||||
|
||||
State.MINING -> {
|
||||
InteractionListeners.run(star.starObject.id, SCENERY, "mine", bot, star.starObject)
|
||||
InteractionListeners.run(star.starObject.id, InteractionListener.SCENERY, "mine", bot, star.starObject)
|
||||
}
|
||||
|
||||
State.TELEPORT_BACK -> {
|
||||
|
||||
@@ -19,7 +19,7 @@ private val LIGHT_ANIM = Animation(7307)
|
||||
* Handles interactions for beacons
|
||||
* @author Ceikry
|
||||
*/
|
||||
class AFUBeaconListeners : InteractionListener(){
|
||||
class AFUBeaconListeners : InteractionListener{
|
||||
|
||||
override fun defineListeners() {
|
||||
on(SCENERY,"add-logs","light"){ player, node ->
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import rs09.game.interaction.InteractionListener
|
||||
* @author bushtail
|
||||
*/
|
||||
|
||||
class CraftingGuildListeners : InteractionListener() {
|
||||
class CraftingGuildListeners : InteractionListener {
|
||||
private val GUILD_DOOR = Scenery.GUILD_DOOR_2647
|
||||
private val APRON = Items.BROWN_APRON_1757
|
||||
private val CAPE = Items.CRAFTING_CAPE_9780
|
||||
|
||||
@@ -11,7 +11,7 @@ import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.node.entity.skill.magic.SpellListener
|
||||
import rs09.game.node.entity.skill.magic.spellconsts.Modern
|
||||
|
||||
class MTAListeners : InteractionListener() {
|
||||
class MTAListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.MAZE_GUARDIAN_3102,NPC,"talk-to"){player,node ->
|
||||
player.dialogueInterpreter.open(node.id, node)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package rs09.game.content.activity.pyramidplunder
|
||||
|
||||
import api.EquipmentSlot
|
||||
import api.openDialogue
|
||||
import api.sendMessage
|
||||
import rs09.game.world.GameWorld.Pulser
|
||||
import rs09.game.content.activity.pyramidplunder.PyramidPlunderMinigame.Companion.GUARDIAN_ROOM
|
||||
import core.plugin.Initializable
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
/**
|
||||
* Adds functionality to the pharoah's scepter
|
||||
* @author ceik
|
||||
*/
|
||||
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)
|
||||
|
||||
on(SCEPTRES, ITEM, "teleport", "operate"){player, node ->
|
||||
val sceptre = node.asItem()
|
||||
|
||||
if(sceptre.id == SCEPTRES.last())
|
||||
{
|
||||
sendMessage(player, "You have used up all the charges on this sceptre.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
openDialogue(player, SceptreDialog())
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
class SceptreDialog : DialogueFile() {
|
||||
fun teleport(location: Location?, player: Player) {
|
||||
player.lock()
|
||||
player.visualize(ANIMATION, GRAPHICS)
|
||||
player.impactHandler.disabledTicks = 4
|
||||
Pulser.submit(object : Pulse(4, player) {
|
||||
override fun pulse(): Boolean {
|
||||
player.unlock()
|
||||
player.properties.teleportLocation = location
|
||||
player.animator.reset()
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
if (player!!.isTeleBlocked) {
|
||||
player!!.sendMessage("A magical force has stopped you from teleporting.")
|
||||
return
|
||||
}
|
||||
when (stage) {
|
||||
0 -> options("Jalsavrah", "Jaleustrophos", "Jaldraocht", "Nowhere").also { stage++ }
|
||||
1 -> {
|
||||
end()
|
||||
when (buttonID) {
|
||||
1 -> {
|
||||
teleport(GUARDIAN_ROOM, player!!)
|
||||
}
|
||||
2 -> {
|
||||
teleport(Location.create(3342, 2827, 0), player!!)
|
||||
}
|
||||
3 -> {
|
||||
teleport(Location.create(3233, 2902, 0), player!!)
|
||||
}
|
||||
4 -> return
|
||||
}
|
||||
//This sucks but I'm too lazy to fix it.
|
||||
if (player!!.equipment.containsItem(Item(9044))) {
|
||||
player!!.equipment.replace(Item(9046), EquipmentSlot.WEAPON.ordinal)
|
||||
player!!.packetDispatch.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has 2 charges remaining.")
|
||||
} else if (player!!.equipment.containsItem(Item(9046))) {
|
||||
player!!.equipment.replace(Item(9048), EquipmentSlot.WEAPON.ordinal)
|
||||
player!!.packetDispatch.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has 1 charge remaining.")
|
||||
} else if (player!!.equipment.containsItem(Item(9048))) {
|
||||
player!!.equipment.replace(Item(9050), EquipmentSlot.WEAPON.ordinal)
|
||||
player!!.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has used its last charge.")
|
||||
} else if (player!!.inventory.containsItem(Item(9050))) {
|
||||
player!!.inventory.remove(Item(9050))
|
||||
player!!.inventory.add(Item(9048))
|
||||
player!!.packetDispatch.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has 2 charges remaining.")
|
||||
} else if (player!!.inventory.containsItem(Item(9048))) {
|
||||
player!!.inventory.remove(Item(9048))
|
||||
player!!.inventory.add(Item(9046))
|
||||
player!!.packetDispatch.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has 1 charge remaining.")
|
||||
} else if (player!!.inventory.containsItem(Item(9046))) {
|
||||
player!!.inventory.remove(Item(9046))
|
||||
player!!.inventory.add(Item(9044))
|
||||
player!!.sendMessage("<col=7f03ff>Your Pharoah's Sceptre has used its last charge.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GRAPHICS = Graphics(715)
|
||||
private val ANIMATION = Animation(714)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -17,11 +17,12 @@ import org.rs09.consts.Scenery
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
|
||||
/**
|
||||
* The "controller" class for pyramid plunder. Handles per-tick updates, logout hooks, and defines interaction listeners for the minigame.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class PyramidPlunderMinigame : InteractionListener(), TickListener, LogoutListener {
|
||||
class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener {
|
||||
override fun tick() {
|
||||
val playersToExpel = PlunderUtils.decrementTimeRemaining()
|
||||
playersToExpel.forEach {player -> PlunderUtils.expel(player, false) }
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
package rs09.game.content.activity.vinesweeper
|
||||
|
||||
import BlinkinDialogue
|
||||
import WinkinDialogue
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.DEAD_PLANT_OBJ
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.FARMERS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.HOLES
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.NUMBERS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.RABBIT
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.SEED_LOCS
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.populateSeeds
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.scheduleNPCs
|
||||
import rs09.game.content.activity.vinesweeper.Vinesweeper.Companion.sendUpdatedPoints
|
||||
import api.*
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
@@ -13,164 +27,52 @@ import core.game.node.scenery.SceneryBuilder
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneBuilder
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
val AVACH_NIMPORTO_LOC = Location.create(1637, 4709)
|
||||
val PORTAL = 29534
|
||||
val SIGNS = intArrayOf(29461, 29462, 29463, 29464)
|
||||
val HOLES = intArrayOf(29476, 29477, 29478)
|
||||
val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455)
|
||||
val DEAD_PLANT_OBJ = 29456
|
||||
val FLAG_OBJ = 29457
|
||||
class Vinesweeper : InteractionListener, InterfaceListener, MapArea {
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(getRegionBorders(6473))
|
||||
}
|
||||
|
||||
val TUTORIAL = 685
|
||||
|
||||
val INSTRUCTION_SIGNS = hashMapOf(
|
||||
29463 to 684,
|
||||
29464 to 687,
|
||||
29462 to 688,
|
||||
29461 to 690
|
||||
)
|
||||
|
||||
val RABBIT = 7125
|
||||
val FARMERS = intArrayOf(7128, 7129, 7130)
|
||||
val FARMER_BLINKIN = 7131
|
||||
val MRS_WINKIN = 7132
|
||||
|
||||
val MAX_SEEDS = 300
|
||||
val FARMER_CLEAR_RADIUS = 3
|
||||
val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735)
|
||||
|
||||
fun sendUpdatedPoints(player: Player) {
|
||||
val points = player.getAttribute("vinesweeper:points", 0);
|
||||
player.varpManager.get(1195).setVarbit(6, points).send(player)
|
||||
}
|
||||
|
||||
data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other is SeedDestination) {
|
||||
return loc.equals(other.loc)
|
||||
} else {
|
||||
return false
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if(entity is Player)
|
||||
{
|
||||
openOverlay(entity, Components.RABBIT_OVERLAY_689)
|
||||
sendUpdatedPoints(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
var SEED_LOCS: HashSet<Location> = HashSet()
|
||||
|
||||
fun isSeed(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && SEED_LOCS.contains(scenery.location)
|
||||
}
|
||||
|
||||
fun populateSeeds() {
|
||||
while(SEED_LOCS.size < MAX_SEEDS) {
|
||||
val loc = VINESWEEPER_BORDERS.getRandomLoc()
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null && HOLES.contains(scenery.id)) {
|
||||
SEED_LOCS.add(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isHole(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && HOLES.contains(scenery.id)
|
||||
}
|
||||
|
||||
fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) {
|
||||
val dest = SeedDestination(player, loc, alive)
|
||||
val ids = if(rabbit) { intArrayOf(RABBIT, *FARMERS) } else { FARMERS }
|
||||
for(npc in findLocalNPCs(player, ids, 30)) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.add(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VinesweeperTeleport {
|
||||
@JvmStatic
|
||||
fun teleport(npc: NPC, player: Player) {
|
||||
npc.animate(Animation(437))
|
||||
npc.faceTemporary(player, 1)
|
||||
npc.graphics(Graphics(108))
|
||||
player.lock()
|
||||
player.audioManager.send(125)
|
||||
Projectile.create(npc, player, 109).send()
|
||||
npc.sendChat("Avach nimporto!")
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
2 -> {
|
||||
player.savedData.globalData.essenceTeleporter = npc.id
|
||||
player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x)
|
||||
player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y)
|
||||
player.properties.teleportLocation = AVACH_NIMPORTO_LOC
|
||||
}
|
||||
3 -> {
|
||||
player.graphics(Graphics(110))
|
||||
player.unlock()
|
||||
return true
|
||||
}
|
||||
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||
if(entity is Player) {
|
||||
entity.interfaceManager.closeOverlay()
|
||||
if(!logout) {
|
||||
entity.sendMessage("Winkin's Farm thanks you for your visit.")
|
||||
entity.sendMessage("Leftover ogleroots and flags have been returned to the establishment.")
|
||||
entity.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.")
|
||||
val flags = entity.inventory.getAmount(Item(Items.FLAG_12625))
|
||||
if(flags > 0) {
|
||||
entity.setAttribute("/save:vinesweeper:stored-flags", flags)
|
||||
entity.inventory.remove(Item(Items.FLAG_12625, flags))
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperListener : InteractionListener() {
|
||||
fun dig(player: Player, loc: Location) {
|
||||
if(isSeed(loc)) {
|
||||
val oldPoints = player.getAttribute("vinesweeper:points", 0)
|
||||
player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0))
|
||||
sendUpdatedPoints(player)
|
||||
player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.");
|
||||
scheduleNPCs(player, loc, false, false)
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(DEAD_PLANT_OBJ))
|
||||
}
|
||||
} else {
|
||||
player.incrementAttribute("/save:vinesweeper:points", 1)
|
||||
sendUpdatedPoints(player)
|
||||
var count = 0
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
if(isSeed(loc.transform(dx, dy, 0))) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count]))
|
||||
}
|
||||
if(count == 0) {
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
val newLoc = loc.transform(dx, dy, 0)
|
||||
if(isHole(newLoc))
|
||||
dig(player, newLoc)
|
||||
}
|
||||
val roots = entity.inventory.getAmount(Item(Items.OGLEROOT_12624))
|
||||
if(roots > 0) {
|
||||
entity.inventory.remove(Item(Items.OGLEROOT_12624, roots))
|
||||
entity.inventory.add(Item(Items.COINS_995, roots * 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
ZoneBuilder.configure(VinesweeperZone())
|
||||
populateSeeds()
|
||||
on(PORTAL, SCENERY, "enter") { player, _ ->
|
||||
val x = player.getAttribute("vinesweeper:return-tele:x", 3052)
|
||||
@@ -227,7 +129,7 @@ class VinesweeperListener : InteractionListener() {
|
||||
return@on true
|
||||
}
|
||||
on(MRS_WINKIN, NPC, "talk-to") { player, npc ->
|
||||
openDialogue(player, WinkinDialogue(), npc)
|
||||
openDialogue(player, WinkinDialogue(), npc)
|
||||
return@on true
|
||||
}
|
||||
on(MRS_WINKIN, NPC, "trade") { player, _ ->
|
||||
@@ -264,7 +166,7 @@ class VinesweeperListener : InteractionListener() {
|
||||
}
|
||||
on(FARMER_BLINKIN, NPC, "talk-to") { player, npc ->
|
||||
//player.interfaceManager.open(Component(TUTORIAL))
|
||||
openDialogue(player, BlinkinDialogue(), npc)
|
||||
openDialogue(player, BlinkinDialogue(), npc)
|
||||
return@on true
|
||||
}
|
||||
on(FARMER_BLINKIN, NPC, "buy-flags") { player, npc ->
|
||||
@@ -280,240 +182,8 @@ class VinesweeperListener : InteractionListener() {
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Initializable
|
||||
class VinesweeperNPC : AbstractNPC {
|
||||
fun compareDistance(a: SeedDestination, b: SeedDestination): Int {
|
||||
val da = a.loc.getDistance(location).toInt()
|
||||
val db = b.loc.getDistance(location).toInt()
|
||||
return db - da
|
||||
}
|
||||
var seedDestinations: ArrayList<SeedDestination> = ArrayList();
|
||||
constructor() : super(RABBIT, null, true) {}
|
||||
private constructor(id: Int, location: Location) : super(id, location) {}
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
|
||||
return VinesweeperNPC(id, location)
|
||||
}
|
||||
|
||||
init {
|
||||
walkRadius = 22
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(RABBIT, *FARMERS)
|
||||
}
|
||||
|
||||
override fun handleTickActions() {
|
||||
val dest = seedDestinations.find { sd -> sd.loc == location }
|
||||
if(dest != null) {
|
||||
for(npc in RegionManager.getRegionPlane(location).npcs) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.remove(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(dest.loc)
|
||||
if(scenery != null) {
|
||||
if(id == RABBIT) {
|
||||
val replacement = if(SEED_LOCS.contains(dest.loc)) { DEAD_PLANT_OBJ } else { HOLES[0] }
|
||||
SceneryBuilder.replace(scenery, scenery.transform(replacement))
|
||||
scheduleNPCs(dest.player, dest.loc, false, false)
|
||||
} else {
|
||||
if(dest.alive) {
|
||||
handleFarmerFlag(scenery, dest)
|
||||
} else {
|
||||
sendChat("Hmm. Looks like there's a plant here.")
|
||||
lock(3)
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Gracious me! This one's dead")
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
seedDestinations.remove(dest)
|
||||
}
|
||||
super.handleTickActions()
|
||||
}
|
||||
|
||||
override fun getMovementDestination(): Location? {
|
||||
if(seedDestinations.size > 0) {
|
||||
seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() }
|
||||
return seedDestinations.first().loc
|
||||
} else {
|
||||
return super.getMovementDestination()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleFarmerFlag(scenery: Scenery, dest: SeedDestination) {
|
||||
sendChat("Ah, another flag to clear. Let's see what's there.")
|
||||
lock(3)
|
||||
animate(Animation(451))
|
||||
if(SEED_LOCS.contains(dest.loc)) {
|
||||
val npc = this
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Ah! A seed. Points for everyone near me!")
|
||||
val level = dest.player.skills.getStaticLevel(Skills.FARMING)
|
||||
val points = RandomFunction.random(level, 4 * level)
|
||||
dest.player.incrementAttribute("/save:vinesweeper:points", points)
|
||||
dest.player.inventory.add(Item(Items.FLAG_12625, 1))
|
||||
sendUpdatedPoints(dest.player)
|
||||
for(neighbor in RegionManager.getLocalPlayers(npc)) {
|
||||
if(neighbor != dest.player) {
|
||||
neighbor.incrementAttribute("/save:vinesweeper:points", points / 2)
|
||||
sendUpdatedPoints(neighbor)
|
||||
}
|
||||
}
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
var i = 0
|
||||
val lines = arrayOf("Hmm, no seeds planted here, I'm afraid.", "I'll have to keep this 'ere flag. Sorry.")
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat(lines[i++])
|
||||
return i >= lines.size
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
fun farmerClear(dest: SeedDestination) {
|
||||
for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
val toClear = getScenery(dest.loc.transform(dx, dy, 0))
|
||||
if(toClear != null && intArrayOf(DEAD_PLANT_OBJ, *NUMBERS).contains(toClear.id)) {
|
||||
SceneryBuilder.replace(toClear, toClear.transform(HOLES[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
SEED_LOCS.remove(dest.loc)
|
||||
populateSeeds()
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperZone : MapZone("Vinesweeper", true) {
|
||||
override fun enter(e: Entity): Boolean {
|
||||
if(e is Player) {
|
||||
e.interfaceManager.openOverlay(Component(689))
|
||||
sendUpdatedPoints(e)
|
||||
}
|
||||
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun leave(e: Entity, logout: Boolean): Boolean {
|
||||
if(e is Player) {
|
||||
e.interfaceManager.closeOverlay()
|
||||
if(!logout) {
|
||||
e.sendMessage("Winkin's Farm thanks you for your visit.")
|
||||
e.sendMessage("Leftover ogleroots and flags have been returned to the establishment.")
|
||||
e.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.")
|
||||
val flags = e.inventory.getAmount(Item(Items.FLAG_12625))
|
||||
if(flags > 0) {
|
||||
e.setAttribute("/save:vinesweeper:stored-flags", flags)
|
||||
e.inventory.remove(Item(Items.FLAG_12625, flags))
|
||||
}
|
||||
val roots = e.inventory.getAmount(Item(Items.OGLEROOT_12624))
|
||||
if(roots > 0) {
|
||||
e.inventory.remove(Item(Items.OGLEROOT_12624, roots))
|
||||
e.inventory.add(Item(Items.COINS_995, roots * 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.leave(e, logout)
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
super.registerRegion(6473)
|
||||
}
|
||||
}
|
||||
|
||||
class VinesweeperRewards : InterfaceListener() {
|
||||
val IFACE = 686
|
||||
val TRADE_FOR_XP_BUTTON = 53
|
||||
val XP_CONFIRM = 72
|
||||
val XP_DENY = 73
|
||||
|
||||
enum class Opcode(val value: Int) {
|
||||
VALUE(155),
|
||||
BUY1(196),
|
||||
BUY5(124),
|
||||
BUY10(199),
|
||||
BUYX(234),
|
||||
}
|
||||
|
||||
data class Reward(val itemID: Int, val points: Int) {}
|
||||
|
||||
val REWARDS = hashMapOf(
|
||||
18 to Reward(Items.TOMATO_SEED_5322, 10),
|
||||
19 to Reward(Items.SWEETCORN_SEED_5320, 150),
|
||||
20 to Reward(Items.STRAWBERRY_SEED_5323, 165),
|
||||
21 to Reward(Items.WATERMELON_SEED_5321, 680),
|
||||
22 to Reward(Items.GUAM_SEED_5291, 10),
|
||||
23 to Reward(Items.MARRENTILL_SEED_5292, 10),
|
||||
24 to Reward(Items.RANARR_SEED_5295, 4000),
|
||||
25 to Reward(Items.KWUARM_SEED_5299, 1000),
|
||||
26 to Reward(Items.TARROMIN_SEED_5293, 10),
|
||||
27 to Reward(Items.NASTURTIUM_SEED_5098, 10),
|
||||
|
||||
28 to Reward(Items.WOAD_SEED_5099, 30),
|
||||
29 to Reward(Items.LIMPWURT_SEED_5100, 70),
|
||||
30 to Reward(Items.ASGARNIAN_SEED_5308, 5),
|
||||
31 to Reward(Items.KRANDORIAN_SEED_5310, 20),
|
||||
32 to Reward(Items.REDBERRY_SEED_5101, 5),
|
||||
33 to Reward(Items.CADAVABERRY_SEED_5102, 5),
|
||||
34 to Reward(Items.DWELLBERRY_SEED_5103, 5),
|
||||
35 to Reward(Items.JANGERBERRY_SEED_5104, 10),
|
||||
36 to Reward(Items.WHITEBERRY_SEED_5105, 25),
|
||||
|
||||
37 to Reward(Items.POISON_IVY_SEED_5106, 30),
|
||||
38 to Reward(Items.ACORN_5312, 100),
|
||||
39 to Reward(Items.WILLOW_SEED_5313, 1800),
|
||||
40 to Reward(Items.MAPLE_SEED_5314, 12000),
|
||||
41 to Reward(Items.PINEAPPLE_SEED_5287, 10000),
|
||||
42 to Reward(Items.YEW_SEED_5315, 29000),
|
||||
43 to Reward(Items.PALM_TREE_SEED_5289, 35000),
|
||||
44 to Reward(Items.SPIRIT_SEED_5317, 55000),
|
||||
45 to Reward(Items.COMPOST_POTION4_6470, 5000),
|
||||
46 to Reward(Items.FLAG_12625, 50),
|
||||
)
|
||||
|
||||
fun buy(player: Player, buttonID: Int, amount: Int) {
|
||||
val reward = REWARDS[buttonID] ?: return
|
||||
val cost = amount * reward.points
|
||||
val points = player.getAttribute("vinesweeper:points", 0)
|
||||
if(cost in 1 until points) {
|
||||
val item = Item(reward.itemID, amount)
|
||||
if(!player.inventory.add(item)) {
|
||||
GroundItemManager.create(item, player)
|
||||
}
|
||||
player.incrementAttribute("/save:vinesweeper:points", -cost)
|
||||
sendUpdatedPoints(player)
|
||||
} else {
|
||||
// TODO (crash): authenticity
|
||||
player.sendMessage("You don't have enough points for that.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
onOpen(IFACE) { _, _ ->
|
||||
/*for((buttonID, reward) in REWARDS) {
|
||||
sendItemOnInterface(player, IFACE, buttonID, reward.itemID, 5)
|
||||
}*/
|
||||
//player.packetDispatch.sendRunScript(2003, "")
|
||||
return@onOpen true
|
||||
}
|
||||
override fun defineInterfaceListeners() {
|
||||
on(IFACE) { player, _, opcode, buttonID, _, _ ->
|
||||
when(opcode) {
|
||||
Opcode.VALUE.value -> {
|
||||
@@ -566,6 +236,338 @@ class VinesweeperRewards : InterfaceListener() {
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if(other is SeedDestination) {
|
||||
loc == other.loc
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return loc.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
companion object
|
||||
{
|
||||
val AVACH_NIMPORTO_LOC = Location.create(1637, 4709)
|
||||
val PORTAL = 29534
|
||||
val SIGNS = intArrayOf(29461, 29462, 29463, 29464)
|
||||
val HOLES = intArrayOf(29476, 29477, 29478)
|
||||
val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455)
|
||||
val DEAD_PLANT_OBJ = 29456
|
||||
val FLAG_OBJ = 29457
|
||||
|
||||
val IFACE = 686
|
||||
val TRADE_FOR_XP_BUTTON = 53
|
||||
val XP_CONFIRM = 72
|
||||
val XP_DENY = 73
|
||||
|
||||
enum class Opcode(val value: Int) {
|
||||
VALUE(155),
|
||||
BUY1(196),
|
||||
BUY5(124),
|
||||
BUY10(199),
|
||||
BUYX(234),
|
||||
}
|
||||
|
||||
data class Reward(val itemID: Int, val points: Int) {}
|
||||
|
||||
val REWARDS = hashMapOf(
|
||||
18 to Reward(Items.TOMATO_SEED_5322, 10),
|
||||
19 to Reward(Items.SWEETCORN_SEED_5320, 150),
|
||||
20 to Reward(Items.STRAWBERRY_SEED_5323, 165),
|
||||
21 to Reward(Items.WATERMELON_SEED_5321, 680),
|
||||
22 to Reward(Items.GUAM_SEED_5291, 10),
|
||||
23 to Reward(Items.MARRENTILL_SEED_5292, 10),
|
||||
24 to Reward(Items.RANARR_SEED_5295, 4000),
|
||||
25 to Reward(Items.KWUARM_SEED_5299, 1000),
|
||||
26 to Reward(Items.TARROMIN_SEED_5293, 10),
|
||||
27 to Reward(Items.NASTURTIUM_SEED_5098, 10),
|
||||
|
||||
28 to Reward(Items.WOAD_SEED_5099, 30),
|
||||
29 to Reward(Items.LIMPWURT_SEED_5100, 70),
|
||||
30 to Reward(Items.ASGARNIAN_SEED_5308, 5),
|
||||
31 to Reward(Items.KRANDORIAN_SEED_5310, 20),
|
||||
32 to Reward(Items.REDBERRY_SEED_5101, 5),
|
||||
33 to Reward(Items.CADAVABERRY_SEED_5102, 5),
|
||||
34 to Reward(Items.DWELLBERRY_SEED_5103, 5),
|
||||
35 to Reward(Items.JANGERBERRY_SEED_5104, 10),
|
||||
36 to Reward(Items.WHITEBERRY_SEED_5105, 25),
|
||||
|
||||
37 to Reward(Items.POISON_IVY_SEED_5106, 30),
|
||||
38 to Reward(Items.ACORN_5312, 100),
|
||||
39 to Reward(Items.WILLOW_SEED_5313, 1800),
|
||||
40 to Reward(Items.MAPLE_SEED_5314, 12000),
|
||||
41 to Reward(Items.PINEAPPLE_SEED_5287, 10000),
|
||||
42 to Reward(Items.YEW_SEED_5315, 29000),
|
||||
43 to Reward(Items.PALM_TREE_SEED_5289, 35000),
|
||||
44 to Reward(Items.SPIRIT_SEED_5317, 55000),
|
||||
45 to Reward(Items.COMPOST_POTION4_6470, 5000),
|
||||
46 to Reward(Items.FLAG_12625, 50),
|
||||
)
|
||||
|
||||
fun buy(player: Player, buttonID: Int, amount: Int) {
|
||||
val reward = REWARDS[buttonID] ?: return
|
||||
val cost = amount * reward.points
|
||||
val points = player.getAttribute("vinesweeper:points", 0)
|
||||
if(cost in 1 until points) {
|
||||
val item = Item(reward.itemID, amount)
|
||||
if(!player.inventory.add(item)) {
|
||||
GroundItemManager.create(item, player)
|
||||
}
|
||||
player.incrementAttribute("/save:vinesweeper:points", -cost)
|
||||
sendUpdatedPoints(player)
|
||||
} else {
|
||||
// TODO (crash): authenticity
|
||||
player.sendMessage("You don't have enough points for that.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val TUTORIAL = 685
|
||||
|
||||
val INSTRUCTION_SIGNS = hashMapOf(
|
||||
29463 to 684,
|
||||
29464 to 687,
|
||||
29462 to 688,
|
||||
29461 to 690
|
||||
)
|
||||
|
||||
val RABBIT = 7125
|
||||
val FARMERS = intArrayOf(7128, 7129, 7130)
|
||||
val FARMER_BLINKIN = 7131
|
||||
val MRS_WINKIN = 7132
|
||||
|
||||
val MAX_SEEDS = 300
|
||||
val FARMER_CLEAR_RADIUS = 3
|
||||
val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735)
|
||||
|
||||
fun sendUpdatedPoints(player: Player) {
|
||||
val points = player.getAttribute("vinesweeper:points", 0);
|
||||
player.varpManager.get(1195).setVarbit(6, points).send(player)
|
||||
}
|
||||
|
||||
var SEED_LOCS: HashSet<Location> = HashSet()
|
||||
|
||||
fun isSeed(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && SEED_LOCS.contains(scenery.location)
|
||||
}
|
||||
|
||||
fun populateSeeds() {
|
||||
while(SEED_LOCS.size < MAX_SEEDS) {
|
||||
val loc = VINESWEEPER_BORDERS.getRandomLoc()
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null && HOLES.contains(scenery.id)) {
|
||||
SEED_LOCS.add(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dig(player: Player, loc: Location) {
|
||||
if(isSeed(loc)) {
|
||||
val oldPoints = player.getAttribute("vinesweeper:points", 0)
|
||||
player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0))
|
||||
sendUpdatedPoints(player)
|
||||
player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.");
|
||||
scheduleNPCs(player, loc, false, false)
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(DEAD_PLANT_OBJ))
|
||||
}
|
||||
} else {
|
||||
player.incrementAttribute("/save:vinesweeper:points", 1)
|
||||
sendUpdatedPoints(player)
|
||||
var count = 0
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
if(isSeed(loc.transform(dx, dy, 0))) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(loc)
|
||||
if(scenery != null) {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count]))
|
||||
}
|
||||
if(count == 0) {
|
||||
for(dx in -1..1) {
|
||||
for(dy in -1..1) {
|
||||
val newLoc = loc.transform(dx, dy, 0)
|
||||
if(isHole(newLoc))
|
||||
dig(player, newLoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isHole(loc: Location): Boolean {
|
||||
val scenery = getScenery(loc)
|
||||
return scenery != null && HOLES.contains(scenery.id)
|
||||
}
|
||||
|
||||
fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) {
|
||||
val dest = SeedDestination(player, loc, alive)
|
||||
val ids = if(rabbit) { intArrayOf(RABBIT, *FARMERS) } else { FARMERS }
|
||||
for(npc in findLocalNPCs(player, ids, 30)) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.add(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VinesweeperTeleport {
|
||||
@JvmStatic
|
||||
fun teleport(npc: NPC, player: Player) {
|
||||
npc.animate(Animation(437))
|
||||
npc.faceTemporary(player, 1)
|
||||
npc.graphics(Graphics(108))
|
||||
player.lock()
|
||||
player.audioManager.send(125)
|
||||
Projectile.create(npc, player, 109).send()
|
||||
npc.sendChat("Avach nimporto!")
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when (counter++) {
|
||||
2 -> {
|
||||
player.savedData.globalData.essenceTeleporter = npc.id
|
||||
player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x)
|
||||
player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y)
|
||||
player.properties.teleportLocation = AVACH_NIMPORTO_LOC
|
||||
}
|
||||
3 -> {
|
||||
player.graphics(Graphics(110))
|
||||
player.unlock()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Initializable
|
||||
class VinesweeperNPC : AbstractNPC {
|
||||
var seedDestinations: ArrayList<Vinesweeper.SeedDestination> = ArrayList();
|
||||
constructor() : super(RABBIT, null, true) {}
|
||||
private constructor(id: Int, location: Location) : super(id, location) {}
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
|
||||
return VinesweeperNPC(id, location)
|
||||
}
|
||||
|
||||
init {
|
||||
walkRadius = 22
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(RABBIT, *FARMERS)
|
||||
}
|
||||
|
||||
override fun handleTickActions() {
|
||||
val dest = seedDestinations.find { sd -> sd.loc == location }
|
||||
if(dest != null) {
|
||||
for(npc in RegionManager.getRegionPlane(location).npcs) {
|
||||
if(npc is VinesweeperNPC) {
|
||||
npc.seedDestinations.remove(dest)
|
||||
npc.resetWalk()
|
||||
}
|
||||
}
|
||||
val scenery = getScenery(dest.loc)
|
||||
if(scenery != null) {
|
||||
if(id == RABBIT) {
|
||||
val replacement = if(SEED_LOCS.contains(dest.loc)) { DEAD_PLANT_OBJ } else { HOLES[0] }
|
||||
SceneryBuilder.replace(scenery, scenery.transform(replacement))
|
||||
scheduleNPCs(dest.player, dest.loc, alive = false, rabbit = false)
|
||||
} else {
|
||||
if(dest.alive) {
|
||||
handleFarmerFlag(scenery, dest)
|
||||
} else {
|
||||
sendChat("Hmm. Looks like there's a plant here.")
|
||||
lock(3)
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Gracious me! This one's dead")
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
seedDestinations.remove(dest)
|
||||
}
|
||||
super.handleTickActions()
|
||||
}
|
||||
|
||||
override fun getMovementDestination(): Location? {
|
||||
if(seedDestinations.size > 0) {
|
||||
seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() }
|
||||
return seedDestinations.first().loc
|
||||
} else {
|
||||
return super.getMovementDestination()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleFarmerFlag(scenery: Scenery, dest: Vinesweeper.SeedDestination) {
|
||||
sendChat("Ah, another flag to clear. Let's see what's there.")
|
||||
lock(3)
|
||||
animate(Animation(451))
|
||||
if(SEED_LOCS.contains(dest.loc)) {
|
||||
val npc = this
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat("Ah! A seed. Points for everyone near me!")
|
||||
val level = dest.player.skills.getStaticLevel(Skills.FARMING)
|
||||
val points = RandomFunction.random(level, 4 * level)
|
||||
dest.player.incrementAttribute("/save:vinesweeper:points", points)
|
||||
dest.player.inventory.add(Item(Items.FLAG_12625, 1))
|
||||
sendUpdatedPoints(dest.player)
|
||||
for(neighbor in RegionManager.getLocalPlayers(npc)) {
|
||||
if(neighbor != dest.player) {
|
||||
neighbor.incrementAttribute("/save:vinesweeper:points", points / 2)
|
||||
sendUpdatedPoints(neighbor)
|
||||
}
|
||||
}
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
farmerClear(dest)
|
||||
return true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
SceneryBuilder.replace(scenery, scenery.transform(HOLES[0]))
|
||||
var i = 0
|
||||
val lines = arrayOf("Hmm, no seeds planted here, I'm afraid.", "I'll have to keep this 'ere flag. Sorry.")
|
||||
GameWorld.Pulser.submit(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
sendChat(lines[i++])
|
||||
return i >= lines.size
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
fun farmerClear(dest: Vinesweeper.SeedDestination) {
|
||||
for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) {
|
||||
val toClear = getScenery(dest.loc.transform(dx, dy, 0))
|
||||
if(toClear != null && intArrayOf(DEAD_PLANT_OBJ, *NUMBERS).contains(toClear.id)) {
|
||||
SceneryBuilder.replace(toClear, toClear.transform(HOLES[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
SEED_LOCS.remove(dest.loc)
|
||||
populateSeeds()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import core.game.node.item.Item
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class WGuildListeners : InteractionListener() {
|
||||
class WGuildListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onEquip(Items.DEFENSIVE_SHIELD_8856){player, node ->
|
||||
if (node is Item) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.rs09.consts.Items
|
||||
import rs09.game.content.ame.RandomEventManager
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class CerterEventInterface : InterfaceListener() {
|
||||
class CerterEventInterface : InterfaceListener {
|
||||
val CERTER_INTERFACE = 184
|
||||
val OPTION_A_CHILD = 1
|
||||
val OPTION_B_CHILD = 2
|
||||
@@ -31,7 +31,7 @@ class CerterEventInterface : InterfaceListener() {
|
||||
Items.NULL_6198 to "A necklace."
|
||||
)
|
||||
val falseOptions = arrayOf("An axe.", "An arrow.", "A pair of boots.", "A pair of gloves.", "A staff.", "A bow.", "A feather.", "The disenfrachaised youth of 1940's Columbia.")
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(CERTER_INTERFACE) { player, _, _, buttonID, _, _ ->
|
||||
val answer = buttonID - 7
|
||||
val correctAnswer = player.getAttribute("certer:correctIndex", 0)
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@ import core.game.content.dialogue.FacialExpression
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import rs09.game.content.global.shops.Shops
|
||||
import rs09.game.system.config.ShopParser
|
||||
|
||||
/**
|
||||
@@ -116,8 +117,8 @@ class GabootyDialogue(player: Player? = null) : DialoguePlugin(player){
|
||||
"you know, a little more of this, a little less of that, it all",
|
||||
"adds up and makes for an interesting tipple!").also { stage = 23 }
|
||||
|
||||
90 -> end().also { ShopParser.Companion.openUid(player,226) }
|
||||
91 -> end().also { ShopParser.Companion.openUid(player,227) }
|
||||
90 -> end().also { Shops.openId(player,226) }
|
||||
91 -> end().also { Shops.openId(player,227) }
|
||||
99 -> end()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import rs09.game.system.config.ItemConfigParser
|
||||
* @author Ceikry
|
||||
* @author Woah
|
||||
*/
|
||||
class EquipHandler : InteractionListener() {
|
||||
class EquipHandler : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
|
||||
@@ -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>()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -48,7 +48,7 @@ class GiftRollPlugin : XPGainPlugin() {
|
||||
}
|
||||
}
|
||||
|
||||
class XMASMboxHandler : InteractionListener() {
|
||||
class XMASMboxHandler : InteractionListener {
|
||||
val MBOX = Items.MYSTERY_BOX_6199
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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 ->
|
||||
|
||||
+10
-10
@@ -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
|
||||
|
||||
-40
@@ -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
|
||||
}
|
||||
}
|
||||
-92
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
-29
@@ -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
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -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)
|
||||
|
||||
-39
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+176
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -7,6 +7,7 @@ import core.game.content.dialogue.FacialExpression
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Initializable
|
||||
import core.tools.RandomFunction
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
@@ -21,6 +22,7 @@ import java.util.concurrent.TimeUnit
|
||||
/**
|
||||
* Dialogue for the star sprite.
|
||||
*/
|
||||
@Initializable
|
||||
class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
/**
|
||||
@@ -70,7 +72,7 @@ class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
} else if (inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && getAttribute(player, "star-ring:bp-shown", false)) {
|
||||
playerl(FacialExpression.HALF_ASKING, "So about those rings...")
|
||||
stage = 2000
|
||||
} else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarOptionHandler.STAR_DUST, 1)) {
|
||||
} else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarPlugin.STAR_DUST, 1)) {
|
||||
npc("Hello, strange creature.")
|
||||
stage = 0
|
||||
} else {
|
||||
@@ -185,8 +187,8 @@ class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
41 -> end()
|
||||
50 -> {
|
||||
val wearingRing = inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)
|
||||
val dust = if (player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST)
|
||||
if (player.getInventory().remove(Item(ShootingStarOptionHandler.STAR_DUST, dust))) {
|
||||
val dust = if (player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST)
|
||||
if (player.getInventory().remove(Item(ShootingStarPlugin.STAR_DUST, dust))) {
|
||||
val cosmicRunes = (Math.ceil(0.76 * dust) * AMPLIFIER).toInt()
|
||||
val astralRunes = (Math.ceil(0.26 * dust) * AMPLIFIER).toInt()
|
||||
val goldOre = (Math.ceil(0.1 * dust) * AMPLIFIER).toInt()
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit
|
||||
* Handles the work-for actions for the NPCs
|
||||
* @author Ceikry
|
||||
*/
|
||||
class WorkForInteractionListener : InteractionListener(), LoginListener {
|
||||
class WorkForInteractionListener : InteractionListener, LoginListener {
|
||||
val possibleWeaponLooks = arrayListOf(
|
||||
Items.BRONZE_SCIMITAR_1321,
|
||||
Items.STEEL_SCIMITAR_1325,
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
|
||||
+1
-1
@@ -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))
|
||||
|
||||
+1
-1
@@ -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))
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ class MonasteryMonkDialogue : DialogueFile() {
|
||||
* Handles BrotherCedricListener to launch the dialogue
|
||||
* @author Kya
|
||||
*/
|
||||
class MonasteryMonkListener : InteractionListener() {
|
||||
class MonasteryMonkListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.MONK_281, NPC, "talk-to"){ player, _ ->
|
||||
player.dialogueInterpreter.open(MonasteryMonkDialogue(), NPC(NPCs.MONK_281))
|
||||
|
||||
@@ -14,13 +14,14 @@ import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.content.global.action.PickupHandler
|
||||
import rs09.game.content.global.shops.Shops
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.node.entity.npc.other.MortMyreGhastNPC
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.config.ShopParser
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
class NSListeners : InteractionListener() {
|
||||
class NSListeners : InteractionListener {
|
||||
|
||||
val GROTTO_TREE = 3517
|
||||
val GROTTO_ENTRANCE = 3516
|
||||
@@ -101,7 +102,7 @@ class NSListeners : InteractionListener() {
|
||||
|
||||
on(WISHING_WELL, SCENERY, "make-wish"){player, node ->
|
||||
if(player.questRepository.isComplete("Nature Spirit") && player.questRepository.isComplete("Wolf Whistle"))
|
||||
ShopParser.openUid(player, 241)
|
||||
Shops.openId(player, 241)
|
||||
else
|
||||
sendDialogue(player, "You can't do that yet.")
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import rs09.game.interaction.InteractionListener
|
||||
* Handles tutorial-specific node interactions
|
||||
* @author Ceikry
|
||||
*/
|
||||
class TutorialListeners : InteractionListener() {
|
||||
class TutorialListeners : InteractionListener {
|
||||
val GUIDE_HOUSE_DOOR = 3014
|
||||
val COOKS_DOOR = 3017
|
||||
val COOKS_EXIT = 3018
|
||||
|
||||
@@ -6,75 +6,52 @@ import core.game.node.entity.Entity
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneBuilder
|
||||
import core.plugin.Initializable
|
||||
import core.plugin.Plugin
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.tools.secondsToTicks
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Initializable
|
||||
class FarmingPatchZone : MapZone("farming patch", true), Plugin<Any> {
|
||||
class FarmingPatchZone : MapArea, TickListener {
|
||||
private val playersInZone = hashMapOf<Player,Int>()
|
||||
|
||||
val playersInZone = hashMapOf<Player,Int>()
|
||||
|
||||
override fun configure() {
|
||||
registerRegion(12083)
|
||||
registerRegion(10548)
|
||||
register(ZoneBorders(3594,3521,3608,3532))
|
||||
submitWorldPulse(zonePulse)
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(
|
||||
getRegionBorders(12083),
|
||||
getRegionBorders(10548),
|
||||
ZoneBorders(3594,3521,3608,3532)
|
||||
)
|
||||
}
|
||||
|
||||
override fun newInstance(arg: Any?): Plugin<Any> {
|
||||
ZoneBuilder.configure(this)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
|
||||
return Unit
|
||||
}
|
||||
|
||||
override fun enter(e: Entity?): Boolean {
|
||||
if(e is Player && playersInZone[e] == null && getStatLevel(e, Skills.FARMING) <= 15) {
|
||||
playersInZone[e] = 0
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if(entity is Player && playersInZone[entity] == null && getStatLevel(entity, Skills.FARMING) <= 15) {
|
||||
playersInZone[entity] = 0
|
||||
}
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun leave(e: Entity?, logout: Boolean): Boolean {
|
||||
if(e is Player){
|
||||
playersInZone.remove(e)
|
||||
}
|
||||
return super.enter(e)
|
||||
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||
if(entity is Player)
|
||||
playersInZone.remove(entity)
|
||||
}
|
||||
|
||||
val zonePulse = object : Pulse(){
|
||||
override fun pulse(): Boolean {
|
||||
playersInZone.toList().forEach { (player, ticks) ->
|
||||
if(ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(5).toInt())){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(true), npc)
|
||||
} else if (ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(10).toInt())){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(false), npc)
|
||||
playersInZone.remove(player)
|
||||
}
|
||||
|
||||
playersInZone[player] = ticks + 1
|
||||
override fun tick() {
|
||||
playersInZone.toList().forEach { (player, ticks) ->
|
||||
if(ticks == 500){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(true), npc)
|
||||
} else if (ticks == 1000){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(false), npc)
|
||||
playersInZone.remove(player)
|
||||
}
|
||||
return false
|
||||
|
||||
playersInZone[player] = ticks + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,5 +71,4 @@ class FarmingPatchZone : MapZone("farming patch", true), Plugin<Any> {
|
||||
poofClear(npc ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import core.tools.RandomFunction
|
||||
*/
|
||||
val SINISTER_CHEST_HERBS = arrayOf(Item(205, 2), Item(207, 3), Item(209), Item(211), Item(213), Item(219))
|
||||
|
||||
public class YanilleAgilityDungeonListeners : InteractionListener() {
|
||||
public class YanilleAgilityDungeonListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
ZoneBuilder.configure(YanilleAgilityDungeon());
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class OrdanDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
* just comment those options out if you don't want em
|
||||
* aka "WOW I CAN'T BELIEVE IT'S ALL CONDITIONALS*/
|
||||
|
||||
class OrdanUnnoteListener : InteractionListener() {
|
||||
class OrdanUnnoteListener : InteractionListener {
|
||||
val notedOre = intArrayOf(
|
||||
Items.IRON_ORE_441,
|
||||
Items.COPPER_ORE_437,
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ private val FILL_ANIM = Animation(1649)
|
||||
private val WIND_ANIM = Animation(1648)
|
||||
private val SCOOP_ANIM = Animation(1650)
|
||||
|
||||
class BoneGrinderListener : InteractionListener() {
|
||||
class BoneGrinderListener : InteractionListener {
|
||||
|
||||
private val boneIDs = Bones.values().map { it.itemId }.toIntArray()
|
||||
|
||||
|
||||
@@ -2,34 +2,13 @@ package rs09.game.ge
|
||||
|
||||
import api.*
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.game.component.CloseEvent
|
||||
import core.game.component.Component
|
||||
import core.game.component.InterfaceType
|
||||
import core.game.container.Container
|
||||
import core.game.container.ContainerEvent
|
||||
import core.game.container.ContainerListener
|
||||
import core.game.container.access.BitregisterAssembler
|
||||
import core.game.container.access.InterfaceContainer
|
||||
import core.game.ge.GEItemSet
|
||||
import core.game.ge.GrandExchangeDatabase
|
||||
import core.game.ge.GrandExchangeEntry
|
||||
import core.game.ge.OfferState
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.monitor.PlayerMonitor
|
||||
import core.net.packet.PacketRepository
|
||||
import core.net.packet.context.ConfigContext
|
||||
import core.net.packet.context.ContainerContext
|
||||
import core.net.packet.out.Config
|
||||
import core.net.packet.out.ContainerPacket
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.ai.AIPlayer
|
||||
import rs09.game.system.SystemLogger
|
||||
import java.text.DecimalFormat
|
||||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -98,12 +77,12 @@ class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer,
|
||||
return
|
||||
}
|
||||
player.interfaceManager.openComponent(Components.STOCKCOLLECT_109)
|
||||
player.packetDispatch.sendAccessMask(6, 18, 109, 0, 2)
|
||||
player.packetDispatch.sendAccessMask(6, 23, 109, 0, 2)
|
||||
player.packetDispatch.sendAccessMask(6, 28, 109, 0, 2)
|
||||
player.packetDispatch.sendAccessMask(6, 36, 109, 0, 2)
|
||||
player.packetDispatch.sendAccessMask(6, 44, 109, 0, 2)
|
||||
player.packetDispatch.sendAccessMask(6, 52, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 18, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 23, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 28, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 36, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 44, 109, 0, 2)
|
||||
player.packetDispatch.sendIfaceSettings(6, 52, 109, 0, 2)
|
||||
|
||||
visualizeRecords()
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import rs09.game.world.GameWorld
|
||||
* Handles the corporeal beast warning interface
|
||||
* @author Ceikry
|
||||
*/
|
||||
class CorporealBeastWarningInterface : InterfaceListener(){
|
||||
class CorporealBeastWarningInterface : InterfaceListener{
|
||||
|
||||
val COMPONENT_ID = 650
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(COMPONENT_ID,17){player,component,_,_,_,_ ->
|
||||
if(player.getAttribute("corp-beast-cave-delay",0) <= GameWorld.ticks) {
|
||||
player.properties.teleportLocation = player.location.transform(4, 0, 0).also { close(player,component) }
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package rs09.game.interaction
|
||||
|
||||
import api.ContentInterface
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Location
|
||||
|
||||
abstract class InteractionListener : Listener{
|
||||
companion object {
|
||||
val ITEM = 0
|
||||
val SCENERY = 1
|
||||
val NPC = 2
|
||||
val GROUNDITEM = 3
|
||||
}
|
||||
interface InteractionListener : ContentInterface{
|
||||
val ITEM: Int
|
||||
get() = 0
|
||||
val SCENERY: Int
|
||||
get() = 1
|
||||
val NPC: Int
|
||||
get() = 2
|
||||
val GROUNDITEM: Int
|
||||
get() = 3
|
||||
|
||||
fun on(id: Int, type: Int, vararg option: String,handler: (player: Player, node: Node) -> Boolean){
|
||||
InteractionListeners.add(id,type,option,handler)
|
||||
}
|
||||
@@ -65,4 +69,14 @@ abstract class InteractionListener : Listener{
|
||||
fun onDig(location: Location,method: (player: Player) -> Unit){
|
||||
SpadeDigListener.registerListener(location,method)
|
||||
}
|
||||
|
||||
fun defineListeners()
|
||||
|
||||
companion object
|
||||
{
|
||||
val ITEM = 0
|
||||
val SCENERY = 1
|
||||
val NPC = 2
|
||||
val GROUNDITEM = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
package rs09.game.interaction
|
||||
|
||||
import api.ContentInterface
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
abstract class InterfaceListener : Listener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to handle game interface interactions
|
||||
*
|
||||
* Interactions should be defined in the required [defineInterfaceListeners] method.
|
||||
*/
|
||||
interface InterfaceListener : ContentInterface {
|
||||
fun defineInterfaceListeners()
|
||||
|
||||
fun on(componentID: Int, buttonID: Int, handler: (player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int) -> Boolean){
|
||||
InterfaceListeners.add(componentID,buttonID,handler)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ package rs09.game.interaction
|
||||
import api.StartupListener
|
||||
|
||||
interface Listener : StartupListener {
|
||||
fun defineListeners()
|
||||
override fun startup() {
|
||||
defineListeners()
|
||||
}
|
||||
|
||||
fun defineListeners()
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class CreditShopInterface : InterfaceListener() {
|
||||
class CreditShopInterface : InterfaceListener {
|
||||
|
||||
val CREDIT_SHOP = Components.CREDIT_SHOP
|
||||
val TEXT_CHILD = 39
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(CREDIT_SHOP){player, component, opcode, buttonID, slot, itemID ->
|
||||
val item = getItem(buttonID)
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ val RING_3 = arrayOf('p','s','r','q')
|
||||
* Handles the fairy ring interface
|
||||
* @author Ceikry
|
||||
*/
|
||||
class FairyRingInterface : InterfaceListener(){
|
||||
class FairyRingInterface : InterfaceListener{
|
||||
|
||||
val RINGS = 734
|
||||
val TRAVEL_LOG = 735
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
|
||||
onOpen(RINGS){player, _ ->
|
||||
player.interfaceManager.openSingleTab(Component(TRAVEL_LOG))
|
||||
|
||||
@@ -11,14 +11,14 @@ import org.rs09.consts.Components
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
import rs09.game.world.GameWorld.settings
|
||||
|
||||
class MainGameInterface : InterfaceListener() {
|
||||
class MainGameInterface : InterfaceListener {
|
||||
val TOPLEVEL = Components.TOPLEVEL_548
|
||||
val TOPLEVEL_FS = Components.TOPLEVEL_FULLSCREEN_746
|
||||
val RUN_BUTTON = Components.TOPSTAT_RUN_750
|
||||
val FILTER_BUTTONS = Components.FILTERBUTTONS_751
|
||||
val REPORT_ABUSE = Components.SNAPSHOT_MAIN_553
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(FILTER_BUTTONS){player, _, _, buttonID, _, _ ->
|
||||
if(buttonID == 27)
|
||||
openReport(player)
|
||||
@@ -102,6 +102,7 @@ class MainGameInterface : InterfaceListener() {
|
||||
player.packetDispatch.sendMessage("You can't do this right now.")
|
||||
return
|
||||
}
|
||||
player.interfaceManager.close()
|
||||
player.interfaceManager.openWindowsPane(Component(755))
|
||||
val posHash = player.location.z shl 28 or (player.location.x shl 14) or player.location.y
|
||||
player.packetDispatch.sendScriptConfigs(622, posHash, "", 0)
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class NPCContactInterface : InterfaceListener() {
|
||||
class NPCContactInterface : InterfaceListener {
|
||||
val contactNPCs = arrayOf(NPCs.HONEST_JIMMY_4362, NPCs.BERT_3108, NPCs.ADVISOR_GHRIM_1375, NPCs.TURAEL_8273, NPCs.LANTHUS_1526, NPCs.SUMONA_7780, NPCs.MAZCHNA_8274, NPCs.DURADEL_8275, NPCs.VANNAKA_1597, NPCs.DARK_MAGE_2262, NPCs.CHAELDAR_1598, NPCs.CYRISUS_432, NPCs.LARRY_5424)
|
||||
val DialogueFiles = arrayOf<DialogueFile?>(
|
||||
/*TODO("Honest Jimmy"),
|
||||
@@ -25,7 +25,7 @@ class NPCContactInterface : InterfaceListener() {
|
||||
TODO("Larry")*/
|
||||
)
|
||||
val INTER = 429
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
|
||||
//Remove a bunch of the buttons/heads so that people don't
|
||||
//waste runes on spells that aren't implemented
|
||||
|
||||
@@ -46,9 +46,9 @@ object RulesAndInfo {
|
||||
}
|
||||
}
|
||||
|
||||
class RulesListener : InterfaceListener()
|
||||
class RulesListener : InterfaceListener
|
||||
{
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
onClose(384){player, _ ->
|
||||
if(!getAttribute(player, "rules:confirmed", false))
|
||||
runTask(player, 1) { RulesAndInfo.openFor(player); sendDialogue(player, "Please read the rules.") }
|
||||
|
||||
@@ -11,12 +11,12 @@ import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class SilverInterface : InterfaceListener() {
|
||||
class SilverInterface : InterfaceListener {
|
||||
|
||||
val IFACE = Components.CRAFTING_SILVER_CASTING_438
|
||||
val ANIM = getAnimation(899)
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen(IFACE){player, _ ->
|
||||
sendItemOnInterface(player, IFACE, 17, 1718)
|
||||
sendItemOnInterface(player, IFACE, 24, 1724)
|
||||
@@ -89,7 +89,7 @@ class SilverInterface : InterfaceListener() {
|
||||
}
|
||||
}
|
||||
|
||||
class SilverBarUseWith : InteractionListener() {
|
||||
class SilverBarUseWith : InteractionListener {
|
||||
val FURNACES = intArrayOf(2966, 3044, 3294, 4304, 6189, 11009, 11010, 11666, 12100, 12809, 18497, 18525, 18526, 21879, 22721, 26814, 28433, 28434, 30021, 30510, 36956, 37651)
|
||||
override fun defineListeners() {
|
||||
onUseWith(SCENERY, Items.SILVER_BAR_2355, *FURNACES){ player, _, _ ->
|
||||
|
||||
@@ -6,10 +6,10 @@ import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Components
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class TeleotherInterface : InterfaceListener() {
|
||||
class TeleotherInterface : InterfaceListener {
|
||||
val IFACE = Components.TELEPORT_OTHER_326
|
||||
|
||||
override fun defineListeners() {
|
||||
override fun defineInterfaceListeners() {
|
||||
on(IFACE){player, _, _, button, _, _ ->
|
||||
if(button == 5){
|
||||
player.lock(2)
|
||||
|
||||
@@ -13,8 +13,8 @@ import core.game.node.entity.player.Player
|
||||
import org.rs09.consts.Components
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class ExchangeItemSets : InterfaceListener() {
|
||||
override fun defineListeners() {
|
||||
class ExchangeItemSets : InterfaceListener {
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen(Components.EXCHANGE_ITEMSETS_645) { player, _ ->
|
||||
val listener: InventoryListener
|
||||
setAttribute(player, "ge-listener", InventoryListener(player).also { listener = it })
|
||||
|
||||
@@ -25,12 +25,12 @@ import rs09.game.system.SystemLogger
|
||||
* Handles the grand exchange interface (Stock Market)
|
||||
* @author Ceikry
|
||||
*/
|
||||
class StockMarket : InterfaceListener() {
|
||||
override fun defineListeners() {
|
||||
class StockMarket : InterfaceListener {
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen(Components.STOCKMARKET_105){player, _ ->
|
||||
player.packetDispatch.sendInterfaceConfig(105, 193, true)
|
||||
player.packetDispatch.sendAccessMask(6, 211, 105, -1, -1)
|
||||
player.packetDispatch.sendAccessMask(6, 209, 105, -1, -1)
|
||||
player.packetDispatch.sendIfaceSettings(6, 211, 105, -1, -1)
|
||||
player.packetDispatch.sendIfaceSettings(6, 209, 105, -1, -1)
|
||||
player.varpManager.get(1112).setVarbit(0, -1).send(player)
|
||||
return@onOpen true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import rs09.game.interaction.InteractionListener
|
||||
* Handles the bracelet of clay operate option.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class BraceletOfClayPlugin : InteractionListener() {
|
||||
class BraceletOfClayPlugin : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import rs09.game.interaction.InteractionListener
|
||||
* @author bushtail
|
||||
*/
|
||||
|
||||
class CadavaPotionListener : InteractionListener() {
|
||||
class CadavaPotionListener : InteractionListener {
|
||||
|
||||
var POTION = Items.CADAVA_POTION_756
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import api.*
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class DrinkBlamishOilListener : InteractionListener() {
|
||||
class DrinkBlamishOilListener : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.BLAMISH_OIL_1582, ITEM, "drink"){player, _ ->
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.*
|
||||
* Listener for enchanted jewellery options
|
||||
* @author Ceikry
|
||||
*/
|
||||
class EnchantedJewelleryListener : InteractionListener() {
|
||||
class EnchantedJewelleryListener : InteractionListener {
|
||||
val IDs: IntArray
|
||||
|
||||
init {
|
||||
|
||||
@@ -9,7 +9,7 @@ import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
class GodBookListeners : InteractionListener() {
|
||||
class GodBookListeners : InteractionListener {
|
||||
|
||||
val GB_SARADOMIN = Items.HOLY_BOOK_3840
|
||||
val GB_ZAMORAK = Items.UNHOLY_BOOK_3842
|
||||
|
||||
@@ -30,7 +30,7 @@ private const val LAUNCH_ANIMATION = 4547
|
||||
* Handles the grand tree pod options
|
||||
* @author Ceikry
|
||||
*/
|
||||
class GrandSeedPodHandler : InteractionListener() {
|
||||
class GrandSeedPodHandler : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.GRAND_SEED_POD_9469, ITEM, "squash", "launch"){player, _ ->
|
||||
|
||||
@@ -9,7 +9,7 @@ import rs09.game.content.global.WeightedItem
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
class ImplingJarListener : InteractionListener() {
|
||||
class ImplingJarListener : InteractionListener {
|
||||
|
||||
val JARS = ImplingLoot.values().map { it.jarId }.toIntArray()
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.interaction.InteractionListeners.run
|
||||
|
||||
class ItemQuestRequirementListener : InteractionListener() {
|
||||
class ItemQuestRequirementListener : InteractionListener {
|
||||
|
||||
private val fremennikIslesEquipment = intArrayOf(Items.HELM_OF_NEITIZNOT_10828, Items.HELM_OF_NEITIZNOT_E_12680, Items.HELM_OF_NEITIZNOT_CHARGED_12681)
|
||||
private val fremennikTrialsEquipment = intArrayOf(Items.BERSERKER_HELM_3751, Items.BERSERKER_HELM_13408, Items.BERSERKER_HELM_E_12674, Items.BERSERKER_HELM_CHARGED_12675,
|
||||
|
||||
@@ -11,7 +11,7 @@ import rs09.tools.END_DIALOGUE
|
||||
/**
|
||||
* Handles the Staff of the Raven's (2021 Hween Reward) Recolor Transformation
|
||||
*/
|
||||
class StaffOfTheRaven : InteractionListener() {
|
||||
class StaffOfTheRaven : InteractionListener {
|
||||
val ids = intArrayOf(14654, 14655, 14656)
|
||||
override fun defineListeners() {
|
||||
on(ids, ITEM, "recolor", "operate"){player, node ->
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package rs09.game.interaction.item
|
||||
|
||||
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 core.game.world.map.Location
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
import rs09.ServerStore
|
||||
import rs09.ServerStore.Companion.getBoolean
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.content.global.worldevents.WorldEvents
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStar
|
||||
import rs09.game.content.global.worldevents.shootingstar.ShootingStarEvent
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class StarRingListener : InteractionListener(){
|
||||
|
||||
val RING = Items.RING_OF_THE_STAR_SPRITE_14652
|
||||
|
||||
override fun defineListeners() {
|
||||
on(RING, ITEM, "rub", "operate"){player, node ->
|
||||
val star = WorldEvents.get("shooting-stars") as? ShootingStarEvent
|
||||
|
||||
if(star == null) sendDialogue(player, "There is currently no active star.").also { return@on true }
|
||||
|
||||
if(getStoreFile().getBoolean(player.username.toLowerCase())){
|
||||
sendDialogue(player, "The ring is still recharging.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
val condition: (Player) -> Boolean = when(star?.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.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.star))
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
internal 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)
|
||||
getStoreFile()[player.username.toLowerCase()] = true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getStoreFile(): JSONObject {
|
||||
return ServerStore.getArchive("daily-star-ring")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import rs09.game.interaction.InteractionListener
|
||||
* Interaction listener for the Toy Horsey item
|
||||
* @author Woah
|
||||
*/
|
||||
class ToyHorseListener : InteractionListener() {
|
||||
class ToyHorseListener : InteractionListener {
|
||||
|
||||
// Map of horse item ids to their correct emote
|
||||
val HORSEY_MAP = mapOf(
|
||||
|
||||
@@ -6,7 +6,7 @@ import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class ToyListeners : InteractionListener() {
|
||||
class ToyListeners : InteractionListener {
|
||||
companion object {
|
||||
val MARIONETTES = intArrayOf(Items.RED_MARIONETTE_6867, Items.GREEN_MARIONETTE_6866, Items.BLUE_MARIONETTE_6865)
|
||||
private val MARIONETTE_JUMP = Animation(3003)
|
||||
|
||||
@@ -5,7 +5,7 @@ import api.*
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class HaySackOnSpear : InteractionListener() {
|
||||
class HaySackOnSpear : InteractionListener {
|
||||
val HAYSACK = Items.HAY_SACK_6057
|
||||
val SPEAR = Items.BRONZE_SPEAR_1237
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class OilFishingRodListener : InteractionListener() {
|
||||
class OilFishingRodListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onUseWith(ITEM, Items.BLAMISH_OIL_1582, Items.FISHING_ROD_307) {player, used, with ->
|
||||
player.pulseManager.run(object : Pulse() {
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import rs09.game.interaction.InteractionListener
|
||||
import kotlin.collections.toIntArray
|
||||
import kotlin.math.min
|
||||
|
||||
class PoisonedWeaponListeners : InteractionListener() {
|
||||
class PoisonedWeaponListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
val poisons = intArrayOf(Items.WEAPON_POISON_187, Items.WEAPON_POISON_PLUS_5937, Items.WEAPON_POISON_PLUS_PLUS_5940)
|
||||
val poisonableItems = PoisonSets.itemMap.keys.toIntArray()
|
||||
|
||||
@@ -7,7 +7,7 @@ import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.tools.END_DIALOGUE
|
||||
|
||||
class TOTTHelmOnCape : InteractionListener() {
|
||||
class TOTTHelmOnCape : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onUseWith(ITEM, Items.SLAYER_HELMET_13263, Items.SLAYER_CAPE_9786, Items.SLAYER_CAPET_9787){ player, used, with ->
|
||||
val alreadyHasHelm = getAttribute(player, "cape_perks:tott:helmet-stored", false)
|
||||
|
||||
@@ -6,7 +6,7 @@ import core.game.node.entity.skill.Skills
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
class WatermelonOnSack : InteractionListener() {
|
||||
class WatermelonOnSack : InteractionListener {
|
||||
val SACK = Items.HAY_SACK_6058
|
||||
val WATERMELON = Items.WATERMELON_5982
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.region.examcentre.ArchaeologistcalExpertUsedOnDialogueFile
|
||||
import rs09.game.interaction.InteractionListener
|
||||
|
||||
open class ArchaeologicalExpertListener() : InteractionListener() {
|
||||
open class ArchaeologicalExpertListener() : InteractionListener {
|
||||
val staff = Items.ANCIENT_STAFF_4675
|
||||
val unidentifiedLiquid = Items.UNIDENTIFIED_LIQUID_702
|
||||
val nitroglycerin = Items.NITROGLYCERIN_703
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user