diff --git a/src/main/kotlin/com/pobnellion/aoe/Aoe.kt b/src/main/kotlin/com/pobnellion/aoe/Aoe.kt index e9f633b..a86742d 100644 --- a/src/main/kotlin/com/pobnellion/aoe/Aoe.kt +++ b/src/main/kotlin/com/pobnellion/aoe/Aoe.kt @@ -6,7 +6,7 @@ import com.pobnellion.aoe.civilisation.Civilisation import com.pobnellion.aoe.civilisation.CivilisationType import com.pobnellion.aoe.map.AoeMap 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.GameMode import org.bukkit.entity.Player @@ -21,7 +21,7 @@ class Aoe : JavaPlugin() { val civilisations: MutableMap = mutableMapOf() val map: AoeMap = AoeMap() val players: MutableMap = mutableMapOf() - val inventoryManager: InventoryManager = InventoryManager() + val uiManager: UiManager = UiManager() fun isPlaying(player: Player) = players.containsKey(player) @@ -44,7 +44,7 @@ class Aoe : JavaPlugin() { } civilisations.values.forEach { civ -> civ.initSetup() } - inventoryManager.initHotbars() + UiManager.setup() gameInProgress = true } @@ -54,7 +54,7 @@ class Aoe : JavaPlugin() { instance = this // Events - Bukkit.getPluginManager().registerEvents(InventoryManager(), this) + Bukkit.getPluginManager().registerEvents(UiManager(), this) Bukkit.getPluginManager().registerEvents(PlaceHint, this) // Commands diff --git a/src/main/kotlin/com/pobnellion/aoe/Constants.kt b/src/main/kotlin/com/pobnellion/aoe/Constants.kt index f5f101d..30a4448 100644 --- a/src/main/kotlin/com/pobnellion/aoe/Constants.kt +++ b/src/main/kotlin/com/pobnellion/aoe/Constants.kt @@ -3,4 +3,6 @@ package com.pobnellion.aoe object Constants { const val MAX_POP_CAP = 200 const val STARTING_VILLAGER_COUNT = 5 + const val ENTITY_SELECTION_TEAM = "entitySelection" + const val PLACE_HINT_TEAM = "placeValid" } \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/building/Building.kt b/src/main/kotlin/com/pobnellion/aoe/building/Building.kt index 9a6682d..4fda446 100644 --- a/src/main/kotlin/com/pobnellion/aoe/building/Building.kt +++ b/src/main/kotlin/com/pobnellion/aoe/building/Building.kt @@ -1,19 +1,15 @@ 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.sk89q.worldedit.WorldEdit 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.Operations import com.sk89q.worldedit.math.BlockVector3 import com.sk89q.worldedit.session.ClipboardHolder import org.bukkit.Location import org.bukkit.util.Vector -import java.io.File -import java.io.FileInputStream abstract class Building(val location: Location, val variant: Int): EntityWorkTarget { override var currentProgressPercent: Float = 0f @@ -26,16 +22,14 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar var sizeX: Int var sizeZ: Int fun center(): Location = location.clone().add(sizeX / 2.0, 0.0, sizeZ / 2.0) - private val clipboard: Clipboard abstract var populationCapacity: Int abstract fun getSchematicName(variant: Int): String init { - val file: File = Aoe.getSchematicFile(this.getSchematicName(variant)) - val format = ClipboardFormats.findByFile(file) - format!!.getReader(FileInputStream(file)).use { reader -> - clipboard = reader.read() - } + val clipboard = SchematicManager.get(this.getSchematicName(variant)) + + if (clipboard == null) + throw NullPointerException("Schematic ${this.getSchematicName(variant)} not found") sizeX = clipboard.maximumPoint.x() - clipboard.minimumPoint.x() sizeZ = clipboard.maximumPoint.z() - clipboard.minimumPoint.z() @@ -43,7 +37,8 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar fun placeFull() { 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 -> val operation: Operation = ClipboardHolder(clipboard) @@ -58,7 +53,18 @@ abstract class Building(val location: Location, val variant: Int): EntityWorkTar interface BuildingInfo { val buildingType: BuildingType val schematicNames: List - fun getSize(variant: Int): Vector fun validate(location: Location): Boolean 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) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/building/SchematicManager.kt b/src/main/kotlin/com/pobnellion/aoe/building/SchematicManager.kt new file mode 100644 index 0000000..742bf4d --- /dev/null +++ b/src/main/kotlin/com/pobnellion/aoe/building/SchematicManager.kt @@ -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 = mutableMapOf() + + fun add(schematicName: String) { + if (!schematics.containsKey(schematicName)) + loadSchematic(schematicName) + } + + fun add(schematicNames: List) { + 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/civilisation/Civilisation.kt b/src/main/kotlin/com/pobnellion/aoe/civilisation/Civilisation.kt index 64f2095..3dd7806 100644 --- a/src/main/kotlin/com/pobnellion/aoe/civilisation/Civilisation.kt +++ b/src/main/kotlin/com/pobnellion/aoe/civilisation/Civilisation.kt @@ -7,11 +7,10 @@ import com.pobnellion.aoe.building.BuildingInfo import com.pobnellion.aoe.building.BuildingType import com.pobnellion.aoe.building.TownCenter import com.pobnellion.aoe.entity.AoeVillager -import com.pobnellion.aoe.ui.AoeInventory 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.craftbukkit.CraftWorld +import org.bukkit.Material import org.bukkit.entity.Player import kotlin.math.min import kotlin.random.Random @@ -20,17 +19,20 @@ abstract class Civilisation( private val setupLocation: Location ) { private val buildings: MutableList = mutableListOf() - private val villagers: MutableList = mutableListOf() + public val villagers: MutableList = mutableListOf() 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 val name: String protected abstract val type: CivilisationType 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 { @@ -72,12 +74,4 @@ abstract class Civilisation( } 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 } - } } \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt b/src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt index a1a6656..f4c41fa 100644 --- a/src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt +++ b/src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt @@ -2,6 +2,7 @@ package com.pobnellion.aoe.civilisation import com.pobnellion.aoe.building.BuildingInfo 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.PlainsTownCenter import org.bukkit.Location @@ -10,6 +11,11 @@ class Plains(setupLocation: Location) : Civilisation(setupLocation) { override val name: String = "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 { return when (type) { BuildingType.ARCHERY_RANGE -> TODO() diff --git a/src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt b/src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt index e578137..7f2930b 100644 --- a/src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt +++ b/src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt @@ -18,6 +18,8 @@ class AoeVillager(location: Location, var civilisationType: CivilisationType) targetSelector.removeAllGoals { true } } + var isActive: Boolean = false; + fun leaveBuilding(building: Building) { targetSelector.addGoal(0, LeaveBuildingGoal(this, building, 1.0)) } diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/AoeInventory.kt b/src/main/kotlin/com/pobnellion/aoe/ui/AoeInventory.kt deleted file mode 100644 index 2e9c159..0000000 --- a/src/main/kotlin/com/pobnellion/aoe/ui/AoeInventory.kt +++ /dev/null @@ -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() { - - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt b/src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt index 01e6306..2add163 100644 --- a/src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt +++ b/src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt @@ -27,7 +27,6 @@ class AreaPlaceHint( onTick: (Location, Float, Float, Float) -> Unit, ) { remove(player) - createOutlineTeam(player) playerHints[player.uniqueId] = AreaPlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, onTick) val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE) diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt b/src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt index 4a3e8fb..b4ef028 100644 --- a/src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt +++ b/src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt @@ -28,7 +28,6 @@ class BuildingPlaceHint( buildingVariant: Int = 0 ) { remove(player) - createOutlineTeam(player) playerHints[player.uniqueId] = BuildingPlaceHint(player, buildingInfo, onConfirm, buildingVariant) val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE) diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/EntitySelectionMode.kt b/src/main/kotlin/com/pobnellion/aoe/ui/EntitySelectionMode.kt new file mode 100644 index 0000000..8266cce --- /dev/null +++ b/src/main/kotlin/com/pobnellion/aoe/ui/EntitySelectionMode.kt @@ -0,0 +1,7 @@ +package com.pobnellion.aoe.ui + +enum class EntitySelectionMode { + RADIUS, + AREA_HINT, + TWO_POINTS +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/InventoryManager.kt b/src/main/kotlin/com/pobnellion/aoe/ui/InventoryManager.kt deleted file mode 100644 index 986bf21..0000000 --- a/src/main/kotlin/com/pobnellion/aoe/ui/InventoryManager.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/PlaceHint.kt b/src/main/kotlin/com/pobnellion/aoe/ui/PlaceHint.kt index 199c8a8..24f7c40 100644 --- a/src/main/kotlin/com/pobnellion/aoe/ui/PlaceHint.kt +++ b/src/main/kotlin/com/pobnellion/aoe/ui/PlaceHint.kt @@ -7,6 +7,7 @@ import com.comphenix.protocol.wrappers.* import com.comphenix.protocol.wrappers.EnumWrappers.ChatFormatting import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry import com.destroystokyo.paper.MaterialSetTag +import com.pobnellion.aoe.Constants import org.bukkit.Location import org.bukkit.block.data.BlockData import org.bukkit.entity.EntityType @@ -44,16 +45,6 @@ abstract class PlaceHint { 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 fun onPlayerLook(event: PlayerMoveEvent) { if (!playerHints.containsKey(event.player.uniqueId)) @@ -83,16 +74,6 @@ abstract class PlaceHint { 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 @@ -120,19 +101,12 @@ abstract class PlaceHint { 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) 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, outlineTeamPacket) ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket) tick() @@ -227,9 +201,9 @@ abstract class PlaceHint { protected open fun updateGlowColour(colour: ChatFormatting) { // outline glow 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) - val params = WrappedTeamParameters.newBuilder(teamParamsTemplate) + val params = WrappedTeamParameters.newBuilder(UiManager.teamParamsTemplate) .color(colour) .build() diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/UiManager.kt b/src/main/kotlin/com/pobnellion/aoe/ui/UiManager.kt new file mode 100644 index 0000000..d80075c --- /dev/null +++ b/src/main/kotlin/com/pobnellion/aoe/ui/UiManager.kt @@ -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, 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> = mutableMapOf() + private val playerSelectionModes: MutableMap = mutableMapOf() + + private fun selectEntities(player: Player, entities: List) { + 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, boundingBox: BoundingBox) = + entities.filter { entity -> boundingBox.contains(entity.x, entity.y, entity.z) } + + fun getEntitiesAroundPlayer(entities: List, player: Player, distance: Float) = + entities.filter { villager -> villager.distanceToSqr(player.x, player.y, player.z) <= distance.pow(2) } + + // endregion +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/inventory/AoeInventory.kt b/src/main/kotlin/com/pobnellion/aoe/ui/inventory/AoeInventory.kt new file mode 100644 index 0000000..66a61ef --- /dev/null +++ b/src/main/kotlin/com/pobnellion/aoe/ui/inventory/AoeInventory.kt @@ -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)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/aoe/ui/inventory/BuildingInventory.kt b/src/main/kotlin/com/pobnellion/aoe/ui/inventory/BuildingInventory.kt new file mode 100644 index 0000000..7a85d2f --- /dev/null +++ b/src/main/kotlin/com/pobnellion/aoe/ui/inventory/BuildingInventory.kt @@ -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 = 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] +} \ No newline at end of file