9
0
mirror of https://github.com/Auxilor/EcoMobs.git synced 2025-12-19 15:09:17 +00:00

Added custom spawn method

This commit is contained in:
Will FP
2023-11-28 13:40:35 +00:00
parent d6358d82e9
commit 393c9d62d1
14 changed files with 308 additions and 4 deletions

View File

@@ -40,6 +40,7 @@ allprojects {
compileOnly("com.willfp:eco:6.65.0")
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.1.5")
}
java {

View File

@@ -5,6 +5,7 @@ import com.willfp.eco.core.display.DisplayModule
import com.willfp.eco.core.entities.ai.EntityGoals
import com.willfp.eco.core.integrations.IntegrationLoader
import com.willfp.ecomobs.category.MobCategories
import com.willfp.ecomobs.category.spawning.spawnpoints.SpawnPointGenerator
import com.willfp.ecomobs.commands.CommandEcoMobs
import com.willfp.ecomobs.display.SpawnEggDisplay
import com.willfp.ecomobs.goals.entity.EntityGoalRandomTeleport
@@ -25,6 +26,8 @@ internal lateinit var plugin: EcoMobsPlugin
private set
class EcoMobsPlugin : LibreforgePlugin() {
val spawnPointGenerator = SpawnPointGenerator(this)
init {
plugin = this
}

View File

@@ -15,7 +15,12 @@ class ConfigDrivenMobCategory(
private val context: ViolationContext
) : MobCategory {
override val spawnMethod = SpawnMethodFactories[config.getString("spawning.type")]
?.create(this, config.getSubsection("spawning.${config.getString("spawning.type")}"), plugin)
?.create(
this,
config.getSubsection("spawning.${config.getString("spawning.type")}"),
plugin,
context.with("spawning").with(config.getString("spawning.type"))
)
?: throw ConfigViolationException(
ConfigViolation(
"type",

View File

@@ -1,6 +1,7 @@
package com.willfp.ecomobs.category.spawning
import com.willfp.eco.core.registry.Registry
import com.willfp.ecomobs.category.spawning.impl.SpawnMethodFactoryCustom
import com.willfp.ecomobs.category.spawning.impl.SpawnMethodFactoryNone
import com.willfp.ecomobs.category.spawning.impl.SpawnMethodFactoryReplace
@@ -8,5 +9,6 @@ object SpawnMethodFactories : Registry<SpawnMethodFactory>() {
init {
register(SpawnMethodFactoryReplace)
register(SpawnMethodFactoryNone)
register(SpawnMethodFactoryCustom)
}
}

View File

@@ -4,7 +4,13 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.registry.KRegistrable
import com.willfp.ecomobs.category.MobCategory
import com.willfp.libreforge.ViolationContext
abstract class SpawnMethodFactory(override val id: String) : KRegistrable {
abstract fun create(category: MobCategory, config: Config, plugin: EcoPlugin): SpawnMethod
abstract fun create(
category: MobCategory,
config: Config,
plugin: EcoPlugin,
context: ViolationContext
): SpawnMethod
}

View File

@@ -0,0 +1,83 @@
package com.willfp.ecomobs.category.spawning.impl
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.entities.Entities
import com.willfp.eco.util.randDouble
import com.willfp.ecomobs.category.MobCategory
import com.willfp.ecomobs.category.spawning.SpawnMethod
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
import com.willfp.ecomobs.category.spawning.spawnpoints.SpawnPointType
import com.willfp.ecomobs.category.spawning.spawnpoints.spawnPoints
import com.willfp.ecomobs.mob.SpawnReason
import com.willfp.libreforge.EmptyProvidedHolder
import com.willfp.libreforge.ViolationContext
import com.willfp.libreforge.conditions.Conditions
import com.willfp.libreforge.enumValueOfOrNull
import com.willfp.libreforge.toDispatcher
import org.bukkit.Bukkit
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.CreatureSpawnEvent
import org.bukkit.scheduler.BukkitTask
object SpawnMethodFactoryCustom : SpawnMethodFactory("custom") {
override fun create(
category: MobCategory,
config: Config,
plugin: EcoPlugin,
context: ViolationContext
): SpawnMethod {
return SpawnMethodCustom(category, config, plugin, context)
}
class SpawnMethodCustom(
category: MobCategory,
config: Config,
plugin: EcoPlugin,
context: ViolationContext
) : SpawnMethod(category, config, plugin), Listener {
private val spawnRate = plugin.configYml.getInt("custom-spawning.spawn-rate").toLong()
private val spawnTypes = config.getStrings("spawn-types")
.mapNotNull { enumValueOfOrNull<SpawnPointType>(it.uppercase()) }
.toSet()
private val conditions = Conditions.compile(
config.getSubsections("conditions"),
context.with("conditions")
)
private val chance = config.getDouble("chance")
private var task: BukkitTask? = null
override fun onStart() {
task = plugin.scheduler.runTimer(spawnRate, spawnRate) {
tick()
}
}
override fun onStop() {
task?.cancel()
}
private fun tick() {
for (player in Bukkit.getOnlinePlayers()) {
for (point in player.spawnPoints.filter { it.type in spawnTypes }) {
if (randDouble(0.0, 100.0) > chance) {
continue
}
if (!conditions.areMet(point.location.toDispatcher(), EmptyProvidedHolder)) {
continue
}
val mob = category.mobs.randomOrNull() ?: continue
mob.spawn(point.location, SpawnReason.NATURAL)
}
}
}
}
}

View File

@@ -7,6 +7,7 @@ import com.willfp.ecomobs.category.MobCategory
import com.willfp.ecomobs.category.spawning.SpawnMethod
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
import com.willfp.ecomobs.mob.SpawnReason
import com.willfp.libreforge.ViolationContext
import com.willfp.libreforge.enumValueOfOrNull
import org.bukkit.entity.EntityType
import org.bukkit.event.EventHandler
@@ -15,7 +16,12 @@ import org.bukkit.event.Listener
import org.bukkit.event.entity.CreatureSpawnEvent
object SpawnMethodFactoryNone : SpawnMethodFactory("replace") {
override fun create(category: MobCategory, config: Config, plugin: EcoPlugin): SpawnMethod {
override fun create(
category: MobCategory,
config: Config,
plugin: EcoPlugin,
context: ViolationContext
): SpawnMethod {
return SpawnMethodNone(category, config, plugin)
}

View File

@@ -7,6 +7,7 @@ import com.willfp.ecomobs.category.MobCategory
import com.willfp.ecomobs.category.spawning.SpawnMethod
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
import com.willfp.ecomobs.mob.SpawnReason
import com.willfp.libreforge.ViolationContext
import com.willfp.libreforge.enumValueOfOrNull
import org.bukkit.entity.EntityType
import org.bukkit.event.EventHandler
@@ -15,7 +16,12 @@ import org.bukkit.event.Listener
import org.bukkit.event.entity.CreatureSpawnEvent
object SpawnMethodFactoryReplace : SpawnMethodFactory("replace") {
override fun create(category: MobCategory, config: Config, plugin: EcoPlugin): SpawnMethod {
override fun create(
category: MobCategory,
config: Config,
plugin: EcoPlugin,
context: ViolationContext
): SpawnMethod {
return SpawnMethodReplace(category, config, plugin)
}

View File

@@ -0,0 +1,20 @@
package com.willfp.ecomobs.category.spawning.spawnpoints
import org.bukkit.Location
class SpawnPoint(
val location: Location,
val type: SpawnPointType
) {
override fun hashCode(): Int {
var result = location.hashCode()
result = 31 * result + type.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
return other is SpawnPoint &&
this.location == other.location &&
this.type == other.type
}
}

View File

@@ -0,0 +1,77 @@
package com.willfp.ecomobs.category.spawning.spawnpoints
import com.github.benmanes.caffeine.cache.Caffeine
import com.willfp.eco.core.EcoPlugin
import com.willfp.ecomobs.math.Int3
import com.willfp.ecomobs.plugin
import org.bukkit.Material
import org.bukkit.entity.Player
import java.util.UUID
import java.util.concurrent.TimeUnit
val spawnPointCache = Caffeine.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.build<UUID, Set<SpawnPoint>>()
val Player.spawnPoints: Set<SpawnPoint>
get() = spawnPointCache.get(this.uniqueId) {
plugin.spawnPointGenerator.generate(this)
}
class SpawnPointGenerator(
private val plugin: EcoPlugin
) {
private val radius = plugin.configYml.getInt("custom-spawning.radius-around-player")
private val max = plugin.configYml.getInt("custom-spawning.max-points-per-player")
private val maxAttempts = plugin.configYml.getInt("custom-spawning.max-attempts")
fun generate(player: Player): Set<SpawnPoint> {
val points = mutableSetOf<SpawnPoint>()
for (i in 1..max) {
val point = generatePoint(player) ?: continue
points.add(point)
}
return points
}
private fun generatePoint(player: Player): SpawnPoint? {
val playerLocation = player.location
val world = playerLocation.world ?: return null
val bottomCorner = Int3(
playerLocation.x.toInt() - radius, playerLocation.y.toInt() - radius, playerLocation.z.toInt() - radius
)
val topCorner = Int3(
playerLocation.x.toInt() + radius, playerLocation.y.toInt() + radius, playerLocation.z.toInt() + radius
)
var attempts = 0
val iterator = (bottomCorner..topCorner).randomIterator()
while (attempts < maxAttempts) {
attempts++
val (x, y, z) = iterator.next()
val block = world.getBlockAt(x, y, z)
val blockAbove = world.getBlockAt(x, y + 1, z)
val blockBelow = world.getBlockAt(x, y - 1, z)
if (blockAbove.isSolid) {
continue
}
if (block.isPassable && blockAbove.isPassable && blockBelow.type.isSolid) {
return SpawnPoint(block.location.add(0.5, 0.5, 0.5), SpawnPointType.LAND)
}
if (block.type == Material.WATER && blockAbove.type == Material.WATER) {
return SpawnPoint(block.location.add(0.5, 0.5, 0.5), SpawnPointType.WATER)
}
}
return null
}
}

View File

@@ -0,0 +1,6 @@
package com.willfp.ecomobs.category.spawning.spawnpoints
enum class SpawnPointType {
LAND,
WATER
}

View File

@@ -0,0 +1,67 @@
package com.willfp.ecomobs.math
data class Int3(
val x: Int,
val y: Int,
val z: Int
) {
operator fun rangeTo(other: Int3): Int3Range {
return Int3Range(this, other)
}
}
data class Int3Range(
val start: Int3,
val end: Int3
) : Iterable<Int3> {
override fun iterator(): Iterator<Int3> {
return Int3Iterator(this)
}
fun randomIterator(): Iterator<Int3> {
return RandomInt3Iterator(this)
}
}
class Int3Iterator(
private val range: Int3Range
) : Iterator<Int3> {
private var current = range.start
override fun hasNext(): Boolean {
return current.x <= range.end.x &&
current.y <= range.end.y &&
current.z <= range.end.z
}
override fun next(): Int3 {
val toReturn = current
if (current.x < range.end.x) {
current = current.copy(x = current.x + 1)
} else if (current.y < range.end.y) {
current = current.copy(x = range.start.x, y = current.y + 1)
} else if (current.z < range.end.z) {
current = current.copy(x = range.start.x, y = range.start.y, z = current.z + 1)
}
return toReturn
}
}
class RandomInt3Iterator(
private val range: Int3Range
) : Iterator<Int3> {
override fun hasNext(): Boolean {
return true
}
override fun next(): Int3 {
// Generate a random Int3 within the bounds
return Int3(
range.start.x + ((range.end.x - range.start.x) * Math.random()).toInt(),
range.start.y + ((range.end.y - range.start.y) * Math.random()).toInt(),
range.start.z + ((range.end.z - range.start.z) * Math.random()).toInt()
)
}
}

View File

@@ -29,5 +29,13 @@ spawning:
# Options for custom spawning
custom:
# Spawn types (choose from land, water)
spawn-types:
- land
# Conditions that the location must match in order for the mob to spawn
# Read here: https://plugins.auxilor.io/effects/configuring-a-condition
conditions: [ ]
# The chance for the mob to spawn if a valid spawn point is found (as a percentage)
chance: 100

View File

@@ -4,3 +4,17 @@
#
discover-recipes: true
custom-spawning:
# The spawn rate is the number of ticks to wait between trying to spawn a mob.
# 20 ticks = 1 second; the higher the number, the less often mobs will spawn.
spawn-rate: 5
# The radius to generate spawn points for around the player
radius-around-player: 32
# The max amount of spawn points to generate per player
max-points-per-player: 8
# The max amount of attempts to generate a spawn point per player
max-attempts: 64