mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-07 16:49:12 +00:00
WIP
This commit is contained in:
@@ -31,11 +31,6 @@ dependencies {
|
|||||||
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: powermockVersion
|
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: powermockVersion
|
||||||
testImplementation group: 'org.assertj', name: 'assertj-core', version: assertjVersion
|
testImplementation group: 'org.assertj', name: 'assertj-core', version: assertjVersion
|
||||||
|
|
||||||
project(":game:plugin").subprojects { pluginProject ->
|
|
||||||
if (pluginProject.buildFile.exists()) {
|
|
||||||
runtimeClasspath pluginProject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationDistribution.from("$rootDir/data") {
|
applicationDistribution.from("$rootDir/data") {
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.apollo.game.plugin.detekt.ApolloPluginRuleSetProvider
|
||||||
+16
@@ -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()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -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"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
class ExampleDeclaration {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import java.lang.IllegalArgumentException
|
|||||||
import org.apollo.cache.def.ItemDefinition
|
import org.apollo.cache.def.ItemDefinition
|
||||||
import org.apollo.cache.def.NpcDefinition
|
import org.apollo.cache.def.NpcDefinition
|
||||||
import org.apollo.cache.def.ObjectDefinition
|
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
|
* Provides plugins with access to item, npc, and object definitions
|
||||||
@@ -76,14 +78,18 @@ object Definitions {
|
|||||||
return findEntity(NpcDefinition::getDefinitions, NpcDefinition::getName, name)
|
return findEntity(NpcDefinition::getDefinitions, NpcDefinition::getName, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun npcs(@Language("RegExp") pattern: String): Sequence<NpcDefinition> {
|
||||||
|
return findEntities(NpcDefinition::getDefinitions, NpcDefinition::getName, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [Regex] used to match 'names' that have an id attached to the end.
|
* The [Regex] used to match 'names' that have an id attached to the end.
|
||||||
*/
|
*/
|
||||||
private val ID_REGEX = Regex(".+_[0-9]+$")
|
private val ID_REGEX = Regex(".+_[0-9]+$")
|
||||||
|
|
||||||
private fun <T : Any> findEntity(
|
private inline fun <T : Any> findEntity(
|
||||||
definitionsProvider: () -> Array<T>,
|
definitionsProvider: () -> Array<T>,
|
||||||
nameSupplier: T.() -> String,
|
crossinline nameSupplier: T.() -> String,
|
||||||
name: String
|
name: String
|
||||||
): T? {
|
): T? {
|
||||||
val definitions = definitionsProvider()
|
val definitions = definitionsProvider()
|
||||||
@@ -98,7 +104,22 @@ object Definitions {
|
|||||||
return definitions[id]
|
return definitions[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
val normalizedName = name.replace('_', ' ')
|
return findEntities(definitionsProvider, nameSupplier, name).firstOrNull()
|
||||||
return definitions.firstOrNull { it.nameSupplier().equals(normalizedName, ignoreCase = true) }
|
}
|
||||||
|
|
||||||
|
private inline fun <T: Any> findEntities(
|
||||||
|
definitionsProvider: () -> Array<T>,
|
||||||
|
crossinline nameSupplier: T.() -> String,
|
||||||
|
regexp: String
|
||||||
|
) : Sequence<T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ dependencies {
|
|||||||
implementation project(':cache')
|
implementation project(':cache')
|
||||||
implementation project(':net')
|
implementation project(':net')
|
||||||
implementation project(':util')
|
implementation project(':util')
|
||||||
|
implementation project(':game:plugin:api')
|
||||||
|
|
||||||
testImplementation project(':game:plugin-testing')
|
testImplementation project(':game:plugin-testing')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,31 @@
|
|||||||
import org.apollo.game.action.DistancedAction
|
import org.apollo.game.action.DistancedAction
|
||||||
import org.apollo.game.message.impl.NpcActionMessage
|
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.Position
|
||||||
import org.apollo.game.model.entity.Npc
|
import org.apollo.game.model.entity.Npc
|
||||||
import org.apollo.game.model.entity.Player
|
import org.apollo.game.model.entity.Player
|
||||||
import org.apollo.game.model.inter.bank.BankUtils
|
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
|
import org.apollo.net.message.Message
|
||||||
|
|
||||||
val BANK_BOOTH_ID = 2213
|
enum class QuickBankObject(override val id: Int) : InteractiveObject {
|
||||||
|
BankBooth(2213)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
on(ObjectAction, "Open Bank", objects = QuickBankObject.values()) {
|
||||||
* Hook into the [ObjectActionMessage] and listen for when a bank booth's second action ("Open Bank") is selected.
|
BankAction.start(player, target.position)
|
||||||
*/
|
}
|
||||||
on { ObjectActionMessage::class }
|
|
||||||
.where { option == 2 && id == BANK_BOOTH_ID }
|
val bankerNpcs = npcs("(Gnome )?Banker")
|
||||||
.then { BankAction.start(this, it, position) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook into the [NpcActionMessage] and listen for when a banker's second action ("Open Bank") is selected.
|
* Hook into the [NpcActionMessage] and listen for when a banker's second action ("Open Bank") is selected.
|
||||||
*/
|
*/
|
||||||
on { NpcActionMessage::class }
|
on(NpcAction, "Open Bank", bankerNpcs) {
|
||||||
.where { option == 2 }
|
BankAction.start(player, target.position)
|
||||||
.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)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [DistancedAction] that opens a [Player]'s bank when they get close enough to a booth or banker.
|
* 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<Player>(0
|
|||||||
/**
|
/**
|
||||||
* Starts a [BankAction] for the specified [Player], terminating the [Message] that triggered.
|
* 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))
|
player.startAction(BankAction(player, position))
|
||||||
message.terminate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
gradle.projectsEvaluated {
|
configure(subprojects.findAll { it.buildFile.exists() }) { subproj ->
|
||||||
configure(subprojects.findAll { it.buildFile.exists() }) { subproj ->
|
apply plugin: "kotlin"
|
||||||
apply from: "$rootDir/gradle/kotlin.gradle"
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
@@ -16,7 +15,7 @@ gradle.projectsEvaluated {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
tasks.withType(Test) {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +26,6 @@ gradle.projectsEvaluated {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation group: 'com.google.guava', name: 'guava', version: guavaVersion
|
kotlinScriptDef(":game")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import org.apollo.game.message.impl.ButtonMessage
|
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 WALK_BUTTON_ID = 152
|
||||||
val RUN_BUTTON_ID = 153
|
val RUN_BUTTON_ID = 153
|
||||||
|
|
||||||
on { ButtonMessage::class }
|
on(ButtonClick, WALK_BUTTON_ID) { player.toggleRunning() }
|
||||||
.where { widgetId == WALK_BUTTON_ID || widgetId == RUN_BUTTON_ID }
|
on(ButtonClick, RUN_BUTTON_ID) { player.toggleRunning() }
|
||||||
.then {
|
|
||||||
it.toggleRunning()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,16 +20,9 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext)
|
|||||||
|
|
||||||
private var stopListener: (World) -> Unit = { _ -> }
|
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(
|
internal fun <T : Any, C : ListenableContext, I : PredicateContext> registerListener(
|
||||||
listenable: Listenable<T, C, I>,
|
listenable: Listenable<T, C, I>,
|
||||||
predicateContext: I?,
|
predicateContext: I,
|
||||||
callback: C.() -> Unit
|
callback: C.() -> Unit
|
||||||
) {
|
) {
|
||||||
// Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise
|
// 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.message.handler.MessageHandler
|
||||||
import org.apollo.game.model.World
|
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.Event
|
||||||
import org.apollo.game.model.event.EventListener
|
import org.apollo.game.model.event.EventListener
|
||||||
import org.apollo.net.message.Message
|
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 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 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>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ class ButtonClick(override val player: Player, val button: Int) : PlayerContext
|
|||||||
|
|
||||||
override fun createHandler(
|
override fun createHandler(
|
||||||
world: World,
|
world: World,
|
||||||
predicateContext: ButtonPredicateContext?,
|
predicateContext: ButtonPredicateContext,
|
||||||
callback: ButtonClick.() -> Unit
|
callback: ButtonClick.() -> Unit
|
||||||
): MessageHandler<ButtonMessage> {
|
): MessageHandler<ButtonMessage> {
|
||||||
return object : MessageHandler<ButtonMessage>(world) {
|
return object : MessageHandler<ButtonMessage>(world) {
|
||||||
|
|
||||||
override fun handle(player: Player, message: ButtonMessage) {
|
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)
|
val context = ButtonClick(player, message.widgetId)
|
||||||
context.callback()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -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)
|
||||||
-2
@@ -9,6 +9,4 @@ interface InteractiveObject {
|
|||||||
|
|
||||||
val id: Int
|
val id: Int
|
||||||
|
|
||||||
fun instanceOf(other: GameObject): Boolean // TODO alternative name?
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+9
-37
@@ -31,30 +31,6 @@ fun <T : InteractiveObject> KotlinPluginScript.on(
|
|||||||
registerListener(listenable, ObjectActionPredicateContext(option, objects), callback)
|
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].
|
* An interaction between a [Player] and an [interactive] [GameObject].
|
||||||
*/
|
*/
|
||||||
@@ -71,12 +47,12 @@ class ObjectAction<T : InteractiveObject?>( // TODO split into two classes, one
|
|||||||
|
|
||||||
override fun createHandler(
|
override fun createHandler(
|
||||||
world: World,
|
world: World,
|
||||||
predicateContext: ObjectActionPredicateContext<*>?,
|
predicateContext: ObjectActionPredicateContext<*>,
|
||||||
callback: ObjectAction<*>.() -> Unit
|
callback: ObjectAction<*>.() -> Unit
|
||||||
): MessageHandler<ObjectActionMessage> {
|
): MessageHandler<ObjectActionMessage> {
|
||||||
return object : MessageHandler<ObjectActionMessage>(world) {
|
val objectMap = predicateContext.objects.associateBy(InteractiveObject::id)
|
||||||
|
|
||||||
override fun handle(player: Player, message: ObjectActionMessage) {
|
return handler(world) { player, message ->
|
||||||
val def = ObjectDefinition.lookup(message.id)
|
val def = ObjectDefinition.lookup(message.id)
|
||||||
val option = def.menuActions[message.option]
|
val option = def.menuActions[message.option]
|
||||||
|
|
||||||
@@ -84,23 +60,19 @@ class ObjectAction<T : InteractiveObject?>( // TODO split into two classes, one
|
|||||||
.fromPosition(message.position)
|
.fromPosition(message.position)
|
||||||
.getEntities<GameObject>(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
|
.getEntities<GameObject>(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
|
||||||
.find { it.definition == def }
|
.find { it.definition == def }
|
||||||
?: return // Could happen if object was despawned this tick, before calling this handle function
|
?: return@handler // Could happen if object was despawned this tick, before calling this handle function
|
||||||
|
|
||||||
val context = when { // Evaluation-order matters here.
|
val context = when { // Evaluation-order matters here.
|
||||||
predicateContext == null -> ObjectAction<InteractiveObject?>(player, option, target, null)
|
!predicateContext.option.equals(option, ignoreCase = true) -> return@handler
|
||||||
!predicateContext.option.equals(option, ignoreCase = true) -> return
|
objectMap.isEmpty() -> ObjectAction(player, option, target, null)
|
||||||
predicateContext.objects.isEmpty() -> ObjectAction(player, option, target, null)
|
objectMap.containsKey(message.id) -> {
|
||||||
predicateContext.objects.any { it.instanceOf(target) } -> {
|
ObjectAction(player, option, target, objectMap[message.id])
|
||||||
val interactive = predicateContext.objects.find { it.instanceOf(target) } ?: return
|
|
||||||
ObjectAction(player, option, target, interactive)
|
|
||||||
}
|
}
|
||||||
else -> return
|
else -> return@handler
|
||||||
}
|
}
|
||||||
|
|
||||||
context.callback()
|
context.callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user