diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinMessageHandler.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinMessageHandler.kt index 6882cc2e..799db9dc 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinMessageHandler.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinMessageHandler.kt @@ -7,19 +7,6 @@ import org.apollo.game.plugin.PluginContext import org.apollo.net.message.Message import kotlin.reflect.KClass -class KotlinMessageHandler( - world: World, - private val listenable: MessageListenable, - private val callback: T.() -> Unit -) : MessageHandler(world) { - - override fun handle(player: Player, message: F) { - val context = listenable.createContext(player, message) - context?.callback() - } - -} - /** * A handler for [Message]s. */ diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinPluginScript.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinPluginScript.kt index cadd4629..37bdfdfc 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinPluginScript.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/KotlinPluginScript.kt @@ -20,35 +20,35 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext) private var stopListener: (World) -> Unit = { _ -> } - fun on(listenable: Listenable, callback: C.() -> Unit) { + fun on( + listenable: Listenable, + callback: C.() -> Unit + ) { + registerListener(listenable, null, callback) + } + + internal fun registerListener( + listenable: Listenable, + predicateContext: I?, + callback: C.() -> Unit + ) { // Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise // pointless `l` values for now. return when (listenable) { is MessageListenable -> { @Suppress("UNCHECKED_CAST") - val l = listenable as MessageListenable + val l = listenable as MessageListenable - val handler = KotlinMessageHandler(world, l, callback) + val handler = l.createHandler(world, predicateContext, callback) context.addMessageHandler(l.type.java, handler) } - is PlayerEventListenable -> { - @Suppress("UNCHECKED_CAST") - val l = listenable as PlayerEventListenable - - world.listenFor(l.type.java) { event -> - val context = l.createContext(event) - context?.callback() - } - } is EventListenable -> { @Suppress("UNCHECKED_CAST") - val l = listenable as EventListenable + val l = listenable as EventListenable - world.listenFor(l.type.java) { event -> - val context = l.createContext(event) - context?.callback() - } + val handler = l.createHandler(world, predicateContext, callback) + world.listenFor(l.type.java, handler) } } } diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/Listenable.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/Listenable.kt index 14e9efbe..9a89fa64 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/Listenable.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/Listenable.kt @@ -1,32 +1,27 @@ package org.apollo.game.plugin.kotlin -import org.apollo.game.model.entity.Player +import org.apollo.game.message.handler.MessageHandler +import org.apollo.game.model.World import org.apollo.game.model.event.Event -import org.apollo.game.model.event.PlayerEvent +import org.apollo.game.model.event.EventListener import org.apollo.net.message.Message import kotlin.reflect.KClass /** * A game occurrence that can be listened to. */ -sealed class Listenable { +sealed class Listenable { abstract val type: KClass } -abstract class EventListenable : Listenable() { - abstract fun createContext(event: T): C? +abstract class EventListenable : Listenable() { + + abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): EventListener + } -abstract class MessageListenable : Listenable() { - abstract fun createContext(player: Player, message: T): C? +abstract class MessageListenable : Listenable() { + + abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): MessageHandler + } - -abstract class PlayerEventListenable : EventListenable() { - - abstract fun createContext(player: Player, event: T): C? - - final override fun createContext(event: T): C? { - return createContext(event.player, event) - } - -} \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/ListenableContext.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/ListenableContext.kt index 5f5fef95..760644a4 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/ListenableContext.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/ListenableContext.kt @@ -12,4 +12,6 @@ interface ListenableContext */ interface PlayerContext : ListenableContext { val player: Player -} \ No newline at end of file +} + +interface PredicateContext \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/ButtonClick.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/ButtonClick.kt index ae004e87..1b62dbf1 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/ButtonClick.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/ButtonClick.kt @@ -1,10 +1,13 @@ package org.apollo.game.plugin.kotlin.message +import org.apollo.game.message.handler.MessageHandler import org.apollo.game.message.impl.ButtonMessage +import org.apollo.game.model.World import org.apollo.game.model.entity.Player import org.apollo.game.plugin.kotlin.KotlinPluginScript import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.PlayerContext +import org.apollo.game.plugin.kotlin.PredicateContext /** * Registers a listener for [ButtonMessage]s that occur on the given [button] id. @@ -20,21 +23,34 @@ fun KotlinPluginScript.on( button: Int, callback: ButtonClick.() -> Unit ) { - on(listenable) { - if (this.button == button) { - callback() - } - } + registerListener(listenable, ButtonPredicateContext(button), callback) } class ButtonClick(override val player: Player, val button: Int) : PlayerContext { - companion object : MessageListenable() { - override val type = ButtonMessage::class + companion object : MessageListenable() { - override fun createContext(player: Player, message: ButtonMessage): ButtonClick { - return ButtonClick(player, message.widgetId) + override val type = ButtonMessage::class + + override fun createHandler( + world: World, + predicateContext: ButtonPredicateContext?, + callback: ButtonClick.() -> Unit + ): MessageHandler { + return object : MessageHandler(world) { + + override fun handle(player: Player, message: ButtonMessage) { + if (predicateContext == null || predicateContext.button == message.widgetId) { + val context = ButtonClick(player, message.widgetId) + context.callback() + } + } + + } } + } -} \ No newline at end of file +} + +class ButtonPredicateContext(val button: Int) : PredicateContext \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/ActionContext.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/ActionContext.kt index 95863ddd..e8ddfb4a 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/ActionContext.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/ActionContext.kt @@ -3,6 +3,7 @@ package org.apollo.game.plugin.kotlin.message.action import org.apollo.game.plugin.kotlin.KotlinPluginScript import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.PlayerContext +import org.apollo.game.plugin.kotlin.PredicateContext import org.apollo.net.message.Message /** @@ -14,18 +15,16 @@ import org.apollo.net.message.Message * } * ``` */ -inline fun KotlinPluginScript.on( - listenable: MessageListenable, +fun KotlinPluginScript.on( + listenable: MessageListenable, option: String, - crossinline callback: T.() -> Unit + callback: T.() -> Unit ) { - on(listenable) { - if (this.option.equals(option, ignoreCase = true)) { - callback() - } - } + registerListener(listenable, ActionPredicateContext(option), callback) } interface ActionContext : PlayerContext { val option: String } + +open class ActionPredicateContext(open val option: String) : PredicateContext \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectAction.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectAction.kt index db4bab0d..2bb0b790 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectAction.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectAction.kt @@ -1,12 +1,60 @@ package org.apollo.game.plugin.kotlin.message.action.obj import org.apollo.cache.def.ObjectDefinition +import org.apollo.game.message.handler.MessageHandler import org.apollo.game.message.impl.ObjectActionMessage +import org.apollo.game.model.World import org.apollo.game.model.entity.EntityType import org.apollo.game.model.entity.Player import org.apollo.game.model.entity.obj.GameObject +import org.apollo.game.plugin.kotlin.KotlinPluginScript +import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.message.action.ActionContext +/** + * Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the + * given [option] (case-insensitive). + * + * ``` + * on(ObjectAction, option = "Open", objects = DOORS.toList()) { + * player.sendMessage("You open the door.") + * } + * ``` + */ +fun KotlinPluginScript.on( + listenable: ObjectAction.Companion, + option: String, + objects: List, + callback: ObjectAction.() -> Unit +) { + @Suppress("UNCHECKED_CAST") (callback as ObjectAction<*>.() -> Unit) + registerListener(listenable, ObjectActionPredicateContext(option, objects), callback) +} + +/** + * Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the + * given [option] (case-insensitive). + * + * ``` + * on(ObjectAction, option = "Open", objects = DOORS.toList()) { + * player.sendMessage("You open the door.") + * } + * ``` + */ +fun KotlinPluginScript.on( + listenable: ObjectAction.Companion, + option: String, + callback: ObjectAction<*>.() -> Unit +) { + registerListener(listenable, ObjectActionPredicateContext(option, emptyList()), callback) +} + +fun KotlinPluginScript.x() { + on(ObjectAction, "walk") { + + } +} + /** * An interaction between a [Player] and an [interactive] [GameObject]. */ @@ -17,44 +65,44 @@ class ObjectAction( // TODO split into two classes, one val interactive: T ) : ActionContext { - companion object : ObjectActionListenable() { + companion object : MessageListenable, ObjectActionPredicateContext<*>>() { override val type = ObjectActionMessage::class - override fun createContext(player: Player, message: ObjectActionMessage): ObjectAction<*>? { - return create(player, message, objects = null) - } + override fun createHandler( + world: World, + predicateContext: ObjectActionPredicateContext<*>?, + callback: ObjectAction<*>.() -> Unit + ): MessageHandler { + return object : MessageHandler(world) { - override fun createContext( - player: Player, - other: ObjectActionMessage, - objects: List - ): ObjectAction? { - @Suppress("UNCHECKED_CAST") - return create(player, other, objects) as ObjectAction - } + override fun handle(player: Player, message: ObjectActionMessage) { + val def = ObjectDefinition.lookup(message.id) + val option = def.menuActions[message.option] - private fun create( - player: Player, - other: ObjectActionMessage, - objects: List? - ): ObjectAction<*>? { - val def = ObjectDefinition.lookup(other.id) - val selectedAction = def.menuActions[other.option] + val target = world.regionRepository + .fromPosition(message.position) + .getEntities(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT) + .find { it.definition == def } + ?: return // Could happen if object was despawned this tick, before calling this handle function - val obj = player.world.regionRepository - .fromPosition(other.position) - .getEntities(other.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT) - .find { it.definition == def } - ?: return null + val context = when { // Evaluation-order matters here. + predicateContext == null -> ObjectAction(player, option, target, null) + !predicateContext.option.equals(option, ignoreCase = true) -> return + predicateContext.objects.isEmpty() -> ObjectAction(player, option, target, null) + predicateContext.objects.any { it.instanceOf(target) } -> { + val interactive = predicateContext.objects.find { it.instanceOf(target) } ?: return + ObjectAction(player, option, target, interactive) + } + else -> return + } + + context.callback() + } - if (objects == null) { - return ObjectAction(player, selectedAction, obj, null) - } else { - val interactive = objects.find { it.instanceOf(obj) } ?: return null - return ObjectAction(player, selectedAction, obj, interactive) } } + } } diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionListenable.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionListenable.kt deleted file mode 100644 index bf9bfbf6..00000000 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionListenable.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.apollo.game.plugin.kotlin.message.action.obj - -import org.apollo.game.message.impl.ObjectActionMessage -import org.apollo.game.model.entity.Player -import org.apollo.game.plugin.kotlin.MessageListenable - -abstract class ObjectActionListenable : MessageListenable>() { - - override val type = ObjectActionMessage::class - - abstract fun createContext( - player: Player, - other: ObjectActionMessage, - objects: List - ): ObjectAction? - -} \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionMessageHandler.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionMessageHandler.kt deleted file mode 100644 index fa3e5de7..00000000 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionMessageHandler.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.apollo.game.plugin.kotlin.message.action.obj - -import org.apollo.cache.def.ObjectDefinition -import org.apollo.game.message.handler.MessageHandler -import org.apollo.game.message.impl.ObjectActionMessage -import org.apollo.game.model.World -import org.apollo.game.model.entity.EntityType -import org.apollo.game.model.entity.Player -import org.apollo.game.model.entity.obj.GameObject -import org.apollo.game.plugin.kotlin.KotlinPluginScript - -/** - * Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the - * given [option] (case-insensitive). - * - * ``` - * on(ObjectAction, option = "Open", objects = DOORS.toList()) { - * player.sendMessage("You open the door.") - * } - * ``` - */ -fun KotlinPluginScript.on( - listenable: ObjectActionListenable, - option: String, - objects: List, - callback: ObjectAction.() -> Unit -) { - if (objects.isEmpty()) { - on(listenable) { - @Suppress("UNCHECKED_CAST") (callback as ObjectAction<*>.() -> Unit) - callback(this) - } - } else { - val handler = ObjectActionMessageHandler(world, listenable, objects, option, callback) - context.addMessageHandler(listenable.type.java, handler) - } -} - -class ObjectActionMessageHandler( - world: World, - private val listenable: ObjectActionListenable, - private val objects: List, - private val option: String, - private val callback: ObjectAction.() -> Unit -) : MessageHandler(world) { - - override fun handle(player: Player, message: ObjectActionMessage) { - val def = ObjectDefinition.lookup(message.id) - val selectedAction = def.menuActions[message.option] - - val obj = player.world.regionRepository - .fromPosition(message.position) - .getEntities(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT) - .first { it.definition == def } - - if (option.equals(selectedAction, ignoreCase = true) && objects.any { it.instanceOf(obj) }) { - val context = listenable.createContext(player, message, objects) - context?.callback() - } - } - -} \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionPredicateContext.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionPredicateContext.kt new file mode 100644 index 00000000..42a64399 --- /dev/null +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/ObjectActionPredicateContext.kt @@ -0,0 +1,8 @@ +package org.apollo.game.plugin.kotlin.message.action.obj + +import org.apollo.game.plugin.kotlin.message.action.ActionPredicateContext + +data class ObjectActionPredicateContext( + override val option: String, + val objects: List +) : ActionPredicateContext(option) \ No newline at end of file