KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/player/QuestInstance.kt

251 lines
6.5 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
}
}