mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-10 10:34:06 +09:00
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:
@@ -1,12 +1,14 @@
|
|||||||
package net.torvald.terrarum.gameactors
|
package net.torvald.terrarum.gameactors
|
||||||
|
|
||||||
|
import net.torvald.terrarum.gameactors.ai.ActorAI
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: AI-controlled actor must be 'Controllable'
|
* Note: AI-controlled actor must be 'Controllable'
|
||||||
*
|
*
|
||||||
* Created by minjaesong on 16-01-31.
|
* Created by minjaesong on 16-01-31.
|
||||||
*/
|
*/
|
||||||
interface AIControlled {
|
interface AIControlled {
|
||||||
val scriptPath: String
|
val ai: ActorAI
|
||||||
|
|
||||||
fun moveLeft(amount: Float = 1f)
|
fun moveLeft(amount: Float = 1f)
|
||||||
fun moveRight(amount: Float = 1f)
|
fun moveRight(amount: Float = 1f)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors
|
|||||||
|
|
||||||
import net.torvald.terrarum.gameactors.ActorHumanoid
|
import net.torvald.terrarum.gameactors.ActorHumanoid
|
||||||
import net.torvald.terrarum.gameactors.ai.AILuaAPI
|
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.EquipPosition
|
||||||
import net.torvald.terrarum.gameitem.InventoryItem
|
import net.torvald.terrarum.gameitem.InventoryItem
|
||||||
import org.luaj.vm2.*
|
import org.luaj.vm2.*
|
||||||
@@ -16,19 +17,14 @@ import java.io.InputStreamReader
|
|||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param ai AI class. Use LuaAIWrapper for Lua script
|
||||||
|
*
|
||||||
* Created by minjaesong on 16-01-31.
|
* Created by minjaesong on 16-01-31.
|
||||||
*/
|
*/
|
||||||
open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorHumanoid(born), AIControlled, CanBeAnItem {
|
open class HumanoidNPC(
|
||||||
|
override val ai: ActorAI, // it's there for written-in-Kotlin, "hard-wired" AIs
|
||||||
protected val luag: Globals = JsePlatform.standardGlobals()
|
born: GameDate
|
||||||
|
) : ActorHumanoid(born), AIControlled, CanBeAnItem {
|
||||||
/**
|
|
||||||
* Initialised in init block.
|
|
||||||
* Use lua function "update(delta)" to step the AI.
|
|
||||||
*/
|
|
||||||
protected val luaInstance: LuaValue
|
|
||||||
|
|
||||||
private val aiLuaAPI: AILuaAPI
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DEFAULT_COLLISION_TYPE = ActorWithSprite.COLLISION_DYNAMIC
|
val DEFAULT_COLLISION_TYPE = ActorWithSprite.COLLISION_DYNAMIC
|
||||||
@@ -36,28 +32,16 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
collisionType = DEFAULT_COLLISION_TYPE
|
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
|
// we're having InventoryItem data so that this class could be somewhat universal
|
||||||
override var itemData: InventoryItem = object : InventoryItem() {
|
override var itemData: InventoryItem = object : InventoryItem() {
|
||||||
override var id = referenceID
|
override var id = referenceID
|
||||||
override val equipPosition: Int = EquipPosition.HAND_GRIP
|
|
||||||
|
|
||||||
override var mass: Double
|
override var baseMass: Double
|
||||||
get() = actorValue.getAsDouble(AVKey.BASEMASS)!!
|
get() = actorValue.getAsDouble(AVKey.BASEMASS)!!
|
||||||
set(value) {
|
set(value) { actorValue[AVKey.BASEMASS] = value }
|
||||||
actorValue[AVKey.BASEMASS] = value
|
override var baseToolSize: Double? = 0.0
|
||||||
}
|
|
||||||
|
|
||||||
override var scale: Double
|
override var scale: Double
|
||||||
get() = actorValue.getAsDouble(AVKey.SCALE)!!
|
get() = actorValue.getAsDouble(AVKey.SCALE)!!
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -85,9 +69,7 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
|
|||||||
|
|
||||||
override fun update(gc: GameContainer, delta: Int) {
|
override fun update(gc: GameContainer, delta: Int) {
|
||||||
super.update(gc, delta)
|
super.update(gc, delta)
|
||||||
|
ai.update(delta)
|
||||||
// run "update()" function in the script
|
|
||||||
luag.get("update").call(delta.toLuaValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun moveLeft(amount: Float) { // hit the buttons on the controller box
|
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
|
// if your NPC should fly, override this
|
||||||
throw UnsupportedOperationException("Humans cannot fly :p")
|
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)
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors
|
|||||||
|
|
||||||
import net.torvald.spriteanimation.SpriteAnimation
|
import net.torvald.spriteanimation.SpriteAnimation
|
||||||
import net.torvald.terrarum.gameactors.ActorHumanoid
|
import net.torvald.terrarum.gameactors.ActorHumanoid
|
||||||
|
import net.torvald.terrarum.gameactors.ai.LuaAIWrapper
|
||||||
import net.torvald.terrarum.mapdrawer.FeaturesDrawer
|
import net.torvald.terrarum.mapdrawer.FeaturesDrawer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,9 +12,12 @@ object PlayerBuilderCynthia {
|
|||||||
|
|
||||||
operator fun invoke(): ActorWithSprite {
|
operator fun invoke(): ActorWithSprite {
|
||||||
//val p: Player = Player(GameDate(100, 143)) // random value thrown
|
//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
|
GameDate(100, 143)) // random value thrown
|
||||||
InjectCreatureRaw(p.actorValue, "CreatureHuman.json")
|
InjectCreatureRaw(p.actorValue, "CreatureHuman.json")
|
||||||
|
(p.ai as LuaAIWrapper).attachActor(p)
|
||||||
|
|
||||||
|
|
||||||
p.actorValue[AVKey.__PLAYER_QUICKBARSEL] = 0
|
p.actorValue[AVKey.__PLAYER_QUICKBARSEL] = 0
|
||||||
p.actorValue[AVKey.NAME] = "Cynthia"
|
p.actorValue[AVKey.NAME] = "Cynthia"
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ package net.torvald.terrarum.gameactors.ai
|
|||||||
* Created by minjaesong on 16-03-02.
|
* Created by minjaesong on 16-03-02.
|
||||||
*/
|
*/
|
||||||
interface ActorAI {
|
interface ActorAI {
|
||||||
|
fun update(delta: Int)
|
||||||
}
|
}
|
||||||
113
src/net/torvald/terrarum/gameactors/ai/LuaAIWrapper.kt
Normal file
113
src/net/torvald/terrarum/gameactors/ai/LuaAIWrapper.kt
Normal 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)
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user