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

393 lines
13 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.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import ru.dbotthepony.kommons.guava.immutableMap
import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.player.TechDefinition
import ru.dbotthepony.kstarbound.lua.NewLuaState
import ru.dbotthepony.kstarbound.lua.luaFunction
import ru.dbotthepony.kstarbound.lua.luaFunction0String
import ru.dbotthepony.kstarbound.lua.luaFunctionN
import ru.dbotthepony.kstarbound.lua.luaStub
import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.util.ItemStack
import java.util.*
import kotlin.collections.ArrayList
/**
* Персонаж - как он есть.
*
* [Avatar] реализует Lua интерфейс `player`.
*/
class Avatar(val uniqueId: UUID) {
enum class EssentialSlot {
BEAM_AXE,
WIRE_TOOL,
PAINT_TOOL,
INSPECTION_TOOL;
}
enum class EquipmentSlot {
HEAD,
CHEST,
LEGS,
BACK,
HEAD_COSMETIC,
CHEST_COSMETIC,
LEGS_COSMETIC,
BACK_COSMETIC;
}
private val essentialSlots = EnumMap<EssentialSlot, ItemStack>(EssentialSlot::class.java)
private val equipmentSlots = EnumMap<EquipmentSlot, ItemStack>(EquipmentSlot::class.java)
private val bags = ArrayList<AvatarBag>()
private val quests = Object2ObjectOpenHashMap<String, QuestInstance>()
var cursorItem = ItemStack.EMPTY
private val availableTechs = ObjectOpenHashSet<Registry.Entry<TechDefinition>>()
private val enabledTechs = ObjectOpenHashSet<Registry.Entry<TechDefinition>>()
private val equippedTechs = Object2ObjectOpenHashMap<String, Registry.Entry<TechDefinition>>()
private val knownBlueprints = ObjectOpenHashSet<ItemStack>()
// С подписью NEW
private val newBlueprints = ObjectOpenHashSet<ItemStack>()
private val currencies = Object2LongOpenHashMap<String>()
/**
* Teaches the player any recipes which can be used to craft the specified item.
*/
fun giveBlueprint(name: String): Boolean {
val item = Starbound.item(name).conciseToNull() ?: return false
if (knownBlueprints.add(item)) {
newBlueprints.add(item)
return true
}
return false
}
/**
* Returns `true` if the player knows one or more recipes to create the specified item and `false` otherwise.
*/
fun blueprintKnown(name: String): Boolean {
return (Starbound.item(name).conciseToNull() ?: return false) in knownBlueprints
}
/**
* Returns `true` if the player knows one or more recipes to create the specified item and `false` otherwise.
*/
private fun blueprintKnown(name: JsonElement): Boolean {
if (name is JsonPrimitive) {
return (Starbound.item(name.asString).conciseToNull() ?: return false) in knownBlueprints
} else if (name is JsonObject) {
return (Starbound.item(name).conciseToNull() ?: return false) in knownBlueprints
} else {
return false
}
}
/**
* Teaches the player any recipes which can be used to craft the specified item.
*/
private fun giveBlueprint(name: JsonElement): Boolean {
val item: ItemStack
if (name is JsonPrimitive) {
item = Starbound.item(name.asString).conciseToNull() ?: return false
} else if (name is JsonObject) {
item = Starbound.item(name).conciseToNull() ?: return false
} else {
return false
}
if (knownBlueprints.add(item)) {
newBlueprints.add(item)
return true
}
return false
}
/**
* Adds the specified tech to the player's list of available (unlockable) techs.
*/
fun makeTechAvailable(name: String): Boolean {
return availableTechs.add(Registries.techs[name] ?: return false)
}
/**
* Removes the specified tech from player's list of available (unlockable) techs.
*/
fun makeTechUnavailable(name: String): Boolean {
val tech = Registries.techs[name] ?: return false
if (availableTechs.remove(tech)) {
enabledTechs.remove(tech)
equippedTechs.remove(tech.value.type)
return true
}
return false
}
/**
* Unlocks the specified tech, allowing it to be equipped through the tech GUI.
*/
fun enableTech(name: String): Boolean {
val tech = Registries.techs[name] ?: return false
availableTechs.add(tech)
return enabledTechs.add(tech)
}
/**
* Equips the specified tech.
*/
fun equipTech(name: String): Boolean {
val tech = Registries.techs[name] ?: return false
availableTechs.add(tech)
enabledTechs.add(tech)
return equippedTechs.put(tech.value.type, tech) != tech
}
/**
* Unequips the specified tech.
*/
fun unequipTech(name: String): Boolean {
val tech = Registries.techs[name] ?: return false
return equippedTechs.remove(tech.value.type) == tech
}
/**
* Returns the player's current total reserves of the specified currency.
*/
fun currency(name: String): Long {
return currencies.getLong(name)
}
/**
* Increases the player's reserve of the specified currency by the specified amount.
*/
fun addCurrency(name: String, amount: Long) {
check(amount >= 0L) { "Negative amount of currency: $amount (currency: $name)" }
currencies.computeLong(name) { key, old -> (old ?: 0L) + amount }
}
/**
* Attempts to consume the specified amount of the specified currency and returns `true` if successful and `false` otherwise.
*/
fun consumeCurrency(name: String, amount: Long): Boolean {
check(amount >= 0L) { "Negative amount of currency: $amount (currency: $name)" }
val current = currencies.getLong(name)
if (current - amount >= 0L) {
currencies[name] = current - amount
return true
}
return false
}
/**
* Triggers an immediate cleanup of the player's inventory, removing item stacks with 0 quantity. May rarely be required in special cases of making several sequential modifications to the player's inventory within a single tick.
*/
fun cleanupItems() {
TODO()
}
/**
* Adds the specified item to the player's inventory.
*/
fun giveItem(descriptor: ItemStack) {
TODO()
}
/**
* Returns `true` if the player's inventory contains an item matching the specified descriptor and `false` otherwise. If exactMatch is `true` then parameters as well as item name must match.
*/
fun hasItem(descriptor: ItemStack, exactMatch: Boolean = false): Boolean {
return false
}
/**
* Returns the total number of items in the player's inventory matching the specified descriptor. If exactMatch is `true` then parameters as well as item name must match.
*/
fun hasCountOfItem(descriptor: ItemStack, exactMatch: Boolean = false): Long {
return 0L
}
/**
* Attempts to consume the specified item from the player's inventory and returns the item consumed if successful. If consumePartial is `true`, matching stacks totalling fewer items than the requested count may be consumed, otherwise the operation will only be performed if the full count can be consumed. If exactMatch is `true` then parameters as well as item name must match.
*/
fun consumeItem(descriptor: ItemStack, allowPartial: Boolean = false, exactMatch: Boolean = false): ItemStack {
return ItemStack.EMPTY
}
fun inventoryTags(): Map<String, Long> {
return mapOf()
}
fun itemsWithTag(): List<ItemStack> {
return listOf()
}
fun consumeTaggedItem(tag: String): Long {
return 0L
}
fun hasItemWithParameter(name: String, value: JsonElement): Boolean {
return false
}
fun consumeItemWithParameter(name: String, value: JsonElement, count: Long): Long {
return 0L
}
fun getItemWithParameter(name: String, value: JsonElement): ItemStack {
return ItemStack.EMPTY
}
var primaryHandItem: ItemStack? = null
var altHandItem: ItemStack? = null
fun essentialItem(slotName: EssentialSlot): ItemStack? {
return essentialSlots[slotName]?.conciseToNull()
}
fun giveEssentialItem(slotName: EssentialSlot, item: ItemStack) {
}
fun removeEssentialItem(slotName: EssentialSlot) {
}
fun equippedItem(slotName: EquipmentSlot): ItemStack {
return equipmentSlots[slotName] ?: ItemStack.EMPTY
}
fun setEquippedItem(slotName: EquipmentSlot, item: ItemStack) {
}
fun addQuest(quest: QuestInstance): QuestInstance? {
check(quest.avatar === this) { "$quest does not belong to $this" }
return quests.put(quest.id, quest)
}
private fun startQuest(value: JsonElement, serverID: String?, worldID: String?): String {
if (value is JsonPrimitive) {
val quest = QuestInstance(this, descriptor = QuestDescriptor(value.asString), serverID = serverID?.let(UUID::fromString), worldID = worldID)
addQuest(quest)
return quest.id
} else if (value is JsonObject) {
val seed = value["seed"]?.asLong ?: QuestDescriptor.makeSeed()
val questId = value["questId"]?.asString ?: throw IllegalArgumentException("Invalid 'questId' in quest descriptor")
val templateId = value["templateId"]?.asString ?: questId
val params = value["parameters"] as? JsonObject ?: JsonObject()
val quest = QuestInstance(this, descriptor = QuestDescriptor(questId, templateId, seed, params), serverID = serverID?.let(UUID::fromString), worldID = worldID)
addQuest(quest)
return quest.id
} else {
throw IllegalArgumentException("Invalid quest descriptor: $value")
}
}
fun provideBindings(lua: NewLuaState) {
val table = lua.state.newTable()
lua.env["player"] = table
lua.env["uniqueId"] = luaFunction { it.returnBuffer.setTo(uniqueId.toString()) }
lua.env["species"] = luaFunction { it.returnBuffer.setTo("human") }
lua.env["gender"] = luaFunction { it.returnBuffer.setTo("male") }
lua.env["giveBlueprint"] = luaFunction0String("giveBlueprint", ::giveBlueprint)
lua.env["blueprintKnown"] = luaFunction0String("blueprintKnown", ::blueprintKnown)
lua.env["makeTechAvailable"] = luaFunction0String("makeTechAvailable", ::makeTechAvailable)
lua.env["makeTechUnavailable"] = luaFunction0String("makeTechUnavailable", ::makeTechUnavailable)
lua.env["enableTech"] = luaFunction0String("enableTech", ::enableTech)
lua.env["equipTech"] = luaFunction0String("equipTech", ::equipTech)
lua.env["unequipTech"] = luaFunction0String("unequipTech", ::unequipTech)
lua.env["availableTechs"] = luaFunction {
val result = it.newTable(availableTechs.size, 0)
for ((i, v) in availableTechs.withIndex()) {
result[i + 1] = v
}
it.returnBuffer.setTo(result)
}
lua.env["enabledTechs"] = luaFunction {
val result = it.newTable(enabledTechs.size, 0)
for ((i, v) in enabledTechs.withIndex()) {
result[i + 1] = v
}
it.returnBuffer.setTo(result)
}
lua.env["equippedTech"] = luaFunctionN("equippedTech") { it, args -> equippedTechs[args.nextString().decode()]?.key }
lua.env["currency"] = luaFunction0String("currency", ::currency)
lua.env["addCurrency"] = luaFunctionN("addCurrency") { it, args -> addCurrency(args.nextString().decode(), args.nextInteger()) }
lua.env["consumeCurrency"] = luaFunctionN("consumeCurrency") { it, args -> consumeCurrency(args.nextString().decode(), args.nextInteger()) }
lua.env["cleanupItems"] = luaFunction { cleanupItems() }
lua.env["giveItem"] = luaStub("giveItem")
lua.env["hasItem"] = luaStub("hasItem")
lua.env["hasCountOfItem"] = luaStub("hasCountOfItem")
lua.env["consumeItem"] = luaStub("consumeItem")
lua.env["inventoryTags"] = luaStub("inventoryTags")
lua.env["itemsWithTag"] = luaStub("itemsWithTag")
lua.env["consumeTaggedItem"] = luaStub("consumeTaggedItem")
lua.env["hasItemWithParameter"] = luaStub("hasItemWithParameter")
lua.env["consumeItemWithParameter"] = luaStub("consumeItemWithParameter")
lua.env["getItemWithParameter"] = luaStub("getItemWithParameter")
lua.env["primaryHandItem"] = luaStub("primaryHandItem")
lua.env["altHandItem"] = luaStub("altHandItem")
lua.env["primaryHandItemTags"] = luaStub("primaryHandItemTags")
lua.env["altHandItemTags"] = luaStub("altHandItemTags")
lua.env["essentialItem"] = luaStub("essentialItem")
lua.env["giveEssentialItem"] = luaStub("giveEssentialItem")
lua.env["removeEssentialItem"] = luaStub("removeEssentialItem")
lua.env["equippedItem"] = luaStub("equippedItem")
lua.env["setEquippedItem"] = luaStub("setEquippedItem")
lua.env["swapSlotItem"] = luaStub("swapSlotItem")
lua.env["setSwapSlotItem"] = luaStub("setSwapSlotItem")
lua.env["startQuest"] = luaStub("startQuest")
lua.env["hasQuest"] = luaStub("hasQuest")
lua.env["hasCompletedQuest"] = luaStub("hasCompletedQuest")
}
companion object {
private val essentialSlotsMap = immutableMap<String, EssentialSlot> {
put("beamaxe", EssentialSlot.BEAM_AXE)
put("inspectiontool", EssentialSlot.INSPECTION_TOOL)
put("wiretool", EssentialSlot.WIRE_TOOL)
put("painttool", EssentialSlot.PAINT_TOOL)
}
private val equipmentSlotsMap = immutableMap<String, EquipmentSlot> {
put("head", EquipmentSlot.HEAD)
put("chest", EquipmentSlot.CHEST)
put("legs", EquipmentSlot.LEGS)
put("back", EquipmentSlot.BACK)
put("headCosmetic", EquipmentSlot.HEAD_COSMETIC)
put("chestCosmetic", EquipmentSlot.CHEST_COSMETIC)
put("legsCosmetic", EquipmentSlot.LEGS_COSMETIC)
put("backCosmetic", EquipmentSlot.BACK_COSMETIC)
}
}
}