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>() 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() private var isInitialized = false private var successfulInit = false private var calledStart = false private var successfulStart = false private val rewards = ArrayList() 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() } }