actor AI from Lua script

Former-commit-id: 184160efc59c4f846f1cc154fe3e60d21b301ee3
Former-commit-id: 4e228542975ea52945a597b7ca1bc06b407c3be7
This commit is contained in:
Song Minjae
2016-12-26 23:55:54 +09:00
parent 1e1692a697
commit bb63816201
15 changed files with 132 additions and 95 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -5,7 +5,7 @@
"basemass": 60.0,
"strength": 1000,
"strengthmult": [100,100,100,100,100,100,100],
"strengthmult": [90,95,98,100,102,105,110],
"accel": 0.32,

View File

@@ -44,7 +44,12 @@ open class KVHashMap {
if (value is JsonPrimitive)
return value.asInt
return get(key) as Int?
try {
return value as Int
}
catch (e: ClassCastException) {
return null
}
}
fun getAsDouble(key: String): Double? {
@@ -56,13 +61,12 @@ open class KVHashMap {
return value.asDouble
try {
return value as Double?
return value as Double
}
catch (e: ClassCastException) {
System.err.println("[KVHashMap] ClassCastException for key '$key'")
e.printStackTrace(System.err)
return null
}
return null
}
fun getAsString(key: String): String? {
@@ -71,7 +75,12 @@ open class KVHashMap {
if (value is JsonPrimitive)
return value.asString
return value as String?
try {
return value as String
}
catch (e: ClassCastException) {
return null
}
}
fun getAsBoolean(key: String): Boolean? {
@@ -80,12 +89,15 @@ open class KVHashMap {
if (value is JsonPrimitive)
return value.asBoolean
return value as Boolean?
try {
return value as Boolean
}
catch (e: ClassCastException) {
return null
}
}
fun hasKey(key: String): Boolean {
return hashMap.containsKey(key)
}
fun hasKey(key: String) = hashMap.containsKey(key)
val keySet: Set<Any>
get() = hashMap.keys

View File

@@ -298,11 +298,13 @@ constructor() : BasicGameState() {
/////////////////
// draw actors //
/////////////////
// --> Change of blend mode <-- introduced by ActorWithBody //
actorContainer.forEach { actor ->
if (actor is ActorWithBody && actor.inScreen() && actor !is Player) {
actor.drawBody(gc, worldDrawFrameBuffer.graphics)
}
}
// --> Change of blend mode <-- introduced by ActorWithBody //
player.drawBody(gc, worldDrawFrameBuffer.graphics)
@@ -311,32 +313,37 @@ constructor() : BasicGameState() {
/////////////////////////////
LightmapRenderer.renderLightMap()
// --> blendMul() <-- by MapCamera.renderFront
MapCamera.renderFront(gc, worldDrawFrameBuffer.graphics)
// --> blendNormal() <-- by MapCamera.renderFront
MapDrawer.render(gc, worldDrawFrameBuffer.graphics)
blendMul()
// --> blendMul() <-- by MapCamera.drawEnvOverlay
MapDrawer.drawEnvOverlay(worldDrawFrameBuffer.graphics)
if (!KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendMul() else blendNormal()
if (!KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendMul()
else blendNormal()
LightmapRenderer.draw(worldDrawFrameBuffer.graphics)
blendNormal()
//////////////////////
// draw actor glows //
//////////////////////
// --> blendLightenOnly() <-- introduced by ActorWithBody //
actorContainer.forEach { actor ->
if (actor is ActorWithBody && actor.inScreen() && actor !is Player) {
actor.drawGlow(gc, worldDrawFrameBuffer.graphics)
}
}
// --> blendLightenOnly() <-- introduced by ActorWithBody //
player.drawGlow(gc, worldDrawFrameBuffer.graphics)
////////////////////////
// debug informations //
////////////////////////
blendNormal()
// draw reference ID if debugWindow is open
if (debugWindow.isVisible) {
actorContainer.forEachIndexed { i, actor ->

View File

@@ -186,16 +186,16 @@ constructor(gamename: String) : StateBasedGame(gamename) {
lateinit var fontSmallNumbers: Font
private set
var joypadLabelStart: Char = 0x00.toChar() // lateinit
var joypadLableSelect:Char = 0x00.toChar() // lateinit
var joypadLabelNinA: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinB: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinX: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinY: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinL: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinR: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinZL: Char = 0x00.toChar() // lateinit TODO
var joypadLabelNinZR: Char = 0x00.toChar() // lateinit TODO
var joypadLabelStart: Char = 0xE000.toChar() // lateinit
var joypadLableSelect:Char = 0xE000.toChar() // lateinit
var joypadLabelNinA: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinB: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinX: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinY: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinL: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinR: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinZL: Char = 0xE000.toChar() // lateinit TODO
var joypadLabelNinZR: Char = 0xE000.toChar() // lateinit TODO
val joypadLabelLEFT = 0xE068.toChar()
val joypadLabelDOWN = 0xE069.toChar()
val joypadLabelUP = 0xE06A.toChar()

View File

@@ -22,7 +22,7 @@ import java.util.*
* Created by minjaesong on 16-10-24.
*/
open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
: HistoricalFigure(birth, death), Controllable, Pocketed, Factionable, Luminous, LandHolder {
: HistoricalFigure(birth, death), Controllable, Pocketed, Factionable, Luminous, LandHolder {
/** Must be set by PlayerFactory */
override var inventory: ActorInventory = ActorInventory()
@@ -197,6 +197,22 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
isDownDown = isFuncDown(input, EnumKeyFunc.MOVE_DOWN)
isRightDown = isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)
isJumpDown = isFuncDown(input, EnumKeyFunc.JUMP)
if (Terrarum.hasController) {
gamepad = Controllers.getController(0)
axisX = gamepad!!.getAxisValue(0)
axisY = gamepad!!.getAxisValue(1)
axisRX = gamepad!!.getAxisValue(2)
axisRY = gamepad!!.getAxisValue(3)
// deadzonning
if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f
if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f
if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f
if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f
gamepad!!.isButtonPressed(GAMEPAD_JUMP)
}
}
}
@@ -204,29 +220,17 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
if (!noClip) {
if (grounded) {
actorValue[AVKey.ACCELMULT] = 1.0
} else {
}
else {
actorValue[AVKey.ACCELMULT] = ACCEL_MULT_IN_FLIGHT
}
} else {
}
else {
actorValue[AVKey.ACCELMULT] = 1.0
}
}
override fun processInput(gc: GameContainer, delta: Int, input: Input) {
if (isGamer && Terrarum.hasController) {
gamepad = Controllers.getController(0)
axisX = gamepad!!.getAxisValue(0)
axisY = gamepad!!.getAxisValue(1)
axisRX = gamepad!!.getAxisValue(2)
axisRY = gamepad!!.getAxisValue(3)
// deadzonning
if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f
if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f
if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f
if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f
}
///////////////////
// MOUSE CONTROL //
///////////////////
@@ -333,16 +337,18 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
/**
* Jump control
*/
if (isJumpDown || isGamer && Terrarum.hasController && gamepad!!.isButtonPressed(GAMEPAD_JUMP)) {
if (isJumpDown) {
if (!noClip) {
if (grounded) {
jumping = true
}
jump()
} else {
}
else {
walkVertical(true, AXIS_POSMAX)
}
} else {
}
else {
jumping = false
jumpCounter = 0
jumpAcc = 0.0
@@ -376,7 +382,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
private fun walkHorizontal(left: Boolean, absAxisVal: Float) {
if ((!walledLeft && left) || (!walledRight && !left)) {
readonly_totalX =
absMax( // keyboard
absMax(// keyboard
actorValue.getAsDouble(AVKey.ACCEL)!! *
actorValue.getAsDouble(AVKey.ACCELMULT)!! *
Math.sqrt(scale) *
@@ -414,7 +420,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
*/
private fun walkVertical(up: Boolean, absAxisVal: Float) {
readonly_totalY =
absMax( // keyboard
absMax(// keyboard
actorValue.getAsDouble(AVKey.ACCEL)!! *
actorValue.getAsDouble(AVKey.ACCELMULT)!! *
Math.sqrt(scale) *
@@ -465,7 +471,6 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
* TODO linear function (play Super Mario Bros. and you'll get what I'm talking about)
*/
private fun jump() {
val len = MAX_JUMP_LENGTH.toFloat()
val pwr = actorValue.getAsDouble(AVKey.JUMPPOWER)!! * (actorValue.getAsDouble(AVKey.JUMPPOWERMULT) ?: 1.0)
val jumpLinearThre = 0.08
@@ -537,7 +542,8 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
if (spriteGlow != null) {
spriteGlow!!.flip(true, false)
}
} else {
}
else {
sprite!!.flip(false, false)
if (spriteGlow != null) {
spriteGlow!!.flip(false, false)

View File

@@ -907,7 +907,7 @@ open class ActorWithBody : Actor() {
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
(scale).toFloat()
)
// Q&D fix for Roundworld anormaly
// Q&D fix for Roundworld anomaly
spriteGlow!!.render(g,
(hitbox.posX - hitboxTranslateX * scale).toFloat() + world.width * TILE_SIZE,
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
@@ -925,7 +925,7 @@ open class ActorWithBody : Actor() {
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
(scale).toFloat()
)
// Q&D fix for Roundworld anormaly
// Q&D fix for Roundworld anomaly
spriteGlow!!.render(g,
(hitbox.posX - scale).toFloat() + world.width * TILE_SIZE,
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
@@ -956,7 +956,7 @@ open class ActorWithBody : Actor() {
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
(scale).toFloat()
)
// Q&D fix for Roundworld anormaly
// Q&D fix for Roundworld anomaly
sprite!!.render(g,
(hitbox.posX - hitboxTranslateX * scale).toFloat() + world.width * TILE_SIZE,
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
@@ -974,7 +974,7 @@ open class ActorWithBody : Actor() {
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),
(scale).toFloat()
)
// Q&D fix for Roundworld anormaly
// Q&D fix for Roundworld anomaly
sprite!!.render(g,
(hitbox.posX - scale).toFloat() + world.width * TILE_SIZE,
(hitbox.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2).toFloat(),

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.gameactors
import net.torvald.terrarum.gameactors.ActorHumanoid
import net.torvald.terrarum.gameactors.ai.AILuaAPI
import net.torvald.terrarum.gameitem.EquipPosition
import net.torvald.terrarum.gameitem.InventoryItem
import org.luaj.vm2.Globals
@@ -11,25 +12,23 @@ import org.luaj.vm2.compiler.LuaC
import org.luaj.vm2.lib.*
import org.luaj.vm2.lib.jse.JseBaseLib
import org.luaj.vm2.lib.jse.JseMathLib
import org.luaj.vm2.lib.jse.JsePlatform
import org.newdawn.slick.GameContainer
import org.newdawn.slick.Input
import java.io.InputStreamReader
import java.io.Reader
/**
* Created by minjaesong on 16-01-31.
*/
open class HumanoidNPC(luaScript: String, born: GameDate) : ActorHumanoid(born), AIControlled, CanBeAnItem {
open class HumanoidNPC(val luaScript: String, born: GameDate) : ActorHumanoid(born), AIControlled, CanBeAnItem {
override val scriptPath: String = ""
companion object {
protected val luag = Globals()
protected val luag: Globals = JsePlatform.standardGlobals()
init {
luag.load(JseBaseLib())
LoadState.install(luag)
LuaC.install(luag)
}
init {
AILuaAPI(luag, this)
}
// we're having InventoryItem data so that this class could be somewhat universal
@@ -72,13 +71,15 @@ open class HumanoidNPC(luaScript: String, born: GameDate) : ActorHumanoid(born),
init {
//val inputStream = javaClass.getResourceAsStream(scriptPath)
//runCommand(InputStreamReader(inputStream), scriptPath)
runCommand(luaScript)
}
override fun update(gc: GameContainer, delta: Int) {
super.update(gc, delta)
//runCommand(luaScript)
luag.load(luaScript).call()
//moveRight()
}
override fun moveLeft() { // hit the buttons on the controller box
@@ -126,7 +127,7 @@ open class HumanoidNPC(luaScript: String, born: GameDate) : ActorHumanoid(born),
fun runCommand(script: String) {
if (!threadRun && !flagDespawn) {
currentExecutionThread = Thread(ThreadRunCommand(luag, script, "="))
currentExecutionThread = Thread(ThreadRunCommand(luag, script, ""))
currentExecutionThread.start()
threadRun = true
}

View File

@@ -14,6 +14,8 @@ import java.security.SecureRandom
*/
object InjectCreatureRaw {
// FIXME strength not injected properly?
const val JSONPATH = "./assets/raw/creatures/"
private const val MULTIPLIER_RAW_ELEM_SUFFIX = AVKey.MULT

View File

@@ -16,6 +16,7 @@ object PlayerBuilderCynthia {
InjectCreatureRaw(p.actorValue, "CreatureHuman.json")
p.actorValue[AVKey.__PLAYER_QUICKBARSEL] = 0
p.actorValue[AVKey.NAME] = "Cynthia"
p.makeNewSprite(26, 42)

View File

@@ -16,12 +16,17 @@ import org.luaj.vm2.lib.ZeroArgFunction
internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
init {
if (actor !is AIControlled) throw IllegalArgumentException("The actor is not AIControlled! $actor")
if (actor !is AIControlled)
throw IllegalArgumentException("The actor is not AIControlled! $actor")
// load things. WARNING: THIS IS MANUAL!
g["ai"] = LuaValue.tableOf()
g["ai"]["getSelfActorInfo"] = GetSelfActorInfo(actor)
g["ai"]["getNearestActor"] = GetNearestActor()
g["ai"]["getNearestPlayer"] = GetNearestPlayer()
g["ai"]["getX"] = GetX(actor)
g["ai"]["getY"] = GetY(actor)
g["ai"]["moveUp"] = MoveUp(actor)
@@ -29,10 +34,14 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
g["ai"]["moveLeft"] = MoveLeft(actor)
g["ai"]["moveRight"] = MoveRight(actor)
g["ai"]["moveTo"] = MoveTo(actor)
g["ai"]["jump"] = Jump(actor)
}
companion object {
/**
* Reads arbitrary ActorWithBody and returns its information as Lua table
*/
fun composeActorObject(actor: ActorWithBody): LuaTable {
val t = LuaValue.tableOf()
@@ -48,7 +57,7 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
t["mass"] = actor.mass
t["collision_type"] = actor.collisionType
t["collisionType"] = actor.collisionType
t["strength"] = actor.actorValue.getAsInt(AVKey.STRENGTH) ?: 0
@@ -56,7 +65,7 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
val MUL_2 = LightmapRenderer.MUL_2
val MUL = LightmapRenderer.MUL
val CHMAX = LightmapRenderer.CHANNEL_MAX
t["luminosity_rgb"] = lumrgb
t["luminosityRGB"] = lumrgb
t["luminosity"] = (lumrgb.div(MUL_2).and(CHMAX).times(3) +
lumrgb.div(MUL).and(CHMAX).times(4) +
lumrgb.and(1023)) / 8 // quick luminosity calculation
@@ -65,6 +74,12 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
}
}
class GetSelfActorInfo(val actor: ActorWithBody) : ZeroArgFunction() {
override fun call(): LuaValue {
return composeActorObject(actor)
}
}
/** ai.getNearestActor(nullable any criterion, nullable number range) */
class GetNearestActor() : LuaFunction() {
override fun call(): LuaValue {
@@ -147,4 +162,11 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
}
}
}
class Jump(val actor: AIControlled) : ZeroArgFunction() {
override fun call(): LuaValue {
actor.moveJump()
return LuaValue.NONE
}
}
}

View File

@@ -1,10 +1,16 @@
package net.torvald.terrarum.gameactors.ai.scripts
/**
* Randomly roams around.
*
* Created by SKYHi14 on 2016-12-23.
*/
object PokemonNPCAI {
operator fun invoke(): String = """
ai.jump()
ai.moveRight()
thisActorInfo = ai.getSelfActorInfo()
print(thisActorInfo.strength)
"""
}

View File

@@ -45,10 +45,12 @@ object GameController {
(Terrarum.ingame.player as Player).vehicleRiding!!.processInput(gc, delta, input)
}
Terrarum.ingame.player.processInput(gc, delta, input)
Terrarum.ingame.actorContainer.forEach {
if (it is Controllable) it.processInput(gc, delta, input)
}
for (ui in Terrarum.ingame.uiContainer) {
ui.processInput(gc, delta, input)
Terrarum.ingame.uiContainer.forEach {
it.processInput(gc, delta, input)
}
}
else {
@@ -56,31 +58,6 @@ object GameController {
}
// test tile remove
/*if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
try {
Terrarum.ingame.world.setTileTerrain(mouseTileX, mouseTileY, Tile.AIR)
// terrarum.game.map.setTileWall(mouseTileX, mouseTileY, Tile.AIR);
}
catch (e: ArrayIndexOutOfBoundsException) {
}
}
// test tile place
else if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) {
try {
Terrarum.ingame.world.setTileTerrain(
mouseTileX, mouseTileY,
Terrarum.ingame.player.actorValue.getAsInt("__selectedtile")!!
)
}
catch (e: ArrayIndexOutOfBoundsException) {
}
}*/
///////////////////
// MOUSE CONTROL //
///////////////////

View File

@@ -188,7 +188,7 @@ object LightmapRenderer {
for (x in lightBoxX.div(TILE_SIZE).floorInt()
..lightBoxX.plus(lightBoxW).div(TILE_SIZE).floorInt()) {
lanternMap.add(Lantern(x, y, it.luminosity))
// Q&D fix for Roundworld anormaly
// Q&D fix for Roundworld anomaly
lanternMap.add(Lantern(x + world.width, y, it.luminosity))
lanternMap.add(Lantern(x - world.width, y, it.luminosity))
}

View File

@@ -6,6 +6,7 @@ import net.torvald.terrarum.tileproperties.Tile
import net.torvald.terrarum.tilestats.TileStats
import com.jme3.math.FastMath
import net.torvald.colourutil.ColourTemp
import net.torvald.terrarum.blendMul
import org.newdawn.slick.*
/**
@@ -49,6 +50,8 @@ object MapDrawer {
colTemp = colTemp_warm + colTemp_cold - ENV_COLTEMP_NOON
val zoom = Terrarum.ingame.screenZoom
blendMul()
g.color = ColourTemp(colTemp)
//g.color = getColourFromMap(3022)
g.fillRect(