Little work for npc entity

This commit is contained in:
DBotThePony 2024-08-10 19:07:58 +07:00
parent f77cf29567
commit 7babb729b0
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 136 additions and 18 deletions

View File

@ -4,7 +4,10 @@ import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.gson.GsonBuilder
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 com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
@ -14,6 +17,7 @@ import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.gson.contains
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.defs.AssetReference
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.JsonFunction
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.ThingDescription
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
import ru.dbotthepony.kstarbound.defs.monster.MonsterSkillDefinition
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
import ru.dbotthepony.kstarbound.defs.npc.NpcTypeDefinition
import ru.dbotthepony.kstarbound.defs.npc.TenantDefinition
import ru.dbotthepony.kstarbound.defs.actor.TenantDefinition
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
import ru.dbotthepony.kstarbound.defs.actor.player.TechDefinition
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.builder.JsonFactory
import ru.dbotthepony.kstarbound.json.fromJsonTreeFast
import ru.dbotthepony.kstarbound.json.mergeJson
import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType
import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.util.random.random
@ -62,6 +66,7 @@ import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import java.util.random.RandomGenerator
import kotlin.NoSuchElementException
import kotlin.collections.ArrayList
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 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 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 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()) }
@ -110,14 +114,14 @@ object Registries {
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 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? {
val key = category to type
val get = monsterParts[key]
if (get.isNullOrEmpty()) {
if (loggedMisses.add(key)) {
if (loggedMonsterPartMisses.add(key)) {
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?>> {
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(questTemplates, patchTree, fileTree["questtemplate"] ?: listOf(), key(QuestTemplate::id)))
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(grassVariants, patchTree, fileTree["grass"] ?: listOf(), key(GrassVariant.Data::name)))
tasks.addAll(loadRegistry(treeStemVariants, patchTree, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name)))
@ -259,6 +305,8 @@ object Registries {
// declaring game data
tasks.addAll(loadMixed(spawnTypes, fileTree["spawntypes"] ?: listOf(), patchTree, SpawnType::name))
tasks.addAll(loadNpcTypes(fileTree["npctype"] ?: listOf(), patchTree))
return tasks
}

View File

@ -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)
}
}
}

View File

@ -10,7 +10,7 @@ import ru.dbotthepony.kstarbound.math.vector.Vector2d
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
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.util.SBPattern
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition

View File

@ -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,
)

View File

@ -461,7 +461,7 @@ fun provideRootBindings(lua: LuaEnvironment) {
table["imageSize"] = imageSize
table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces)
table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion)
table["npcConfig"] = registryDef(Registries.npcTypes)
//table["npcConfig"] = registryDef(Registries.npcTypes)
table["npcVariant"] = luaStub("npcVariant")
table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier")

View File

@ -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")
}
}