Compare commits
No commits in common. "0ed06e789f114b13ea3f2d78d7773858ede1fd80" and "c33cd859fee78c98035d88b73a6c37614b495f3c" have entirely different histories.
0ed06e789f
...
c33cd859fe
1
hcu-core
1
hcu-core
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 520a378cd5c3da6321305d07cc0b402b73292add
|
|
||||||
|
|
@ -55,20 +55,10 @@ class App : JavaPlugin() {
|
||||||
}
|
}
|
||||||
this.playerIdService = pIdService
|
this.playerIdService = pIdService
|
||||||
|
|
||||||
// Register public API as a Bukkit service
|
|
||||||
val landsAPI = net.hareworks.hcu.lands.api.LandsAPIImpl(service)
|
|
||||||
server.servicesManager.register(
|
|
||||||
net.hareworks.hcu.lands.api.LandsAPI::class.java,
|
|
||||||
landsAPI,
|
|
||||||
this,
|
|
||||||
org.bukkit.plugin.ServicePriority.Normal
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run visualizer every 20 ticks (1 second)
|
// Run visualizer every 20 ticks (1 second)
|
||||||
server.scheduler.runTaskTimer(this, VisualizerTask(service), 20L, 10L)
|
server.scheduler.runTaskTimer(this, VisualizerTask(service), 20L, 10L)
|
||||||
|
|
||||||
logger.info("Lands plugin initialized successfully.")
|
logger.info("Lands plugin initialized successfully.")
|
||||||
logger.info("LandsAPI registered as a Bukkit service.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
package net.hareworks.hcu.lands.api
|
|
||||||
|
|
||||||
import net.hareworks.hcu.lands.model.Land
|
|
||||||
import net.hareworks.hcu.lands.model.LandData
|
|
||||||
import org.bukkit.Location
|
|
||||||
import org.bukkit.block.Block
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public API for the Lands plugin.
|
|
||||||
* This service can be obtained via Bukkit's ServicesManager.
|
|
||||||
*
|
|
||||||
* Example usage:
|
|
||||||
* ```kotlin
|
|
||||||
* val landsAPI = Bukkit.getServicesManager().load(LandsAPI::class.java)
|
|
||||||
* val land = landsAPI?.getLandAt(block)
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
interface LandsAPI {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the land that contains the specified block.
|
|
||||||
*
|
|
||||||
* @param block The block to check
|
|
||||||
* @return The land containing the block, or null if no land contains it
|
|
||||||
*/
|
|
||||||
fun getLandAt(block: Block): Land?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the land that contains the specified location.
|
|
||||||
*
|
|
||||||
* @param location The location to check
|
|
||||||
* @return The land containing the location, or null if no land contains it
|
|
||||||
*/
|
|
||||||
fun getLandAt(location: Location): Land?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the land that contains the specified coordinates.
|
|
||||||
*
|
|
||||||
* @param world The world name
|
|
||||||
* @param x The x coordinate
|
|
||||||
* @param y The y coordinate
|
|
||||||
* @param z The z coordinate
|
|
||||||
* @return The land containing the coordinates, or null if no land contains it
|
|
||||||
*/
|
|
||||||
fun getLandAt(world: String, x: Double, y: Double, z: Double): Land?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a land by its name.
|
|
||||||
*
|
|
||||||
* @param name The name of the land
|
|
||||||
* @return The land with the specified name, or null if not found
|
|
||||||
*/
|
|
||||||
fun getLand(name: String): Land?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all lands in the specified world.
|
|
||||||
*
|
|
||||||
* @param world The world name
|
|
||||||
* @return List of all lands in the world
|
|
||||||
*/
|
|
||||||
fun getLandsInWorld(world: String): List<Land>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all lands owned by the specified actor.
|
|
||||||
*
|
|
||||||
* @param actorId The actor ID
|
|
||||||
* @return List of all lands owned by the actor
|
|
||||||
*/
|
|
||||||
fun getLandsByOwner(actorId: Int): List<Land>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all lands.
|
|
||||||
*
|
|
||||||
* @return List of all lands
|
|
||||||
*/
|
|
||||||
fun getAllLands(): List<Land>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new land.
|
|
||||||
*
|
|
||||||
* @param name The name of the land
|
|
||||||
* @param actorId The owner's actor ID
|
|
||||||
* @param world The world name
|
|
||||||
* @return true if the land was created successfully, false if a land with that name already exists
|
|
||||||
*/
|
|
||||||
fun createLand(name: String, actorId: Int, world: String): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a land.
|
|
||||||
*
|
|
||||||
* @param name The name of the land to delete
|
|
||||||
* @return true if the land was deleted successfully, false if the land doesn't exist
|
|
||||||
*/
|
|
||||||
fun deleteLand(name: String): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies a land's data.
|
|
||||||
*
|
|
||||||
* @param name The name of the land to modify
|
|
||||||
* @param modifier A function that modifies the land's data
|
|
||||||
* @return true if the land was modified successfully, false if the land doesn't exist
|
|
||||||
*/
|
|
||||||
fun modifyLand(name: String, modifier: (LandData) -> Unit): Boolean
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
package net.hareworks.hcu.lands.api
|
|
||||||
|
|
||||||
import net.hareworks.hcu.lands.model.Land
|
|
||||||
import net.hareworks.hcu.lands.model.LandData
|
|
||||||
import net.hareworks.hcu.lands.service.LandService
|
|
||||||
import org.bukkit.Location
|
|
||||||
import org.bukkit.block.Block
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the LandsAPI interface.
|
|
||||||
* This is registered as a Bukkit service and delegates to the internal LandService.
|
|
||||||
*/
|
|
||||||
class LandsAPIImpl(private val landService: LandService) : LandsAPI {
|
|
||||||
|
|
||||||
override fun getLandAt(block: Block): Land? {
|
|
||||||
return getLandAt(block.world.name, block.x + 0.5, block.y + 0.5, block.z + 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLandAt(location: Location): Land? {
|
|
||||||
return getLandAt(location.world.name, location.x, location.y, location.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLandAt(world: String, x: Double, y: Double, z: Double): Land? {
|
|
||||||
return landService.getAllLands()
|
|
||||||
.filter { it.world == world }
|
|
||||||
.firstOrNull { land ->
|
|
||||||
land.data.parts.any { shape ->
|
|
||||||
when (shape) {
|
|
||||||
is net.hareworks.hcu.lands.model.Shape.Cuboid -> shape.contains(x, y, z)
|
|
||||||
is net.hareworks.hcu.lands.model.Shape.Cylinder -> shape.contains(x, y, z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLand(name: String): Land? {
|
|
||||||
return landService.getLand(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLandsInWorld(world: String): List<Land> {
|
|
||||||
return landService.getAllLands().filter { it.world == world }.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLandsByOwner(actorId: Int): List<Land> {
|
|
||||||
return landService.getAllLands().filter { it.actorId == actorId }.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAllLands(): List<Land> {
|
|
||||||
return landService.getAllLands().toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createLand(name: String, actorId: Int, world: String): Boolean {
|
|
||||||
return landService.createLand(name, actorId, world)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deleteLand(name: String): Boolean {
|
|
||||||
return landService.deleteLand(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun modifyLand(name: String, modifier: (LandData) -> Unit): Boolean {
|
|
||||||
return landService.modifyLand(name, modifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@ import io.papermc.paper.math.Position
|
||||||
import net.hareworks.hcu.lands.App
|
import net.hareworks.hcu.lands.App
|
||||||
import net.hareworks.hcu.lands.model.Land
|
import net.hareworks.hcu.lands.model.Land
|
||||||
import net.hareworks.hcu.lands.model.Shape
|
import net.hareworks.hcu.lands.model.Shape
|
||||||
import net.hareworks.hcu.lands.model.normalizeToBlockInt
|
|
||||||
import net.hareworks.hcu.lands.task.LandsVisualizer
|
import net.hareworks.hcu.lands.task.LandsVisualizer
|
||||||
import net.hareworks.kommand_lib.kommand
|
import net.hareworks.kommand_lib.kommand
|
||||||
import net.kyori.adventure.text.Component
|
import net.kyori.adventure.text.Component
|
||||||
|
|
@ -89,14 +88,7 @@ class LandsCommand(
|
||||||
executes {
|
executes {
|
||||||
val p1: Position = argument("pos1")
|
val p1: Position = argument("pos1")
|
||||||
val p2: Position = argument("pos2")
|
val p2: Position = argument("pos2")
|
||||||
val shape = Shape.Cuboid(
|
val shape = Shape.Cuboid(p1.x(), p1.y(), p1.z(), p2.x(), p2.y(), p2.z())
|
||||||
normalizeToBlockInt(p1.x()),
|
|
||||||
normalizeToBlockInt(p1.y()),
|
|
||||||
normalizeToBlockInt(p1.z()),
|
|
||||||
normalizeToBlockInt(p2.x()),
|
|
||||||
normalizeToBlockInt(p2.y()),
|
|
||||||
normalizeToBlockInt(p2.z())
|
|
||||||
)
|
|
||||||
val name: String = argument("name")
|
val name: String = argument("name")
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
updateLandData(sender, name) { parts ->
|
||||||
|
|
@ -110,99 +102,17 @@ class LandsCommand(
|
||||||
literal("cylinder") {
|
literal("cylinder") {
|
||||||
blockCoordinates("center") {
|
blockCoordinates("center") {
|
||||||
float("radius") {
|
float("radius") {
|
||||||
literal("center") {
|
float("height") {
|
||||||
integer("totalHeight", min = 1) {
|
executes {
|
||||||
executes {
|
val c: Position = argument("center")
|
||||||
val c: Position = argument("center")
|
val r: Double = argument("radius")
|
||||||
val r: Double = argument("radius")
|
val h: Double = argument("height")
|
||||||
val total: Int = argument("totalHeight")
|
val shape = Shape.Cylinder(c.x(), c.y(), c.z(), r, h)
|
||||||
val name: String = argument("name")
|
val name: String = argument("name")
|
||||||
|
|
||||||
// Subtract 1 for center block, distribute remaining
|
updateLandData(sender, name) { parts ->
|
||||||
val remaining = total - 1
|
parts.add(shape)
|
||||||
val topH = (remaining + 1) / 2 // Prefer top
|
sender.sendMessage(Component.text("Shape added.", NamedTextColor.GREEN))
|
||||||
val bottomH = remaining - topH
|
|
||||||
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, bottomH, topH
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
parts.add(shape)
|
|
||||||
sender.sendMessage(Component.text("Shape added.", NamedTextColor.GREEN))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
literal("top") {
|
|
||||||
integer("topHeight", min = 1) {
|
|
||||||
literal("bottom") {
|
|
||||||
integer("bottomHeight", min = 0) {
|
|
||||||
executes {
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val topH: Int = argument("topHeight")
|
|
||||||
val bottomH: Int = argument("bottomHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, bottomH, topH
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
parts.add(shape)
|
|
||||||
sender.sendMessage(Component.text("Shape added.", NamedTextColor.GREEN))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executes {
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val total: Int = argument("topHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
// total - 1 goes to top, 0 to bottom
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, 0, total - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
parts.add(shape)
|
|
||||||
sender.sendMessage(Component.text("Shape added.", NamedTextColor.GREEN))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
literal("bottom") {
|
|
||||||
integer("bottomHeight", min = 1) {
|
|
||||||
executes {
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val total: Int = argument("bottomHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
// total - 1 goes to bottom, 0 to top
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, total - 1, 0
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
parts.add(shape)
|
|
||||||
sender.sendMessage(Component.text("Shape added.", NamedTextColor.GREEN))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,14 +130,7 @@ class LandsCommand(
|
||||||
val index: Int = argument("index")
|
val index: Int = argument("index")
|
||||||
val p1: Position = argument("pos1")
|
val p1: Position = argument("pos1")
|
||||||
val p2: Position = argument("pos2")
|
val p2: Position = argument("pos2")
|
||||||
val shape = Shape.Cuboid(
|
val shape = Shape.Cuboid(p1.x(), p1.y(), p1.z(), p2.x(), p2.y(), p2.z())
|
||||||
normalizeToBlockInt(p1.x()),
|
|
||||||
normalizeToBlockInt(p1.y()),
|
|
||||||
normalizeToBlockInt(p1.z()),
|
|
||||||
normalizeToBlockInt(p2.x()),
|
|
||||||
normalizeToBlockInt(p2.y()),
|
|
||||||
normalizeToBlockInt(p2.z())
|
|
||||||
)
|
|
||||||
val name: String = argument("name")
|
val name: String = argument("name")
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
updateLandData(sender, name) { parts ->
|
||||||
|
|
@ -245,115 +148,21 @@ class LandsCommand(
|
||||||
literal("cylinder") {
|
literal("cylinder") {
|
||||||
blockCoordinates("center") {
|
blockCoordinates("center") {
|
||||||
float("radius") {
|
float("radius") {
|
||||||
literal("center") {
|
float("height") {
|
||||||
integer("totalHeight", min = 1) {
|
executes {
|
||||||
executes {
|
val index: Int = argument("index")
|
||||||
val index: Int = argument("index")
|
val c: Position = argument("center")
|
||||||
val c: Position = argument("center")
|
val r: Double = argument("radius")
|
||||||
val r: Double = argument("radius")
|
val h: Double = argument("height")
|
||||||
val total: Int = argument("totalHeight")
|
val shape = Shape.Cylinder(c.x(), c.y(), c.z(), r, h)
|
||||||
val name: String = argument("name")
|
val name: String = argument("name")
|
||||||
|
|
||||||
val remaining = total - 1
|
updateLandData(sender, name) { parts ->
|
||||||
val topH = (remaining + 1) / 2
|
if (index in parts.indices) {
|
||||||
val bottomH = remaining - topH
|
parts[index] = shape
|
||||||
|
sender.sendMessage(Component.text("Shape modified at index $index.", NamedTextColor.GREEN))
|
||||||
val shape = Shape.Cylinder(
|
} else {
|
||||||
normalizeToBlockInt(c.x()),
|
sender.sendMessage(Component.text("Index out of bounds.", NamedTextColor.RED))
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, bottomH, topH
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
if (index in parts.indices) {
|
|
||||||
parts[index] = shape
|
|
||||||
sender.sendMessage(Component.text("Shape modified at index $index.", NamedTextColor.GREEN))
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(Component.text("Index out of bounds.", NamedTextColor.RED))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
literal("top") {
|
|
||||||
integer("topHeight", min = 1) {
|
|
||||||
literal("bottom") {
|
|
||||||
integer("bottomHeight", min = 0) {
|
|
||||||
executes {
|
|
||||||
val index: Int = argument("index")
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val topH: Int = argument("topHeight")
|
|
||||||
val bottomH: Int = argument("bottomHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, bottomH, topH
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
if (index in parts.indices) {
|
|
||||||
parts[index] = shape
|
|
||||||
sender.sendMessage(Component.text("Shape modified at index $index.", NamedTextColor.GREEN))
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(Component.text("Index out of bounds.", NamedTextColor.RED))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executes {
|
|
||||||
val index: Int = argument("index")
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val total: Int = argument("topHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, 0, total - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
if (index in parts.indices) {
|
|
||||||
parts[index] = shape
|
|
||||||
sender.sendMessage(Component.text("Shape modified at index $index.", NamedTextColor.GREEN))
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(Component.text("Index out of bounds.", NamedTextColor.RED))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
literal("bottom") {
|
|
||||||
integer("bottomHeight", min = 1) {
|
|
||||||
executes {
|
|
||||||
val index: Int = argument("index")
|
|
||||||
val c: Position = argument("center")
|
|
||||||
val r: Double = argument("radius")
|
|
||||||
val total: Int = argument("bottomHeight")
|
|
||||||
val name: String = argument("name")
|
|
||||||
|
|
||||||
val shape = Shape.Cylinder(
|
|
||||||
normalizeToBlockInt(c.x()),
|
|
||||||
normalizeToBlockInt(c.y()),
|
|
||||||
normalizeToBlockInt(c.z()),
|
|
||||||
r, total - 1, 0
|
|
||||||
)
|
|
||||||
|
|
||||||
updateLandData(sender, name) { parts ->
|
|
||||||
if (index in parts.indices) {
|
|
||||||
parts[index] = shape
|
|
||||||
sender.sendMessage(Component.text("Shape modified at index $index.", NamedTextColor.GREEN))
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(Component.text("Index out of bounds.", NamedTextColor.RED))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ sealed class Shape {
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("cuboid")
|
@SerialName("cuboid")
|
||||||
data class Cuboid(
|
data class Cuboid(
|
||||||
val x1: Int, val y1: Int, val z1: Int,
|
val x1: Double, val y1: Double, val z1: Double,
|
||||||
val x2: Int, val y2: Int, val z2: Int
|
val x2: Double, val y2: Double, val z2: Double
|
||||||
) : Shape() {
|
) : Shape() {
|
||||||
fun minX() = minOf(x1, x2)
|
fun minX() = minOf(x1, x2)
|
||||||
fun maxX() = maxOf(x1, x2)
|
fun maxX() = maxOf(x1, x2)
|
||||||
|
|
@ -32,116 +32,24 @@ sealed class Shape {
|
||||||
fun maxZ() = maxOf(z1, z2)
|
fun maxZ() = maxOf(z1, z2)
|
||||||
|
|
||||||
fun contains(x: Double, y: Double, z: Double): Boolean {
|
fun contains(x: Double, y: Double, z: Double): Boolean {
|
||||||
// Add 0.5 to block coordinates for center-based comparison
|
return x >= minX() && x <= maxX() &&
|
||||||
return x >= minX() + 0.5 && x <= maxX() + 0.5 &&
|
y >= minY() && y <= maxY() &&
|
||||||
y >= minY() + 0.5 && y <= maxY() + 0.5 &&
|
z >= minZ() && z <= maxZ()
|
||||||
z >= minZ() + 0.5 && z <= maxZ() + 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the two corner blocks (min and max corners) for visualization
|
|
||||||
*/
|
|
||||||
fun getCornerBlocks(): Pair<Triple<Int, Int, Int>, Triple<Int, Int, Int>> {
|
|
||||||
val minCorner = Triple(minX(), minY(), minZ())
|
|
||||||
val maxCorner = Triple(maxX(), maxY(), maxZ())
|
|
||||||
return minCorner to maxCorner
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("cylinder")
|
@SerialName("cylinder")
|
||||||
data class Cylinder(
|
data class Cylinder(
|
||||||
val x: Int, val y: Int, val z: Int,
|
val x: Double, val y: Double, val z: Double,
|
||||||
val radius: Double,
|
val radius: Double,
|
||||||
val bottomHeight: Int, // Height extending downward from center block
|
val height: Double // Assuming vertical cylinder
|
||||||
val topHeight: Int // Height extending upward from center block
|
|
||||||
) : Shape() {
|
) : Shape() {
|
||||||
fun contains(tx: Double, ty: Double, tz: Double): Boolean {
|
fun contains(tx: Double, ty: Double, tz: Double): Boolean {
|
||||||
// Add 0.5 to block coordinates for center-based comparison
|
if (ty < y || ty > y + height) return false
|
||||||
val centerX = x + 0.5
|
val dx = tx - x
|
||||||
val centerY = y + 0.5
|
val dz = tz - z
|
||||||
val centerZ = z + 0.5
|
|
||||||
|
|
||||||
// Total height includes center block (1) + bottomHeight + topHeight
|
|
||||||
val minY = centerY - bottomHeight
|
|
||||||
val maxY = centerY + 1.0 + topHeight
|
|
||||||
|
|
||||||
if (ty < minY || ty > maxY) return false
|
|
||||||
val dx = tx - centerX
|
|
||||||
val dz = tz - centerZ
|
|
||||||
return (dx * dx + dz * dz) <= (radius * radius)
|
return (dx * dx + dz * dz) <= (radius * radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the center block for visualization
|
|
||||||
*/
|
|
||||||
fun getCenterBlock(): Triple<Int, Int, Int> {
|
|
||||||
return Triple(x, y, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total height of the cylinder including center block
|
|
||||||
*/
|
|
||||||
fun totalHeight(): Int = 1 + bottomHeight + topHeight
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a set of blocks that form the outline of the blockified cylinder.
|
|
||||||
* This includes blocks at the edge of the cylinder's radius at each Y level.
|
|
||||||
*/
|
|
||||||
fun getBlockifiedOutline(): Set<Triple<Int, Int, Int>> {
|
|
||||||
val outline = mutableSetOf<Triple<Int, Int, Int>>()
|
|
||||||
val centerX = x + 0.5
|
|
||||||
val centerZ = z + 0.5
|
|
||||||
val radiusSquared = radius * radius
|
|
||||||
|
|
||||||
// Calculate the bounding box for the cylinder
|
|
||||||
val minBlockX = (x - radius - 1).toInt()
|
|
||||||
val maxBlockX = (x + radius + 1).toInt()
|
|
||||||
val minBlockZ = (z - radius - 1).toInt()
|
|
||||||
val maxBlockZ = (z + radius + 1).toInt()
|
|
||||||
|
|
||||||
// For each Y level in the cylinder
|
|
||||||
val minY = y - bottomHeight
|
|
||||||
val maxY = y + topHeight
|
|
||||||
|
|
||||||
for (blockY in minY..maxY) {
|
|
||||||
// Check each block in the XZ plane
|
|
||||||
for (blockX in minBlockX..maxBlockX) {
|
|
||||||
for (blockZ in minBlockZ..maxBlockZ) {
|
|
||||||
// Check if this block is inside the cylinder
|
|
||||||
val dx = (blockX + 0.5) - centerX
|
|
||||||
val dz = (blockZ + 0.5) - centerZ
|
|
||||||
val isInside = (dx * dx + dz * dz) <= radiusSquared
|
|
||||||
|
|
||||||
if (isInside) {
|
|
||||||
// Check if any adjacent block is outside (making this an edge block)
|
|
||||||
val hasOutsideNeighbor = listOf(
|
|
||||||
Pair(blockX - 1, blockZ),
|
|
||||||
Pair(blockX + 1, blockZ),
|
|
||||||
Pair(blockX, blockZ - 1),
|
|
||||||
Pair(blockX, blockZ + 1)
|
|
||||||
).any { (nx, nz) ->
|
|
||||||
val ndx = (nx + 0.5) - centerX
|
|
||||||
val ndz = (nz + 0.5) - centerZ
|
|
||||||
(ndx * ndx + ndz * ndz) > radiusSquared
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasOutsideNeighbor) {
|
|
||||||
outline.add(Triple(blockX, blockY, blockZ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return outline
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalizes a coordinate to block integer (floor)
|
|
||||||
*/
|
|
||||||
fun normalizeToBlockInt(coord: Double): Int {
|
|
||||||
return kotlin.math.floor(coord).toInt()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -51,158 +51,40 @@ class VisualizerTask(private val landService: LandService) : Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawCuboid(player: Player, cuboid: Shape.Cuboid) {
|
private fun drawCuboid(player: Player, cuboid: Shape.Cuboid) {
|
||||||
val (minCorner, maxCorner) = cuboid.getCornerBlocks()
|
// Draw edges
|
||||||
|
val minX = cuboid.minX()
|
||||||
|
val maxX = cuboid.maxX()
|
||||||
|
val minY = cuboid.minY()
|
||||||
|
val maxY = cuboid.maxY()
|
||||||
|
val minZ = cuboid.minZ()
|
||||||
|
val maxZ = cuboid.maxZ()
|
||||||
|
|
||||||
// Draw outline of min corner block
|
// Just corners for now or simple lines
|
||||||
drawBlockOutline(player, minCorner.first, minCorner.second, minCorner.third, Color.LIME)
|
// A simple way to visualize is to spawn particles at corners and periodically along edges
|
||||||
|
// Doing full wireframe every tick is heavy.
|
||||||
// Draw outline of max corner block
|
// Let's do corners.
|
||||||
drawBlockOutline(player, maxCorner.first, maxCorner.second, maxCorner.third, Color.YELLOW)
|
val corners = listOf(
|
||||||
|
Triple(minX, minY, minZ), Triple(maxX, minY, minZ),
|
||||||
// Draw edges of the cuboid using outer corners
|
Triple(minX, maxY, minZ), Triple(maxX, maxY, minZ),
|
||||||
// Min corner is at block position, max corner extends to outer edge (+1.0)
|
Triple(minX, minY, maxZ), Triple(maxX, minY, maxZ),
|
||||||
val minX = cuboid.minX().toDouble()
|
Triple(minX, maxY, maxZ), Triple(maxX, maxY, maxZ)
|
||||||
val maxX = cuboid.maxX() + 1.0
|
)
|
||||||
val minY = cuboid.minY().toDouble()
|
corners.forEach { (x, y, z) ->
|
||||||
val maxY = cuboid.maxY() + 1.0
|
player.spawnParticle(Particle.DUST, x, y, z, 1, 0.0, 0.0, 0.0, Particle.DustOptions(Color.LIME, 1.0f))
|
||||||
val minZ = cuboid.minZ().toDouble()
|
}
|
||||||
val maxZ = cuboid.maxZ() + 1.0
|
|
||||||
|
|
||||||
val edgeColor = Particle.DustOptions(Color.fromRGB(100, 255, 100), 0.75f)
|
|
||||||
val step = 0.5
|
|
||||||
|
|
||||||
// Bottom face edges
|
|
||||||
drawLine(player, minX, minY, minZ, maxX, minY, minZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, minY, minZ, maxX, minY, maxZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, minY, maxZ, minX, minY, maxZ, edgeColor, step)
|
|
||||||
drawLine(player, minX, minY, maxZ, minX, minY, minZ, edgeColor, step)
|
|
||||||
|
|
||||||
// Top face edges
|
|
||||||
drawLine(player, minX, maxY, minZ, maxX, maxY, minZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, maxY, minZ, maxX, maxY, maxZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, maxY, maxZ, minX, maxY, maxZ, edgeColor, step)
|
|
||||||
drawLine(player, minX, maxY, maxZ, minX, maxY, minZ, edgeColor, step)
|
|
||||||
|
|
||||||
// Vertical edges
|
|
||||||
drawLine(player, minX, minY, minZ, minX, maxY, minZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, minY, minZ, maxX, maxY, minZ, edgeColor, step)
|
|
||||||
drawLine(player, maxX, minY, maxZ, maxX, maxY, maxZ, edgeColor, step)
|
|
||||||
drawLine(player, minX, minY, maxZ, minX, maxY, maxZ, edgeColor, step)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawCylinder(player: Player, cylinder: Shape.Cylinder) {
|
private fun drawCylinder(player: Player, cylinder: Shape.Cylinder) {
|
||||||
val center = cylinder.getCenterBlock()
|
val segments = 20
|
||||||
|
|
||||||
// Draw outline of center block
|
|
||||||
drawBlockOutline(player, center.first, center.second, center.third, Color.AQUA)
|
|
||||||
|
|
||||||
// Draw cylinder edges (top and bottom circles + vertical lines)
|
|
||||||
// Add 0.5 to block coordinates for center on X/Z axis
|
|
||||||
val centerX = cylinder.x + 0.5
|
|
||||||
val centerZ = cylinder.z + 0.5
|
|
||||||
|
|
||||||
// Y coordinates use block boundaries (no +0.5 offset)
|
|
||||||
val bottomY = cylinder.y.toDouble() - cylinder.bottomHeight
|
|
||||||
val topY = cylinder.y.toDouble() + 1.0 + cylinder.topHeight
|
|
||||||
|
|
||||||
val segments = 32
|
|
||||||
val step = (Math.PI * 2) / segments
|
val step = (Math.PI * 2) / segments
|
||||||
val edgeColor = Particle.DustOptions(Color.fromRGB(100, 200, 255), 0.75f)
|
// Top and Bottom circles
|
||||||
|
|
||||||
// Bottom circle
|
|
||||||
for (i in 0 until segments) {
|
for (i in 0 until segments) {
|
||||||
val angle1 = i * step
|
val angle = i * step
|
||||||
val angle2 = (i + 1) * step
|
val dx = cos(angle) * cylinder.radius
|
||||||
val x1 = centerX + cos(angle1) * cylinder.radius
|
val dz = sin(angle) * cylinder.radius
|
||||||
val z1 = centerZ + sin(angle1) * cylinder.radius
|
|
||||||
val x2 = centerX + cos(angle2) * cylinder.radius
|
|
||||||
val z2 = centerZ + sin(angle2) * cylinder.radius
|
|
||||||
|
|
||||||
drawLine(player, x1, bottomY, z1, x2, bottomY, z2, edgeColor, 0.25)
|
player.spawnParticle(Particle.DUST, cylinder.x + dx, cylinder.y, cylinder.z + dz, 1, 0.0, 0.0, 0.0, Particle.DustOptions(Color.AQUA, 1.0f))
|
||||||
}
|
player.spawnParticle(Particle.DUST, cylinder.x + dx, cylinder.y + cylinder.height, cylinder.z + dz, 1, 0.0, 0.0, 0.0, Particle.DustOptions(Color.AQUA, 1.0f))
|
||||||
|
|
||||||
// Top circle
|
|
||||||
for (i in 0 until segments) {
|
|
||||||
val angle1 = i * step
|
|
||||||
val angle2 = (i + 1) * step
|
|
||||||
val x1 = centerX + cos(angle1) * cylinder.radius
|
|
||||||
val z1 = centerZ + sin(angle1) * cylinder.radius
|
|
||||||
val x2 = centerX + cos(angle2) * cylinder.radius
|
|
||||||
val z2 = centerZ + sin(angle2) * cylinder.radius
|
|
||||||
|
|
||||||
drawLine(player, x1, topY, z1, x2, topY, z2, edgeColor, 0.25)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertical lines (8 cardinal directions)
|
|
||||||
val verticalSegments = 8
|
|
||||||
for (i in 0 until verticalSegments) {
|
|
||||||
val angle = i * (Math.PI * 2) / verticalSegments
|
|
||||||
val x = centerX + cos(angle) * cylinder.radius
|
|
||||||
val z = centerZ + sin(angle) * cylinder.radius
|
|
||||||
|
|
||||||
drawLine(player, x, bottomY, z, x, topY, z, edgeColor, 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw blockified outline
|
|
||||||
val blockifiedOutline = cylinder.getBlockifiedOutline()
|
|
||||||
val blockColor = Particle.DustOptions(Color.fromRGB(150, 220, 255), 0.5f)
|
|
||||||
|
|
||||||
for ((bx, by, bz) in blockifiedOutline) {
|
|
||||||
// Draw a subtle outline for each block in the blockified cylinder
|
|
||||||
drawBlockOutline(player, bx, by, bz, Color.fromRGB(150, 220, 255))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws an outline of a block using particles
|
|
||||||
*/
|
|
||||||
private fun drawBlockOutline(player: Player, x: Int, y: Int, z: Int, color: Color) {
|
|
||||||
val dustOptions = Particle.DustOptions(color, 1.0f)
|
|
||||||
val step = 0.25 // Particle spacing along edges
|
|
||||||
|
|
||||||
// Bottom face edges
|
|
||||||
drawLine(player, x.toDouble(), y.toDouble(), z.toDouble(), x + 1.0, y.toDouble(), z.toDouble(), dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y.toDouble(), z.toDouble(), x + 1.0, y.toDouble(), z + 1.0, dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y.toDouble(), z + 1.0, x.toDouble(), y.toDouble(), z + 1.0, dustOptions, step)
|
|
||||||
drawLine(player, x.toDouble(), y.toDouble(), z + 1.0, x.toDouble(), y.toDouble(), z.toDouble(), dustOptions, step)
|
|
||||||
|
|
||||||
// Top face edges
|
|
||||||
drawLine(player, x.toDouble(), y + 1.0, z.toDouble(), x + 1.0, y + 1.0, z.toDouble(), dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y + 1.0, z.toDouble(), x + 1.0, y + 1.0, z + 1.0, dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y + 1.0, z + 1.0, x.toDouble(), y + 1.0, z + 1.0, dustOptions, step)
|
|
||||||
drawLine(player, x.toDouble(), y + 1.0, z + 1.0, x.toDouble(), y + 1.0, z.toDouble(), dustOptions, step)
|
|
||||||
|
|
||||||
// Vertical edges
|
|
||||||
drawLine(player, x.toDouble(), y.toDouble(), z.toDouble(), x.toDouble(), y + 1.0, z.toDouble(), dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y.toDouble(), z.toDouble(), x + 1.0, y + 1.0, z.toDouble(), dustOptions, step)
|
|
||||||
drawLine(player, x + 1.0, y.toDouble(), z + 1.0, x + 1.0, y + 1.0, z + 1.0, dustOptions, step)
|
|
||||||
drawLine(player, x.toDouble(), y.toDouble(), z + 1.0, x.toDouble(), y + 1.0, z + 1.0, dustOptions, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a line of particles between two points
|
|
||||||
*/
|
|
||||||
private fun drawLine(
|
|
||||||
player: Player,
|
|
||||||
x1: Double, y1: Double, z1: Double,
|
|
||||||
x2: Double, y2: Double, z2: Double,
|
|
||||||
dustOptions: Particle.DustOptions,
|
|
||||||
step: Double
|
|
||||||
) {
|
|
||||||
val dx = x2 - x1
|
|
||||||
val dy = y2 - y1
|
|
||||||
val dz = z2 - z1
|
|
||||||
val distance = kotlin.math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
||||||
val steps = (distance / step).toInt()
|
|
||||||
|
|
||||||
if (steps == 0) return
|
|
||||||
|
|
||||||
for (i in 0..steps) {
|
|
||||||
val t = i.toDouble() / steps
|
|
||||||
val x = x1 + dx * t
|
|
||||||
val y = y1 + dy * t
|
|
||||||
val z = z1 + dz * t
|
|
||||||
player.spawnParticle(Particle.DUST, x, y, z, 1, 0.0, 0.0, 0.0, dustOptions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user