mirror of
https://github.com/chylex/Hardcore-Ender-Expansion-2.git
synced 2025-02-27 15:46:01 +01:00
Implement Tomb Dungeon mob spawn triggers
This commit is contained in:
parent
5211731e62
commit
322c005648
src
main/java/chylex/hee
system/src/main/java/chylex/hee/network/fx
test/java/chylex/hee/test/main
@ -25,6 +25,7 @@ import chylex.hee.system.random.nextFloat
|
||||
import chylex.hee.system.random.nextInt
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.item.BlockItemUseContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.state.StateContainer.Builder
|
||||
@ -88,6 +89,12 @@ open class BlockGraveDirt(builder: BlockBuilder) : BlockSimpleShaped(builder, Ax
|
||||
super.getShape(state, source, pos, context)
|
||||
}
|
||||
|
||||
// Mobs
|
||||
|
||||
override fun canEntitySpawn(state: BlockState, worldIn: IBlockReader, pos: BlockPos, type: EntityType<*>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// Explosions
|
||||
|
||||
override fun canDropFromExplosion(explosion: Explosion): Boolean {
|
||||
|
@ -56,11 +56,15 @@ import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.CreatureAttribute
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.ILivingEntityData
|
||||
import net.minecraft.entity.SharedMonsterAttributes.ATTACK_DAMAGE
|
||||
import net.minecraft.entity.SharedMonsterAttributes.FOLLOW_RANGE
|
||||
import net.minecraft.entity.SharedMonsterAttributes.MAX_HEALTH
|
||||
import net.minecraft.entity.SharedMonsterAttributes.MOVEMENT_SPEED
|
||||
import net.minecraft.entity.SpawnReason
|
||||
import net.minecraft.entity.SpawnReason.SPAWNER
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier
|
||||
import net.minecraft.nbt.CompoundNBT
|
||||
import net.minecraft.network.IPacket
|
||||
import net.minecraft.network.datasync.DataSerializers
|
||||
import net.minecraft.pathfinding.PathNavigator
|
||||
@ -76,6 +80,8 @@ import net.minecraft.util.math.RayTraceResult.Type
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.Difficulty.HARD
|
||||
import net.minecraft.world.Difficulty.NORMAL
|
||||
import net.minecraft.world.DifficultyInstance
|
||||
import net.minecraft.world.IWorld
|
||||
import net.minecraft.world.IWorldReader
|
||||
import net.minecraft.world.LightType.BLOCK
|
||||
import net.minecraft.world.LightType.SKY
|
||||
@ -213,7 +219,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
}
|
||||
else if (wakeUpTimer > 0) {
|
||||
if (--wakeUpTimer == 0) {
|
||||
wakeUp(instant = false, preventSleep = false)
|
||||
wakeUp(preventSleep = false)
|
||||
}
|
||||
}
|
||||
else if (ticksExisted % 4 == 0) {
|
||||
@ -291,10 +297,18 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun wakeUp(instant: Boolean, preventSleep: Boolean) {
|
||||
fun wakeUpInstantly(preventSleep: Boolean) {
|
||||
wakeUp(preventSleep, aiDelayTicks = 1)
|
||||
}
|
||||
|
||||
fun wakeUp(preventSleep: Boolean) {
|
||||
wakeUp(preventSleep, aiDelayTicks = rand.nextInt(25, 40))
|
||||
}
|
||||
|
||||
fun wakeUp(preventSleep: Boolean, aiDelayTicks: Int) {
|
||||
if (isSleeping) {
|
||||
isSleepingProp = false
|
||||
wakeUpDelayAI = if (instant) 1 else rand.nextInt(25, 40)
|
||||
wakeUpDelayAI = aiDelayTicks
|
||||
|
||||
if (preventSleep) {
|
||||
canSleepAgain = false
|
||||
@ -303,7 +317,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
}
|
||||
|
||||
override fun setFire(seconds: Int) {
|
||||
wakeUp(instant = true, preventSleep = true)
|
||||
wakeUpInstantly(preventSleep = true)
|
||||
super.setFire(seconds)
|
||||
}
|
||||
|
||||
@ -314,7 +328,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
// Behavior (Light)
|
||||
|
||||
override fun onLightStartled(): Boolean {
|
||||
wakeUp(instant = false, preventSleep = true)
|
||||
wakeUp(preventSleep = true)
|
||||
|
||||
if (world.totalTime < lightStartleResetTime) {
|
||||
return false
|
||||
@ -401,7 +415,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
return false
|
||||
}
|
||||
|
||||
wakeUp(instant = true, preventSleep = true)
|
||||
wakeUpInstantly(preventSleep = true)
|
||||
|
||||
if (!super.attackEntityFrom(source, if (source.isFireDamage) amount * 1.25F else amount)) {
|
||||
return false
|
||||
@ -445,6 +459,16 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
return true
|
||||
}
|
||||
|
||||
// Spawning
|
||||
|
||||
override fun onInitialSpawn(world: IWorld, difficulty: DifficultyInstance, reason: SpawnReason, data: ILivingEntityData?, nbt: CompoundNBT?): ILivingEntityData? {
|
||||
if (reason == SPAWNER) {
|
||||
wakeUpInstantly(preventSleep = true)
|
||||
}
|
||||
|
||||
return super.onInitialSpawn(world, difficulty, reason, data, nbt)
|
||||
}
|
||||
|
||||
// Despawning
|
||||
|
||||
override fun isDespawnPeaceful(): Boolean {
|
||||
@ -515,7 +539,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
|
||||
val sleepState = getInt(SLEEP_STATE_TAG)
|
||||
|
||||
if (sleepState != 2) {
|
||||
wakeUp(instant = true, preventSleep = sleepState != 1)
|
||||
wakeUpInstantly(preventSleep = sleepState != 1)
|
||||
}
|
||||
|
||||
lightStartleResetTime = getLong(LIGHT_STARTLE_RESET_TIME_TAG)
|
||||
|
@ -137,19 +137,19 @@ class EntityMobUndread(type: EntityType<EntityMobUndread>, world: World) : Entit
|
||||
super.onDeathUpdate()
|
||||
}
|
||||
|
||||
override fun playStepSound(pos: BlockPos, state: BlockState) {
|
||||
public override fun playStepSound(pos: BlockPos, state: BlockState) {
|
||||
playSound(Sounds.ENTITY_ZOMBIE_STEP, rand.nextFloat(0.4F, 0.5F), rand.nextFloat(0.9F, 1F))
|
||||
}
|
||||
|
||||
override fun getHurtSound(source: DamageSource): SoundEvent {
|
||||
public override fun getHurtSound(source: DamageSource): SoundEvent {
|
||||
return ModSounds.MOB_UNDREAD_HURT
|
||||
}
|
||||
|
||||
override fun getDeathSound(): SoundEvent {
|
||||
public override fun getDeathSound(): SoundEvent {
|
||||
return ModSounds.MOB_UNDREAD_DEATH
|
||||
}
|
||||
|
||||
override fun getSoundPitch(): Float {
|
||||
public override fun getSoundPitch(): Float {
|
||||
return rand.nextFloat(0.8F, 1F)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Main_Portal
|
||||
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_CornerHoles
|
||||
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_Prison
|
||||
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_TallIntersection
|
||||
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb
|
||||
import chylex.hee.init.ModEntities
|
||||
import chylex.hee.system.delegate.NotifyOnChange
|
||||
import chylex.hee.system.serialization.TagCompound
|
||||
@ -28,6 +29,10 @@ class EntityTechnicalTrigger(type: EntityType<EntityTechnicalTrigger>, world: Wo
|
||||
this.type = type
|
||||
}
|
||||
|
||||
constructor(world: World, type: Types, nbt: TagCompound) : this(world, type) {
|
||||
handler.deserializeNBT(nbt)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TYPE_TAG = "Type"
|
||||
private const val DATA_TAG = "Data"
|
||||
@ -62,6 +67,7 @@ class EntityTechnicalTrigger(type: EntityType<EntityTechnicalTrigger>, world: Wo
|
||||
STRONGHOLD_TRAP_TALL_INTERSECTION({ StrongholdRoom_Trap_TallIntersection.Trigger }),
|
||||
ENERGY_SHRINE_GENERATOR({ EnergyShrineGenerator.GeneratorTrigger }),
|
||||
ENERGY_SHRINE_GLOBAL({ EnergyShrineRoom_Main_Start.Particles }),
|
||||
TOMB_DUNGEON_UNDREAD_SPAWNER(TombDungeonRoom_Tomb::MobSpawnerTrigger),
|
||||
OBSIDIAN_TOWER_TOP_GLOWSTONE(ObsidianTowerLevel_Top::GlowstoneTrigger),
|
||||
OBSIDIAN_TOWER_DEATH_ANIMATION(ObsidianTowerLevel_Top::DeathAnimationTrigger)
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package chylex.hee.game.world.feature.tombdungeon
|
||||
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount.HIGH
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount.MEDIUM
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_BASIC
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_BORDER
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_SPLIT
|
||||
@ -19,6 +21,7 @@ import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonAbstractPiece
|
||||
import chylex.hee.system.math.floorToInt
|
||||
import chylex.hee.system.random.nextFloat
|
||||
import chylex.hee.system.random.nextInt
|
||||
import chylex.hee.system.random.nextRounded
|
||||
import java.util.Random
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@ -101,4 +104,46 @@ enum class TombDungeonLevel(val isFancy: Boolean, private val corridorFactor: In
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun pickUndreadAndSpiderlingSpawns(rand: Random, amount: MobAmount): Pair<Int, Int> {
|
||||
val m = if (amount >= MEDIUM) 1 else 0
|
||||
val h = if (amount >= HIGH) 1 else 0
|
||||
|
||||
return when(this) {
|
||||
FIRST -> when(rand.nextInt(0, 3)) {
|
||||
0 -> MOBS(undreads = rand.nextRounded(0.2F), spiderlings = 1 + m + rand.nextInt(0, h))
|
||||
1 -> MOBS(undreads = rand.nextRounded(0.3F * m), spiderlings = 1 + h + rand.nextInt(0, m))
|
||||
2 -> MOBS(undreads = rand.nextRounded(0.3F * h), spiderlings = 2 + m)
|
||||
else -> MOBS(undreads = 0, spiderlings = 1 + rand.nextInt(0, 1 + m))
|
||||
}
|
||||
|
||||
SECOND -> when(rand.nextInt(0, 3)) {
|
||||
0 -> MOBS(undreads = rand.nextRounded(0.3F), spiderlings = 1 + m + rand.nextInt(0, h))
|
||||
1 -> MOBS(undreads = rand.nextRounded(0.4F * m), spiderlings = 1 + rand.nextInt(0, 2 * m))
|
||||
else -> MOBS(undreads = 0, spiderlings = rand.nextRounded(1.8F + (m * 0.55F) + (h * 0.55F)))
|
||||
}
|
||||
|
||||
THIRD -> when(rand.nextInt(0, 2)) {
|
||||
0 -> MOBS(undreads = rand.nextInt(0, 1 + h), spiderlings = rand.nextRounded(1.4F + (m * 0.5F)) + rand.nextInt(0, m))
|
||||
1 -> MOBS(undreads = rand.nextInt(0, 1 + m), spiderlings = rand.nextRounded(1.2F) + rand.nextInt(0, 2 * h))
|
||||
else -> MOBS(undreads = rand.nextInt(0, 1 + m), spiderlings = 2 + rand.nextInt(0, m + h))
|
||||
}
|
||||
|
||||
FOURTH -> when(rand.nextInt(0, 5)) {
|
||||
in 0..1 -> MOBS(undreads = 1 + rand.nextInt(h, 3 + h), spiderlings = rand.nextRounded(0.4F * m) + rand.nextInt(0, m + h))
|
||||
else -> MOBS(undreads = 2 + rand.nextInt(m, 2 * m), spiderlings = rand.nextRounded(0.3F + (m * 0.1F) + (h * 0.3F)))
|
||||
}
|
||||
|
||||
else -> when(rand.nextInt(0, 2)) {
|
||||
0 -> MOBS(undreads = 1 + h + rand.nextInt(m, 1 + (m * 2)) + rand.nextInt(h, 1 + h), spiderlings = rand.nextInt(0, m))
|
||||
else -> MOBS(undreads = 2 + h + rand.nextInt(m, 2 * m), spiderlings = rand.nextRounded(0.2F + (m * 0.2F)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MOBS(undreads: Int, spiderlings: Int) = undreads to spiderlings
|
||||
|
||||
enum class MobAmount {
|
||||
LOW, MEDIUM, HIGH
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,57 @@
|
||||
package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.block.BlockGraveDirt
|
||||
import chylex.hee.game.entity.living.EntityMobSpiderling
|
||||
import chylex.hee.game.entity.living.EntityMobUndread
|
||||
import chylex.hee.game.entity.posVec
|
||||
import chylex.hee.game.entity.selectExistingEntities
|
||||
import chylex.hee.game.entity.selectVulnerableEntities
|
||||
import chylex.hee.game.entity.technical.EntityTechnicalTrigger
|
||||
import chylex.hee.game.entity.technical.EntityTechnicalTrigger.ITriggerHandler
|
||||
import chylex.hee.game.entity.technical.EntityTechnicalTrigger.Types.TOMB_DUNGEON_UNDREAD_SPAWNER
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.distanceSqTo
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnection
|
||||
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnectionType.TOMB_ENTRANCE_INSIDE
|
||||
import chylex.hee.game.world.getBlock
|
||||
import chylex.hee.game.world.getState
|
||||
import chylex.hee.game.world.isAir
|
||||
import chylex.hee.game.world.offsetWhile
|
||||
import chylex.hee.game.world.playClient
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.game.world.structure.piece.IStructurePieceConnection
|
||||
import chylex.hee.game.world.structure.trigger.EntityStructureTrigger
|
||||
import chylex.hee.init.ModEntities
|
||||
import chylex.hee.network.client.PacketClientFX
|
||||
import chylex.hee.network.fx.FxVecData
|
||||
import chylex.hee.network.fx.FxVecHandler
|
||||
import chylex.hee.system.math.Vec
|
||||
import chylex.hee.system.math.Vec3
|
||||
import chylex.hee.system.math.addY
|
||||
import chylex.hee.system.math.directionTowards
|
||||
import chylex.hee.system.math.square
|
||||
import chylex.hee.system.math.toYaw
|
||||
import chylex.hee.system.migration.BlockWeb
|
||||
import chylex.hee.system.migration.EntityLivingBase
|
||||
import chylex.hee.system.migration.EntityPlayer
|
||||
import chylex.hee.system.migration.Facing.DOWN
|
||||
import chylex.hee.system.migration.Facing.NORTH
|
||||
import chylex.hee.system.migration.Facing.SOUTH
|
||||
import chylex.hee.system.random.nextFloat
|
||||
import chylex.hee.system.random.nextInt
|
||||
import chylex.hee.system.random.nextItem
|
||||
import chylex.hee.system.serialization.TagCompound
|
||||
import chylex.hee.system.serialization.use
|
||||
import net.minecraft.entity.EntitySpawnPlacementRegistry
|
||||
import net.minecraft.entity.SpawnReason.STRUCTURE
|
||||
import net.minecraft.util.Direction.Axis
|
||||
import net.minecraft.util.math.AxisAlignedBB
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import java.util.Random
|
||||
|
||||
abstract class TombDungeonRoom_Tomb(file: String, entranceY: Int, allowSecrets: Boolean, isFancy: Boolean) : TombDungeonRoom(file, isFancy) {
|
||||
final override val secretAttachWeight = if (allowSecrets) 2 else 0
|
||||
@ -13,4 +60,176 @@ abstract class TombDungeonRoom_Tomb(file: String, entranceY: Int, allowSecrets:
|
||||
final override val connections = arrayOf<IStructurePieceConnection>(
|
||||
TombDungeonConnection(TOMB_ENTRANCE_INSIDE, Pos(centerX, entranceY, maxZ), SOUTH)
|
||||
)
|
||||
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
super.generate(world, instance)
|
||||
generateSpawnerTrigger(world, instance)
|
||||
}
|
||||
|
||||
protected open fun generateSpawnerTrigger(world: IStructureWorld, instance: Instance) {
|
||||
val rand = world.rand
|
||||
val level = instance.context ?: return
|
||||
val mobAmount = getSpawnerTriggerMobAmount(rand, level) ?: return
|
||||
|
||||
val (undreads, spiderlings) = level.pickUndreadAndSpiderlingSpawns(rand, mobAmount)
|
||||
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = maxX, depth = maxZ, undreads, spiderlings)
|
||||
}
|
||||
|
||||
protected abstract fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount?
|
||||
|
||||
class MobSpawnerTrigger() : ITriggerHandler {
|
||||
companion object {
|
||||
private const val WIDTH_TAG = "Width"
|
||||
private const val DEPTH_TAG = "Depth"
|
||||
private const val UNDREADS_TAG = "Undreads"
|
||||
private const val SPIDERLINGS_TAG = "Spiderlings"
|
||||
|
||||
fun place(world: IStructureWorld, entrance: BlockPos, width: Int, depth: Int, undreads: Int, spiderlings: Int) {
|
||||
val nbt = MobSpawnerTrigger(width - 1.0, depth - 1.0, undreads, spiderlings).serializeNBT()
|
||||
|
||||
world.addTrigger(entrance, EntityStructureTrigger({ wrld ->
|
||||
EntityTechnicalTrigger(wrld, TOMB_DUNGEON_UNDREAD_SPAWNER, nbt).apply { rotationYaw = NORTH.horizontalAngle }
|
||||
}, yOffset = 0.5))
|
||||
}
|
||||
|
||||
val FX_SPAWN_UNDREAD = object : FxVecHandler() {
|
||||
override fun handle(world: World, rand: Random, vec: Vec3d) {
|
||||
EntityMobUndread(world).apply {
|
||||
setLocationAndAngles(vec.x, vec.y, vec.z, 0F, 0F)
|
||||
spawnExplosionParticle()
|
||||
deathSound.playClient(vec, soundCategory, volume = 1.2F, pitch = soundPitch * 0.7F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val FX_SPAWN_SPIDERLING = object : FxVecHandler() {
|
||||
override fun handle(world: World, rand: Random, vec: Vec3d) {
|
||||
EntityMobSpiderling(world).apply {
|
||||
setLocationAndAngles(vec.x, vec.y, vec.z, 0F, 0F)
|
||||
spawnExplosionParticle()
|
||||
ambientSound.playClient(vec, soundCategory, volume = 1F, pitch = soundPitch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var width = 0.0
|
||||
private var depth = 0.0
|
||||
private var undreads = 0
|
||||
private var spiderlings = 0
|
||||
|
||||
constructor(width: Double, depth: Double, undreads: Int, spiderlings: Int) : this() {
|
||||
this.width = width
|
||||
this.depth = depth
|
||||
this.undreads = undreads
|
||||
this.spiderlings = spiderlings
|
||||
}
|
||||
|
||||
override fun check(world: World): Boolean {
|
||||
return !world.isRemote
|
||||
}
|
||||
|
||||
override fun update(entity: EntityTechnicalTrigger) {
|
||||
val world = entity.world
|
||||
val facing = entity.horizontalFacing
|
||||
val vecF = Vec3.fromYaw(facing.horizontalAngle)
|
||||
val vecL = Vec3.fromYaw(facing.rotateYCCW().horizontalAngle)
|
||||
val vecR = Vec3.fromYaw(facing.rotateY().horizontalAngle)
|
||||
|
||||
val aabb = AxisAlignedBB(
|
||||
vecL.scale(width * 0.5).add(vecF.scale(0.3)),
|
||||
vecR.scale(width * 0.5).add(vecF.scale(depth)).addY(2.5)
|
||||
).offset(entity.posVec)
|
||||
|
||||
val nearbyPlayers = world.selectVulnerableEntities.inBox<EntityPlayer>(aabb)
|
||||
if (nearbyPlayers.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val rand = world.rand
|
||||
val minPlayerDist = ((depth * 0.4) + (width * 0.16)).coerceIn(1.5, 3.5)
|
||||
|
||||
for((entityCount, entityType, spawnParticle) in listOf(
|
||||
Triple(undreads, ModEntities.UNDREAD, FX_SPAWN_UNDREAD),
|
||||
Triple(spiderlings, ModEntities.SPIDERLING, FX_SPAWN_SPIDERLING)
|
||||
)) {
|
||||
repeat(entityCount) {
|
||||
var bestPos: BlockPos? = null
|
||||
|
||||
for(attempt in 1..75) {
|
||||
val pos = Pos(
|
||||
rand.nextFloat(aabb.minX, aabb.maxX),
|
||||
rand.nextFloat(aabb.minY, aabb.maxY) + 0.5,
|
||||
rand.nextFloat(aabb.minZ, aabb.maxZ),
|
||||
).offsetWhile(DOWN, 1..3) {
|
||||
it.isAir(world) || it.getBlock(world) is BlockWeb
|
||||
}
|
||||
|
||||
val collisionCheckAABB = entityType.getBoundingBoxWithSizeApplied(pos.x + 0.5, pos.y.toDouble(), pos.z + 0.5).grow(0.2, 0.0, 0.2)
|
||||
|
||||
if (EntitySpawnPlacementRegistry.func_223515_a(entityType, world, STRUCTURE, pos, rand) &&
|
||||
world.hasNoCollisions(collisionCheckAABB) &&
|
||||
world.selectExistingEntities.inBox<EntityLivingBase>(collisionCheckAABB.grow(0.4, 0.0, 0.4)).isEmpty() &&
|
||||
world.players.none { pos.distanceSqTo(it) < square(minPlayerDist) }
|
||||
) {
|
||||
bestPos = pos
|
||||
|
||||
if (entityType === ModEntities.UNDREAD) {
|
||||
if (rand.nextInt(5) == 0 || pos.down().getBlock(world) is BlockGraveDirt) {
|
||||
break
|
||||
}
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestPos != null) {
|
||||
val x = bestPos.x + rand.nextFloat(0.35, 0.65)
|
||||
val y = bestPos.y + bestPos.down().getState(world).getCollisionShape(world, bestPos).getEnd(Axis.Y) - 1.0
|
||||
val z = bestPos.z + rand.nextFloat(0.35, 0.65)
|
||||
val vec = Vec(x, y, z)
|
||||
val target = rand.nextItem(nearbyPlayers)
|
||||
|
||||
entityType.create(world)?.apply {
|
||||
setLocationAndAngles(x, y, z, vec.directionTowards(target.posVec).toYaw(), 0F)
|
||||
rotationYawHead = rotationYaw
|
||||
attackTarget = target
|
||||
onGround = true // allow instant pathfinding in MeleeAttackGoal
|
||||
|
||||
if (this is EntityMobSpiderling) {
|
||||
wakeUp(preventSleep = true, aiDelayTicks = if (rand.nextInt(10) == 0) rand.nextInt(4, 6) else rand.nextInt(11, 18))
|
||||
}
|
||||
|
||||
onInitialSpawn(world, world.getDifficultyForLocation(bestPos), STRUCTURE, null, null)
|
||||
world.addEntity(this)
|
||||
|
||||
PacketClientFX(spawnParticle, FxVecData(vec)).sendToAllAround(this, 24.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entity.remove()
|
||||
}
|
||||
|
||||
override fun nextTimer(rand: Random): Int {
|
||||
return 3
|
||||
}
|
||||
|
||||
override fun serializeNBT() = TagCompound().apply {
|
||||
putDouble(WIDTH_TAG, width)
|
||||
putDouble(DEPTH_TAG, depth)
|
||||
putInt(UNDREADS_TAG, undreads)
|
||||
putInt(SPIDERLINGS_TAG, spiderlings)
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: TagCompound) = nbt.use {
|
||||
width = getDouble(WIDTH_TAG)
|
||||
depth = getDouble(DEPTH_TAG)
|
||||
undreads = getInt(UNDREADS_TAG)
|
||||
spiderlings = getInt(SPIDERLINGS_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.block.BlockGraveDirt
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces
|
||||
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnection
|
||||
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnectionType.TOMB_ENTRANCE_INSIDE
|
||||
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb.MobSpawnerTrigger
|
||||
import chylex.hee.game.world.generation.IBlockPicker.Single
|
||||
import chylex.hee.game.world.generation.IBlockPicker.Single.Air
|
||||
import chylex.hee.game.world.math.Size
|
||||
@ -32,6 +35,8 @@ class TombDungeonRoom_Tomb_Mass(width: Int, depth: Int, private val border: Bool
|
||||
super.generate(world, instance)
|
||||
|
||||
val rand = world.rand
|
||||
val level = instance.context
|
||||
|
||||
val centerX = size.centerX
|
||||
val maxX = size.maxX
|
||||
val maxY = size.maxY
|
||||
@ -56,8 +61,21 @@ class TombDungeonRoom_Tomb_Mass(width: Int, depth: Int, private val border: Bool
|
||||
|
||||
if (rand.nextInt(6) == 0 && (border || split)) {
|
||||
placeJars(world, instance, listOf(Pos(centerX, 2, 1)))
|
||||
|
||||
if (level != null && rand.nextInt(9) != 0) {
|
||||
placeSpawnerTrigger(world, level)
|
||||
}
|
||||
}
|
||||
else if (level != null && rand.nextInt(3) != 0) {
|
||||
placeSpawnerTrigger(world, level)
|
||||
}
|
||||
|
||||
placeCobwebs(world, instance)
|
||||
}
|
||||
|
||||
private fun placeSpawnerTrigger(world: IStructureWorld, level: TombDungeonLevel) {
|
||||
val area = (size.x - 2) * (size.z - 2)
|
||||
val (undreads, spiderlings) = level.pickUndreadAndSpiderlingSpawns(world.rand, if (area <= 30) MobAmount.LOW else MobAmount.MEDIUM)
|
||||
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = size.maxX, depth = size.maxZ, undreads, spiderlings)
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.system.migration.Facing.NORTH
|
||||
import chylex.hee.system.random.nextInt
|
||||
import java.util.Random
|
||||
|
||||
class TombDungeonRoom_Tomb_MassSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
@ -32,4 +34,8 @@ class TombDungeonRoom_Tomb_MassSpacious(file: String, entranceY: Int, isFancy: B
|
||||
placeJars(world, instance, listOf(Pos(jarX, 3, maxZ - 2)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
|
||||
return MobAmount.HIGH.takeIf { rand.nextInt(11) < (if (level <= TombDungeonLevel.SECOND) 7 else 4) }
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,14 @@ package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.block.BlockGraveDirt
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.system.migration.Facing.EAST
|
||||
import chylex.hee.system.migration.Facing.WEST
|
||||
import chylex.hee.system.random.nextInt
|
||||
import chylex.hee.system.random.nextRounded
|
||||
import java.util.Random
|
||||
|
||||
class TombDungeonRoom_Tomb_MultiDeep(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
@ -37,4 +40,15 @@ class TombDungeonRoom_Tomb_MultiDeep(file: String, private val tombsPerColumn: I
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
|
||||
if (rand.nextBoolean()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return when {
|
||||
tombsPerColumn <= 6 -> MobAmount.MEDIUM
|
||||
else -> MobAmount.HIGH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import java.util.Random
|
||||
|
||||
class TombDungeonRoom_Tomb_MultiNarrow(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
@ -18,4 +21,16 @@ class TombDungeonRoom_Tomb_MultiNarrow(file: String, private val tombsPerColumn:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
|
||||
if (rand.nextInt(10) >= if (level.isFancy) 3 else 5) {
|
||||
return null
|
||||
}
|
||||
|
||||
return when {
|
||||
tombsPerColumn <= 4 -> MobAmount.LOW
|
||||
tombsPerColumn <= 6 -> MobAmount.MEDIUM
|
||||
else -> MobAmount.HIGH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,14 @@ package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.block.BlockGraveDirt
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.system.migration.Facing.EAST
|
||||
import chylex.hee.system.migration.Facing.WEST
|
||||
import chylex.hee.system.random.nextInt
|
||||
import chylex.hee.system.random.nextRounded
|
||||
import java.util.Random
|
||||
|
||||
class TombDungeonRoom_Tomb_MultiSpacious(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
@ -37,4 +40,15 @@ class TombDungeonRoom_Tomb_MultiSpacious(file: String, private val tombsPerColum
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
|
||||
if (rand.nextInt(10) >= 6) {
|
||||
return null
|
||||
}
|
||||
|
||||
return when {
|
||||
tombsPerColumn <= 6 -> MobAmount.MEDIUM
|
||||
else -> MobAmount.HIGH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.system.migration.Facing.SOUTH
|
||||
import java.util.Random
|
||||
|
||||
abstract class TombDungeonRoom_Tomb_Single(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
super.generate(world, instance)
|
||||
|
||||
if (world.rand.nextInt(10) < 3) {
|
||||
placeChest(world, instance, Pos(centerX, 1, maxZ - 4), SOUTH)
|
||||
}
|
||||
}
|
||||
|
||||
final override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
|
||||
return null
|
||||
}
|
||||
|
||||
protected fun placeSingleTombUndreadSpawner(world: IStructureWorld) {
|
||||
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = maxX, depth = maxZ, undreads = 1, spiderlings = 0)
|
||||
}
|
||||
}
|
@ -1,15 +1,13 @@
|
||||
package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
import chylex.hee.system.migration.Facing.SOUTH
|
||||
|
||||
open class TombDungeonRoom_Tomb_SingleNarrow(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
|
||||
class TombDungeonRoom_Tomb_SingleNarrow(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_Single(file, entranceY, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
super.generate(world, instance)
|
||||
|
||||
if (world.rand.nextInt(10) < 3) {
|
||||
placeChest(world, instance, Pos(centerX, 1, maxZ - 4), SOUTH)
|
||||
if (world.rand.nextInt(5) == 0) {
|
||||
placeSingleTombUndreadSpawner(world)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package chylex.hee.game.world.feature.tombdungeon.piece
|
||||
import chylex.hee.game.world.Pos
|
||||
import chylex.hee.game.world.structure.IStructureWorld
|
||||
|
||||
class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_SingleNarrow(file, entranceY, isFancy) {
|
||||
class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_Single(file, entranceY, isFancy) {
|
||||
override fun generate(world: IStructureWorld, instance: Instance) {
|
||||
super.generate(world, instance)
|
||||
|
||||
@ -14,6 +14,11 @@ class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy:
|
||||
Pos(centerX - 2, 4, if (rand.nextBoolean()) maxZ - 3 else maxZ - 4),
|
||||
Pos(centerX + 2, 4, if (rand.nextBoolean()) maxZ - 3 else maxZ - 4),
|
||||
))
|
||||
|
||||
placeSingleTombUndreadSpawner(world)
|
||||
}
|
||||
else if (rand.nextInt(10) < 4) {
|
||||
placeSingleTombUndreadSpawner(world)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import chylex.hee.game.item.ItemTableLink
|
||||
import chylex.hee.game.mechanics.scorching.ScorchingHelper
|
||||
import chylex.hee.game.mechanics.table.TableParticleHandler
|
||||
import chylex.hee.game.potion.PotionBanishment
|
||||
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb
|
||||
import chylex.hee.network.BaseClientPacket
|
||||
import chylex.hee.network.fx.IFxData
|
||||
import chylex.hee.network.fx.IFxHandler
|
||||
@ -79,7 +80,9 @@ class PacketClientFX<T : IFxData>() : BaseClientPacket() {
|
||||
EnderEyeSpawnerParticles.FX_PARTICLE,
|
||||
EndermanTeleportHandler.FX_TELEPORT_FAIL,
|
||||
EndermanTeleportHandler.FX_TELEPORT_OUT_OF_WORLD,
|
||||
PotionBanishment.FX_BANISH
|
||||
PotionBanishment.FX_BANISH,
|
||||
TombDungeonRoom_Tomb.MobSpawnerTrigger.FX_SPAWN_UNDREAD,
|
||||
TombDungeonRoom_Tomb.MobSpawnerTrigger.FX_SPAWN_SPIDERLING,
|
||||
)
|
||||
}
|
||||
|
||||
|
12
src/system/src/main/java/chylex/hee/network/fx/FxVecData.kt
Normal file
12
src/system/src/main/java/chylex/hee/network/fx/FxVecData.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package chylex.hee.network.fx
|
||||
|
||||
import chylex.hee.system.serialization.use
|
||||
import chylex.hee.system.serialization.writeVec
|
||||
import net.minecraft.network.PacketBuffer
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class FxVecData(private val vec: Vec3d) : IFxData {
|
||||
override fun write(buffer: PacketBuffer) = buffer.use {
|
||||
writeVec(vec)
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package chylex.hee.network.fx
|
||||
|
||||
import chylex.hee.system.serialization.readVec
|
||||
import chylex.hee.system.serialization.use
|
||||
import net.minecraft.network.PacketBuffer
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import java.util.Random
|
||||
|
||||
abstract class FxVecHandler : IFxHandler<FxVecData> {
|
||||
override fun handle(buffer: PacketBuffer, world: World, rand: Random) = buffer.use {
|
||||
handle(world, rand, readVec())
|
||||
}
|
||||
|
||||
abstract fun handle(world: World, rand: Random, vec: Vec3d)
|
||||
}
|
@ -2,6 +2,7 @@ package chylex.hee.test.main
|
||||
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
|
||||
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
|
||||
import java.util.Locale.ROOT
|
||||
import java.util.Random
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -11,19 +12,24 @@ fun main() {
|
||||
for(level in TombDungeonLevel.values()) {
|
||||
val reps = 100000
|
||||
|
||||
println("\n")
|
||||
println("-".repeat(level.name.length))
|
||||
println(level.name)
|
||||
println("-".repeat(level.name.length))
|
||||
println(MobAmount.values().joinToString("\n") { amount ->
|
||||
"\n " + amount.name + "\n\n" + (1..reps)
|
||||
println()
|
||||
println("== ${level.name} ${"=".repeat(24 - level.name.length)}")
|
||||
|
||||
for(amount in MobAmount.values()) {
|
||||
println()
|
||||
println(" ${amount.name}")
|
||||
println()
|
||||
|
||||
val results = (1..reps)
|
||||
.map { level.pickUndreadAndSpiderlingSpawns(rand, amount) }
|
||||
.groupingBy { it }
|
||||
.eachCount()
|
||||
.entries
|
||||
.map { ((it.value * 100.0) / reps).roundToInt() to it.key }
|
||||
.sortedWith(compareBy({ -it.first }, { -it.second.first }, { -it.second.second }))
|
||||
.joinToString("\n") { " U = ${it.second.first}, S = ${it.second.second} ... ${it.first} %" }
|
||||
})
|
||||
|
||||
println(results.joinToString("\n") { " U = ${it.second.first} S = ${it.second.second} ${it.first.toString().padStart(2)} %" })
|
||||
println(" U ~ ${"%.1f".format(ROOT, results.sumBy { it.second.first * it.first } * 0.01)} S ~ ${"%.1f".format(ROOT, results.sumBy { it.second.second * it.first } * 0.01)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user