This commit is contained in:
2025-02-23 19:36:39 +08:00
parent 9175c5d291
commit 5f9346a34e
4 changed files with 131 additions and 63 deletions

View File

@@ -2,6 +2,8 @@ package xyz.fortern.minehunt
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.Style
import net.kyori.adventure.text.format.TextDecoration
import net.kyori.adventure.title.Title import net.kyori.adventure.title.Title
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Difficulty import org.bukkit.Difficulty
@@ -17,6 +19,7 @@ import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import org.bukkit.scoreboard.Criteria import org.bukkit.scoreboard.Criteria
import org.bukkit.scoreboard.DisplaySlot import org.bukkit.scoreboard.DisplaySlot
import org.bukkit.scoreboard.RenderType
import org.bukkit.scoreboard.Team import org.bukkit.scoreboard.Team
import xyz.fortern.minehunt.rule.GameRules import xyz.fortern.minehunt.rule.GameRules
import xyz.fortern.minehunt.rule.RuleKey import xyz.fortern.minehunt.rule.RuleKey
@@ -164,8 +167,7 @@ class Console {
/** /**
* 猎人出生倒计时 * 猎人出生倒计时
*/ */
var hunterSpawnCD: BukkitTask? = null private var hunterSpawnCD: BukkitTask? = null
private set
/** /**
* 指南针刷新任务 * 指南针刷新任务
@@ -189,7 +191,7 @@ class Console {
init { init {
instance = this instance = this
setRuleScoreboard() initScoreboard()
// 初始化 Minecraft 游戏规则 // 初始化 Minecraft 游戏规则
overworld.worldBorder.size = 32.0 overworld.worldBorder.size = 32.0
overworld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false) overworld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
@@ -207,54 +209,57 @@ class Console {
hunterTeam = scoreboard.getTeam("hunter") ?: scoreboard.registerNewTeam("hunter") hunterTeam = scoreboard.getTeam("hunter") ?: scoreboard.registerNewTeam("hunter")
spectatorTeam = scoreboard.getTeam("spectator") ?: scoreboard.registerNewTeam("spectator") spectatorTeam = scoreboard.getTeam("spectator") ?: scoreboard.registerNewTeam("spectator")
speedrunnerTeam.entries.forEach { speedrunnerTeam.removeEntries(it) } speedrunnerTeam.let { t ->
hunterTeam.entries.forEach { hunterTeam.removeEntries(it) } t.color(NamedTextColor.BLUE)
spectatorTeam.entries.forEach { spectatorTeam.removeEntries(it) } t.prefix(Component.text("[速通者] ", NamedTextColor.BLUE))
t.entries.forEach { speedrunnerTeam.removeEntries(it) }
}
hunterTeam.let { t ->
t.color(NamedTextColor.RED)
t.prefix(Component.text("[猎人] ", NamedTextColor.RED))
t.entries.forEach { hunterTeam.removeEntries(it) }
}
spectatorTeam.let { t ->
t.color(NamedTextColor.GRAY)
t.prefix(Component.text("[观众] ", NamedTextColor.GRAY))
t.entries.forEach { spectatorTeam.removeEntries(it) }
}
} }
/** /**
* 判断玩家是否为猎人 * 判断玩家是否为猎人
*/ */
fun isHunter(player: Player) = hunterSet.contains(player.uniqueId) fun isHunter(player: Player): Boolean = hunterSet.contains(player.uniqueId)
/** /**
* 判断是否为观察者 * 判断是否为观察者
*/ */
fun isSpectator(player: Player) = spectatorSet.contains(player.uniqueId) fun isSpectator(player: Player): Boolean = spectatorSet.contains(player.uniqueId)
/** /**
* 判断是否为速通者 * 判断是否为速通者
*/ */
fun isSpeedrunner(player: Player) = speedrunnerSet.contains(player.uniqueId) fun isSpeedrunner(player: Player): Boolean = speedrunnerSet.contains(player.uniqueId)
/** /**
* 加入猎人阵营 * 加入猎人阵营
*/ */
fun joinHunter(player: Player) { fun joinHunter(player: Player) {
val uuid = player.uniqueId hunterTeam.addEntry(player.name)
speedrunnerSet.remove(uuid)
spectatorSet.remove(uuid)
hunterSet.add(uuid)
} }
/** /**
* 加入速通者阵营 * 加入速通者阵营
*/ */
fun joinSpeedrunner(player: Player) { fun joinSpeedrunner(player: Player) {
val uuid = player.uniqueId speedrunnerTeam.addEntry(player.name)
hunterSet.remove(uuid)
spectatorSet.remove(uuid)
speedrunnerSet.add(uuid)
} }
/** /**
* 加入观察者阵营 * 加入观察者阵营
*/ */
fun joinSpectator(player: Player) { fun joinSpectator(player: Player) {
val uuid = player.uniqueId spectatorTeam.addEntry(player.name)
hunterSet.remove(uuid)
speedrunnerSet.remove(uuid)
spectatorSet.add(uuid)
} }
/** /**
@@ -295,6 +300,11 @@ class Console {
}, 0, 20) }, 0, 20)
} }
fun interruptCountdownToStart() {
beginningCountdown?.cancel()
beginningCountdown = null
}
/** /**
* 开始游戏 * 开始游戏
* *
@@ -326,13 +336,21 @@ class Console {
} }
// 速通者更改为生存模式并加入speedrunnerList // 速通者更改为生存模式并加入speedrunnerList
speedrunnerSet.forEach { Bukkit.getPlayer(it)?.gameMode = GameMode.SURVIVAL } speedrunnerTeam.entries.forEach { entry ->
speedrunnerList = speedrunnerSet.toList() Bukkit.getPlayer(entry)?.let {
speedrunnerSet.add(it.uniqueId)
it.gameMode = GameMode.SURVIVAL
}
}
speedrunnerSet.toList()
// 将猎人传送到世界底部,且指南针开始有所指向 // 将猎人传送到世界底部,且指南针开始有所指向
hunterSet.forEach { hunterTeam.entries.forEach { entry ->
Bukkit.getPlayer(it)?.teleport(Location(world, 0.0, -64.0, 0.0)) Bukkit.getPlayer(entry)?.let {
trackRunnerMap[it] = 0 hunterSet.add(it.uniqueId)
it.teleport(Location(world, 0.0, -64.0, 0.0))
trackRunnerMap[it.uniqueId] = 0
}
} }
// 创建指南针更新任务 // 创建指南针更新任务
@@ -342,24 +360,9 @@ class Console {
val hunter = Bukkit.getPlayer(it) ?: return@forEach val hunter = Bukkit.getPlayer(it) ?: return@forEach
val i = (trackRunnerMap[it] ?: return@forEach) % speedrunnerList.size val i = (trackRunnerMap[it] ?: return@forEach) % speedrunnerList.size
// hunter 正在追踪的 speedrunner // hunter 正在追踪的 speedrunner
// 这意味着如果速通者掉线,指南针将指向掉线时的位置
val speedrunner = Bukkit.getPlayer(speedrunnerList[i]) ?: return@forEach val speedrunner = Bukkit.getPlayer(speedrunnerList[i]) ?: return@forEach
val items = hunter.inventory.all(hunterCompass) refreshCompassTrack(hunter, speedrunner)
var flag = false
items.forEach inner@{ (k, v) ->
// 避免玩家身上有多个猎人指南针
if (flag) {
hunter.inventory.clear(k)
return@inner
}
val lore = v.lore()
if (!lore.isNullOrEmpty() && lore[0].equals(compassFlag)) {
flag = true
// 让指南针指向某一个猎人
val meta = v.itemMeta as CompassMeta
meta.isLodestoneTracked = false
meta.lodestone = speedrunner.location
}
}
} }
} }
} }
@@ -379,7 +382,9 @@ class Console {
compassRefreshTask = compassTask.runTaskTimer(Minehunt.instance(), 0, 20) compassRefreshTask = compassTask.runTaskTimer(Minehunt.instance(), 0, 20)
// 通知速通者 // 通知速通者
speedrunnerSet.forEach { Bukkit.getPlayer(it)?.sendMessage(Component.text("猎人开始追杀", NamedTextColor.RED)) } speedrunnerSet.forEach {
Bukkit.getPlayer(it)?.sendMessage(Component.text("猎人开始追杀", NamedTextColor.RED))
}
}, gameRules.getRuleValue(RuleKey.HUNTER_READY_CD) * 20L) }, gameRules.getRuleValue(RuleKey.HUNTER_READY_CD) * 20L)
} }
@@ -467,20 +472,51 @@ class Console {
/** /**
* 让该玩家所追踪的目标切换到下一个 * 让该玩家所追踪的目标切换到下一个
*/ */
fun trackNextPlayer(player: Player) { fun trackNextPlayer(hunter: Player) {
if (stage != GameStage.PROCESSING) return if (stage != GameStage.PROCESSING) return
val i = trackRunnerMap[player.uniqueId] ?: return val i = trackRunnerMap[hunter.uniqueId] ?: return
if (speedrunnerList.isEmpty()) return if (speedrunnerList.isEmpty()) return
var j = i var j = i
while (true) { while (true) {
j++ j++
j %= speedrunnerList.size j %= speedrunnerList.size
val speedrunner = speedrunnerList[i] if (i == j) {
if (!outPlayers.contains(speedrunner) && Bukkit.getPlayer(speedrunner) != null || i == j) { // 极端情况,所有速通者都掉线了
trackRunnerMap[player.uniqueId] = j
break break
} }
val uuid = speedrunnerList[j]
val speedrunner = Bukkit.getPlayer(uuid)
if (!outPlayers.contains(uuid) && speedrunner != null) {
trackRunnerMap[hunter.uniqueId] = j
val location = speedrunner.location
// hunter操作指南针时立即刷新位置
refreshCompassTrack(hunter, speedrunner)
break
}
}
}
/**
* 使hunter的指南针指向此时speedrunner的位置
*/
private fun refreshCompassTrack(hunter: Player, speedrunner: Player) {
val items = hunter.inventory.all(hunterCompass)
var flag = false
items.forEach inner@{ (k, v) ->
// 避免玩家身上有多个猎人指南针
if (flag) {
hunter.inventory.clear(k)
return@inner
}
val lore = v.lore()
if (!lore.isNullOrEmpty() && lore[0].equals(compassFlag)) {
flag = true
// 让指南针指向某一个猎人
val meta = v.itemMeta as CompassMeta
meta.isLodestoneTracked = false
meta.lodestone = speedrunner.location
}
} }
} }
@@ -488,11 +524,18 @@ class Console {
* 处理玩家死亡 * 处理玩家死亡
*/ */
fun handlePlayerDeath(player: Player) { fun handlePlayerDeath(player: Player) {
if (stage != GameStage.PROCESSING) return
Bukkit.getPlayer(player.uniqueId) Bukkit.getPlayer(player.uniqueId)
if (isSpeedrunner(player)) { if (isSpeedrunner(player)) {
// 速通者置为旁观者模式,加入淘汰名单 // 速通者置为旁观者模式,加入淘汰名单
player.gameMode = GameMode.SPECTATOR player.gameMode = GameMode.SPECTATOR
outPlayers.add(player.uniqueId) outPlayers.add(player.uniqueId)
// 给淘汰的玩家名字上加删除线
val playerListName = player.playerListName()
playerListName.style(Style.style(TextDecoration.STRIKETHROUGH))
player.playerListName(playerListName)
// 如给所有hunter都淘汰则游戏结束
if (outPlayers.size == hunterSet.size) { if (outPlayers.size == hunterSet.size) {
end("hunter") end("hunter")
} }
@@ -507,11 +550,16 @@ class Console {
} }
} }
private fun setRuleScoreboard() { private fun initScoreboard() {
//清除旧的计分板信息 //清除旧的计分板信息
scoreboard.teams.forEach { it.unregister() } scoreboard.teams.forEach { it.unregister() }
val objective = scoreboard.getObjective("rule-list") scoreboard.getObjective("rule-list")?.unregister()
objective?.unregister() scoreboard.getObjective("players")?.unregister()
scoreboard.registerNewObjective("players", Criteria.HEALTH, null).let {
it.displaySlot = DisplaySlot.PLAYER_LIST
it.renderType = RenderType.HEARTS
}
//设置新的计分板信息 //设置新的计分板信息
val ruleListObjective = scoreboard.registerNewObjective( val ruleListObjective = scoreboard.registerNewObjective(
@@ -531,7 +579,6 @@ class Console {
Component.text(": ").append(Component.text(entry.value.toString(), NamedTextColor.GREEN)) Component.text(": ").append(Component.text(entry.value.toString(), NamedTextColor.GREEN))
) )
} }
ruleListObjective.displaySlot = DisplaySlot.SIDEBAR ruleListObjective.displaySlot = DisplaySlot.SIDEBAR
} }

View File

@@ -132,6 +132,12 @@ class MinehuntCommand(
* 玩家加入队伍 * 玩家加入队伍
*/ */
private fun onJoin(sender: CommandSender, args: List<String>, flag: Boolean): List<String>? { private fun onJoin(sender: CommandSender, args: List<String>, flag: Boolean): List<String>? {
if (console.stage != Console.GameStage.PREPARING && console.beginningCountdown != null) {
if (flag) {
sender.sendMessage(Component.text("只能在准备阶段加入队伍", NamedTextColor.RED))
}
return null
}
if (args.size == 1) { if (args.size == 1) {
if (flag) { if (flag) {
sender.sendMessage(Component.text("输入正确的队伍名称", NamedTextColor.RED)) sender.sendMessage(Component.text("输入正确的队伍名称", NamedTextColor.RED))

View File

@@ -59,6 +59,12 @@ class TestCommand : CommandExecutor {
) )
} }
} }
// 调试
"list_name" -> {
if (sender is Player && sender.name == "Fortern") {
sender.sendMessage(sender.playerListName)
}
}
} }
return true return true
} }

View File

@@ -10,6 +10,7 @@ import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.event.player.PlayerPortalEvent import org.bukkit.event.player.PlayerPortalEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.event.player.PlayerTeleportEvent import org.bukkit.event.player.PlayerTeleportEvent
import xyz.fortern.minehunt.Console import xyz.fortern.minehunt.Console
import xyz.fortern.minehunt.Console.GameStage import xyz.fortern.minehunt.Console.GameStage
@@ -32,16 +33,22 @@ class PlayerListener(
player.scoreboard.teams.forEach { it.removeEntry(player.name) } player.scoreboard.teams.forEach { it.removeEntry(player.name) }
// 自动加入观察者队伍 // 自动加入观察者队伍
console.spectatorTeam.addEntry(player.name) console.spectatorTeam.addEntry(player.name)
} else {
// 在倒计时或进行阶段玩家自动加入各自的Team
if (console.isSpeedrunner(player)) {
console.speedrunnerTeam.addEntry(player.name)
} else if (console.isHunter(player)) {
console.hunterTeam.addEntry(player.name)
} else {
console.joinSpectator(player)
} }
} }
/**
* 参与游戏的玩家在倒计时阶段退出,则中断倒计时
*/
@EventHandler
fun onPlayerJoin(event: PlayerQuitEvent) {
val player = event.player
val stage = console.stage
if (stage == GameStage.PREPARING
&& console.beginningCountdown != null
&& (console.isHunter(player) || console.isSpeedrunner(player))
) {
console.interruptCountdownToStart()
}
} }
/** /**
@@ -99,7 +106,9 @@ class PlayerListener(
} }
} }
// 监听传送门传送事件。改变维度时,记录最后的位置。 /**
* 监听传送门传送事件。改变维度时,记录最后的位置。
*/
@EventHandler @EventHandler
fun onPlayerChangeWorld(event: PlayerPortalEvent) { fun onPlayerChangeWorld(event: PlayerPortalEvent) {
if (console.stage != GameStage.PROCESSING) return if (console.stage != GameStage.PROCESSING) return