Compare commits

...
37 Commits
Author SHA1 Message Date
tony_all ef8b5c973f format code 2024-08-08 20:12:34 +08:00
tony_all ff96cf59da fix 重生后村民不可见
finish 方块禁止破坏
update 计分板显示
finish TNT自动点燃
finish 优化传送体验
finish 防止重复加入检测
2024-08-08 20:11:55 +08:00
tony_all a063e8c6dd update TabooLib 2024-08-04 16:22:11 +08:00
tony_all be0bc6ffba finish item generating part
update TabooLib
2024-08-04 16:11:12 +08:00
tony_all 439cbccfe2 finish ore generate part (Part Of MAXMC-T-4) 2023-06-25 02:15:01 +08:00
tony_all 31e52382b2 sync 2023-06-23 17:03:52 +08:00
tony_all 9f5f657ea1 finish rule match part 2023-06-21 16:22:42 +08:00
tony_all 45629b0d1b rule change start 2023-06-21 00:44:44 +08:00
tony_all b462eaa518 remove due to bug 2023-06-19 02:19:36 +08:00
tony_all 29e9b188b1 fix artifact uploading 2023-06-16 11:57:00 +08:00
tony_all 202eb473fa fix code error 2023-06-16 11:50:16 +08:00
tony_all 2aa6761412 fix code error 2023-06-16 11:47:24 +08:00
tony_all 1d753801dd update automation script & test 2023-06-16 11:44:18 +08:00
tony_all 60295330de add runtime map file 2023-06-16 02:09:20 +08:00
tony_all f6e34d8efc fix placing rule 2023-06-16 02:07:14 +08:00
tony_all 6f1a727691 fix MAXMC-T-8 2023-06-16 01:56:10 +08:00
tony_all 3d09b48da8 fix MAXMC-T-6 2023-06-16 01:44:03 +08:00
tony_all 9416250e13 fix MAXMC-T-6 2023-06-16 01:41:22 +08:00
tony_all 7f4ef2a447 optimize MAXMC-T-2 2023-06-16 01:34:10 +08:00
tony_all 1053fde43f fix MAXMC-T-2 2023-06-16 01:27:30 +08:00
tony_all f1ae6d7f4c sync 2023-06-16 00:14:37 +08:00
tony_all ef8e2391d6 fix rule logic 2023-06-10 22:02:00 +08:00
tony_all 20b1004e62 fix test data 2023-06-10 21:06:13 +08:00
tony_all 3d35de3fe3 add toString for Area 2023-06-10 19:40:14 +08:00
tony_all 764ead46c2 fix rule match
add debug
2023-06-07 16:05:45 +08:00
tony_all 84baa309e3 optimize rule structure 2023-06-07 15:54:37 +08:00
tony_all ed6f40b462 fix rule initialize
add dynamic rule when the wall falls
2023-06-07 15:47:36 +08:00
tony_all 45882c8cb6 add break & place check
optimize code logic of Teleport Listener
2023-06-07 15:34:44 +08:00
tony_all 97c5c1c9fb sync with remote 2023-06-07 14:47:30 +08:00
tony_all 362941fe58 fix location
format code
2023-06-06 22:50:16 +08:00
tony_all accec4fe8f fix 2023-06-06 14:46:11 +08:00
tony_all e4612e0220 upload result 2023-06-06 14:44:42 +08:00
tony_all dce4300386 rm useless repo
update image
2023-06-06 14:06:10 +08:00
tony_all 786d80172c use official 2023-06-06 00:51:57 +08:00
tony_all 216bff831e fix distro 2023-06-06 00:30:51 +08:00
tony_all 1636f8a159 update space 2023-06-06 00:27:10 +08:00
TONY AllandSpace a7c7a647d9 Init space job 2023-06-05 06:57:16 +00:00
28 changed files with 1880 additions and 95 deletions
+1
View File
@@ -2,3 +2,4 @@
.idea
build/
test_server
+17
View File
@@ -0,0 +1,17 @@
job("Build and run tests") {
// run 'gradlew build'
gradlew("azul/zulu-openjdk:17", "build", "--no-daemon") {
// mountDir = "/root"
// cache {
// storeKey = "dot-gradle"
// localPath = "/root/.gradle"
// }
fileArtifacts {
localPath = "build/libs/*"
archive = true
remotePath = "{{ run:number }}/build"
onStatus = OnStatus.SUCCESS
}
}
}
+22 -17
View File
@@ -1,8 +1,9 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import io.izzel.taboolib.gradle.*
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm") version "1.7.20"
id("io.izzel.taboolib") version "1.50"
kotlin("jvm") version "2.0.0"
id("io.izzel.taboolib") version "2.0.11"
}
group = "cc.maxmc.blastingcrisis"
@@ -11,17 +12,14 @@ version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
mavenLocal()
maven("https://repo.maxmc.cn/repository/maven-snapshots/") {
name = "maxmc"
credentials(PasswordCredentials::class.java)
}
maven("https://repo.vip.maxmc.cc:30443/releases")
}
dependencies {
implementation(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
implementation("ink.ptms.core:v10800:10800")
// taboo("cc.maxmc.agones:AgonesKt:0.1.3")
// implementation("ink.ptms.core:v11200:11200")
}
@@ -32,15 +30,22 @@ taboolib {
name("TONY_All")
}
}
install("common", "common-5")
install("platform-bukkit")
install("module-nms", "module-nms-util")
install("module-chat", "module-lang", "module-configuration")
env {
// install("common", "common-5")
install(BUKKIT_ALL)
install(NMS, NMS_UTIL)
install(CHAT, LANG, CONFIGURATION)
}
options("skip-kotlin-relocate")
version = "6.0.10-12"
// classifier = null
// options("skip-kotlin-relocate")
version {
taboolib = "6.1.2-beta2"
}
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_1_8
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -9,11 +9,13 @@ import cc.maxmc.blastingcrisis.misc.GameManager
import cc.maxmc.blastingcrisis.misc.info
import cc.maxmc.blastingcrisis.misc.pluginScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Location
import taboolib.common.env.RuntimeDependency
import taboolib.common.platform.Plugin
import taboolib.common.platform.function.submit
@RuntimeDependency(
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4",
@@ -22,11 +24,22 @@ object BlastingCrisis : Plugin() {
override fun onEnable() {
info("§a| §7Loading BlastingCrisis")
DebugCommand.debug("debugcmd")
info("§a| §7Loading BlastingCrisis")
createDefaultGame()
submit(delay = 1) {
pluginScope.launch {
// AgonesSDK().use { sdk ->
// sdk.ready()
// }
}
}
}
override fun onDisable() {
pluginScope.launch {
// AgonesSDK().use { sdk ->
// sdk.shutdown()
// }
}
pluginScope.cancel()
}
@@ -35,17 +48,17 @@ object BlastingCrisis : Plugin() {
Location(Bukkit.getWorlds().first(), x.toDouble(), y.toDouble(), z.toDouble())
val teamRed = MapTeam(
"红队",
ChatColor.RED,
location(0, 72, -44),
location(0, 72, -44),
location(-8, 73, -47),
Area(location(14, 72, -48), location(-13, 78, -34)),
Area(location(-13, 78, -33), location(13, 72, -33)),
Area(location(-13, 78, -32), location(13, 71, -1)),
Area(location(7, 72, -45), location(9, 74, -48)),
listOf(
Area(location(15, 72, -1), location(19, 78, -48)), Area(location(19, 78, -48), location(-15, 72, -1))
name = "红队",
color = ChatColor.RED,
spawn = location(0, 72, -44),
villager = location(0, 72, -44),
upgrade = location(-8, 73, -47),
home = Area(location(14, 72, -48), location(-14, 78, -34)),
wall = Area(location(-13, 78, -33), location(13, 72, -33)),
mine = Area(location(-13, 78, -32), location(13, 72, -1)),
teleport = Area(location(7, 72, -45), location(9, 74, -48)),
sides = listOf(
Area(location(15, 72, -1), location(19, 78, -48)), Area(location(-19, 78, -48), location(-15, 72, -1))
),
)
val teamGreen = MapTeam(
@@ -54,12 +67,13 @@ object BlastingCrisis : Plugin() {
location(0, 72, 44),
location(0, 72, 44),
location(8, 73, 47),
Area(location(-14, 72, 48), location(13, 78, 34)),
Area(location(-14, 72, 48), location(14, 78, 34)),
Area(location(13, 78, 33), location(-13, 72, 33)),
Area(location(13, 78, 32), location(-13, 71, 1)),
Area(location(-7, 72, 45), location(-9, 74, 48)),
Area(location(13, 78, 32), location(-13, 72, 1)),
Area(location(-7, 71, 48), location(-9, 71, 46)),
// Area(location(-7, 72, 45), location(-9, 74, 48)),
listOf(
Area(location(-15, 72, 1), location(-19, 78, 48)), Area(location(-19, 78, 48), location(15, 72, 1))
Area(location(-15, 72, 1), location(-19, 78, 48)), Area(location(19, 78, 48), location(15, 72, 1))
),
)
val map = GameMap(
@@ -69,7 +83,12 @@ object BlastingCrisis : Plugin() {
listOf(
teamRed, teamGreen
),
1
1,
"default",
listOf(
location(-5, 72, -44), location(5, 72, -44), location(0, 72, -48),
location(-5, 72, 44), location(5, 72, 44), location(0, 72, 48)
)
)
DebugCommand.game = Game(map)
GameManager.currentGame = DebugCommand.game
@@ -2,8 +2,11 @@ package cc.maxmc.blastingcrisis.command
import cc.maxmc.blastingcrisis.game.Game
import cc.maxmc.blastingcrisis.game.GameOreGenerator
import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule.MatchResult
import cc.maxmc.blastingcrisis.game.GameState
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.GameManager
import cc.maxmc.blastingcrisis.misc.UniArea
import cc.maxmc.blastingcrisis.packet.BEntityVillager
import kotlinx.coroutines.cancel
import org.bukkit.Bukkit
@@ -16,11 +19,25 @@ import taboolib.module.configuration.Configuration
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.sendActionBar
import java.io.File
import java.util.*
import cc.maxmc.blastingcrisis.misc.debug as debuglog
object DebugCommand {
lateinit var game: Game
lateinit var villager: BEntityVillager
var autoRespawn = false
val builders = ArrayList<UUID>()
private val init by lazy {
// allow builders
debuglog("init builder rule")
GameManager.currentGame.placeBreakRule.addRuleFirst("builder") { (player, _, _, _) ->
// cc.maxmc.blastingcrisis.misc.debug("matching builder rule")
if (builders.contains(player.uniqueId)) MatchResult.ALLOW else MatchResult.DEFAULT
}
}
fun debug(cmd: String) = command(cmd) {
literal("game") {
literal("join") {
execute<Player> { sender, _, _ ->
@@ -77,6 +94,20 @@ object DebugCommand {
}
}
literal("respawn") {
execute<Player> { sender, _, _ ->
villager.removeViewer(sender)
villager.addViewer(sender)
}
}
literal("autoRespawn") {
execute<Player> { sender, _, _ ->
autoRespawn = !autoRespawn
sender.sendMessage("Auto Respawn is now $autoRespawn.")
}
}
literal("rename") {
dynamic {
execute<Player> { _, _, arg ->
@@ -116,11 +147,24 @@ object DebugCommand {
println("saved")
val config = Configuration.loadFromFile(File(getDataFolder(), "ore-generators/default.yml"))
val gen = GameOreGenerator(config)
val job = gen.enable(area)
val job = gen.enable(UniArea(listOf(area)))
Bukkit.getScheduler().runTaskLater(BukkitPlugin.getInstance(), {
job.cancel("Stop by hand")
}, 60 * 20)
}
}
literal("build_mode") {
execute<Player> { sender, _, _ ->
init
if (builders.contains(sender.uniqueId)) {
builders.remove(sender.uniqueId)
sender.sendMessage("U r no longer in build mode")
return@execute
}
sender.sendMessage("U r now in build mode")
builders.add(sender.uniqueId)
}
}
}
}
@@ -8,7 +8,10 @@ object GlobalSettings {
lateinit var settings: Configuration
val timeToStart
get() = settings.getInt("time-to-start")
get() = settings.getInt("time_to_start")
val itemGenDelay
get() = settings.getLong("item_gen_delay")
object GameSettings {
val villagerMaxHealth
@@ -1,10 +1,17 @@
package cc.maxmc.blastingcrisis.debug
import cc.maxmc.blastingcrisis.command.DebugCommand
import cc.maxmc.blastingcrisis.command.DebugCommand.villager
import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy
import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntityLiving
import org.bukkit.Bukkit
import org.bukkit.event.player.PlayerRespawnEvent
import taboolib.common.platform.event.SubscribeEvent
import taboolib.common.platform.function.submit
import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.module.nms.PacketSendEvent
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.broadcast
object DListener {
@SubscribeEvent
@@ -12,13 +19,29 @@ object DListener {
Bukkit.getScheduler().runTask(BukkitPlugin.getInstance()) {
if (it.player.name != "TONY_All") return@runTask
// val source = it.packet.source
if (!it.packet.name.lowercase().contains("entity")) return@runTask
// println(it.packet.name)
if (it.packet.source !is PacketPlayOutSpawnEntityLiving) {
return@runTask
// if (!it.packet.name.lowercase().contains("entity")) return@runTask
val pname = listOf("PacketPlayOutSpawnEntityLiving", "PacketPlayOutEntityDestroy")
if (pname.contains(it.packet.name)) println(it.packet.name)
if (it.packet.source is PacketPlayOutSpawnEntityLiving) {
it.packet.source.getProperty<Int>("a")!!.broadcast()
it.packet.source.getProperty<Int>("b")!!.broadcast()
}
if (it.packet.source is PacketPlayOutEntityDestroy) {
it.packet.source.getProperty<IntArray>("a")!!.contentToString().broadcast()
}
}
}
@SubscribeEvent
fun debugRespawn(respawnE: PlayerRespawnEvent) {
if (DebugCommand.autoRespawn) {
submit(delay = 1L) {
villager.removeViewer(respawnE.player)
"Respawning".broadcast()
villager.addViewer(respawnE.player)
}
// it.packet.source.getProperty<Int>("a")!!.broadcast()
// it.packet.source.getProperty<Int>("b")!!.broadcast()
}
}
}
@@ -1,7 +1,11 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.map.GameMap
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.BlockGenManager
import cc.maxmc.blastingcrisis.misc.UniArea
import cc.maxmc.blastingcrisis.misc.debug
import kotlinx.coroutines.Job
import org.bukkit.Material
import org.bukkit.entity.Player
import taboolib.common.platform.function.submit
@@ -15,7 +19,11 @@ class Game(
val scoreboard: GameScoreboard = GameScoreboard(this)
val timer: GameTimer = GameTimer(this)
val players = ArrayList<Player>()
val placeBreakRule = GamePlaceBreakRule(this)
val generator = BlockGenManager.getGenerator(map.blockGen)!!
var state: GameState = GameState.WAITING
private lateinit var oreGenJob: Job
private lateinit var itemGenJob: List<Job>
private fun autoJoinTeam() {
players.filter { it.team == null }.shuffled().forEach {
@@ -24,6 +32,10 @@ class Game(
}
fun join(player: Player) {
if (players.map { it.uniqueId }.contains(player.uniqueId)) {
player.sendLang("game_already_join")
return
}
players += player
broadcast {
it.sendLang("game_join", player.name)
@@ -45,18 +57,31 @@ class Game(
checkEnd()
}
fun respawnPlayer(player: Player) {
submit(delay = 1L) {
teams.forEach { it.villager.respawn(player) }
}
}
fun start() {
debug("game ${map.name} started.")
placeBreakRule.loadDefaultRule()
startOreGen()
startItemGen()
state = GameState.START
timer.startTimer()
timer.submitEvent("wall_fall", Duration.ofMinutes(1)) {
broadcast { it.sendLang("game_wall_fall") }
placeBreakRule.addRuleLast("allow_center_wall") { (_, _, loc, _) ->
if (map.wall.contains(loc)) GamePlaceBreakRule.MatchResult.ALLOW else GamePlaceBreakRule.MatchResult.DEFAULT
}
submit {
map.wall.forBlocksInArea().forEach {
it.block.type = Material.AIR
}
}
}
autoJoinTeam()
teams.forEach {
it.start()
@@ -75,17 +100,34 @@ class Game(
}
}
private fun startOreGen() {
val mines = map.teams.fold(ArrayList<Area>()) { list, team ->
list += team.mine
list += team.sides
list
}
oreGenJob = generator.enable(UniArea(mines))
}
private fun startItemGen() {
val generator = GameItemGenerator(Material.LOG)
itemGenJob = map.itemGen.map {
generator.generate(it)
}
}
fun checkEnd() {
if (teams.filter { it.teamSurvive }.size > 1) {
return
}
broadcast { it.sendLang("game_end") }
broadcast {
it.sendLang("game_end")
}
//TODO restart game logic
}
fun broadcast(action: (Player) -> Unit) {
players.forEach(action)
}
}
@@ -0,0 +1,31 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.configuration.GlobalSettings
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.pluginScope
import cc.maxmc.blastingcrisis.misc.toEntityLocation
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import org.bukkit.util.Vector
import taboolib.platform.BukkitPlugin
class GameItemGenerator(val type: Material) {
fun generate(location: Location): Job = pluginScope.launch {
while (true) {
Bukkit.getScheduler().runTask(BukkitPlugin.getInstance()) {
val item = location.world.dropItem(location.toEntityLocation(), ItemStack(type))
item.teleport(location.toEntityLocation())
item.velocity = Vector(0.0, 0.2, 0.0)
debug("Generate Item at ${location.toVector()}")
}
delay(GlobalSettings.itemGenDelay * 50)
}
}
}
@@ -1,9 +1,6 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.WeightRandom
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.pluginScope
import cc.maxmc.blastingcrisis.misc.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -24,13 +21,14 @@ class GameOreGenerator(config: Configuration) {
}
private val random = WeightRandom(ores.map { it.key to it.value })
fun enable(area: Area): Job {
fun enable(area: UniArea): Job {
return pluginScope.launch {
while (true) {
val airPercent = area.getAirPercentage()
val targetArea = area.randomizer.random()
val airPercent = targetArea.getAirPercentage()
debug("remain air: $airPercent.")
if (airPercent > 0.5) {
generate(area)
generate(targetArea)
}
delay(rate * 50L)
}
@@ -54,4 +52,5 @@ class GameOreGenerator(config: Configuration) {
return generate(area, times + 1)
}
}
}
@@ -0,0 +1,121 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule.ActionType.BREAK
import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule.ActionType.PLACE
import cc.maxmc.blastingcrisis.misc.EnhancedLinkedList
import cc.maxmc.blastingcrisis.misc.debug
import org.bukkit.Location
import org.bukkit.entity.Player
import taboolib.library.xseries.XMaterial
typealias PlaceBreakRule = (GamePlaceBreakRule.GamePlaceBreakEvent) -> GamePlaceBreakRule.MatchResult
@Suppress("unused") // API
class GamePlaceBreakRule(val game: Game) {
enum class ActionType {
PLACE, BREAK
}
enum class MatchResult {
DEFAULT, ALLOW, DENY
}
data class GamePlaceBreakEvent(
val player: Player,
val actionType: ActionType,
val loc: Location,
val type: XMaterial
)
private val rules = HashMap<String, PlaceBreakRule>()
private val rulesPriority = EnhancedLinkedList<String>()
fun matchRule(player: Player, actionType: ActionType, location: Location, type: XMaterial): Boolean {
// match rules
rules.forEach { (name, func) ->
val result = func(GamePlaceBreakEvent(player, actionType, location, type))
if (result != MatchResult.DEFAULT) {
debug("Hit rule $name")
return result == MatchResult.ALLOW
}
}
// fallback strategy
return false
}
private fun recordRule(name: String, rule: PlaceBreakRule) {
if (rules.containsKey(name)) {
throw IllegalArgumentException("Rule $name already exists.")
}
debug("rule $name added")
rules[name] = rule
}
fun addRuleLast(
name: String, rule: PlaceBreakRule
) {
recordRule(name, rule)
rulesPriority.addLast(name)
}
fun addRuleFirst(
name: String, rule: PlaceBreakRule
) {
recordRule(name, rule)
rulesPriority.addFirst(name)
}
fun addRuleBefore(
base: String, name: String, rule: PlaceBreakRule
) {
recordRule(name, rule)
rulesPriority.addBefore(base, name)
}
fun addRuleAfter(
base: String, name: String, rule: PlaceBreakRule
) {
recordRule(name, rule)
rulesPriority.addAfter(base, name)
}
fun loadDefaultRule() {
// allow mine
addRuleLast("allow_mine") { (_, _, loc, _) ->
val result = game.map.teams.flatMap {
it.sides + it.mine
}.map {
it.containsBlock(loc)
}.reduce { a, b -> a || b }
if (result) MatchResult.ALLOW else MatchResult.DEFAULT
}
// allow enemy wall
addRuleLast("allow_wall_conditional") { (player, actionType, loc, _) ->
val team = player.team ?: return@addRuleLast MatchResult.DENY
val isWall = game.teams.map {
it.teamInfo.wall.containsBlock(loc)
.also { result -> debug("area ${it.teamInfo.wall} contains $loc is $result") }
}.reduce { a, b -> a || b }
if (!isWall) return@addRuleLast MatchResult.DEFAULT
val isHome = team.teamInfo.wall.containsBlock(loc)
if ((actionType == BREAK) xor isHome) MatchResult.ALLOW else MatchResult.DENY
}
// allow tnt in enemy's home
addRuleFirst("allow_tnt_conditional") { (player, actionType, loc, type) ->
if (type != XMaterial.TNT) return@addRuleFirst MatchResult.DEFAULT
val team = player.team ?: return@addRuleFirst MatchResult.DENY
val isHome = game.teams.map {
it.teamInfo.home.containsBlock(loc)
.also { result -> debug("area ${it.teamInfo.home} contains $loc is $result") }
}.reduce { a, b -> a || b }
if (!isHome) return@addRuleFirst MatchResult.DENY
val isSelf = team.teamInfo.home.containsBlock(loc)
if ((actionType == PLACE) xor isSelf) MatchResult.ALLOW else MatchResult.DENY
}
}
}
@@ -29,9 +29,10 @@ class GameScoreboard(val game: Game) {
appendLine(player.asLangText("scoreboard_team_info", it.teamInfo.name, it.villager.health))
}
}
val eventName = player.asLangText("event_${event.second}")
player.asLangText(
"scoreboard_start",
event.second,
eventName,
"${event.first.inWholeMinutes}:${event.first.inWholeSeconds}",
teamMessage,
player.team?.villager?.health ?: throw IllegalStateException("player ${player.name} has no team."),
@@ -43,7 +44,5 @@ class GameScoreboard(val game: Game) {
}
player.sendScoreboard(*scoreboardText.lines().toTypedArray())
}
}
@@ -3,7 +3,7 @@ package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.listener.GameListener
import cc.maxmc.blastingcrisis.map.MapTeam
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.toPlayerLocation
import cc.maxmc.blastingcrisis.misc.toEntityLocation
import org.bukkit.entity.Player
import taboolib.platform.util.sendLang
@@ -15,11 +15,17 @@ class GameTeam(val game: Game, val teamInfo: MapTeam) {
private set
fun start() {
players.forEach { it.teleport(teamInfo.spawn.toPlayerLocation()) }
players.forEach { it.teleport(teamInfo.spawn.toEntityLocation()) }
villager.spawn()
GameListener.interactSubscribed[teamInfo.upgrade] = {
debug("interact team ${teamInfo.name} upgrade.")
}
GameListener.moveSubscribed.add(teamInfo.teleport to {
val player = it.player ?: throw IllegalStateException("Bukkit API LOL")
debug("teleporting $player to battle field")
val team = player.team ?: throw IllegalStateException("Player ${player.name} should have a team")
player.teleport(team.teamInfo.mine.randomLocationRestrict().toEntityLocation())
})
}
fun join(player: Player) {
@@ -1,14 +1,16 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.configuration.GlobalSettings
import cc.maxmc.blastingcrisis.misc.toPlayerLocation
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.toEntityLocation
import cc.maxmc.blastingcrisis.packet.BEntityVillager
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.asLangText
class TeamVillager(private val team: GameTeam) {
private val packetVillager = BEntityVillager.create(team.teamInfo.villager.toPlayerLocation())
private val packetVillager = BEntityVillager.create(team.teamInfo.villager.toEntityLocation())
var health: Int = GlobalSettings.GameSettings.villagerMaxHealth
private set
@@ -40,5 +42,10 @@ class TeamVillager(private val team: GameTeam) {
team.game.checkEnd()
}
fun respawn(player: Player) {
debug("respawning for ${player.name}")
packetVillager.respawnForPlayer(player)
}
}
@@ -1,26 +1,40 @@
package cc.maxmc.blastingcrisis.listener
import cc.maxmc.blastingcrisis.game.GamePlaceBreakRule
import cc.maxmc.blastingcrisis.game.team
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.GameManager
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.toPlayerLocation
import cc.maxmc.blastingcrisis.misc.toEntityLocation
import org.bukkit.Location
import org.bukkit.event.entity.EntityExplodeEvent
import org.bukkit.Material
import org.bukkit.entity.EntityType
import org.bukkit.entity.Player
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.entity.ExplosionPrimeEvent
import org.bukkit.event.entity.FoodLevelChangeEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.event.player.PlayerRespawnEvent
import taboolib.common.platform.event.EventPriority
import taboolib.common.platform.event.SubscribeEvent
import taboolib.library.xseries.XMaterial
import taboolib.platform.util.buildItem
import taboolib.platform.util.sendLang
object GameListener {
val interactSubscribed = HashMap<Location, (PlayerInteractEvent) -> Unit>()
val moveSubscribed = ArrayList<Pair<Area, (PlayerMoveEvent) -> Unit>>()
@SubscribeEvent
fun onMoveInTeleport(event: PlayerMoveEvent) {
fun portalTeleport(event: PlayerMoveEvent) {
if (!GameManager.currentGame.players.contains(event.player)) return
val player = event.player
val team = player.team ?: return
if (!team.teamInfo.teleport.isInArea(event.to)) return
debug("teleporting ${event.player} to battle field")
player.teleport(team.teamInfo.mine.randomLocation().toPlayerLocation())
moveSubscribed.forEach { (area, func) ->
if (area.contains(event.to)) func(event)
}
}
@SubscribeEvent
@@ -30,15 +44,100 @@ object GameListener {
}
@SubscribeEvent
fun onTNT(tntExplode: EntityExplodeEvent) {
debug("${tntExplode.entityType} exploded at ${tntExplode.location}")
tntExplode.blockList().clear()
fun onTNT(tntExplode: ExplosionPrimeEvent) {
debug("${tntExplode.entityType} exploded at ${tntExplode.entity.location}")
tntExplode.radius = 0.0f
if (!GameManager.currentGame.state.isStarted()) return
GameManager.currentGame.teams.findLast {
it.teamSurvive && it.teamInfo.home.isInArea(tntExplode.location)
it.teamSurvive && it.teamInfo.home.contains(tntExplode.entity.location)
}?.apply {
villager.damage()
debug("team ${teamInfo.name}'s villager damaged")
} ?: throw IllegalStateException("TNT Exploded at no team, which shouldn't happen")
} ?: return run {
debug("§cTNT Exploded at no team, which shouldn't happen")
tntExplode.isCancelled = true
}
}
@SubscribeEvent(priority = EventPriority.LOW)
fun onBreak(breakEvent: BlockBreakEvent) {
val loc = breakEvent.block.location ?: return
val player = breakEvent.player ?: return
if (GameManager.currentGame.placeBreakRule.matchRule(
player,
GamePlaceBreakRule.ActionType.BREAK,
loc,
XMaterial.matchXMaterial(breakEvent.block.type)
)
) return
breakEvent.isCancelled = true
player.sendLang("game_cant_break_block")
}
@SubscribeEvent(priority = EventPriority.LOW)
fun onPlace(placeEvent: BlockPlaceEvent) {
val loc = placeEvent.block.location ?: return
val player = placeEvent.player ?: return
if (GameManager.currentGame.placeBreakRule.matchRule(
player,
GamePlaceBreakRule.ActionType.PLACE,
loc,
XMaterial.matchXMaterial(placeEvent.blockPlaced.type)
)
) return
placeEvent.isCancelled = true
player.sendLang("game_cant_place_block")
}
@SubscribeEvent
fun onRespawn(respawnEvent: PlayerRespawnEvent) {
val team = respawnEvent.player.team ?: return // ignore none game player
respawnEvent.respawnLocation = team.teamInfo.spawn.toEntityLocation()
GameManager.currentGame.respawnPlayer(respawnEvent.player)
}
// @SubscribeEvent
fun protectDamageBeforeGame(damageEvent: EntityDamageEvent) {
if (damageEvent.entity !is Player) return // ignore none player entity
if (GameManager.currentGame.state.isStarted()) return
damageEvent.isCancelled = true
}
@SubscribeEvent
fun protectHunger(hungerEvent: FoodLevelChangeEvent) {
hungerEvent.isCancelled = true
(hungerEvent.entity as Player).run {
foodLevel = 20
saturation = 20f
}
}
@SubscribeEvent(EventPriority.HIGH, true)
fun tntIgnite(tntPlace: BlockPlaceEvent) {
if (tntPlace.blockPlaced.type != Material.TNT) return
val loc = tntPlace.blockPlaced.location
tntPlace.block.type = Material.AIR
loc.world.spawnEntity(loc.clone().apply { y += 0.5 }, EntityType.PRIMED_TNT)
}
@SubscribeEvent
fun onLeft(leftEvent: PlayerQuitEvent) {
if (!GameManager.currentGame.players.contains(leftEvent.player)) return
GameManager.currentGame.leave(leftEvent.player)
}
private val ores = mapOf(Material.IRON_ORE to Material.IRON_INGOT, Material.GOLD_ORE to Material.GOLD_INGOT)
@SubscribeEvent(ignoreCancelled = true)
fun autoSmelt(breakEvent: BlockBreakEvent) {
if (!GameManager.currentGame.players.contains(breakEvent.player)) return
val type = breakEvent.block.type
val loc = breakEvent.block.location
if (!ores.containsKey(type)) return
debug("Auto Smelting: ${breakEvent.block.type}")
breakEvent.block.type = Material.AIR
breakEvent.isCancelled = true
loc.world.dropItemNaturally(loc.toEntityLocation().apply { y += 0.5 }, buildItem(ores[type]!!))
}
}
@@ -1,8 +1,17 @@
package cc.maxmc.blastingcrisis.map
import cc.maxmc.blastingcrisis.misc.Area
import org.bukkit.Location
class GameMap(val name: String, val area: Area, val wall: Area, val teams: List<MapTeam>, val maxPlayersPerTeam: Int) {
class GameMap(
val name: String,
val area: Area,
val wall: Area,
val teams: List<MapTeam>,
val maxPlayersPerTeam: Int,
val blockGen: String,
val itemGen: List<Location>
) {
val maxPlayer: Int
get() = maxPlayersPerTeam * teams.size
}
@@ -9,5 +9,4 @@ class MapInfo(
var waitArea: Area,
val teams: List<MapTeam>,
val maxPlayersPerTeam: Int,
)
)
@@ -2,7 +2,6 @@ package cc.maxmc.blastingcrisis.misc
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.configuration.serialization.ConfigurationSerializable
import org.bukkit.configuration.serialization.SerializableAs
import org.bukkit.util.Vector
@@ -15,6 +14,7 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
val locTop: Location
val locMin: Location
@Suppress("unused") // Bukkit Specification
constructor(map: Map<String, Any>) : this(map["locTop"] as Location, map["locMin"] as Location)
init {
@@ -30,21 +30,18 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
*
* @return true if the location is in this area
*/
fun isInArea(location: Location): Boolean {
fun contains(location: Location): Boolean {
if (location.world != locTop.world) return false
if (location.x !in (locMin.x - 1)..(locTop.x + 1)) return false
if (location.x !in (locMin.x)..(locTop.x + 1)) return false
if (location.y !in locMin.y..locTop.y + 1) return false
if (location.z !in (locMin.z - 1)..(locTop.z + 1)) return false
return true
return location.z in (locMin.z)..(locTop.z + 1)
}
fun isBlockInArea(block: Block): Boolean {
val location = block.location
fun containsBlock(location: Location): Boolean {
if (location.world != locTop.world) return false
if (location.x !in locMin.x..locTop.x) return false
if (location.y !in locMin.y..locTop.y) return false
if (location.z !in locMin.z..locTop.z) return false
return true
return location.z in locMin.z..locTop.z
}
/**
@@ -83,7 +80,7 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
*
* @return a random Location that player can stand
*/
tailrec fun randomLocation(): Location {
tailrec fun randomLocationRestrict(): Location {
val x = random(locMin.blockX, locTop.blockX)
val z = random(locMin.blockZ, locTop.blockZ)
for (y in locMin.blockY..locTop.blockY) {
@@ -91,9 +88,21 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
val add = loc.clone().add(0.0, 1.0, 0.0)
if (loc.block.type == Material.AIR && add.block.type == Material.AIR) return loc
}
return randomLocation()
return randomLocationRestrict()
}
tailrec fun randomLocationAny(): Location {
val x = random(locMin.blockX, locTop.blockX)
val y = random(locMin.blockY, locTop.blockY)
val z = random(locMin.blockZ, locTop.blockZ)
val loc = Vector(x, y, z).toLocation(locMin.world)
if (loc.block.type == Material.AIR) return loc
return randomLocationAny()
}
fun volume(): Int =
(locTop.blockX - locMin.blockX) * (locTop.blockY - locMin.blockY) * (locTop.blockZ - locMin.blockZ)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -101,11 +110,10 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
other as Area
if (locTop != other.locTop) return false
if (locMin != other.locMin) return false
return true
return locMin == other.locMin
}
override fun hashCode(): Int {
var result = locTop.hashCode()
result = 31 * result + locMin.hashCode()
@@ -114,4 +122,8 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
override fun serialize() = mutableMapOf<String, Any>("locTop" to locTop, "locMin" to locMin)
override fun toString(): String {
return "Area(locTop=$locTop, locMin=$locMin)"
}
}
@@ -0,0 +1,25 @@
package cc.maxmc.blastingcrisis.misc
import cc.maxmc.blastingcrisis.game.GameOreGenerator
import taboolib.common.platform.function.getDataFolder
import taboolib.module.configuration.Configuration
object BlockGenManager {
private val generators = HashMap<String, GameOreGenerator>()
init {
load()
}
private fun load() {
getDataFolder().resolve("ore-generators").listFiles()!!.forEach {
try {
generators[it.nameWithoutExtension] = GameOreGenerator(Configuration.loadFromFile(it))
} catch (e: Exception) {
info("§c| §7加载矿石生成器 §c${it.nameWithoutExtension} §7时出现错误,已跳过.")
}
}
}
fun getGenerator(name: String): GameOreGenerator? = generators[name]
}
@@ -11,7 +11,7 @@ class WeightRandom<K, V : Number>(list: List<Pair<K, V>>) {
init {
Preconditions.checkNotNull(list, "list can NOT be null!")
for (pair in list) {
Preconditions.checkArgument(pair.second.toDouble() > 0, "非法权重值:pair=$pair")
// Preconditions.checkArgument(pair.second.toDouble() > 0, "非法权重值:pair=$pair")
val lastWeight: Double = if (weightMap.size == 0) 0.0 else weightMap.lastKey().toDouble() //统一转为double
weightMap[pair.second.toDouble() + lastWeight] = pair.first //权重累加
}
@@ -24,7 +24,11 @@ class WeightRandom<K, V : Number>(list: List<Pair<K, V>>) {
}
}
fun Location.toPlayerLocation(): Location = clone().apply {
fun <K, V : Number> List<Pair<K, V>>.weightedRandom(): WeightRandom<K, V> {
return WeightRandom(this)
}
fun Location.toEntityLocation(): Location = clone().apply {
x = blockX + 0.5
y = blockY.toDouble()
z = blockZ + 0.5
@@ -0,0 +1,6 @@
package cc.maxmc.blastingcrisis.misc
class UniArea(subArea: List<Area>) {
val randomizer = subArea.map { it to it.volume() }.weightedRandom()
}
@@ -1,6 +1,7 @@
package cc.maxmc.blastingcrisis.packet
import cc.maxmc.blastingcrisis.listener.BEntityInteract
import cc.maxmc.blastingcrisis.misc.debug
import gnu.trove.TDecorators
import gnu.trove.map.hash.TIntObjectHashMap
import net.minecraft.server.v1_8_R3.*
@@ -11,6 +12,7 @@ import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.library.reflex.Reflex.Companion.setProperty
import taboolib.library.reflex.Reflex.Companion.unsafeInstance
import taboolib.module.nms.sendPacket
import taboolib.module.nms.sendPacketBlocking
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantReadWriteLock
@@ -58,7 +60,7 @@ abstract class BEntity(var loc: Location, val entityType: Int) {
}
packet.setProperty("l", dataWatcher)
viewers.forEach { it.sendPacket(packet) }
player.sendPacketBlocking(packet)
}
open fun addViewer(viewer: Player) {
@@ -67,10 +69,18 @@ abstract class BEntity(var loc: Location, val entityType: Int) {
}
open fun removeViewer(viewer: Player) {
viewer.sendPacket(PacketPlayOutEntityDestroy(entityID))
viewer.sendPacketBlocking(PacketPlayOutEntityDestroy(entityID))
viewers.remove(viewer)
}
fun respawnForPlayer(player: Player) {
debug(viewers.toString())
if (!viewers.contains(player)) return
debug("Respawning?")
removeViewer(player)
addViewer(player)
}
fun destroy() {
ArrayList(viewers).forEach { removeViewer(it) }
}
@@ -30,6 +30,7 @@ class BEntityVillager private constructor(loc: Location) : BEntity(loc, 120) {
nameTag.removeViewer(viewer)
}
override fun DataWatcher.initEntity() {
a(6, 1.0f) // Health
a(7, 0) // Potion Effect Color
+4 -2
View File
@@ -1,7 +1,9 @@
# Time before game start
# Unit: second
time-to-start: 15
time_to_start: 15
# Unit: tick
item_gen_delay: 20
game:
villager_max_health: 150
Binary file not shown.