Ui management reshuffling and start on schematic manager

This commit is contained in:
2025-01-14 01:28:36 +13:00
parent 685a1af396
commit 9ed4a296f6
16 changed files with 291 additions and 183 deletions

View File

@ -6,7 +6,7 @@ import com.pobnellion.aoe.civilisation.Civilisation
import com.pobnellion.aoe.civilisation.CivilisationType import com.pobnellion.aoe.civilisation.CivilisationType
import com.pobnellion.aoe.map.AoeMap import com.pobnellion.aoe.map.AoeMap
import com.pobnellion.aoe.ui.PlaceHint import com.pobnellion.aoe.ui.PlaceHint
import com.pobnellion.aoe.ui.InventoryManager import com.pobnellion.aoe.ui.UiManager
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.entity.Player import org.bukkit.entity.Player
@ -21,7 +21,7 @@ class Aoe : JavaPlugin() {
val civilisations: MutableMap<CivilisationType, Civilisation> = mutableMapOf() val civilisations: MutableMap<CivilisationType, Civilisation> = mutableMapOf()
val map: AoeMap = AoeMap() val map: AoeMap = AoeMap()
val players: MutableMap<Player, CivilisationType> = mutableMapOf() val players: MutableMap<Player, CivilisationType> = mutableMapOf()
val inventoryManager: InventoryManager = InventoryManager() val uiManager: UiManager = UiManager()
fun isPlaying(player: Player) = players.containsKey(player) fun isPlaying(player: Player) = players.containsKey(player)
@ -44,7 +44,7 @@ class Aoe : JavaPlugin() {
} }
civilisations.values.forEach { civ -> civ.initSetup() } civilisations.values.forEach { civ -> civ.initSetup() }
inventoryManager.initHotbars() UiManager.setup()
gameInProgress = true gameInProgress = true
} }
@ -54,7 +54,7 @@ class Aoe : JavaPlugin() {
instance = this instance = this
// Events // Events
Bukkit.getPluginManager().registerEvents(InventoryManager(), this) Bukkit.getPluginManager().registerEvents(UiManager(), this)
Bukkit.getPluginManager().registerEvents(PlaceHint, this) Bukkit.getPluginManager().registerEvents(PlaceHint, this)
// Commands // Commands

View File

@ -3,4 +3,6 @@ package com.pobnellion.aoe
object Constants { object Constants {
const val MAX_POP_CAP = 200 const val MAX_POP_CAP = 200
const val STARTING_VILLAGER_COUNT = 5 const val STARTING_VILLAGER_COUNT = 5
const val ENTITY_SELECTION_TEAM = "entitySelection"
const val PLACE_HINT_TEAM = "placeValid"
} }

View File

@ -1,19 +1,15 @@
package com.pobnellion.aoe.building package com.pobnellion.aoe.building
import com.pobnellion.aoe.Aoe import com.pobnellion.aoe.building.plains.PlainsTownCenter
import com.pobnellion.aoe.entity.goals.EntityWorkTarget import com.pobnellion.aoe.entity.goals.EntityWorkTarget
import com.sk89q.worldedit.WorldEdit import com.sk89q.worldedit.WorldEdit
import com.sk89q.worldedit.bukkit.BukkitAdapter 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.function.operation.Operation import com.sk89q.worldedit.function.operation.Operation
import com.sk89q.worldedit.function.operation.Operations import com.sk89q.worldedit.function.operation.Operations
import com.sk89q.worldedit.math.BlockVector3 import com.sk89q.worldedit.math.BlockVector3
import com.sk89q.worldedit.session.ClipboardHolder import com.sk89q.worldedit.session.ClipboardHolder
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.util.Vector import org.bukkit.util.Vector
import java.io.File
import java.io.FileInputStream
abstract class Building(val location: Location, val variant: Int): EntityWorkTarget { abstract class Building(val location: Location, val variant: Int): EntityWorkTarget {
override var currentProgressPercent: Float = 0f override var currentProgressPercent: Float = 0f
@ -26,16 +22,14 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar
var sizeX: Int var sizeX: Int
var sizeZ: Int var sizeZ: Int
fun center(): Location = location.clone().add(sizeX / 2.0, 0.0, sizeZ / 2.0) fun center(): Location = location.clone().add(sizeX / 2.0, 0.0, sizeZ / 2.0)
private val clipboard: Clipboard
abstract var populationCapacity: Int abstract var populationCapacity: Int
abstract fun getSchematicName(variant: Int): String abstract fun getSchematicName(variant: Int): String
init { init {
val file: File = Aoe.getSchematicFile(this.getSchematicName(variant)) val clipboard = SchematicManager.get(this.getSchematicName(variant))
val format = ClipboardFormats.findByFile(file)
format!!.getReader(FileInputStream(file)).use { reader -> if (clipboard == null)
clipboard = reader.read() throw NullPointerException("Schematic ${this.getSchematicName(variant)} not found")
}
sizeX = clipboard.maximumPoint.x() - clipboard.minimumPoint.x() sizeX = clipboard.maximumPoint.x() - clipboard.minimumPoint.x()
sizeZ = clipboard.maximumPoint.z() - clipboard.minimumPoint.z() sizeZ = clipboard.maximumPoint.z() - clipboard.minimumPoint.z()
@ -43,7 +37,8 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar
fun placeFull() { fun placeFull() {
currentProgressPercent = 1f currentProgressPercent = 1f
val offset = clipboard.region.minimumPoint.subtract(clipboard.origin) val clipboard = SchematicManager.get(this.getSchematicName(variant))
val offset = clipboard!!.region.minimumPoint.subtract(clipboard.origin)
WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(location.world)).use { editSession -> WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(location.world)).use { editSession ->
val operation: Operation = ClipboardHolder(clipboard) val operation: Operation = ClipboardHolder(clipboard)
@ -58,7 +53,18 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar
interface BuildingInfo { interface BuildingInfo {
val buildingType: BuildingType val buildingType: BuildingType
val schematicNames: List<String> val schematicNames: List<String>
fun getSize(variant: Int): Vector
fun validate(location: Location): Boolean fun validate(location: Location): Boolean
fun create(location: Location, variant: Int): Building fun create(location: Location, variant: Int): Building
fun getSize(variant: Int): Vector {
val clipboard = SchematicManager.get(schematicNames[variant])
if (clipboard == null)
throw NullPointerException("Schematic ${schematicNames[variant]} not found")
val sizeX = clipboard.maximumPoint.x() - clipboard.minimumPoint.x()
val sizeY = clipboard.maximumPoint.y() - clipboard.minimumPoint.y()
val sizeZ = clipboard.maximumPoint.z() - clipboard.minimumPoint.z()
return Vector(sizeX, sizeY, sizeZ)
}
} }

View File

@ -0,0 +1,35 @@
package com.pobnellion.aoe.building
import com.pobnellion.aoe.Aoe
import com.sk89q.worldedit.extent.clipboard.Clipboard
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import java.io.File
import java.io.FileInputStream
object SchematicManager {
private val schematics: MutableMap<String, Clipboard> = mutableMapOf()
fun add(schematicName: String) {
if (!schematics.containsKey(schematicName))
loadSchematic(schematicName)
}
fun add(schematicNames: List<String>) {
for (schematicName in schematicNames)
if (!schematics.containsKey(schematicName))
loadSchematic(schematicName)
}
fun get(schematicName: String): Clipboard? = schematics[schematicName]
private fun loadSchematic(schematicName: String) {
// TODO: do this in the background async style
val clipboard: Clipboard
val file: File = Aoe.getSchematicFile(schematicName)
val format = ClipboardFormats.findByFile(file)
format!!.getReader(FileInputStream(file)).use { reader ->
clipboard = reader.read()
}
schematics[schematicName] = clipboard
}
}

View File

@ -7,11 +7,10 @@ import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.building.TownCenter import com.pobnellion.aoe.building.TownCenter
import com.pobnellion.aoe.entity.AoeVillager import com.pobnellion.aoe.entity.AoeVillager
import com.pobnellion.aoe.ui.AoeInventory
import com.pobnellion.aoe.ui.BuildingPlaceHint import com.pobnellion.aoe.ui.BuildingPlaceHint
import net.minecraft.world.phys.AABB import com.pobnellion.aoe.ui.inventory.BuildingInventory
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld import org.bukkit.Material
import org.bukkit.entity.Player import org.bukkit.entity.Player
import kotlin.math.min import kotlin.math.min
import kotlin.random.Random import kotlin.random.Random
@ -20,17 +19,20 @@ abstract class Civilisation(
private val setupLocation: Location private val setupLocation: Location
) { ) {
private val buildings: MutableList<Building> = mutableListOf() private val buildings: MutableList<Building> = mutableListOf()
private val villagers: MutableList<AoeVillager> = mutableListOf() public val villagers: MutableList<AoeVillager> = mutableListOf()
val players get() = Aoe.players.filter { player -> player.value == this.type }.keys val players get() = Aoe.players.filter { player -> player.value == this.type }.keys
val buildingInventory = AoeInventory("Buildings", BuildingType.entries.size) val buildingInventory = BuildingInventory()
protected abstract fun addSchematicsToManager()
protected abstract fun getBuildingInfo(type: BuildingType): BuildingInfo protected abstract fun getBuildingInfo(type: BuildingType): BuildingInfo
protected abstract val name: String protected abstract val name: String
protected abstract val type: CivilisationType protected abstract val type: CivilisationType
init { init {
buildingInventory.addItem() buildingInventory.add(BuildingType.TOWN_CENTER, Material.BELL, "Town center")
buildingInventory.add(BuildingType.HOUSE, Material.OAK_DOOR, "House")
addSchematicsToManager()
} }
fun addBuilding(location: Location, buildingInfo: BuildingInfo, variant: Int): Building { fun addBuilding(location: Location, buildingInfo: BuildingInfo, variant: Int): Building {
@ -72,12 +74,4 @@ abstract class Civilisation(
} }
fun populationCap() = min(Constants.MAX_POP_CAP, buildings.sumOf { building -> building.populationCapacity }) fun populationCap() = min(Constants.MAX_POP_CAP, buildings.sumOf { building -> building.populationCapacity })
fun selectVillagers(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) {
(location.world as CraftWorld).handle.getEntitiesOfClass(
AoeVillager::class.java,
AABB(location.x, location.y, location.z, location.x + sizeX, location.y + sizeY, location.z + sizeZ)
)
{ villager -> villager.civilisationType == type }
}
} }

View File

@ -2,6 +2,7 @@ package com.pobnellion.aoe.civilisation
import com.pobnellion.aoe.building.BuildingInfo import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.building.SchematicManager
import com.pobnellion.aoe.building.plains.PlainsHouse import com.pobnellion.aoe.building.plains.PlainsHouse
import com.pobnellion.aoe.building.plains.PlainsTownCenter import com.pobnellion.aoe.building.plains.PlainsTownCenter
import org.bukkit.Location import org.bukkit.Location
@ -10,6 +11,11 @@ class Plains(setupLocation: Location) : Civilisation(setupLocation) {
override val name: String = "Plains" override val name: String = "Plains"
override val type: CivilisationType = CivilisationType.PLAINS override val type: CivilisationType = CivilisationType.PLAINS
override fun addSchematicsToManager() {
SchematicManager.add(PlainsTownCenter.Info.schematicNames)
SchematicManager.add(PlainsHouse.Info.schematicNames)
}
override fun getBuildingInfo(type: BuildingType): BuildingInfo { override fun getBuildingInfo(type: BuildingType): BuildingInfo {
return when (type) { return when (type) {
BuildingType.ARCHERY_RANGE -> TODO() BuildingType.ARCHERY_RANGE -> TODO()

View File

@ -18,6 +18,8 @@ class AoeVillager(location: Location, var civilisationType: CivilisationType)
targetSelector.removeAllGoals { true } targetSelector.removeAllGoals { true }
} }
var isActive: Boolean = false;
fun leaveBuilding(building: Building) { fun leaveBuilding(building: Building) {
targetSelector.addGoal(0, LeaveBuildingGoal(this, building, 1.0)) targetSelector.addGoal(0, LeaveBuildingGoal(this, building, 1.0))
} }

View File

@ -1,13 +0,0 @@
package com.pobnellion.aoe.ui
import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.inventory.Inventory
class AoeInventory(name: String, size: Int) {
val inventory: Inventory = Bukkit.createInventory(null, size + size % 9, Component.text(name))
fun addItem() {
}
}

View File

@ -27,7 +27,6 @@ class AreaPlaceHint(
onTick: (Location, Float, Float, Float) -> Unit, onTick: (Location, Float, Float, Float) -> Unit,
) { ) {
remove(player) remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = AreaPlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, onTick) playerHints[player.uniqueId] = AreaPlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, onTick)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE) val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)

View File

@ -28,7 +28,6 @@ class BuildingPlaceHint(
buildingVariant: Int = 0 buildingVariant: Int = 0
) { ) {
remove(player) remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = BuildingPlaceHint(player, buildingInfo, onConfirm, buildingVariant) playerHints[player.uniqueId] = BuildingPlaceHint(player, buildingInfo, onConfirm, buildingVariant)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE) val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE)

View File

@ -0,0 +1,7 @@
package com.pobnellion.aoe.ui
enum class EntitySelectionMode {
RADIUS,
AREA_HINT,
TWO_POINTS
}

View File

@ -1,106 +0,0 @@
package com.pobnellion.aoe.ui
import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.getCivilisation
import net.kyori.adventure.text.Component
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.ItemStack
class InventoryManager : Listener {
private val buildingInventory =
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.EMERALD, "Villager", "willager"))
}
fun initHotbars() {
for (player in Aoe.players.keys) {
player.inventory.clear()
player.inventory.setItem(0, createGuiItem(Material.BRICKS, "Buildings"))
// TODO: double click select selects all of looking at type
player.inventory.setItem(6, createGuiItem(Material.WOODEN_HOE, "Select inactive villagers"))
player.inventory.setItem(7, createGuiItem(Material.IRON_HOE, "Select villagers"))
player.inventory.setItem(8, createGuiItem(Material.IRON_SWORD, "Select army"))
}
}
fun showBuildingInventory(player: Player) {
if (Aoe.isPlaying(player))
player.openInventory(player.getCivilisation()!!.buildingInventory.inventory)
}
// region events
@EventHandler
fun onRightClickItem(event: PlayerInteractEvent) {
if (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK)
return
if (PlaceHint.contains(event.player))
return
if (event.material != Material.BRICKS)
return
if (Aoe.getTeam(event.player) == null)
return
event.isCancelled = true
showBuildingInventory(event.player)
}
@EventHandler
fun onInventoryClick(event: InventoryClickEvent) {
if (Aoe.players.containsKey(event.whoClicked as Player))
event.isCancelled = true
val player = event.whoClicked as Player
val team = Aoe.getTeam(player) ?: return
if (event.inventory != buildingInventory)
return
var validClick = true
when (event.currentItem?.type) {
Material.OAK_LOG -> team.showBuildingPlaceHint(BuildingType.HOUSE, player)
Material.BELL -> team.showBuildingPlaceHint(BuildingType.TOWN_CENTER, player)
Material.EMERALD -> {}
else -> validClick = false
}
if (validClick)
event.whoClicked.closeInventory(InventoryCloseEvent.Reason.PLUGIN)
}
@EventHandler
fun onInventoryClick(event: InventoryDragEvent) {
if (Aoe.players.containsKey(event.whoClicked as Player))
event.isCancelled = true
}
// endregion
private fun createGuiItem(material: Material, name: String, lore: String? = null): ItemStack {
val item = ItemStack(material, 1)
item.itemMeta.displayName(Component.text(name))
if (lore != null)
item.itemMeta.lore(mutableListOf(Component.text(lore)))
return item
}
}

View File

@ -7,6 +7,7 @@ import com.comphenix.protocol.wrappers.*
import com.comphenix.protocol.wrappers.EnumWrappers.ChatFormatting import com.comphenix.protocol.wrappers.EnumWrappers.ChatFormatting
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.Constants
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.block.data.BlockData import org.bukkit.block.data.BlockData
import org.bukkit.entity.EntityType import org.bukkit.entity.EntityType
@ -44,16 +45,6 @@ abstract class PlaceHint {
return playerHints.containsKey(player.uniqueId) return playerHints.containsKey(player.uniqueId)
} }
@JvmStatic
protected fun createOutlineTeam(player: Player) {
val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
packet.strings.write(0, "placeValid")
packet.integers.write(0, 0)
packet.optionalTeamParameters.write(0, Optional.of(teamParamsTemplate))
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
}
@EventHandler @EventHandler
fun onPlayerLook(event: PlayerMoveEvent) { fun onPlayerLook(event: PlayerMoveEvent) {
if (!playerHints.containsKey(event.player.uniqueId)) if (!playerHints.containsKey(event.player.uniqueId))
@ -83,16 +74,6 @@ abstract class PlaceHint {
remove(event.player) remove(event.player)
} }
} }
private val teamParamsTemplate: WrappedTeamParameters = WrappedTeamParameters.newBuilder()
.displayName(WrappedChatComponent.fromText(""))
.collisionRule("never")
.nametagVisibility("never")
.options(0)
.color(ChatFormatting.RED)
.prefix(WrappedChatComponent.fromText(""))
.suffix(WrappedChatComponent.fromText(""))
.build()
} }
protected abstract val player: Player protected abstract val player: Player
@ -120,19 +101,12 @@ abstract class PlaceHint {
addDisplays(initialLocation) addDisplays(initialLocation)
val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
outlineTeamPacket.strings.write(0, "placeValid")
outlineTeamPacket.integers.write(0, 3)
outlineTeamPacket.getSpecificModifier(Collection::class.java).write(0, displayIds.map { it.second.toString() })
val ids = displayIds.map { it.first }.toMutableList()
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)
passengerPacket.integerArrays.write(0, ids.toIntArray()) passengerPacket.integerArrays.write(0, displayIds.map { it.first }.toIntArray())
UiManager.updateTeamEntities(player, Constants.PLACE_HINT_TEAM, displayIds.map { it.second.toString() })
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnMarkerPacket) ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnMarkerPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineTeamPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket) ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket)
tick() tick()
@ -227,9 +201,9 @@ abstract class PlaceHint {
protected open fun updateGlowColour(colour: ChatFormatting) { protected open fun updateGlowColour(colour: ChatFormatting) {
// outline glow // outline glow
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, Constants.PLACE_HINT_TEAM)
outlineTeamPacket.integers.write(0, 2) outlineTeamPacket.integers.write(0, 2)
val params = WrappedTeamParameters.newBuilder(teamParamsTemplate) val params = WrappedTeamParameters.newBuilder(UiManager.teamParamsTemplate)
.color(colour) .color(colour)
.build() .build()

View File

@ -0,0 +1,173 @@
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.EnumWrappers.ChatFormatting
import com.comphenix.protocol.wrappers.WrappedChatComponent
import com.comphenix.protocol.wrappers.WrappedTeamParameters
import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.Constants
import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.getCivilisation
import net.kyori.adventure.text.Component
import net.minecraft.world.entity.Entity
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.util.BoundingBox
import java.util.Optional
import kotlin.math.pow
class UiManager : Listener {
companion object {
fun createGuiItem(material: Material, name: String, lore: String? = null): ItemStack {
val item = ItemStack(material, 1)
item.itemMeta.displayName(Component.text(name))
if (lore != null)
item.itemMeta.lore(mutableListOf(Component.text(lore)))
return item
}
fun setup() {
for (player in Aoe.players.keys) {
// Init hotbars
player.inventory.clear()
player.inventory.setItem(0, createGuiItem(Material.BRICKS, "Buildings"))
// TODO: double click select selects all of looking at type
player.inventory.setItem(6, createGuiItem(Material.WOODEN_HOE, "Select inactive villagers"))
player.inventory.setItem(7, createGuiItem(Material.IRON_HOE, "Select villagers"))
player.inventory.setItem(8, createGuiItem(Material.IRON_SWORD, "Select army"))
// TODO: resend these on rejoin
// Send teams
createTeamForPlayer(player, Constants.ENTITY_SELECTION_TEAM)
createTeamForPlayer(player, Constants.PLACE_HINT_TEAM)
}
}
fun createTeamForPlayer(player: Player, teamName: String) {
val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
packet.strings.write(0, teamName)
packet.integers.write(0, 0)
packet.optionalTeamParameters.write(0, Optional.of(teamParamsTemplate))
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
}
fun updateTeamEntities(player: Player, teamName: String, entityIds: List<String>, remove: Boolean = false) {
val teamEntitiesPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
teamEntitiesPacket.strings.write(0, teamName)
teamEntitiesPacket.integers.write(0, if (remove) 4 else 3)
teamEntitiesPacket.getSpecificModifier(Collection::class.java).write(0, entityIds)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, teamEntitiesPacket)
}
val teamParamsTemplate: WrappedTeamParameters = WrappedTeamParameters.newBuilder()
.displayName(WrappedChatComponent.fromText(""))
.collisionRule("never")
.nametagVisibility("never")
.options(0)
.color(ChatFormatting.WHITE)
.prefix(WrappedChatComponent.fromText(""))
.suffix(WrappedChatComponent.fromText(""))
.build()
}
private val playerSelections: MutableMap<Player, List<Entity>> = mutableMapOf()
private val playerSelectionModes: MutableMap<Player, EntitySelectionMode> = mutableMapOf()
private fun selectEntities(player: Player, entities: List<Entity>) {
deselectEntities(player)
playerSelections[player] = entities
updateTeamEntities(player, Constants.ENTITY_SELECTION_TEAM, entities.map { it.uuid.toString() })
}
private fun deselectEntities(player: Player) {
if (!playerSelections.containsKey(player))
return
updateTeamEntities(player, Constants.ENTITY_SELECTION_TEAM, playerSelections[player]!!.map { it.uuid.toString() }, true)
playerSelections.remove(player)
}
// region events
@EventHandler
fun onRightClickItem(event: PlayerInteractEvent) {
if (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK)
return
if (Aoe.isPlaying(event.player))
return
if (PlaceHint.contains(event.player))
return
val civilisation = event.player.getCivilisation()
if (civilisation == null)
return
when (event.material) {
Material.BRICKS -> event.player.openInventory(civilisation.buildingInventory.inventory)
Material.WOODEN_HOE -> selectEntities(event.player, civilisation.villagers.filter { !it.isActive })
Material.IRON_HOE -> selectEntities(event.player, civilisation.villagers)
Material.IRON_SWORD -> {}
else -> return
}
event.isCancelled = true
}
@EventHandler
fun onInventoryClick(event: InventoryClickEvent) {
if (Aoe.players.containsKey(event.whoClicked as Player))
event.isCancelled = true
val player = event.whoClicked as Player
val team = Aoe.getTeam(player) ?: return
var validClick = true
when (event.currentItem?.type) {
Material.OAK_LOG -> team.showBuildingPlaceHint(BuildingType.HOUSE, player)
Material.BELL -> team.showBuildingPlaceHint(BuildingType.TOWN_CENTER, player)
Material.EMERALD -> {}
else -> validClick = false
}
if (validClick)
event.whoClicked.closeInventory(InventoryCloseEvent.Reason.PLUGIN)
}
@EventHandler
fun onInventoryClick(event: InventoryDragEvent) {
if (Aoe.players.containsKey(event.whoClicked as Player))
event.isCancelled = true
}
// endregion
// region helper functions
fun getEntitiesInArea(entities: List<Entity>, boundingBox: BoundingBox) =
entities.filter { entity -> boundingBox.contains(entity.x, entity.y, entity.z) }
fun getEntitiesAroundPlayer(entities: List<Entity>, player: Player, distance: Float) =
entities.filter { villager -> villager.distanceToSqr(player.x, player.y, player.z) <= distance.pow(2) }
// endregion
}

View File

@ -0,0 +1,15 @@
package com.pobnellion.aoe.ui.inventory
import com.pobnellion.aoe.ui.UiManager
import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.inventory.Inventory
abstract class AoeInventory(name: String, size: Int) {
val inventory: Inventory = Bukkit.createInventory(null, size + size % 9, Component.text(name))
fun addItem(material: Material, name: String, lore: String? = null) {
inventory.addItem(UiManager.Companion.createGuiItem(material, name, lore))
}
}

View File

@ -0,0 +1,15 @@
package com.pobnellion.aoe.ui.inventory
import com.pobnellion.aoe.building.BuildingType
import org.bukkit.Material
class BuildingInventory: AoeInventory("Buildings", BuildingType.entries.size) {
private val buildings: MutableMap<Material, BuildingType> = mutableMapOf()
fun add(buildingType: BuildingType, material: Material, name: String, lore: String? = null) {
addItem(material, name, lore)
buildings[material] = buildingType
}
fun getBuilding(material: Material) = buildings[material]
}