Little work for npc entity
This commit is contained in:
parent
f77cf29567
commit
7babb729b0
@ -4,7 +4,10 @@ import com.google.common.collect.ImmutableList
|
|||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonPrimitive
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapterFactory
|
import com.google.gson.TypeAdapterFactory
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
@ -14,6 +17,7 @@ import kotlinx.coroutines.future.asCompletableFuture
|
|||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.defs.AssetReference
|
import ru.dbotthepony.kstarbound.defs.AssetReference
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageKind
|
import ru.dbotthepony.kstarbound.defs.DamageKind
|
||||||
@ -21,14 +25,13 @@ import ru.dbotthepony.kstarbound.defs.Json2Function
|
|||||||
import ru.dbotthepony.kstarbound.defs.JsonConfigFunction
|
import ru.dbotthepony.kstarbound.defs.JsonConfigFunction
|
||||||
import ru.dbotthepony.kstarbound.defs.JsonFunction
|
import ru.dbotthepony.kstarbound.defs.JsonFunction
|
||||||
import ru.dbotthepony.kstarbound.defs.MarkovTextGenerator
|
import ru.dbotthepony.kstarbound.defs.MarkovTextGenerator
|
||||||
import ru.dbotthepony.kstarbound.defs.Species
|
import ru.dbotthepony.kstarbound.defs.actor.Species
|
||||||
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
|
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.ThingDescription
|
import ru.dbotthepony.kstarbound.defs.ThingDescription
|
||||||
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
|
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterSkillDefinition
|
import ru.dbotthepony.kstarbound.defs.monster.MonsterSkillDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
|
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.npc.NpcTypeDefinition
|
import ru.dbotthepony.kstarbound.defs.actor.TenantDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.npc.TenantDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.player.TechDefinition
|
import ru.dbotthepony.kstarbound.defs.actor.player.TechDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.ParticleConfig
|
import ru.dbotthepony.kstarbound.defs.animation.ParticleConfig
|
||||||
@ -54,6 +57,7 @@ import ru.dbotthepony.kstarbound.item.ItemRegistry
|
|||||||
import ru.dbotthepony.kstarbound.json.JsonPatch
|
import ru.dbotthepony.kstarbound.json.JsonPatch
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.fromJsonTreeFast
|
import ru.dbotthepony.kstarbound.json.fromJsonTreeFast
|
||||||
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType
|
import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType
|
||||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
@ -62,6 +66,7 @@ import java.util.*
|
|||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
@ -87,7 +92,6 @@ object Registries {
|
|||||||
val jsonFunctions = Registry<JsonFunction>("json function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val jsonFunctions = Registry<JsonFunction>("json function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val json2Functions = Registry<Json2Function>("json 2function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val json2Functions = Registry<Json2Function>("json 2function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val jsonConfigFunctions = Registry<JsonConfigFunction>("json config function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val jsonConfigFunctions = Registry<JsonConfigFunction>("json config function").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val npcTypes = Registry<NpcTypeDefinition>("npc type").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
|
||||||
val projectiles = Registry<ProjectileDefinition>("projectile").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val projectiles = Registry<ProjectileDefinition>("projectile").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val tenants = Registry<TenantDefinition>("tenant").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val tenants = Registry<TenantDefinition>("tenant").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val treasurePools = Registry<TreasurePoolDefinition>("treasure pool").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val treasurePools = Registry<TreasurePoolDefinition>("treasure pool").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
@ -110,14 +114,14 @@ object Registries {
|
|||||||
val damageKinds = Registry<DamageKind>("damage kind").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val damageKinds = Registry<DamageKind>("damage kind").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
|
|
||||||
private val monsterParts = HashMap<Pair<String, String>, HashMap<String, Pair<MonsterPartDefinition, IStarboundFile>>>()
|
private val monsterParts = HashMap<Pair<String, String>, HashMap<String, Pair<MonsterPartDefinition, IStarboundFile>>>()
|
||||||
private val loggedMisses = Collections.synchronizedSet(ObjectOpenHashSet<Pair<String, String>>())
|
private val loggedMonsterPartMisses = Collections.synchronizedSet(ObjectOpenHashSet<Pair<String, String>>())
|
||||||
|
|
||||||
fun selectMonsterPart(category: String, type: String, random: RandomGenerator): MonsterPartDefinition? {
|
fun selectMonsterPart(category: String, type: String, random: RandomGenerator): MonsterPartDefinition? {
|
||||||
val key = category to type
|
val key = category to type
|
||||||
val get = monsterParts[key]
|
val get = monsterParts[key]
|
||||||
|
|
||||||
if (get.isNullOrEmpty()) {
|
if (get.isNullOrEmpty()) {
|
||||||
if (loggedMisses.add(key)) {
|
if (loggedMonsterPartMisses.add(key)) {
|
||||||
LOGGER.error("No such monster part combination of category '$category' and type '$type'")
|
LOGGER.error("No such monster part combination of category '$category' and type '$type'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +161,49 @@ object Registries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val npcTypes = HashMap<String, Pair<IStarboundFile, JsonObject>>()
|
||||||
|
|
||||||
|
fun buildNPCConfig(type: String, overrides: JsonElement = JsonNull.INSTANCE): JsonObject {
|
||||||
|
val baseConfig = npcTypes.getOrElse(type) { throw NoSuchElementException("No such NPC type $type") }.second
|
||||||
|
val config = mergeJson(baseConfig.deepCopy(), overrides)
|
||||||
|
|
||||||
|
if ("baseType" in baseConfig) {
|
||||||
|
return buildNPCConfig(baseConfig["baseType"].asString, config)
|
||||||
|
} else {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadNpcTypes(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()]) as JsonObject
|
||||||
|
val type = elem.get("type").asString
|
||||||
|
|
||||||
|
if ("scripts" in elem) {
|
||||||
|
val fPath = listedFile.computeFullPath()
|
||||||
|
val scripts = elem["scripts"].asJsonArray
|
||||||
|
|
||||||
|
for (i in 0 until scripts.size()) {
|
||||||
|
scripts[i] = JsonPrimitive(AssetPathStack.relativeTo(fPath, scripts[i].asString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Starbound.submit {
|
||||||
|
val existing = npcTypes.put(type, listedFile to elem)
|
||||||
|
|
||||||
|
if (existing != null) {
|
||||||
|
LOGGER.warn("Overwriting existing NPC definition '$type' (new originate from $listedFile, old originating from ${existing.first})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.error("Loading NPC type definition file $listedFile", err)
|
||||||
|
}
|
||||||
|
}.asCompletableFuture()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T> key(mapper: (T) -> String): (T) -> Pair<String, KOptional<Int?>> {
|
private fun <T> key(mapper: (T) -> String): (T) -> Pair<String, KOptional<Int?>> {
|
||||||
return { mapper.invoke(it) to KOptional() }
|
return { mapper.invoke(it) to KOptional() }
|
||||||
}
|
}
|
||||||
@ -236,7 +283,6 @@ object Registries {
|
|||||||
tasks.addAll(loadRegistry(particles, patchTree, fileTree["particle"] ?: listOf(), { (it.kind ?: throw NullPointerException("Missing 'kind' value")) to KOptional() }))
|
tasks.addAll(loadRegistry(particles, patchTree, fileTree["particle"] ?: listOf(), { (it.kind ?: throw NullPointerException("Missing 'kind' value")) to KOptional() }))
|
||||||
tasks.addAll(loadRegistry(questTemplates, patchTree, fileTree["questtemplate"] ?: listOf(), key(QuestTemplate::id)))
|
tasks.addAll(loadRegistry(questTemplates, patchTree, fileTree["questtemplate"] ?: listOf(), key(QuestTemplate::id)))
|
||||||
tasks.addAll(loadRegistry(techs, patchTree, fileTree["tech"] ?: listOf(), key(TechDefinition::name)))
|
tasks.addAll(loadRegistry(techs, patchTree, fileTree["tech"] ?: listOf(), key(TechDefinition::name)))
|
||||||
tasks.addAll(loadRegistry(npcTypes, patchTree, fileTree["npctype"] ?: listOf(), key(NpcTypeDefinition::type)))
|
|
||||||
tasks.addAll(loadRegistry(biomes, patchTree, fileTree["biome"] ?: listOf(), key(BiomeDefinition::name)))
|
tasks.addAll(loadRegistry(biomes, patchTree, fileTree["biome"] ?: listOf(), key(BiomeDefinition::name)))
|
||||||
tasks.addAll(loadRegistry(grassVariants, patchTree, fileTree["grass"] ?: listOf(), key(GrassVariant.Data::name)))
|
tasks.addAll(loadRegistry(grassVariants, patchTree, fileTree["grass"] ?: listOf(), key(GrassVariant.Data::name)))
|
||||||
tasks.addAll(loadRegistry(treeStemVariants, patchTree, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name)))
|
tasks.addAll(loadRegistry(treeStemVariants, patchTree, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name)))
|
||||||
@ -259,6 +305,8 @@ object Registries {
|
|||||||
// declaring game data
|
// declaring game data
|
||||||
tasks.addAll(loadMixed(spawnTypes, fileTree["spawntypes"] ?: listOf(), patchTree, SpawnType::name))
|
tasks.addAll(loadMixed(spawnTypes, fileTree["spawntypes"] ?: listOf(), patchTree, SpawnType::name))
|
||||||
|
|
||||||
|
tasks.addAll(loadNpcTypes(fileTree["npctype"] ?: listOf(), patchTree))
|
||||||
|
|
||||||
return tasks
|
return tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.actor
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||||
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.nextRange
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
data class NPCVariant(
|
||||||
|
val species: Registry.Entry<Species>,
|
||||||
|
val scripts: ImmutableList<AssetPath>,
|
||||||
|
val initialScriptDelta: Int = 5,
|
||||||
|
) {
|
||||||
|
@JsonFactory
|
||||||
|
data class SerializedData(
|
||||||
|
val levelVariance: Vector2d = Vector2d.ZERO,
|
||||||
|
val scripts: ImmutableList<AssetPath>,
|
||||||
|
val initialScriptDelta: Int = 5,
|
||||||
|
val scriptConfig: JsonElement = JsonNull.INSTANCE,
|
||||||
|
val humanoidConfig: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
suspend fun create(species: Registry.Entry<Species>, type: String, level: Double, random: RandomGenerator, overrides: JsonElement = JsonNull.INSTANCE): NPCVariant {
|
||||||
|
val config = Registries.buildNPCConfig(type, overrides)
|
||||||
|
|
||||||
|
val serialized = Starbound.gson.fromJson(config, SerializedData::class.java)
|
||||||
|
val finalLevel = max(0.0, random.nextRange(serialized.levelVariance) + level)
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun create(species: Registry.Entry<Species>, type: String, level: Double, seed: Long, overrides: JsonElement = JsonNull.INSTANCE): NPCVariant {
|
||||||
|
return create(species, type, level, random(seed), overrides)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.AssetReference
|
import ru.dbotthepony.kstarbound.defs.AssetReference
|
||||||
import ru.dbotthepony.kstarbound.defs.Species
|
import ru.dbotthepony.kstarbound.defs.actor.Species
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.StatusControllerConfig
|
import ru.dbotthepony.kstarbound.defs.actor.StatusControllerConfig
|
||||||
import ru.dbotthepony.kstarbound.util.SBPattern
|
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.npc
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
|
||||||
|
|
||||||
@JsonFactory
|
|
||||||
data class NpcTypeDefinition(
|
|
||||||
val type: String,
|
|
||||||
val baseType: String? = null,
|
|
||||||
)
|
|
@ -461,7 +461,7 @@ fun provideRootBindings(lua: LuaEnvironment) {
|
|||||||
table["imageSize"] = imageSize
|
table["imageSize"] = imageSize
|
||||||
table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces)
|
table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces)
|
||||||
table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion)
|
table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion)
|
||||||
table["npcConfig"] = registryDef(Registries.npcTypes)
|
//table["npcConfig"] = registryDef(Registries.npcTypes)
|
||||||
|
|
||||||
table["npcVariant"] = luaStub("npcVariant")
|
table["npcVariant"] = luaStub("npcVariant")
|
||||||
table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier")
|
table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier")
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.api.InteractiveEntity
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
|
class NPCEntity : ActorEntity(), InteractiveEntity, ScriptedEntity {
|
||||||
|
override val type: EntityType
|
||||||
|
get() = EntityType.NPC
|
||||||
|
override val statusController: StatusController
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
override val damageBarType: DamageBarType
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
|
||||||
|
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val metaBoundingBox: AABB
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
override val name: String
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
override val description: String
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
|
||||||
|
override fun callScript(fnName: String, vararg arguments: Any?): Array<Any?> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun evalScript(code: String): Array<Any?> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user