diff --git a/common/src/main/kotlin/quaedam/Quaedam.kt b/common/src/main/kotlin/quaedam/Quaedam.kt index a05fe76..157e29c 100644 --- a/common/src/main/kotlin/quaedam/Quaedam.kt +++ b/common/src/main/kotlin/quaedam/Quaedam.kt @@ -25,6 +25,7 @@ object Quaedam { val blocks = DeferredRegister.create(ID, Registries.BLOCK)!! val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!! val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!! + val schedule = DeferredRegister.create(ID, Registries.SCHEDULE)!! val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!! val creativeModeTab: RegistrySupplier = creativeModeTabs.register("quaedam") { @@ -43,6 +44,7 @@ object Quaedam { blocks.register() blockEntities.register() entities.register() + schedule.register() projectionEffects.register() } diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonAI.kt b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonAI.kt new file mode 100644 index 0000000..05fc7fd --- /dev/null +++ b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonAI.kt @@ -0,0 +1,126 @@ +package quaedam.projection.swarm + +import com.google.common.collect.ImmutableList +import com.mojang.datafixers.util.Pair +import net.minecraft.world.entity.ai.Brain +import net.minecraft.world.entity.ai.behavior.* +import net.minecraft.world.entity.ai.memory.MemoryModuleType +import net.minecraft.world.entity.ai.sensing.SensorType +import net.minecraft.world.entity.schedule.Activity +import net.minecraft.world.entity.schedule.Schedule +import net.minecraft.world.entity.schedule.ScheduleBuilder +import quaedam.Quaedam + +object ProjectedPersonAI { + + private val memoryTypes = listOf( + MemoryModuleType.PATH, + MemoryModuleType.LOOK_TARGET, + MemoryModuleType.WALK_TARGET, + MemoryModuleType.ATTACK_TARGET, + MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, + MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, + MemoryModuleType.HURT_BY, + MemoryModuleType.ATTACK_COOLING_DOWN + ) + + private val sensorTypes = listOf( + SensorType.NEAREST_LIVING_ENTITIES, + SensorType.NEAREST_PLAYERS, + SensorType.HURT_BY, + SensorType.NEAREST_ITEMS + ) + + val defaultSchedule = Quaedam.schedule.register("projected_person_default") { + ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE) + .changeActivityAt(10, Activity.IDLE) + .changeActivityAt(2000, Activity.WORK) + .changeActivityAt(7300, Activity.IDLE) + .changeActivityAt(9000, Activity.WORK) + .changeActivityAt(10700, Activity.IDLE) + .changeActivityAt(11000, Activity.PLAY) + .changeActivityAt(11500, Activity.IDLE) + .changeActivityAt(12000, Activity.REST) + .build() + } + + val babySchedule = Quaedam.schedule.register("projected_person_baby") { + ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE) + .changeActivityAt(10, Activity.IDLE) + .changeActivityAt(3200, Activity.PLAY) + .changeActivityAt(7000, Activity.IDLE) + .changeActivityAt(9000, Activity.PLAY) + .changeActivityAt(11000, Activity.REST) + .build() + } + + fun provider(): Brain.Provider = Brain.provider(memoryTypes, sensorTypes) + + fun initBrain(entity: ProjectedPersonEntity, brain: Brain) { + if (entity.shape.baby) { + brain.schedule = babySchedule.get() + } else { + brain.schedule = defaultSchedule.get() + } + + initCoreActivity(brain) + initIdleActivity(brain) + initPlayActivity(brain) + initWorkActivity(brain) + initRestActivity(brain) + brain.setCoreActivities(setOf(Activity.CORE)) + brain.setDefaultActivity(Activity.IDLE) + brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime) + } + + private fun initCoreActivity(brain: Brain) { + brain.addActivity( + Activity.CORE, 0, ImmutableList.of( + Swim(0.8f), + InteractWithDoor.create(), + LookAtTargetSink(40, 70), + MoveToTargetSink(), + WakeUp.create(), + ) + ) + brain.addActivity( + Activity.CORE, 3, ImmutableList.of( + GoToWantedItem.create(0.7f, false, 7) + ) + ) + } + + private fun initIdleActivity(brain: Brain) { + brain.addActivity(Activity.IDLE, 99, ImmutableList.of(UpdateActivityFromSchedule.create())) + } + + private fun initPlayActivity(brain: Brain) { + brain.addActivity( + Activity.PLAY, 3, ImmutableList.of( + GoToWantedItem.create(1.75f, true, 32), + ) + ) + brain.addActivity( + Activity.PLAY, 5, ImmutableList.of( + JumpOnBed(0.5f), + RunOne( + listOf( + Pair.of(RandomStroll.stroll(0.5f), 2), + Pair.of(SetWalkTargetFromLookTarget.create(1.0f, 5), 2), + Pair.of(DoNothing(30, 60), 1) + ) + ), + ) + ) + brain.addActivity(Activity.PLAY, 99, ImmutableList.of(UpdateActivityFromSchedule.create())) + } + + private fun initWorkActivity(brain: Brain) { + brain.addActivity(Activity.WORK, 99, ImmutableList.of(UpdateActivityFromSchedule.create())) + } + + private fun initRestActivity(brain: Brain) { + brain.addActivity(Activity.REST, 99, ImmutableList.of(UpdateActivityFromSchedule.create())) + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonEntity.kt b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonEntity.kt index 126e8cd..f2f6811 100644 --- a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonEntity.kt +++ b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonEntity.kt @@ -1,7 +1,7 @@ package quaedam.projection.swarm +import com.mojang.serialization.Dynamic import dev.architectury.platform.Platform -import dev.architectury.registry.client.level.entity.EntityRendererRegistry import dev.architectury.registry.level.entity.EntityAttributeRegistry import net.fabricmc.api.EnvType import net.minecraft.nbt.CompoundTag @@ -13,6 +13,7 @@ import net.minecraft.network.syncher.SynchedEntityData import net.minecraft.world.DifficultyInstance import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.* +import net.minecraft.world.entity.ai.Brain import net.minecraft.world.entity.ai.attributes.AttributeSupplier import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.entity.item.ItemEntity @@ -22,8 +23,8 @@ import net.minecraft.world.level.ServerLevelAccessor import quaedam.Quaedam import quaedam.projector.Projector -class ProjectedPersonEntity(entityType: EntityType, level: Level) : - PathfinderMob(entityType, level), InventoryCarrier { +class ProjectedPersonEntity(entityType: EntityType, level: Level) : PathfinderMob(entityType, level), + InventoryCarrier { companion object { @@ -35,28 +36,24 @@ class ProjectedPersonEntity(entityType: EntityType, level: Le const val BOUNDING_HEIGHT = 1.8f val entity = Quaedam.entities.register(ID) { - EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE) - .canSpawnFarFromPlayer() - .sized(BOUNDING_WIDTH, BOUNDING_HEIGHT * 1.2f) - .build("quaedam:$ID") + EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE).canSpawnFarFromPlayer() + .sized(BOUNDING_WIDTH, BOUNDING_HEIGHT * 1.2f).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) - if (Platform.getEnv() == EnvType.CLIENT) { - EntityRendererRegistry.register(entity, ::ProjectedPersonRenderer) - } + if (Platform.getEnv() == EnvType.CLIENT) ProjectedPersonRenderer ProjectedPersonShape + ProjectedPersonAI } + private fun createAttributes(): AttributeSupplier.Builder = + Mob.createMobAttributes().add(Attributes.ATTACK_DAMAGE, 1.5).add(Attributes.MOVEMENT_SPEED, 0.11) + .add(Attributes.ATTACK_SPEED) + } override fun finalizeSpawn( @@ -106,8 +103,8 @@ class ProjectedPersonEntity(entityType: EntityType, level: Le override fun shouldShowName() = true - override fun getTypeName(): Component = shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } - ?: super.getTypeName() + override fun getTypeName(): Component = + shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } ?: super.getTypeName() override fun getNameTagOffsetY() = super.getNameTagOffsetY() - (BOUNDING_HEIGHT * (1.2f - shape.scaleY)) @@ -116,8 +113,7 @@ class ProjectedPersonEntity(entityType: EntityType, level: Le override fun tick() { super.tick() if (tickCount % 20 == 0) { - if (!checkProjectionEffect()) - remove(RemovalReason.KILLED) + if (!checkProjectionEffect()) remove(RemovalReason.KILLED) } } @@ -126,8 +122,7 @@ class ProjectedPersonEntity(entityType: EntityType, level: Le override fun checkDespawn() { super.checkDespawn() - if (!checkProjectionEffect()) - discard() + if (!checkProjectionEffect()) discard() } private val inventory = SimpleContainer(10) @@ -146,4 +141,16 @@ class ProjectedPersonEntity(entityType: EntityType, level: Le override fun removeWhenFarAway(d: Double) = false + // Type signature referenced from: https://github.com/bbrk24/amurians-mod/blob/7a0f0c3c7a3e84c22e5c631286ad23795207adc0/src/main/kotlin/org/bbrk24/amurians/amurian/AmurianEntity.kt#L220 + override fun brainProvider() = ProjectedPersonAI.provider() + + @Suppress("UNCHECKED_CAST") + override fun makeBrain(dynamic: Dynamic<*>): Brain = brainProvider().makeBrain(dynamic) + .also { ProjectedPersonAI.initBrain(this, it as Brain) } + + @Suppress("UNCHECKED_CAST") + override fun getBrain(): Brain = super.getBrain() as Brain + + override fun isBaby() = shape.baby + } \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonRenderer.kt b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonRenderer.kt index 28fceb0..383c38d 100644 --- a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonRenderer.kt +++ b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonRenderer.kt @@ -1,6 +1,7 @@ package quaedam.projection.swarm import com.mojang.blaze3d.vertex.PoseStack +import dev.architectury.registry.client.level.entity.EntityRendererRegistry import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.client.model.PlayerModel @@ -18,6 +19,12 @@ class ProjectedPersonRenderer(context: EntityRendererProvider.Context) : 0.4f ) { + companion object { + init { + EntityRendererRegistry.register(ProjectedPersonEntity.entity, ::ProjectedPersonRenderer) + } + } + init { addLayer(CustomHeadLayer(this, context.modelSet, context.itemInHandRenderer)) addLayer(ItemInHandLayer(this, context.itemInHandRenderer)) diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonShape.kt b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonShape.kt index edfb599..4e160dc 100644 --- a/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonShape.kt +++ b/common/src/main/kotlin/quaedam/projection/swarm/ProjectedPersonShape.kt @@ -22,6 +22,7 @@ data class ProjectedPersonShape( val scaleZ: Float = 1.0f, val name: String = "", val skin: Int = 0, + val baby: Boolean = false, ) { companion object { @@ -31,6 +32,7 @@ data class ProjectedPersonShape( const val KEY_SCALE_Z = "ScaleZ" const val KEY_NAME = "Name" const val KEY_SKIN = "Skin" + const val KEY_BABY = "Baby" init { Names @@ -45,6 +47,7 @@ data class ProjectedPersonShape( scaleZ = rand.nextInt(0..2 * 4) * 0.025f + 0.9f, name = Names.random(rand), skin = Skins.random(rand), + baby = rand.nextInt(500) == 1 ) fun fromTag(tag: CompoundTag) = ProjectedPersonShape( @@ -53,6 +56,7 @@ data class ProjectedPersonShape( scaleZ = tag.getFloat(KEY_SCALE_Z), name = tag.getString(KEY_NAME), skin = tag.getInt(KEY_SKIN), + baby = tag.getBoolean(KEY_BABY) ) } @@ -63,6 +67,7 @@ data class ProjectedPersonShape( putFloat(KEY_SCALE_Z, scaleZ) putString(KEY_NAME, name) putInt(KEY_SKIN, skin) + putBoolean(KEY_BABY, baby) } object Names {