feat: swarm projected entity
This commit is contained in:
parent
b3743bf624
commit
23b30a1084
7
Makefile
Normal file
7
Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
common/src/main/resources/data/quaedam/projected-person-names:
|
||||||
|
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/English_Names_Corpus/English_Names_Corpus%EF%BC%882W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 > $@.tmp
|
||||||
|
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/Chinese_Names_Corpus/Chinese_Names_Corpus%EF%BC%88120W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 >> $@.tmp
|
||||||
|
cat $@.tmp | tr -d '\015' > $@
|
||||||
|
rm $@.tmp
|
||||||
|
|
||||||
|
.PHONY: common/src/main/resources/data/quaedam/projected-person-names
|
@ -8,6 +8,7 @@ import net.minecraft.network.chat.Component
|
|||||||
import net.minecraft.world.item.CreativeModeTab
|
import net.minecraft.world.item.CreativeModeTab
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
import net.minecraft.world.item.Items
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
import quaedam.projection.SkylightProjection
|
import quaedam.projection.SkylightProjection
|
||||||
import quaedam.projection.swarm.SwarmProjection
|
import quaedam.projection.swarm.SwarmProjection
|
||||||
@ -17,10 +18,13 @@ object Quaedam {
|
|||||||
|
|
||||||
const val ID = "quaedam"
|
const val ID = "quaedam"
|
||||||
|
|
||||||
|
val logger = LoggerFactory.getLogger("Quaedam")
|
||||||
|
|
||||||
val creativeModeTabs = DeferredRegister.create(ID, Registries.CREATIVE_MODE_TAB)!!
|
val creativeModeTabs = DeferredRegister.create(ID, Registries.CREATIVE_MODE_TAB)!!
|
||||||
val items = DeferredRegister.create(ID, Registries.ITEM)!!
|
val items = DeferredRegister.create(ID, Registries.ITEM)!!
|
||||||
val blocks = DeferredRegister.create(ID, Registries.BLOCK)!!
|
val blocks = DeferredRegister.create(ID, Registries.BLOCK)!!
|
||||||
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
|
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
|
||||||
|
val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
|
||||||
val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!!
|
val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!!
|
||||||
|
|
||||||
val creativeModeTab: RegistrySupplier<CreativeModeTab> = creativeModeTabs.register("quaedam") {
|
val creativeModeTab: RegistrySupplier<CreativeModeTab> = creativeModeTabs.register("quaedam") {
|
||||||
@ -38,6 +42,7 @@ object Quaedam {
|
|||||||
items.register()
|
items.register()
|
||||||
blocks.register()
|
blocks.register()
|
||||||
blockEntities.register()
|
blockEntities.register()
|
||||||
|
entities.register()
|
||||||
projectionEffects.register()
|
projectionEffects.register()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@ abstract class ProjectionEffect {
|
|||||||
|
|
||||||
open fun deactivate(level: Level, pos: BlockPos) {}
|
open fun deactivate(level: Level, pos: BlockPos) {}
|
||||||
|
|
||||||
open fun update(level: Level, pos: BlockPos, old: ProjectionEffect) {}
|
|
||||||
|
|
||||||
open fun randomTick(level: ServerLevel, pos: BlockPos) {}
|
open fun randomTick(level: ServerLevel, pos: BlockPos) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package quaedam.projection.swarm
|
||||||
|
|
||||||
|
import dev.architectury.registry.client.level.entity.EntityRendererRegistry
|
||||||
|
import dev.architectury.registry.level.entity.EntityAttributeRegistry
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData
|
||||||
|
import net.minecraft.world.DifficultyInstance
|
||||||
|
import net.minecraft.world.entity.*
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.ServerLevelAccessor
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) :
|
||||||
|
PathfinderMob(entityType, level) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val ID = "projected_person"
|
||||||
|
|
||||||
|
val entity = Quaedam.entities.register(ID) {
|
||||||
|
EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE)
|
||||||
|
.canSpawnFarFromPlayer()
|
||||||
|
.sized(2.0f, 2.0f)
|
||||||
|
.build("quaedam:$ID")
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val dataShape =
|
||||||
|
SynchedEntityData.defineId(ProjectedPersonEntity::class.java, EntityDataSerializers.COMPOUND_TAG)
|
||||||
|
|
||||||
|
private fun createAttributes(): AttributeSupplier.Builder = Mob.createMobAttributes()
|
||||||
|
.add(Attributes.ATTACK_DAMAGE, 1.5)
|
||||||
|
.add(Attributes.MOVEMENT_SPEED, 0.11)
|
||||||
|
.add(Attributes.ATTACK_SPEED)
|
||||||
|
|
||||||
|
init {
|
||||||
|
EntityAttributeRegistry.register(entity, ::createAttributes)
|
||||||
|
EntityRendererRegistry.register(entity, ::ProjectedPersonRenderer)
|
||||||
|
ProjectedPersonShape
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finalizeSpawn(
|
||||||
|
serverLevelAccessor: ServerLevelAccessor,
|
||||||
|
difficultyInstance: DifficultyInstance,
|
||||||
|
mobSpawnType: MobSpawnType,
|
||||||
|
spawnGroupData: SpawnGroupData?,
|
||||||
|
compoundTag: CompoundTag?
|
||||||
|
): SpawnGroupData? {
|
||||||
|
shape = ProjectedPersonShape.create(serverLevelAccessor.random.nextLong())
|
||||||
|
return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, mobSpawnType, spawnGroupData, compoundTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineSynchedData() {
|
||||||
|
super.defineSynchedData()
|
||||||
|
entityData.define(dataShape, CompoundTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shapeTag
|
||||||
|
get() = entityData.get(dataShape)
|
||||||
|
set(value) = entityData.set(dataShape, value)
|
||||||
|
|
||||||
|
var shape = ProjectedPersonShape()
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
shapeTag = shape.toTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSyncedDataUpdated(data: EntityDataAccessor<*>) {
|
||||||
|
if (data == dataShape) {
|
||||||
|
shape = ProjectedPersonShape.fromTag(shapeTag)
|
||||||
|
}
|
||||||
|
super.onSyncedDataUpdated(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldShowName() = true
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package quaedam.projection.swarm
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import net.minecraft.client.model.PlayerModel
|
||||||
|
import net.minecraft.client.model.geom.ModelLayers
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider
|
||||||
|
import net.minecraft.client.renderer.entity.MobRenderer
|
||||||
|
import net.minecraft.client.renderer.entity.layers.CustomHeadLayer
|
||||||
|
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer
|
||||||
|
|
||||||
|
class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
|
||||||
|
MobRenderer<ProjectedPersonEntity, PlayerModel<ProjectedPersonEntity>>(
|
||||||
|
context,
|
||||||
|
PlayerModel(context.bakeLayer(ModelLayers.PLAYER), false),
|
||||||
|
0.4f
|
||||||
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
addLayer(CustomHeadLayer(this, context.modelSet, context.itemInHandRenderer))
|
||||||
|
addLayer(ItemInHandLayer(this, context.itemInHandRenderer))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTextureLocation(entity: ProjectedPersonEntity) = ProjectedPersonShape.Skins[entity.shape.skin]
|
||||||
|
|
||||||
|
override fun scale(entity: ProjectedPersonEntity, poseStack: PoseStack, f: Float) {
|
||||||
|
poseStack.scale(entity.shape.scaleX, entity.shape.scaleY, entity.shape.scaleZ)
|
||||||
|
super.scale(entity, poseStack, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package quaedam.projection.swarm
|
||||||
|
|
||||||
|
import dev.architectury.registry.ReloadListenerRegistry
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.server.packs.PackType
|
||||||
|
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import java.util.function.IntFunction
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextInt
|
||||||
|
|
||||||
|
data class ProjectedPersonShape(
|
||||||
|
val scaleX: Float = 1.0f,
|
||||||
|
val scaleY: Float = 1.0f,
|
||||||
|
val scaleZ: Float = 1.0f,
|
||||||
|
val name: String = "[DESYNC]",
|
||||||
|
val skin: Int = 0,
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val KEY_SCALE_X = "ScaleX"
|
||||||
|
const val KEY_SCALE_Y = "ScaleY"
|
||||||
|
const val KEY_SCALE_Z = "ScaleZ"
|
||||||
|
const val KEY_NAME = "Name"
|
||||||
|
const val KEY_SKIN = "Skin"
|
||||||
|
|
||||||
|
init {
|
||||||
|
Names
|
||||||
|
Skins
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(seed: Long) = create(Random(seed))
|
||||||
|
|
||||||
|
fun create(rand: Random) = ProjectedPersonShape(
|
||||||
|
scaleX = rand.nextInt(0..6) * 0.1f + 0.7f,
|
||||||
|
scaleY = rand.nextInt(0..6) * 0.1f + 0.7f,
|
||||||
|
scaleZ = rand.nextInt(0..2) * 0.1f + 0.9f,
|
||||||
|
name = Names.random(rand),
|
||||||
|
skin = Skins.random(rand),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fromTag(tag: CompoundTag) = ProjectedPersonShape(
|
||||||
|
scaleX = tag.getFloat(KEY_SCALE_X),
|
||||||
|
scaleY = tag.getFloat(KEY_SCALE_Y),
|
||||||
|
scaleZ = tag.getFloat(KEY_SCALE_Z),
|
||||||
|
name = tag.getString(KEY_NAME),
|
||||||
|
skin = tag.getInt(KEY_SKIN),
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toTag() = CompoundTag().apply {
|
||||||
|
putFloat(KEY_SCALE_X, scaleX)
|
||||||
|
putFloat(KEY_SCALE_Y, scaleY)
|
||||||
|
putFloat(KEY_SCALE_Z, scaleZ)
|
||||||
|
putString(KEY_NAME, name)
|
||||||
|
putInt(KEY_SKIN, skin)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Names {
|
||||||
|
|
||||||
|
val id = ResourceLocation("quaedam", "projected-person-names")
|
||||||
|
|
||||||
|
var names = emptySet<String>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
ReloadListenerRegistry.register(PackType.SERVER_DATA, ReloadListener, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun random(random: Random) = names.random(random)
|
||||||
|
|
||||||
|
private object ReloadListener : PreparableReloadListener {
|
||||||
|
|
||||||
|
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||||
|
override fun reload(
|
||||||
|
preparationBarrier: PreparableReloadListener.PreparationBarrier,
|
||||||
|
resourceManager: ResourceManager,
|
||||||
|
profilerFiller: ProfilerFiller,
|
||||||
|
profilerFiller2: ProfilerFiller,
|
||||||
|
executor: Executor,
|
||||||
|
executor2: Executor
|
||||||
|
): CompletableFuture<Void> = preparationBarrier.wait(null)
|
||||||
|
.thenAcceptAsync({
|
||||||
|
names = resourceManager.getResource(id).get().openAsReader().use { it.readLines() }
|
||||||
|
.filterNot { it.isBlank() }
|
||||||
|
.filterNot { it.startsWith("#") }
|
||||||
|
.toSet()
|
||||||
|
Quaedam.logger.info("Loaded ${names.size} unique projected person names")
|
||||||
|
}, executor2)
|
||||||
|
|
||||||
|
override fun getName() = "quaedam:projected_person_names"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object Skins {
|
||||||
|
|
||||||
|
val id = ResourceLocation("quaedam", "skins")
|
||||||
|
|
||||||
|
// only available on client
|
||||||
|
var skins = emptyList<ResourceLocation>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
ReloadListenerRegistry.register(PackType.CLIENT_RESOURCES, ReloadListener, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(index: Int) = skins[abs(index) % skins.size]
|
||||||
|
fun random(random: Random) = random.nextInt()
|
||||||
|
|
||||||
|
private object ReloadListener : PreparableReloadListener {
|
||||||
|
|
||||||
|
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||||
|
override fun reload(
|
||||||
|
preparationBarrier: PreparableReloadListener.PreparationBarrier,
|
||||||
|
resourceManager: ResourceManager,
|
||||||
|
profilerFiller: ProfilerFiller,
|
||||||
|
profilerFiller2: ProfilerFiller,
|
||||||
|
executor: Executor,
|
||||||
|
executor2: Executor
|
||||||
|
): CompletableFuture<Void> = preparationBarrier.wait(null)
|
||||||
|
.thenAcceptAsync({
|
||||||
|
val skins = mutableSetOf<ResourceLocation>()
|
||||||
|
skins.addAll(resourceManager.listResources("textures/entity/player/wide") { it.path.endsWith(".png") }.keys)
|
||||||
|
skins.addAll(resourceManager.listResources("textures/entity/projected_person") { it.namespace == "quaedam" }.keys)
|
||||||
|
Skins.skins = skins.toSet().toList().sorted()
|
||||||
|
Quaedam.logger.info("Loaded ${Skins.skins.size} unique projected person skins")
|
||||||
|
Quaedam.logger.debug("Projected person skins ring: $skins")
|
||||||
|
}, executor2)
|
||||||
|
|
||||||
|
override fun getName() = "quaedam:projected_person_skins"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -23,4 +23,8 @@ object SwarmProjection {
|
|||||||
ProjectionEffectType { SwarmProjectionEffect() }
|
ProjectionEffectType { SwarmProjectionEffect() }
|
||||||
}!!
|
}!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
ProjectedPersonEntity
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -12,6 +12,7 @@ import net.minecraft.world.level.ChunkPos
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraft.world.level.levelgen.structure.BoundingBox
|
import net.minecraft.world.level.levelgen.structure.BoundingBox
|
||||||
|
import quaedam.Quaedam
|
||||||
import quaedam.projection.ProjectionEffect
|
import quaedam.projection.ProjectionEffect
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
import quaedam.projection.ProjectionProvider
|
import quaedam.projection.ProjectionProvider
|
||||||
@ -62,7 +63,7 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateEffects(effects)
|
updateEffects(effects, notify = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
@ -89,17 +90,22 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
fun updateEffects(effects: Map<ProjectionEffectType<*>, ProjectionEffect>, notify: Boolean = true) {
|
fun updateEffects(effects: Map<ProjectionEffectType<*>, ProjectionEffect>, notify: Boolean = true) {
|
||||||
if (effects != this.effects) {
|
if (effects != this.effects) {
|
||||||
val oldEffects = this.effects
|
val oldEffects = this.effects
|
||||||
val level = level!!
|
|
||||||
this.effects = effects
|
this.effects = effects
|
||||||
if (!level.isClientSide) {
|
if (level != null) {
|
||||||
sendBlockUpdated()
|
val level = level!!
|
||||||
|
if (level.isClientSide && notify) {
|
||||||
|
sendBlockUpdated()
|
||||||
|
}
|
||||||
|
val addedEffects = effects.filterKeys { it !in oldEffects }
|
||||||
|
val removedEffects = oldEffects.filterKeys { it !in effects }
|
||||||
|
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != null && oldEffects[k] != v }
|
||||||
|
addedEffects.values.forEach { it.activate(level, blockPos) }
|
||||||
|
removedEffects.values.forEach { it.deactivate(level, blockPos) }
|
||||||
|
updatedEffects.forEach { (k, v) ->
|
||||||
|
oldEffects[k]!!.deactivate(level, blockPos)
|
||||||
|
v.activate(level, blockPos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val addedEffects = effects.filterKeys { it !in oldEffects }
|
|
||||||
val removedEffects = oldEffects.filterKeys { it !in effects }
|
|
||||||
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != v }
|
|
||||||
addedEffects.values.forEach { it.activate(level, blockPos) }
|
|
||||||
removedEffects.values.forEach { it.deactivate(level, blockPos) }
|
|
||||||
updatedEffects.forEach { (k, v) -> v.update(level, blockPos, oldEffects[k]!!) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5000
common/src/main/resources/data/quaedam/projected-person-names
Normal file
5000
common/src/main/resources/data/quaedam/projected-person-names
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@ architectury {
|
|||||||
loom {
|
loom {
|
||||||
accessWidenerPath.set(project(":common").loom.accessWidenerPath)
|
accessWidenerPath.set(project(":common").loom.accessWidenerPath)
|
||||||
|
|
||||||
forge.apply {
|
forge {
|
||||||
convertAccessWideners.set(true)
|
convertAccessWideners.set(true)
|
||||||
extraAccessWideners.add(loom.accessWidenerPath.get().asFile.name)
|
extraAccessWideners.add(loom.accessWidenerPath.get().asFile.name)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user