restructure things a bit for use in actual gameplay

This commit is contained in:
ruby
2025-01-05 21:23:32 +13:00
parent b2d9c1b36a
commit 57626e4f95
17 changed files with 229 additions and 116 deletions

5
.gitignore vendored
View File

@ -115,8 +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
# Ignore gradle.properties cos its got sensitive info
gradle.properties

View File

@ -2,7 +2,9 @@ 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.command.TestTabCompleter
import com.pobnellion.aoe.team.Team 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.PlaceItem
import org.bukkit.Bukkit import org.bukkit.Bukkit
@ -13,15 +15,24 @@ import java.nio.file.Paths
class Aoe : JavaPlugin() { class Aoe : JavaPlugin() {
companion object { companion object {
private var gameInProgress: Boolean = false
private var instance: Aoe? = null private var instance: Aoe? = null
val teams: MutableList<Team> = mutableListOf() val civilisations: MutableMap<CivilisationType, Civilisation> = mutableMapOf()
val map: AoeMap = AoeMap()
fun getTeam(player: Player): Team? { fun getTeam(player: Player): Civilisation? {
return teams.singleOrNull() { team -> team.players.contains(player) } return civilisations.values.singleOrNull { team -> team.players.contains(player) }
} }
fun getSchematicFile(schematicName: String): File = fun getSchematicFile(schematicName: String): File =
Paths.get(instance!!.dataFolder.path, "schematics", "$schematicName.schem").toFile() Paths.get(instance!!.dataFolder.path, "schematics", "$schematicName.schem").toFile()
fun startGame() {
if (!gameInProgress)
civilisations.values.forEach { civ -> civ.initSetup() }
gameInProgress = true
}
} }
override fun onEnable() { override fun onEnable() {

View File

@ -0,0 +1,6 @@
package com.pobnellion.aoe
object Constants {
const val MAX_POP_CAP = 200
const val STARTING_VILLAGER_COUNT = 5
}

View File

@ -1,16 +1,8 @@
package com.pobnellion.aoe.building package com.pobnellion.aoe.building
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material
class Blacksmith(location: Location, variant: Int): Building(location, variant) { class Blacksmith(location: Location, variant: Int): Building(location, variant) {
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float, offsetY: Int) { override var populationCapacity: Int = 0
for (x in 0..<sizeX.toInt()) override fun getSchematicName(variant: Int): String = ""
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
}
} }

View File

@ -1,8 +1,50 @@
package com.pobnellion.aoe.building package com.pobnellion.aoe.building
import com.pobnellion.aoe.Aoe
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.Location
import java.io.File
import java.io.FileInputStream
abstract class Building(val location: Location, val variant: Int) { abstract class Building(val location: Location, val variant: Int): EntityWorkTarget {
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() {}
abstract var populationCapacity: Int
abstract fun getSchematicName(variant: Int): String
fun placeFull() {
currentProgressPercent = 1f
var clipboard: Clipboard
val file: File = Aoe.getSchematicFile(getSchematicName(variant))
val format = ClipboardFormats.findByFile(file)
format!!.getReader(FileInputStream(file)).use { reader ->
clipboard = reader.read()
}
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 { interface BuildingInfo {

View File

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

View File

@ -1,52 +1,10 @@
package com.pobnellion.aoe.building package com.pobnellion.aoe.building
import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.ui.PlaceHintValidators
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.Bukkit
import org.bukkit.Location import org.bukkit.Location
import java.io.File
import java.io.FileInputStream
class TownCenter(location: Location, variant: Int): Building(location, variant) { abstract class TownCenter(location: Location, variant: Int): Building(location, variant) {
companion object Info: BuildingInfo { override var populationCapacity: Int = 5
override val buildingType = BuildingType.TOWN_CENTER abstract val villagerSpawnLocation: Location
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 TownCenter(location, variant)
}
}
init {
var clipboard: Clipboard
val file: File = Aoe.getSchematicFile(schematicNames[variant])
val format = ClipboardFormats.findByFile(file)
format!!.getReader(FileInputStream(file)).use { reader ->
clipboard = reader.read()
}
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)) // configure here
.build()
Operations.complete(operation)
}
}
} }

View File

@ -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]
}

View File

@ -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]
}

View File

@ -0,0 +1,55 @@
package com.pobnellion.aoe.civilisation
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 org.bukkit.Location
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()
private val villagers: MutableList<AoeVillager> = mutableListOf()
val players: MutableList<Player> = mutableListOf()
fun addPlayer(player: Player) {
players.add(player)
player.sendMessage("Joined team ${name()}")
}
protected abstract fun getBuildingInfo(type: BuildingType): BuildingInfo
protected abstract fun name(): String
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)
villagers.add(AoeVillager(townCenter.villagerSpawnLocation))
}
fun populationCap() = min(Constants.MAX_POP_CAP, buildings.sumOf { building -> building.populationCapacity })
}

View File

@ -0,0 +1,5 @@
package com.pobnellion.aoe.civilisation
enum class CivilisationType {
PLAINS
}

View File

@ -1,12 +1,12 @@
package com.pobnellion.aoe.team package com.pobnellion.aoe.civilisation
import com.pobnellion.aoe.building.BuildingInfo import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.building.House import com.pobnellion.aoe.building.plains.PlainsHouse
import com.pobnellion.aoe.building.TownCenter import com.pobnellion.aoe.building.plains.PlainsTownCenter
import org.bukkit.entity.Player import org.bukkit.Location
class Plains(players: List<Player>) : Team(players) { class Plains(setupLocation: Location) : Civilisation(setupLocation) {
override fun name(): String = "Plains" override fun name(): String = "Plains"
override fun getBuildingInfo(type: BuildingType): BuildingInfo { override fun getBuildingInfo(type: BuildingType): BuildingInfo {
@ -15,12 +15,12 @@ class Plains(players: List<Player>) : Team(players) {
BuildingType.BARRACKS -> TODO() BuildingType.BARRACKS -> TODO()
BuildingType.DOCK -> TODO() BuildingType.DOCK -> TODO()
BuildingType.FARM -> TODO() BuildingType.FARM -> TODO()
BuildingType.HOUSE -> House.Info BuildingType.HOUSE -> PlainsHouse.Info
BuildingType.LUMBER_CAMP -> TODO() BuildingType.LUMBER_CAMP -> TODO()
BuildingType.MILL -> TODO() BuildingType.MILL -> TODO()
BuildingType.MINING_CAMP -> TODO() BuildingType.MINING_CAMP -> TODO()
BuildingType.STABLE -> TODO() BuildingType.STABLE -> TODO()
BuildingType.TOWN_CENTER -> TownCenter.Info BuildingType.TOWN_CENTER -> PlainsTownCenter.Info
BuildingType.UNIQUE -> TODO() BuildingType.UNIQUE -> TODO()
BuildingType.WALL -> TODO() BuildingType.WALL -> TODO()
BuildingType.WATCH_TOWER -> TODO() BuildingType.WATCH_TOWER -> TODO()

View File

@ -1,7 +1,8 @@
package com.pobnellion.aoe.command package com.pobnellion.aoe.command
import com.pobnellion.aoe.Aoe import com.pobnellion.aoe.Aoe
import com.pobnellion.aoe.team.Plains 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
@ -22,6 +23,7 @@ class TestCommand : CommandExecutor {
when (args[0]) { when (args[0]) {
"team" -> team(sender, args) "team" -> team(sender, args)
"start" -> Aoe.startGame()
else -> sender.sendMessage("Invalid argument: ${args[0]}") else -> sender.sendMessage("Invalid argument: ${args[0]}")
} }
@ -35,21 +37,33 @@ class TestCommand : CommandExecutor {
} }
when (args[1]) { when (args[1]) {
"plains" -> Aoe.teams.add(Plains(listOf(sender as Player))) "plains" -> joinCiv(sender as Player, CivilisationType.PLAINS)
else -> sender.sendMessage("Invalid team: ${args[1]}") 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.civilisations[civilisationType]!!.addPlayer(player)
}
} }
class TestTabCompleter: TabCompleter { class TestTabCompleter: TabCompleter {
override fun onTabComplete(sender: CommandSender, command: Command, label: String, args: Array<out String>): MutableList<String>? { override fun onTabComplete(sender: CommandSender, command: Command, label: String, args: Array<out String>): MutableList<String>? {
val options: MutableList<String> = mutableListOf() val options: MutableList<String> = mutableListOf()
if (args.size == 1) if (args.size == 1) {
options.add("team") options.add("team")
options.add("start")
}
if (args.size == 2) if (args.size == 2)
options.add("plains") if (args[0] == "team")
options.add("plains")
return options return options
} }

View File

@ -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()
}

View 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))
}
}

View File

@ -1,30 +0,0 @@
package com.pobnellion.aoe.team
import com.pobnellion.aoe.building.Building
import com.pobnellion.aoe.building.BuildingInfo
import com.pobnellion.aoe.building.BuildingType
import com.pobnellion.aoe.ui.BuildingPlaceHint
import org.bukkit.Location
import org.bukkit.entity.Player
import kotlin.random.Random
abstract class Team(val players: List<Player>) {
private val buildings: MutableList<Building> = mutableListOf()
init {
players.forEach { player -> player.sendMessage("Joined team ${name()}") }
}
protected abstract fun getBuildingInfo(type: BuildingType): BuildingInfo
protected abstract fun name(): String
fun addBuilding(location: Location, building: BuildingInfo, variant: Int) {
buildings.add(building.create(location, variant))
}
fun showBuildingPlaceHint(type: BuildingType, player: Player) {
val info = getBuildingInfo(type)
val variant = Random.nextInt(info.schematicNames.size)
BuildingPlaceHint.add(player, info, ::addBuilding, variant)
}
}

View File

@ -77,5 +77,4 @@ class PlaceItem : Listener {
item.itemMeta.lore(mutableListOf(Component.text(lore))) item.itemMeta.lore(mutableListOf(Component.text(lore)))
return item return item
} }
} }