Skip to content
This repository has been archived by the owner on May 27, 2024. It is now read-only.

Commit

Permalink
feat(spawning): Prevent spawning mobs in blocks, allow retrying the s…
Browse files Browse the repository at this point in the history
…pawn n blocks up when a block collision happens

feat(spawning): Attempt spawn command
  • Loading branch information
0ffz committed Mar 27, 2024
1 parent 15f5966 commit 5837855
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import com.ticxo.modelengine.api.ModelEngineAPI
import com.ticxo.modelengine.api.utils.data.io.SavedData
import org.bukkit.Color
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.CreatureSpawnEvent
import org.bukkit.event.entity.EntitySpawnEvent
import org.bukkit.event.world.EntitiesLoadEvent

class ModelEngineWorldListener : Listener {
Expand All @@ -21,6 +24,13 @@ class ModelEngineWorldListener : Listener {
}
}

@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
fun CreatureSpawnEvent.onSpawn() {
entity.toGearyOrNull()?.with { model: SetModelEngineModel ->
ensureModelLoaded(entity, model)
}
}

companion object {
/** Idempotent function that makes sure the correct model is loaded on an entity. */
fun ensureModelLoaded(bukkit: BukkitEntity, model: SetModelEngineModel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class SpawnConfig(
SpawnCategory.AMBIENT to 0
),
val spawnHeightRange: Int = 40,
val preventSpawningInsideBlock: Boolean = true,
val retriesUpWhenInsideBlock: Int = 3
) {
/** @return The spawn cap for that mob in config. */
fun getCreatureTypeCap(creatureType: SpawnCategory): Int = creatureTypeCaps[creatureType] ?: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.mineinabyss.geary.autoscan.AutoScan
import com.mineinabyss.geary.modules.GearyModule
import com.mineinabyss.geary.papermc.tracking.entities.helpers.spawnFromPrefab
import com.mineinabyss.geary.prefabs.PrefabKey
import com.mineinabyss.geary.serialization.serializers.InnerSerializer
import com.mineinabyss.geary.systems.accessors.*
import com.mineinabyss.geary.systems.builders.listener
import com.mineinabyss.geary.systems.query.ListenerQuery
Expand All @@ -18,8 +19,12 @@ import com.mineinabyss.mobzy.spawning.vertical.checkUp
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.builtins.serializer
import org.bukkit.Location
import org.bukkit.entity.LivingEntity
import org.bukkit.util.Vector
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.sign
import kotlin.random.Random

Expand All @@ -29,17 +34,29 @@ class WGRegions(
val keys: Set<String>
)

@Serializable
@SerialName("mobzy:spawn.spread")
@Serializable(with = SpawnSpread.Serializer::class)
class SpawnSpread(
val radius: Double,
)
) {
class Serializer : InnerSerializer<Double, SpawnSpread>(
"mobzy:spawn.spread",
Double.serializer(),
{ SpawnSpread(it) },
{ it.radius },
)
}

@Serializable
@SerialName("mobzy:spawn.amount")
@Serializable(with = SpawnAmount.Serializer::class)
class SpawnAmount(
val amount: IntRange,
)
) {
class Serializer : InnerSerializer<IntRange, SpawnAmount>(
"mobzy:spawn.amount",
IntRangeSerializer,
{ SpawnAmount(it) },
{ it.amount },
)
}

@Serializable
@SerialName("mobzy:spawn.type")
Expand Down Expand Up @@ -78,9 +95,7 @@ enum class SpawnPosition {
*/
data class DoSpawn(
val location: Location
) {
var spawnedAmount: Int = 0
}
)

@AutoScan
fun GearyModule.spawnRequestListener() = listener(object : ListenerQuery() {
Expand All @@ -93,15 +108,42 @@ fun GearyModule.spawnRequestListener() = listener(object : ListenerQuery() {
}).exec {
val location = spawnEvent.location
val spawns = amount?.randomOrMin() ?: 1
for (i in 0 until spawns) {
val config = mobzySpawning.config
repeat(spawns) {
val chosenLoc =
if (spawnPos != SpawnPosition.AIR)
getSpawnInRadius(location, radius) ?: location
else location

chosenLoc.spawnFromPrefab(type.prefab)
val prefab = type.prefab.toEntity()
chosenLoc.spawnFromPrefab(prefab).onSuccess { spawned ->
if (spawned !is LivingEntity || !config.preventSpawningInsideBlock) return@onSuccess
val bb = spawned.boundingBox
// We shrink the box by a bit since overlap checks are strict inequalities
val bbShrunk = spawned.boundingBox.apply {
expand(-0.1, -0.1, -0.1, -0.1, -0.1, -0.1)
}

repeat(config.retriesUpWhenInsideBlock + 1) { offsetY ->
checkLoop@for (x in floor(bb.minX).toInt()..ceil(bb.maxX).toInt())
for (y in floor(bb.minY).toInt()..ceil(bb.maxY).toInt())
for (z in floor(bb.minZ).toInt()..ceil(bb.maxZ).toInt())
if (chosenLoc.world.getBlockAt(x, y, z).collisionShape.boundingBoxes.any { shape ->
shape.shift(x.toDouble(), y.toDouble(), z.toDouble())
shape.overlaps(bbShrunk)
}) {
bb.shift(0.0, 1.0, 0.0)
bbShrunk.shift(0.0, 1.0, 0.0)
return@repeat
}
if(offsetY != 0) {
spawned.teleport(chosenLoc.clone().add(0.0, offsetY.toDouble(), 0.0))
}
return@onSuccess
}
spawned.remove()
}
}
spawnEvent.spawnedAmount = spawns
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class SpawnTask {
val choice: GearyEntity = WeightedDice(priorities).roll()

//TODO this should be immutable but bukkit doesn't have an immutable location!
val spawnLoc = spawnInfo.getSpawnFor(choice.get() ?: SpawnPosition.GROUND)
val spawnLoc = spawnInfo.getSpawnFor(choice.get<SpawnPosition>() ?: SpawnPosition.GROUND)
val spawnCheckLoc = spawnLoc.clone().add(0.0, -1.0, 0.0)
val success = temporaryEntity { target -> //TODO should just pass null for target
target.add<KeepArchetype>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mineinabyss.mobzy.spawning.vertical

import com.mineinabyss.geary.components.relations.InstanceOf
import com.mineinabyss.geary.datatypes.GearyEntity
import com.mineinabyss.geary.helpers.componentId
import com.mineinabyss.geary.helpers.getArchetype
import com.mineinabyss.geary.helpers.toGeary
Expand Down
19 changes: 17 additions & 2 deletions src/main/kotlin/com/mineinabyss/mobzy/DebugCommands.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mineinabyss.mobzy

import com.mineinabyss.geary.helpers.temporaryEntity
import com.mineinabyss.geary.prefabs.PrefabKey
import com.mineinabyss.idofront.commands.Command
import com.mineinabyss.idofront.commands.arguments.intArg
Expand All @@ -8,8 +9,7 @@ import com.mineinabyss.idofront.commands.extensions.actions.playerAction
import com.mineinabyss.idofront.messaging.broadcastVal
import com.mineinabyss.idofront.messaging.info
import com.mineinabyss.idofront.messaging.success
import com.mineinabyss.mobzy.spawning.DoSpawn
import com.mineinabyss.mobzy.spawning.PlayerGroups
import com.mineinabyss.mobzy.spawning.*
import com.mineinabyss.mobzy.spawning.vertical.VerticalSpawn
import org.bukkit.Bukkit
import kotlin.system.measureTimeMillis
Expand All @@ -19,6 +19,21 @@ fun Int.toChunkLoc() = (this % 16).let { if (it < 0) it + 16 else it }
//TODO move debugging into its own module (perhaps in Geary-addons)
internal fun Command.createDebugCommands() {
"spawn" {
"attempt" {
val prefab: String by stringArg()
val amount: Int by intArg { default = 1 }
val spread: Int by intArg { default = 0 }

playerAction {
temporaryEntity { spawnInfo ->
spawnInfo.set(SpawnAmount(amount..amount))
spawnInfo.set(SpawnSpread(spread.toDouble()))
spawnInfo.set(SpawnType(PrefabKey.of(prefab)))
spawnInfo.set(SpawnPosition.AIR)
spawnInfo.callEvent(DoSpawn(player.location))
}
}
}
"groups" {
action {
sender.info(PlayerGroups.group(Bukkit.getOnlinePlayers()))
Expand Down

0 comments on commit 5837855

Please sign in to comment.