Compare commits
10 Commits
aac3d2d4cb
...
9ed4a296f6
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ed4a296f6 | |||
| 685a1af396 | |||
| 57626e4f95 | |||
| b2d9c1b36a | |||
| 3875ca5d0a | |||
| f8825d535f | |||
| 35c7124451 | |||
| c082691845 | |||
| 3e8731eb1c | |||
| c061cff342 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -115,5 +115,9 @@ gradle-app.setting
|
|||||||
run/
|
run/
|
||||||
runs/
|
runs/
|
||||||
|
|
||||||
|
# Ignore gradle.properties cos its got sensitive info
|
||||||
|
gradle.properties
|
||||||
|
|
||||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
!gradle-wrapper.jar
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
|
import de.nilsdruyen.gradle.ftp.UploadExtension
|
||||||
|
|
||||||
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.gradleup.shadow") version "8.3.5"
|
||||||
// id("de.nilsdruyen.gradle-ftp-upload-plugin") version "0.5.0"
|
id("io.papermc.paperweight.userdev") version "2.0.0-beta.11"
|
||||||
|
id("de.nilsdruyen.gradle-ftp-upload-plugin") version "0.5.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.pobnellion"
|
group = "com.pobnellion"
|
||||||
@ -18,13 +21,19 @@ repositories {
|
|||||||
maven("https://repo.dmulloy2.net/repository/public/") {
|
maven("https://repo.dmulloy2.net/repository/public/") {
|
||||||
name = "dmulloy2-repo"
|
name = "dmulloy2-repo"
|
||||||
}
|
}
|
||||||
|
maven("https://maven.enginehub.org/repo/") {
|
||||||
|
name = "enginehub"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(files("libs/ProtocolLib.jar"))
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
// implementation("de.nilsdruyen.gradle-ftp-upload-plugin:de.nilsdruyen.gradle-ftp-upload-plugin.gradle.plugin:0.5.0")
|
implementation(platform("com.intellectualsites.bom:bom-newest:1.51")) // Ref: https://github.com/IntellectualSites/bom
|
||||||
|
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetJavaVersion = 21
|
val targetJavaVersion = 21
|
||||||
@ -57,3 +66,16 @@ tasks.shadowJar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure<UploadExtension> {
|
||||||
|
host = properties.getOrDefault("ftp.host", "").toString()
|
||||||
|
port = properties.getOrDefault("ftp.port", 22).toString().toInt()
|
||||||
|
username = properties.getOrDefault("ftp.username", "").toString()
|
||||||
|
password = properties.getOrDefault("ftp.password", "").toString()
|
||||||
|
sourceDir = "${layout.buildDirectory.get()}/libs"
|
||||||
|
targetDir = "/plugins/"
|
||||||
|
clearDirectoryBeforeUpload = false
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.uploadFilesToFtp {
|
||||||
|
dependsOn("shadowJar")
|
||||||
|
}
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||||
|
|||||||
@ -1,20 +1,65 @@
|
|||||||
package com.pobnellion.aoe
|
package com.pobnellion.aoe
|
||||||
|
|
||||||
import com.pobnellion.aoe.command.TestCommand
|
import com.pobnellion.aoe.command.TestCommand
|
||||||
|
import com.pobnellion.aoe.command.TestTabCompleter
|
||||||
|
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.PlaceHint
|
||||||
import com.pobnellion.aoe.ui.PlaceItem
|
import com.pobnellion.aoe.ui.UiManager
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.GameMode
|
||||||
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
class Aoe : JavaPlugin() {
|
class Aoe : JavaPlugin() {
|
||||||
|
companion object {
|
||||||
|
private var gameInProgress: Boolean = false
|
||||||
|
private var instance: Aoe? = null
|
||||||
|
val civilisations: MutableMap<CivilisationType, Civilisation> = mutableMapOf()
|
||||||
|
val map: AoeMap = AoeMap()
|
||||||
|
val players: MutableMap<Player, CivilisationType> = mutableMapOf()
|
||||||
|
val uiManager: UiManager = UiManager()
|
||||||
|
|
||||||
|
fun isPlaying(player: Player) = players.containsKey(player)
|
||||||
|
|
||||||
|
fun getTeam(player: Player): Civilisation? {
|
||||||
|
return civilisations.values.singleOrNull { team -> team.players.contains(player) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSchematicFile(schematicName: String): File =
|
||||||
|
Paths.get(instance!!.dataFolder.path, "schematics", "$schematicName.schem").toFile()
|
||||||
|
|
||||||
|
fun startGame() {
|
||||||
|
if (gameInProgress)
|
||||||
|
return
|
||||||
|
|
||||||
|
for (player in Bukkit.getOnlinePlayers()) {
|
||||||
|
if (isPlaying(player))
|
||||||
|
player.gameMode = GameMode.CREATIVE
|
||||||
|
else
|
||||||
|
player.gameMode = GameMode.SPECTATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
civilisations.values.forEach { civ -> civ.initSetup() }
|
||||||
|
UiManager.setup()
|
||||||
|
|
||||||
|
gameInProgress = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
|
instance = this
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
Bukkit.getPluginManager().registerEvents(PlaceHint(), this)
|
Bukkit.getPluginManager().registerEvents(UiManager(), this)
|
||||||
Bukkit.getPluginManager().registerEvents(PlaceItem(), this)
|
Bukkit.getPluginManager().registerEvents(PlaceHint, this)
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
this.getCommand("debug")!!.setExecutor(TestCommand())
|
this.getCommand("aoe")!!.setExecutor(TestCommand())
|
||||||
|
this.getCommand("aoe")!!.tabCompleter = TestTabCompleter()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
|||||||
8
src/main/kotlin/com/pobnellion/aoe/Constants.kt
Normal file
8
src/main/kotlin/com/pobnellion/aoe/Constants.kt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
8
src/main/kotlin/com/pobnellion/aoe/Extensions.kt
Normal file
8
src/main/kotlin/com/pobnellion/aoe/Extensions.kt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package com.pobnellion.aoe
|
||||||
|
|
||||||
|
import com.pobnellion.aoe.civilisation.Civilisation
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
fun Player.getCivilisation(): Civilisation? {
|
||||||
|
return Aoe.civilisations[Aoe.players[this]]
|
||||||
|
}
|
||||||
@ -1,23 +1,8 @@
|
|||||||
package com.pobnellion.aoe.building
|
package com.pobnellion.aoe.building
|
||||||
|
|
||||||
import com.pobnellion.aoe.ui.PlaceHint
|
|
||||||
import com.pobnellion.aoe.ui.PlaceHintValidators
|
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.Material
|
|
||||||
import org.bukkit.entity.Player
|
|
||||||
|
|
||||||
class Blacksmith: Building {
|
class Blacksmith(location: Location, variant: Int): Building(location, variant) {
|
||||||
override fun showPlaceHint(player: Player) {
|
override var populationCapacity: Int = 0
|
||||||
PlaceHint.add(player, 4.0f, 3.0f, 4.0f, ::place, PlaceHintValidators::allReplaceable)
|
override fun getSchematicName(variant: Int): String = ""
|
||||||
}
|
|
||||||
|
|
||||||
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) {
|
|
||||||
for (x in 0..<sizeX.toInt())
|
|
||||||
for (y in 0..<sizeY.toInt())
|
|
||||||
for (z in 0..<sizeZ.toInt())
|
|
||||||
location.world.getBlockAt(
|
|
||||||
location.blockX + x,
|
|
||||||
location.blockY + y,
|
|
||||||
location.blockZ + z ).type = Material.SMOOTH_STONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,8 +1,70 @@
|
|||||||
package com.pobnellion.aoe.building
|
package com.pobnellion.aoe.building
|
||||||
|
|
||||||
|
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.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.Location
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.util.Vector
|
||||||
|
|
||||||
interface Building {
|
abstract class Building(val location: Location, val variant: Int): EntityWorkTarget {
|
||||||
fun showPlaceHint(player: Player)
|
override var currentProgressPercent: Float = 0f
|
||||||
|
override fun isComplete(): Boolean = currentProgressPercent >= 1.0f
|
||||||
|
override fun addProgress(amount: Float) {}
|
||||||
|
override fun setProgress(amount: Float) {}
|
||||||
|
override fun removeProgress(amount: Float) {}
|
||||||
|
override fun onComplete() {}
|
||||||
|
|
||||||
|
var sizeX: Int
|
||||||
|
var sizeZ: Int
|
||||||
|
fun center(): Location = location.clone().add(sizeX / 2.0, 0.0, sizeZ / 2.0)
|
||||||
|
abstract var populationCapacity: Int
|
||||||
|
abstract fun getSchematicName(variant: Int): String
|
||||||
|
|
||||||
|
init {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun placeFull() {
|
||||||
|
currentProgressPercent = 1f
|
||||||
|
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)
|
||||||
|
.createPaste(editSession)
|
||||||
|
.to(BlockVector3.at(location.x, location.y, location.z).subtract(offset))
|
||||||
|
.build()
|
||||||
|
Operations.complete(operation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BuildingInfo {
|
||||||
|
val buildingType: BuildingType
|
||||||
|
val schematicNames: List<String>
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
17
src/main/kotlin/com/pobnellion/aoe/building/BuildingType.kt
Normal file
17
src/main/kotlin/com/pobnellion/aoe/building/BuildingType.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.pobnellion.aoe.building
|
||||||
|
|
||||||
|
enum class BuildingType {
|
||||||
|
ARCHERY_RANGE,
|
||||||
|
BARRACKS,
|
||||||
|
DOCK,
|
||||||
|
FARM,
|
||||||
|
HOUSE,
|
||||||
|
LUMBER_CAMP,
|
||||||
|
MILL,
|
||||||
|
MINING_CAMP,
|
||||||
|
STABLE,
|
||||||
|
TOWN_CENTER,
|
||||||
|
UNIQUE,
|
||||||
|
WALL,
|
||||||
|
WATCH_TOWER,
|
||||||
|
}
|
||||||
@ -1,17 +1,12 @@
|
|||||||
package com.pobnellion.aoe.building
|
package com.pobnellion.aoe.building
|
||||||
|
|
||||||
import com.pobnellion.aoe.ui.PlaceHint
|
|
||||||
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 {
|
abstract class House(location: Location, variant: Int): Building(location, variant) {
|
||||||
override fun showPlaceHint(player: Player) {
|
override var populationCapacity: Int = 5
|
||||||
PlaceHint.add(player, 3.0f, 3.0f, 3.0f, ::place, PlaceHintValidators::allAir)
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/kotlin/com/pobnellion/aoe/building/TownCenter.kt
Normal file
10
src/main/kotlin/com/pobnellion/aoe/building/TownCenter.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.pobnellion.aoe.building
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
|
||||||
|
|
||||||
|
abstract class TownCenter(location: Location, variant: Int): Building(location, variant) {
|
||||||
|
override var populationCapacity: Int = 5
|
||||||
|
abstract val villagerSpawnLocation: Location
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.pobnellion.aoe.building.plains
|
||||||
|
|
||||||
|
import com.pobnellion.aoe.building.Building
|
||||||
|
import com.pobnellion.aoe.building.BuildingInfo
|
||||||
|
import com.pobnellion.aoe.building.BuildingType
|
||||||
|
import com.pobnellion.aoe.building.House
|
||||||
|
import com.pobnellion.aoe.ui.PlaceHintValidators
|
||||||
|
import org.bukkit.Location
|
||||||
|
|
||||||
|
class PlainsHouse(location: Location, variant: Int): House(location, variant) {
|
||||||
|
companion object Info: BuildingInfo {
|
||||||
|
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 = PlainsHouse(location, variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSchematicName(variant: Int) = schematicNames[variant]
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.pobnellion.aoe.building.plains
|
||||||
|
|
||||||
|
import com.pobnellion.aoe.building.Building
|
||||||
|
import com.pobnellion.aoe.building.BuildingInfo
|
||||||
|
import com.pobnellion.aoe.building.BuildingType
|
||||||
|
import com.pobnellion.aoe.building.TownCenter
|
||||||
|
import com.pobnellion.aoe.ui.PlaceHintValidators
|
||||||
|
import org.bukkit.Location
|
||||||
|
|
||||||
|
class PlainsTownCenter(location: Location, variant: Int): TownCenter(location, variant) {
|
||||||
|
companion object Info: BuildingInfo {
|
||||||
|
override val buildingType = BuildingType.TOWN_CENTER
|
||||||
|
override val schematicNames = listOf("plains_towncenter")
|
||||||
|
|
||||||
|
override fun validate(location: Location): Boolean {
|
||||||
|
return PlaceHintValidators.allReplaceable(location, 10f, 10f, 10f, -2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(location: Location, variant: Int): Building {
|
||||||
|
return PlainsTownCenter(location, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val villagerSpawnLocation: Location = location.clone().add(7.0,2.0, 7.0)
|
||||||
|
override fun getSchematicName(variant: Int) = schematicNames[variant]
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package com.pobnellion.aoe.civilisation
|
||||||
|
|
||||||
|
import com.pobnellion.aoe.Aoe
|
||||||
|
import com.pobnellion.aoe.Constants
|
||||||
|
import com.pobnellion.aoe.building.Building
|
||||||
|
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.BuildingPlaceHint
|
||||||
|
import com.pobnellion.aoe.ui.inventory.BuildingInventory
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
abstract class Civilisation(
|
||||||
|
private val setupLocation: Location
|
||||||
|
) {
|
||||||
|
private val buildings: MutableList<Building> = mutableListOf()
|
||||||
|
public val villagers: MutableList<AoeVillager> = mutableListOf()
|
||||||
|
|
||||||
|
val players get() = Aoe.players.filter { player -> player.value == this.type }.keys
|
||||||
|
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.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 {
|
||||||
|
val building = buildingInfo.create(location, variant)
|
||||||
|
buildings.add(building)
|
||||||
|
return building
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showBuildingPlaceHint(type: BuildingType, player: Player) {
|
||||||
|
val info = getBuildingInfo(type)
|
||||||
|
val variant = Random.nextInt(info.schematicNames.size)
|
||||||
|
BuildingPlaceHint.add(player, info, ::addBuilding, variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initSetup() {
|
||||||
|
// Create town center
|
||||||
|
val townCenterInfo = getBuildingInfo(BuildingType.TOWN_CENTER)
|
||||||
|
val variant = Random.nextInt(townCenterInfo.schematicNames.size)
|
||||||
|
val townCenter = addBuilding(setupLocation, townCenterInfo, variant) as TownCenter
|
||||||
|
townCenter.placeFull()
|
||||||
|
|
||||||
|
// Spawn initial villagers
|
||||||
|
for (i in 0..< Constants.STARTING_VILLAGER_COUNT) {
|
||||||
|
val villager = AoeVillager(townCenter.villagerSpawnLocation, type)
|
||||||
|
villager.leaveBuilding(townCenter)
|
||||||
|
villagers.add(villager)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tp players above town center
|
||||||
|
for (player in players) {
|
||||||
|
player.teleport(townCenter.location
|
||||||
|
.clone()
|
||||||
|
.add(townCenterInfo.getSize(variant).x / 2, 0.0, townCenterInfo.getSize(variant).z / 2)
|
||||||
|
.toHighestLocation()
|
||||||
|
.add(0.0, 10.0, 0.0))
|
||||||
|
|
||||||
|
player.isFlying = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun populationCap() = min(Constants.MAX_POP_CAP, buildings.sumOf { building -> building.populationCapacity })
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.pobnellion.aoe.civilisation
|
||||||
|
|
||||||
|
enum class CivilisationType {
|
||||||
|
PLAINS
|
||||||
|
}
|
||||||
37
src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt
Normal file
37
src/main/kotlin/com/pobnellion/aoe/civilisation/Plains.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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()
|
||||||
|
BuildingType.BARRACKS -> TODO()
|
||||||
|
BuildingType.DOCK -> TODO()
|
||||||
|
BuildingType.FARM -> TODO()
|
||||||
|
BuildingType.HOUSE -> PlainsHouse.Info
|
||||||
|
BuildingType.LUMBER_CAMP -> TODO()
|
||||||
|
BuildingType.MILL -> TODO()
|
||||||
|
BuildingType.MINING_CAMP -> TODO()
|
||||||
|
BuildingType.STABLE -> TODO()
|
||||||
|
BuildingType.TOWN_CENTER -> PlainsTownCenter.Info
|
||||||
|
BuildingType.UNIQUE -> TODO()
|
||||||
|
BuildingType.WALL -> TODO()
|
||||||
|
BuildingType.WATCH_TOWER -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,20 +1,70 @@
|
|||||||
package com.pobnellion.aoe.command
|
package com.pobnellion.aoe.command
|
||||||
|
|
||||||
import com.pobnellion.aoe.ui.PlaceHint
|
import com.pobnellion.aoe.Aoe
|
||||||
import org.bukkit.Material
|
import com.pobnellion.aoe.civilisation.CivilisationType
|
||||||
|
import com.pobnellion.aoe.civilisation.Plains
|
||||||
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.command.TabCompleter
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
val player: Player = sender
|
if (args.isEmpty()) {
|
||||||
|
sender.sendMessage("invalid command")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
when (args[0]) {
|
||||||
|
"team" -> team(sender, args)
|
||||||
|
"start" -> Aoe.startGame()
|
||||||
|
else -> sender.sendMessage("Invalid argument: ${args[0]}")
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun team(sender: CommandSender, args: Array<out String>) {
|
||||||
|
if (args.size == 1) {
|
||||||
|
sender.sendMessage("please provide a team")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when (args[1]) {
|
||||||
|
"plains" -> joinCiv(sender as Player, CivilisationType.PLAINS)
|
||||||
|
else -> sender.sendMessage("Invalid team: ${args[1]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun joinCiv(player: Player, civilisationType: CivilisationType) {
|
||||||
|
if (!Aoe.civilisations.containsKey(civilisationType))
|
||||||
|
when (civilisationType) {
|
||||||
|
CivilisationType.PLAINS -> Aoe.civilisations[CivilisationType.PLAINS] = Plains(Aoe.map.getStartingLocations()[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
Aoe.players[player] = civilisationType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestTabCompleter: TabCompleter {
|
||||||
|
override fun onTabComplete(sender: CommandSender, command: Command, label: String, args: Array<out String>): MutableList<String>? {
|
||||||
|
val options: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
|
if (args.size == 1) {
|
||||||
|
options.add("team")
|
||||||
|
options.add("start")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size == 2)
|
||||||
|
if (args[0] == "team")
|
||||||
|
options.add("plains")
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
}
|
}
|
||||||
30
src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt
Normal file
30
src/main/kotlin/com/pobnellion/aoe/entity/AoeVillager.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package com.pobnellion.aoe.entity
|
||||||
|
|
||||||
|
import com.pobnellion.aoe.building.Building
|
||||||
|
import com.pobnellion.aoe.civilisation.CivilisationType
|
||||||
|
import com.pobnellion.aoe.entity.goals.GoToBuildingGoal
|
||||||
|
import com.pobnellion.aoe.entity.goals.LeaveBuildingGoal
|
||||||
|
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, var civilisationType: CivilisationType)
|
||||||
|
: Villager(EntityType.VILLAGER, (location.world as CraftWorld).handle) {
|
||||||
|
init {
|
||||||
|
setPos(location.x, location.y, location.z)
|
||||||
|
level().addFreshEntity(this)
|
||||||
|
|
||||||
|
targetSelector.removeAllGoals { true }
|
||||||
|
}
|
||||||
|
|
||||||
|
var isActive: Boolean = false;
|
||||||
|
|
||||||
|
fun leaveBuilding(building: Building) {
|
||||||
|
targetSelector.addGoal(0, LeaveBuildingGoal(this, building, 1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun goToBuilding(building: Building) {
|
||||||
|
targetSelector.addGoal(0, GoToBuildingGoal(this, building, 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.pobnellion.aoe.entity.goals
|
||||||
|
|
||||||
|
interface EntityWorkTarget {
|
||||||
|
var currentProgressPercent: Float
|
||||||
|
fun isComplete(): Boolean
|
||||||
|
fun addProgress(amount: Float)
|
||||||
|
fun setProgress(amount: Float)
|
||||||
|
fun removeProgress(amount: Float)
|
||||||
|
fun onComplete()
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
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 net.minecraft.world.entity.ai.util.DefaultRandomPos
|
||||||
|
import net.minecraft.world.level.pathfinder.Path
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class LeaveBuildingGoal(
|
||||||
|
private val mob: PathfinderMob,
|
||||||
|
private val building: Building,
|
||||||
|
private var speedModifier: Double
|
||||||
|
): Goal() {
|
||||||
|
private var path: Path? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.setFlags(EnumSet.of(Flag.MOVE))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canUse(): Boolean {
|
||||||
|
val buildingCornerDistance = sqrt((building.sizeX.toFloat().pow(2) + building.sizeZ.toFloat().pow(2)))
|
||||||
|
val buildingCenter = building.center()
|
||||||
|
val fromPosition = Vec3(buildingCenter.x, buildingCenter.y, buildingCenter.z)
|
||||||
|
val posAway = DefaultRandomPos.getPosAway(mob, (buildingCornerDistance / 2).toInt(), 2, fromPosition)
|
||||||
|
|
||||||
|
if (posAway == null)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (fromPosition.distanceToSqr(posAway.x, posAway.y, posAway.z) < fromPosition.distanceToSqr(mob.position()))
|
||||||
|
return false
|
||||||
|
|
||||||
|
path = mob.navigation.createPath(posAway.x, posAway.y, posAway.z, 0)
|
||||||
|
return path != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
mob.navigation.moveTo(path, speedModifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
if (!canContinueToUse())
|
||||||
|
mob.targetSelector.removeGoal(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canContinueToUse(): Boolean {
|
||||||
|
return !mob.navigation.isDone
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/kotlin/com/pobnellion/aoe/map/AoeMap.kt
Normal file
10
src/main/kotlin/com/pobnellion/aoe/map/AoeMap.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.pobnellion.aoe.map
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Location
|
||||||
|
|
||||||
|
class AoeMap {
|
||||||
|
fun getStartingLocations(): List<Location> {
|
||||||
|
return listOf(Location(Bukkit.getWorld("world"), -165.0, 63.0, 32.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +0,0 @@
|
|||||||
package com.pobnellion.aoe.team
|
|
||||||
|
|
||||||
import com.pobnellion.aoe.building.Building
|
|
||||||
|
|
||||||
class Team {
|
|
||||||
public val Buildings: List<Building> = listOf()
|
|
||||||
}
|
|
||||||
77
src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt
Normal file
77
src/main/kotlin/com/pobnellion/aoe/ui/AreaPlaceHint.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package com.pobnellion.aoe.ui
|
||||||
|
|
||||||
|
import com.comphenix.protocol.wrappers.Pair
|
||||||
|
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 onTick: (Location, Float, Float, Float) -> Unit,
|
||||||
|
) : PlaceHint() {
|
||||||
|
companion object {
|
||||||
|
fun add(
|
||||||
|
player: Player,
|
||||||
|
sizeX: Float,
|
||||||
|
sizeY: Float,
|
||||||
|
sizeZ: Float,
|
||||||
|
onConfirm: (Location, Float, Float, Float) -> Unit,
|
||||||
|
onTick: (Location, Float, Float, Float) -> Unit,
|
||||||
|
) {
|
||||||
|
remove(player)
|
||||||
|
|
||||||
|
playerHints[player.uniqueId] = AreaPlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, onTick)
|
||||||
|
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 tick() {
|
||||||
|
currentLocation?.let { onTick(it, sizeX, sizeY, sizeZ) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun confirm(): Boolean {
|
||||||
|
currentLocation?.let { onConfirm(it, sizeX, sizeY, sizeZ) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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 updateGlowColour(colour: ChatFormatting) {
|
||||||
|
// super.updateGlowColour(colour)
|
||||||
|
//
|
||||||
|
// // 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)
|
||||||
|
// }
|
||||||
|
}
|
||||||
88
src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt
Normal file
88
src/main/kotlin/com/pobnellion/aoe/ui/BuildingPlaceHint.kt
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package com.pobnellion.aoe.ui
|
||||||
|
|
||||||
|
import com.comphenix.protocol.wrappers.EnumWrappers
|
||||||
|
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.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)
|
||||||
|
|
||||||
|
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
|
||||||
|
private var isValid = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val schematicName = buildingInfo.schematicNames[buildingVariant]
|
||||||
|
val file = Aoe.getSchematicFile(schematicName)
|
||||||
|
|
||||||
|
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() + 1,
|
||||||
|
-floor(clipboard.dimensions.z() / 2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
// Update material
|
||||||
|
val valid = currentLocation?.let { buildingInfo.validate(it) } ?: false
|
||||||
|
|
||||||
|
if (valid != isValid) {
|
||||||
|
isValid = valid
|
||||||
|
updateGlowColour(if (isValid) EnumWrappers.ChatFormatting.GREEN else EnumWrappers.ChatFormatting.RED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun confirm(): Boolean {
|
||||||
|
if (!isValid || currentLocation == null)
|
||||||
|
return false
|
||||||
|
|
||||||
|
onConfirm(currentLocation!!, buildingInfo, buildingVariant)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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() - schematic.minimumPoint.y()).toFloat()
|
||||||
|
val z = (blockPosition.z() - schematic.minimumPoint.z()).toFloat()
|
||||||
|
|
||||||
|
displayIds.add(spawnBlockDisplay(initialLocation, block, offset = Vector3f(x, y, z), options = 0b01000000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.pobnellion.aoe.ui
|
||||||
|
|
||||||
|
enum class EntitySelectionMode {
|
||||||
|
RADIUS,
|
||||||
|
AREA_HINT,
|
||||||
|
TWO_POINTS
|
||||||
|
}
|
||||||
@ -1,14 +1,15 @@
|
|||||||
package com.pobnellion.aoe.ui;
|
package com.pobnellion.aoe.ui
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType
|
import com.comphenix.protocol.PacketType
|
||||||
import com.comphenix.protocol.ProtocolLibrary
|
import com.comphenix.protocol.ProtocolLibrary
|
||||||
import com.comphenix.protocol.events.PacketContainer
|
import com.comphenix.protocol.events.PacketContainer
|
||||||
import com.comphenix.protocol.wrappers.*
|
import com.comphenix.protocol.wrappers.*
|
||||||
|
import com.comphenix.protocol.wrappers.EnumWrappers.ChatFormatting
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry
|
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry
|
||||||
import it.unimi.dsi.fastutil.ints.IntList
|
import com.destroystokyo.paper.MaterialSetTag
|
||||||
import org.bukkit.Bukkit
|
import com.pobnellion.aoe.Constants
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.Material
|
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
|
||||||
import org.bukkit.event.EventHandler
|
import org.bukkit.event.EventHandler
|
||||||
@ -16,35 +17,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.potion.PotionEffectType
|
import org.bukkit.util.Vector
|
||||||
import org.joml.Math
|
import org.joml.Math
|
||||||
import org.joml.Vector3f
|
import org.joml.Vector3f
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.floor
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class PlaceHint : Listener {
|
abstract class PlaceHint {
|
||||||
|
companion object : Listener {
|
||||||
companion object {
|
const val MAX_TARGET_DISTANCE = 64
|
||||||
private const val MAX_TARGET_DISTANCE = 64
|
@JvmStatic
|
||||||
private val playerHints = HashMap<UUID, HintMarker>();
|
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,
|
|
||||||
materialValid: Material = Material.LIME_STAINED_GLASS,
|
|
||||||
materialInvalid: Material = Material.RED_STAINED_GLASS,
|
|
||||||
) {
|
|
||||||
remove(player)
|
|
||||||
createOutlineTeam(player)
|
|
||||||
playerHints[player.uniqueId] = HintMarker(player, sizeX, sizeY, sizeZ, onConfirm, validate, materialValid, materialInvalid)
|
|
||||||
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))
|
||||||
@ -59,116 +41,82 @@ class PlaceHint : Listener {
|
|||||||
playerHints.clear()
|
playerHints.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createOutlineTeam(player: Player) {
|
fun contains(player: Player): Boolean {
|
||||||
val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
|
return playerHints.containsKey(player.uniqueId)
|
||||||
packet.strings.write(0, "placeValid")
|
}
|
||||||
packet.integers.write(0, 0)
|
|
||||||
packet.optionalTeamParameters.write(0, Optional.of(Utils.teamParams))
|
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
|
@EventHandler
|
||||||
|
fun onPlayerLook(event: PlayerMoveEvent) {
|
||||||
|
if (!playerHints.containsKey(event.player.uniqueId))
|
||||||
|
return
|
||||||
|
|
||||||
|
val target = event.player.getTargetBlockExact(MAX_TARGET_DISTANCE)
|
||||||
|
|
||||||
|
val block = if (target == null) null else event.player.getTargetBlock(MaterialSetTag.REPLACEABLE.values, MAX_TARGET_DISTANCE)
|
||||||
|
playerHints[event.player.uniqueId]!!.moveTo(block?.location)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onClick(event: PlayerInteractEvent) {
|
||||||
|
if (!playerHints.containsKey(event.player.uniqueId))
|
||||||
|
return
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
// left click - confirm
|
||||||
|
if (event.action == Action.LEFT_CLICK_BLOCK || event.action == Action.LEFT_CLICK_AIR) {
|
||||||
|
if (playerHints[event.player.uniqueId]!!.confirm())
|
||||||
|
remove(event.player)
|
||||||
|
}
|
||||||
|
|
||||||
|
// right click - cancel
|
||||||
|
if (event.action == Action.RIGHT_CLICK_BLOCK || event.action == Action.RIGHT_CLICK_AIR) {
|
||||||
|
remove(event.player)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
protected abstract val player: Player
|
||||||
fun onPlayerLook(event: PlayerMoveEvent) {
|
protected lateinit var offset: Vector
|
||||||
if (!playerHints.containsKey(event.player.uniqueId))
|
private var marker: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID())
|
||||||
return
|
protected val displayIds: MutableList<Pair<Int, UUID>> = mutableListOf()
|
||||||
|
|
||||||
val block = event.player.getTargetBlockExact(MAX_TARGET_DISTANCE);
|
|
||||||
playerHints[event.player.uniqueId]!!.moveTo(block?.location)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
fun onLeftClick(event: PlayerInteractEvent) {
|
|
||||||
if (!playerHints.containsKey(event.player.uniqueId) || (event.action != Action.LEFT_CLICK_BLOCK && event.action != Action.LEFT_CLICK_AIR))
|
|
||||||
return
|
|
||||||
|
|
||||||
if (!playerHints[event.player.uniqueId]!!.isValid)
|
|
||||||
return
|
|
||||||
|
|
||||||
val marker = playerHints[event.player.uniqueId]!!
|
|
||||||
marker.onConfirm(marker.currentLocation!!, marker.sizeX, marker.sizeY, marker.sizeZ)
|
|
||||||
remove(event.player)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HintMarker(
|
|
||||||
val player: Player,
|
|
||||||
val sizeX: Float,
|
|
||||||
val sizeY: Float,
|
|
||||||
val sizeZ: Float,
|
|
||||||
val onConfirm: (Location, Float, Float, Float) -> Unit,
|
|
||||||
val validate: (Location, Float, Float, Float) -> Boolean,
|
|
||||||
val materialValid: Material,
|
|
||||||
val materialInvalid: Material,
|
|
||||||
) {
|
|
||||||
private val markerUuid = UUID.randomUUID()
|
|
||||||
private val outlineUuid = UUID.randomUUID()
|
|
||||||
private val markerId = (Math.random() * Int.MAX_VALUE).toInt()
|
|
||||||
private val outlineId = markerId + 1
|
|
||||||
var currentLocation: Location? = null
|
var currentLocation: Location? = null
|
||||||
var isValid = false
|
|
||||||
|
protected abstract fun addDisplays(initialLocation: Location)
|
||||||
|
protected abstract fun confirm(): Boolean
|
||||||
|
protected abstract fun tick()
|
||||||
|
|
||||||
private fun show(initialLocation: Location) {
|
private fun show(initialLocation: Location) {
|
||||||
initialLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0))
|
initialLocation.add(this.offset)
|
||||||
|
currentLocation = initialLocation
|
||||||
|
|
||||||
spawnBlockDisplay(markerId, markerUuid, initialLocation, Material.GRAY_STAINED_GLASS, Vector3f(sizeX, 0.2f, sizeZ), 0b01000000)
|
val spawnMarkerPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
|
||||||
// spawnBlockDisplay(outlineId, outlineUuid, initialLocation, Material.STONE, Vector3f(sizeX, sizeY, sizeZ), 0b00100000)
|
spawnMarkerPacket.integers.write(0, marker.first)
|
||||||
|
spawnMarkerPacket.uuiDs.write(0, marker.second)
|
||||||
val spawnSlimePacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
|
spawnMarkerPacket.entityTypeModifier.write(0, EntityType.BLOCK_DISPLAY)
|
||||||
spawnSlimePacket.integers.write(0, outlineId)
|
spawnMarkerPacket.doubles
|
||||||
spawnSlimePacket.uuiDs.write(0, outlineUuid)
|
|
||||||
spawnSlimePacket.entityTypeModifier.write(0, EntityType.SLIME)
|
|
||||||
|
|
||||||
spawnSlimePacket.doubles
|
|
||||||
.write(0, initialLocation.x)
|
.write(0, initialLocation.x)
|
||||||
.write(1, initialLocation.y)
|
.write(1, initialLocation.y)
|
||||||
.write(2, initialLocation.z)
|
.write(2, initialLocation.z)
|
||||||
|
|
||||||
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
|
addDisplays(initialLocation)
|
||||||
dataPacket.integers.write(0, outlineId)
|
|
||||||
|
|
||||||
// TODO: 1 - display riding slime for nicer movement
|
val passengerPacket = PacketContainer(PacketType.Play.Server.MOUNT)
|
||||||
// TODO: 2 - slime riding display for nicer movement
|
passengerPacket.integers.write(0, marker.first)
|
||||||
// TODO: 3 - resource pack for outline with really low opacity
|
passengerPacket.integerArrays.write(0, displayIds.map { it.first }.toIntArray())
|
||||||
|
|
||||||
val watcher = WrappedDataWatcher()
|
UiManager.updateTeamEntities(player, Constants.PLACE_HINT_TEAM, displayIds.map { it.second.toString() })
|
||||||
watcher.setObject(0, Registry.get(Byte::class.javaObjectType), 0b01100000.toByte())
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnMarkerPacket)
|
||||||
// watcher.setObject(5, Registry.get(Boolean::class.javaObjectType), true)
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket)
|
||||||
watcher.setObject(16, Registry.get(Int::class.javaObjectType), min(sizeX, min(sizeY, sizeZ)).toInt())
|
|
||||||
|
|
||||||
dataPacket.dataValueCollectionModifier.write(0, watcher.watchableObjects
|
tick()
|
||||||
.map { WrappedDataValue(it.watcherObject.index, it.watcherObject.serializer, it.rawValue) })
|
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnSlimePacket)
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
|
|
||||||
|
|
||||||
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, listOf(outlineUuid.toString()))
|
|
||||||
|
|
||||||
// val outlineInvisiblePacket = PacketContainer(PacketType.Play.Server.ENTITY_EFFECT)
|
|
||||||
// outlineInvisiblePacket.integers
|
|
||||||
// .write(0, outlineId)
|
|
||||||
// .write(1, 1)
|
|
||||||
// .write(2, -1)
|
|
||||||
//
|
|
||||||
// outlineInvisiblePacket.effectTypes.write(0, PotionEffectType.INVISIBILITY)
|
|
||||||
// outlineInvisiblePacket.bytes.write(0, 0b0000.toByte())
|
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineTeamPacket)
|
|
||||||
// ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineInvisiblePacket)
|
|
||||||
|
|
||||||
isValid = validate(initialLocation, sizeX, sizeY, sizeZ)
|
|
||||||
updateColour()
|
|
||||||
|
|
||||||
currentLocation = initialLocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hide() {
|
private fun hide() {
|
||||||
val packet = PacketContainer(PacketType.Play.Server.ENTITY_DESTROY)
|
val packet = PacketContainer(PacketType.Play.Server.ENTITY_DESTROY)
|
||||||
packet.intLists.write(0, IntList.of(markerId, outlineId))
|
val ids = displayIds.map { it.first }.toMutableList()
|
||||||
|
ids.add(marker.first)
|
||||||
|
packet.intLists.write(0, ids)
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
|
||||||
|
|
||||||
@ -187,45 +135,37 @@ private class HintMarker(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0))
|
newLocation.add(this.offset)
|
||||||
|
|
||||||
val markerPacket = PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT)
|
val basePacket = PacketContainer(PacketType.Play.Server.ENTITY_POSITION_SYNC)
|
||||||
markerPacket.integers.write(0, markerId)
|
basePacket.integers.write(0, marker.first)
|
||||||
|
|
||||||
markerPacket.doubles
|
basePacket.structures.values[0].structures.values[0].doubles
|
||||||
.write(0, newLocation.x)
|
.write(0, newLocation.x)
|
||||||
.write(1, newLocation.y)
|
.write(1, newLocation.y)
|
||||||
.write(2, newLocation.z)
|
.write(2, newLocation.z)
|
||||||
|
|
||||||
markerPacket.booleans.write(0, true)
|
basePacket.booleans.write(0, true)
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, markerPacket)
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, basePacket)
|
||||||
|
|
||||||
val outlinePacket = PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT)
|
|
||||||
outlinePacket.integers.write(0, outlineId)
|
|
||||||
|
|
||||||
outlinePacket.doubles
|
|
||||||
.write(0, newLocation.x)
|
|
||||||
.write(1, newLocation.y)
|
|
||||||
.write(2, newLocation.z)
|
|
||||||
|
|
||||||
outlinePacket.booleans.write(0, true)
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlinePacket)
|
|
||||||
|
|
||||||
currentLocation = newLocation
|
currentLocation = newLocation
|
||||||
|
|
||||||
// Update material
|
this.tick()
|
||||||
val valid = validate(newLocation, sizeX, sizeY, sizeZ)
|
|
||||||
|
|
||||||
if (valid != isValid) {
|
|
||||||
isValid = valid
|
|
||||||
updateColour()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// region helper functions
|
// region helper functions
|
||||||
|
|
||||||
private fun spawnBlockDisplay(id: Int, uuid: UUID, location: Location, material: Material, scale: Vector3f, options: Byte) {
|
protected fun spawnBlockDisplay(
|
||||||
|
location: Location,
|
||||||
|
block: BlockData,
|
||||||
|
scale: Vector3f = Vector3f(1f, 1f, 1f),
|
||||||
|
offset: Vector3f = Vector3f(0f, 0f, 0f),
|
||||||
|
options: Byte = 0b00000000
|
||||||
|
): Pair<Int, UUID> {
|
||||||
|
val id = (Math.random() * Int.MAX_VALUE).toInt()
|
||||||
|
val uuid = UUID.randomUUID()
|
||||||
|
|
||||||
val spawnPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
|
val spawnPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
|
||||||
spawnPacket.integers.write(0, id)
|
spawnPacket.integers.write(0, id)
|
||||||
spawnPacket.uuiDs.write(0, uuid)
|
spawnPacket.uuiDs.write(0, uuid)
|
||||||
@ -239,13 +179,14 @@ private class HintMarker(
|
|||||||
dataPacket.integers.write(0, id)
|
dataPacket.integers.write(0, id)
|
||||||
|
|
||||||
// woated
|
// woated
|
||||||
val blockData = BukkitConverters
|
val blockData = BukkitConverters.getWrappedBlockDataConverter()
|
||||||
.getWrappedBlockDataConverter()
|
.getGeneric(WrappedBlockData.createData(block))
|
||||||
.getGeneric(WrappedBlockData.createData(material))
|
|
||||||
|
|
||||||
val watcher = WrappedDataWatcher()
|
val watcher = WrappedDataWatcher()
|
||||||
watcher.setObject(0, Registry.get(Byte::class.javaObjectType), options)
|
watcher.setObject(0, Registry.get(Byte::class.javaObjectType), options)
|
||||||
|
watcher.setObject(11, Registry.get(Vector3f::class.javaObjectType), offset)
|
||||||
watcher.setObject(12, Registry.get(Vector3f::class.javaObjectType), scale)
|
watcher.setObject(12, Registry.get(Vector3f::class.javaObjectType), scale)
|
||||||
|
watcher.setObject(16, Registry.get(Int::class.javaObjectType), 200)
|
||||||
watcher.setObject(23, Registry.getBlockDataSerializer(false), blockData)
|
watcher.setObject(23, Registry.getBlockDataSerializer(false), blockData)
|
||||||
|
|
||||||
dataPacket.dataValueCollectionModifier.write(0, watcher.watchableObjects
|
dataPacket.dataValueCollectionModifier.write(0, watcher.watchableObjects
|
||||||
@ -253,31 +194,17 @@ private class HintMarker(
|
|||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnPacket)
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnPacket)
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
|
||||||
|
|
||||||
|
return Pair<Int, UUID>(id, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateColour() {
|
protected open fun updateGlowColour(colour: ChatFormatting) {
|
||||||
// marker
|
// outline glow
|
||||||
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
|
|
||||||
dataPacket.integers.write(0,markerId);
|
|
||||||
|
|
||||||
val material = if (isValid) materialValid else materialInvalid
|
|
||||||
val blockData = BukkitConverters
|
|
||||||
.getWrappedBlockDataConverter()
|
|
||||||
.getGeneric(WrappedBlockData.createData(material))
|
|
||||||
|
|
||||||
val metadata = listOf(
|
|
||||||
WrappedDataValue(23, WrappedDataWatcher.Registry.getBlockDataSerializer(false), blockData)
|
|
||||||
)
|
|
||||||
|
|
||||||
dataPacket.dataValueCollectionModifier.write(0, metadata)
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
|
|
||||||
|
|
||||||
// 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, Constants.PLACE_HINT_TEAM)
|
||||||
outlineTeamPacket.integers.write(0, 2)
|
outlineTeamPacket.integers.write(0, 2)
|
||||||
val params = WrappedTeamParameters.newBuilder(Utils.teamParams)
|
val params = WrappedTeamParameters.newBuilder(UiManager.teamParamsTemplate)
|
||||||
.color(if (isValid) EnumWrappers.ChatFormatting.GREEN else EnumWrappers.ChatFormatting.RED)
|
.color(colour)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
outlineTeamPacket.optionalTeamParameters.write(0, Optional.of(params))
|
outlineTeamPacket.optionalTeamParameters.write(0, Optional.of(params))
|
||||||
@ -286,17 +213,3 @@ private class HintMarker(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Utils {
|
|
||||||
companion object {
|
|
||||||
val teamParams: WrappedTeamParameters = WrappedTeamParameters.newBuilder()
|
|
||||||
.displayName(WrappedChatComponent.fromText(""))
|
|
||||||
.collisionRule("never")
|
|
||||||
.nametagVisibility("never")
|
|
||||||
.options(0)
|
|
||||||
.color(EnumWrappers.ChatFormatting.RED)
|
|
||||||
.prefix(WrappedChatComponent.fromText(""))
|
|
||||||
.suffix(WrappedChatComponent.fromText(""))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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,
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
package com.pobnellion.aoe.ui
|
|
||||||
|
|
||||||
import com.pobnellion.aoe.building.Blacksmith
|
|
||||||
import com.pobnellion.aoe.building.House
|
|
||||||
import net.kyori.adventure.text.Component
|
|
||||||
import org.bukkit.Bukkit
|
|
||||||
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 PlaceItem : Listener {
|
|
||||||
private val buildingInventory = Bukkit.createInventory(null, 9, Component.text("Buildings"))
|
|
||||||
|
|
||||||
init {
|
|
||||||
buildingInventory.addItem(createGuiItem(Material.OAK_LOG, "House", "Just a house bro"))
|
|
||||||
buildingInventory.addItem(createGuiItem(Material.ANVIL, "Blacksmith", "smith ."))
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
fun onRightClickItem(event: PlayerInteractEvent) {
|
|
||||||
if (event.material != Material.BRICKS || (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK))
|
|
||||||
return
|
|
||||||
|
|
||||||
event.isCancelled = true
|
|
||||||
event.player.openInventory(buildingInventory)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
fun onInventoryClick(event: InventoryClickEvent) {
|
|
||||||
if (event.inventory != buildingInventory)
|
|
||||||
return
|
|
||||||
|
|
||||||
event.isCancelled = true
|
|
||||||
var validClick = true
|
|
||||||
|
|
||||||
when (event.currentItem?.type) {
|
|
||||||
Material.OAK_LOG -> House().showPlaceHint(event.whoClicked as Player)
|
|
||||||
Material.ANVIL -> Blacksmith().showPlaceHint(event.whoClicked as Player)
|
|
||||||
else -> validClick = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validClick)
|
|
||||||
event.whoClicked.closeInventory(InventoryCloseEvent.Reason.PLUGIN)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
fun onInventoryClick(event: InventoryDragEvent) {
|
|
||||||
if (event.inventory == buildingInventory)
|
|
||||||
event.isCancelled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createGuiItem(material: Material, name: String, lore: String): ItemStack {
|
|
||||||
val item = ItemStack(material, 1)
|
|
||||||
item.itemMeta.displayName(Component.text(name))
|
|
||||||
item.itemMeta.lore(mutableListOf(Component.text(lore)))
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
173
src/main/kotlin/com/pobnellion/aoe/ui/UiManager.kt
Normal file
173
src/main/kotlin/com/pobnellion/aoe/ui/UiManager.kt
Normal 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
|
||||||
|
}
|
||||||
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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]
|
||||||
|
}
|
||||||
@ -4,7 +4,8 @@ main: com.pobnellion.aoe.Aoe
|
|||||||
api-version: '1.21'
|
api-version: '1.21'
|
||||||
depend:
|
depend:
|
||||||
- ProtocolLib
|
- ProtocolLib
|
||||||
|
- WorldEdit
|
||||||
commands:
|
commands:
|
||||||
debug:
|
aoe:
|
||||||
description: Debug command
|
description: Debug command
|
||||||
usage: /debug
|
usage: /aoe
|
||||||
|
|||||||
Reference in New Issue
Block a user