feat: projection person AI (incomplete)

This commit is contained in:
xtex 2023-07-02 17:34:30 +08:00
parent 24080f0437
commit 89473d2dcc
Signed by: xtex
GPG Key ID: B918086ED8045B91
5 changed files with 168 additions and 21 deletions

View File

@ -25,6 +25,7 @@ object Quaedam {
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 entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
val schedule = DeferredRegister.create(ID, Registries.SCHEDULE)!!
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") {
@ -43,6 +44,7 @@ object Quaedam {
blocks.register() blocks.register()
blockEntities.register() blockEntities.register()
entities.register() entities.register()
schedule.register()
projectionEffects.register() projectionEffects.register()
} }

View File

@ -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<out ProjectedPersonEntity> = Brain.provider(memoryTypes, sensorTypes)
fun initBrain(entity: ProjectedPersonEntity, brain: Brain<ProjectedPersonEntity>) {
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<ProjectedPersonEntity>) {
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<ProjectedPersonEntity>) {
brain.addActivity(Activity.IDLE, 99, ImmutableList.of(UpdateActivityFromSchedule.create()))
}
private fun initPlayActivity(brain: Brain<ProjectedPersonEntity>) {
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<ProjectedPersonEntity>) {
brain.addActivity(Activity.WORK, 99, ImmutableList.of(UpdateActivityFromSchedule.create()))
}
private fun initRestActivity(brain: Brain<ProjectedPersonEntity>) {
brain.addActivity(Activity.REST, 99, ImmutableList.of(UpdateActivityFromSchedule.create()))
}
}

View File

@ -1,7 +1,7 @@
package quaedam.projection.swarm package quaedam.projection.swarm
import com.mojang.serialization.Dynamic
import dev.architectury.platform.Platform import dev.architectury.platform.Platform
import dev.architectury.registry.client.level.entity.EntityRendererRegistry
import dev.architectury.registry.level.entity.EntityAttributeRegistry import dev.architectury.registry.level.entity.EntityAttributeRegistry
import net.fabricmc.api.EnvType import net.fabricmc.api.EnvType
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
@ -13,6 +13,7 @@ import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.DifficultyInstance import net.minecraft.world.DifficultyInstance
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.* 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.AttributeSupplier
import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.entity.item.ItemEntity
@ -22,8 +23,8 @@ import net.minecraft.world.level.ServerLevelAccessor
import quaedam.Quaedam import quaedam.Quaedam
import quaedam.projector.Projector import quaedam.projector.Projector
class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) : class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) : PathfinderMob(entityType, level),
PathfinderMob(entityType, level), InventoryCarrier { InventoryCarrier {
companion object { companion object {
@ -35,28 +36,24 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
const val BOUNDING_HEIGHT = 1.8f const val BOUNDING_HEIGHT = 1.8f
val entity = Quaedam.entities.register(ID) { val entity = Quaedam.entities.register(ID) {
EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE) EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE).canSpawnFarFromPlayer()
.canSpawnFarFromPlayer() .sized(BOUNDING_WIDTH, BOUNDING_HEIGHT * 1.2f).build("quaedam:$ID")
.sized(BOUNDING_WIDTH, BOUNDING_HEIGHT * 1.2f)
.build("quaedam:$ID")
}!! }!!
val dataShape = val dataShape =
SynchedEntityData.defineId(ProjectedPersonEntity::class.java, EntityDataSerializers.COMPOUND_TAG) 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 { init {
EntityAttributeRegistry.register(entity, ::createAttributes) EntityAttributeRegistry.register(entity, ::createAttributes)
if (Platform.getEnv() == EnvType.CLIENT) { if (Platform.getEnv() == EnvType.CLIENT) ProjectedPersonRenderer
EntityRendererRegistry.register(entity, ::ProjectedPersonRenderer)
}
ProjectedPersonShape 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( override fun finalizeSpawn(
@ -106,8 +103,8 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
override fun shouldShowName() = true override fun shouldShowName() = true
override fun getTypeName(): Component = shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } override fun getTypeName(): Component =
?: super.getTypeName() shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } ?: super.getTypeName()
override fun getNameTagOffsetY() = super.getNameTagOffsetY() - (BOUNDING_HEIGHT * (1.2f - shape.scaleY)) override fun getNameTagOffsetY() = super.getNameTagOffsetY() - (BOUNDING_HEIGHT * (1.2f - shape.scaleY))
@ -116,8 +113,7 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
override fun tick() { override fun tick() {
super.tick() super.tick()
if (tickCount % 20 == 0) { if (tickCount % 20 == 0) {
if (!checkProjectionEffect()) if (!checkProjectionEffect()) remove(RemovalReason.KILLED)
remove(RemovalReason.KILLED)
} }
} }
@ -126,8 +122,7 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
override fun checkDespawn() { override fun checkDespawn() {
super.checkDespawn() super.checkDespawn()
if (!checkProjectionEffect()) if (!checkProjectionEffect()) discard()
discard()
} }
private val inventory = SimpleContainer(10) private val inventory = SimpleContainer(10)
@ -146,4 +141,16 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
override fun removeWhenFarAway(d: Double) = false 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<out ProjectedPersonEntity> = brainProvider().makeBrain(dynamic)
.also { ProjectedPersonAI.initBrain(this, it as Brain<ProjectedPersonEntity>) }
@Suppress("UNCHECKED_CAST")
override fun getBrain(): Brain<ProjectedPersonEntity> = super.getBrain() as Brain<ProjectedPersonEntity>
override fun isBaby() = shape.baby
} }

View File

@ -1,6 +1,7 @@
package quaedam.projection.swarm package quaedam.projection.swarm
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import dev.architectury.registry.client.level.entity.EntityRendererRegistry
import net.fabricmc.api.EnvType import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment import net.fabricmc.api.Environment
import net.minecraft.client.model.PlayerModel import net.minecraft.client.model.PlayerModel
@ -18,6 +19,12 @@ class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
0.4f 0.4f
) { ) {
companion object {
init {
EntityRendererRegistry.register(ProjectedPersonEntity.entity, ::ProjectedPersonRenderer)
}
}
init { init {
addLayer(CustomHeadLayer(this, context.modelSet, context.itemInHandRenderer)) addLayer(CustomHeadLayer(this, context.modelSet, context.itemInHandRenderer))
addLayer(ItemInHandLayer(this, context.itemInHandRenderer)) addLayer(ItemInHandLayer(this, context.itemInHandRenderer))

View File

@ -22,6 +22,7 @@ data class ProjectedPersonShape(
val scaleZ: Float = 1.0f, val scaleZ: Float = 1.0f,
val name: String = "", val name: String = "",
val skin: Int = 0, val skin: Int = 0,
val baby: Boolean = false,
) { ) {
companion object { companion object {
@ -31,6 +32,7 @@ data class ProjectedPersonShape(
const val KEY_SCALE_Z = "ScaleZ" const val KEY_SCALE_Z = "ScaleZ"
const val KEY_NAME = "Name" const val KEY_NAME = "Name"
const val KEY_SKIN = "Skin" const val KEY_SKIN = "Skin"
const val KEY_BABY = "Baby"
init { init {
Names Names
@ -45,6 +47,7 @@ data class ProjectedPersonShape(
scaleZ = rand.nextInt(0..2 * 4) * 0.025f + 0.9f, scaleZ = rand.nextInt(0..2 * 4) * 0.025f + 0.9f,
name = Names.random(rand), name = Names.random(rand),
skin = Skins.random(rand), skin = Skins.random(rand),
baby = rand.nextInt(500) == 1
) )
fun fromTag(tag: CompoundTag) = ProjectedPersonShape( fun fromTag(tag: CompoundTag) = ProjectedPersonShape(
@ -53,6 +56,7 @@ data class ProjectedPersonShape(
scaleZ = tag.getFloat(KEY_SCALE_Z), scaleZ = tag.getFloat(KEY_SCALE_Z),
name = tag.getString(KEY_NAME), name = tag.getString(KEY_NAME),
skin = tag.getInt(KEY_SKIN), skin = tag.getInt(KEY_SKIN),
baby = tag.getBoolean(KEY_BABY)
) )
} }
@ -63,6 +67,7 @@ data class ProjectedPersonShape(
putFloat(KEY_SCALE_Z, scaleZ) putFloat(KEY_SCALE_Z, scaleZ)
putString(KEY_NAME, name) putString(KEY_NAME, name)
putInt(KEY_SKIN, skin) putInt(KEY_SKIN, skin)
putBoolean(KEY_BABY, baby)
} }
object Names { object Names {