Port the walkto and following plugins to Kotlin

This commit is contained in:
Gary Tierney
2017-06-24 17:40:08 +01:00
parent c15f18c609
commit 9a8ed0d7a9
12 changed files with 252 additions and 36 deletions
@@ -85,7 +85,7 @@ public enum Direction {
int deltaX = next.getX() - current.getX(); int deltaX = next.getX() - current.getX();
int deltaY = next.getY() - current.getY(); int deltaY = next.getY() - current.getY();
return fromDeltas(deltaX, deltaY); return fromDeltas(Integer.signum(deltaX), Integer.signum(deltaY));
} }
/** /**
@@ -7,7 +7,7 @@ import org.apollo.game.model.Position;
* *
* @author Major * @author Major
*/ */
abstract class Heuristic { public abstract class Heuristic {
/** /**
* Estimates the value for this heuristic. * Estimates the value for this heuristic.
@@ -6,6 +6,8 @@ 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.entity.Player
import org.apollo.game.model.entity.setting.PrivilegeLevel import org.apollo.game.model.entity.setting.PrivilegeLevel
import org.apollo.game.model.event.EventListener
import org.apollo.game.model.event.PlayerEvent
import org.apollo.game.plugin.PluginContext import org.apollo.game.plugin.PluginContext
import org.apollo.net.message.Message import org.apollo.net.message.Message
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -18,19 +20,18 @@ abstract class KotlinPluginScript(private var world: World, val context: PluginC
var startListener: (World) -> Unit = { _ -> }; var startListener: (World) -> Unit = { _ -> };
var stopListener: (World) -> Unit = { _ -> }; var stopListener: (World) -> Unit = { _ -> };
protected fun <T : Message> on(type: () -> KClass<T>): KotlinMessageHandler<T> { fun <T : Message> on(type: () -> KClass<T>) = KotlinMessageHandler<T>(world, context, type.invoke())
return KotlinMessageHandler(world, context, type.invoke())
}
protected fun on_command(command: String, privileges: PrivilegeLevel): KotlinCommandHandler { fun <T : PlayerEvent> on_player_event(type: () -> KClass<T>) = KotlinPlayerEventHandler(world, type.invoke())
return KotlinCommandHandler(world, command, privileges)
}
protected fun start(callback: (World) -> Unit) { fun on_command(command: String, privileges: PrivilegeLevel) = KotlinCommandHandler(world, command, privileges)
fun start(callback: (World) -> Unit) {
this.startListener = callback this.startListener = callback
} }
protected fun stop(callback: (World) -> Unit) { fun stop(callback: (World) -> Unit) {
this.stopListener = callback this.stopListener = callback
} }
@@ -43,45 +44,59 @@ abstract class KotlinPluginScript(private var world: World, val context: PluginC
} }
} }
class KotlinMessageHandler<T : Message>(val world: World, val context: PluginContext, val type: KClass<T>) : MessageHandler<T>(world) { interface KotlinPlayerHandlerProxyTrait<S : Any> {
override fun handle(player: Player, message: T) { var callback: S.(Player) -> Unit
if (message.predicate()) { var predicate: S.() -> Boolean
message.function(player)
}
}
var function: T.(Player) -> Unit = { _ -> } fun where(predicate: S.() -> Boolean): KotlinPlayerHandlerProxyTrait<S> {
var predicate: T.() -> Boolean = { true }
fun where(predicate: T.() -> Boolean): KotlinMessageHandler<T> {
this.predicate = predicate this.predicate = predicate
return this return this
} }
fun then(function: T.(Player) -> Unit) { fun then(callback: S.(Player) -> Unit) {
this.function = function this.callback = callback
this.context.addMessageHandler(type.java, this) this.register()
} }
fun register()
fun handleProxy(player: Player, subject: S) {
if (subject.predicate()) {
subject.callback(player)
}
}
}
class KotlinPlayerEventHandler<T : PlayerEvent>(val world: World, val type: KClass<T>) :
KotlinPlayerHandlerProxyTrait<T>, EventListener<T> {
override var callback: T.(Player) -> Unit = {}
override var predicate: T.() -> Boolean = { true }
override fun handle(event: T) = handleProxy(event.player, event)
override fun register() = world.listenFor(type.java, this)
} }
class KotlinCommandListener(val level: PrivilegeLevel, val function: Command.(Player) -> Unit) : CommandListener(level) { class KotlinMessageHandler<T : Message>(val world: World, val context: PluginContext, val type: KClass<T>) :
KotlinPlayerHandlerProxyTrait<T>, MessageHandler<T>(world) {
override fun execute(player: Player, command: Command) { override var callback: T.(Player) -> Unit = {}
function.invoke(command, player) override var predicate: T.() -> Boolean = { true }
}
override fun handle(player: Player, message: T) = handleProxy(player, message)
override fun register() = context.addMessageHandler(type.java, this)
} }
class KotlinCommandHandler(val world : World, val command: String, val privileges: PrivilegeLevel) { class KotlinCommandHandler(val world: World, val command: String, privileges: PrivilegeLevel) :
KotlinPlayerHandlerProxyTrait<Command>, CommandListener(privileges) {
var function: Command.(Player) -> Unit = { _ -> } override var callback: Command.(Player) -> Unit = {}
override var predicate: Command.() -> Boolean = { true }
fun then(function: Command.(Player) -> Unit) { override fun execute(player: Player, command: Command) = handleProxy(player, command)
this.function = function override fun register() = world.commandDispatcher.register(command, this)
world.commandDispatcher.register(command, KotlinCommandListener(privileges, function))
}
} }
+8 -2
View File
@@ -6,16 +6,22 @@
* required to resolve references within plugin code. * required to resolve references within plugin code.
*/ */
import org.apollo.game.message.handler.MessageHandlerChainSet
import org.apollo.game.model.World import org.apollo.game.model.World
import org.apollo.game.model.area.RegionRepository
import org.apollo.game.model.entity.*
import org.apollo.game.model.entity.setting.PrivilegeLevel import org.apollo.game.model.entity.setting.PrivilegeLevel
import org.apollo.game.plugin.kotlin.KotlinCommandHandler import org.apollo.game.model.event.PlayerEvent
import org.apollo.game.plugin.kotlin.KotlinMessageHandler import org.apollo.game.plugin.kotlin.*
import org.apollo.net.message.Message import org.apollo.net.message.Message
import kotlin.reflect.KClass import kotlin.reflect.KClass
fun <T : Message> on(type: () -> KClass<T>): KotlinMessageHandler<T> { fun <T : Message> on(type: () -> KClass<T>): KotlinMessageHandler<T> {
null!! null!!
} }
fun <T : PlayerEvent> on_player_event(type: () -> KClass<T>): KotlinPlayerEventHandler<T> {
null!!
}
fun on_command(command: String, privileges: PrivilegeLevel): KotlinCommandHandler { fun on_command(command: String, privileges: PrivilegeLevel): KotlinCommandHandler {
null!! null!!
@@ -0,0 +1,8 @@
name = "following"
package = "org.apollo.game.plugin.entity"
authors = [ "Gary Tierney" ]
dependencies = [ "walkto", "command_utilities", "player_action"]
[config]
srcDir = "src/"
testDir = "test/"
@@ -0,0 +1,52 @@
package org.apollo.plugin.entity.following
import org.apollo.game.action.Action
import org.apollo.game.model.Direction
import org.apollo.game.model.Position
import org.apollo.game.model.entity.Mob
import org.apollo.game.model.entity.Player
import org.apollo.net.message.Message
import org.apollo.plugin.entity.walkto.walkBehind
import org.apollo.plugin.entity.walkto.walkTo
class FollowAction(player: Player, val target: Player) : Action<Player>(0, true, player) {
var lastPosition: Position? = null
companion object {
fun start(player: Player, target: Player, message: Message? = null) {
player.startAction(FollowAction(player, target))
message?.terminate()
}
}
override fun execute() {
if (!target.isActive) {
stop()
return
}
mob.interactingMob = target
if (target.position == lastPosition) {
return
}
val distance = mob.position.getDistance(target.position)
if (distance >= 15) {
stop()
return
}
if (mob.position == target.position) {
val directions = Direction.NESW
val directionOffset = (Math.random() * directions.size).toInt()
mob.walkTo(target.position.step(1, directions[directionOffset]))
return
}
mob.walkBehind(target)
lastPosition = target.position
}
}
@@ -0,0 +1,11 @@
import com.google.common.primitives.Ints
import org.apollo.game.message.impl.PlayerActionMessage
import org.apollo.game.model.entity.setting.PrivilegeLevel
import org.apollo.plugin.entity.following.FollowAction
on_player_event { PlayerActionEvent::class }
.where { action == PlayerActionType.FOLLOW }
.then {
FollowAction.start(it, target)
terminate()
}
@@ -0,0 +1,8 @@
name = "player_action"
package = "org.apollo.game.plugin.entity"
authors = [ "Gary Tierney" ]
dependencies = []
[config]
srcDir = "src/"
testDir = "test/"
@@ -0,0 +1,35 @@
import org.apollo.game.message.impl.SetPlayerActionMessage
import org.apollo.game.model.entity.Player
import org.apollo.game.model.event.PlayerEvent
import java.util.*
enum class PlayerActionType(val displayName: String, val slot: Int, val primary: Boolean = true) {
ATTACK("Attack", 2),
CHALLENGE("Challenge", 2),
FOLLOW("Follow", 4),
TRADE("Trade with", 5)
}
class PlayerActionEvent(player: Player, val target: Player, val action: PlayerActionType) : PlayerEvent(player)
private val playerActionsMap = mutableMapOf<Player, EnumSet<PlayerActionType>>()
private val Player.actions: EnumSet<PlayerActionType>
get() = playerActionsMap.computeIfAbsent(this, { EnumSet.noneOf(PlayerActionType::class.java) })
fun Player.enableAction(action: PlayerActionType) {
send(SetPlayerActionMessage(action.displayName, action.slot, action.primary))
actions.add(action)
}
fun Player.disableAction(action: PlayerActionType) {
send(SetPlayerActionMessage("null", action.slot, action.primary))
actions.remove(action)
}
fun Player.actionEnabled(action: PlayerActionType): Boolean {
return actions.contains(action)
}
fun Player.actionAt(slot: Int): PlayerActionType? {
return actions.find { it.slot == slot }
}
@@ -0,0 +1,18 @@
import org.apollo.game.message.impl.PlayerActionMessage
import org.apollo.game.model.event.impl.LoginEvent
on { PlayerActionMessage::class }
.then {
val action = it.actionAt(option)
if (action != null) {
it.world.submit(PlayerActionEvent(it, it.world.playerRepository[index], action))
}
terminate()
}
on_player_event { LoginEvent::class }
.then {
it.enableAction(PlayerActionType.FOLLOW)
it.enableAction(PlayerActionType.TRADE)
}
@@ -0,0 +1,4 @@
name = "walkto"
package = "org.apollo.plugin.entity.walkto"
authors = ["Gary Tierney"]
dependencies = []
@@ -0,0 +1,59 @@
package org.apollo.plugin.entity.walkto
import org.apollo.game.model.Direction
import org.apollo.game.model.Position
import org.apollo.game.model.entity.*
import org.apollo.game.model.entity.obj.GameObject
import org.apollo.game.model.entity.path.SimplePathfindingAlgorithm
private fun bounds(target: Entity): Pair<Int, Int> = when (target) {
is GameObject -> {
val orientation = Direction.WNES[target.orientation]
val rotated = (orientation == Direction.WEST || orientation == Direction.EAST)
val width = if (rotated) target.definition.length else target.definition.width
val height = if (rotated) target.definition.width else target.definition.length
Pair(width, height)
}
is Npc -> Pair(target.definition.size, target.definition.size)
is Player -> Pair(1, 1)
else -> error("Invalid entity type")
}
fun Mob.walkTo(target: Entity, positioningDirection: Direction? = null) {
val (sourceWidth, sourceHeight) = bounds(target)
val (targetWidth, targetHeight) = bounds(target)
val direction = positioningDirection ?: Direction.between(position, target.position)
val dx = direction.deltaX()
val dy = direction.deltaY()
val targetX = if (dx <= 0) target.position.x else target.position.x + targetWidth - 1
val targetY = if (dy <= 0) target.position.y else target.position.y + targetHeight - 1
val offsetX = if (dx < 0) -sourceWidth else if (dx > 0) 1 else 0
val offsetY = if (dy < 0) -sourceHeight else if (dy > 0) 1 else 0
walkTo(Position(targetX + offsetX, targetY + offsetY, position.height))
}
fun Mob.walkBehind(target: Mob) {
walkTo(target, target.lastDirection.opposite())
}
fun Mob.walkTo(target: Position, positionPredicate: (Position) -> Boolean = { true }) {
if (position == target) {
return
}
val pathfinder = SimplePathfindingAlgorithm(world.collisionManager)
val path = pathfinder.find(position, target)
for (step in path) {
if (!positionPredicate.invoke(step)) {
return
}
walkingQueue.addStep(step)
}
}