feat: 回転と破壊・設置
This commit is contained in:
parent
ad4245d764
commit
af4008ee45
|
|
@ -64,6 +64,9 @@ class LandSectorPlugin : JavaPlugin() {
|
|||
service.flushChanges()
|
||||
}, 6000L, 6000L)
|
||||
|
||||
// Schedule rotation task
|
||||
net.hareworks.hcu.landsector.task.SectorRotationTask(this).runTaskTimer(this, 1L, 1L)
|
||||
|
||||
logger.info("LandSector initialized with services.")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,19 +103,18 @@ class LandSectorCommand(private val landSectorPlugin: LandSectorPlugin) {
|
|||
val blockBase = world.getBlockAt(x, y - 1, z)
|
||||
val blockTop = world.getBlockAt(x, y + 1, z)
|
||||
|
||||
if (blockBase.type == Material.BEDROCK) blockBase.type = Material.AIR
|
||||
if (blockTop.type == Material.BEDROCK) blockTop.type = Material.AIR
|
||||
if (blockBase.type == Material.DEEPSLATE_TILE_SLAB) blockBase.type = Material.AIR
|
||||
if (blockTop.type == Material.DEEPSLATE_TILE_SLAB) blockTop.type = Material.AIR
|
||||
|
||||
// Remove Entity
|
||||
// Remove All Entities with this sector ID
|
||||
val center = Location(world, x + 0.5, y.toDouble(), z + 0.5)
|
||||
if (center.chunk.isLoaded) {
|
||||
val entities = world.getNearbyEntities(center, 0.5, 0.5, 0.5)
|
||||
entities.forEach { entity ->
|
||||
if (entity is Shulker) {
|
||||
val key = NamespacedKey(landSectorPlugin, "sector_id")
|
||||
val sId = entity.persistentDataContainer.get(key, PersistentDataType.INTEGER)
|
||||
if (sId == id || sId == null) {
|
||||
entity.remove()
|
||||
val sectorKey = NamespacedKey(landSectorPlugin, "sector_id")
|
||||
center.chunk.entities.forEach { entity ->
|
||||
if (entity.persistentDataContainer.has(sectorKey, PersistentDataType.INTEGER)) {
|
||||
val sId = entity.persistentDataContainer.get(sectorKey, PersistentDataType.INTEGER)
|
||||
if (sId == id) {
|
||||
entity.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import org.bukkit.entity.Shulker
|
|||
import org.bukkit.attribute.Attribute
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.block.BlockBreakEvent
|
||||
import org.bukkit.event.block.BlockPlaceEvent
|
||||
import org.bukkit.event.entity.EntityDamageEvent
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
|
|
@ -32,8 +33,31 @@ class SectorListener(
|
|||
private val playerIdService: PlayerIdService
|
||||
) : Listener {
|
||||
|
||||
@EventHandler
|
||||
fun onBreak(event: BlockBreakEvent) {
|
||||
val block = event.block
|
||||
val loc = block.location
|
||||
val player = event.player
|
||||
|
||||
if (sectorService.isSectorBlock(loc.world.name, loc.blockX, loc.blockY, loc.blockZ)) {
|
||||
player.sendMessage(Component.text("You cannot break Sector Core blocks!", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlace(event: BlockPlaceEvent) {
|
||||
val block = event.block
|
||||
val loc = block.location
|
||||
val player = event.player
|
||||
|
||||
// Global protection check
|
||||
if (sectorService.isSectorArea(loc.world.name, loc.blockX, loc.blockY, loc.blockZ)) {
|
||||
player.sendMessage(Component.text("This area is protected by a Sector Core.", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
||||
val item = event.itemInHand
|
||||
val meta = item.itemMeta ?: return
|
||||
val key = NamespacedKey(plugin, "component")
|
||||
|
|
@ -42,48 +66,54 @@ class SectorListener(
|
|||
return
|
||||
}
|
||||
|
||||
val player = event.player
|
||||
val playerEntry = playerIdService.find(player.uniqueId)
|
||||
if (playerEntry == null) {
|
||||
player.sendMessage(Component.text("Identity not found.", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
||||
val block = event.blockPlaced
|
||||
val loc = block.location
|
||||
|
||||
// Space check
|
||||
val above1 = block.getRelative(0, 1, 0)
|
||||
val above2 = block.getRelative(0, 2, 0)
|
||||
|
||||
if (!above1.type.isAir || !above2.type.isAir) {
|
||||
player.sendMessage(Component.text("Not enough space for Sector Core.", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
return
|
||||
// ... (rest of the code)
|
||||
|
||||
// Check 3x3x3 space availability (relative to placed block base)
|
||||
val baseLoc = block.location
|
||||
for (dx in -1..1) {
|
||||
for (dy in 0..2) {
|
||||
for (dz in -1..1) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue // Skip the placed block itself
|
||||
val checkLoc = baseLoc.clone().add(dx.toDouble(), dy.toDouble(), dz.toDouble())
|
||||
if (!checkLoc.block.type.isAir) {
|
||||
player.sendMessage(Component.text("Not enough space! Need 3x3x3 free area.", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record to DB first to get ID
|
||||
// Define Center Location (This will be the DB coordinates)
|
||||
val centerLoc = baseLoc.clone().add(0.0, 1.0, 0.0) // y+1 from base
|
||||
|
||||
val sector = sectorService.createSector(
|
||||
playerEntry.actorId,
|
||||
player.world.name,
|
||||
above1.x,
|
||||
above1.y,
|
||||
above1.z
|
||||
centerLoc.blockX,
|
||||
centerLoc.blockY,
|
||||
centerLoc.blockZ
|
||||
)
|
||||
|
||||
if (sector == null) {
|
||||
player.sendMessage(Component.text("Failed to create sector record.", NamedTextColor.RED))
|
||||
player.sendMessage(Component.text("Failed to create sector.", NamedTextColor.RED))
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
||||
// Create Visuals
|
||||
val sectorKey = NamespacedKey(plugin, "sector_id")
|
||||
val locCenter = loc.clone().add(0.5, 0.0, 0.5)
|
||||
val visualCenter = centerLoc.clone().add(0.5, 0.0, 0.5) // Center of the block space
|
||||
|
||||
// 1. Command Block
|
||||
val cbLoc = locCenter.clone().add(0.0, 1.5, 0.0)
|
||||
// 1. Command Block (Center + 0.5y) -> Matches old base+1.5
|
||||
val cbLoc = visualCenter.clone().add(0.0, 0.5, 0.0)
|
||||
val cb = player.world.spawnEntity(cbLoc, EntityType.BLOCK_DISPLAY) as BlockDisplay
|
||||
cb.block = Material.COMMAND_BLOCK.createBlockData {
|
||||
(it as org.bukkit.block.data.Directional).facing = org.bukkit.block.BlockFace.UP
|
||||
|
|
@ -96,7 +126,7 @@ class SectorListener(
|
|||
)
|
||||
cb.persistentDataContainer.set(sectorKey, PersistentDataType.INTEGER, sector.id)
|
||||
|
||||
// 2. Tinted Glass
|
||||
// 2. Tinted Glass (Center + 0.5y) -> Matches old base+1.5
|
||||
val glass = player.world.spawnEntity(cbLoc, EntityType.BLOCK_DISPLAY) as BlockDisplay
|
||||
glass.block = Material.TINTED_GLASS.createBlockData()
|
||||
glass.transformation = Transformation(
|
||||
|
|
@ -107,8 +137,8 @@ class SectorListener(
|
|||
)
|
||||
glass.persistentDataContainer.set(sectorKey, PersistentDataType.INTEGER, sector.id)
|
||||
|
||||
// 3. Top Cauldron
|
||||
val topCauldronLoc = locCenter.clone().add(0.0, 2.3125, 0.0)
|
||||
// 3. Top Cauldron (Center + 1.3125y) -> Matches old base+2.3125
|
||||
val topCauldronLoc = visualCenter.clone().add(0.0, 1.3125, 0.0)
|
||||
val topCauldron = player.world.spawnEntity(topCauldronLoc, EntityType.BLOCK_DISPLAY) as BlockDisplay
|
||||
topCauldron.block = Material.CAULDRON.createBlockData()
|
||||
topCauldron.transformation = Transformation(
|
||||
|
|
@ -119,8 +149,8 @@ class SectorListener(
|
|||
)
|
||||
topCauldron.persistentDataContainer.set(sectorKey, PersistentDataType.INTEGER, sector.id)
|
||||
|
||||
// 4. Bottom Cauldron
|
||||
val botCauldronLoc = locCenter.clone().add(0.0, 0.6875, 0.0)
|
||||
// 4. Bottom Cauldron (Center - 0.3125y) -> Matches old base+0.6875
|
||||
val botCauldronLoc = visualCenter.clone().add(0.0, -0.3125, 0.0)
|
||||
val botCauldron = player.world.spawnEntity(botCauldronLoc, EntityType.BLOCK_DISPLAY) as BlockDisplay
|
||||
botCauldron.block = Material.CAULDRON.createBlockData()
|
||||
botCauldron.transformation = Transformation(
|
||||
|
|
@ -131,8 +161,8 @@ class SectorListener(
|
|||
)
|
||||
botCauldron.persistentDataContainer.set(sectorKey, PersistentDataType.INTEGER, sector.id)
|
||||
|
||||
// Spawn Shulker (Hitbox)
|
||||
val shulkerLoc = loc.clone().add(0.5, 1.0, 0.5) // Y+1, sits in space 1-2.
|
||||
// Spawn Shulker (Hitbox) at Center
|
||||
val shulkerLoc = visualCenter.clone() // Center is exactly y+1 from base
|
||||
val shulker = player.world.spawnEntity(shulkerLoc, EntityType.SHULKER) as Shulker
|
||||
shulker.setAI(false)
|
||||
shulker.isInvisible = true
|
||||
|
|
@ -146,16 +176,20 @@ class SectorListener(
|
|||
// Tag Shulker with Sector ID
|
||||
shulker.persistentDataContainer.set(sectorKey, PersistentDataType.INTEGER, sector.id)
|
||||
|
||||
// Place Blocks
|
||||
// Place Blocks (Base and Top)
|
||||
// Base is at center.y - 1 (The placed block)
|
||||
// Top is at center.y + 1
|
||||
|
||||
block.type = Material.DEEPSLATE_TILE_SLAB
|
||||
val bottomSlab = block.blockData as Slab
|
||||
bottomSlab.type = Slab.Type.BOTTOM
|
||||
block.blockData = bottomSlab
|
||||
|
||||
above2.type = Material.DEEPSLATE_TILE_SLAB
|
||||
val topSlab = above2.blockData as Slab
|
||||
val topBlock = centerLoc.clone().add(0.0, 1.0, 0.0).block
|
||||
topBlock.type = Material.DEEPSLATE_TILE_SLAB
|
||||
val topSlab = topBlock.blockData as Slab
|
||||
topSlab.type = Slab.Type.TOP
|
||||
above2.blockData = topSlab
|
||||
topBlock.blockData = topSlab
|
||||
|
||||
player.sendMessage(Component.text("Sector Core placed!", NamedTextColor.GREEN))
|
||||
}
|
||||
|
|
@ -175,80 +209,81 @@ class SectorListener(
|
|||
val damage = event.finalDamage
|
||||
val maxHealth = entity.getAttribute(Attribute.MAX_HEALTH)?.value ?: 1000.0
|
||||
|
||||
val newHp = sectorService.reduceHealth(sectorId, damage.toInt())
|
||||
|
||||
// Cancel the event so it doesn't actually die in vanilla terms,
|
||||
// OR let it happen but force health reset.
|
||||
// Cancelling prevents the red flash in some versions/cases, so setting damage to 0 is often better
|
||||
// if we want the effect. But resetting health is robust.
|
||||
// Let's reset health.
|
||||
entity.health = maxHealth
|
||||
entity.noDamageTicks = 0
|
||||
val oldHp = sectorService.getHealth(sectorId) ?: return // Get current health from service
|
||||
val newHp = sectorService.reduceHealth(sectorId, damage.toInt())
|
||||
|
||||
entity.health = maxHealth
|
||||
entity.noDamageTicks = 0 // Disable invulnerability
|
||||
|
||||
if (newHp != null) {
|
||||
val loc = entity.location
|
||||
val world = entity.world
|
||||
if (newHp != null) {
|
||||
val loc = entity.location
|
||||
val world = entity.world
|
||||
|
||||
// Sound Logic
|
||||
val threshold = 100 // 10% of 1000
|
||||
val crossedThreshold = (oldHp / threshold) > (newHp / threshold)
|
||||
|
||||
// Effects
|
||||
world.playSound(loc, Sound.BLOCK_ANVIL_PLACE, 1.0f, 0.5f) // Heavy metallic sound
|
||||
world.spawnParticle(Particle.BLOCK, loc.add(0.0, 0.5, 0.0), 20, 0.3, 0.3, 0.3, Material.BEDROCK.createBlockData())
|
||||
|
||||
// Action Bar Display
|
||||
if (event is EntityDamageByEntityEvent && event.damager is Player) {
|
||||
val player = event.damager as Player
|
||||
val percent = newHp.toDouble() / maxHealth.toDouble()
|
||||
val color = when {
|
||||
percent > 0.5 -> NamedTextColor.GREEN
|
||||
percent > 0.2 -> NamedTextColor.YELLOW
|
||||
else -> NamedTextColor.RED
|
||||
}
|
||||
|
||||
val progressBar = createProgressBar(newHp, maxHealth.toInt(), color)
|
||||
|
||||
val message = Component.text()
|
||||
.append(Component.text("Sector Core ", NamedTextColor.GOLD))
|
||||
.append(progressBar)
|
||||
.append(Component.text(" "))
|
||||
.append(Component.text(newHp, color))
|
||||
.append(Component.text("/", NamedTextColor.GRAY))
|
||||
.append(Component.text(maxHealth.toInt(), NamedTextColor.GRAY))
|
||||
.build()
|
||||
|
||||
player.sendActionBar(message)
|
||||
}
|
||||
|
||||
if (newHp <= 0) {
|
||||
// Destruction
|
||||
entity.remove()
|
||||
|
||||
// Remove other visuals in chunk
|
||||
val chunk = entity.chunk
|
||||
chunk.entities.forEach { ent ->
|
||||
val sId = ent.persistentDataContainer.get(sectorKey, PersistentDataType.INTEGER)
|
||||
if (sId == sectorId) ent.remove()
|
||||
}
|
||||
|
||||
// Remove blocks.
|
||||
// We know Shulker is at x, y, z.
|
||||
// Base is y-1, Top is y+1
|
||||
val loc = entity.location
|
||||
val world = entity.world
|
||||
val x = loc.blockX
|
||||
val y = loc.blockY
|
||||
val z = loc.blockZ
|
||||
|
||||
val base = world.getBlockAt(x, y - 1, z)
|
||||
val top = world.getBlockAt(x, y + 1, z)
|
||||
|
||||
if (base.type == Material.DEEPSLATE_TILE_SLAB) base.type = Material.AIR
|
||||
if (top.type == Material.DEEPSLATE_TILE_SLAB) top.type = Material.AIR
|
||||
|
||||
world.dropItemNaturally(loc, org.bukkit.inventory.ItemStack(Material.DEEPSLATE_TILE_SLAB, 2))
|
||||
|
||||
// Maybe delete from DB or mark as destroyed?
|
||||
// For now, valid destruction.
|
||||
}
|
||||
if (crossedThreshold) {
|
||||
world.playSound(loc, Sound.BLOCK_ANVIL_PLACE, 1.0f, 0.5f)
|
||||
} else {
|
||||
world.playSound(loc, Sound.BLOCK_STONE_BREAK, 1.0f, 1.0f)
|
||||
}
|
||||
|
||||
world.spawnParticle(Particle.BLOCK, loc.add(0.0, 0.5, 0.0), 20, 0.3, 0.3, 0.3, Material.BEDROCK.createBlockData())
|
||||
|
||||
if (event is EntityDamageByEntityEvent && event.damager is Player) {
|
||||
val player = event.damager as Player
|
||||
|
||||
val percent = newHp.toDouble() / maxHealth
|
||||
val color = when {
|
||||
percent > 0.5 -> NamedTextColor.GREEN
|
||||
percent > 0.2 -> NamedTextColor.YELLOW
|
||||
else -> NamedTextColor.RED
|
||||
}
|
||||
|
||||
val progressBar = createProgressBar(newHp, maxHealth.toInt(), color)
|
||||
|
||||
val message = Component.text()
|
||||
.append(Component.text("Sector Core ", NamedTextColor.GOLD))
|
||||
.append(progressBar)
|
||||
.append(Component.text(" "))
|
||||
.append(Component.text(newHp, color))
|
||||
.append(Component.text("/", NamedTextColor.GRAY))
|
||||
.append(Component.text(maxHealth.toInt(), NamedTextColor.GRAY))
|
||||
.build()
|
||||
|
||||
player.sendActionBar(message)
|
||||
}
|
||||
|
||||
if (newHp <= 0) {
|
||||
// Destruction
|
||||
entity.remove()
|
||||
|
||||
// Remove other visuals in chunk
|
||||
val chunk = entity.chunk
|
||||
chunk.entities.forEach { ent ->
|
||||
val sId = ent.persistentDataContainer.get(sectorKey, PersistentDataType.INTEGER)
|
||||
if (sId == sectorId) ent.remove()
|
||||
}
|
||||
|
||||
// Remove blocks.
|
||||
val x = loc.blockX
|
||||
val y = loc.blockY
|
||||
val z = loc.blockZ
|
||||
|
||||
val base = world.getBlockAt(x, y - 1, z)
|
||||
val top = world.getBlockAt(x, y + 1, z)
|
||||
|
||||
if (base.type == Material.DEEPSLATE_TILE_SLAB) base.type = Material.AIR
|
||||
if (top.type == Material.DEEPSLATE_TILE_SLAB) top.type = Material.AIR
|
||||
|
||||
world.dropItemNaturally(loc, org.bukkit.inventory.ItemStack(Material.DEEPSLATE_TILE_SLAB, 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createProgressBar(current: Int, max: Int, color: NamedTextColor): Component {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,24 @@ class SectorService(private val database: Database) {
|
|||
|
||||
private val hpCache = ConcurrentHashMap<Int, Int>()
|
||||
private val dirtySet = ConcurrentHashMap.newKeySet<Int>()
|
||||
private val sectorsCache = ConcurrentHashMap<Int, Sector>()
|
||||
|
||||
fun init() {
|
||||
transaction(database) {
|
||||
SchemaUtils.createMissingTablesAndColumns(SectorsTable)
|
||||
SectorsTable.selectAll().forEach {
|
||||
val id = it[SectorsTable.id]
|
||||
val sector = Sector(
|
||||
id,
|
||||
it[SectorsTable.ownerActorId],
|
||||
it[SectorsTable.world],
|
||||
it[SectorsTable.x],
|
||||
it[SectorsTable.y],
|
||||
it[SectorsTable.z],
|
||||
it[SectorsTable.hp]
|
||||
)
|
||||
sectorsCache[id] = sector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,42 +51,41 @@ class SectorService(private val database: Database) {
|
|||
}[SectorsTable.id]
|
||||
|
||||
hpCache[id] = 1000
|
||||
Sector(id, ownerActorId, world, x, y, z, 1000)
|
||||
val sector = Sector(id, ownerActorId, world, x, y, z, 1000)
|
||||
sectorsCache[id] = sector
|
||||
sector
|
||||
}
|
||||
}
|
||||
|
||||
fun getSectorAt(world: String, x: Int, y: Int, z: Int): Sector? {
|
||||
return transaction(database) {
|
||||
SectorsTable.selectAll()
|
||||
.andWhere { SectorsTable.world eq world }
|
||||
.andWhere { SectorsTable.x eq x }
|
||||
.andWhere { SectorsTable.y eq y }
|
||||
.andWhere { SectorsTable.z eq z }
|
||||
.map {
|
||||
Sector(
|
||||
it[SectorsTable.id],
|
||||
it[SectorsTable.ownerActorId],
|
||||
it[SectorsTable.world],
|
||||
it[SectorsTable.x],
|
||||
it[SectorsTable.y],
|
||||
it[SectorsTable.z],
|
||||
it[SectorsTable.hp]
|
||||
)
|
||||
}.singleOrNull()
|
||||
// Optimized to use cache
|
||||
return sectorsCache.values.firstOrNull {
|
||||
it.world == world && it.x == x && it.y == y && it.z == z
|
||||
}
|
||||
}
|
||||
|
||||
fun getHealth(id: Int): Int? {
|
||||
// Try get from cache
|
||||
var currentHp = hpCache[id]
|
||||
|
||||
// If not in cache, load from DB
|
||||
if (currentHp == null) {
|
||||
val sector = sectorsCache[id] ?: return null
|
||||
|
||||
currentHp = sector.hp
|
||||
hpCache[id] = currentHp
|
||||
}
|
||||
return currentHp
|
||||
}
|
||||
|
||||
fun reduceHealth(id: Int, amount: Int): Int? {
|
||||
// Try get from cache
|
||||
var currentHp = hpCache[id]
|
||||
|
||||
// If not in cache, load from DB
|
||||
// If not in cache, try to init from sectorsCache
|
||||
if (currentHp == null) {
|
||||
val sector = transaction(database) {
|
||||
SectorsTable.selectAll().andWhere { SectorsTable.id eq id }.singleOrNull()
|
||||
} ?: return null // Not found
|
||||
|
||||
currentHp = sector[SectorsTable.hp]
|
||||
val sector = sectorsCache[id] ?: return null
|
||||
currentHp = sector.hp
|
||||
hpCache[id] = currentHp
|
||||
}
|
||||
|
||||
|
|
@ -122,10 +135,32 @@ class SectorService(private val database: Database) {
|
|||
if (sector != null) {
|
||||
hpCache.remove(id)
|
||||
dirtySet.remove(id)
|
||||
sectorsCache.remove(id)
|
||||
}
|
||||
|
||||
return sector
|
||||
}
|
||||
|
||||
fun isSectorBlock(world: String, x: Int, y: Int, z: Int): Boolean {
|
||||
// Sector blocks are at center.y - 1 (Base) and center.y + 1 (Top)
|
||||
// Center is at sector.y
|
||||
return sectorsCache.values.any {
|
||||
it.world == world &&
|
||||
it.x == x &&
|
||||
it.z == z &&
|
||||
(it.y - 1 == y || it.y + 1 == y)
|
||||
}
|
||||
}
|
||||
|
||||
fun isSectorArea(world: String, x: Int, y: Int, z: Int): Boolean {
|
||||
// Check 3x3x3 around center (sector.x, sector.y, sector.z)
|
||||
return sectorsCache.values.any {
|
||||
it.world == world &&
|
||||
x >= it.x - 1 && x <= it.x + 1 &&
|
||||
y >= it.y - 1 && y <= it.y + 1 &&
|
||||
z >= it.z - 1 && z <= it.z + 1
|
||||
}
|
||||
}
|
||||
|
||||
fun exists(id: Int): Boolean {
|
||||
if (hpCache.containsKey(id)) return true
|
||||
|
|
@ -135,6 +170,25 @@ class SectorService(private val database: Database) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAllSectorsList(): List<Sector> {
|
||||
return transaction(database) {
|
||||
SectorsTable.selectAll().map {
|
||||
val id = it[SectorsTable.id]
|
||||
val cachedHp = hpCache[id]
|
||||
val hp = cachedHp ?: it[SectorsTable.hp]
|
||||
Sector(
|
||||
id,
|
||||
it[SectorsTable.ownerActorId],
|
||||
it[SectorsTable.world],
|
||||
it[SectorsTable.x],
|
||||
it[SectorsTable.y],
|
||||
it[SectorsTable.z],
|
||||
hp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllSectors(world: String): List<Sector> {
|
||||
return transaction(database) {
|
||||
SectorsTable.selectAll()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
package net.hareworks.hcu.landsector.task
|
||||
|
||||
import net.hareworks.hcu.landsector.LandSectorPlugin
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.NamespacedKey
|
||||
import org.bukkit.entity.BlockDisplay
|
||||
import org.bukkit.entity.EntityType
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
import org.bukkit.scheduler.BukkitRunnable
|
||||
import org.bukkit.util.Transformation
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.PI
|
||||
|
||||
class SectorRotationTask(private val plugin: LandSectorPlugin) : BukkitRunnable() {
|
||||
|
||||
private val sectorKey = NamespacedKey(plugin, "sector_id")
|
||||
private val scale = Vector3f(0.5f, 0.5f, 0.5f)
|
||||
private val centerOffset = Vector3f(0.25f, 0.25f, 0.25f) // Scale 0.5 * Model Center (0.5, 0.5, 0.5)
|
||||
|
||||
override fun run() {
|
||||
val sectors = plugin.sectorService?.getAllSectorsList() ?: return
|
||||
// Bukkit.getLogger().info("Task running. Sectors count: ${sectors.size}")
|
||||
|
||||
sectors.forEach { sector ->
|
||||
val world = Bukkit.getWorld(sector.world) ?: return@forEach
|
||||
// Simple check if chunk is loaded to avoid loading chunks
|
||||
val chunkX = sector.x shr 4
|
||||
val chunkZ = sector.z shr 4
|
||||
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
// Bukkit.getLogger().info("Chunk not loaded for sector ${sector.id}")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
// Check entities at the core location
|
||||
val centerLoc = org.bukkit.Location(world, sector.x + 0.5, sector.y + 0.5, sector.z + 0.5)
|
||||
|
||||
// Search radius expanded to 2.0 to ensure hit
|
||||
val entities = world.getNearbyEntities(centerLoc, 2.0, 2.0, 2.0)
|
||||
|
||||
// Bukkit.getLogger().info("Checking sector ${sector.id} at $centerLoc. Found entities: ${entities.size}")
|
||||
|
||||
entities.forEach { entity ->
|
||||
if (entity.type == EntityType.BLOCK_DISPLAY && entity.persistentDataContainer.has(sectorKey, PersistentDataType.INTEGER)) {
|
||||
val bd = entity as BlockDisplay
|
||||
// Check if it's the command block
|
||||
if (bd.block.material == Material.COMMAND_BLOCK) {
|
||||
// Bukkit.getLogger().info("Rotater found target: ${bd.uniqueId}")
|
||||
updateRotation(bd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRotation(bd: BlockDisplay) {
|
||||
// Continuous rotation based on time: 1 full rotation every 4 seconds
|
||||
val periodMs = 4000L
|
||||
val time = System.currentTimeMillis()
|
||||
val phase = (time % periodMs).toDouble() / periodMs.toDouble()
|
||||
val angle = (phase * 2 * Math.PI).toFloat() // 0 to 2PI
|
||||
|
||||
// Log for debug (once per 100 ticks approx to avoid spam? No, just once to verify)
|
||||
// Bukkit.getLogger().info("Rotating BD: angle=$angle")
|
||||
|
||||
// Rotation around Y axis
|
||||
val rot = Quaternionf().rotateY(angle)
|
||||
|
||||
val centerOffset = Vector3f(0.25f, 0.25f, 0.25f) // The center in Scaled space
|
||||
// T = - (R * C)
|
||||
val rotatedOffset = Vector3f(centerOffset).rotate(rot)
|
||||
val newTranslation = Vector3f(rotatedOffset).negate()
|
||||
|
||||
val newTrans = Transformation(
|
||||
newTranslation,
|
||||
rot,
|
||||
scale,
|
||||
Quaternionf(0f, 0f, 0f, 1f)
|
||||
)
|
||||
|
||||
// Interpolation settings
|
||||
bd.interpolationDuration = 3 // Short duration to match high frequency update
|
||||
bd.interpolationDelay = -1 // Start immediately
|
||||
bd.transformation = newTrans
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user