nms custom villager and restructuring for teams

This commit is contained in:
ruby
2025-01-05 01:27:42 +13:00
parent 35c7124451
commit f8825d535f
17 changed files with 347 additions and 162 deletions

View File

@ -1,6 +1,7 @@
plugins { plugins {
kotlin("jvm") version "2.1.20-Beta1" kotlin("jvm") version "2.1.20-Beta1"
id("com.github.johnrengelman.shadow") version "8.1.1" id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.11"
// id("de.nilsdruyen.gradle-ftp-upload-plugin") version "0.5.0" // id("de.nilsdruyen.gradle-ftp-upload-plugin") version "0.5.0"
} }
@ -24,13 +25,13 @@ repositories {
} }
dependencies { dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.3-R0.1-SNAPSHOT") // compileOnly("io.papermc.paper:paper-api:1.21.3-R0.1-SNAPSHOT")
// compileOnly("com.comphenix.protocol:ProtocolLib:5.3.0") // compileOnly("com.comphenix.protocol:ProtocolLib:5.3.0")
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit")
compileOnly(files("libs/ProtocolLib.jar")) compileOnly(files("libs/ProtocolLib.jar"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation(platform("com.intellectualsites.bom:bom-newest:1.51")) // Ref: https://github.com/IntellectualSites/bom implementation(platform("com.intellectualsites.bom:bom-newest:1.51")) // Ref: https://github.com/IntellectualSites/bom
// implementation("de.nilsdruyen.gradle-ftp-upload-plugin:de.nilsdruyen.gradle-ftp-upload-plugin.gradle.plugin:0.5.0") paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
} }
val targetJavaVersion = 21 val targetJavaVersion = 21

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -1,15 +1,22 @@
package com.pobnellion.aoe package com.pobnellion.aoe
import com.pobnellion.aoe.command.TestCommand import com.pobnellion.aoe.command.TestCommand
import com.pobnellion.aoe.team.Team
import com.pobnellion.aoe.ui.PlaceHint import com.pobnellion.aoe.ui.PlaceHint
import com.pobnellion.aoe.ui.PlaceItem import com.pobnellion.aoe.ui.PlaceItem
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import java.io.File import java.io.File
class Aoe : JavaPlugin() { class Aoe : JavaPlugin() {
companion object { companion object {
private var instance: Aoe? = null private var instance: Aoe? = null
val teams: MutableList<Team> = mutableListOf()
fun getTeam(player: Player): Team? {
return teams.singleOrNull() { team -> team.players.contains(player) }
}
fun getSchematicsFolder(): File = File(instance!!.dataFolder, "schematics") fun getSchematicsFolder(): File = File(instance!!.dataFolder, "schematics")
} }
@ -18,8 +25,8 @@ class Aoe : JavaPlugin() {
instance = this instance = this
// Events // Events
Bukkit.getPluginManager().registerEvents(PlaceHint, this)
Bukkit.getPluginManager().registerEvents(PlaceItem(), this) Bukkit.getPluginManager().registerEvents(PlaceItem(), this)
Bukkit.getPluginManager().registerEvents(PlaceHint, this)
// Commands // Commands
this.getCommand("debug")!!.setExecutor(TestCommand()) this.getCommand("debug")!!.setExecutor(TestCommand())

View File

@ -6,12 +6,8 @@ import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Player import org.bukkit.entity.Player
class Blacksmith: Building { class Blacksmith(location: Location, variant: Int): Building(location, variant) {
override fun showPlaceHint(player: Player) { fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, offsetY: Int) {
PlaceHint.add(player, "house2", ::place, PlaceHintValidators::allReplaceable)
}
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) {
for (x in 0..<sizeX.toInt()) for (x in 0..<sizeX.toInt())
for (y in 0..<sizeY.toInt()) for (y in 0..<sizeY.toInt())
for (z in 0..<sizeZ.toInt()) for (z in 0..<sizeZ.toInt())

View File

@ -3,6 +3,12 @@ package com.pobnellion.aoe.building
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.entity.Player import org.bukkit.entity.Player
interface Building { abstract class Building(val location: Location, val variant: Int) {
fun showPlaceHint(player: Player) }
interface BuildingInfo {
val buildingType: BuildingType
val schematicNames: List<String>
fun validate(location: Location): Boolean
fun create(location: Location, variant: Int): Building
} }

View File

@ -0,0 +1,12 @@
package com.pobnellion.aoe.building
enum class BuildingType {
BARRACKS,
FARM,
HOUSE,
MILL,
TOWN_CENTER,
UNIQUE,
WALL,
WATCH_TOWER,
}

View File

@ -1,17 +1,22 @@
package com.pobnellion.aoe.building package com.pobnellion.aoe.building
import com.pobnellion.aoe.ui.PlaceHint
import com.pobnellion.aoe.ui.PlaceHintValidators import com.pobnellion.aoe.ui.PlaceHintValidators
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Player
class House: Building { class House(location: Location, variant: Int): Building(location, variant) {
override fun showPlaceHint(player: Player) { companion object Info: BuildingInfo {
PlaceHint.add(player, "house1", ::place, PlaceHintValidators::allReplaceable) override val buildingType: BuildingType = BuildingType.HOUSE
override val schematicNames: List<String> = listOf("house1")
override fun validate(location: Location): Boolean {
return PlaceHintValidators.allReplaceable(location, 5f, 5f, 5f, -1)
}
override fun create(location: Location, variant: Int): Building = House(location, variant)
} }
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) { fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, int: Int) {
for (x in 0..<sizeX.toInt()) for (x in 0..<sizeX.toInt())
for (y in 0..<sizeY.toInt()) for (y in 0..<sizeY.toInt())
for (z in 0..<sizeZ.toInt()) for (z in 0..<sizeZ.toInt())

View File

@ -1,14 +1,13 @@
package com.pobnellion.aoe.command package com.pobnellion.aoe.command
import com.pobnellion.aoe.ui.PlaceHint import com.pobnellion.aoe.entity.AoeVillager
import org.bukkit.Material
import org.bukkit.command.Command import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
class TestCommand : CommandExecutor { class TestCommand : CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>?): Boolean { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (sender !is Player) { if (sender !is Player) {
sender.sendMessage("Command can only be executed by players") sender.sendMessage("Command can only be executed by players")
return true return true

View File

@ -0,0 +1,21 @@
package com.pobnellion.aoe.entity
import com.pobnellion.aoe.building.Building
import com.pobnellion.aoe.entity.goals.GoToBuildingGoal
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.npc.Villager
import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld
class AoeVillager(location: Location) : Villager(EntityType.VILLAGER, (location.world as CraftWorld).handle) {
init {
setPos(location.x, location.y, location.z)
level().addFreshEntity(this)
targetSelector.removeAllGoals { true }
}
fun goToBuilding(building: Building) {
targetSelector.addGoal(0, GoToBuildingGoal(this, building, 1.0))
}
}

View File

@ -0,0 +1,35 @@
package com.pobnellion.aoe.entity.goals
import com.pobnellion.aoe.building.Building
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import java.util.*
class GoToBuildingGoal(
private val mob: PathfinderMob,
private val building: Building,
private var speedModifier: Double
): Goal() {
private var recalculateTicks = 0
init {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.JUMP))
}
override fun canUse(): Boolean {
return true
}
override fun start() {
mob.navigation.moveTo(building.location.x, building.location.y, building.location.z, speedModifier)
}
override fun tick() {
recalculateTicks++
if (recalculateTicks % 40 == 0)
mob.navigation.moveTo(building.location.x, building.location.y, building.location.z, speedModifier)
}
override fun requiresUpdateEveryTick(): Boolean = true
}

View File

@ -0,0 +1,23 @@
package com.pobnellion.aoe.team
import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.building.House
import org.bukkit.entity.Player
class Plains(players: List<Player>) : Team(players) {
override fun getBuildingInfo(type: BuildingType): BuildingInfo {
return when (type) {
BuildingType.BARRACKS -> TODO()
BuildingType.FARM -> TODO()
BuildingType.HOUSE -> House.Info
BuildingType.MILL -> TODO()
BuildingType.TOWN_CENTER -> TODO()
BuildingType.UNIQUE -> TODO()
BuildingType.WALL -> TODO()
BuildingType.WATCH_TOWER -> TODO()
}
}
}

View File

@ -1,7 +1,25 @@
package com.pobnellion.aoe.team package com.pobnellion.aoe.team
import com.pobnellion.aoe.building.Building import com.pobnellion.aoe.building.Building
import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.ui.BuildingPlaceHint
import org.bukkit.Location
import org.bukkit.entity.Player
import kotlin.random.Random
class Team { abstract class Team(val players: List<Player>) {
public val Buildings: List<Building> = listOf() val buildings: MutableList<Building> = mutableListOf()
protected abstract fun getBuildingInfo(type: BuildingType): BuildingInfo
fun addBuilding(location: Location, building: BuildingInfo, variant: Int) {
buildings.add(building.create(location, variant))
}
fun showBuildingPlaceHint(type: BuildingType, player: Player) {
val info = getBuildingInfo(type)
val variant = Random.nextInt(info.schematicNames.size)
BuildingPlaceHint.add(player, info, ::addBuilding, variant)
}
} }

View File

@ -0,0 +1,80 @@
package com.pobnellion.aoe.ui
import com.comphenix.protocol.PacketType
import com.comphenix.protocol.ProtocolLibrary
import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.wrappers.BukkitConverters
import com.comphenix.protocol.wrappers.Pair
import com.comphenix.protocol.wrappers.WrappedBlockData
import com.comphenix.protocol.wrappers.WrappedDataValue
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.util.Vector
import org.joml.Math
import org.joml.Vector3f
import java.util.*
class AreaPlaceHint(
override val player: Player,
private val sizeX: Float,
private val sizeY: Float,
private val sizeZ: Float,
private val onConfirm: (Location, Float, Float, Float) -> Unit,
private val validate: (Location, Float, Float, Float) -> Boolean
) : PlaceHint() {
companion object {
fun add(
player: Player,
sizeX: Float,
sizeY: Float,
sizeZ: Float,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean,
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = AreaPlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, validate)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
}
private var base: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID())
init {
this.offset = Vector(sizeX / 2.0, 0.0, sizeZ / 2.0)
}
override fun validate(location: Location) = validate(location, sizeX, sizeY, sizeZ)
override fun confirm() = onConfirm(currentLocation!!, sizeX, sizeY, sizeZ)
override fun addDisplays(initialLocation: Location) {
base = spawnBlockDisplay(initialLocation, Material.GRAY_STAINED_GLASS.createBlockData(),
scale = Vector3f(sizeX + 0.1f, 0.2f, sizeZ + 0.1f), offset = Vector3f(-0.05f, -0.05f, -0.05f))
}
override fun updateColour() {
super.updateColour()
// base
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
dataPacket.integers.write(0, base.first)
val material = if (isValid) Material.LIME_STAINED_GLASS else Material.RED_STAINED_GLASS
val blockData = BukkitConverters
.getWrappedBlockDataConverter()
.getGeneric(WrappedBlockData.createData(material))
val metadata = listOf(
WrappedDataValue(23, Registry.getBlockDataSerializer(false), blockData)
)
dataPacket.dataValueCollectionModifier.write(0, metadata)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
}
}

View File

@ -0,0 +1,75 @@
package com.pobnellion.aoe.ui
import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.building.BuildingInfo
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldedit.extent.clipboard.Clipboard
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import com.sk89q.worldedit.math.BlockVector3
import org.bukkit.Location
import org.bukkit.entity.Player
import org.bukkit.util.Vector
import org.joml.Vector3f
import java.io.File
import java.io.FileInputStream
import kotlin.math.floor
class BuildingPlaceHint(
override val player: Player,
private val buildingInfo: BuildingInfo,
private val onConfirm: (Location, BuildingInfo, Int) -> Unit,
private val buildingVariant: Int = 0
) : PlaceHint() {
companion object {
fun add(
player: Player,
buildingInfo: BuildingInfo,
onConfirm: (Location, BuildingInfo, Int) -> Unit,
buildingVariant: Int = 0
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = BuildingPlaceHint(player, buildingInfo, onConfirm, buildingVariant)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
}
private val schematic: Clipboard
init {
val schematicName = buildingInfo.schematicNames[buildingVariant]
val file = File(Aoe.getSchematicsFolder(),
if (schematicName.endsWith(".schem")) schematicName else "$schematicName.schem")
val format = ClipboardFormats.findByFile(file)
val reader = format?.getReader(FileInputStream(file))
val clipboard = reader?.read() ?: throw NullPointerException("Could not load schematic ${file.path}. Is it present?")
clipboard.origin = BlockVector3.ZERO
this.schematic = clipboard
this.offset = Vector(
-floor(clipboard.dimensions.x() / 2.0),
clipboard.minY.toDouble(),
-floor(clipboard.dimensions.z() / 2.0))
}
override fun validate(location: Location) = buildingInfo.validate(location)
override fun confirm() = onConfirm(currentLocation!!, buildingInfo, buildingVariant)
override fun addDisplays(initialLocation: Location) {
schematic.iterator().forEach { blockPosition ->
val block = BukkitAdapter.adapt(schematic.getBlock(blockPosition))
if (!block.material.isAir && !block.material.isEmpty) {
val x = (blockPosition.x() - schematic.minimumPoint.x()).toFloat()
val y = blockPosition.y().toFloat()
val z = (blockPosition.z() - schematic.minimumPoint.z()).toFloat()
displayIds.add(spawnBlockDisplay(initialLocation, block, offset = Vector3f(x, y, z), options = 0b01000000))
}
}
}
}

View File

@ -6,12 +6,7 @@ import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.wrappers.* import com.comphenix.protocol.wrappers.*
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry
import com.destroystokyo.paper.MaterialSetTag import com.destroystokyo.paper.MaterialSetTag
import com.pobnellion.aoe.Aoe
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldedit.extent.clipboard.Clipboard
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.data.BlockData import org.bukkit.block.data.BlockData
import org.bukkit.entity.EntityType import org.bukkit.entity.EntityType
import org.bukkit.entity.Player import org.bukkit.entity.Player
@ -20,47 +15,16 @@ import org.bukkit.event.Listener
import org.bukkit.event.block.Action import org.bukkit.event.block.Action
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.util.Vector
import org.joml.Math import org.joml.Math
import org.joml.Vector3f import org.joml.Vector3f
import java.io.File
import java.io.FileInputStream
import java.util.* import java.util.*
import kotlin.math.floor
class PlaceHint { abstract class PlaceHint {
companion object : Listener { companion object : Listener {
private const val MAX_TARGET_DISTANCE = 64 const val MAX_TARGET_DISTANCE = 64
private val playerHints = HashMap<UUID, PlaceHint>() @JvmStatic
protected val playerHints = HashMap<UUID, PlaceHint>()
fun add(
player: Player,
sizeX: Float,
sizeY: Float,
sizeZ: Float,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean,
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = PlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, validate)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
fun add(
player: Player,
schematicName: String,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean,
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = PlaceHint(player, schematicName, onConfirm, validate)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
fun remove(player: Player) { fun remove(player: Player) {
if (!playerHints.containsKey(player.uniqueId)) if (!playerHints.containsKey(player.uniqueId))
@ -79,7 +43,8 @@ class PlaceHint {
return playerHints.containsKey(player.uniqueId) return playerHints.containsKey(player.uniqueId)
} }
private fun createOutlineTeam(player: Player) { @JvmStatic
protected fun createOutlineTeam(player: Player) {
val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM) val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
packet.strings.write(0, "placeValid") packet.strings.write(0, "placeValid")
packet.integers.write(0, 0) packet.integers.write(0, 0)
@ -105,13 +70,12 @@ class PlaceHint {
event.isCancelled = true event.isCancelled = true
// left click - place // left click - confirm
if (event.action == Action.LEFT_CLICK_BLOCK || event.action == Action.LEFT_CLICK_AIR) { if (event.action == Action.LEFT_CLICK_BLOCK || event.action == Action.LEFT_CLICK_AIR) {
if (!playerHints[event.player.uniqueId]!!.isValid) if (!playerHints[event.player.uniqueId]!!.isValid)
return return
val marker = playerHints[event.player.uniqueId]!! playerHints[event.player.uniqueId]!!.confirm()
marker.onConfirm(marker.currentLocation!!, marker.sizeX, marker.sizeY, marker.sizeZ)
remove(event.player) remove(event.player)
} }
@ -132,53 +96,19 @@ class PlaceHint {
.build() .build()
} }
private val player: Player protected abstract val player: Player
private val sizeX: Float protected lateinit var offset: Vector
private val sizeY: Float
private val sizeZ: Float
val onConfirm: (Location, Float, Float, Float) -> Unit
val validate: (Location, Float, Float, Float) -> Boolean
private val schematic: Clipboard?
private var marker: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID()) private var marker: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID())
private var base: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID()) protected val displayIds: MutableList<Pair<Int, UUID>> = mutableListOf()
private val displayIds: MutableList<Pair<Int, UUID>> = mutableListOf()
var currentLocation: Location? = null var currentLocation: Location? = null
var isValid = false var isValid = false
constructor(player: Player, sizeX: Float, sizeY: Float, sizeZ: Float, protected abstract fun addDisplays(initialLocation: Location)
onConfirm: (Location, Float, Float, Float) -> Unit, protected abstract fun confirm()
validate: (Location, Float, Float, Float) -> Boolean) { protected abstract fun validate(location: Location): Boolean
this.player = player
this.sizeX = sizeX
this.sizeY = sizeY
this.sizeZ = sizeZ
this.onConfirm = onConfirm
this.validate = validate
this.schematic = null
}
constructor(player: Player, schematicName: String, protected fun show(initialLocation: Location) {
onConfirm: (Location, Float, Float, Float) -> Unit, initialLocation.add(this.offset)
validate: (Location, Float, Float, Float) -> Boolean) {
this.player = player
this.onConfirm = onConfirm
this.validate = validate
val file = File(Aoe.getSchematicsFolder(),
if (schematicName.endsWith(".schem")) schematicName else "$schematicName.schem")
val format = ClipboardFormats.findByFile(file)
val reader = format?.getReader(FileInputStream(file))
val clipboard = reader?.read() ?: throw NullPointerException("Could not load schematic ${file.path}. Is it present?")
this.schematic = clipboard
this.sizeX = clipboard.dimensions.x().toFloat()
this.sizeY = clipboard.dimensions.y().toFloat()
this.sizeZ = clipboard.dimensions.z().toFloat()
}
private fun show(initialLocation: Location) {
initialLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0))
currentLocation = initialLocation currentLocation = initialLocation
val spawnMarkerPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY) val spawnMarkerPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
@ -190,20 +120,7 @@ class PlaceHint {
.write(1, initialLocation.y) .write(1, initialLocation.y)
.write(2, initialLocation.z) .write(2, initialLocation.z)
base = spawnBlockDisplay(initialLocation, Material.GRAY_STAINED_GLASS.createBlockData(), addDisplays(initialLocation)
scale = Vector3f(sizeX + 0.1f, 0.2f, sizeZ + 0.1f), offset = Vector3f(-0.05f, -0.05f, -0.05f))
schematic?.iterator()?.forEach { blockPosition ->
val block = BukkitAdapter.adapt(schematic.getBlock(blockPosition))
if (!block.material.isAir && !block.material.isEmpty) {
val x = (blockPosition.x() - schematic.minimumPoint.x()).toFloat()
val y = (blockPosition.y() - schematic.minimumPoint.y()).toFloat()
val z = (blockPosition.z() - schematic.minimumPoint.z()).toFloat()
displayIds.add(spawnBlockDisplay(initialLocation, block, offset = Vector3f(x, y, z), options = 0b01000000))
}
}
val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM) val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
outlineTeamPacket.strings.write(0, "placeValid") outlineTeamPacket.strings.write(0, "placeValid")
@ -211,7 +128,6 @@ class PlaceHint {
outlineTeamPacket.getSpecificModifier(Collection::class.java).write(0, displayIds.map { it.second.toString() }) outlineTeamPacket.getSpecificModifier(Collection::class.java).write(0, displayIds.map { it.second.toString() })
val ids = displayIds.map { it.first }.toMutableList() val ids = displayIds.map { it.first }.toMutableList()
ids.add(base.first)
val passengerPacket = PacketContainer(PacketType.Play.Server.MOUNT) val passengerPacket = PacketContainer(PacketType.Play.Server.MOUNT)
passengerPacket.integers.write(0, marker.first) passengerPacket.integers.write(0, marker.first)
@ -221,7 +137,7 @@ class PlaceHint {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineTeamPacket) ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineTeamPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket) ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket)
isValid = validate(initialLocation, sizeX, sizeY, sizeZ) isValid = validate(initialLocation)
updateColour() updateColour()
} }
@ -229,7 +145,6 @@ class PlaceHint {
val packet = PacketContainer(PacketType.Play.Server.ENTITY_DESTROY) val packet = PacketContainer(PacketType.Play.Server.ENTITY_DESTROY)
val ids = displayIds.map { it.first }.toMutableList() val ids = displayIds.map { it.first }.toMutableList()
ids.add(marker.first) ids.add(marker.first)
ids.add(base.first)
packet.intLists.write(0, ids) packet.intLists.write(0, ids)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet) ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
@ -245,11 +160,11 @@ class PlaceHint {
} }
if (currentLocation == null) { if (currentLocation == null) {
show(newLocation) addDisplays(newLocation)
return return
} }
newLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0)) newLocation.add(this.offset)
val basePacket = PacketContainer(PacketType.Play.Server.ENTITY_POSITION_SYNC) val basePacket = PacketContainer(PacketType.Play.Server.ENTITY_POSITION_SYNC)
basePacket.integers.write(0, marker.first) basePacket.integers.write(0, marker.first)
@ -266,7 +181,7 @@ class PlaceHint {
currentLocation = newLocation currentLocation = newLocation
// Update material // Update material
val valid = validate(newLocation, sizeX, sizeY, sizeZ) val valid = validate(newLocation)
if (valid != isValid) { if (valid != isValid) {
isValid = valid isValid = valid
@ -276,7 +191,7 @@ class PlaceHint {
// region helper functions // region helper functions
private fun spawnBlockDisplay( protected fun spawnBlockDisplay(
location: Location, location: Location,
block: BlockData, block: BlockData,
scale: Vector3f = Vector3f(1f, 1f, 1f), scale: Vector3f = Vector3f(1f, 1f, 1f),
@ -318,23 +233,7 @@ class PlaceHint {
return Pair<Int, UUID>(id, uuid) return Pair<Int, UUID>(id, uuid)
} }
private fun updateColour() { protected open fun updateColour() {
// base
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
dataPacket.integers.write(0, base.first)
val material = if (isValid) Material.LIME_STAINED_GLASS else Material.RED_STAINED_GLASS
val blockData = BukkitConverters
.getWrappedBlockDataConverter()
.getGeneric(WrappedBlockData.createData(material))
val metadata = listOf(
WrappedDataValue(23, Registry.getBlockDataSerializer(false), blockData)
)
dataPacket.dataValueCollectionModifier.write(0, metadata)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
// outline // outline
val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM) val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
outlineTeamPacket.strings.write(0, "placeValid") outlineTeamPacket.strings.write(0, "placeValid")

View File

@ -5,13 +5,13 @@ import org.bukkit.Location
class PlaceHintValidators { class PlaceHintValidators {
companion object { companion object {
fun alwaysTrue(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float): Boolean { fun alwaysTrue(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, offsetY: Int): Boolean {
return true return true
} }
fun allAir(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float): Boolean { fun allAir(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, offsetY: Int): Boolean {
for (x in 0..<sizeX.toInt()) for (x in 0..<sizeX.toInt())
for (y in 0..<sizeY.toInt()) for (y in -offsetY..<sizeY.toInt())
for (z in 0..<sizeZ.toInt()) for (z in 0..<sizeZ.toInt())
if (!location.world.getBlockAt( if (!location.world.getBlockAt(
location.blockX + x, location.blockX + x,
@ -22,9 +22,9 @@ class PlaceHintValidators {
return true return true
} }
fun allReplaceable(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float): Boolean { fun allReplaceable(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, offsetY: Int): Boolean {
for (x in 0..<sizeX.toInt()) for (x in 0..<sizeX.toInt())
for (y in 0..<sizeY.toInt()) for (y in -offsetY..<sizeY.toInt())
for (z in 0..<sizeZ.toInt()) for (z in 0..<sizeZ.toInt())
if (!location.world.getBlockAt( if (!location.world.getBlockAt(
location.blockX + x, location.blockX + x,

View File

@ -1,7 +1,7 @@
package com.pobnellion.aoe.ui package com.pobnellion.aoe.ui
import com.pobnellion.aoe.building.Blacksmith import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.building.House import com.pobnellion.aoe.building.BuildingType
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
@ -14,22 +14,26 @@ import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent import org.bukkit.event.inventory.InventoryDragEvent
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import java.util.*
class PlaceItem : Listener { class PlaceItem : Listener {
private val buildingInventory = Bukkit.createInventory(null, 9, Component.text("Buildings")) private val buildingInventory = Bukkit.createInventory(null, 9, Component.text("Buildings"))
init { init {
buildingInventory.addItem(createGuiItem(Material.BELL, "Town center", "middle of the town :D"))
buildingInventory.addItem(createGuiItem(Material.OAK_LOG, "House", "Just a house bro")) buildingInventory.addItem(createGuiItem(Material.OAK_LOG, "House", "Just a house bro"))
buildingInventory.addItem(createGuiItem(Material.ANVIL, "Blacksmith", "smith .")) buildingInventory.addItem(createGuiItem(Material.EMERALD, "Villager", "willager"))
} }
@EventHandler @EventHandler
fun onRightClickItem(event: PlayerInteractEvent) { fun onRightClickItem(event: PlayerInteractEvent) {
if (event.material != Material.BRICKS if (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK)
|| (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK) return
|| PlaceHint.contains(event.player))
if (PlaceHint.contains(event.player))
return
if (event.material != Material.BRICKS)
return return
event.isCancelled = true event.isCancelled = true
@ -38,6 +42,9 @@ class PlaceItem : Listener {
@EventHandler @EventHandler
fun onInventoryClick(event: InventoryClickEvent) { fun onInventoryClick(event: InventoryClickEvent) {
val player = event.whoClicked as Player
val team = Aoe.getTeam(player) ?: return
if (event.inventory != buildingInventory) if (event.inventory != buildingInventory)
return return
@ -45,8 +52,9 @@ class PlaceItem : Listener {
var validClick = true var validClick = true
when (event.currentItem?.type) { when (event.currentItem?.type) {
Material.OAK_LOG -> House().showPlaceHint(event.whoClicked as Player) Material.OAK_LOG -> team.showBuildingPlaceHint(BuildingType.HOUSE, player)
Material.ANVIL -> Blacksmith().showPlaceHint(event.whoClicked as Player) Material.BELL -> team.showBuildingPlaceHint(BuildingType.TOWN_CENTER, player)
Material.EMERALD -> {}
else -> validClick = false else -> validClick = false
} }