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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.willfp.ecomobs.category.spawning.spawnpoints
|
||||
|
||||
enum class SpawnPointType {
|
||||
LAND,
|
||||
WATER
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user