This commit is contained in:
Gary Tierney
2019-08-04 02:00:48 +01:00
parent 12b4bef1f8
commit 8c50d3e091
20 changed files with 284 additions and 122 deletions
@@ -20,16 +20,9 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext)
private var stopListener: (World) -> Unit = { _ -> }
fun <T : Any, C : ListenableContext, I : PredicateContext> on(
listenable: Listenable<T, C, I>,
callback: C.() -> Unit
) {
registerListener(listenable, null, callback)
}
internal fun <T : Any, C : ListenableContext, I : PredicateContext> registerListener(
listenable: Listenable<T, C, I>,
predicateContext: I?,
predicateContext: I,
callback: C.() -> Unit
) {
// Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise
@@ -2,6 +2,7 @@ package org.apollo.game.plugin.kotlin
import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.model.World
import org.apollo.game.model.entity.Player
import org.apollo.game.model.event.Event
import org.apollo.game.model.event.EventListener
import org.apollo.net.message.Message
@@ -16,12 +17,20 @@ sealed class Listenable<T : Any, C : ListenableContext, P : PredicateContext> {
abstract class EventListenable<T : Event, C : ListenableContext, P : PredicateContext> : Listenable<T, C, P>() {
abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): EventListener<T>
abstract fun createHandler(world: World, predicateContext: P, callback: C.() -> Unit): EventListener<T>
}
abstract class MessageListenable<T : Message, C : ListenableContext, P : PredicateContext> : Listenable<T, C, P>() {
abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): MessageHandler<T>
protected fun handler(world: World, callback: (Player, T) -> Unit): MessageHandler<T> {
return object : MessageHandler<T>(world) {
override fun handle(player: Player, message: T) {
callback(player, message)
}
}
}
abstract fun createHandler(world: World, predicateContext: P, callback: C.() -> Unit): MessageHandler<T>
}
@@ -31,16 +31,16 @@ class ButtonClick(override val player: Player, val button: Int) : PlayerContext
companion object : MessageListenable<ButtonMessage, ButtonClick, ButtonPredicateContext>() {
override val type = ButtonMessage::class
override fun createHandler(
world: World,
predicateContext: ButtonPredicateContext?,
predicateContext: ButtonPredicateContext,
callback: ButtonClick.() -> Unit
): MessageHandler<ButtonMessage> {
return object : MessageHandler<ButtonMessage>(world) {
override fun handle(player: Player, message: ButtonMessage) {
if (predicateContext == null || predicateContext.button == message.widgetId) {
if (predicateContext.button == message.widgetId) {
val context = ButtonClick(player, message.widgetId)
context.callback()
}
@@ -0,0 +1,66 @@
import org.apollo.cache.def.NpcDefinition
import org.apollo.game.message.impl.NpcActionMessage
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.plugin.kotlin.KotlinPluginScript
import org.apollo.game.plugin.kotlin.message.action.npc.NpcAction
import org.apollo.game.plugin.kotlin.message.action.npc.NpcActionPredicateContext
import org.apollo.game.plugin.kotlin.message.action.obj.InteractiveObject
import org.apollo.game.plugin.kotlin.message.action.obj.ObjectAction
import org.apollo.game.plugin.kotlin.message.action.obj.ObjectActionPredicateContext
/**
* 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 <T : InteractiveObject> KotlinPluginScript.on(
listenable: ObjectAction.Companion,
option: String,
interactives: List<T>,
callback: ObjectAction<T>.() -> Unit
) {
val cb = callback as ObjectAction<*>.() -> Unit
registerListener(listenable, ObjectActionPredicateContext<T>(option, interactives.toList()), cb)
}
fun <T : InteractiveObject> KotlinPluginScript.on(
listenable: ObjectAction.Companion,
option: String,
objects: Array<T>,
callback: ObjectAction<T>.() -> Unit
) {
on(listenable, option, objects.toList(), callback)
}
/**
* Registers a listener for [NpcActionMessage]s that occur on any of the given [NpcDefinition]s using the
* given [option] (case-insensitive).
*
* ```
* on(NpcAction, option = "Talk-to", npcs = npcs("(Gnome )?Banker") {
* ...
* }
* ```
*/
fun KotlinPluginScript.on(
listenable: NpcAction.Companion,
option: String,
npcs: Sequence<NpcDefinition>,
callback: NpcAction.() -> Unit
) {
registerListener(listenable, NpcActionPredicateContext(option, npcs.toList()), callback)
}
@@ -0,0 +1,37 @@
package org.apollo.game.plugin.kotlin.message.action.npc
import org.apollo.cache.def.NpcDefinition
import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.message.impl.NpcActionMessage
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.World
import org.apollo.game.model.entity.Npc
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.message.action.ActionContext
import org.apollo.game.plugin.kotlin.message.action.obj.InteractiveObject
import org.apollo.game.plugin.kotlin.message.action.obj.ObjectAction
import org.apollo.game.plugin.kotlin.message.action.obj.ObjectActionPredicateContext
class NpcAction(override val option: String, override val player: Player, val target: Npc) : ActionContext {
companion object : MessageListenable<NpcActionMessage, NpcAction, NpcActionPredicateContext>() {
override val type = NpcActionMessage::class
override fun createHandler(world: World, predicateContext: NpcActionPredicateContext, callback: NpcAction.() -> Unit): MessageHandler<NpcActionMessage> {
val ids = predicateContext.npcDefinitions.map(NpcDefinition::getId).toSet()
return handler(world) { player, message ->
val npc = world.npcRepository[message.index]
val option = npc.definition.interactions[message.option]
val npcMatched = ids.isEmpty() || npc.id in ids
if (npcMatched && predicateContext.option.equals(option, ignoreCase = true)) {
val context = NpcAction(option, player, npc)
context.callback()
}
}
}
}
}
@@ -0,0 +1,10 @@
package org.apollo.game.plugin.kotlin.message.action.npc
import org.apollo.cache.def.NpcDefinition
import org.apollo.game.plugin.kotlin.message.action.ActionPredicateContext
import org.apollo.game.plugin.kotlin.message.action.obj.InteractiveObject
data class NpcActionPredicateContext(
override val option: String,
val npcDefinitions: List<NpcDefinition>
) : ActionPredicateContext(option)
@@ -9,6 +9,4 @@ interface InteractiveObject {
val id: Int
fun instanceOf(other: GameObject): Boolean // TODO alternative name?
}
@@ -31,30 +31,6 @@ fun <T : InteractiveObject> KotlinPluginScript.on(
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].
*/
@@ -71,35 +47,31 @@ class ObjectAction<T : InteractiveObject?>( // TODO split into two classes, one
override fun createHandler(
world: World,
predicateContext: ObjectActionPredicateContext<*>?,
predicateContext: ObjectActionPredicateContext<*>,
callback: ObjectAction<*>.() -> Unit
): MessageHandler<ObjectActionMessage> {
return object : MessageHandler<ObjectActionMessage>(world) {
val objectMap = predicateContext.objects.associateBy(InteractiveObject::id)
override fun handle(player: Player, message: ObjectActionMessage) {
val def = ObjectDefinition.lookup(message.id)
val option = def.menuActions[message.option]
return handler(world) { player, message ->
val def = ObjectDefinition.lookup(message.id)
val option = def.menuActions[message.option]
val target = world.regionRepository
.fromPosition(message.position)
.getEntities<GameObject>(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 target = world.regionRepository
.fromPosition(message.position)
.getEntities<GameObject>(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
.find { it.definition == def }
?: return@handler // Could happen if object was despawned this tick, before calling this handle function
val context = when { // Evaluation-order matters here.
predicateContext == null -> ObjectAction<InteractiveObject?>(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
val context = when { // Evaluation-order matters here.
!predicateContext.option.equals(option, ignoreCase = true) -> return@handler
objectMap.isEmpty() -> ObjectAction(player, option, target, null)
objectMap.containsKey(message.id) -> {
ObjectAction(player, option, target, objectMap[message.id])
}
context.callback()
else -> return@handler
}
context.callback()
}
}