mirror of
https://github.com/Auxilor/EcoMobs.git
synced 2025-12-20 07:29:21 +00:00
Added custom spawn method
This commit is contained in:
@@ -40,6 +40,7 @@ allprojects {
|
|||||||
compileOnly("com.willfp:eco:6.65.0")
|
compileOnly("com.willfp:eco:6.65.0")
|
||||||
compileOnly("org.jetbrains:annotations:23.0.0")
|
compileOnly("org.jetbrains:annotations:23.0.0")
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
|
||||||
|
compileOnly("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
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.entities.ai.EntityGoals
|
||||||
import com.willfp.eco.core.integrations.IntegrationLoader
|
import com.willfp.eco.core.integrations.IntegrationLoader
|
||||||
import com.willfp.ecomobs.category.MobCategories
|
import com.willfp.ecomobs.category.MobCategories
|
||||||
|
import com.willfp.ecomobs.category.spawning.spawnpoints.SpawnPointGenerator
|
||||||
import com.willfp.ecomobs.commands.CommandEcoMobs
|
import com.willfp.ecomobs.commands.CommandEcoMobs
|
||||||
import com.willfp.ecomobs.display.SpawnEggDisplay
|
import com.willfp.ecomobs.display.SpawnEggDisplay
|
||||||
import com.willfp.ecomobs.goals.entity.EntityGoalRandomTeleport
|
import com.willfp.ecomobs.goals.entity.EntityGoalRandomTeleport
|
||||||
@@ -25,6 +26,8 @@ internal lateinit var plugin: EcoMobsPlugin
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
class EcoMobsPlugin : LibreforgePlugin() {
|
class EcoMobsPlugin : LibreforgePlugin() {
|
||||||
|
val spawnPointGenerator = SpawnPointGenerator(this)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
plugin = this
|
plugin = this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ class ConfigDrivenMobCategory(
|
|||||||
private val context: ViolationContext
|
private val context: ViolationContext
|
||||||
) : MobCategory {
|
) : MobCategory {
|
||||||
override val spawnMethod = SpawnMethodFactories[config.getString("spawning.type")]
|
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(
|
?: throw ConfigViolationException(
|
||||||
ConfigViolation(
|
ConfigViolation(
|
||||||
"type",
|
"type",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.willfp.ecomobs.category.spawning
|
package com.willfp.ecomobs.category.spawning
|
||||||
|
|
||||||
import com.willfp.eco.core.registry.Registry
|
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.SpawnMethodFactoryNone
|
||||||
import com.willfp.ecomobs.category.spawning.impl.SpawnMethodFactoryReplace
|
import com.willfp.ecomobs.category.spawning.impl.SpawnMethodFactoryReplace
|
||||||
|
|
||||||
@@ -8,5 +9,6 @@ object SpawnMethodFactories : Registry<SpawnMethodFactory>() {
|
|||||||
init {
|
init {
|
||||||
register(SpawnMethodFactoryReplace)
|
register(SpawnMethodFactoryReplace)
|
||||||
register(SpawnMethodFactoryNone)
|
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.config.interfaces.Config
|
||||||
import com.willfp.eco.core.registry.KRegistrable
|
import com.willfp.eco.core.registry.KRegistrable
|
||||||
import com.willfp.ecomobs.category.MobCategory
|
import com.willfp.ecomobs.category.MobCategory
|
||||||
|
import com.willfp.libreforge.ViolationContext
|
||||||
|
|
||||||
abstract class SpawnMethodFactory(override val id: String) : KRegistrable {
|
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.SpawnMethod
|
||||||
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
|
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
|
||||||
import com.willfp.ecomobs.mob.SpawnReason
|
import com.willfp.ecomobs.mob.SpawnReason
|
||||||
|
import com.willfp.libreforge.ViolationContext
|
||||||
import com.willfp.libreforge.enumValueOfOrNull
|
import com.willfp.libreforge.enumValueOfOrNull
|
||||||
import org.bukkit.entity.EntityType
|
import org.bukkit.entity.EntityType
|
||||||
import org.bukkit.event.EventHandler
|
import org.bukkit.event.EventHandler
|
||||||
@@ -15,7 +16,12 @@ import org.bukkit.event.Listener
|
|||||||
import org.bukkit.event.entity.CreatureSpawnEvent
|
import org.bukkit.event.entity.CreatureSpawnEvent
|
||||||
|
|
||||||
object SpawnMethodFactoryNone : SpawnMethodFactory("replace") {
|
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)
|
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.SpawnMethod
|
||||||
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
|
import com.willfp.ecomobs.category.spawning.SpawnMethodFactory
|
||||||
import com.willfp.ecomobs.mob.SpawnReason
|
import com.willfp.ecomobs.mob.SpawnReason
|
||||||
|
import com.willfp.libreforge.ViolationContext
|
||||||
import com.willfp.libreforge.enumValueOfOrNull
|
import com.willfp.libreforge.enumValueOfOrNull
|
||||||
import org.bukkit.entity.EntityType
|
import org.bukkit.entity.EntityType
|
||||||
import org.bukkit.event.EventHandler
|
import org.bukkit.event.EventHandler
|
||||||
@@ -15,7 +16,12 @@ import org.bukkit.event.Listener
|
|||||||
import org.bukkit.event.entity.CreatureSpawnEvent
|
import org.bukkit.event.entity.CreatureSpawnEvent
|
||||||
|
|
||||||
object SpawnMethodFactoryReplace : SpawnMethodFactory("replace") {
|
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)
|
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
|
# Options for custom spawning
|
||||||
custom:
|
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
|
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