Stagehands, they don't work because of Luna (:
This commit is contained in:
parent
aeca7836cd
commit
3b454374ec
@ -50,6 +50,7 @@ operator fun <K : Any, V : Any> ImmutableMap.Builder<K, V>.set(key: K, value: V)
|
||||
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
||||
inline fun <reified T> Gson.fromJson(reader: JsonElement): T? = getAdapter(T::class.java).read(FastJsonTreeReader(reader))
|
||||
|
||||
// TODO: mark these nullable
|
||||
fun <T> Gson.fromJsonFast(reader: JsonElement, type: Class<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
||||
fun <T> Gson.fromJsonFast(reader: JsonElement, type: TypeToken<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
||||
inline fun <reified T> Gson.fromJsonFast(reader: JsonElement): T = getAdapter(object : TypeToken<T>() {}).read(FastJsonTreeReader(reader))
|
||||
|
@ -117,6 +117,33 @@ object Registries {
|
||||
|
||||
private val monsterParts = HashMap<Pair<String, String>, HashMap<String, Pair<MonsterPartDefinition, IStarboundFile>>>()
|
||||
private val loggedMonsterPartMisses = Collections.synchronizedSet(ObjectOpenHashSet<Pair<String, String>>())
|
||||
private val stagehands = HashMap<String, Pair<JsonObject, IStarboundFile>>()
|
||||
|
||||
fun makeStagehandConfig(type: String, overrides: JsonElement? = JsonNull.INSTANCE): JsonObject {
|
||||
val (data) = stagehands[type] ?: throw NoSuchElementException("No such stagehand: $type")
|
||||
return mergeJson(data.deepCopy(), overrides ?: JsonNull.INSTANCE)
|
||||
}
|
||||
|
||||
private fun loadStagehands(files: Collection<IStarboundFile>, patches: Map<String, Collection<IStarboundFile>>): List<Future<*>> {
|
||||
return files.map { listedFile ->
|
||||
Starbound.GLOBAL_SCOPE.launch {
|
||||
try {
|
||||
val elem = JsonPatch.applyAsync(listedFile.asyncJsonReader(), patches[listedFile.computeFullPath()]).asJsonObject
|
||||
val type = elem["type"].asString
|
||||
|
||||
Starbound.submit {
|
||||
val existing = stagehands.put(type, elem to listedFile)
|
||||
|
||||
if (existing != null) {
|
||||
LOGGER.warn("Redefining stagehand prototype $type (new def originate from $listedFile, existing originate from ${existing.second})")
|
||||
}
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Loading stagehand definition file $listedFile", err)
|
||||
}
|
||||
}.asCompletableFuture()
|
||||
}
|
||||
}
|
||||
|
||||
fun selectMonsterPart(category: String, type: String, random: RandomGenerator): MonsterPartDefinition? {
|
||||
val key = category to type
|
||||
@ -275,6 +302,7 @@ object Registries {
|
||||
tasks.addAll(loadRegistry(dungeons, patchTree, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name)))
|
||||
|
||||
tasks.addAll(loadMonsterParts(fileTree["monsterpart"] ?: listOf(), patchTree))
|
||||
tasks.addAll(loadStagehands(fileTree["stagehand"] ?: listOf(), patchTree))
|
||||
|
||||
tasks.addAll(loadRegistry(worldObjects, patchTree, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName)))
|
||||
tasks.addAll(loadRegistry(monsterTypes, patchTree, fileTree["monstertype"] ?: listOf(), key(MonsterTypeDefinition::type)))
|
||||
|
@ -16,6 +16,7 @@ import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.ProjectileEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.StagehandEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.ContainerObject
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.LoungeableObject
|
||||
@ -102,11 +103,11 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
||||
|
||||
STAGEHAND("stagehand", "StagehandEntity", true, true) {
|
||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||
TODO("STAGEHAND")
|
||||
return StagehandEntity(stream, isLegacy)
|
||||
}
|
||||
|
||||
override fun fromStorage(data: JsonObject): AbstractEntity {
|
||||
TODO("STAGEHAND")
|
||||
return StagehandEntity(data)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.lua.bindings
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
@ -11,17 +12,22 @@ import org.classdump.luna.runtime.ExecutionContext
|
||||
import org.classdump.luna.runtime.LuaFunction
|
||||
import ru.dbotthepony.kommons.collect.map
|
||||
import ru.dbotthepony.kommons.collect.toList
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.defs.actor.NPCVariant
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
|
||||
import ru.dbotthepony.kstarbound.defs.world.TerrestrialWorldParameters
|
||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.contains
|
||||
@ -63,7 +69,10 @@ import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||
import ru.dbotthepony.kstarbound.world.castRay
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.PathFinder
|
||||
import ru.dbotthepony.kstarbound.world.entities.StagehandEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
@ -430,7 +439,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
||||
LOGGER.debug("Lua script tried to create non existing item {} at {}", itemType, pos)
|
||||
returnBuffer.setTo()
|
||||
} else {
|
||||
val create = ItemDropEntity(descriptor)
|
||||
val create = ItemDropEntity(descriptor, lua.random)
|
||||
create.movement.velocity = initialVelocity
|
||||
|
||||
if (intangibleTime is Number) {
|
||||
@ -453,8 +462,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
||||
val items = Registries.treasurePools
|
||||
.getOrThrow(pool.decode())
|
||||
.value
|
||||
// not using lua.random because we are, well, world's bindings
|
||||
.evaluate(if (seed != null) random(seed.toLong()) else self.random, level.toDouble())
|
||||
.evaluate(if (seed != null) random(seed.toLong()) else lua.random, level.toDouble())
|
||||
|
||||
val pos = toVector2d(position)
|
||||
|
||||
@ -465,18 +473,79 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
||||
entities.add(entity.entityID)
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while spawning treasure from $pool at $position", err)
|
||||
LOGGER.error("Exception while spawning treasure from pool '$pool' at $position", err)
|
||||
}
|
||||
|
||||
returnBuffer.setTo(tableOf(*entities.toTypedArray()))
|
||||
}
|
||||
|
||||
callbacks["spawnMonster"] = luaFunction {
|
||||
// TODO
|
||||
returnBuffer.setTo(0L)
|
||||
callbacks["spawnMonster"] = luaFunction { type: ByteString, position: Table, overrides: Any? ->
|
||||
try {
|
||||
val parameters = JsonObject()
|
||||
parameters["aggressive"] = lua.random.nextBoolean()
|
||||
|
||||
if (overrides != null) {
|
||||
mergeJson(parameters, toJsonFromLua(overrides))
|
||||
}
|
||||
|
||||
val level = parameters["level"]?.asDouble ?: 1.0
|
||||
val seed: Long
|
||||
|
||||
if ("seed" !in parameters || !parameters["seed"].asJsonPrimitive.isNumber) {
|
||||
seed = lua.random.nextLong()
|
||||
} else {
|
||||
seed = parameters["seed"].asLong
|
||||
}
|
||||
|
||||
val variant = Registries.monsterTypes.getOrThrow(type.decode()).value.create(seed, parameters)
|
||||
val monster = MonsterEntity(variant, level)
|
||||
monster.position = toVector2d(position)
|
||||
monster.joinWorld(self)
|
||||
returnBuffer.setTo(monster.entityID)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception caught while spawning Monster type $type", err)
|
||||
}
|
||||
}
|
||||
callbacks["spawnNpc"] = luaStub("spawnNpc")
|
||||
callbacks["spawnStagehand"] = luaStub("spawnStagehand")
|
||||
|
||||
callbacks["spawnNpc"] = luaFunctionN("spawnNpc") {
|
||||
val position = it.nextTable()
|
||||
val species = it.nextString().decode()
|
||||
val type = it.nextString().decode()
|
||||
val level = it.nextFloat()
|
||||
val seed = it.nextOptionalInteger() ?: lua.random.nextLong()
|
||||
val overrides = toJsonFromLua(it.nextOptionalAny(null))
|
||||
|
||||
try {
|
||||
// TODO: this blocks world thread
|
||||
val npc = runBlocking {
|
||||
NPCEntity(NPCVariant.create(
|
||||
Registries.species.getOrThrow(species),
|
||||
type,
|
||||
level,
|
||||
seed,
|
||||
overrides
|
||||
))
|
||||
}
|
||||
|
||||
npc.position = toVector2d(position)
|
||||
npc.joinWorld(self)
|
||||
returnBuffer.setTo(npc.entityID)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception caught while spawning NPC $type with species $species", err)
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["spawnStagehand"] = luaFunction { position: Table, type: ByteString, overrides: Any? ->
|
||||
try {
|
||||
val stagehand = StagehandEntity(type.decode(), toJsonFromLua(overrides))
|
||||
stagehand.position = toVector2d(position)
|
||||
stagehand.joinWorld(self)
|
||||
returnBuffer.setTo(stagehand.entityID)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception caught while spawning stagehand of type '$type'", err)
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["spawnProjectile"] = luaStub("spawnProjectile")
|
||||
callbacks["spawnVehicle"] = luaStub("spawnVehicle")
|
||||
|
||||
|
@ -72,7 +72,7 @@ private val centerStr = ByteString.of("center")
|
||||
private val boundModeStr = ByteString.of("boundMode")
|
||||
private val orderStr = ByteString.of("order")
|
||||
|
||||
private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table, predicate: Predicate<AbstractEntity> = Predicate { true }): Table {
|
||||
private fun ExecutionContext.entityQueryImpl(self: World<*, *>, lua: LuaEnvironment, options: Table, predicate: Predicate<AbstractEntity> = Predicate { true }): Table {
|
||||
val withoutEntityId = (indexNoYield(options, withoutEntityIdStr) as Number?)?.toInt()
|
||||
|
||||
val includedTypes = EnumSet.allOf(EntityType::class.java)
|
||||
@ -200,7 +200,7 @@ private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table,
|
||||
|
||||
when (val order = (indexNoYield(options, orderStr) as ByteString?)?.decode()?.lowercase()) {
|
||||
null -> {} // do nothing
|
||||
"random" -> entitites.shuffle(self.random)
|
||||
"random" -> entitites.shuffle(lua.random)
|
||||
"nearest" -> {
|
||||
val nearestPosition = lineQuery?.p0 ?: polyQuery?.centre ?: rectQuery?.centre ?: radiusQuery?.first ?: Vector2d.ZERO
|
||||
|
||||
@ -213,7 +213,7 @@ private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table,
|
||||
return tableOf(*entitites.map { it.entityID.toLong() }.toTypedArray())
|
||||
}
|
||||
|
||||
private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, pos1: Table, pos2OrRadius: Any, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||
private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, lua: LuaEnvironment, pos1: Table, pos2OrRadius: Any, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||
val actualOptions = options ?: tableOf()
|
||||
|
||||
if (pos2OrRadius is Number) {
|
||||
@ -224,35 +224,35 @@ private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, pos1:
|
||||
actualOptions[rectStr] = tableOf(pos1[1L], pos1[2L], pos2OrRadius[1L], pos2OrRadius[2L])
|
||||
}
|
||||
|
||||
returnBuffer.setTo(entityQueryImpl(self, actualOptions, predicate))
|
||||
returnBuffer.setTo(entityQueryImpl(self, lua, actualOptions, predicate))
|
||||
}
|
||||
|
||||
private fun ExecutionContext.intermediateLineQueryFunction(self: World<*, *>, pos1: Table, pos2: Table, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||
private fun ExecutionContext.intermediateLineQueryFunction(self: World<*, *>, lua: LuaEnvironment, pos1: Table, pos2: Table, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||
val actualOptions = options ?: tableOf()
|
||||
actualOptions[lineStr] = tableOf(pos1, pos2)
|
||||
returnBuffer.setTo(entityQueryImpl(self, actualOptions, predicate))
|
||||
returnBuffer.setTo(entityQueryImpl(self, lua, actualOptions, predicate))
|
||||
}
|
||||
|
||||
private inline fun <reified T : AbstractEntity> createQueryFunction(self: World<*, *>) = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate { it is T })
|
||||
private inline fun <reified T : AbstractEntity> createQueryFunction(self: World<*, *>, lua: LuaEnvironment) = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
||||
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate { it is T })
|
||||
}
|
||||
|
||||
private inline fun <reified T : AbstractEntity> createLineQueryFunction(self: World<*, *>) = luaFunction { pos1: Table, pos2: Table, options: Table? ->
|
||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate { it is T })
|
||||
private inline fun <reified T : AbstractEntity> createLineQueryFunction(self: World<*, *>, lua: LuaEnvironment) = luaFunction { pos1: Table, pos2: Table, options: Table? ->
|
||||
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate { it is T })
|
||||
}
|
||||
|
||||
fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEnvironment) {
|
||||
callbacks["entityQuery"] = createQueryFunction<AbstractEntity>(self)
|
||||
callbacks["monsterQuery"] = createQueryFunction<AbstractEntity>(self) // TODO
|
||||
callbacks["npcQuery"] = createQueryFunction<AbstractEntity>(self) // TODO
|
||||
callbacks["itemDropQuery"] = createQueryFunction<ItemDropEntity>(self)
|
||||
callbacks["playerQuery"] = createQueryFunction<PlayerEntity>(self)
|
||||
callbacks["entityQuery"] = createQueryFunction<AbstractEntity>(self, lua)
|
||||
callbacks["monsterQuery"] = createQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||
callbacks["npcQuery"] = createQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||
callbacks["itemDropQuery"] = createQueryFunction<ItemDropEntity>(self, lua)
|
||||
callbacks["playerQuery"] = createQueryFunction<PlayerEntity>(self, lua)
|
||||
|
||||
callbacks["entityLineQuery"] = createLineQueryFunction<AbstractEntity>(self)
|
||||
callbacks["monsterLineQuery"] = createLineQueryFunction<AbstractEntity>(self) // TODO
|
||||
callbacks["npcLineQuery"] = createLineQueryFunction<AbstractEntity>(self) // TODO
|
||||
callbacks["itemDropLineQuery"] = createLineQueryFunction<ItemDropEntity>(self)
|
||||
callbacks["playerLineQuery"] = createLineQueryFunction<PlayerEntity>(self)
|
||||
callbacks["entityLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua)
|
||||
callbacks["monsterLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||
callbacks["npcLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||
callbacks["itemDropLineQuery"] = createLineQueryFunction<ItemDropEntity>(self, lua)
|
||||
callbacks["playerLineQuery"] = createLineQueryFunction<PlayerEntity>(self, lua)
|
||||
|
||||
callbacks["objectQuery"] = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
||||
var objectName: String? = null
|
||||
@ -260,7 +260,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
||||
if (options != null)
|
||||
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
||||
|
||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate {
|
||||
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate {
|
||||
it is WorldObject && (objectName == null || it.config.key == objectName)
|
||||
})
|
||||
}
|
||||
@ -271,7 +271,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
||||
if (options != null)
|
||||
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
||||
|
||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate {
|
||||
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate {
|
||||
it is WorldObject && (objectName == null || it.config.key == objectName)
|
||||
})
|
||||
}
|
||||
@ -287,7 +287,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
||||
else -> LoungeOrientation.entries.valueOf(orientationName)
|
||||
}
|
||||
|
||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate {
|
||||
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate {
|
||||
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
||||
})
|
||||
}
|
||||
@ -303,7 +303,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
||||
else -> LoungeOrientation.entries.valueOf(orientationName!!)
|
||||
}
|
||||
|
||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate {
|
||||
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate {
|
||||
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
||||
})
|
||||
}
|
||||
|
@ -748,7 +748,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
||||
enqueueWarp(WarpAlias.OwnShip)
|
||||
}
|
||||
|
||||
//enqueueWarp(WarpAction.World(WorldID.Instance("outpost")))
|
||||
enqueueWarp(WarpAction.World(WorldID.Instance("outpost")))
|
||||
|
||||
scope.launch { shipFlightEventLoop(context.shipCoordinate, context.systemLocation) }
|
||||
}
|
||||
|
@ -670,6 +670,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
ticks++
|
||||
simulationTime += delta
|
||||
|
||||
// TODO: Zombie entity handling (don't tick entities ending up in partially loaded chunks,
|
||||
// or chunks which border with unloaded/not sufficiently loaded regions)
|
||||
if (entityList.size < 128) {
|
||||
entityList.forEach { it.tickParallel(delta) }
|
||||
} else {
|
||||
|
@ -30,6 +30,7 @@ import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.util.function.Predicate
|
||||
import java.util.random.RandomGenerator
|
||||
import kotlin.math.min
|
||||
|
||||
class ItemDropEntity() : DynamicEntity() {
|
||||
@ -70,8 +71,8 @@ class ItemDropEntity() : DynamicEntity() {
|
||||
movement.applyParameters(MovementParameters(collisionPoly = Either.left(Poly(AABB(Vector2d(-0.5, -0.5), Vector2d(0.5, 0.5))))))
|
||||
}
|
||||
|
||||
constructor(item: ItemDescriptor) : this() {
|
||||
this.item = item.build()
|
||||
constructor(item: ItemDescriptor, random: RandomGenerator? = null) : this() {
|
||||
this.item = item.build(random = random)
|
||||
this.owningEntity = 0
|
||||
this.state = State.AVAILABLE
|
||||
}
|
||||
|
@ -0,0 +1,152 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||
import ru.dbotthepony.kstarbound.json.putAll
|
||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
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.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEntity {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readJsonElement().asJsonObject, true)
|
||||
|
||||
constructor(data: JsonObject, isRemote: Boolean = false) : this(isRemote) {
|
||||
deserialize(data)
|
||||
}
|
||||
|
||||
constructor(type: String, overrides: JsonElement? = JsonNull.INSTANCE) : this(Registries.makeStagehandConfig(type, overrides))
|
||||
|
||||
var xPosition by networkedFloat().also { networkGroup.upstream.add(it) }
|
||||
var yPosition by networkedFloat().also { networkGroup.upstream.add(it) }
|
||||
|
||||
init {
|
||||
networkGroup.upstream.add(uniqueID)
|
||||
this.isRemote = isRemote
|
||||
}
|
||||
|
||||
override var position: Vector2d
|
||||
get() = Vector2d(xPosition, yPosition)
|
||||
set(value) {
|
||||
xPosition = value.x
|
||||
yPosition = value.y
|
||||
}
|
||||
|
||||
override val type: EntityType
|
||||
get() = EntityType.STAGEHAND
|
||||
|
||||
private var config: JsonObject = JsonObject()
|
||||
|
||||
var isScripted = false
|
||||
private set
|
||||
|
||||
val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
|
||||
init {
|
||||
lua.globals["storage"] = lua.tableOf()
|
||||
}
|
||||
|
||||
override fun deserialize(data: JsonObject) {
|
||||
super.deserialize(data)
|
||||
|
||||
config = data.deepCopy()
|
||||
isScripted = config["scripts"] is JsonArray
|
||||
|
||||
if ("position" in config) {
|
||||
val pos = config["position"].asJsonArray
|
||||
xPosition = pos[0].asDouble
|
||||
yPosition = pos[1].asDouble
|
||||
}
|
||||
|
||||
var broadcastArea: AABB? = null
|
||||
|
||||
if ("broadcastArea" in config) {
|
||||
broadcastArea = Starbound.gson.fromJsonFast(config["broadcastArena"], AABB::class.java)
|
||||
|
||||
if (broadcastArea.width < 0.0 || broadcastArea.height > 0.0)
|
||||
broadcastArea = null
|
||||
}
|
||||
|
||||
boundingBox = broadcastArea ?: AABB.withSide(Vector2d.ZERO, 5.0)
|
||||
|
||||
if (isScripted && isLocal) {
|
||||
lua.attach(config["scripts"].asJsonArray.map { AssetPath(it.asString) })
|
||||
luaUpdate.stepCount = config.get("scriptDelta", 5.0)
|
||||
|
||||
if ("scriptStorage" in config) {
|
||||
lua.globals["storage"] = lua.from(config["scriptStorage"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
super.onJoinWorld(world)
|
||||
|
||||
if (isLocal && isScripted) {
|
||||
lua.init()
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(data: JsonObject) {
|
||||
super.serialize(data)
|
||||
|
||||
data.putAll(config, true)
|
||||
data["position"] = jsonArrayOf(JsonPrimitive(xPosition), JsonPrimitive(yPosition))
|
||||
|
||||
if (uniqueID.get() != null)
|
||||
data["uniqueId"] = uniqueID.get()!!
|
||||
else
|
||||
data.remove("uniqueId")
|
||||
|
||||
if (isScripted)
|
||||
data["scriptStorage"] = toJsonFromLua(lua.globals["storage"])
|
||||
}
|
||||
|
||||
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.writeJsonElement(config)
|
||||
}
|
||||
|
||||
private var boundingBox = AABB.withSide(Vector2d.ZERO, 5.0)
|
||||
|
||||
override val metaBoundingBox: AABB
|
||||
get() = boundingBox + position
|
||||
override val name: String
|
||||
get() = "Stagehand"
|
||||
override val description: String
|
||||
get() = "Technical entity responsible for doing cool stuff"
|
||||
|
||||
override fun callScript(fnName: String, vararg arguments: Any?): Array<Any?> {
|
||||
return lua.call(fnName, *arguments)
|
||||
}
|
||||
|
||||
override fun evalScript(code: String): Array<Any?> {
|
||||
return lua.eval(code)
|
||||
}
|
||||
}
|
@ -318,7 +318,7 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
}
|
||||
|
||||
fun deserialize(data: SerializedData) {
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
// ----- Persistent status effects
|
||||
|
@ -549,7 +549,6 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
provideEntityBindings(this, lua)
|
||||
provideAnimatorBindings(animator, lua)
|
||||
lua.attach(config.value.scripts)
|
||||
lua.random = world.random
|
||||
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
||||
lua.init()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user