diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt index 65472d9d..bc9309ea 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt @@ -1,18 +1,23 @@ package ru.dbotthepony.kstarbound +import com.google.common.collect.ImmutableList +import com.google.common.collect.ImmutableMap import com.google.gson.GsonBuilder import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonSyntaxException import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import org.apache.logging.log4j.LogManager import ru.dbotthepony.kommons.util.KOptional +import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.Json2Function import ru.dbotthepony.kstarbound.defs.JsonConfigFunction import ru.dbotthepony.kstarbound.defs.JsonFunction import ru.dbotthepony.kstarbound.defs.Species import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition +import ru.dbotthepony.kstarbound.defs.ThingDescription import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.impl.BackArmorItemDefinition @@ -36,14 +41,18 @@ import ru.dbotthepony.kstarbound.defs.dungeon.DungeonDefinition import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition +import ru.dbotthepony.kstarbound.defs.tile.RenderParameters +import ru.dbotthepony.kstarbound.defs.tile.TileDamageConfig import ru.dbotthepony.kstarbound.defs.tile.TileModifierDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.world.BushVariant import ru.dbotthepony.kstarbound.defs.world.GrassVariant import ru.dbotthepony.kstarbound.defs.world.TreeVariant import ru.dbotthepony.kstarbound.defs.world.BiomeDefinition +import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType import ru.dbotthepony.kstarbound.util.AssetPathStack +import ru.dbotthepony.kstarbound.world.physics.CollisionType import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.Future @@ -156,6 +165,9 @@ object Registries { tasks.addAll(loadRegistry(tileModifiers, fileTree["matmod"] ?: listOf(), key(TileModifierDefinition::modName, TileModifierDefinition::modId))) tasks.addAll(loadRegistry(liquid, fileTree["liquid"] ?: listOf(), key(LiquidDefinition::name, LiquidDefinition::liquidId))) + tasks.add(loadMetaMaterials()) + tasks.addAll(loadRegistry(dungeons, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name))) + tasks.addAll(loadRegistry(worldObjects, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName))) tasks.addAll(loadRegistry(statusEffects, fileTree["statuseffect"] ?: listOf(), key(StatusEffectDefinition::name))) tasks.addAll(loadRegistry(species, fileTree["species"] ?: listOf(), key(Species::kind))) @@ -169,7 +181,6 @@ object Registries { tasks.addAll(loadRegistry(treeStemVariants, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name))) tasks.addAll(loadRegistry(treeFoliageVariants, fileTree["modularfoliage"] ?: listOf(), key(TreeVariant.FoliageData::name))) tasks.addAll(loadRegistry(bushVariants, fileTree["bush"] ?: listOf(), key(BushVariant.Data::name))) - tasks.addAll(loadRegistry(dungeons, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name))) tasks.addAll(loadCombined(jsonFunctions, fileTree["functions"] ?: listOf())) tasks.addAll(loadCombined(json2Functions, fileTree["2functions"] ?: listOf())) @@ -282,4 +293,44 @@ object Registries { return tasks } + + @JsonFactory + data class MetaMaterialDef( + val materialId: Int, + val name: String, + val collisionKind: CollisionType, + val blocksLiquidFlow: Boolean = collisionKind.isSolidCollision, + val isConnectable: Boolean = true, + val supportsMods: Boolean = false, + ) + + private fun loadMetaMaterials(): Future<*> { + return Starbound.EXECUTOR.submit { + val read = Starbound.loadJsonAsset("/metamaterials.config") ?: return@submit + val read2 = Starbound.gson.getAdapter(object : TypeToken>() {}).fromJsonTree(read) + + for (def in read2) { + tiles.add { + tiles.add(key = "metamaterial:${def.name}", id = KOptional(def.materialId), value = TileDefinition( + isMeta = true, + materialId = def.materialId, + materialName = "metamaterial:${def.name}", + descriptionData = ThingDescription.EMPTY, + category = "meta", + renderTemplate = AssetReference.empty(), + renderParameters = RenderParameters.META, + isConnectable = def.isConnectable, + supportsMods = def.supportsMods, + damageTable = AssetReference(TileDamageConfig( + damageFactors = ImmutableMap.of(), + damageRecovery = Double.MAX_VALUE, + maximumEffectTime = 0.0, + totalHealth = Double.MAX_VALUE, + harvestLevel = Int.MAX_VALUE, + )) + )) + } + } + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimatedPartsDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimatedPartsDefinition.kt index aa382114..ac30ad1a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimatedPartsDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimatedPartsDefinition.kt @@ -47,7 +47,7 @@ data class AnimatedPartsDefinition( init { var index = 0 - for ((k, v) in states) { + for ((k, v) in sortedStates) { v.name = k v.index = index++ diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt index 4e2bbe53..efe40fda 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt @@ -315,7 +315,7 @@ class Image private constructor( if (xpixel !in 0 until width) continue - if (!isTransparent(xpixel, ypixel, flip)) { + if (!isTransparent(xpixel, height - ypixel - 1, flip)) { fillRatio += 1.0 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/BuiltinMetaMaterials.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/BuiltinMetaMaterials.kt index e999cc1c..17374cbd 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/BuiltinMetaMaterials.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/BuiltinMetaMaterials.kt @@ -120,7 +120,7 @@ const val PROTECTED_ZERO_GRAVITY_DUNGEON_ID = 65524 const val FIRST_RESERVED_DUNGEON_ID = 65520 object BuiltinMetaMaterials { - private fun make(id: Int, name: String, collisionType: CollisionType, isConnectable: Boolean = true) = Registries.tiles.add(key = name, id = KOptional(id), value = TileDefinition( + private fun make(id: Int, name: String, collisionType: CollisionType, isConnectable: Boolean = true) = Registries.tiles.add(key = "metamaterial:$name", id = KOptional(id), value = TileDefinition( materialId = id, materialName = "metamaterial:$name", descriptionData = ThingDescription.EMPTY, @@ -140,7 +140,7 @@ object BuiltinMetaMaterials { )) ), isBuiltin = true) - private fun makeMod(id: Int, name: String) = Registries.tileModifiers.add(key = name, id = KOptional(id), value = TileModifierDefinition( + private fun makeMod(id: Int, name: String) = Registries.tileModifiers.add(key = "metamod:$name", id = KOptional(id), value = TileModifierDefinition( modId = id, modName = "metamod:$name", descriptionData = ThingDescription.EMPTY, @@ -176,7 +176,7 @@ object BuiltinMetaMaterials { val BIOME_MOD = makeMod(65534, "biome") val UNDERGROUND_BIOME_MOD = makeMod(65533, "underground_biome") - val NO_LIQUID = Registries.liquid.add(key = "empty", id = KOptional(0), value = LiquidDefinition( + val NO_LIQUID = Registries.liquid.add(key = "metaliquid:empty", id = KOptional(0), value = LiquidDefinition( name = "metaliquid:empty", liquidId = 0, color = RGBAColor.TRANSPARENT_BLACK, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/Functions.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/Functions.kt index 8a625217..a222af64 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/Functions.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/Functions.kt @@ -4,6 +4,7 @@ import com.google.gson.JsonElement import it.unimi.dsi.fastutil.objects.ObjectIterators import org.classdump.luna.LuaRuntimeException import org.classdump.luna.Table +import org.classdump.luna.TableFactory import org.classdump.luna.impl.NonsuspendableFunctionException import org.classdump.luna.lib.ArgumentIterator import org.classdump.luna.runtime.AbstractFunction0 @@ -99,6 +100,16 @@ operator fun Table.iterator(): Iterator> { } } +fun TableFactory.tableOf(vararg values: Any?): Table { + val table = newTable(values.size, 0) + + for ((i, v) in values.withIndex()) { + table[i + 1L] = v + } + + return table +} + @Deprecated("Function is a stub") fun luaStub(message: String = "not yet implemented"): LuaFunction { return object : LuaFunction() { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt index 3a5e14ae..9166b56b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt @@ -242,20 +242,23 @@ class LuaEnvironment : StateContext { return true } - fun invokeGlobal(name: String) { + fun invokeGlobal(name: String, vararg arguments: Any?): Array { if (errorState) - return + return arrayOf() val load = globals[name] if (load is LuaFunction<*, *, *, *, *>) { - try { - executor.call(this, load) + return try { + executor.call(this, load, *arguments) } catch (err: Throwable) { errorState = true - throw err + LOGGER.error("Exception while calling global $name", err) + arrayOf() } } + + return arrayOf() } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaUpdateComponent.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaUpdateComponent.kt index 0b9b5391..0b394899 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaUpdateComponent.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaUpdateComponent.kt @@ -3,8 +3,8 @@ package ru.dbotthepony.kstarbound.lua import ru.dbotthepony.kstarbound.Starbound class LuaUpdateComponent(val lua: LuaEnvironment) { - var stepCount = 1 - private var steps = 0 + var stepCount = 1.0 + private var steps = 0.0 init { val script = lua.newTable() @@ -14,15 +14,20 @@ class LuaUpdateComponent(val lua: LuaEnvironment) { returnBuffer.setTo(stepCount * Starbound.TIMESTEP) } - script["setUpdateDelta"] = luaFunction { ticks: Int -> - stepCount = ticks + script["setUpdateDelta"] = luaFunction { ticks: Number -> + stepCount = ticks.toDouble() } } - fun update() { - if (steps++ >= stepCount) { - steps = 0 - lua.invokeGlobal("update") + fun update(delta: Double, vararg arguments: Any?) { + if (stepCount == 0.0) + return + + steps += delta / Starbound.TIMESTEP + + if (steps >= stepCount) { + steps %= stepCount + lua.invokeGlobal("update", stepCount * Starbound.TIMESTEP, *arguments) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt index 76a51db2..581cce93 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt @@ -172,7 +172,7 @@ class Animator() { // sorted by key // Basically, this is definition of each separate state - val states = config.states + val states = config.sortedStates var timer = 0.0 var frame = 0 @@ -192,7 +192,7 @@ class Animator() { private var activeStateChanged = false private var frameChanged = false - private var firstTime = true + private var firstTime = false var activeState: AnimatedPartsDefinition.StateType.State? = null set(value) { @@ -233,7 +233,7 @@ class Animator() { activeState = getState timer = 0.0 startedEvent.trigger() - firstTime = true + firstTime = false return true } @@ -304,6 +304,8 @@ class Animator() { } fun finishAnimations() { + firstTime = true + while (true) { val active = activeState ?: break diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt index 667215bb..e3266490 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt @@ -11,6 +11,7 @@ import com.google.gson.JsonPrimitive import com.google.gson.TypeAdapter import com.google.gson.reflect.TypeToken import org.apache.logging.log4j.LogManager +import org.classdump.luna.ByteString import org.classdump.luna.Table import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.vector.Vector2i @@ -71,7 +72,9 @@ import ru.dbotthepony.kstarbound.lua.bindings.provideWorldObjectBindings import ru.dbotthepony.kstarbound.lua.from import ru.dbotthepony.kstarbound.lua.get import ru.dbotthepony.kstarbound.lua.set +import ru.dbotthepony.kstarbound.lua.tableOf import ru.dbotthepony.kstarbound.lua.toJson +import ru.dbotthepony.kstarbound.lua.toJsonFromLua import ru.dbotthepony.kstarbound.util.asStringOrNull import ru.dbotthepony.kstarbound.world.Direction import ru.dbotthepony.kstarbound.world.LightCalculator @@ -412,6 +415,7 @@ open class WorldObject(val config: Registry.Entry) : TileEntit provideEntityBindings(this, lua) provideAnimatorBindings(animator, lua) lua.attach(config.value.scripts) + luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble lua.init() } @@ -451,6 +455,24 @@ open class WorldObject(val config: Registry.Entry) : TileEntit override fun interact(request: InteractRequest): InteractAction { val diff = world.geometry.diff(request.sourcePos, position) + val result = lua.invokeGlobal("onInteraction", lua.newTable().apply { + this["source"] = lua.tableOf(diff.x, diff.y) + this["sourceId"] = request.source + }) + + if (result.isNotEmpty()) { + if (result[0] == null) + return InteractAction.NONE + else if (result[0] is ByteString) { + val decoded = (result[0] as ByteString).decode() + return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!")) + } else { + val data = result[0] as Table + val decoded = (data[1L] as ByteString).decode() + return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), 0, toJsonFromLua(data[2L])) + } + } + if (!interactAction.isJsonNull) { return InteractAction(interactAction.asString, entityID, interactData) } @@ -493,7 +515,7 @@ open class WorldObject(val config: Registry.Entry) : TileEntit } try { - luaUpdate.update() + luaUpdate.update(delta) } catch (err: Throwable) { LogManager.getLogger().error("Error running update", err) }