251 lines
6.5 KiB
Kotlin
251 lines
6.5 KiB
Kotlin
package ru.dbotthepony.kstarbound.player
|
||
|
||
import com.google.gson.JsonArray
|
||
import com.google.gson.JsonElement
|
||
import com.google.gson.JsonObject
|
||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||
import org.apache.logging.log4j.LogManager
|
||
import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate
|
||
import ru.dbotthepony.kstarbound.lua.LuaState
|
||
import ru.dbotthepony.kstarbound.lua.MessageHandler
|
||
import ru.dbotthepony.kstarbound.lua.exposeConfig
|
||
import ru.dbotthepony.kstarbound.util.ItemDescriptor
|
||
import ru.dbotthepony.kstarbound.util.set
|
||
import java.util.UUID
|
||
|
||
class QuestInstance(
|
||
val avatar: Avatar,
|
||
val descriptor: QuestDescriptor,
|
||
val seed: Long = System.nanoTime().rotateLeft(27).xor(System.currentTimeMillis()),
|
||
val serverID: UUID? = null,
|
||
val worldID: String? = null
|
||
) {
|
||
val template: QuestTemplate = avatar.starbound.questTemplates[descriptor.templateId]?.value ?: throw IllegalArgumentException("No such quest template ${descriptor.templateId}")
|
||
val id get() = descriptor.questId
|
||
|
||
val lua = LuaState()
|
||
val messages = MessageHandler(lua)
|
||
|
||
enum class State(val serializedName: String) {
|
||
NEW("New"),
|
||
OFFER("Offer"),
|
||
ACTIVE("Active"),
|
||
COMPLETE("Complete"),
|
||
FAILED("Failed")
|
||
}
|
||
|
||
private val objectiveList = ArrayList<Pair<String, Boolean>>()
|
||
|
||
var canTurnIn = false
|
||
var failureText = ""
|
||
var completionText = ""
|
||
var text = ""
|
||
var title = ""
|
||
var state = State.NEW
|
||
var progress: Double? = null
|
||
var compassDirection: Double? = null
|
||
|
||
private val portraits = JsonObject()
|
||
private val params = descriptor.parameters.deepCopy()
|
||
|
||
private val portraitTitles = Object2ObjectOpenHashMap<String, String>()
|
||
|
||
private var isInitialized = false
|
||
private var successfulInit = false
|
||
private var calledStart = false
|
||
private var successfulStart = false
|
||
|
||
private val rewards = ArrayList<ItemDescriptor>()
|
||
|
||
fun complete() {
|
||
|
||
}
|
||
|
||
fun fail() {
|
||
|
||
}
|
||
|
||
private fun setObjectiveList(value: JsonArray) {
|
||
|
||
}
|
||
|
||
private fun addReward(value: JsonElement) {
|
||
val item = avatar.starbound.item(value)
|
||
|
||
if (!item.isEmpty) {
|
||
rewards.add(item)
|
||
}
|
||
}
|
||
|
||
init {
|
||
lua.pushTable()
|
||
lua.storeGlobal("self")
|
||
lua.pushTable()
|
||
lua.storeGlobal("storage")
|
||
|
||
lua.pushTable()
|
||
lua.storeGlobal("quest")
|
||
lua.loadGlobal("quest")
|
||
|
||
lua.setTableClosure("setParameter", this) { setParameter(it) }
|
||
|
||
lua.setTableFunction("parameters", this) {
|
||
it.lua.push(params)
|
||
1
|
||
}
|
||
|
||
lua.setTableClosure("setPortrait", this) { setPortrait(it) }
|
||
lua.setTableClosure("setPortraitTitle", this) { setPortraitTitle(it) }
|
||
|
||
lua.setTableFunction("state", this) { it.lua.push(state.serializedName); 1 }
|
||
lua.setTableClosure("complete", this) { complete() }
|
||
lua.setTableClosure("fail", this) { fail() }
|
||
lua.setTableClosure("setCanTurnIn", this) { canTurnIn = it.getBool() }
|
||
lua.setTableFunction("questDescriptor", this) { it.lua.push(avatar.starbound.gson.toJsonTree(descriptor)); 1 }
|
||
lua.setTableFunction("questId", this) { it.lua.push(id); 1 }
|
||
lua.setTableFunction("templateId", this) { it.lua.push(template.id); 1 }
|
||
lua.setTableFunction("seed", this) { it.lua.push(seed); 1 }
|
||
lua.setTableFunction("questArcDescriptor", this) { TODO(); 1 }
|
||
lua.setTableFunction("questArcPosition", this) { TODO(); 1 }
|
||
lua.setTableFunction("worldId", this) { it.lua.push(worldID); 1 }
|
||
lua.setTableFunction("serverUuid", this) { it.lua.push(serverID?.toString()); 1 }
|
||
lua.setTableClosure("setObjectiveList", this) { setObjectiveList(it.getArray()) }
|
||
lua.setTableClosure("setProgress", this) { progress = it.getDoubleOrNil() }
|
||
lua.setTableClosure("setCompassDirection", this) { compassDirection = it.getDoubleOrNil() }
|
||
lua.setTableClosure("setTitle", this) { title = it.getString() }
|
||
lua.setTableClosure("setText", this) { text = it.getString() }
|
||
lua.setTableClosure("setCompletionText", this) { completionText = it.getString() }
|
||
lua.setTableClosure("setFailureText", this) { failureText = it.getString() }
|
||
lua.setTableClosure("addReward", this) { addReward(it.getValue()) }
|
||
|
||
lua.pop()
|
||
|
||
avatar.starbound.pushLuaAPI(lua)
|
||
avatar.pushLuaAPI(lua)
|
||
lua.exposeConfig(template.scriptConfig)
|
||
}
|
||
|
||
private fun setParameter(argStack: LuaState.ArgStack) {
|
||
params.add(argStack.getString(), argStack.getValue())
|
||
}
|
||
|
||
private fun setPortrait(argStack: LuaState.ArgStack) {
|
||
val name = argStack.getString()
|
||
val value = argStack.getAnything()
|
||
|
||
if (value == null)
|
||
portraits.remove(name)
|
||
else
|
||
portraits.add(name, value)
|
||
}
|
||
|
||
private fun setPortraitTitle(argStack: LuaState.ArgStack) {
|
||
val name = argStack.getString()
|
||
val title = argStack.getStringOrNil()
|
||
|
||
if (title == null)
|
||
portraitTitles.remove(name)
|
||
else
|
||
portraitTitles[name] = title
|
||
}
|
||
|
||
init {
|
||
for ((k, v) in descriptor.parameters.entrySet()) {
|
||
params[k] = v.deepCopy()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Читает главный скриптовый файл квеста и вызывает его (глобальную) функцию init()
|
||
*/
|
||
fun init(): Boolean {
|
||
if (!isInitialized) {
|
||
isInitialized = true
|
||
|
||
val script = avatar.starbound.locate(template.script.fullPath)
|
||
|
||
if (!script.isFile) {
|
||
LOGGER.error("Quest ${template.id} specifies ${template.script.fullPath} as its script, but it is not a file or does not exist!")
|
||
return false
|
||
}
|
||
|
||
try {
|
||
lua.load(script.readToString(), "@" + template.script.fullPath)
|
||
} catch(err: Throwable) {
|
||
LOGGER.error("Error loading Lua code for quest ${descriptor.questId}", err)
|
||
return false
|
||
}
|
||
|
||
try {
|
||
lua.call()
|
||
} catch(err: Throwable) {
|
||
LOGGER.error("Error running Lua code for quest ${descriptor.questId}", err)
|
||
return false
|
||
}
|
||
|
||
try {
|
||
lua.loadGlobal("init")
|
||
lua.call()
|
||
} catch(err: Throwable) {
|
||
LOGGER.error("Error running init() function for quest ${descriptor.questId}", err)
|
||
return false
|
||
}
|
||
|
||
successfulInit = true
|
||
}
|
||
|
||
return successfulInit
|
||
}
|
||
|
||
fun start(): Boolean {
|
||
if (!init()) {
|
||
return false
|
||
}
|
||
|
||
if (calledStart) {
|
||
return successfulStart
|
||
}
|
||
|
||
lua.loadGlobal("questStart")
|
||
|
||
try {
|
||
if (lua.isFunction()) {
|
||
lua.call()
|
||
} else {
|
||
lua.pop()
|
||
}
|
||
} catch(err: Throwable) {
|
||
LOGGER.error("Error running questStart() function for quest ${descriptor.questId}", err)
|
||
return false
|
||
}
|
||
|
||
return successfulStart
|
||
}
|
||
|
||
fun update(delta: Int): Boolean {
|
||
if (!init()) {
|
||
return false
|
||
}
|
||
|
||
lua.loadGlobal("update")
|
||
|
||
try {
|
||
if (lua.isFunction()) {
|
||
lua.push(delta)
|
||
lua.call(numArgs = 1)
|
||
} else {
|
||
lua.pop()
|
||
}
|
||
} catch(err: Throwable) {
|
||
LOGGER.error("Error running update() function for quest ${descriptor.questId}", err)
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
companion object {
|
||
private val LOGGER = LogManager.getLogger()
|
||
}
|
||
}
|