Actually utilising ActorAI interface to support AIs written in Kotlin as well as in Lua

Former-commit-id: 53901f5a5d53b33c4254091ec507be82289d57fd
Former-commit-id: 86ce8d2a646a1564ee5e33ef07c5affb338c028e
This commit is contained in:
Song Minjae
2017-02-04 22:53:36 +09:00
parent 2fd7487248
commit 5daf424554
6 changed files with 133 additions and 101 deletions

View File

@@ -1,12 +1,14 @@
package net.torvald.terrarum.gameactors
import net.torvald.terrarum.gameactors.ai.ActorAI
/**
* Note: AI-controlled actor must be 'Controllable'
*
* Created by minjaesong on 16-01-31.
*/
interface AIControlled {
val scriptPath: String
val ai: ActorAI
fun moveLeft(amount: Float = 1f)
fun moveRight(amount: Float = 1f)

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors
import net.torvald.terrarum.gameactors.ActorHumanoid
import net.torvald.terrarum.gameactors.ai.AILuaAPI
import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.gameitem.EquipPosition
import net.torvald.terrarum.gameitem.InventoryItem
import org.luaj.vm2.*
@@ -16,19 +17,14 @@ import java.io.InputStreamReader
import java.io.Reader
/**
* @param ai AI class. Use LuaAIWrapper for Lua script
*
* Created by minjaesong on 16-01-31.
*/
open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorHumanoid(born), AIControlled, CanBeAnItem {
protected val luag: Globals = JsePlatform.standardGlobals()
/**
* Initialised in init block.
* Use lua function "update(delta)" to step the AI.
*/
protected val luaInstance: LuaValue
private val aiLuaAPI: AILuaAPI
open class HumanoidNPC(
override val ai: ActorAI, // it's there for written-in-Kotlin, "hard-wired" AIs
born: GameDate
) : ActorHumanoid(born), AIControlled, CanBeAnItem {
companion object {
val DEFAULT_COLLISION_TYPE = ActorWithSprite.COLLISION_DYNAMIC
@@ -36,28 +32,16 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
init {
collisionType = DEFAULT_COLLISION_TYPE
luag["io"] = LuaValue.NIL
luag["os"] = LuaValue.NIL
luag["luajava"] = LuaValue.NIL
aiLuaAPI = AILuaAPI(luag, this)
// load the script and execute it (initialises target script)
val inputStream = javaClass.getResourceAsStream(scriptPath)
luaInstance = luag.load(InputStreamReader(inputStream), scriptPath.split(Regex("[\\/]")).last())
luaInstance.call()
}
// we're having InventoryItem data so that this class could be somewhat universal
override var itemData: InventoryItem = object : InventoryItem() {
override var id = referenceID
override val equipPosition: Int = EquipPosition.HAND_GRIP
override var mass: Double
override var baseMass: Double
get() = actorValue.getAsDouble(AVKey.BASEMASS)!!
set(value) {
actorValue[AVKey.BASEMASS] = value
}
set(value) { actorValue[AVKey.BASEMASS] = value }
override var baseToolSize: Double? = 0.0
override var scale: Double
get() = actorValue.getAsDouble(AVKey.SCALE)!!
set(value) {
@@ -85,9 +69,7 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
override fun update(gc: GameContainer, delta: Int) {
super.update(gc, delta)
// run "update()" function in the script
luag.get("update").call(delta.toLuaValue())
ai.update(delta)
}
override fun moveLeft(amount: Float) { // hit the buttons on the controller box
@@ -121,65 +103,4 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
// if your NPC should fly, override this
throw UnsupportedOperationException("Humans cannot fly :p")
}
var currentExecutionThread = Thread()
var threadRun = false
fun runCommand(reader: Reader, filename: String) {
if (!threadRun && !flagDespawn) {
currentExecutionThread = Thread(ThreadRunCommand(luag, reader, filename))
currentExecutionThread.start()
threadRun = true
}
}
fun runCommand(script: String) {
if (!threadRun && !flagDespawn) {
currentExecutionThread = Thread(ThreadRunCommand(luag, script, ""))
currentExecutionThread.start()
threadRun = true
}
}
class ThreadRunCommand : Runnable {
val mode: Int
val arg1: Any
val arg2: String
val lua: Globals
constructor(luaInstance: Globals, line: String, env: String) {
mode = 0
arg1 = line
arg2 = env
lua = luaInstance
}
constructor(luaInstance: Globals, reader: Reader, filename: String) {
mode = 1
arg1 = reader
arg2 = filename
lua = luaInstance
}
override fun run() {
try {
val chunk: LuaValue
if (mode == 0)
chunk = lua.load(arg1 as String, arg2)
else if (mode == 1)
chunk = lua.load(arg1 as Reader, arg2)
else
throw IllegalArgumentException("Unsupported mode: $mode")
chunk.call()
}
catch (e: LuaError) {
e.printStackTrace(System.err)
}
}
}
fun Int.toLuaValue(): LuaValue = LuaInteger.valueOf(this)
}

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.terrarum.gameactors.ActorHumanoid
import net.torvald.terrarum.gameactors.ai.LuaAIWrapper
import net.torvald.terrarum.mapdrawer.FeaturesDrawer
/**
@@ -11,9 +12,12 @@ object PlayerBuilderCynthia {
operator fun invoke(): ActorWithSprite {
//val p: Player = Player(GameDate(100, 143)) // random value thrown
val p: HumanoidNPC = HumanoidNPC("/net/torvald/terrarum/gameactors/ai/scripts/PokemonNPCAI.lua",
val p: HumanoidNPC = HumanoidNPC(
LuaAIWrapper("/net/torvald/terrarum/gameactors/ai/scripts/PokemonNPCAI.lua"),
GameDate(100, 143)) // random value thrown
InjectCreatureRaw(p.actorValue, "CreatureHuman.json")
(p.ai as LuaAIWrapper).attachActor(p)
p.actorValue[AVKey.__PLAYER_QUICKBARSEL] = 0
p.actorValue[AVKey.NAME] = "Cynthia"

View File

@@ -4,4 +4,5 @@ package net.torvald.terrarum.gameactors.ai
* Created by minjaesong on 16-03-02.
*/
interface ActorAI {
fun update(delta: Int)
}

View File

@@ -0,0 +1,113 @@
package net.torvald.terrarum.gameactors.ai
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithSprite
import org.luaj.vm2.Globals
import org.luaj.vm2.LuaError
import org.luaj.vm2.LuaInteger
import org.luaj.vm2.LuaValue
import org.luaj.vm2.lib.jse.JsePlatform
import java.io.InputStreamReader
import java.io.Reader
/**
* Created by SKYHi14 on 2017-02-04.
*/
class LuaAIWrapper(private val scriptPath: String) : ActorAI {
protected val luag: Globals = JsePlatform.standardGlobals()
/**
* Initialised in init block.
* Use lua function "update(delta)" to step the AI.
*/
protected lateinit var luaInstance: LuaValue
private lateinit var aiLuaAPI: AILuaAPI
private lateinit var targetActor: ActorWithSprite
/**
* The initialiser
*
* Use ```(p.ai as LuaAIWrapper).attachActor(p)```
*/
fun attachActor(actor: ActorWithSprite) {
targetActor = actor
luag["io"] = LuaValue.NIL
luag["os"] = LuaValue.NIL
luag["luajava"] = LuaValue.NIL
aiLuaAPI = AILuaAPI(luag, targetActor)
// load the script and execute it (initialises target script)
val inputStream = javaClass.getResourceAsStream(scriptPath)
luaInstance = luag.load(InputStreamReader(inputStream), scriptPath.split(Regex("[\\/]")).last())
luaInstance.call()
}
override fun update(delta: Int) {
// run "update()" function in the script
luag.get("update").call(delta.toLuaValue())
}
var currentExecutionThread = Thread()
var threadRun = false
fun runCommand(reader: Reader, filename: String) {
if (!threadRun && !targetActor.flagDespawn) {
currentExecutionThread = Thread(ThreadRunCommand(luag, reader, filename))
currentExecutionThread.start()
threadRun = true
}
}
fun runCommand(script: String) {
if (!threadRun && !targetActor.flagDespawn) {
currentExecutionThread = Thread(ThreadRunCommand(luag, script, ""))
currentExecutionThread.start()
threadRun = true
}
}
class ThreadRunCommand : Runnable {
val mode: Int
val arg1: Any
val arg2: String
val lua: Globals
constructor(luaInstance: Globals, line: String, env: String) {
mode = 0
arg1 = line
arg2 = env
lua = luaInstance
}
constructor(luaInstance: Globals, reader: Reader, filename: String) {
mode = 1
arg1 = reader
arg2 = filename
lua = luaInstance
}
override fun run() {
try {
val chunk: LuaValue
if (mode == 0)
chunk = lua.load(arg1 as String, arg2)
else if (mode == 1)
chunk = lua.load(arg1 as Reader, arg2)
else
throw IllegalArgumentException("Unsupported mode: $mode")
chunk.call()
}
catch (e: LuaError) {
e.printStackTrace(System.err)
}
}
}
fun Int.toLuaValue(): LuaValue = LuaInteger.valueOf(this)
}

View File

@@ -1,9 +0,0 @@
package net.torvald.terrarum.itemproperties
/**
* Created by minjaesong on 16-03-18.
*/
internal data class ItemProp (
var baseMass: Float,
var material: Material
)