From 8c50d3e091cf7225283a29f1cd41b78108c13f7b Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Sun, 4 Aug 2019 02:00:48 +0100 Subject: [PATCH] WIP --- game/build.gradle | 5 -- ...tlab.arturbosch.detekt.api.RuleSetProvider | 1 + .../detekt/ApolloPluginRuleSetProvider.kt | 16 +++++ .../detekt/rules/DeclarationInScriptRule.kt | 31 +++++++++ .../rules/DeclarationInScriptRuleTest.kt | 19 ++++++ .../bin/test/testData/example.kts | 3 + .../org/apollo/game/plugin/api/Definitions.kt | 29 ++++++-- game/plugin/bank/build.gradle | 1 + game/plugin/bank/src/bank.plugin.kts | 40 +++++------ game/plugin/build.gradle | 44 ++++++------- game/plugin/run/src/run.plugin.kts | 9 ++- .../game/plugin/kotlin/KotlinPluginScript.kt | 9 +-- .../apollo/game/plugin/kotlin/Listenable.kt | 13 +++- .../game/plugin/kotlin/message/ButtonClick.kt | 6 +- .../plugin/kotlin/message/action/actions.kt | 66 +++++++++++++++++++ .../kotlin/message/action/npc/NpcAction.kt | 37 +++++++++++ .../action/npc/NpcActionPredicateContext.kt | 10 +++ .../message/action/obj/InteractiveObject.kt | 2 - .../kotlin/message/action/obj/ObjectAction.kt | 62 +++++------------ gradle/kotlin.gradle | 3 +- 20 files changed, 284 insertions(+), 122 deletions(-) create mode 100644 game/plugin-detekt-rules/bin/main/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider create mode 100644 game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/ApolloPluginRuleSetProvider.kt create mode 100644 game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRule.kt create mode 100644 game/plugin-detekt-rules/bin/test/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRuleTest.kt create mode 100644 game/plugin-detekt-rules/bin/test/testData/example.kts create mode 100644 game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/actions.kt create mode 100644 game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcAction.kt create mode 100644 game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcActionPredicateContext.kt diff --git a/game/build.gradle b/game/build.gradle index 1a451eb9..d72505e2 100644 --- a/game/build.gradle +++ b/game/build.gradle @@ -31,11 +31,6 @@ dependencies { testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: powermockVersion testImplementation group: 'org.assertj', name: 'assertj-core', version: assertjVersion - project(":game:plugin").subprojects { pluginProject -> - if (pluginProject.buildFile.exists()) { - runtimeClasspath pluginProject - } - } } applicationDistribution.from("$rootDir/data") { diff --git a/game/plugin-detekt-rules/bin/main/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider b/game/plugin-detekt-rules/bin/main/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider new file mode 100644 index 00000000..3eb44c25 --- /dev/null +++ b/game/plugin-detekt-rules/bin/main/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider @@ -0,0 +1 @@ +org.apollo.game.plugin.detekt.ApolloPluginRuleSetProvider \ No newline at end of file diff --git a/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/ApolloPluginRuleSetProvider.kt b/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/ApolloPluginRuleSetProvider.kt new file mode 100644 index 00000000..c084d54b --- /dev/null +++ b/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/ApolloPluginRuleSetProvider.kt @@ -0,0 +1,16 @@ +package org.apollo.game.plugin.detekt + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.RuleSet +import io.gitlab.arturbosch.detekt.api.RuleSetProvider +import org.apollo.game.plugin.detekt.rules.DeclarationInScriptRule + +class ApolloPluginRuleSetProvider : RuleSetProvider { + override val ruleSetId = "apollo-plugin" + + override fun instance(config: Config): RuleSet { + return RuleSet(ruleSetId, listOf( + DeclarationInScriptRule() + )) + } +} \ No newline at end of file diff --git a/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRule.kt b/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRule.kt new file mode 100644 index 00000000..055b5fd1 --- /dev/null +++ b/game/plugin-detekt-rules/bin/main/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRule.kt @@ -0,0 +1,31 @@ +package org.apollo.game.plugin.detekt.rules + +import io.gitlab.arturbosch.detekt.api.* +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtObjectDeclaration + +class DeclarationInScriptRule : Rule() { + override val issue = Issue( + "DeclarationInScript", + Severity.CodeSmell, + "This rule reports a plugin file containing class or object declarations.", + Debt.FIVE_MINS + ) + + override fun visit(root: KtFile) { + super.visit(root) + + val script = root.script ?: return + val declarations = script.declarations.filter { it is KtClass || it is KtObjectDeclaration } + + declarations + .forEach { + report(CodeSmell( + issue, + Entity.from(it), + message = "Declaration of ${it.name} should live in a top-level file, not a script" + )) + } + } +} \ No newline at end of file diff --git a/game/plugin-detekt-rules/bin/test/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRuleTest.kt b/game/plugin-detekt-rules/bin/test/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRuleTest.kt new file mode 100644 index 00000000..f6ca6dae --- /dev/null +++ b/game/plugin-detekt-rules/bin/test/org/apollo/game/plugin/detekt/rules/DeclarationInScriptRuleTest.kt @@ -0,0 +1,19 @@ +package org.apollo.game.plugin.detekt.rules + +import io.gitlab.arturbosch.detekt.test.lint +import java.nio.file.Paths +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +internal class DeclarationInScriptRuleTest { + val rule = DeclarationInScriptRule() + + @Test + fun `Finds warning in script file`() { + val srcPath = Paths.get(this.javaClass.getResource("/testData/example.kts").toURI()) + val findings = rule.lint(srcPath) + + assertEquals(1, findings.size) + assertEquals("Declaration of ExampleDeclaration should live in a top-level file, not a script", findings[0].message) + } +} \ No newline at end of file diff --git a/game/plugin-detekt-rules/bin/test/testData/example.kts b/game/plugin-detekt-rules/bin/test/testData/example.kts new file mode 100644 index 00000000..0fb1729b --- /dev/null +++ b/game/plugin-detekt-rules/bin/test/testData/example.kts @@ -0,0 +1,3 @@ +class ExampleDeclaration { + +} \ No newline at end of file diff --git a/game/plugin/api/src/org/apollo/game/plugin/api/Definitions.kt b/game/plugin/api/src/org/apollo/game/plugin/api/Definitions.kt index f0873b99..627730cc 100644 --- a/game/plugin/api/src/org/apollo/game/plugin/api/Definitions.kt +++ b/game/plugin/api/src/org/apollo/game/plugin/api/Definitions.kt @@ -4,6 +4,8 @@ import java.lang.IllegalArgumentException import org.apollo.cache.def.ItemDefinition import org.apollo.cache.def.NpcDefinition import org.apollo.cache.def.ObjectDefinition +import org.intellij.lang.annotations.Language +import java.util.regex.Pattern /** * Provides plugins with access to item, npc, and object definitions @@ -76,14 +78,18 @@ object Definitions { return findEntity(NpcDefinition::getDefinitions, NpcDefinition::getName, name) } + fun npcs(@Language("RegExp") pattern: String): Sequence { + return findEntities(NpcDefinition::getDefinitions, NpcDefinition::getName, pattern) + } + /** * The [Regex] used to match 'names' that have an id attached to the end. */ private val ID_REGEX = Regex(".+_[0-9]+$") - private fun findEntity( + private inline fun findEntity( definitionsProvider: () -> Array, - nameSupplier: T.() -> String, + crossinline nameSupplier: T.() -> String, name: String ): T? { val definitions = definitionsProvider() @@ -98,7 +104,22 @@ object Definitions { return definitions[id] } - val normalizedName = name.replace('_', ' ') - return definitions.firstOrNull { it.nameSupplier().equals(normalizedName, ignoreCase = true) } + return findEntities(definitionsProvider, nameSupplier, name).firstOrNull() + } + + private inline fun findEntities( + definitionsProvider: () -> Array, + crossinline nameSupplier: T.() -> String, + regexp: String + ) : Sequence { + val definitions = definitionsProvider().asSequence() + val pattern = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE) + + return definitions.filter { + val name = it.nameSupplier() + val matcher = pattern.matcher(name) + + matcher.matches() + } } } \ No newline at end of file diff --git a/game/plugin/bank/build.gradle b/game/plugin/bank/build.gradle index 3a935139..81df4884 100644 --- a/game/plugin/bank/build.gradle +++ b/game/plugin/bank/build.gradle @@ -5,6 +5,7 @@ dependencies { implementation project(':cache') implementation project(':net') implementation project(':util') + implementation project(':game:plugin:api') testImplementation project(':game:plugin-testing') } diff --git a/game/plugin/bank/src/bank.plugin.kts b/game/plugin/bank/src/bank.plugin.kts index 0aef5ac1..1bcca95b 100644 --- a/game/plugin/bank/src/bank.plugin.kts +++ b/game/plugin/bank/src/bank.plugin.kts @@ -1,38 +1,31 @@ import org.apollo.game.action.DistancedAction import org.apollo.game.message.impl.NpcActionMessage -import org.apollo.game.message.impl.ObjectActionMessage import org.apollo.game.model.Position import org.apollo.game.model.entity.Npc import org.apollo.game.model.entity.Player import org.apollo.game.model.inter.bank.BankUtils +import org.apollo.game.plugin.api.Definitions.npcs +import org.apollo.game.plugin.kotlin.message.action.npc.NpcAction +import org.apollo.game.plugin.kotlin.message.action.obj.InteractiveObject +import org.apollo.game.plugin.kotlin.message.action.obj.ObjectAction import org.apollo.net.message.Message -val BANK_BOOTH_ID = 2213 +enum class QuickBankObject(override val id: Int) : InteractiveObject { + BankBooth(2213) +} -/** - * Hook into the [ObjectActionMessage] and listen for when a bank booth's second action ("Open Bank") is selected. - */ -on { ObjectActionMessage::class } - .where { option == 2 && id == BANK_BOOTH_ID } - .then { BankAction.start(this, it, position) } +on(ObjectAction, "Open Bank", objects = QuickBankObject.values()) { + BankAction.start(player, target.position) +} + +val bankerNpcs = npcs("(Gnome )?Banker") /** * Hook into the [NpcActionMessage] and listen for when a banker's second action ("Open Bank") is selected. */ -on { NpcActionMessage::class } - .where { option == 2 } - .then { - val npc = it.world.npcRepository[index] - - if (npc.id in BANKER_NPCS) { - BankAction.start(this, it, npc.position) - } - } - -/** - * The ids of all banker [Npcs][Npc]. - */ -val BANKER_NPCS = setOf(166, 494, 495, 496, 497, 498, 499, 1036, 1360, 1702, 2163, 2164, 2354, 2355, 2568, 2569, 2570) +on(NpcAction, "Open Bank", bankerNpcs) { + BankAction.start(player, target.position) +} /** * A [DistancedAction] that opens a [Player]'s bank when they get close enough to a booth or banker. @@ -51,9 +44,8 @@ class BankAction(player: Player, position: Position) : DistancedAction(0 /** * Starts a [BankAction] for the specified [Player], terminating the [Message] that triggered. */ - fun start(message: Message, player: Player, position: Position) { + fun start(player: Player, position: Position) { player.startAction(BankAction(player, position)) - message.terminate() } } diff --git a/game/plugin/build.gradle b/game/plugin/build.gradle index 24ba2cc8..7cfb4c7b 100644 --- a/game/plugin/build.gradle +++ b/game/plugin/build.gradle @@ -1,33 +1,31 @@ -gradle.projectsEvaluated { - configure(subprojects.findAll { it.buildFile.exists() }) { subproj -> - apply from: "$rootDir/gradle/kotlin.gradle" +configure(subprojects.findAll { it.buildFile.exists() }) { subproj -> + apply plugin: "kotlin" - sourceSets { - main { - kotlin { - srcDirs += "src" - } - } - - test { - kotlin { - srcDirs += "test" - } + sourceSets { + main { + kotlin { + srcDirs += "src" } } test { - useJUnitPlatform() - } - - tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - jvmTarget = "1.8" + kotlin { + srcDirs += "test" } } + } - dependencies { - implementation group: 'com.google.guava', name: 'guava', version: guavaVersion + tasks.withType(Test) { + useJUnitPlatform() + } + + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "1.8" } } -} \ No newline at end of file + + dependencies { + kotlinScriptDef(":game") + } +} diff --git a/game/plugin/run/src/run.plugin.kts b/game/plugin/run/src/run.plugin.kts index fa65f049..f52aaba0 100644 --- a/game/plugin/run/src/run.plugin.kts +++ b/game/plugin/run/src/run.plugin.kts @@ -1,10 +1,9 @@ import org.apollo.game.message.impl.ButtonMessage +import org.apollo.game.plugin.kotlin.message.ButtonClick +import org.apollo.game.plugin.kotlin.message.on val WALK_BUTTON_ID = 152 val RUN_BUTTON_ID = 153 -on { ButtonMessage::class } - .where { widgetId == WALK_BUTTON_ID || widgetId == RUN_BUTTON_ID } - .then { - it.toggleRunning() - } \ No newline at end of file +on(ButtonClick, WALK_BUTTON_ID) { player.toggleRunning() } +on(ButtonClick, RUN_BUTTON_ID) { player.toggleRunning() } 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 37bdfdfc..93cba3fe 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,16 +20,9 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext) private var stopListener: (World) -> Unit = { _ -> } - fun on( - listenable: Listenable, - callback: C.() -> Unit - ) { - registerListener(listenable, null, callback) - } - internal fun registerListener( listenable: Listenable, - predicateContext: I?, + predicateContext: I, callback: C.() -> Unit ) { // Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise 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 9a89fa64..be4838ae 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 @@ -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 { abstract class EventListenable : Listenable() { - abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): EventListener + abstract fun createHandler(world: World, predicateContext: P, callback: C.() -> Unit): EventListener } abstract class MessageListenable : Listenable() { - abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): MessageHandler + protected fun handler(world: World, callback: (Player, T) -> Unit): MessageHandler { + return object : MessageHandler(world) { + override fun handle(player: Player, message: T) { + callback(player, message) + } + } + } + + abstract fun createHandler(world: World, predicateContext: P, callback: C.() -> Unit): MessageHandler } 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 1b62dbf1..3a22b30c 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 @@ -31,16 +31,16 @@ class ButtonClick(override val player: Player, val button: Int) : PlayerContext companion object : MessageListenable() { override val type = ButtonMessage::class - + override fun createHandler( world: World, - predicateContext: ButtonPredicateContext?, + predicateContext: ButtonPredicateContext, callback: ButtonClick.() -> Unit ): MessageHandler { return object : MessageHandler(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() } diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/actions.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/actions.kt new file mode 100644 index 00000000..4550dec9 --- /dev/null +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/actions.kt @@ -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 KotlinPluginScript.on( + listenable: ObjectAction.Companion, + option: String, + interactives: List, + callback: ObjectAction.() -> Unit +) { + val cb = callback as ObjectAction<*>.() -> Unit + registerListener(listenable, ObjectActionPredicateContext(option, interactives.toList()), cb) +} + + +fun KotlinPluginScript.on( + listenable: ObjectAction.Companion, + option: String, + objects: Array, + callback: ObjectAction.() -> 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, + callback: NpcAction.() -> Unit +) { + registerListener(listenable, NpcActionPredicateContext(option, npcs.toList()), callback) +} \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcAction.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcAction.kt new file mode 100644 index 00000000..aac20745 --- /dev/null +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcAction.kt @@ -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() { + override val type = NpcActionMessage::class + + override fun createHandler(world: World, predicateContext: NpcActionPredicateContext, callback: NpcAction.() -> Unit): MessageHandler { + 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() + } + } + } + } +} diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcActionPredicateContext.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcActionPredicateContext.kt new file mode 100644 index 00000000..68a8cad4 --- /dev/null +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/npc/NpcActionPredicateContext.kt @@ -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 +) : ActionPredicateContext(option) \ No newline at end of file diff --git a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/InteractiveObject.kt b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/InteractiveObject.kt index 85e2c695..fdb9ae5e 100644 --- a/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/InteractiveObject.kt +++ b/game/src/main/kotlin/org/apollo/game/plugin/kotlin/message/action/obj/InteractiveObject.kt @@ -9,6 +9,4 @@ interface InteractiveObject { val id: Int - fun instanceOf(other: GameObject): Boolean // TODO alternative name? - } \ 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 2bb0b790..1612b7ff 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 @@ -31,30 +31,6 @@ fun 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( // TODO split into two classes, one override fun createHandler( world: World, - predicateContext: ObjectActionPredicateContext<*>?, + predicateContext: ObjectActionPredicateContext<*>, callback: ObjectAction<*>.() -> Unit ): MessageHandler { - return object : MessageHandler(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(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(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(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() } } diff --git a/gradle/kotlin.gradle b/gradle/kotlin.gradle index 04894ed9..7acbdf85 100644 --- a/gradle/kotlin.gradle +++ b/gradle/kotlin.gradle @@ -1,3 +1,4 @@ + kotlin { - + }