Iceboat/Iceboat.py
2025-05-19 17:30:41 +08:00

5477 lines
275 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# coding: utf-8
# 冰船系统 by DreamCityToger
import time
import pyspigot as ps
from org.bukkit import Bukkit, Sound, Location, Material, Particle, SoundCategory, GameMode, ChatColor
from org.bukkit.util import Vector
from org.bukkit.block import BlockFace
from org.bukkit.entity import Player, EntityType, Boat, ArmorStand, ItemDisplay
from org.bukkit.Bukkit import getServer
from org.bukkit.inventory import ItemStack
from org.bukkit.inventory.EquipmentSlot import HAND
from org.bukkit.scoreboard import Criteria, DisplaySlot, Team
from org.bukkit.event.block import Action
from org.bukkit.event.vehicle import VehicleExitEvent
from org.bukkit.event.world import EntitiesLoadEvent
from org.bukkit.event.entity import EntityDamageEvent
from org.bukkit.event.entity import EntityDamageByEntityEvent
from org.bukkit.event.inventory import InventoryClickEvent
from org.bukkit.event.inventory import InventoryDragEvent
from org.bukkit.event.inventory import InventoryCloseEvent
from org.bukkit.event.player import PlayerQuitEvent
from org.bukkit.event.player import PlayerJoinEvent
from org.bukkit.event.player import PlayerInteractEvent
from org.bukkit.event.player import PlayerInteractEntityEvent
from org.bukkit.event.player import PlayerSwapHandItemsEvent
from org.bukkit.event.player import PlayerDropItemEvent
from org.bukkit.event.player import PlayerTeleportEvent
from org.bukkit.event.player import PlayerArmorStandManipulateEvent
from org.bukkit.potion import PotionEffect, PotionEffectType
from org.bukkit.NamespacedKey import fromString
from io.papermc.paper.scoreboard.numbers import NumberFormat
from com.destroystokyo.paper.event.player import PlayerStopSpectatingEntityEvent
from net.milkbowl.vault.economy import Economy
from net.kyori.adventure.text import Component
from net.md_5.bungee.api.chat import TextComponent
from net.md_5.bungee.api.ChatMessageType import ACTION_BAR
from util2.plugins.head import PlayerHeadsAPI
from util2.develop.gui.builder import initializeItemStack
from util2.develop.gui.builder import isGUI
from util2.develop.gui.builder import getGUIValue
from util2.develop.gui.bugfixer import closeGuiForAll
from util2.develop.gui.bugfixer import cancelShiftClickFromPlayerInventory
from util2.develop.gui.bugfixer import denyAllInventoryDrag
from util2.develop.gui.manager import GUIManager
from util2.develop.gui.manager import GUIController
# 插件加载 -------------------------------------------------------------------
vaultEconomy = None
def setupEconomy():
global vaultEconomy
if Economy is None:
return False
plugin = getServer().getPluginManager().getPlugin("Vault")
if plugin is None:
return False
registeredServices = getServer().getServicesManager().getRegistrations(Economy)
if not registeredServices:
return False
vaultEconomy = registeredServices[0].getProvider()
return True
setupEconomy() # 初始化Vault Economy
playerHeadsAPI = PlayerHeadsAPI() # 初始化Player Heads
# 全局常量变量 -------------------------------------------------------------------
MAX_PLAYERS = 8 #单房间最多可以加入的玩家数量
MIN_COMPETITION_PLAYERS = 1 #比赛最少需要的玩家数量
AVAILABLE_MAPNUM = 39 #可用地图数量
WORLD_NAME = u'iceboat' #世界名
LEAVE_INTERVAL = 12 #玩家离开房间操作要求(刻)
COLLECTION_CONFIG_PATH = u"iceboat/collection.yml"
ROOMS_CONFIG_PATH = u'iceboat/rooms.yml' #房间配置文件路径
HOLOGRAME_CONFIG_PATH = u'iceboat/hologram.yml'
START_LINE_MATERIAL = Material.GLASS #启动线材料(默认玻璃)
ENDING_INTERVAL = 60 #结束时间间隔(刻)
ENDING_COUNTDOWN = 600 #结束倒计时(刻) 至少为200
ARMOR_STAND_INTERVAL = 0.26 #盔甲架间距
LOBBY_POSITION = 0, 64, 0 #大厅位置
LOBBY_RADIUS = 300 #大厅半径
BOAT_PITCH = 27
PLAYER_RADIUS = 48 #玩家渲染距离
DEFAULT_BOAT_MATERIAL = u'OAK_BOAT' #默认船类型
DEFAULT_BOAT_DISPLAY = u'橡木船' #默认船展示名
DEFAULT_PARTICLE_MATERIAL = u'END_ROD' #默认粒子材质
DEFAULT_PARTICLE_TYPE = u'END_ROD' #默认粒子类型
DEFAULT_PARTICLE_DISPLAY = u'烛光' #默认粒子展示名
DEFAULT_TITLE_DISPLAY = u'新手' #默认称号展示名
DEFAULT_TITLE_MATERIAL = u'NAME_TAG' #默认称号材质
REWARD_AMOUNT = 100 #默认奖励金额基准
ADVERTISEMENT_TEXT_1 = u"§e● DC商业梦想城官网"
ADVERTISEMENT_TEXT_2 = u"§f○ https://mcrail.top/"
SIGNS = [ #告示牌类型
Material.OAK_SIGN, Material.OAK_WALL_SIGN,
Material.SPRUCE_SIGN, Material.SPRUCE_WALL_SIGN,
Material.BIRCH_SIGN, Material.BIRCH_WALL_SIGN,
Material.JUNGLE_SIGN, Material.JUNGLE_WALL_SIGN,
Material.ACACIA_SIGN, Material.ACACIA_WALL_SIGN,
Material.DARK_OAK_SIGN, Material.DARK_OAK_WALL_SIGN,
Material.CRIMSON_SIGN, Material.CRIMSON_WALL_SIGN,
Material.WARPED_SIGN, Material.WARPED_WALL_SIGN,
Material.MANGROVE_SIGN, Material.MANGROVE_WALL_SIGN,
Material.CHERRY_SIGN, Material.CHERRY_WALL_SIGN,
Material.BAMBOO_SIGN, Material.BAMBOO_WALL_SIGN,
Material.PALE_OAK_SIGN, Material.PALE_OAK_WALL_SIGN
]
singleGames = {} #单人游戏房间
multiGames = {} #多人游戏房间
playersInParticlePreview = {} #预览玩家
playersInSpectate = {} #旁观者
playerPage = {} #玩家页数
# 调用管理类 -------------------------------------------------------------------
class PlayerManager: #玩家管理
def __init__(self, player):
self.player = player
self.playerName = player.getName()
def removeBoat(self): #移除玩家的船
boat = self.player.getVehicle() #进服时如果坐船则删船
if boat and isinstance(boat, Boat):
boat.remove()
def teleportToJoinLoc(self): #传送到加入位置
playerData = PlayerDataManager(self.playerName) #获取玩家数据
joinLoc = playerData.getJoinLoc() #获取玩家加入位置
loc = Location(worldManager.getWorld(), joinLoc[0], joinLoc[1], joinLoc[2])
self.player.teleport(loc)
def teleportToLobby(self): #传送到大厅
lobbyLoc = Location(worldManager.getWorld(), LOBBY_POSITION[0], LOBBY_POSITION[1], LOBBY_POSITION[2])
self.player.teleport(lobbyLoc)
def outGame(self): #玩家异常离开游戏处理
objective = getScoreboardObjective(u'InGame')
playerScore = objective.getScore(self.playerName) #获取玩家分数
score = playerScore.getScore() #获取分数
if score == 1: #1为玩家在游戏中
self.teleportToJoinLoc()
playerScore.setScore(0)
def leaveRoom(self): #让玩家离开房间
if self.leaveSingleRoom():
return True
if self.leaveMultiRoom():
return True
if self.leaveSpectateRoom():
return True
if self.leaveCompetition():
return True
self.player.sendTitle(u"§f", u"§c你不在任何游戏房间中", 5, 60, 10)
self.player.playSound(self.player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
def leaveSingleRoom(self):
singleRoom = self.inSingleRoom()
if singleRoom: #如果在单人房间内
singleRoom.closeRoom()
self.player.playSound(self.player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
return True
return False
def leaveMultiRoom(self):
multiRoom = self.inMultiRoom()
if multiRoom: #如果在多人房间内
multiRoom.delPlayer(self.playerName)
self.player.playSound(self.player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
return True
return False
def leaveSpectateRoom(self):
spectateRoom = self.inSpectateRoom()
if spectateRoom: #如果在旁观者房间内
spectateRoom.leave()
self.player.playSound(self.player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
return True
def leaveCompetition(self):
competition = self.inCompetition()
if competition: #如果在比赛房间内
competition.delPlayer(self.playerName)
competition.delPlayer(self.playerName)
self.player.playSound(self.player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
return True
def delayExit(self): #延迟离开载具判定
objective = getScoreboardObjective(u'IBLeaveAction')
playerScore = objective.getScore(self.playerName) #获取玩家
score = playerScore.getScore()
num = score + 1
playerScore.setScore(num)
if num == 1:
ps.scheduler.runTaskLater(lambda : playerScore.setScore(0), LEAVE_INTERVAL) #重置计分板
if num < LEAVE_INTERVAL:
remainTime = round(LEAVE_INTERVAL - num) / 20 #剩余时间
if num > 2: #前两下不反馈
self.player.sendTitle("", u"§c{:.2f} §4秒后离开船只".format(remainTime), 0, 2, 10)
return True
else:
playerScore.setScore(0)
return False
def updateInventory(self): # 更新玩家背包
worldName = self.player.getWorld().getName()
if worldName != WORLD_NAME: #只修改冰船世界的玩家背包
return
inv = self.player.getInventory() # 清空玩家背包
inv.clear()
def getItemstacks(): #异步获取物品
itemstacks = {}
itemstacks[4] = initializeItemStack(Material.SLIME_BALL, u"§e>> §a单人模式 §e<<")
itemstacks[6] = initializeItemStack(Material.MUSIC_DISC_RELIC, u"§6>> §c关闭当前音乐 §6<<", u'§7需安装冰船音乐资源包')
itemstacks[2] = initializeItemStack(Material.MAGMA_CREAM, u"§6>> §c离开房间 §6<<")
itemstacks[9] = initializeItemStack(Material.CHEST, u"§6>> §b打开收集品菜单 §6<<", u'§7设置船/粒子/称号等')
itemstacks[11] = initializeItemStack(Material.SPYGLASS, u"§6>> §b打开粒子预览页面 §6<<", u"§7仅在大厅可预览粒子效果")
itemstacks[13] = initializeItemStack(Material.ENDER_EYE, u"§6>> §b打开旁观选择页面 §6<<", u'§7选择旁观玩家')
itemstacks[15] = initializeItemStack(Material.PAPER, u"§6>> §b打开驾照等级页面 §6<<", u"§7查看驾照等级要求和升级")
itemstacks[17] = initializeItemStack(Material.SKULL_BANNER_PATTERN, u"§6>> §b打开排名查询页面 §6<<", u'§7查询全服玩家各数据排名')
return itemstacks
def setItemstacks(itemstacks): #同步设置物品
for slot, itemstack in itemstacks.items():
inv.setItem(slot, itemstack)
ps.scheduler.runSyncCallbackTask(getItemstacks, setItemstacks) #同步回调
ps.scheduler.runTaskLater(lambda s=self: self.updateInventoryPlayerInfo(), 1) #更新玩家背包玩家信息显示
self.updateInventoryPlayAgain() #更新玩家背包再来一把显示
self.updateInventoryMode(mode=u"detail")
self.updateInventoryMode(mode=u"broadcast")
self.updateInventoryMode(mode=u"music")
self.updateInventoryMode(mode=u"particle")
def updateInventoryPlayerInfo(self): #更新玩家背包玩家信息显示
worldName = self.player.getWorld().getName()
if worldName != WORLD_NAME: #只修改冰船世界的玩家背包
return
inv = self.player.getInventory()
def getItemstackPlayerInfo(): #异步获取物品
playerName = self.player.getName()
playerData = PlayerDataManager(playerName)
itemstack = initializeItemStack(playerHeadsAPI.getPlayerHead(playerName), u"§6〓 §a{} §6〓".format(self.playerName),
u'§eDC币余额 §f{}'.format(playerData.getBalance()),
u'§e完成次数 §f{}'.format(playerData.getTotalMapTimes()),
u'§e当季分数 §f{}'.format(playerData.getSeasonScore()),
u'§e驾照等级 {} §7[{}]'.format(playerData.getLevelTitle(), playerData.getLevel()),
u'§f',
u'§6>> §a点击更新 §6<<'
)
return itemstack
def setItemstackPlayerInfo(itemstack): #同步设置物品
inv.setItem(8, itemstack)
ps.scheduler.runSyncCallbackTask(getItemstackPlayerInfo, setItemstackPlayerInfo)
def updateInventoryPlayAgain(self):
worldName = self.player.getWorld().getName()
if worldName != WORLD_NAME: #只修改冰船世界的玩家背包
return
inv = self.player.getInventory()
def getItemstackPlayAgain(): #异步获取物品
playerData = PlayerDataManager(self.playerName)
lastMap = playerData.getLastMap()
mapDisplay = MapManager(lastMap).getDisplay()
itemstack = initializeItemStack(Material.NETHER_STAR, u"§6>> §a再来一把 §e{} §6<<".format(mapDisplay), u"§7快速进入上次游玩的地图(单人模式)", data=str(lastMap))
return itemstack
def setItemstackPlayAgain(itemstack): #同步设置物品
inv.setItem(0, itemstack)
ps.scheduler.runSyncCallbackTask(getItemstackPlayAgain, setItemstackPlayAgain)
def updateInventoryMode(self, mode=u"detail"):
#类型有detail/music/broadcast/particle
worldName = self.player.getWorld().getName()
if worldName != WORLD_NAME: #只修改冰船世界的玩家背包
return
inv = self.player.getInventory()
def getItemstackMode(): #异步获取物品
playerData = PlayerDataManager(self.playerName)
isOn = False
place = 28
display = u"§f未命名"
material = Material.BARRIER
if mode == u"broadcast":
isOn = playerData.getBroadcastMode()
display = u"§6>> §e切换成绩广播模式 §6<<"
description = u'§7将成绩广播给同世界玩家'
material = Material.BEACON
place = 28
elif mode == u"music":
isOn = playerData.getMusicMode()
display = u"§6>> §e切换音乐模式 §6<<"
description = u'§7需安装冰船音乐资源包'
material = Material.NOTE_BLOCK
place = 34
elif mode == u"particle":
isOn = playerData.getParticleMode()
display = u"§6>> §e切换粒子模式 §6<<"
description = u'§7使用自定义粒子效果'
material = Material.FIREWORK_STAR
place = 30
else:
isOn = playerData.getDetailMode()
display = u"§6>> §e切换详细播报模式 §6<<"
description = u'§7在聊天框播报详细信息'
material = Material.OAK_HANGING_SIGN
place = 32
lores = [u'§e当前状态 {}'.format(u"§f启用中" if isOn else u"§7未启用"), description]
itemstack = initializeItemStack(material, display, *lores)
return itemstack, place
def setItemstackMode(result): #同步设置物品
itemstack, place = result
inv.setItem(place, itemstack)
ps.scheduler.runSyncCallbackTask(getItemstackMode, setItemstackMode)
def removePlayerFromRoom(self): #将玩家从任一房间移除
for roomId, room in multiGames.items():
if self.playerName in room.playerList:
room.delPlayer(self.playerName)
if self.playerName in singleGames:
singleGames[self.playerName].closeRoom()
if self.playerName in playersInSpectate:
playersInSpectate[self.playerName].leave()
if self.playerName in competitionGame.playerList:
competitionGame.delPlayer(self.playerName)
competitionGame.delPlayer(self.playerName)
def joinSingleRoom(self, mapNum):
if (self.inAnyRoom()) or (not self.inLobby()): #房间内禁止其它操作
self.player.sendMessage(u"§c你已经在某房间中了")
self.player.playSound(self.player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
available = roomsManager.getAvailableRoomCount(mapNum)
if available > 0:
mapNum = int(mapNum)
roomNum = roomsManager.getAvailableRoom(mapNum) # 获取可用的副本编号
if roomNum is None:
self.player.sendTitle(u"§f", u"§c暂无空房", 5, 60, 10)
self.player.playSound(self.player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if self.playerName in singleGames:
self.player.sendMessage(u"§c你已经在单人模式中了")
self.player.playSound(self.player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
else:
singleGames[self.playerName] = SingleGameRoom(self.playerName, mapNum, roomNum)
roomsManager.setAvailableRoom(mapNum, roomNum, True) # 标记房间为已使用
singleGames[self.playerName].enterRunning()
return True
else:
self.player.playSound(self.player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
self.player.sendMessage(u"§c该地图暂无空房请稍后再试")
return False
def actionCooldown(self):
objective = getScoreboardObjective(u'IBActionCooldown')
playerScore = objective.getScore(self.playerName)
lastAction = playerScore.getScore()
timestamp = time.time()
currentTime = int(timestamp * 10) % 10000000
if lastAction != 0:
cooldown = currentTime - lastAction
if cooldown < 3: #0.5秒
return True
else:
playerScore.setScore(currentTime)
return False
else:
playerScore.setScore(currentTime)
return False
def joinCompetitionTeam(self):
team = getScoreboardTeam(u"Competition")
team.addEntry(self.playerName)
def leaveCompetitionTeam(self):
team = getScoreboardTeam(u"Competition")
if team.hasEntry(self.playerName):
team.removeEntry(self.playerName)
#玩家判定
def inAnyRoom(self): #检查玩家是否在任一房间内
if self.inMultiRoom() or self.inSingleRoom() or self.inSpectateRoom() or self.inCompetition():
return True
return False
def inSingleRoom(self):
if self.playerName in singleGames:
return singleGames[self.playerName]
return False
def inMultiRoom(self): #检测玩家是否在多人房间内
for room in multiGames.values():
if room.isPlayerInRoom(self.playerName):
return room #返回房间实例
return False
def inSpectateRoom(self):
if self.playerName in playersInSpectate:
return playersInSpectate[self.playerName]
return False
def inCompetition(self):
if self.playerName in competitionGame.playerList:
return competitionGame
return False
def inLobby(self): #检查玩家是否在大厅内
playerLocation = self.player.getLocation()
playerX = playerLocation.getX()
playerZ = playerLocation.getZ()
lobbyXMax = LOBBY_POSITION[0] + LOBBY_RADIUS
lobbyXMin = LOBBY_POSITION[0] - LOBBY_RADIUS
lobbyZMax = LOBBY_POSITION[2] + LOBBY_RADIUS
lobbyZMin = LOBBY_POSITION[2] - LOBBY_RADIUS
if lobbyXMin < playerX < lobbyXMax and lobbyZMin < playerZ < lobbyZMax:
return True
else:
return False
class PlayerDataManager: #玩家数据管理
def __init__(self, playerName):
self.name = playerName
self.player = Bukkit.getPlayer(self.name)
self.playerData = ps.config.loadConfig("iceboat/playerData/{}.yml".format(self.name))
def getJoinLoc(self): #获取玩家进入前位置
x = self.playerData.getDouble("info.joinLoc.x", LOBBY_POSITION[0])
y = self.playerData.getDouble("info.joinLoc.y", LOBBY_POSITION[1])
z = self.playerData.getDouble("info.joinLoc.z", LOBBY_POSITION[2])
joinLoc = x, y, z
return joinLoc
def getBoatMaterial(self): #获取玩家船类型
index = self.playerData.getString("setting.boatMaterial", None)
if index is None:
return DEFAULT_BOAT_MATERIAL
return collectionManager.getBoatMaterial(index)
def getBoatDisplay(self): #获取玩家船显示名
index = self.playerData.getString("setting.boatMaterial", None)
if index is None:
return DEFAULT_BOAT_DISPLAY
return collectionManager.getBoatDisplay(index)
def getParticleMaterial(self): #获取玩家粒子类型
index = self.playerData.getString("setting.particleType", None)
if index is None:
return DEFAULT_PARTICLE_MATERIAL
return collectionManager.getParticleMaterial(index) #获取玩家粒子类型
def getParticleType(self): #获取玩家粒子类型
index = self.playerData.getString("setting.particleType", None)
if index is None:
return DEFAULT_PARTICLE_TYPE
return collectionManager.getParticleType(index)
def getParticleInfo(self): #获取玩家粒子信息
index = self.playerData.getString("setting.particleType", None)
if index is None:
return 1, 0.0, 0.0, 0.0, 0.0, 0.0
return collectionManager.getParticleInfo(index)
def getParticleDisplay(self): #获取玩家粒子显示名
index = self.playerData.getString("setting.particleType", None)
if index is None:
return DEFAULT_PARTICLE_DISPLAY
return collectionManager.getParticleDisplay(index)
def getTitleMaterial(self): #获取玩家称号材质
index = self.playerData.getString("setting.title", None)
if index is None:
return DEFAULT_TITLE_MATERIAL
return collectionManager.getTitleMaterial(index)
def getTitleDisplay(self): #获取玩家称号
index = self.playerData.getString("setting.title", None)
if index is None:
return DEFAULT_TITLE_DISPLAY
return collectionManager.getTitleDisplay(index)
def getLevel(self): #获取玩家等级
self.level = self.playerData.getDouble("info.level", 0)
return self.level
def getLevelTitle(self): #获取玩家等级标识
level = self.getLevel()
if level < 1:
title = u'§7N'
elif level < 2:
title = u'§fL5'
elif level < 3:
title = u'§aL4'
elif level < 4:
title = u'§bL3'
elif level < 5:
title = u'§dL2'
elif level < 6:
title = u'§eL1'
else:
title = u'§cPRO'
return title
def getLevelColor(self): #获取玩家等级颜色
level = self.getLevel()
if level < 1:
colorCode = u"§7"
elif level < 2:
colorCode = u"§f"
elif level < 3:
colorCode = u"§a"
elif level < 4:
colorCode = u"§b"
elif level < 5:
colorCode = u"§d"
elif level < 6:
colorCode = u"§e"
else:
colorCode = u"§c"
return colorCode
def getBalance(self): #获取玩家余额
if vaultEconomy:
return vaultEconomy.getBalance(self.player)
return 0
def getParticleMode(self): #获取玩家粒子模式
mode = self.playerData.getBoolean("setting.particleMode", True)
return mode
def getMusicMode(self): #获取玩家音乐模式
mode = self.playerData.getBoolean("setting.musicMode", False)
return mode
def getDetailMode(self): #获取玩家详细信息模式
mode = self.playerData.getBoolean("setting.detailMode", False)
return mode
def getBroadcastMode(self): #获取玩家广播模式
mode = self.playerData.getBoolean("setting.broadcastMode", True)
return mode
def getOwnedItems(self, type='boats'):
self.ownedItems = self.playerData.getList("info.{}".format(type), [])
return self.ownedItems
def getTotalMapTimes(self): #获取玩家总完成次数
self.totalMapTimes = self.playerData.getInt("info.totalMapTimes", 0)
return self.totalMapTimes
def getSingleBestMapRecord(self, mapNum): #获取玩家单人最好成绩
self.singleBestMapRecord = self.playerData.getDouble("record.{}.singleBestMapRecord".format(mapNum), 0)
return self.singleBestMapRecord
def getSingleBestMapRecordTimestamp(self, mapNum): #获取玩家单人最好成绩时间戳
self.singleBestMapRecordTimestamp = self.playerData.getDouble("record.{}.singleBestMapRecordTimestamp".format(mapNum), 0)
return self.singleBestMapRecordTimestamp
def getSingleBestMapRecordDay(self, mapNum): #获取玩家单人最好成绩日期
timestamp = self.getSingleBestMapRecordTimestamp(mapNum)
if timestamp == 0:
return u'暂无'
day = time.strftime("%Y/%m/%d", time.localtime(timestamp))
return day
def getSingleBestMapRecordPointTimer(self, mapNum, lap, point): #获取单人最佳纪录某个点位的时间
return self.playerData.getDouble('record.{}.singleBestMapRecordPointsTimer.{}.{}'.format(mapNum, lap, point), 0)
def getMultiBestMapRecord(self, mapNum): #获取玩家多人最好成绩
self.multiBestMapRecord = self.playerData.getDouble("record.{}.multiBestMapRecord".format(mapNum), 0)
return self.multiBestMapRecord
def getMultiBestMapRecordTimestamp(self, mapNum): #获取玩家多人最好成绩时间戳
self.multiBestMapRecordTimestamp = self.playerData.getDouble("record.{}.multiBestMapRecordTimestamp".format(mapNum), 0)
return self.multiBestMapRecordTimestamp
def getMultiBestMapRecordDay(self, mapNum): #获取玩家多人最好成绩日期
timestamp = self.getMultiBestMapRecordTimestamp(mapNum)
if timestamp == 0:
return u'暂无'
day = time.strftime("%Y/%m/%d", time.localtime(timestamp))
return day
def getMultiBestMapRecordPointTimer(self, mapNum, lap, point): #获取多人最佳纪录某个点位的事件
return self.playerData.getDouble('record.{}.multiBestMapRecordPointsTimer.{}.{}'.format(mapNum, lap, point), 0)
def getSeasonBestMapRecord(self, mapNum): #获取玩家当前季节最好成绩
self.seasonBestMapRecord = self.playerData.getDouble("record.{}.seasonBestMapRecord".format(mapNum), 0)
return self.seasonBestMapRecord
def getSeasonMapScore(self, mapNum): #获取玩家当前季节成绩
self.seasonMapScore = self.playerData.getDouble("record.{}.seasonMapScore".format(mapNum), 0)
return self.seasonMapScore
def getSeasonScore(self):
self.seasonScore = self.playerData.getDouble("info.seasonScore", 0.0)
return self.seasonScore
def getMapTimes(self, mapNum): #获取玩家完成地图次数
self.mapTimes = self.playerData.getInt("record.{}.mapTimes".format(mapNum), 0)
return self.mapTimes
def getLastMap(self): #获取玩家上一次完成的地图
self.lastMap = self.playerData.getInt("info.lastMap", 1)
return self.lastMap
def setJoinLoc(self, x=0, y=64, z=0): #设置玩家的进入位置
self.playerData.set('info.joinLoc.x', x)
self.playerData.set('info.joinLoc.y', y)
self.playerData.set('info.joinLoc.z', z)
self.playerData.save()
def setBoatMaterial(self, boatMaterial): #设置玩家船类型
self.playerData.set("setting.boatMaterial", boatMaterial)
self.playerData.save()
def setParticleType(self, particleType): #设置玩家粒子类型
self.playerData.set("setting.particleType", particleType)
self.playerData.save()
def setTitle(self, title): #设置玩家称号
self.playerData.set("setting.title", title)
self.playerData.save()
ps.scheduler.runTaskLater(lambda h=hallOfFameManager:hallOfFameManager.updateHallOfFameByName(self.name), 40)
def setLevel(self, level): #设置玩家等级
self.playerData.set("info.level", level)
self.playerData.save()
playerName = self.player.getName()
rankManager.updateRankingAsyn(playerName, level, None, recordType=u'level')
ps.scheduler.runTaskLater(lambda h=hallOfFameManager:hallOfFameManager.updateHallOfFameByName(self.name), 40)
def setTotalMapTimes(self, totalMapTimes): #设置玩家总完成次数
self.playerData.set("info.totalMapTimes", totalMapTimes)
self.playerData.save()
def setMapTimes(self, mapNum, mapTimes): #设置玩家完成地图次数
self.playerData.set("record.{}.mapTimes".format(mapNum), mapTimes)
self.playerData.save()
def setLastMap(self, mapNum): #设置玩家上一次完成的地图
self.playerData.set("info.lastMap", int(mapNum))
self.playerData.save()
playerManager = PlayerManager(self.player)
playerManager.updateInventoryPlayAgain()
def setSingleBestMapRecord(self, mapNum, singleBestMapRecord): #设置玩家单人最好成绩
self.playerData.set("record.{}.singleBestMapRecord".format(mapNum), singleBestMapRecord)
self.playerData.save()
def setSingleBestMapRecordTimestamp(self, mapNum, singleBestMapRecordTimestamp): #设置玩家单人最好成绩时间戳
self.playerData.set("record.{}.singleBestMapRecordTimestamp".format(mapNum), singleBestMapRecordTimestamp)
self.playerData.save()
def setSingleBestMapRecordPointsTimer(self, mapNum, singleBestMapRecordPointsTimer): #设置玩家单人最好成绩检查点
self.playerData.set("record.{}.singleBestMapRecordPointsTimer".format(mapNum), singleBestMapRecordPointsTimer)
self.playerData.save()
def setMultiBestMapRecord(self, mapNum, multiBestMapRecord): #设置玩家多人最好成绩
self.playerData.set("record.{}.multiBestMapRecord".format(mapNum), multiBestMapRecord)
self.playerData.save()
def setMultiBestMapRecordTimestamp(self, mapNum, multiBestMapRecordTimestamp): #设置玩家多人最好成绩时间戳
self.playerData.set("record.{}.multiBestMapRecordTimestamp".format(mapNum), multiBestMapRecordTimestamp)
self.playerData.save()
def setMultiBestMapRecordPointsTimer(self, mapNum, multiBestMapRecordPointsTimer): #设置玩家多人最好成绩检查点
self.playerData.set("record.{}.multiBestMapRecordPointsTimer".format(mapNum), multiBestMapRecordPointsTimer)
self.playerData.save()
def setSeasonBestMapRecord(self, mapNum, seasonBestMapRecord): #设置玩家当前季节最好成绩
self.playerData.set("record.{}.seasonBestMapRecord".format(mapNum), seasonBestMapRecord)
self.playerData.save()
def setSeasonMapScore(self, mapNum, seasonMapScore): #设置玩家当前季节成绩
self.playerData.set("record.{}.seasonMapScore".format(mapNum), seasonMapScore)
self.playerData.save()
seasonMaps = seasonManager.getSeasonMaps()
score = 0.0
for mapNum in seasonMaps:
mapScore = self.getSeasonMapScore(int(mapNum))
score += mapScore
self.playerData.set("info.seasonScore", score)
self.playerData.save()
rankManager.updateRankingAsyn(self.name, score, u"seasonScore") #更新季节分数排名
ps.scheduler.runTaskLater(lambda h=hallOfFameManager:hallOfFameManager.updateHallOfFameByName(self.name), 40)
def setOwnedItems(self, type='boats', items=[]): #设置玩家拥有的收集品
if type not in ['boats', 'particles', 'titles']:
return False
self.playerData.set("info.{}".format(type), [str(item) for item in items])
self.playerData.save()
return True
def toggleModeBoolean(self, mode='detailMode'): #切换模式布尔值
if mode not in ['particleMode', 'musicMode', 'detailMode', 'broadcastMode']:
return False
if mode in ['broadcastMode', 'particleMode']:
currentValue = self.playerData.getBoolean(u"setting.{}".format(mode), True)
else:
currentValue = self.playerData.getBoolean(u"setting.{}".format(mode), False)
newValue = not currentValue
self.playerData.set(u"setting.{}".format(mode), newValue)
self.playerData.save()
return newValue
class MapManager: #地图管理
def __init__(self, mapNum):
self.mapNum = mapNum
self.mapData = ps.config.loadConfig("iceboat/maps/{}.yml".format(mapNum))
def getLap(self): #获取地图圈数
self.lap = self.mapData.getInt("lap", 1) #地图圈数
return self.lap
def getYaw(self): #获取地图偏航角
self.yaw = self.mapData.getDouble("yaw", 0.0) #地图偏航角
return self.yaw
def getPoint(self): #获取地图点位数
self.point = self.mapData.getInt("point", 2) #地图点位数
return self.point
def getDisplay(self):
self.display = self.mapData.getString("display", u'未命名') #地图名字
return self.display
def getMaterial(self): #获取地图显示材质
self.material = self.mapData.getString("material", "PAPER") #地图显示材质
return self.material
def getDifficulty(self): #获取地图难度
self.difficulty = float(self.mapData.getString("difficulty", "0.0")) #地图难度
return self.difficulty
def getDifficultySuffix(self): #获取地图难度后缀
self.difficulty = float(self.mapData.getString("difficulty", "0.0")) #地图难度
if 1 <= self.difficulty < 2:
suffix = u"§a★"
elif 2 <= self.difficulty < 3:
suffix = u"§b★★"
elif 3 <= self.difficulty < 4:
suffix = u"§d★★★"
elif 4 <= self.difficulty < 5:
suffix = u"§e★★★★"
elif 5 <= self.difficulty:
suffix = u"§c★★★★★"
else:
suffix = u"§f★"
return suffix
def getCreator(self): #获取地图创建者
self.creator = self.mapData.getString("creator", "DreamCityToger") #地图创建者
return self.creator
def getPoints(self): #获取地图所有点位坐标
points = self.mapData.getConfigurationSection('points')
return points
# 全局管理类 -------------------------------------------------------------------
class WorldManager: #世界管理
def __init__(self):
self.world = getServer().getWorld(WORLD_NAME)
def getWorld(self): #获取世界对象
return self.world
class CollectionManager: #收集品管理
def __init__(self):
self.collectionData = ps.config.loadConfig(COLLECTION_CONFIG_PATH)
self.boats = self.collectionData.get("boats", {})
self.particles = self.collectionData.get("particles", {})
self.titles = self.collectionData.get("titles", {})
def getBoatMaterial(self, index): #获取对应的船的材质
return self.boats.getString(u"{}.material".format(index), DEFAULT_BOAT_MATERIAL)
def getBoatDisplay(self, index): #获取对应的船的显示名
return self.boats.getString(u'{}.display'.format(index), DEFAULT_BOAT_DISPLAY)
def getParticleType(self, index): #获取对应的粒子类型
return self.particles.getString(u'{}.type'.format(index), DEFAULT_PARTICLE_TYPE)
def getParticleInfo(self, index): #获取对应的粒子信息
count = self.particles.getInt(u'{}.count'.format(index), 1)
rx = self.particles.getDouble(u'{}.rx'.format(index), 0.0)
ry = self.particles.getDouble(u'{}.ry'.format(index), 0.0)
rz = self.particles.getDouble(u'{}.rz'.format(index), 0.0)
speed = self.particles.getDouble(u'{}.speed'.format(index), 0.0)
oy = self.particles.getDouble(u'{}.oy'.format(index), 0.0)
return count, rx, ry, rz, speed, oy
def getParticleDisplay(self, index): #获取对应的粒子的显示名
return self.particles.getString(u'{}.display'.format(index), DEFAULT_PARTICLE_DISPLAY)
def getParticleMaterial(self, index): #获取对应的粒子的材质
return self.particles.getString(u'{}.material'.format(index), DEFAULT_PARTICLE_MATERIAL)
def getTitleMaterial(self, index): #获取对应的称号的材质
return self.titles.getString(u'{}.material'.format(index), DEFAULT_TITLE_MATERIAL)
def getTitleDisplay(self, index): #获取对应的称号
return self.titles.getString(u'{}.display'.format(index), DEFAULT_TITLE_DISPLAY)
def getAllItems(self, itemType='boats'):
if itemType == 'boats':
return self.getAllBoats()
elif itemType == 'particles':
return self.getAllParticles()
elif itemType == 'titles':
return self.getAllTitles()
else:
return None # 无效的 itemType
def getAllBoats(self): #获取所有船
return self.boats.getKeys(False)
def getAllParticles(self): #获取所有粒子
return self.particles.getKeys(False)
def getAllTitles(self): #获取所有称号
return self.titles.getKeys(False)
def getCollectionCondition(self, type, index): #获取收集品价格
itemSection = self.collectionData.get(type)
if itemSection is not None:
iteminfo = itemSection.get(str(index))
if iteminfo is not None:
notForSale = iteminfo.get("notForSale", False)
if notForSale:
return ["notForSale", notForSale]
price = iteminfo.get("cost", None)
if price is not None:
return ["cost", price]
level = iteminfo.get("level", None)
if level is not None:
return ["level", level]
record = iteminfo.get("record", None)
if record is not None:
maps = list(record.getKeys(False))
if maps is not None:
recordDict = {}
for mapNum in maps:
mapRecord = record.getDouble(u"{}.record".format(str(mapNum)))
if mapRecord is not None:
recordDict[mapNum] = mapRecord
return ["record", recordDict]
times = iteminfo.get("times", None)
if times is not None:
maps = list(times.getKeys(False))
if maps is not None:
timesDict = {}
for mapNum in maps:
mapTimes = times.getInt(u"{}.times".format(str(mapNum)))
if mapTimes is not None:
timesDict[mapNum] = mapTimes
return ["times", timesDict]
return ["cost", 0] # 默认类型为cost价格为 0
def getCollectionLore(self, type, index): #获取收集品描述
itemSection = self.collectionData.get(type)
if itemSection is not None:
iteminfo = itemSection.get(str(index))
if iteminfo is not None:
lore = iteminfo.get("lore", [])
if lore is not None:
return lore
return None
def getDisplay(self, itemType, itemName): #获取收集品显示名
itemSection = self.collectionData.get(itemType)
if itemSection is not None:
iteminfo = itemSection.get(str(itemName))
if iteminfo is not None:
display = iteminfo.get("display")
if display is not None:
return display
return itemName # 默认显示名为 itemName
def getMaterial(self, itemType, itemName): #获取收集品显示材质
itemSection = self.collectionData.get(itemType)
if itemSection is not None:
iteminfo = itemSection.get(str(itemName))
if iteminfo is not None:
material = iteminfo.get("material")
if material is not None:
return material
return u'BARRIER'
class PlayersManager: #全服玩家名单管理
def __init__(self):
self.playersConfig = ps.config.loadConfig("iceboat/players.yml")
def getPlayers(self): #获取所有玩家
self.players = list(self.playersConfig.getKeys(False))
return self.players
def inPlayers(self, playerName): #判断玩家是否存在于全服名单
players = self.getPlayers() #获取所有玩家
if playerName in players: #如果玩家已存在于玩家列表
return True
return False
def addPlayer(self, playerName): #新增玩家到全服名单
if self.inPlayers(playerName): #如果玩家已存在于玩家列表
return
self.playersConfig.set(playerName, {}) #新增玩家到全服名单
self.playersConfig.save()
def addPlayerAsync(self, playerName): #异步执行新增玩家到全服名单
ps.scheduler.runTaskAsync(lambda s=self,pN=playerName: s.addPlayer(pN))
class SeasonManager: #季节管理
def __init__(self):
self.seasonData = ps.config.loadConfig("iceboat/season.yml")
def getSeasonMaps(self): #获取当前季节地图列表
self.seasonMaps = list(self.seasonData.get("maps").getKeys(False))
return self.seasonMaps
def getSeasonFirstMap(self): #获取当前季节第一图
self.seasonMaps = self.getSeasonMaps() #获取当前季节地图列表
self.seasonFirstMap = self.seasonMaps[0] #获取当前季节第一图
if self.seasonFirstMap:
return self.seasonFirstMap
return int(1)
def inSeasonMaps(self, mapNum): #判断地图是否存在于当前季节
seasonMaps = self.getSeasonMaps() #获取当前季节地图列表
if str(mapNum) in seasonMaps: #如果地图已存在于当前季节
return True
return False
def getSeasonStandard(self, mapNum):
self.seasonStandard = self.seasonData.get("maps").getDouble(str(mapNum), 0.0)
return self.seasonStandard
def resetSeasonRecords(self):
playersPath = u"iceboat/players.yml"
playersConfig = ps.config.loadConfig(playersPath)
players = list(playersConfig.getKeys(False))
for playerName in players:
playerDataPath = u"iceboat/playerData/{}.yml".format(playerName)
playerData = ps.config.loadConfig(playerDataPath)
playerData.set("info.seasonScore", 0)
recordSection = playerData.get("record", {})
if recordSection is not None:
if hasattr(recordSection, 'getKeys'):
maps = list(recordSection.getKeys(False))
else:
maps = list(recordSection.keys())
for mapNum in maps:
mapSection = recordSection.get(mapNum)
if mapSection is not None:
mapSection.set("seasonBestMapRecord", 0)
mapSection.set("seasonMapScore", 0)
playerData.save()
seasonRankPath = u'iceboat/ranking/seasonScore.yml'
seasonRankConfig = ps.config.loadConfig(seasonRankPath)
players = list(seasonRankConfig.getKeys(False))
players.remove("0")
for playerName in players:
seasonRankConfig.set("{}.score".format(playerName), 0.0)
seasonRankConfig.save()
class MapsManager: #全地图管理
def __init__(self):
self.mapNumbers = []
mapNum = 1 #从1号图开始检索
while True:
mapFilePath = u"iceboat/maps/{}.yml".format(mapNum)
if ps.config.doesConfigExist(mapFilePath):
self.mapNumbers.append(str(mapNum))
mapNum += 1
else:
break
mapNum = 2999 #从2999号图开始反着检索
while True:
mapFilePath = u"iceboat/maps/{}.yml".format(mapNum)
if ps.config.doesConfigExist(mapFilePath):
self.mapNumbers.append(str(mapNum))
mapNum -= 1
else:
break
def getMapsNum(self):
return self.mapNumbers
class RoomsManager: #可用房间管理
def __init__(self):
self.roomsData = ps.config.loadConfig(ROOMS_CONFIG_PATH)
def getAvailableRoomCount(self, mapNum): #获取指定地图的可用房间数量
mapRooms = self.roomsData.get(str(mapNum), {})
if hasattr(mapRooms, 'getKeys'):
keys = mapRooms.getKeys(False) # 获取所有子键
availableRooms = sum(1 for key in keys if mapRooms.get(key) is False)
return availableRooms
def getTotalRoomCount(self, mapNum): #获取指定地图的总房间数量
mapRooms = self.roomsData.get(str(mapNum), {})
if hasattr(mapRooms, 'getKeys'):
keys = mapRooms.getKeys(False) # 获取所有子键
totalRooms = len(keys)
return totalRooms
def getAvailableRoom(self, mapNum): #获取指定地图的可用房间编号
mapRooms = self.roomsData.get(str(mapNum), {})
if hasattr(mapRooms, 'getKeys'):
keys = mapRooms.getKeys(False) # 获取所有子键
for key in keys:
if mapRooms.get(key) is False:
return key
return None
def setAvailableRoom(self, mapNum, roomNum, using=True): #设置指定地图的指定房间为可用
mapRooms = self.roomsData.get(str(mapNum), {})
if hasattr(mapRooms, 'getKeys'):
keys = mapRooms.getKeys(False) # 获取所有子键
if roomNum in keys:
mapRooms.set(roomNum, using)
self.roomsData.save()
return True
return False
def resetAvailableRoom(self, mapNum): #重置指定地图的可用房间
mapRooms = self.roomsData.get(str(mapNum), {})
if hasattr(mapRooms, 'getKeys'):
keys = mapRooms.getKeys(False) # 获取所有子键
for key in keys:
mapRooms.set(key, False)
return True
return False
def resetAvailableRooms(self): #重置所有可用房间
self.roomsNum = self.roomsData.getKeys(False) #所有房间编号
for mapNum in self.roomsNum:
self.resetAvailableRoom(mapNum)
class LevelManager: #驾照等级管理
def __init__(self):
self.levelData = ps.config.loadConfig("iceboat/level.yml")
self.levels = self.levelData.getKeys(False)
def getAllLevels(self): #获取所有等级
return self.levels
def getAvailableLevels(self, playerLevel): #获取可解锁的等级
availableLevels = []
for level in self.levels:
if self.getLevelNeed(level) <= playerLevel:
if self.getLevelTarget(level) > playerLevel:
availableLevels.append(level)
return availableLevels
def getLevelDisplay(self, index): #获取等级显示名
return self.levelData.getString(u"{}.display".format(index), u'未命名')
def getLevelNeed(self, index): #获取需要达到的等级
return self.levelData.getDouble(u"{}.need".format(index), 0.0)
def getLevelTarget(self, index): #获取目标等级
return self.levelData.getDouble(u"{}.target".format(index), 0.0)
def getLevelMaterial(self, index): #获取等级显示材质
return self.levelData.getString(u"{}.material".format(index), u'BARRIER')
def getLevelRecords(self, index): #获取等级成绩要求
record = self.levelData.getConfigurationSection(u"{}.record".format(index))
if record is not None:
maps = list(record.getKeys(False))
if maps is not None:
recordDict = {}
for mapNum in maps:
mapRecord = record.getDouble(u"{}.record".format(str(mapNum)))
if mapRecord is not None:
recordDict[mapNum] = mapRecord
return recordDict
return None
class RankManager: #排名管理
def __init__(self):
pass
def getSingleMapRecordRank(self, playerName, mapNum=None): #获取单人最好成绩排名
if mapNum is None:
return 0
config = ps.config.loadConfig("iceboat/ranking/singleMapRecord.yml")
playerData = config.get(u"{}.{}".format(mapNum, playerName))
if playerData is not None:
mapRankData = config.get(str(mapNum))
if mapRankData:
currentKey = playerName
rank = 0
while currentKey != '0':
prevKey = mapRankData.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def getMultiMapRecordRank(self, playerName, mapNum=None): #获取多人最好成绩排名
if mapNum is None:
return 0
config = ps.config.loadConfig("iceboat/ranking/multiMapRecord.yml")
playerData = config.get(u"{}.{}".format(mapNum, playerName))
if playerData is not None:
mapRankData = config.get(str(mapNum))
if mapRankData:
currentKey = playerName
rank = 0
while currentKey!= '0':
prevKey = mapRankData.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def getMapTimesRank(self, playerName, mapNum=None): #获取完成次数排名
if mapNum is None:
return 0
config = ps.config.loadConfig("iceboat/ranking/mapTimes.yml")
playerData = config.get(u"{}.{}".format(mapNum, playerName))
if playerData is not None:
mapRankData = config.get(str(mapNum))
if mapRankData:
currentKey = playerName
rank = 0
while currentKey!= '0':
prevKey = mapRankData.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def getSeasonScoreRank(self, playerName): #获取季节分数排名
config = ps.config.loadConfig("iceboat/ranking/seasonScore.yml")
playerData = config.get(u"{}".format(playerName))
if playerData is not None:
currentKey = playerName
rank = 0
while currentKey!= '0':
prevKey = config.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def getLevelRank(self, playerName): #获取等级排名
config = ps.config.loadConfig("iceboat/ranking/level.yml")
playerData = config.get(u"{}".format(playerName))
if playerData is not None:
currentKey = playerName
rank = 0
while currentKey!= '0':
prevKey = config.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def getTotalMapTimesRank(self, playerName): #获取总完成次数排名
config = ps.config.loadConfig("iceboat/ranking/totalMapTimes.yml")
playerData = config.get(u"{}".format(playerName))
if playerData is not None:
currentKey = playerName
rank = 0
while currentKey!= '0':
prevKey = config.get(currentKey, {}).get('prev')
if prevKey == '0':
rank += 1
break
elif prevKey:
currentKey = prevKey
rank += 1
return rank
return 0
def updateRankingAsyn(self, playerName, newScore, mapNum=None, recordType=u"seasonScore"): #异步更新
ps.scheduler.runTaskAsync(lambda: self.updateRanking(playerName, newScore, mapNum, recordType))
def updateRanking(self, playerName, newScore, mapNum=None, recordType=u"seasonScore"):
if recordType == u"seasonScore":
filePath = u'iceboat/ranking/seasonScore.yml'
smallToBig = False
mapNum = None # 不需要地图编号
elif recordType == u"single":
filePath = u'iceboat/ranking/singleMapRecord.yml'
smallToBig = True
elif recordType == u"multi":
filePath = u'iceboat/ranking/multiMapRecord.yml'
smallToBig = True
elif recordType == u"mapTimes":
filePath = u'iceboat/ranking/mapTimes.yml'
smallToBig = False
elif recordType == u"level":
filePath = u'iceboat/ranking/level.yml'
smallToBig = False
mapNum = None # 不需要地图编号
elif recordType == u"totalMapTimes":
filePath = u'iceboat/ranking/totalMapTimes.yml'
smallToBig = False
mapNum = None # 不需要地图编号
else:
return
config = ps.config.loadConfig(filePath)
if mapNum is None: #处理非地图类排名
players = list(config.getKeys(False))
if '0' not in players: # 文件初始化
initial = {
'score': 0,
'prev': '0',
'next': '0'
}
config.set("0", initial)
players.append('0') #为玩家列表增加头指针
config.save()
config = ps.config.loadConfig(filePath) #重新加载
if playerName in players: # 纪录刷新
score = config.get(u"{}.score".format(playerName))
if smallToBig:
if newScore >= score:
return
else:
if newScore <= score:
return
config.set(u"{}.score".format(playerName), newScore) #更新成绩
else: # 玩家数据初始化
playerData = {
'score': newScore,
'prev': playerName,
'next': playerName
}
config.set(u"{}".format(playerName), playerData)
config.save()
config = ps.config.loadConfig(filePath) #重新加载
players.append(playerName)
ranking = {}
for key in players: # 获取字典
ranking[key] = config.get(key).getValues(True)
prevPlayer = ranking[playerName]['prev']
nextPlayer = ranking[playerName]['next']
ranking[prevPlayer]['next'] = nextPlayer
ranking[nextPlayer]['prev'] = prevPlayer
config.set(u"{}.next".format(prevPlayer), nextPlayer)
config.set(u"{}.prev".format(nextPlayer), prevPlayer)
#从排名靠后不断往前检查
currentPlayer = prevPlayer if prevPlayer != playerName else ranking['0']['prev']
# 找到合适的插入位置
if smallToBig:
while currentPlayer != '0' and ranking[currentPlayer]['score'] > newScore:
currentPlayer = ranking[currentPlayer]['prev']
else:
while currentPlayer!= '0' and ranking[currentPlayer]['score'] < newScore:
currentPlayer = ranking[currentPlayer]['prev']
prevPlayer = currentPlayer
nextPlayer = ranking[currentPlayer]['next']
config.set(u"{}.prev".format(playerName), prevPlayer)
config.set(u"{}.next".format(playerName), nextPlayer)
config.set(u"{}.next".format(prevPlayer), playerName)
config.set(u"{}.prev".format(nextPlayer), playerName)
config.save()
else: #处理地图类排名
maps = list(config.getKeys(False))
mapNum = str(mapNum)
if mapNum not in maps: #地图初始化
initial = {
'score': 0,
'prev': '0',
'next': '0'
}
config.set("{}.0".format(mapNum), initial)
config.save()
config = ps.config.loadConfig(filePath) #重新加载
maps.append(mapNum)
mapSection = config.get(u"{}".format(mapNum))
players = list(mapSection.getKeys(False)) #获取所有玩家名
if playerName in players: # 纪录没有刷新
score = config.get(u"{}.{}.score".format(mapNum, playerName))
if smallToBig:
if newScore >= score:
return
else:
if newScore <= score:
return
config.set(u"{}.{}.score".format(mapNum, playerName), newScore) # 更新玩家分数
else:
playerData = {
'score': newScore,
'prev': str(playerName),
'next': str(playerName)
}
config.set(u"{}.{}".format(mapNum, str(playerName)), playerData)
config.save()
config = ps.config.loadConfig(filePath) #重新加载
players.append(playerName)
mapRanking = {} # 获取字典
for key in players:
value = mapSection.get(key)
if hasattr(value, 'getValues'):
value = value.getValues(False)
mapRanking[key] = value
prevPlayer = mapRanking[playerName]['prev']
nextPlayer = mapRanking[playerName]['next']
mapRanking[prevPlayer]['next'] = nextPlayer
mapRanking[nextPlayer]['prev'] = prevPlayer
config.set(u"{}.{}.next".format(mapNum, prevPlayer), nextPlayer)
config.set(u"{}.{}.prev".format(mapNum, nextPlayer), prevPlayer)
currentPlayer = prevPlayer if prevPlayer != playerName else mapRanking['0']['prev']
# 找到合适的插入位置
if smallToBig:
while currentPlayer!= '0' and mapRanking[currentPlayer]['score'] > newScore:
currentPlayer = mapRanking[currentPlayer]['prev']
else:
while currentPlayer!= '0' and mapRanking[currentPlayer]['score'] < newScore:
currentPlayer = mapRanking[currentPlayer]['prev']
prevPlayer = currentPlayer
nextPlayer = mapRanking[currentPlayer]['next']
config.set(u"{}.{}.prev".format(mapNum, playerName), prevPlayer)
config.set(u"{}.{}.next".format(mapNum, playerName), nextPlayer)
config.set(u"{}.{}.next".format(mapNum, prevPlayer), playerName)
config.set(u"{}.{}.prev".format(mapNum, nextPlayer), playerName)
config.save()
class HallOfFameManager:
def __init__(self):
self.fameConfig = ps.config.loadConfig("iceboat/fame.yml")
self.world = Bukkit.getWorld(WORLD_NAME)
self.armorStandEntities = []
def updateHallOfFame(self):
self.clearHallOfFame() # 清除旧的名人堂实体
def getList():
config = ps.config.loadConfig("iceboat/ranking/seasonScore.yml")
if config.get('0') is None: #初始化
config.set("0", {
'score': 0,
'prev': '0',
'next': '0'
})
config.save()
currentPlayer = config.get('0').get('next')
players = []
for _ in range(16):
if currentPlayer == '0':
break
players.append(currentPlayer)
currentPlayer = config.get(currentPlayer).get('next')
# 获取展示坐标
positions = []
for rank in range(1, len(players) + 1):
positionConfig = self.fameConfig.get(str(rank))
if positionConfig:
position = (
positionConfig.getDouble('x'),
positionConfig.getDouble('y'),
positionConfig.getDouble('z'),
positionConfig.getDouble('yaw'),
positionConfig.getDouble('pitch')
)
positions.append(position)
pack = {
u'players': players,
u'positions': positions,
u'config': config,
}
return pack
def spawnArmorStands(pack):
players = pack.get(u'players')
positions = pack.get(u'positions')
config = pack.get(u'config')
for index, playerName in enumerate(players):
if index >= len(positions):
break
position = positions[index]
x = position[0]
y = position[1]
z = position[2]
yaw = position[3]
pitch = position[4]
loc = Location(self.world, x, y, z, yaw, pitch)
seasonScore = config.get(playerName).get('score')
playerDataManager = PlayerDataManager(playerName)
color = playerDataManager.getLevelColor()
titleDisplay = playerDataManager.getTitleDisplay()
playerHeadItem = playerHeadsAPI.getPlayerHead(playerName)
def createArmorStand(customName):
# 创建 ArmorStand 实体
armorStand = self.world.spawn(loc, ArmorStand) #先在底部创造
armorStand.setGravity(False) #设置无重力
armorStand.setVisible(False) #设置不可见
armorStand.setInvulnerable(True)
armorStand.setCollidable(False) #设置无碰撞箱
armorStand.setCustomNameVisible(True) #设置显示名可见
armorStand.setCustomName(customName)
self.armorStandEntities.append(armorStand) #将展示框添加到房间
return armorStand
loc.add(Vector(0, 0.26, 0))
customName = u"§a§l第 §e§l{} §a§l名".format(index+1)
createArmorStand(customName)
loc.add(Vector(0, -0.26, 0))
customName = u"{}[{}]§f{}".format(color, titleDisplay, playerName)
createArmorStand(customName)
loc.add(Vector(0, -0.26, 0))
customName = u"§f{:.2f}".format(seasonScore)
armorStand = createArmorStand(customName)
armorStand.getEquipment().setHelmet(playerHeadItem)
ps.scheduler.runSyncCallbackTask(getList, spawnArmorStands)
def updateHallOfFameByName(self, playerName):
isInHallOfFame = hallOfFameManager.isInHallOfFame(playerName)
if isInHallOfFame: #如果玩家在名人堂则更新
hallOfFameManager.updateHallOfFame()
def clearHallOfFame(self):
for armorStand in self.armorStandEntities:
if armorStand and not armorStand.isDead():
armorStand.remove()
self.armorStandEntities = []
def isInHallOfFame(self, playerName):
config = ps.config.loadConfig("iceboat/ranking/seasonScore.yml")
if config.get('0') is None: #初始化
config.set("0", {
'score': 0,
'prev': '0',
'next': '0'
})
config.save()
currentPlayer = config.get('0').get('next')
for _ in range(16):
if currentPlayer == '0':
break
if currentPlayer == playerName:
return True
else:
currentPlayer = config.get(currentPlayer).get('next')
return False
worldManager = WorldManager() #初始化世界管理
collectionManager = CollectionManager() #收集品管理
playersManager = PlayersManager() #玩家管理
seasonManager = SeasonManager() #季节管理
mapsManager = MapsManager() #全地图管理
roomsManager = RoomsManager() #可用房间管理
levelManager = LevelManager() #驾照等级管理
rankManager = RankManager() #排名管理
hallOfFameManager = HallOfFameManager() #名人堂管理
guiManager = GUIManager() #菜单管理
# 房间相关 -------------------------------------------------------------------
class IceboatMap: #地图类
def __init__(self, mapNum, maproomNum):
self.mapNum = int(mapNum)
self.maproomNum = int(maproomNum)
self.world = getServer().getWorld(WORLD_NAME)
#地图配置数据
self.mapData = MapManager(self.mapNum)
self.yaw = self.mapData.getYaw()
self.lap = self.mapData.getLap()
self.point = self.mapData.getPoint()
self.display = self.mapData.getDisplay()
self.difficulty = self.mapData.getDifficulty()
#地图坐标字典集
self.pointPositionDict = {}
points = self.mapData.getPoints()
#计算坐标 join
dimensions = []
for axis in ['x', 'y', 'z']:
a = points.getInt('join.{}'.format(axis))
if axis == 'x':
a = a + (self.mapNum * 10000)
if axis == 'z':
a = a + (self.maproomNum * 10000)
dimensions.append(a)
self.pointPositionDict['join'] = list(dimensions)
self.pointPositionDict['join'].append(self.yaw)
self.pointPositionDict['join'].append(0) #pitch 0
#计算区间 glass ready points
for key in ['glass', 'ready'] + list(range(1, int(self.point) + 1)):
mins, maxs = [], []
for axis in ['x', 'y', 'z']:
a, b = sorted([
points.getInt('{}.pos1.{}'.format(key, axis)),
points.getInt('{}.pos2.{}'.format(key, axis))
])
if axis == 'x':
a += self.mapNum * 10000
b += self.mapNum * 10000
if axis == 'z':
a += self.maproomNum * 10000
b += self.maproomNum * 10000
mins.append(a)
maxs.append(b)
self.pointPositionDict['{}'.format(key)] = tuple(mins + maxs)
def initialSpecialMap(self): #初始化特殊地图
if self.mapNum == 9: #西南克克十字城
world = getServer().getWorld(WORLD_NAME)
x = 95000
y = 54
z = 5000 + (self.maproomNum * 10000)
block = world.getBlockAt(x, y, z)
blockType = block.getType()
if blockType != Material.REDSTONE_BLOCK:
ps.scheduler.runTaskLater(lambda: block.setType(Material.REDSTONE_BLOCK), 20)
else:
ps.scheduler.runTaskLater(lambda: block.setType(Material.AIR), 10)
ps.scheduler.runTaskLater(lambda: block.setType(Material.REDSTONE_BLOCK), 20)
def getJoinPosition(self): #获取进入点坐标
return self.pointPositionDict['join']
def getPreparingArea(self): #获取准备区域
return self.pointPositionDict['ready']
def getPointArea(self, point): #获取检查点区域
return self.pointPositionDict[str(point + 1)]
def replaceBlock(self, targetType): #开始区域线替换为目标材质
if targetType == START_LINE_MATERIAL:
type = Material.TRIPWIRE
else:
type = START_LINE_MATERIAL
x1, y1, z1, x2, y2, z2 = self.pointPositionDict['glass']
for x in range(int(x1), int(x2) + 1):
for y in range(int(y1), int(y2) + 1):
for z in range(int(z1), int(z2) + 1):
block = self.world.getBlockAt(x, y, z)
if block.type == type:
block.setType(targetType)
class PlayerInGame: #游戏内玩家类
def __init__(self, playerName):
self.name = playerName
self.player = Bukkit.getPlayer(playerName)
self.world = worldManager.getWorld()
self.boat = None #玩家绑定的船实体
# 计时相关
self.point = 0 # 当前通过检查点
self.lap = 1 # 当前圈数
self.timer = 0 #个人计时器
self.rank = 1 #当前排名
self.startMoment = 0 # 游戏开始时间戳
self.passMoment = 0 # 通过点位时间戳
self.lastMoment = 0 # 上一点位时间戳
self.hasFinished = False
self.dnf = False
self.pointsTimer = {} #圈数对应记录点 [lap] point : Timer
self.bestMapRecordPointTimer = 0 #点位最佳用时
self.dValueText = ''
# 任务
self.actionBarTaskId = None # 动态栏任务ID
self.particleTaskId = None # 船粒子任务ID
self.readyParticleTaskId = None #开局粒子任务ID
# 玩家配置文件
self.playerData = PlayerDataManager(playerName)
self.updatePlayerData()
# 初始化设置
self.player.setFireTicks(0) #灭火
# 信息反馈 --------------------------------
def playSound(self, sound, volume, pitch): #为自己播放音效
self.player.playSound(self.player, sound, volume, pitch)
def sendTitle(self, title, subtitle, fadeIn, stay, fadeOut): #为自己发送标题
self.player.sendTitle(title, subtitle, fadeIn, stay, fadeOut)
def sendMessage(self, message): #为自己发送消息
self.player.sendMessage(message)
def addLapSound(self): #完成一圈后播放音效
self.playSound(Sound.BLOCK_NOTE_BLOCK_PLING, 1.0, 2.0)
def addLapTitle(self): #完成一圈后显示标题
self.sendTitle(u'§f', u'§a进入第 §e{} §a圈'.format(self.lap), 0, 30, 10)
def spawnBoatParticles(self): #生成船的粒子
if self.particleMode: #自定义粒子模式
leftLoc = self.getRelativeOffset(0.5, -1.0)
rightLoc = self.getRelativeOffset(-0.5, -1.0)
# 直接生成左右两边的粒子(完全对应原版命令)
worldManager.getWorld().spawnParticle(
Particle.valueOf(self.particleType), # 粒子类型
leftLoc.add(Vector(0, self.particleInfo[5], 0)), # 相对坐标
self.particleInfo[0], # 粒子数量
self.particleInfo[1], # 偏移量x
self.particleInfo[2], # 偏移量y
self.particleInfo[3], # 偏移量z
self.particleInfo[4], # 速度
None, # 数据
True # force模式
)
worldManager.getWorld().spawnParticle(
Particle.valueOf(self.particleType),
rightLoc.add(Vector(0, self.particleInfo[5], 0)), # 相对坐标
self.particleInfo[0], # 粒子数量
self.particleInfo[1], # 偏移量x
self.particleInfo[2], # 偏移量y
self.particleInfo[3], # 偏移量z
self.particleInfo[4], # 速度
None, # 数据
True
)
return True
else: #单粒子模式
worldManager.getWorld().spawnParticle(
Particle.DRAGON_BREATH,
self.boat.getLocation().add(Vector(0, 0.1, 0)),
0,0,0,0,0,
None, # 数据
True # force模式
)
def spawnPlayerParticles(self): #生成玩家开局展示粒子
self.player.getWorld().spawnParticle(
Particle.valueOf(DEFAULT_PARTICLE_TYPE),
self.player.getLocation().add(Vector(0, 0.1, 0)),
1, # 粒子数量
0, 0, 0, # 偏移量
0, # 速度
None, # 数据
True # force模式
)
def playIceboatMapSound(self, mapNum): #音乐模式播放自定义音乐
if int(mapNum) > (AVAILABLE_MAPNUM):
mapNum = 0
soundName = u"iceboatmap{}".format(mapNum)
self.player.playSound(self.player, soundName, SoundCategory.RECORDS, 1.0, 1.0)
def stopRecordSounds(self): #停止自定义音乐播放
self.player.stopSound(SoundCategory.RECORDS)
def showSingleTimerInActionBar(self): #在动态栏为自己显示单人时间信息
text = u"§fPoint {}-{} {} §f| 用时 §e§l{:.2f} §f秒".format(
self.lap, self.point, self.dValueText, self.timer)
actionBar(self.player, text)
def showMultiTimerInActionBar(self): #在动态栏为自己显示多人时间信息
text = u"§fPoint {}-{} {} §f| 排名 §b§l{} §f| 用时 §e§l{:.2f} §f秒".format(
self.lap, self.point, self.dValueText, self.rank, self.timer)
actionBar(self.player, text)
def showTimerByMessage(self, mapDisplay): #在聊天框为自己显示时间信息 详细信息模式
interval = round((self.passMoment - self.lastMoment) * 20) / 20
self.player.sendMessage(u"§b[§e{}§b] §fPoint {}-{} 用时 §e{:.2f} §f秒 {} §7(点间用时 {:.2f} 秒)".format(
mapDisplay, self.lap, self.point, self.timer, self.dValueText, interval))
# 信息判断 --------------------------------
def inVehicle(self): #判断玩家是否在载具内
if self.player.isInsideVehicle():
return True
return False
def isOnline(self): #判断玩家是否在线
return self.player.isOnline()
def inLobby(self):
playerManager = PlayerManager(self.player)
return playerManager.inLobby()
def inArea(self, area): #判断玩家是否在指定区域
loc = self.player.getLocation()
x = loc.getX()
y = loc.getY()
z = loc.getZ()
x1, y1, z1, x2, y2, z2 = area
if (x1 <= x <= x2 and
y1 <= y <= y2 and
z1 <= z <= z2):
return True
return False
def getSpectate(self): #判断是否有玩家在旁观正在游戏中的玩家
spectators = []
for name, spectator in playersInSpectate.items():
if self.name == spectator.targetPlayerName:
spectators.append(spectator.playerName)
return spectators
# 信息更新 --------------------------------
def arrivePoint(self, pointArea): #通过检查点
if self.inArea(pointArea): #是否在点位区域
if self.lastMoment < self.passMoment: #保存上一点位时间
self.lastMoment = self.passMoment
self.passMoment = time.time() # 记录新通过点位时间
self.point += 1
return True
return False
def resetPoint(self): #重置检查点
self.point = 0
def addLap(self): #玩家完成一圈
self.lap += 1
def updateTimer(self): #设置计时器时间
self.timer = round((self.passMoment - self.startMoment) * 20) / 20
def updateRank(self, rank): #更新排名
self.rank = rank
def updateJoinLoc(self): #更新玩家进入房间前位置到配置和实例
loc = self.player.getLocation()
x = loc.getX()
y = loc.getY()
z = loc.getZ()
self.joinLoc = x, y, z
self.playerData.setJoinLoc(x, y, z)
def setInGame(self, score=1): #在记分板中标记玩家在游戏中1为在0为不在
playerName = self.player.getName()
objective = getScoreboardObjective(u'InGame')
playerScore = objective.getScore(playerName) #获取玩家分数
playerScore.setScore(score) #设置玩家分数
def checkMultiRecord(self, mapNum): #检测多人纪录并更新完成次数
self.newRecord = round(self.timer, 2) #记录新纪录
totalMapTimes = self.playerData.getTotalMapTimes() #获取完成总数
mapTimes = self.playerData.getMapTimes(mapNum) #获取地图完成次数
rankManager.updateRankingAsyn(self.name, totalMapTimes + 1, None, u"totalMapTimes") #更新总完成次数排行榜
rankManager.updateRankingAsyn(self.name, mapTimes + 1, mapNum, u"mapTimes") #更新地图完成次数排行榜
self.playerData.setTotalMapTimes(totalMapTimes + 1) #更新总完成次数
self.playerData.setMapTimes(mapNum, mapTimes + 1) #更新地图完成次数
multiBestMapRecord = self.playerData.getMultiBestMapRecord(mapNum) #获取最好成绩
self.updateSeasonBestMapRecord(mapNum, self.newRecord) #更新赛季最好纪录
def updateMultiBestMapRecordData(): #更新最好成绩
multiBestMapRecord = self.newRecord #刷新最佳纪录
multiBestMapRecordTimestamp = time.time() #记录多人最好成绩获得时间
self.playerData.setMultiBestMapRecord(mapNum, multiBestMapRecord) #更新最好成绩
self.playerData.setMultiBestMapRecordTimestamp(mapNum, multiBestMapRecordTimestamp) #更新最好成绩时间戳
self.playerData.setMultiBestMapRecordPointsTimer(mapNum, self.pointsTimer) #更新最好成绩时间戳
rankManager.updateRankingAsyn(self.name, self.newRecord, mapNum, u"multi") #更新地图多人纪录排行榜
if multiBestMapRecord == 0: #如果尚未在多人模式玩过该地图
updateMultiBestMapRecordData()
return '0'
else: #有过该地图多人记录
oldRecord = multiBestMapRecord
if oldRecord > self.newRecord: #刷新记录
updateMultiBestMapRecordData()
return oldRecord #刷新记录返回旧记录
return False #未刷新记录返回False
def checkSingleRecord(self, mapNum):
self.newRecord = round(self.timer, 2) #记录新纪录
totalMapTimes = self.playerData.getTotalMapTimes() #获取完成总数
mapTimes = self.playerData.getMapTimes(mapNum) #获取地图完成次数
rankManager.updateRankingAsyn(self.name, totalMapTimes + 1, None, u"totalMapTimes") #更新总完成次数排行榜
rankManager.updateRankingAsyn(self.name, mapTimes + 1, mapNum, u"mapTimes") #更新地图完成次数排行榜
self.playerData.setTotalMapTimes(totalMapTimes + 1) #更新总完成次数
self.playerData.setMapTimes(mapNum, mapTimes + 1) #更新地图完成次数
singleBestMapRecord = self.playerData.getSingleBestMapRecord(mapNum) #获取最好成绩
self.updateSeasonBestMapRecord(mapNum, self.newRecord) #更新赛季最好纪录
def updatesingleBestMapRecordData(): #更新最好成绩
singleBestMapRecord = self.newRecord #刷新最佳纪录
singleBestMapRecordTimestamp = time.time() #记录多人最好成绩获得时间
self.playerData.setSingleBestMapRecord(mapNum, singleBestMapRecord) #更新最好成绩
self.playerData.setSingleBestMapRecordTimestamp(mapNum, singleBestMapRecordTimestamp) #更新最好成绩时间戳
self.playerData.setSingleBestMapRecordPointsTimer(mapNum, self.pointsTimer) #更新最好成绩时间戳
rankManager.updateRankingAsyn(self.name, self.newRecord, mapNum, u"single") #更新地图多人纪录排行榜
if singleBestMapRecord == 0: #如果尚未在多人模式玩过该地图
updatesingleBestMapRecordData()
return '0'
else: #有过该地图多人记录
oldRecord = singleBestMapRecord
if oldRecord > self.newRecord: #刷新记录
updatesingleBestMapRecordData()
return oldRecord #刷新记录返回旧记录
return False #未刷新记录返回False
def updateSeasonBestMapRecord(self, mapNum, newRecord): #更新赛季最好纪录(多人/单人
seasonBestMapRecord = self.playerData.getSeasonBestMapRecord(mapNum) #获取最好成绩
if seasonBestMapRecord == 0: #如果尚未在赛季模式玩过该地图
self.playerData.setSeasonBestMapRecord(mapNum, newRecord) #更新最好成绩
else:
oldRecord = seasonBestMapRecord
if oldRecord > newRecord: #刷新记录
self.playerData.setSeasonBestMapRecord(mapNum, newRecord) #更新最好成绩
else:
return False #未刷新记录则不更新纪录分数
seasonMaps = seasonManager.getSeasonMaps()
if str(mapNum) in seasonMaps: #如果该地图在赛季中
scoreStandard = seasonManager.getSeasonStandard(mapNum)
score = 100.0 - (float(newRecord) - float(scoreStandard))
if score > 0:
seasonMapScore = round(score, 2)
else:
seasonMapScore = 0.0
self.playerData.setSeasonMapScore(mapNum, seasonMapScore) #更新地图当季分数
def updatePlayerData(self): #更新玩家可设置的配置数据
self.playerData = PlayerDataManager(self.name) #重新读取
self.joinLoc = self.playerData.getJoinLoc()
self.level = self.playerData.getLevel()
self.boatMaterial = self.playerData.getBoatMaterial()
self.particleType = self.playerData.getParticleType()
self.particleInfo = self.playerData.getParticleInfo()
self.titleDisplay = self.playerData.getTitleDisplay()
self.particleMode = self.playerData.getParticleMode()
self.musicMode = self.playerData.getMusicMode()
self.detailMode = self.playerData.getDetailMode()
self.broadcastMode = self.playerData.getBroadcastMode()
def updateSingleBestMapRecordPointTimer(self, mapNum, lap, point): #更新单人最佳纪录时间
self.bestMapRecordPointTimer = self.playerData.getSingleBestMapRecordPointTimer(mapNum, lap, point)
def updateMultiBestMapRecordPointTimer(self, mapNum, lap, point): #更新多人最佳纪录时间
self.bestMapRecordPointTimer = self.playerData.getMultiBestMapRecordPointTimer(mapNum, lap, point)
def updateDValueText(self): #更新与最佳纪录的差值
dValue = self.timer - self.bestMapRecordPointTimer #获取与最佳纪录的差值
value = abs(dValue) #取绝对值
if dValue < 0:
color = u'§a'
mark = u'-'
else:
color = u'§c'
mark = u'+'
self.dValueText = u"{}[{}{:.2f}]".format(color, mark, value)
def updateLastMap(self, mapNum): #更新上一次游玩的地图
self.playerData.setLastMap(mapNum)
# 玩家操作 --------------------------------
def teleport(self, position): #传送玩家到指定坐标
if self.isOnline(): #如果玩家在线
playerLoc = self.player.getLocation()
loc = Location(
self.world,
position[0],
position[1],
position[2],
position[3],
position[4]
)
self.player.teleport(loc)
self.setInGame(1) #标记进入游戏
def teleportToJoinLoc(self): #传送玩家到进入房间前的位置
if self.isOnline(): #如果玩家在线
playerLoc = self.player.getLocation()
loc = Location(
self.world,
self.joinLoc[0],
self.joinLoc[1],
self.joinLoc[2],
playerLoc.getYaw(),
playerLoc.getPitch()
)
self.player.teleport(loc)
self.setInGame(0) #标记离开游戏
def spawnAndRideBoat(self): #根据玩家自己的船的类型自动上船
loc = self.player.getLocation()
loc.setPitch(BOAT_PITCH)
boat = self.world.spawnEntity(loc, EntityType.valueOf(self.boatMaterial.upper())) #生成船
boat.addPassenger(self.player) #自动骑乘
self.boat = boat
def clearBoat(self): #清理与自己绑定了的船
if self.boat: #如果有绑定船
if not self.boat.isDead(): #如果船还存在
self.boat.remove() #清除船
self.boat = None
def setInvisible(self): #让自己隐身
self.player.addPotionEffect(PotionEffect(
PotionEffectType.INVISIBILITY,
600,
0,
False, #显示粒子
False #显示图标
))
def unsetInvisible(self): #解除自己隐身
self.player.removePotionEffect(PotionEffectType.INVISIBILITY)
def finishAddVault(self, vault=REWARD_AMOUNT, mag=1.0): #完成游戏并增加Vault默认奖励金额
if vaultEconomy:
if self.timer <= 180: #如果小于180秒则百分比减少
vault = round(vault * (self.timer / 180), 1)
vault = round(vault * mag, 1)
vaultEconomy.depositPlayer(self.player, vault)
self.player.sendTitle(u'§a§l第 §e§l{} §a§l名'.format(self.rank), u'§6用时 §e{:.2f} §6| §6获得 §e{:.1f} §6DC币'.format(self.timer, vault), 0, 80, 20)
else:
self.player.sendTitle(u'§a§l第 §e§l{} §a§l名'.format(self.rank), u'§6用时 §e{:.2f}'.format(self.timer), 0, 80, 20)
return vault
def leaveAddVault(self, lap, point, vault=REWARD_AMOUNT): #离开游戏并扣除Vault默认惩罚金额
if vaultEconomy:
mag = 0.1
if point > 0:
mag = float(self.point) / float(point)
if lap > 1:
mag = (float(self.lap) - 1.0 + mag) / float(lap)
if mag < 0:
mag = 0.0
elif mag > 1.0:
mag = 1.0
if self.timer <= 180: #如果小于180秒则百分比减少
vault = round(vault * (self.timer / 180), 1)
vault = round(vault * mag, 1)
vaultEconomy.depositPlayer(self.player, vault)
return vault
# 时间操作 --------------------------------
def setStartMoment(self): #设置游戏开始时间戳
self.startMoment = time.time()
self.lastMoment = self.startMoment #设置上一点位时间戳为开始时间戳
def setPassMoment(self): #设置通过点位时间戳
self.passMoment = time.time()
def setLastMoment(self): #设置上一点位时间戳
self.lastMoment = time.time()
def setPointsTimer(self): #设置玩家通过检查点的时间戳
if self.lap not in self.pointsTimer:
self.pointsTimer[self.lap] = {}
self.pointsTimer[self.lap][self.point] = self.timer
# 辅助方法 --------------------------------
def getRelativeOffset(self, dx=0, dz=0): #获取相对偏移坐标
direction = self.boat.getLocation().getDirection().normalize().clone()
directionX = self.boat.getLocation().getDirection().normalize().clone()
right = directionX.crossProduct(Vector(0, 1, 0)).normalize() # 计算垂直于方向的右侧向量
offset = (right.multiply(dx) # 水平偏移
.add(direction.multiply(dz))) # 前后偏移
offset.setY(0.1) #粒子固定抬升0.1
return self.boat.getLocation().add(offset)
# 循环任务 --------------------------------
def startActionBarUpdates(self, isSingle=False): #开始玩家动态栏显示
if isSingle:
self.actionBarTaskId = ps.scheduler.scheduleRepeatingTask(
lambda: self.showSingleTimerInActionBar(),
0, # 延迟
20 # 间隔(ticks)
)
else:
self.actionBarTaskId = ps.scheduler.scheduleRepeatingTask(
lambda: self.showMultiTimerInActionBar(),
0, # 延迟
20 # 间隔(ticks)
)
def stopActionBarUpdates(self): #结束玩家个人动态栏显示
taskId = self.actionBarTaskId
if taskId is not None:
self.actionBarTaskId = None
ps.scheduler.stopTask(taskId)
def startParticle(self): #开始更新玩家船的粒子
if self.boat: #如果船存在的话
self.particleTaskId = ps.scheduler.scheduleRepeatingTask(
lambda: self.spawnBoatParticles(),
0, # 延迟
1 # 间隔(ticks)
)
def stopParticle(self): #停止更新玩家船的粒子
taskId = self.particleTaskId
if taskId is not None:
self.particleTaskId = None
ps.scheduler.stopTask(taskId)
def startReadyParticle(self): #准备阶段隐形的粒子显示 仅多人
if self.player:
self.readyParticleTaskId = ps.scheduler.scheduleRepeatingTask(
lambda: self.spawnPlayerParticles(),
0, # 延迟
1 # 间隔(ticks)
)
def stopReadyParticle(self): #结束准备阶段隐形的粒子显示 仅多人
taskId = self.readyParticleTaskId
if taskId is not None:
self.readyParticleTaskId = None
ps.scheduler.stopTask(taskId)
class PlayerInParticlePreview: #粒子预览类
def __init__(self, playerName, index):
self.name = playerName
self.player = Bukkit.getPlayer(playerName)
self.playerData = PlayerDataManager(self.name) #重新读取
self.boatMaterial = self.playerData.getBoatMaterial() #获取玩家船类型
self.particleMode = self.playerData.getParticleMode() #获取玩家粒子模式
self.particleType = collectionManager.getParticleType(index) #获取粒子类型
self.particleInfo = collectionManager.getParticleInfo(index) #获取粒子信息
self.boat = None
playersInParticlePreview[self.name] = self
self.particleTaskId = None #存储任务ID
#创造类后直接开始预览
self.spawnAndRideBoat()
self.startPreviewing()
def getRelativeOffset(self, dx=0, dz=0): #获取相对偏移坐标
direction = self.boat.getLocation().getDirection().normalize().clone()
directionX = self.boat.getLocation().getDirection().normalize().clone()
right = directionX.crossProduct(Vector(0, 1, 0)).normalize() # 计算垂直于方向的右侧向量
offset = (right.multiply(dx) # 水平偏移
.add(direction.multiply(dz))) # 前后偏移
offset.setY(0.1) #粒子固定抬升0.1
return self.boat.getLocation().add(offset)
def startPreviewing(self): #开始预览
self.particleTaskId = ps.scheduler.scheduleRepeatingTask(lambda: self.previewing(),0,1)
def previewing(self): #生成船的粒子
leftLoc = self.getRelativeOffset(0.5, -1.0)
rightLoc = self.getRelativeOffset(-0.5, -1.0)
# 直接生成左右两边的粒子(完全对应原版命令)
worldManager.getWorld().spawnParticle(
Particle.valueOf(self.particleType), # 粒子类型
leftLoc.add(Vector(0, self.particleInfo[5], 0)), # 相对坐标
self.particleInfo[0], # 粒子数量
self.particleInfo[1], # 偏移量x
self.particleInfo[2], # 偏移量y
self.particleInfo[3], # 偏移量z
self.particleInfo[4], # 速度
None, # 数据
True # force模式
)
worldManager.getWorld().spawnParticle(
Particle.valueOf(self.particleType),
rightLoc.add(Vector(0, self.particleInfo[5], 0)), # 相对坐标
self.particleInfo[0], # 粒子数量
self.particleInfo[1], # 偏移量x
self.particleInfo[2], # 偏移量y
self.particleInfo[3], # 偏移量z
self.particleInfo[4], # 速度
None, # 数据
True
)
if not self.player.isInsideVehicle(): #如果不在载具里
self.stopPreviewing() #停止预览
def stopPreviewing(self): #停止预览
if self.particleTaskId is not None:
ps.scheduler.stopTask(self.particleTaskId) #停止粒子展示
if self.boat: #如果有绑定船
if not self.boat.isDead(): #如果船还存在
self.boat.remove() #清除船
if self.name in playersInParticlePreview:
del playersInParticlePreview[self.name]
def spawnAndRideBoat(self): #根据玩家自己的船的类型自动上船
loc = self.player.getLocation()
loc.setPitch(BOAT_PITCH)
boat = worldManager.getWorld().spawnEntity(loc, EntityType.valueOf(self.boatMaterial.upper())) #生成船
boat.addPassenger(self.player) #自动骑乘
self.boat = boat
class PlayerInSpectate: #旁观玩家
def __init__(self, player):
self.player = player
self.playerName = player.getName()
self.playerManager = PlayerManager(player)
self.playerData = PlayerDataManager(self.playerName)
self.boat = None
self.display = None
self.targetPlayer = None
self.targetPlayerName = None
self.teleportTaskID = None
self.roomId = 0
self.ticks = 0
self.leaving = False
def attachBoat(self, target):
boat = target.getVehicle()
if boat and isinstance(boat, Boat):
self.targetPlayer = target
self.targetPlayerName = self.targetPlayer.getName()
self.roomId = 0
for roomId, room in multiGames.items():
if room.isPlayerInRoom(self.targetPlayerName):
self.roomId = roomId #如果目标是某个多人房间内的玩家
self.boat = boat
self.spec()
boatLoc = self.boat.getLocation()
playerLoc = self.player.getLocation()
distance = playerLoc.distance(boatLoc)
if self.playerManager.inLobby(): #传送前,若在大厅旁观船则记录进入房间与位置
self.updateJoinLoc()
self.setInGame(1) #标记进入游戏
if distance < PLAYER_RADIUS: #在渲染距离内直接附身
self.player.setSpectatorTarget(self.boat)
else: #不在渲染距离内则循环传送后附身
self.player.teleport(boatLoc)
self.display = worldManager.getWorld().spawn(self.player.getLocation(), ItemDisplay)
self.player.setSpectatorTarget(self.display)
self.ticks = 0
self.teleportTaskID = ps.scheduler.scheduleRepeatingTask(lambda s=self: s.forceSpec(), 0, 1)
return True
else:
self.player.sendMessage(u"§c目标玩家已离开房间")
if self.playerManager.inLobby():
del playersInSpectate[self.playerManager.playerName]
return False
def changeTarget(self):
if self.roomId > 0: #如果是多人房间
playerList = list(multiGames[self.roomId].playerList)
if playerList:
anotherTargetName = playerList[0]
anotherTarget = getServer().getPlayer(anotherTargetName)
self.attachBoat(anotherTarget)
return True
return False
def forceSpec(self):
if self.boat.isDead(): #如果船在强制旁观过程中消失
self.stopTPTask() #停止本次传送
if not self.changeTarget(): #如果没有成功切换目标
self.leave() #玩家离开旁观
self.ticks += 1
if self.ticks <= 7:
if self.display:
self.display.teleport(self.boat.getLocation().add(Vector(0, 0.5, 0)))
else:
self.stopTPTask()
self.player.setSpectatorTarget(self.boat)
self.ticks = 0
def spec(self):
if self.player.getGameMode() != GameMode.SPECTATOR:
self.player.setGameMode(GameMode.SPECTATOR)
def updateJoinLoc(self): #更新玩家进入房间前位置到配置和实例
loc = self.player.getLocation()
x = loc.getX()
y = loc.getY()
z = loc.getZ()
self.joinLoc = x, y, z
self.playerData.setJoinLoc(x, y, z)
def setInGame(self, score=1): #在记分板中标记玩家在游戏中1为在0为不在
objective = getScoreboardObjective(u'InGame')
playerScore = objective.getScore(self.playerManager.playerName) #获取玩家分数
playerScore.setScore(score) #设置玩家分数
def stopTPTask(self):
if self.teleportTaskID is not None:
ps.scheduler.stopTask(self.teleportTaskID) #停止循环任务
self.teleportTaskID = None
if self.display: #清除传送中介
self.display.remove()
self.display = None
def leave(self):
self.leaving = True #标记允许离开
if not self.playerManager.inLobby():
self.playerManager.teleportToJoinLoc()
self.setInGame(0) #标记离开游戏
self.stopTPTask()
if self.player.getGameMode()== GameMode.SPECTATOR:
self.player.setGameMode(GameMode.ADVENTURE)
self.player.sendTitle(u"§f", u"§6离开成功", 5, 60, 10)
del playersInSpectate[self.playerManager.playerName]
class CompetitionGameRoom: #比赛房间类
def __init__(self):
self.initRoom()
# 流程管理 ----------------------------------------------
def initRoom(self):
self.mapNum = seasonManager.getSeasonFirstMap() #默认赛季首图
self.roomNum = 1 #默认1号房
self.map = IceboatMap(self.mapNum, self.roomNum) #地图对象
self.playerCount = 0 # 房间内玩家数量
self.finishCount = 0 # 完成玩家数量
self.playingCount = 0 # 正在游戏中的玩家数量
self.round = 1 # 当前轮次
self.targetScore = 100 #目标分数
self.playerList = {} #玩家列表
self.scoreList = {} #分数列表
self.rankList = [] #实时排名
self.finishList = [] #完成列表
self.playingList = [] #游戏中列表
self.resetMaps() #重置比赛房间可选地图
self.isCompeting = False #是否正在比赛
self.isReady = False #开局准备完成阶段
self.isRunning = False #运行状态 包含Preparing和Gaming
self.isPreparing = False #准备状态
self.isGaming = False #游戏状态 包含Starting和Ending
self.isStarting = False #开始状态
self.isEnding = False #结束状态
self.isHalftime = False #是否半场休息
self.allFinished = False #是否所有玩家都完成
self.hasFinished = False #是否有玩家完成
self.firstPlayerName = None #第一名玩家
#非流程类存储
self.activeTasks = {}
self.playerMapTimes = {}
self.playerTotalMapTimes = {}
self.mapDisplayArmorStands = [] # 存储该房间的盔甲架实体
self.mapDisplayItemFrame = None # 存储该房间的展示框实体
self.countdownTaskId = None
self.playerScoreboardObj = None
def enterReady(self):
if len(self.playerList) < MIN_COMPETITION_PLAYERS:
for playerObj in self.playerList.values():
player = playerObj.player
if player:
player.sendTitle(u"", u"§c比赛需要至少{}人参与".format(MIN_COMPETITION_PLAYERS), 5, 50, 20)
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
return False
self.isCompeting = True
if not self.isReady:
#准备阶段过程
self.isReady = True
self.enterRunning()
def enterRunning(self):
# 运行前检查是否有可用副本
roomNum = roomsManager.getAvailableRoom(self.mapNum)
if roomNum is None: #无可用房间
for playerObj in self.playerList.values():
player = playerObj.player
if player:
player.sendTitle(u"", u"§c剩余空房不足请稍后再试", 5, 50, 20)
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
return False
self.isHalftime = False
self.isRunning = True # 开启成功
self.roomNum = roomNum # 更新房间号
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, True) #更新房间状态
self.map = IceboatMap(self.mapNum, self.roomNum) # 更新地图
self.map.initialSpecialMap() #初始化特殊地图
self.mapsList.remove(self.mapNum) #从可选地图列表中移除
self.createMapDisplayArmorStands() # 更新全息显示
mapManager = MapManager(self.mapNum)
mapDisplay = mapManager.getDisplay()
self.broadcastTitle(u"§a{}".format(mapDisplay), u"§eRound {}".format(self.round), 0, 90, 10)
# 为房间内每个玩家对象执行
for playerObj in self.playerList.values():
if playerObj.player.isInsideVehicle(): #如果玩家在船内则删除船
playerObj.player.getVehicle().remove()
self.playingList.append(playerObj.name) #添加到游戏中列表
playerObj.updatePlayerData() #更新玩家数据
playerObj.updateJoinLoc() #更新玩家进入房间前的位置
joinPos = self.map.getJoinPosition() #获取玩家进入房间的位置
playerObj.teleport(joinPos) #传送玩家到房间内
playerObj.updateLastMap(self.mapNum) #更新玩家上一次游玩的地图
playerObj.setInvisible() #设置玩家隐身
playerObj.startReadyParticle() #开始准备粒子效果
if playerObj.musicMode: #如果玩家启用了音乐模式
playerObj.stopRecordSounds() #停止可能在播放的音乐
playerObj.playIceboatMapSound(self.mapNum)
self.playerMapTimes[playerObj.name] = playerObj.playerData.getMapTimes(self.mapNum) #获取每个玩家的完成次数
self.playerTotalMapTimes[playerObj.name] = playerObj.playerData.getTotalMapTimes() #获取每个玩家的总完成次数
if playerObj.detailMode: #如果玩家启用了详细模式
self.mapManager = MapManager(self.mapNum)
playerObj.sendMessage(u"§b[§e{}§b] §a本地图由 §f{} §a等人参与制作".format(self.map.display, self.mapManager.getCreator()))
self.bindPlayerInPreparingArea(playerObj) #为玩家绑定检查点任务
self.playingCount = self.playerCount #更新正在游戏中的玩家数量
self.enterPrepaing() #进入准备阶段
def enterPrepaing(self): #待更新更改进入阶段的时长并加入OB旁观机制
self.isPreparing = True #进入准备阶段
# 准备阶段设置
self.map.replaceBlock(START_LINE_MATERIAL) # 启动线设置
# 准备阶段流程
def countdownTask1():
self.countdownTaskId = None
if self.isEmpty(): # 检查房间内是否还有玩家
self.closeRoom() #没有玩家则关闭房间
return
self.countdownToAll(u'§f〓 5 〓', u'§7●●●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask2, 20)
def countdownTask2():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§a〓 4 〓', u'§c●§7●●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask3, 20)
def countdownTask3():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§b〓 3 〓', u'§c●●§7●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask4, 20)
def countdownTask4():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§d〓 2 〓', u'§c●●●§7●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask5, 20)
def countdownTask5():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§e〓 1 〓', u'§c●●●●§7●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask6, 20)
def countdownTask6():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§c§l〓 GO 〓', u'§a●●●●●', 2.0)
self.isPreparing = False #准备阶段结束
for playerObj in self.playerList.values(): #获取玩家对象
playerObj.stopReadyParticle() #结束准备粒子展示
self.enterStarting()
# 准备阶段开始
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask1, 101)
def enterStarting(self):
self.isStarting = True # 进入开始阶段
# 开始阶段设置
for key in list(self.activeTasks.keys()): #清理非系统任务
if key != 'system':
self.clearTasks(key)
removeScoreboardObjective(u"IBScoreList")
self.createScoreboardRanking() #初始化计分板
# 各玩家设置
for playerObj in self.playerList.values(): #获取玩家对象
playerObj.unsetInvisible() #解除玩家隐身
playerObj.spawnAndRideBoat() #生成船并绑定
playerObj.setStartMoment() #设置开始时间戳
playerObj.startActionBarUpdates(False)
playerObj.startParticle() #开始粒子展示
#command = u'minecraft:scoreboard players display numberformat {playerName} IBPlayerRank blank'.format(playerName=playerObj.name)
#getServer().dispatchCommand(playerObj.player, command)
# 开始阶段设置
self.map.replaceBlock(Material.TRIPWIRE) # 启动线移除
self.isGaming = True # 进入游戏阶段
self.rankPlayerInGame() # 更新排名
self.trackTask('system', ps.scheduler.scheduleRepeatingTask(lambda: self.updateGamingState(), 0, 1))
def updateGamingState(self):
# 调试启用,用于检测循环任务是否停止
if self.isEmpty(): #如果房间为空则关闭房间
self.closeRoom()
return
# 循环检测玩家状态
self.allFinished = True #默认所有玩家都完成
for playerObj in self.playerList.values():
if playerObj.name not in self.playingList: #如果玩家不在游戏中列表
continue
if not playerObj.inVehicle(): #如果玩家不在载具内
if playerObj.name in self.playingList: #如果玩家还在游戏中
self.dnfPlayer(playerObj.name) #移出玩家
if self.isEnding: #若进入结束阶段则跳过
return
# 检查点位进度
if not playerObj.hasFinished: #玩家还没完成
self.allFinished = False #存在未完成玩家
mapNum = self.map.mapNum #获取地图编号
pointArea = self.map.getPointArea(playerObj.point) #获取玩家当前检查点区域
if playerObj.arrivePoint(pointArea): #玩家到达检查点
self.rankPlayerInGame() #通过检查点时更新全局排名
playerObj.updateTimer() #更新玩家时间戳
if playerObj.point >= self.map.point: #完成一圈
playerObj.addLap() #圈数增加
playerObj.resetPoint() #重置检查点
if playerObj.lap <= self.map.lap: #未完成所有圈的情况
playerObj.addLapTitle()
playerObj.addLapSound()
else:
if not self.hasFinished: #如果此前没有完成的玩家
self.enterEndCountdown() #进入结束倒计时
self.settleFinishedPlayer(playerObj) #对完成玩家的结算
playerObj.setPointsTimer() #设置玩家检查点时间戳
playerObj.updateMultiBestMapRecordPointTimer(mapNum, playerObj.lap, playerObj.point) #更新玩家多人最好成绩点位
playerObj.updateDValueText()
playerObj.showMultiTimerInActionBar() #更新玩家动态栏显示
if playerObj.detailMode: #详细模式
if playerObj.lap <= self.map.lap:
playerObj.showTimerByMessage(self.map.display) #显示详细信息
if self.allFinished: #所有玩家完成则在3秒后关闭房间
self.isStarting = False #结束启动状态
self.enterEnding() #进入结束阶段
def enterEndCountdown(self):
countdown = int(ENDING_COUNTDOWN) if ENDING_COUNTDOWN >= 200 else 200
countdownSecond = int(countdown / 20)
self.broadcastTitle(u"§f", u"§c有玩家完成进入{}秒结束倒计时".format(countdownSecond), 0, 15, 5)
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
def countdownTask1():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastTitle(u"§f", u"§c还剩5秒", 0, 15, 5)
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask2, 20)
def countdownTask2():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask3, 20)
def countdownTask3():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask4, 20)
def countdownTask4():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask5, 20)
def countdownTask5():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask6, 20)
def countdownTask6():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
if self.allFinished: #如果全部完成则不继续倒计时
return
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_BASS, 1, 2)
self.broadcastTitle(u"§f", u"§c本局结束", 0, 15, 5)
self.enterHalfTime()
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask1, countdown-100)
def enterEnding(self):
self.isEnding = True
ps.scheduler.runTaskLater(lambda: self.enterHalfTime(), ENDING_INTERVAL) #阶段
def enterHalfTime(self): #待更新,进入中场环节
if self.isHalftime:
return #不重复进入中场休息
if self.isEmpty():
self.closeRoom()
return
self.isHalftime = True
self.stopCountdownTask() # 停止倒计时任务
playingList = list(self.playingList) #获取游戏中列表
for index, playerName in enumerate(playingList):
self.dnfPlayer(playerName) #执行玩家退出
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, False) #房间可用
self.round += 1 #增加轮次
if not self.mapsList: #如果没有可选地图
self.resetMaps() #重置可选地图
self.mapNum = seasonManager.getSeasonFirstMap() #默认赛季首图
else:
self.mapNum = self.mapsList[0]
self.roomNum = 1 #默认1号房
self.map = IceboatMap(self.mapNum, self.roomNum)
self.createHologram()
removeScoreboardObjective(u"IBPlayerRank")
self.createScoreboardScoreList()
self.updateScoreboardScoreList()
self.resetRoom()
#判断游戏结束
for playerName, score in self.scoreList.items():
if score >= self.targetScore:
self.settleRoom()
return
def settleRoom(self): #结算房间
broadcast(u"§f")
broadcast(u"§a[比赛房间] §a比赛结束 | 回合数 §e{}".format(self.round-1))
sortedList = sorted(self.scoreList.items(), key=lambda x: x[1], reverse=True)
rank = 0
tempRank = 0
lastScore = 0
for playerName, score in sortedList:
if score == lastScore:
tempRank += 1
else:
if tempRank > 0:
rank += tempRank + 1
tempRank = 0
else:
rank += 1
if playerName not in self.playerList:
continue
playerObj = self.playerList[playerName]
if playerObj:
vault = self.addVaultByScore(playerObj, score) #结算Vault
message = u"§a[比赛房间] §eNo.{rank} {score} §a分 {levelColor}[{titleDisplay}]§f{playerName} §a| 获得 §e{vault} §aDC币".format(
levelColor=playerObj.playerData.getLevelColor(), rank=rank,
titleDisplay=playerObj.playerData.getTitleDisplay() ,playerName=playerObj.name, score=score, vault=vault)
broadcast(message)
lastScore = score
broadcast(u"§f")
self.closeRoom()
def resetRoom(self):
for playerObj in self.playerList.values():
playerName = playerObj.name
self.playerList[playerName] = PlayerInGame(playerName) #重置玩家对象
self.stopCountdownTask() # 停止倒计时任务
self.clearTasks('system')# 清理系统任务
self.rankList = [] #实时排名
self.finishList = [] #完成列表
self.playingList = [] #游戏中列表
self.finishCount = 0 # 完成玩家数量
self.playingCount = 0 # 正在游戏中的玩家数量
self.isRunning = False
self.isPreparing = False
self.isGaming = False
self.isStarting = False
self.isEnding = False
self.hasFinished = False
self.createMapDisplayArmorStands()
def closeRoom(self): #在任意节点关闭房间
self.clearHologram() # 清理全息显示
if not self.isCompeting: #不重复关闭
return
self.isCompeting = False
self.stopCountdownTask() # 停止倒计时任务
removeScoreboardObjective(u"IBPlayerRank")
removeScoreboardObjective(u"IBScoreList")
if self.isRunning: #在地图中则重置房间可用
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, False)
for playerObj in self.playerList.values():
self.delPlayer(playerObj.name)
self.delPlayer(playerObj.name)
self.clearTasks('system')# 清理系统任务
self.initRoom() #重置房间
# 玩家管理 ----------------------------------------------
def addPlayer(self, playerName):
if self.isRunning: #若房间已开启
return False, u"比赛启动中,无法加入"
if self.playerCount >= MAX_PLAYERS: #若房间已满
return False, u"比赛房间已满,无法加入"
if len(self.scoreList) >= MAX_PLAYERS: #若房间已满
if playerName not in self.scoreList: #若玩家不在计分中
return False, u"计分中玩家数已达上限,无法加入"
if playerName in self.playerList: #若玩家已经在房间内
return False, u"你已在该比赛房间内"
initial = False
if self.isEmpty(): #若房间为空的时候进行初始化
initial = True
if initial:
self.createMapDisplayItemFrame() # 地图显示功能
self.createScoreboardScoreList()
self.playerList[playerName] = PlayerInGame(playerName) #创建玩家对象
if playerName not in self.scoreList: #若玩家不在分数列表中
self.scoreList[playerName] = 0 #初始化分数
self.updateScoreboardScoreList()
self.playerCount += 1 #玩家数量+1
player = getServer().getPlayer(playerName)
playerManager = PlayerManager(player)
playerManager.joinCompetitionTeam()
self.createMapDisplayArmorStands() # 更新全息显示
self.save() # 保存房间状态
return True, u"成功加入比赛"
def delPlayer(self, playerName): #玩家退出房间
if playerName in self.playerList: #若玩家在房间内
objective = getScoreboardObjective(u'InGame')
playerScore = objective.getScore(playerName) #获取玩家分数
score = playerScore.getScore() #获取分数
if score == 1: #1为玩家在游戏中
self.dnfPlayer(playerName) #执行玩家退出
else: #0为玩家不在游戏中
self.removePlayer(playerName) #执行玩家移除
def dnfPlayer(self, playerName):
if playerName in self.playerList: #若玩家在房间内
self.clearTasks(playerName) # 清理玩家关联的循环任务
playerObj = self.playerList[playerName] #获取玩家对象
if not playerObj.hasFinished: #玩家未完成时离开
self.playingCount -= 1 #正在游戏中的玩家数量-1
self.scoreList[playerName] -= 2 #分数-2
playerObj.sendTitle(u'§a§lDNF', u'§c-2分', 0, 80, 20)
playerObj.dnf = True
self.updateScoreboardRanking()
if self.isRunning: #启动后才执行的内容
playerObj.teleportToJoinLoc() #传送回进入房间前的位置
if not playerObj.hasFinished:
self.broadcastMessage(
u"§b[§e{}§b] {}[{}]§f{} §aDNF".format(
self.map.display, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name
)
) #游戏内播报退出
if self.isPreparing: #准备中才执行的内容
playerObj.stopReadyParticle() #停止准备粒子效果
playerObj.unsetInvisible() #取消隐身
if self.isStarting: #游戏开始中才执行的内容
playerObj.stopActionBarUpdates() #停止ActionBar更新
playerObj.stopParticle() #停止粒子效果
if self.isGaming: #游戏中才执行的内容
playerObj.clearBoat() #离开时清理绑定的船
if playerObj.name in self.playingList:
self.playingList.remove(playerObj.name) #从游戏中列表中移除
if not self.isAnyoneInGame():
if not self.hasFinished: #若没有一个玩家完成比赛
self.firstPlayerName = playerName #记录第一名玩家
self.enterHalfTime()
self.save() #保存房间状态
def removePlayer(self, playerName):
if playerName in self.playerList: #若玩家在房间内
self.clearTasks(playerName) # 清理玩家关联的循环任务
playerObj = self.playerList[playerName] #获取玩家对象
if self.isCompeting:
self.broadcastMessage(
u"§b[§e{}§b] {}[{}]§f{} §a离开了比赛房间".format(
self.map.display, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name
)
) #游戏内播报退出
player = playerObj.player
playerManager = PlayerManager(player)
playerManager.leaveCompetitionTeam()
if playerName == self.firstPlayerName: #如果是第一名玩家
self.firstPlayerName = None #删除第一名玩家
del self.playerList[playerName] #删除玩家对象
self.createMapDisplayArmorStands() # 更新全息显示
#房间设置
self.playerCount -= 1 #玩家数量-1
self.save() #保存房间状态
if self.isEmpty(): #最后一个玩家离开
self.closeRoom() #关闭房间
def rankPlayerInGame(self):
players = list(self.playerList.values()) # 获取所有玩家对象
# 多级排序:圈数(降) -> 检查点(降) -> 单圈时间戳() -> 名字(升)
players.sort(key=lambda p: (
-p.lap,
-p.point,
p.passMoment,
p.name.lower()
))
players = list(filter(lambda x: x not in self.finishList, players)) #过滤掉完成的玩家
self.rankList = players # 存储排名好的对象列表
for rank, player in enumerate(players, start=self.finishCount+1): # 更新未完成的玩家排名
player.updateRank(rank)
self.updateScoreboardRanking()
def isPlayerInRoom(self, playerName):
if playerName in self.playerList:
return True
return False
def settleFinishedPlayer(self, playerObj):
self.finishCount += 1
self.playingCount -= 1
self.finishList.append(playerObj)
self.hasFinished = True #存在完成玩家
finishTime = playerObj.timer #完成时间戳
playerObj.finishTime = finishTime
# 为所有玩家广播消息
message = u"§b[§e{}§b] §a第 §e{} §a名 {}[{}]§f{} §a总用时 §e{:.2f} §a秒".format(
self.map.display, playerObj.rank, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name, finishTime)
broadcast(message)
playerObj.hasFinished = True # 标记玩家已完成
playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0, 1.0)
playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_TWINKLE, 1.0, 1.0)
playerObj.stopActionBarUpdates()
playerObj.stopParticle()
playerObj.sendTitle(u'§a§l第 §e§l{} §a§l名'.format(playerObj.rank), u'§6用时 §e{:.2f} §6| 获得 §e{} §6分'.format(playerObj.timer, self.getRankScore(playerObj.rank)), 0, 80, 20)
rankScore = self.getRankScore(playerObj.rank)
if playerObj.rank == 1:
self.firstPlayerName = playerObj.name #更新第一名玩家
self.scoreList[playerObj.name] += rankScore
record = playerObj.checkMultiRecord(self.map.mapNum)
self.updateScoreboardRanking()
if record: #纪录更新
record = float(record)
playerObj.playSound(Sound.ENTITY_PLAYER_LEVELUP, 1.0, 1.0)
self.broadcastMessage(
u"§b[§e{}§b] {}[{}]§f{} §a刷新了多人纪录§7(旧纪录:{:.2f})".format(
self.map.display, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name, record
)
)
if playerObj.detailMode: #如果玩家启用了详细模式
playerObj.sendMessage(u"§b[§e{}§b] §a最佳纪录 §e{:.2f} §a秒 | 第 §e{} §a次完成该地图".format(
self.map.display,
playerObj.playerData.getMultiBestMapRecord(self.mapNum),
self.playerMapTimes[playerObj.name] + 1))
if seasonManager.inSeasonMaps(self.mapNum): #如果是赛季地图
playerObj.sendMessage(u"§b[§e{}§b] §a该地图当季分数 §e{:.2f} §a| 当季总分 §e{:.2f}".format(
self.map.display,
playerObj.playerData.getSeasonMapScore(self.mapNum),
playerObj.playerData.getSeasonScore()))
else:
playerObj.sendMessage(u"§b[§e{}§b] §a该地图不是当季地图".format(self.map.display))
def bindPlayerInPreparingArea(self, playerObj):
def checkTask():
if not playerObj.inArea(self.map.getPreparingArea()):
actionBar(playerObj.player, u'§c请勿离开起点区域')
playerObj.teleport(self.map.getJoinPosition())
taskId = ps.scheduler.scheduleRepeatingTask(checkTask, 0, 20)
self.trackTask(playerObj.name, taskId)
def updatePlayerInfo(self, playerName):
if playerName in self.playerList:
self.playerList[playerName].updatePlayerData() #更新玩家对象数据
self.save() #保存房间状态
# 房间管理 ----------------------------------------------
def isEmpty(self):
return self.playerCount == 0
def isAnyoneInGame(self):
if self.playingList:
return True
return False
def setIceboatMap(self, mapNum, roomNum):
self.mapNum = mapNum
self.roomNum = roomNum
self.map = IceboatMap(self.mapNum, self.roomNum)
def setMapNum(self, mapNum):
self.mapNum = int(mapNum)
def setRoomNum(self, roomNum):
self.roomNum = roomNum
def setTargetScore(self, score):
score = int(score)
for playerName, playerScore in self.scoreList.items():
if int(playerScore) >= score: #若有玩家已经超过设定分数则设定失败
return False
self.targetScore = score
self.updateScoreboardScoreListDisplay()
return True
def resetMaps(self):
self.mapsList = list(seasonManager.getSeasonMaps())
def save(self):
global competitionGame
competitionGame = self
# 任务管理 --------------------------------
def trackTask(self, owner, taskId):
if owner not in self.activeTasks:
self.activeTasks[owner] = []
self.activeTasks[owner].append(taskId)
def clearTasks(self, owner):
if owner in self.activeTasks:
taskIds = list(self.activeTasks[owner])
for taskId in taskIds:
if taskId is not None:
ps.scheduler.stopTask(taskId)
del self.activeTasks[owner]
def stopCountdownTask(self):
if self.countdownTaskId is not None:
ps.scheduler.stopTask(self.countdownTaskId)
self.countdownTaskId = None
# 信息显示 ----------------------------------------------
def createHologram(self):
self.createMapDisplayArmorStands()
self.createMapDisplayItemFrame()
self.save() #保存房间状态
def createMapDisplayArmorStands(self):
config = ps.config.loadConfig(HOLOGRAME_CONFIG_PATH)
hologramLoc = config.get("8") #比赛房间为8号
if hologramLoc is not None:
x = hologramLoc.get("x")
y = hologramLoc.get("y")
z = hologramLoc.get("z")
if x is not None and y is not None and z is not None:
world = worldManager.getWorld()
self.clearMapDisplayArmorStands() # 清除旧的盔甲架
sortedPlayers = sorted(self.playerList.keys(), key=lambda playerName: PlayerDataManager(playerName).getSeasonScore()) #按玩家当季总分排序
# 设置名字之间的垂直偏移量
xOffset = 2
yOffset = 0.13 - (ARMOR_STAND_INTERVAL / 2) * (len(sortedPlayers) + 2)
zOffset = - 1.1
bottomLoc = Location(world, x, world.getMinHeight(), z) #预处理位置
# 为每个玩家创建盔甲架显示名字
for playerName in sortedPlayers:
# 加载玩家数据
playerData = PlayerDataManager(playerName)
if playerName == self.firstPlayerName:
nameColor = u"§e"
else:
nameColor = u"§f"
score = self.scoreList[playerName]
newScore = score + 10
scoreColor = self.getScoreColor(newScore)
customName = u"{}[{}]{}{}{}[{}]".format(playerData.getLevelColor(), playerData.getTitleDisplay(),
nameColor, playerName, scoreColor, score)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
# 地图信息盔甲架
customName = u"§a圈数 §e{} §a回合 §e{} §a抢 §e{}".format(self.map.lap, self.round, self.targetScore)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
# 地图名称盔甲架
if self.isRunning: #房间状态判定
roomStatus = u"§c[启动]"
else:
roomStatus = u"§a[等待]"
customName = u"§b§l【§e§l{}§b§l】{}".format(self.map.display, roomStatus)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
def createMapDisplayItemFrame(self):
config = ps.config.loadConfig(HOLOGRAME_CONFIG_PATH)
hologramLoc = config.get("8") #比赛房间为8号
if hologramLoc is not None:
x = hologramLoc.get("x")
y = hologramLoc.get("y")
z = hologramLoc.get("z")
if x is not None and y is not None and z is not None:
world = getServer().getWorld(WORLD_NAME)
if self.mapDisplayItemFrame: # 删除并覆盖已有展示框
self.mapDisplayItemFrame.remove()
displayLoc = Location(world, x - 1, y + 1, z) #最终展示框位置,偏移可修改
bottomLoc = Location(world, x, world.getMinHeight(), z) # 预处理位置
itemFrame = bottomLoc.getWorld().spawnEntity(bottomLoc, EntityType.ITEM_FRAME)#生成展示框
mapItem = ItemStack(Material.MAP)
meta = mapItem.getItemMeta()
if int(self.mapNum) > int(AVAILABLE_MAPNUM):
num = 0
else:
num = self.mapNum
customModelKey = fromString("template:map{}".format(int(num)))
meta.setItemModel(customModelKey)
mapItem.setItemMeta(meta)
itemFrame.setItem(mapItem)
itemFrame.setVisible(False)
self.mapDisplayItemFrame = itemFrame # 存储展示框实体
ps.scheduler.runTaskLater(lambda: itemFrame.teleport(displayLoc), 1) #延迟传送
ps.scheduler.runTaskLater(lambda: itemFrame.setFacingDirection(BlockFace.SOUTH), 1) #调整面朝方向
def clearHologram(self):
self.clearMapDisplayArmorStands()
self.clearMapDisplayItemFrame()
def clearMapDisplayArmorStands(self):
for armorStand in self.mapDisplayArmorStands:
if armorStand and not armorStand.isDead():
armorStand.remove()
self.mapDisplayArmorStands = []
def clearMapDisplayItemFrame(self):
if self.mapDisplayItemFrame:
self.mapDisplayItemFrame.remove()
self.mapDisplayItemFrame = None
def createScoreboardRanking(self):
removeScoreboardObjective(u"IBPlayerRank") #重置已有对应计分板
self.playerScoreboardObj = getScoreboardObjective(u"IBPlayerRank")
self.playerScoreboardObj.setDisplaySlot(DisplaySlot.SIDEBAR_TEAM_AQUA)
self.playerScoreboardObj.setDisplayName(u"§b§l【§e§l实时排名§b§l】")
displayScore = self.playerScoreboardObj.getScore(u"第一行空行")
displayScore.setScore(0)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(u"§f"))
displayScore = self.playerScoreboardObj.getScore(u"最后一行空行")
displayScore.setScore(-9)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(u"§f"))
displayScore = self.playerScoreboardObj.getScore(u"广告行1")
displayScore.setScore(-10)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(ADVERTISEMENT_TEXT_1))
displayScore = self.playerScoreboardObj.getScore(u"广告行2")
displayScore.setScore(-11)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(ADVERTISEMENT_TEXT_2))
def updateScoreboardRanking(self):
if self.playerScoreboardObj:
for playerObj in self.rankList:
rank = playerObj.rank
playerScore = self.playerScoreboardObj.getScore(playerObj.name)
playerScore.setScore(-rank)
playerScore.numberFormat(NumberFormat.blank())
color = u"§e"
namecolor = u"§f"
if playerObj.hasFinished:
color = u"§a"
namecolor = u"§a"
if playerObj.dnf:
color = u"§c"
namecolor = u"§c"
score = self.scoreList[playerObj.name]
addScore = self.getRankScore(rank)
newScore = score + addScore
scoreColor = self.getScoreColor(newScore)
playerScore.customName(Component.text(u" {color}§lNo.{} {namecolor}{} {scoreColor}[{}] ".format(rank, playerObj.name, score, color=color, namecolor=namecolor, scoreColor=scoreColor)))
def createScoreboardScoreList(self):
removeScoreboardObjective(u"IBScoreList") #重置已有对应计分板
self.playerScoreboardObj = getScoreboardObjective(u"IBScoreList")
self.playerScoreboardObj.setDisplaySlot(DisplaySlot.SIDEBAR_TEAM_AQUA)
self.updateScoreboardScoreListDisplay()
displayScore = self.playerScoreboardObj.getScore(u"第一行空行")
displayScore.setScore(9999)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(u"§f"))
displayScore = self.playerScoreboardObj.getScore(u"最后一行空行")
displayScore.setScore(-9999)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(u"§f"))
displayScore = self.playerScoreboardObj.getScore(u"广告行1")
displayScore.setScore(-10000)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(ADVERTISEMENT_TEXT_1))
displayScore = self.playerScoreboardObj.getScore(u"广告行2")
displayScore.setScore(-10001)
displayScore.numberFormat(NumberFormat.blank())
displayScore.customName(Component.text(ADVERTISEMENT_TEXT_2))
def updateScoreboardScoreListDisplay(self):
self.playerScoreboardObj.setDisplayName(u"§b§l【§e§l积分排名 抢{}§b§l】".format(self.targetScore))
def updateScoreboardScoreList(self):
if self.playerScoreboardObj:
sortedList = sorted(self.scoreList.items(), key=lambda x: x[1], reverse=True)
rank = 0
tempRank = 0
lastScore = 0
for playerName, score in sortedList:
if score == lastScore:
tempRank += 1
else:
if tempRank > 0:
rank += tempRank + 1
tempRank = 0
else:
rank += 1
playerScore = self.playerScoreboardObj.getScore(playerName)
playerScore.setScore(score)
playerScore.numberFormat(NumberFormat.blank())
if rank <= 3: #前三名金色
color = u"§e"
else:
color = u"§f"
score = self.scoreList[playerName]
newScore = score + 10
scoreColor = self.getScoreColor(newScore)
playerScore.customName(Component.text(u" {color}§lNo.{} {color}{} {scoreColor}[{}] ".format(rank, playerName, score, color=color, scoreColor=scoreColor)))
lastScore = score
# 辅助方法 --------------------------------
def addVaultByScore(self, playerObj, score):
if playerObj.name in self.playerList:
if vaultEconomy:
vault = 5 * score * len(self.playerList)
if vault < 0:
vault = 0
vaultEconomy.depositPlayer(playerObj.player, vault)
return vault
def getRankScore(self, rank):
score = 0
if rank == 1:
score = 10
elif rank == 2:
score = 7
elif rank == 3:
score = 5
elif rank == 4:
score = 4
elif rank == 5:
score = 3
elif rank == 6:
score = 1
elif rank == 7:
score = 0
elif rank == 8:
score = -1
else:
score = -2
return score
def getScoreColor(self, score):
color = u"§b"
dvalue = int(self.targetScore) - int(score)
if dvalue <= 0:
color = u"§c§l"
return color
def countdownToAll(self, title, subtitle, pitch):
self.broadcastTitle(title, subtitle, 0, 20, 10)
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_PLING, 1.0, pitch)
def broadcastMessage(self, message):
for playerObj in self.playerList.values():
playerObj.player.sendMessage(message)
def broadcastTitle(self, title=u"§f", subtitle=u"§f", fadeIn=0, stay=90, fadeOut=10):
for playerObj in self.playerList.values():
playerObj.player.sendTitle(title, subtitle, fadeIn, stay, fadeOut)
def broadcastSound(self, sound=Sound.BLOCK_NOTE_BLOCK_PLING, volume=1.0, pitch=1.0):
for playerObj in self.playerList.values():
playerObj.player.playSound(playerObj.player, sound, volume, pitch)
def createArmorStand(self, world, bottomLoc, x, y, z, customName):
armorStand = world.spawn(bottomLoc, ArmorStand) #先在底部创造
armorStand.setGravity(False) #设置无重力
armorStand.setVisible(False) #设置不可见
armorStand.setCollidable(False) #设置无碰撞箱
armorStand.setCustomName(customName) #设置显示名
armorStand.setCustomNameVisible(True) #设置显示名可见
displayLoc = Location(world, x, y, z) #设置展示位置
armorStand.teleport(displayLoc) #传送展示框
self.mapDisplayArmorStands.append(armorStand) #将展示框添加到房间
class MultiGameRoom: #多人游戏房间类
def __init__(self, roomId):
self.roomId = roomId
self.mapNum = 1 #默认1号地图
self.roomNum = 1 #默认1号房
self.map = None
self.playerCount = 0 # 房间内玩家数量
self.finishCount = 0 # 完成玩家数量
self.playingCount = 0 # 正在游戏中的玩家数量
self.playerList = {} # PlayerInGame对象的字典键为玩家名称
self.activeTasks = {}
self.countdownTaskId = None
self.rankList = [] #实时排名
self.finishList = [] #完成列表
self.mapDisplayArmorStands = [] # 存储该房间的盔甲架实体
self.mapDisplayItemFrame = None # 存储该房间的展示框实体
self.isRunning = False #运行状态 包含Preparing和Gaming
self.isPreparing = False #准备状态
self.isGaming = False #游戏状态 包含Starting和Ending
self.isStarting = False #开始状态
self.isEnding = False #结束状态
self.isClosed = False #是否关闭
self.allFinished = False #是否所有玩家都完成
self.playerMapTimes = {}
self.playerTotalMapTimes = {}
# 房间管理 ----------------------------------------------
def isEmpty(self): #判断房间是否为空
return self.playerCount == 0
def setIceboatMap(self, mapNum, roomNum): #设置房间地图对象
self.mapNum = mapNum
self.roomNum = roomNum
self.map = IceboatMap(self.mapNum, self.roomNum)
def setMapNum(self, mapNum): #设置房间地图编号
self.mapNum = int(mapNum)
def setRoomNum(self, roomNum): #设置房间房号
self.roomNum = roomNum
def save(self): # 更新房间对象
multiGames[self.roomId] = self
# 玩家管理 ----------------------------------------------
def addPlayer(self, playerName): #玩家进入房间
if self.isRunning: #若房间已开启
return False, u"房间已启动,无法加入"
if self.playerCount >= MAX_PLAYERS: #若房间已满
return False, u"房间已满,无法加入"
if playerName in self.playerList: #若玩家已经在房间内
return False, u"你已在该房间内"
initial = False
if self.isEmpty(): #若房间为空的时候进行初始化
initial = True
self.playerList[playerName] = PlayerInGame(playerName) #创建玩家对象
if initial:
mapNum = self.playerList[playerName].playerData.getLastMap() #获取玩家上一次玩的地图
self.setMapNum(mapNum) #设置房间地图编号
self.map = IceboatMap(self.mapNum, self.roomNum) #设置地图
self.createMapDisplayItemFrame() # 地图显示功能
self.playerCount += 1 #玩家数量+1
self.createMapDisplayArmorStands() # 更新全息显示
self.save() # 保存房间状态
return True, u"成功加入房间"
def delPlayer(self, playerName): #玩家在游戏中离开房间
if playerName in self.playerList: #若玩家在房间内
# 清理玩家关联的循环任务
if playerName in self.activeTasks:
for taskId in self.activeTasks[playerName]:
if taskId is not None:
ps.scheduler.stopTask(taskId)
del self.activeTasks[playerName]
#玩家设置
playerObj = self.playerList[playerName] #获取玩家对象
if not playerObj.hasFinished: #玩家未完成时离开
self.playingCount -= 1 #正在游戏中的玩家数量-1
if self.isRunning: #启动后才执行的内容
playerObj.teleportToJoinLoc() #传送回进入房间前的位置
self.broadcastMessage(
u"§b[§e{}§b] {}[{}]§f{} §a离开了房间".format(
self.map.display, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name
)
) #游戏内播报退出
if self.isPreparing: #准备中才执行的内容
playerObj.stopReadyParticle() #停止准备粒子效果
playerObj.unsetInvisible() #取消隐身
if self.isPreparing or playerObj.hasFinished or not self.isRunning: #准备中或已完成才执行的内容
if not self.isClosed:
playerObj.sendTitle(u"§f", u"§6离开成功", 5, 60, 10)
else:
if self.map:
lap = self.map.lap
point = self.map.point
vault = playerObj.leaveAddVault(lap, point)
playerObj.sendTitle(u"§f", u"§6离开成功 | 获得 §e{:.1f} §6DC币".format(vault), 5, 60, 10)
if self.isStarting: #游戏开始中才执行的内容
playerObj.stopActionBarUpdates() #停止ActionBar更新
playerObj.stopParticle() #停止粒子效果
if self.isGaming: #游戏中才执行的内容
playerObj.clearBoat() #离开时清理绑定的船
spectators = playerObj.getSpectate()
del self.playerList[playerName] #删除玩家对象
if not self.isClosed: # 房间未关闭时执行的内容
self.createMapDisplayArmorStands() # 更新全息显示
#房间设置
self.playerCount -= 1 #玩家数量-1
self.save() #保存房间状态
for spectatorName in spectators:
spectatorObj = playersInSpectate[spectatorName]
if not spectatorObj.changeTarget():
spectatorObj.leave()
if self.isEmpty(): #最后一个玩家离开
self.closeRoom() #关闭房间
def rankPlayerInGame(self): #更新房间内玩家排名
players = list(self.playerList.values()) # 获取所有玩家对象
# 多级排序:圈数(降) -> 检查点(降) -> 单圈时间戳() -> 名字(升)
players.sort(key=lambda p: (
-p.lap,
-p.point,
p.passMoment,
p.name.lower()
))
players = list(filter(lambda x: x not in self.finishList, players)) #过滤掉完成的玩家
self.rankList = players # 存储排名好的对象列表
for rank, player in enumerate(players, start=self.finishCount+1): # 更新未完成的玩家排名
player.updateRank(rank)
def isPlayerInRoom(self, playerName): #判断玩家是否在房间内
if playerName in self.playerList:
return True
return False
def settleFinishedPlayer(self, playerObj): #完成玩家结算
self.finishCount += 1
self.playingCount -= 1
self.finishList.append(playerObj)
finishTime = playerObj.timer #完成时间戳
playerObj.finishTime = finishTime
# 为所有玩家广播消息
message = u"§b[§e{}§b] §a第 §e{} §a名 {}[{}]§f{} §a总用时 §e{:.2f} §a秒".format(
self.map.display, playerObj.rank, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name, finishTime)
if playerObj.broadcastMode: #如果开启了成绩广播模式
broadcast(message)
else:
self.broadcastMessage(message)
playerObj.hasFinished = True # 标记玩家已完成
playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0, 1.0)
playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_TWINKLE, 1.0, 1.0)
playerObj.stopActionBarUpdates()
playerObj.stopParticle()
mag = 0.75 + (self.playingCount + self.finishCount) * 0.25
vault = playerObj.finishAddVault(mag=mag)
record = playerObj.checkMultiRecord(self.map.mapNum)
if record: #纪录更新
record = float(record)
playerObj.playSound(Sound.ENTITY_PLAYER_LEVELUP, 1.0, 1.0)
self.broadcastMessage(
u"§b[§e{}§b] {}[{}]§f{} §a刷新了多人纪录§7(旧纪录:{:.2f})".format(
self.map.display, playerObj.playerData.getLevelColor(),
playerObj.playerData.getTitleDisplay() ,playerObj.name, record
)
)
if playerObj.detailMode: #如果玩家启用了详细模式
playerObj.sendMessage(u"§b[§e{}§b] §a最佳纪录 §e{:.2f} §a秒 | 第 §e{} §a次完成该地图 | 获得 §e{:.1f} §aDC币".format(
self.map.display,
playerObj.playerData.getMultiBestMapRecord(self.mapNum),
self.playerMapTimes[playerObj.name] + 1,
vault))
if seasonManager.inSeasonMaps(self.mapNum): #如果是赛季地图
playerObj.sendMessage(u"§b[§e{}§b] §a该地图当季分数 §e{:.2f} §a| 当季总分 §e{:.2f}".format(
self.map.display,
playerObj.playerData.getSeasonMapScore(self.mapNum),
playerObj.playerData.getSeasonScore()))
else:
playerObj.sendMessage(u"§b[§e{}§b] §a该地图不是当季地图".format(self.map.display))
def bindPlayerInPreparingArea(self, playerObj): #绑定玩家在准备区域
def checkTask():
if not playerObj.inArea(self.map.getPreparingArea()):
actionBar(playerObj.player, u'§c请勿离开起点区域')
playerObj.teleport(self.map.getJoinPosition())
taskId = ps.scheduler.scheduleRepeatingTask(checkTask, 0, 20)
self.trackTask(playerObj.name, taskId)
def updatePlayerInfo(self, playerName): #更新玩家信息
if playerName in self.playerList:
self.playerList[playerName].updatePlayerData() #更新玩家对象数据
self.save() #保存房间状态
# 信息显示 ----------------------------------------------
def createHologram(self): #创建/更新全息显示
self.createMapDisplayArmorStands()
self.createMapDisplayItemFrame()
self.save() #保存房间状态
def createMapDisplayArmorStands(self): #创造盔甲架显示
config = ps.config.loadConfig(HOLOGRAME_CONFIG_PATH)
hologramLoc = config.get(str(self.roomId))
if hologramLoc is not None:
x = hologramLoc.get("x")
y = hologramLoc.get("y")
z = hologramLoc.get("z")
if x is not None and y is not None and z is not None:
world = getServer().getWorld(WORLD_NAME)
self.clearMapDisplayArmorStands() # 清除旧的盔甲架
sortedPlayers = sorted(self.playerList.keys(), reverse=True) # 按玩家名字首字母排序
# 设置名字之间的垂直偏移量
xOffset = 2
yOffset = 0.13 - (ARMOR_STAND_INTERVAL / 2) * (len(sortedPlayers) + 2)
zOffset = - 1.1
bottomLoc = Location(world, x, world.getMinHeight(), z) #预处理位置
# 为每个玩家创建盔甲架显示名字
for playerName in sortedPlayers:
# 加载玩家数据
playerData = PlayerDataManager(playerName)
customName = u"{}[{}]§f{}".format(playerData.getLevelColor(), playerData.getTitleDisplay(), playerName)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
# 地图信息盔甲架
customName = u"§a难度 §e{} §a圈数 §e{}".format(self.map.difficulty, self.map.lap)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
# 地图名称盔甲架
if self.isRunning: #房间状态判定
roomStatus = u"§c[启动]"
else:
roomStatus = u"§a[等待]"
customName = u"§b§l【§e§l{}§b§l】{}".format(self.map.display, roomStatus)
self.createArmorStand(world, bottomLoc, x + xOffset, y + yOffset, z + zOffset, customName)
yOffset += ARMOR_STAND_INTERVAL
def createMapDisplayItemFrame(self): #创造展示框显示
config = ps.config.loadConfig(HOLOGRAME_CONFIG_PATH)
hologramLoc = config.get(str(self.roomId))
if hologramLoc is not None:
x = hologramLoc.get("x")
y = hologramLoc.get("y")
z = hologramLoc.get("z")
if x is not None and y is not None and z is not None:
world = getServer().getWorld(WORLD_NAME)
if self.mapDisplayItemFrame: # 删除并覆盖已有展示框
self.mapDisplayItemFrame.remove()
displayLoc = Location(world, x - 1, y + 1, z) #最终展示框位置,偏移可修改
bottomLoc = Location(world, x, world.getMinHeight(), z) # 预处理位置
itemFrame = bottomLoc.getWorld().spawnEntity(bottomLoc, EntityType.ITEM_FRAME)#生成展示框
mapItem = ItemStack(Material.MAP)
meta = mapItem.getItemMeta()
if int(self.mapNum) > int(AVAILABLE_MAPNUM):
num = 0
else:
num = self.mapNum
customModelKey = fromString("template:map{}".format(int(num)))
meta.setItemModel(customModelKey)
mapItem.setItemMeta(meta)
itemFrame.setItem(mapItem)
itemFrame.setVisible(False)
self.mapDisplayItemFrame = itemFrame # 存储展示框实体
ps.scheduler.runTaskLater(lambda: itemFrame.teleport(displayLoc), 1) #延迟传送
ps.scheduler.runTaskLater(lambda: itemFrame.setFacingDirection(BlockFace.SOUTH), 1) #调整面朝方向
def clearHologram(self): #清理全息显示
self.clearMapDisplayArmorStands()
self.clearMapDisplayItemFrame()
def clearMapDisplayArmorStands(self): #清理盔甲架显示
for armorStand in self.mapDisplayArmorStands:
if armorStand and not armorStand.isDead():
armorStand.remove()
self.mapDisplayArmorStands = []
def clearMapDisplayItemFrame(self): #清理展示框显示
if self.mapDisplayItemFrame:
self.mapDisplayItemFrame.remove()
self.mapDisplayItemFrame = None
# 流程管理 ----------------------------------------------
def enterRunning(self): #尝试运行
# 运行前检查是否有可用副本
roomNum = roomsManager.getAvailableRoom(self.mapNum)
if roomNum is None: #无可用房间
for playerObj in self.playerList.values():
player = playerObj.player
if player:
player.sendTitle(u"", u"§c剩余空房不足", 5, 50, 20)
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1)
return False
# 运行成功设置
self.isRunning = True # 开启成功
self.roomNum = roomNum # 更新房间号
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, True) #更新房间状态
self.map = IceboatMap(self.mapNum, self.roomNum) # 更新地图
self.map.initialSpecialMap() #初始化特殊地图
self.createMapDisplayArmorStands() # 更新全息显示
# 为房间内每个玩家对象执行
for playerObj in self.playerList.values():
if playerObj.player.isInsideVehicle(): #如果玩家在船内则删除船
playerObj.player.getVehicle().remove()
playerObj.updatePlayerData() #更新玩家数据
playerObj.updateJoinLoc() #更新玩家进入房间前的位置
joinPos = self.map.getJoinPosition() #获取玩家进入房间的位置
playerObj.teleport(joinPos) #传送玩家到房间内
playerObj.updateLastMap(self.mapNum) #更新玩家上一次游玩的地图
playerObj.setInvisible() #设置玩家隐身
playerObj.startReadyParticle() #开始准备粒子效果
if playerObj.musicMode: #如果玩家启用了音乐模式
playerObj.stopRecordSounds() #停止可能在播放的音乐
playerObj.playIceboatMapSound(self.mapNum)
self.playerMapTimes[playerObj.name] = playerObj.playerData.getMapTimes(self.mapNum) #获取每个玩家的完成次数
self.playerTotalMapTimes[playerObj.name] = playerObj.playerData.getTotalMapTimes() #获取每个玩家的总完成次数
if playerObj.detailMode: #如果玩家启用了详细模式
self.mapManager = MapManager(self.mapNum)
playerObj.sendMessage(u"§b[§e{}§b] §a本地图由 §f{} §a等人参与制作".format(self.map.display, self.mapManager.getCreator()))
self.bindPlayerInPreparingArea(playerObj) #为玩家绑定检查点任务
self.playingCount = self.playerCount #更新正在游戏中的玩家数量
self.enterPrepaing() #进入准备阶段
def enterPrepaing(self):
self.isPreparing = True #进入准备阶段
# 准备阶段设置
self.map.replaceBlock(START_LINE_MATERIAL) # 启动线设置
# 准备阶段流程
def countdownTask1():
self.countdownTaskId = None
if self.isEmpty(): # 检查房间内是否还有玩家
self.closeRoom() #没有玩家则关闭房间
return
self.countdownToAll(u'§f〓 5 〓', u'§7●●●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask2, 20)
def countdownTask2():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§a〓 4 〓', u'§c●§7●●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask3, 20)
def countdownTask3():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§b〓 3 〓', u'§c●●§7●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask4, 20)
def countdownTask4():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§d〓 2 〓', u'§c●●●§7●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask5, 20)
def countdownTask5():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§e〓 1 〓', u'§c●●●●§7●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask6, 20)
def countdownTask6():
self.countdownTaskId = None
if self.isEmpty():
self.closeRoom()
return
self.countdownToAll(u'§c§l〓 GO 〓', u'§a●●●●●', 2.0)
self.isPreparing = False #准备阶段结束
for playerObj in self.playerList.values(): #获取玩家对象
playerObj.stopReadyParticle() #结束准备粒子展示
self.enterStarting()
# 准备阶段开始
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask1, 1)
def enterStarting(self): # 进入开始阶段 包括Starting和Gaming
self.isStarting = True # 进入开始阶段
# 开始阶段设置
for key in list(self.activeTasks.keys()): #清理非系统任务
if key != 'system':
self.clearTasks(key)
# 各玩家设置
for playerObj in self.playerList.values(): #获取玩家对象
playerObj.unsetInvisible() #取消隐身
playerObj.spawnAndRideBoat() #生成船并绑定
playerObj.setStartMoment() #设置开始时间戳
playerObj.startActionBarUpdates(False)
playerObj.startParticle() #开始粒子展示
# 开始阶段设置
self.map.replaceBlock(Material.TRIPWIRE) # 启动线移除
self.isGaming = True # 进入游戏阶段
self.rankPlayerInGame() # 更新排名
self.trackTask('system', ps.scheduler.scheduleRepeatingTask(lambda: self.updateGamingState(), 0, 1))
def updateGamingState(self): # 游戏阶段每游戏刻更新状态
# 调试启用,用于检测循环任务是否停止
if self.isEmpty(): #如果房间为空则关闭房间
self.closeRoom()
return
# 循环检测玩家状态
self.allFinished = True #默认所有玩家都完成
for playerObj in self.playerList.values():
if not playerObj.inVehicle(): #如果玩家不在载具内
self.delPlayer(playerObj.name) #移出玩家
if self.isEnding: #若进入结束阶段则跳过
return
# 检查点位进度
if not playerObj.hasFinished: #玩家还没完成
self.allFinished = False #存在未完成玩家
mapNum = self.map.mapNum #获取地图编号
pointArea = self.map.getPointArea(playerObj.point) #获取玩家当前检查点区域
if playerObj.arrivePoint(pointArea): #玩家到达检查点
self.rankPlayerInGame() #通过检查点时更新全局排名
playerObj.updateTimer() #更新玩家时间戳
if playerObj.point >= self.map.point: #完成一圈
playerObj.addLap() #圈数增加
playerObj.resetPoint() #重置检查点
if playerObj.lap <= self.map.lap: #未完成所有圈的情况
playerObj.addLapTitle()
playerObj.addLapSound()
else:
self.settleFinishedPlayer(playerObj) #对完成玩家的结算
playerObj.setPointsTimer() #设置玩家检查点时间戳
playerObj.updateMultiBestMapRecordPointTimer(mapNum, playerObj.lap, playerObj.point) #更新玩家多人最好成绩点位
playerObj.updateDValueText()
playerObj.showMultiTimerInActionBar() #更新玩家动态栏显示
if playerObj.detailMode: #详细模式
if playerObj.lap <= self.map.lap:
playerObj.showTimerByMessage(self.map.display) #显示详细信息
if self.allFinished: #所有玩家完成则在3秒后关闭房间
self.isStarting = False #结束启动状态
self.enterEnding() #进入结束阶段
def enterEnding(self): #游戏结束阶段
self.isEnding = True
ps.scheduler.runTaskLater(lambda: self.closeRoom(), ENDING_INTERVAL) #阶段
def closeRoom(self): #关闭房间操作
if self.isClosed: #不重复关闭
return
self.isClosed = True
# 停止倒计时任务
if self.countdownTaskId is not None:
ps.scheduler.stopTask(self.countdownTaskId)
self.countdownTaskId = None
roomId = self.roomId
players = list(self.playerList.keys())
self.clearHologram() #清理全息显示
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, False) #房间可用
for playerObj in self.playerList.values():
self.delPlayer(playerObj.name) #执行玩家退出
self.clearTasks('system')# 清理系统任务
self.isRunning = False
self.isGaming = False
self.isEnding = False
del multiGames[roomId] #删除房间
multiGames[roomId] = MultiGameRoom(roomId) #重新创建房间
for playerName in players: #将玩家重新添加
multiGames[roomId].addPlayer(playerName)
# 任务管理 --------------------------------
def trackTask(self, owner, taskId): #仅循环任务需要任务管理
if owner not in self.activeTasks:
self.activeTasks[owner] = []
self.activeTasks[owner].append(taskId)
def clearTasks(self, owner): #停止指定循环任务
if owner in self.activeTasks:
taskIds = list(self.activeTasks[owner])
for taskId in taskIds:
if taskId is not None:
ps.scheduler.stopTask(taskId)
del self.activeTasks[owner]
# 辅助方法 --------------------------------
def countdownToAll(self, title, subtitle, pitch): #为所有人播放倒计时反馈
self.broadcastTitle(title, subtitle, 0, 20, 10)
self.broadcastSound(Sound.BLOCK_NOTE_BLOCK_PLING, 1.0, pitch)
def broadcastMessage(self, message): #游戏内广播信息
for playerObj in self.playerList.values():
playerObj.player.sendMessage(message)
def broadcastTitle(self, title=u"§f", subtitle=u"§f", fadeIn=0, stay=90, fadeOut=10): #游戏内广播标题
for playerObj in self.playerList.values():
playerObj.player.sendTitle(title, subtitle, fadeIn, stay, fadeOut)
def broadcastSound(self, sound=Sound.BLOCK_NOTE_BLOCK_PLING, volume=1.0, pitch=1.0): #游戏内广播声音
for playerObj in self.playerList.values():
playerObj.player.playSound(playerObj.player, sound, volume, pitch)
def createArmorStand(self, world, bottomLoc, x, y, z, customName): #生成盔甲架
armorStand = world.spawn(bottomLoc, ArmorStand) #先在底部创造
armorStand.setGravity(False) #设置无重力
armorStand.setVisible(False) #设置不可见
armorStand.setCollidable(False) #设置无碰撞箱
armorStand.setCustomName(customName) #设置显示名
armorStand.setCustomNameVisible(True) #设置显示名可见
displayLoc = Location(world, x, y, z) #设置展示位置
armorStand.teleport(displayLoc) #传送展示框
self.mapDisplayArmorStands.append(armorStand) #将展示框添加到房间
class SingleGameRoom: #单人游戏房间类
def __init__(self, playerName, mapNum, roomNum): #玩家名:单人房间实例
self.playerName = playerName
self.player = getServer().getPlayer(playerName)
self.playerObj = PlayerInGame(playerName)
self.activeTasks = {}
self.countdownTaskId = None
self.mapNum = mapNum
self.roomNum = roomNum
self.map = IceboatMap(self.mapNum, self.roomNum)
self.map.initialSpecialMap() #初始化特殊地图
self.isRunning = False
self.isPreparing = False
self.isStarting = False
self.isClosed = False
self.mapManager = MapManager(self.mapNum)
# 房间管理 --------------------------------------------------
def save(self): #更新状态
singleGames[self.playerName] = self
# 玩家管理 --------------------------------------------------
def playerLeave(self): #玩家离开
# 清理玩家任务
if hasattr(self, 'activeTasks') and self.playerName in self.activeTasks:
for taskId in self.activeTasks[self.playerName]:
if taskId is not None:
ps.scheduler.stopTask(taskId)
del self.activeTasks[self.playerName]
# 清理玩家自身状态并执行操作
playerObj = self.playerObj
if self.isRunning:
playerObj.teleportToJoinLoc() #传送回进入房间前的位置
if self.isStarting:
playerObj.clearBoat() #离开时清理绑定的船
playerObj.stopActionBarUpdates()
playerObj.stopParticle()
if self.map and not playerObj.hasFinished:
lap = self.map.lap
point = self.map.point
vault = playerObj.leaveAddVault(lap, point)
playerObj.sendTitle(u"§f", u"§6离开成功 | 获得 §e{:.1f} §6DC币".format(vault), 5, 60, 10)
elif not playerObj.hasFinished:
playerObj.sendTitle(u"§f", u"§6离开成功", 5, 60, 10)
self.save()
# 流程管理 --------------------------------------------------
def enterRunning(self): #游戏初始化阶段
self.isRunning = True
if self.player.isInsideVehicle(): #如果玩家在船内则删除船
self.player.getVehicle().remove()
self.playerObj.updatePlayerData() #更新玩家数据
self.playerObj.updateJoinLoc() #更新玩家进入房间前的位置
joinPos = self.map.getJoinPosition() #获取玩家进入房间的位置
self.playerObj.teleport(joinPos) #传送玩家到房间内
self.playerObj.updateLastMap(self.map.mapNum) #更新玩家上一次游玩的地图
if self.playerObj.musicMode: #如果玩家启用了音乐模式
self.playerObj.stopRecordSounds() #停止可能在播放的音乐
self.playerObj.playIceboatMapSound(self.map.mapNum)
if self.playerObj.detailMode: #如果玩家启用了详细模式
self.playerObj.sendMessage(u"§b[§e{}§b] §a本地图由 §f{} §a等人参与制作".format(self.map.display, self.mapManager.getCreator()))
self.bindPlayerInPreparingArea() #为玩家绑定检查点任务
self.enterPreparing() #进入准备阶段
def enterPreparing(self):
self.isPreparing = True #进入准备阶段
# 准备阶段设置
self.map.replaceBlock(START_LINE_MATERIAL) # 启动线设置
# 准备阶段流程
def countdownTask1():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§f〓 5 〓', u'§7●●●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask2, 20)
def countdownTask2():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§a〓 4 〓', u'§c●§7●●●●',1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask3, 20)
def countdownTask3():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§b〓 3 〓', u'§c●●§7●●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask4, 20)
def countdownTask4():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§d〓 2 〓', u'§c●●●§7●●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask5, 20)
def countdownTask5():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§e〓 1 〓', u'§c●●●●§7●', 1.0)
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask6, 20)
def countdownTask6():
self.countdownTaskId = None
if self.playerName in singleGames:
self.countdownToPlayer(u'§c§lGO', u'§a●●●●●', 2.0)
self.isPreparing = False
self.enterStarting()
# 准备阶段开始
self.countdownTaskId = ps.scheduler.runTaskLater(countdownTask1, 1)
def enterStarting(self): #开始阶段
self.isStarting = True
# 开始阶段设置
self.clearTasks(self.playerObj.name) #清理玩家任务:循环检测区域
self.playerObj.spawnAndRideBoat() #生成船并绑定
self.playerObj.setStartMoment() #设置开始时间戳
self.playerObj.startActionBarUpdates(True)
self.playerObj.startParticle() #开始粒子展示
self.map.replaceBlock(Material.TRIPWIRE) #启动线移除
self.trackTask('system', ps.scheduler.scheduleRepeatingTask(lambda: self.updateGamingState(), 0, 1))
def updateGamingState(self):
if not self.playerObj.isOnline(): #玩家不在线则关闭房间
self.closeRoom()
return
if not self.playerObj.inVehicle(): #玩家不在载具内则关闭房间
self.closeRoom()
return
mapNum = self.map.mapNum
pointArea = self.map.getPointArea(self.playerObj.point)
if self.playerObj.arrivePoint(pointArea):
self.playerObj.updateTimer()
if self.playerObj.point >= self.map.point:
self.playerObj.addLap()
self.playerObj.resetPoint()
if self.playerObj.lap <= self.map.lap:
self.playerObj.addLapTitle() #没有到最后一圈则展现标题
self.playerObj.addLapSound() #没有到最后一圈则播放音效
else:
self.enterEnding()
self.playerObj.setPointsTimer()
self.playerObj.updateSingleBestMapRecordPointTimer(mapNum, self.playerObj.lap, self.playerObj.point) #更新玩家单人最好成绩点位
self.playerObj.updateDValueText() #更新D值文本
self.playerObj.showSingleTimerInActionBar()
if self.playerObj.detailMode: #详细模式下显示详细信息
if self.playerObj.lap <= self.map.lap: #仅在未完成前播报
self.playerObj.showTimerByMessage(self.map.display)
def enterEnding(self):
finishTime = self.playerObj.timer
self.playerObj.finishTime = finishTime
message = u"§b[§e{}§b] {}[{}]§f{} §a总用时 §e{:.2f} §a秒".format(
self.map.display, self.playerObj.playerData.getLevelColor(), self.playerObj.playerData.getTitleDisplay(), self.playerObj.name, finishTime)
if self.playerObj.broadcastMode: #如果开启了成绩广播模式
broadcast(message)
else:
self.playerObj.sendMessage(message)
self.playerObj.hasFinished = True # 标记玩家已完成
self.playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0, 1.0)
self.playerObj.playSound(Sound.ENTITY_FIREWORK_ROCKET_TWINKLE, 1.0, 1.0)
self.playerObj.stopActionBarUpdates()
self.playerObj.stopParticle()
vault = self.playerObj.finishAddVault()
record = self.playerObj.checkSingleRecord(self.map.mapNum)
if record: #纪录更新
record = float(record)
self.playerObj.playSound(Sound.ENTITY_PLAYER_LEVELUP, 1.0, 1.0)
self.playerObj.sendMessage(
u"§b[§e{}§b] {}[{}]§f{} §a刷新了个人纪录§7(旧纪录:{:.2f})".format(
self.map.display, self.playerObj.playerData.getLevelColor(), self.playerObj.playerData.getTitleDisplay(), self.playerObj.name, record
)
)
if self.playerObj.detailMode: #如果玩家启用了详细模式
self.playerObj.sendMessage(u"§b[§e{}§b] §a最佳纪录 §e{:.2f} §a秒 | 第 §e{} §a次完成该地图 | 获得 §e{:.1f} §aDC币".format(
self.map.display,
self.playerObj.playerData.getSingleBestMapRecord(self.mapNum),
self.playerObj.playerData.getMapTimes(self.mapNum),
vault))
if seasonManager.inSeasonMaps(self.mapNum): #如果是赛季地图
self.playerObj.sendMessage(u"§b[§e{}§b] §a该地图当季分数 §e{:.2f} §a| 当季总分 §e{:.2f}".format(
self.map.display,
self.playerObj.playerData.getSeasonMapScore(self.mapNum),
self.playerObj.playerData.getSeasonScore()))
else:
self.playerObj.sendMessage(u"§b[§e{}§b] §a该地图不是当季地图".format(self.map.display))
self.closeRoom()
def closeRoom(self): #游戏结束
if self.isClosed: #不重复关闭
return
self.isClosed = True
# 停止倒计时任务
if self.countdownTaskId is not None:
ps.scheduler.stopTask(self.countdownTaskId)
self.countdownTaskId = None
roomsManager.setAvailableRoom(self.mapNum, self.roomNum, False)
spectators = self.playerObj.getSpectate()
self.playerLeave()
self.clearTasks('system') # 清理系统任务
self.isStarting = False
self.isRunning = False
del singleGames[self.playerName]
for spectatorName in spectators:
spectatorObj = playersInSpectate[spectatorName]
if not spectatorObj.changeTarget():
spectatorObj.leave()
# 任务管理 --------------------------------
def trackTask(self, owner, taskId):
if not hasattr(self, 'activeTasks'):
self.activeTasks = {}
if owner not in self.activeTasks:
self.activeTasks[owner] = []
self.activeTasks[owner].append(taskId)
def clearTasks(self, owner):
if hasattr(self, 'activeTasks') and owner in self.activeTasks:
taskIds = list(self.activeTasks[owner])
for taskId in taskIds:
if taskId is not None:
ps.scheduler.stopTask(taskId)
del self.activeTasks[owner]
# 辅助方法 --------------------------------
def countdownToPlayer(self, title, subtitle, pitch):
player = self.playerObj.player
player.sendTitle(title, subtitle, 0, 20, 10)
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_PLING, 1.0, pitch)
def bindPlayerInPreparingArea(self): #准备阶段循环检测
def checkTask():
if not self.playerObj.inArea(self.map.getPreparingArea()):
actionBar(self.playerObj.player, u'§c请勿离开起点区域')
self.playerObj.teleport(self.map.getJoinPosition())
taskId = ps.scheduler.scheduleRepeatingTask(checkTask, 0, 20)
self.trackTask(self.playerObj.name, taskId)
competitionGame = CompetitionGameRoom()
# 通用函数 -------------------------------------------------------------------
def actionBar(player, text): #为玩家设置动态栏
if player:
player.spigot().sendMessage(ACTION_BAR, TextComponent.fromLegacyText(text))
def createScoreboardObjective(name): #初始化计分板
manager = getServer().getScoreboardManager()
scoreboard = manager.getMainScoreboard()
if not scoreboard.getObjective(name):
scoreboard.registerNewObjective(name, Criteria.DUMMY, name)
def getScoreboardObjective(scoreboardName):
scoreboard = getServer().getScoreboardManager().getMainScoreboard()
if not scoreboard.getObjective(scoreboardName): #初始化计分板
createScoreboardObjective(scoreboardName)
objective = scoreboard.getObjective(scoreboardName)
return objective
def removeScoreboardObjective(scoreboardName):
scoreboard = getServer().getScoreboardManager().getMainScoreboard()
if scoreboard.getObjective(scoreboardName): #初始化计分板
scoreboard.getObjective(scoreboardName).unregister()
def getScoreboardTeam(teamName):
scoreboard = getServer().getScoreboardManager().getMainScoreboard()
if not scoreboard.getTeam(teamName): #初始化计分板
scoreboard.registerNewTeam(teamName)
team = scoreboard.getTeam(teamName)
team.setColor(ChatColor.AQUA)
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER)
team.setCanSeeFriendlyInvisibles(False)
return team
def removeScoreboardTeam(teamName):
scoreboard = getServer().getScoreboardManager().getMainScoreboard()
team = scoreboard.getTeam(teamName)
if team: #初始化计分板
team.unregister()
def broadcast(message): #世界广播内容
for player in worldManager.getWorld().getPlayers():
player.sendMessage(message)
# 命令 -------------------------------------------------------------------
def iceboatCommand(sender, label, args): # 房间命令
if len(args) == 0:
return False
command = args[0]
if command == u'start': # 启动房间命令,不需要玩家名
if len(args) < 2:
return False
roomId = args[1]
if roomId in multiGames:
room = multiGames[roomId]
if isinstance(sender, Player):
playerName = sender.getName()
player = sender
if not room.isPlayerInRoom(playerName):
player.sendTitle(u"§f", u"§c你不在该房间内无法启动房间", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
room.enterRunning()
return True
else:
if isinstance(sender, Player):
sender.sendTitle(u"§f", u"§c房间未被创建请先进入房间", 5, 60, 10)
sender.playSound(sender, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
elif command == u'cstart':
if isinstance(sender, Player):
playerName = sender.getName()
player = sender
if not competitionGame.isPlayerInRoom(playerName):
player.sendTitle(u"§f", u"§c你不在该房间内无法启动房间", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if competitionGame.isRunning:
player.sendTitle(u"§f", u"§c比赛房间已启动", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if competitionGame.firstPlayerName is not None:
if playerName != competitionGame.firstPlayerName:
player.sendTitle(u"§f", u"§c仅上局第一可开始游戏", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
competitionGame.enterReady()
return True
elif command == u'seasonreset':
if isinstance(sender, Player):
if sender.isOp():
def resetOver():
sender.sendTitle(u"§f", u"§a所有玩家当季地图纪录已重置", 5, 60, 10)
sender.playSound(sender, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
ps.scheduler.runTaskLater(lambda h=hallOfFameManager:hallOfFameManager.updateHallOfFame(), 40)
ps.scheduler.runSyncCallbackTask(lambda sm=seasonManager: sm.resetSeasonRecords(), resetOver())
if len(args) < 2:
return False
if command == 'compete':
targetPlayerName = args[1]
targetPlayer = Bukkit.getPlayer(targetPlayerName)
if targetPlayer is None:
return False
playerManager = PlayerManager(targetPlayer)
if playerManager.inAnyRoom(): #判断玩家是否在房间内
targetPlayer.sendTitle(u"§f", u"§c你已经在某房间内", 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
result = competitionGame.addPlayer(targetPlayerName)
if isinstance(result, tuple) and len(result) == 2:
success, message = result
if success:
targetPlayer.sendTitle(u"§f", u"§a{}".format(message), 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
targetPlayer.sendTitle(u"§f", u"§c{}".format(message), 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
else:
targetPlayer.sendMessage(u"§c发生未知错误请稍后重试。")
if command == 'multi': # 命令格式 /ib multi <roomId> <playerName>
if len(args) < 3:
return False
roomId = args[1]
targetPlayerName = args[2]
targetPlayer = Bukkit.getPlayer(targetPlayerName)
if targetPlayer is None:
return False
playerManager = PlayerManager(targetPlayer)
if playerManager.inAnyRoom(): #判断玩家是否在房间内
targetPlayer.sendTitle(u"§f", u"§c你已经在某房间内", 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if roomId not in multiGames: # 若该ID的房间未开启 则新增一个实例
multiGames[roomId] = MultiGameRoom(roomId)
result = multiGames[roomId].addPlayer(targetPlayerName)
if isinstance(result, tuple) and len(result) == 2:
success, message = result
if success:
targetPlayer.sendTitle(u"§f", u"§a{}".format(message), 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
targetPlayer.sendTitle(u"§f", u"§c{}".format(message), 5, 60, 10)
targetPlayer.playSound(targetPlayer, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
else:
targetPlayer.sendMessage(u"§c发生未知错误请稍后重试。")
if command == 'leave':
if len(args) < 2:
return False
targetPlayerName = args[1]
targetPlayer = getServer().getPlayer(targetPlayerName)
if targetPlayer is None:
return False
playerManager = PlayerManager(targetPlayer)
if playerManager.leaveRoom():
return True
return False
def iceboatMenuCommand(sender, label, args): # 菜单命令
if not isinstance(sender, Player): #仅玩家可用
return False
player = sender
worldName = player.getWorld().getName()
if worldName != WORLD_NAME:
player.sendMessage(u"§c请在冰船世界中使用该指令")
return False
if len(args) > 0:
if args[0].lower() == "single":
playerManager = PlayerManager(player)
if playerManager.inAnyRoom():
player.sendMessage(u"§c你正在一个游戏房间内")
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
MenuSingle(player).openWithRender() # 打开单人地图选择页面
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return True
elif args[0].lower() == "competition":
playerName = player.getName()
if not competitionGame.isPlayerInRoom(playerName):
player.sendTitle(u"§f", u"§c你不在比赛中", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if competitionGame.isRunning:
player.sendTitle(u"§f", u"§c比赛房间启动中", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
if competitionGame.firstPlayerName is not None:
if playerName != competitionGame.firstPlayerName:
player.sendTitle(u"§f", u"§c仅上局第一可修改设置", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
MenuCompetition(player).openWithRender() # 打开比赛地图选择页面
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
return True
elif args[0].lower() == "multi" and len(args) > 1:
roomId = args[1]
if roomId not in multiGames:
player.sendTitle(u"§f", u"§c房间未被创建请先进入房间", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
room = multiGames.get(str(roomId))
playerName = player.getName()
if not room.isPlayerInRoom(playerName):
player.sendTitle(u"§f", u"§c你不在该房间请先进入房间", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
MenuMulti(player, roomId).openWithRender() # 打开多人地图选择页面
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
return True
elif args[0].lower() == "collection":
MenuCollection(player).openWithRender() # 打开收集品菜单
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
return True
return False
ps.command.registerCommand(iceboatCommand, "ib")
ps.command.registerCommand(iceboatCommand, "iceboat")
ps.command.registerCommand(iceboatMenuCommand, "ibm")
ps.command.registerCommand(iceboatMenuCommand, "iceboatmenu")
# 菜单处理 -------------------------------------------------------------------
class MenuSingle(GUIController):# 单人房间菜单
def __init__(self, player, page=1, sortMode="default", isSeason=False):
super(MenuSingle, self).__init__(player)
self.page = page
self.sortMode = sortMode
self.isSeason = isSeason
self.setInfo("iceboat.single", u"§0单人模式地图选择 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
mapNumbers = []
if self.isSeason:
mapNumbers = seasonManager.getSeasonMaps()
else:
mapNumbers = mapsManager.getMapsNum()
# 根据排序模式排序地图
if self.sortMode == "descDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)), reverse=True)
elif self.sortMode == "ascDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)))
elif self.sortMode == "descNumber":
mapNumbers.sort(key=lambda num: int(num), reverse=True)
elif self.sortMode == "ascNumber":
mapNumbers.sort(key=lambda num: int(num))
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
self.totalPages = (len(mapNumbers) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = mapNumbers[startIndex:endIndex]
playerDataManager = PlayerDataManager(playerName)
#创建物品
for index, mapNumber in enumerate(currentPageItems): # 为每个地图创建一个物品
mapFilePath = u"iceboat/maps/{}.yml".format(mapNumber)
mapConfig = ps.config.loadConfig(mapFilePath)
displayName = mapConfig.get("display", u"§b§l【§e§l地图 {}§b§l】§f".format(mapNumber))
material = mapConfig.get("material", "PAPER")
lap = mapConfig.get("lap", "未知")
mapManager = MapManager(mapNumber)
difficulty = mapManager.getDifficulty()
difficultySuffix = mapManager.getDifficultySuffix()
available = roomsManager.getAvailableRoomCount(mapNumber)
total = roomsManager.getTotalRoomCount(mapNumber)
singleMapBestRecord = playerDataManager.getSingleBestMapRecord(mapNumber)
singleMapBestRecordDay = playerDataManager.getSingleBestMapRecordDay(mapNumber)
seasonMapBestRecord = playerDataManager.getSeasonBestMapRecord(mapNumber)
mapTimes = playerDataManager.getMapTimes(mapNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
lores = [u"§e序号 §f{}".format(mapNumber),
u"§e难度 §f{} {}".format(difficulty, difficultySuffix),
u"§e圈数 §f{}".format(lap),
"",
u"§e剩余空房 §f{}/{}".format(available, total),
u"§e当季纪录 §f{:.2f}".format(seasonMapBestRecord) if seasonMapBestRecord else u"§e当季纪录 §f暂无"
]
if self.isSeason:
lores.append(u"§e当季标准 §f{:.2f}".format(seasonManager.getSeasonStandard(mapNumber)))
lores.append(u"§e当季分数 §f{:.2f}".format(playerDataManager.getSeasonMapScore(mapNumber)))
lores.extend([
"",
u"§e完成次数 §f{}".format(mapTimes) if mapTimes else u"§e完成次数 §f0",
u"§e单人纪录 §f{:.2f}".format(singleMapBestRecord) if singleMapBestRecord else u"§e单人纪录 §f暂无",
u"§e纪录日期 §f{}".format(singleMapBestRecordDay) if singleMapBestRecordDay else u"§e纪录日期 §f暂无",
"",
u"§6>> §a点击进入 §6<<"
])
self.set(index, material,
u"§b§l【§e§l{}§b§l】§f".format(displayName),
*lores,
data=str(mapNumber)
)
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
self.set(47, Material.LIME_STAINED_GLASS_PANE, u"§a按序号升序") # 序号升序按钮
self.set(48, Material.ORANGE_STAINED_GLASS_PANE, u"§c按序号降序") # 序号降序按钮
buttonText = u"§e仅看当季图池" if not self.isSeason else u"§e显示所有地图"
self.set(49, Material.YELLOW_STAINED_GLASS_PANE, buttonText) # 当季图池按钮
self.set(50, Material.GREEN_STAINED_GLASS_PANE, u"§a按难度升序")# 难度升序按钮
self.set(51, Material.RED_STAINED_GLASS_PANE, u"§c按难度降序") # 难度降序按钮
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
# 记录玩家当前页码和排序模式
playerPage[playerName] = {
"page": self.page,
"sortMode": self.sortMode,
"isSeason": self.isSeason
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
if clickInt == 45 and self.page > 1:
MenuSingle(player, page=self.page - 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 47:
MenuSingle(player, sortMode="ascNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 48:
MenuSingle(player, sortMode="descNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 49:
MenuSingle(player, sortMode=self.sortMode, isSeason=not self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 50:
MenuSingle(player, sortMode="ascDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 51:
MenuSingle(player, sortMode="descDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 53 and self.page < self.totalPages:
MenuSingle(player, page=self.page + 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
mapNumber = getGUIValue(item)
if mapNumber is not None:
playerManager = PlayerManager(player)
if not playerManager.joinSingleRoom(mapNumber):
MenuSingle(player, page=self.page, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
class MenuMulti(GUIController):
def __init__(self, player, roomId, page=1, sortMode="default", isSeason=False):
super(MenuMulti, self).__init__(player)
self.roomId = roomId
self.page = page
self.sortMode = sortMode
self.isSeason = isSeason
self.setInfo("iceboat.multi", u"§0多人模式地图选择 - 房间 {} - 第 {}".format(roomId, page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
mapNumbers = []
if self.isSeason:
mapNumbers = seasonManager.getSeasonMaps()
else:
mapNumbers = mapsManager.getMapsNum()
if self.sortMode == "descDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)), reverse=True)
elif self.sortMode == "ascDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)))
elif self.sortMode == "descNumber":
mapNumbers.sort(key=lambda num: int(num), reverse=True)
elif self.sortMode == "ascNumber":
mapNumbers.sort(key=lambda num: int(num))
itemsPerPage = 45
self.totalPages = (len(mapNumbers) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = mapNumbers[startIndex:endIndex]
playerDataManager = PlayerDataManager(playerName)
for index, mapNumber in enumerate(currentPageItems):
mapFilePath = u"iceboat/maps/{}.yml".format(mapNumber)
mapConfig = ps.config.loadConfig(mapFilePath)
displayName = mapConfig.get("display", u"§b§l【§e§l地图 {}§b§l】§f".format(mapNumber))
material = mapConfig.get("material", "PAPER")
lap = mapConfig.get("lap", "未知")
mapManager = MapManager(mapNumber)
difficulty = mapManager.getDifficulty()
difficultySuffix = mapManager.getDifficultySuffix()
available = roomsManager.getAvailableRoomCount(mapNumber)
total = roomsManager.getTotalRoomCount(mapNumber)
multiMapBestRecord = playerDataManager.getMultiBestMapRecord(mapNumber)
multiMapBestRecordDay = playerDataManager.getMultiBestMapRecordDay(mapNumber)
seasonMapBestRecord = playerDataManager.getSeasonBestMapRecord(mapNumber)
mapTimes = playerDataManager.getMapTimes(mapNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
lores = [
u"§e序号 §f{}".format(mapNumber),
u"§e难度 §f{} {}".format(difficulty, difficultySuffix),
u"§e圈数 §f{}".format(lap),
"",
u"§e剩余空房 §f{}/{}".format(available, total),
u"§e当季纪录 §f{:.2f}".format(seasonMapBestRecord) if seasonMapBestRecord else u"§e当季纪录 §f暂无",
]
if self.isSeason:
lores.append(u"§e当季标准 §f{:.2f}".format(seasonManager.getSeasonStandard(mapNumber)))
lores.append(u"§e当季分数 §f{:.2f}".format(playerDataManager.getSeasonMapScore(mapNumber)))
lores.extend([
"",
u"§e完成次数 §f{}".format(mapTimes) if mapTimes else u"§e完成次数 §f0",
u"§e多人纪录 §f{:.2f}".format(multiMapBestRecord) if multiMapBestRecord else u"§e多人纪录 §f暂无",
u"§e纪录日期 §f{}".format(multiMapBestRecordDay) if multiMapBestRecordDay else u"§e纪录日期 §f暂无",
"",
u"§6>> §a点击设置地图 §6<<",
])
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(displayName),
*lores,
data=str(mapNumber)
)
self.spawnSeparators(45, 53)
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页")
self.set(47, Material.LIME_STAINED_GLASS_PANE, u"§a按序号升序")
self.set(48, Material.ORANGE_STAINED_GLASS_PANE, u"§c按序号降序")
buttonText = u"§e仅看当季图池" if not self.isSeason else u"§e显示所有地图"
self.set(49, Material.YELLOW_STAINED_GLASS_PANE, buttonText)
self.set(50, Material.GREEN_STAINED_GLASS_PANE, u"§a按难度升序")
self.set(51, Material.RED_STAINED_GLASS_PANE, u"§c按难度降序")
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页")
playerPage[playerName] = {
"page": self.page,
"sortMode": self.sortMode,
"isSeason": self.isSeason,
"roomId": self.roomId
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
room = multiGames.get(str(self.roomId))
if not room or not room.isPlayerInRoom(playerName):
player.closeInventory()
return
if clickInt == 45 and self.page > 1:
MenuMulti(player, self.roomId, page=self.page - 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 47:
MenuMulti(player, self.roomId, sortMode="ascNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 48:
MenuMulti(player, self.roomId, sortMode="descNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 49:
MenuMulti(player, self.roomId, sortMode=self.sortMode, isSeason=not self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 50:
MenuMulti(player, self.roomId, sortMode="ascDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 51:
MenuMulti(player, self.roomId, sortMode="descDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 53 and self.page < self.totalPages:
MenuMulti(player, self.roomId, page=self.page + 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
mapNumber = getGUIValue(item)
if mapNumber is not None:
available = roomsManager.getAvailableRoomCount(mapNumber)
if available > 0:
mapNum = str(mapNumber)
if str(self.roomId) not in multiGames:
player.sendTitle(u"§f", u"§c房间未被创建请先进入房间", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
roomNum = roomsManager.getAvailableRoom(mapNum)
if roomNum is None:
player.sendTitle(u"§f", u"§c暂无空房", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
multiGames[str(self.roomId)].setIceboatMap(mapNum, roomNum)
multiGames[str(self.roomId)].createHologram() #更新全息显示
mapManager = MapManager(mapNum)
multiGames[str(self.roomId)].broadcastTitle(u"§f", u"§e{} §a将地图设置为 §e{}".format(player.getName(), mapManager.getDisplay()))
multiGames[str(self.roomId)].broadcastSound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
player.sendMessage(u"§c该地图暂无空房请稍后再试")
MenuMulti(player, self.roomId, self.page, self.sortMode, self.isSeason).openWithRender()
class MenuCollection(GUIController):
def __init__(self, player, page=1, itemType=u"particles", showOwnedOnly=False):
super(MenuCollection, self).__init__(player)
self.page = page
self.itemType = itemType
self.showOwnedOnly = showOwnedOnly
self.setInfo("iceboat.collection", u"§0冰船收集品菜单 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
items = list(collectionManager.getAllItems(self.itemType))
itemsCount = len(items)
playerData = PlayerDataManager(playerName)
ownedItems = list(playerData.getOwnedItems(self.itemType))
ownedItemsCount = len(ownedItems)
if self.showOwnedOnly:
items = ownedItems
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
self.totalPages = (len(items) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = items[startIndex:endIndex]
for index, itemNumber in enumerate(currentPageItems):
display = collectionManager.getDisplay(self.itemType, itemNumber)
material = collectionManager.getMaterial(self.itemType, itemNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
owned = False #默认未拥有
available = False #默认不可用
notForSale = False #默认可卖
goToMap = 0 #默认不跳转到地图
lore = []
if itemNumber in ownedItems:
owned = True
available = True
lore.append(u"§7已拥有")
lore.append(u"§f")
addLore = collectionManager.getCollectionLore(self.itemType, itemNumber)
if addLore is not None:
lore.extend(addLore)
conditions = collectionManager.getCollectionCondition(self.itemType, itemNumber)
color = u"§c"
if conditions[0] == u"notForSale":
notForSale = True
elif conditions[0] == u"cost":
price = conditions[1]
balance = playerData.getBalance()
if price <= balance:
available = True
color = u"§f"
lore.append(u"§e价格 {}{} DC币".format(color, price))
elif conditions[0] == u"level":
level = conditions[1]
playerLevel = playerData.getLevel()
if playerLevel >= level:
available = True
color = u"§f"
lore.append(u"§e驾照等级要求 {}{}".format(color, level))
elif conditions[0] == u"record":
lore.append(u"§e单人/多人纪录要求:")
recordDict = conditions[1]
satisfied = True
for mapNum, record in recordDict.items():
mapDisplay = MapManager(mapNum).getDisplay()
playerRecord = 0
playerSingleRecord = playerData.getSingleBestMapRecord(mapNum)
if playerSingleRecord > 0:
playerRecord = playerSingleRecord
playerMultiRecord = playerData.getMultiBestMapRecord(mapNum)
if playerMultiRecord > 0:
if playerRecord > 0:
if playerMultiRecord < playerRecord:
playerRecord = playerMultiRecord
else:
playerRecord = playerMultiRecord
color = u"§c"
if playerRecord > 0:
if playerRecord <= record:
color = u"§f"
else:
satisfied = False
lore.append(u"§7- {}{} {:.2f} <= {:.2f}".format(color, mapDisplay, playerRecord, record))
else:
satisfied = False
playerRecord = u"暂无成绩"
lore.append(u"§7- {}{} {} <= {:.2f}".format(color, mapDisplay, playerRecord, record))
if not satisfied and goToMap == 0:
goToMap = int(mapNum) #设置第一个不满足条件的地图为跳转地图
if satisfied:
available = True
elif conditions[0] == u"times":
lore.append(u"§e地图完成次数要求")
timesDict = conditions[1]
satisfied = True
for mapNum, times in timesDict.items():
mapDisplay = MapManager(mapNum).getDisplay()
playerTimes = playerData.getMapTimes(mapNum)
color = u"§c"
if playerTimes >= times:
color = u"§f"
else:
satisfied = False
lore.append(u"§7- {}{} {} >= {}".format(color, mapDisplay, int(playerTimes), int(times)))
if not satisfied and goToMap == 0:
goToMap = int(mapNum) #设置第一个不满足条件的地图为跳转地图
if satisfied:
available = True
lore.append(u"§f")
if owned:
lore.append(u"§6>> §a点击使用 §6<<")
goToMap = 0 #拥有则不跳转地图
elif available:
lore.append(u"§6>> §e点击获取 §6<<")
goToMap = 0 #可获得则不跳转地图
elif notForSale:
lore.append(u"§6>> §c非卖品 §6<<")
goToMap = 0 #非卖品则不跳转地图
else:
if conditions[0] in [u"times", u"record"]:
lore.append(u"§6<< §c未达要求点击挑战 §6>>")
else:
lore.append(u"§6<< §c未达要求 §6>>")
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(display), *lore,
data="{}:{}:{}:{}:{}".format(owned, available, self.itemType, itemNumber, goToMap))
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
self.set(47, Material.valueOf(playerData.getParticleMaterial()), u"§6>> §a选择粒子 §6<<", u'§7当前{}'.format(playerData.getParticleDisplay())) # 粒子类型按钮
self.set(48, Material.SPYGLASS, u"§6>> §b打开粒子预览页面 §6<<", u"§7仅在大厅可预览粒子效果") # 粒子预览
buttonText = u"§6>> §6仅看已拥有 §6<<" if not self.showOwnedOnly else u"§6>> §6显示所有内容 §6<<"
if self.itemType == u"particles":
collectionType = u"粒子"
elif self.itemType == u"boats":
collectionType = u"船型"
elif self.itemType == u"titles":
collectionType = u"称号"
completion = float(ownedItemsCount) / float(itemsCount) * 100
self.set(49, Material.YELLOW_STAINED_GLASS_PANE, buttonText, u"§e{}收集完成度§f{:.2f}% §7({}/{})".format(collectionType, completion, ownedItemsCount, itemsCount)) # 仅看已拥有/显示所有内容按钮
self.set(50, Material.valueOf(playerData.getBoatMaterial()), u"§6>> §a选择船 §6<<", u'§7当前{}'.format(playerData.getBoatDisplay())) # 船类型按钮
self.set(51, Material.valueOf(playerData.getTitleMaterial()), u"§6>> §a选择称号 §6<<", u'§7当前{}'.format(playerData.getTitleDisplay())) # 称号按钮
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
# 记录玩家当前页码和筛选设置
playerPage[playerName] = {
"page": self.page,
"showOwnedOnly": self.showOwnedOnly,
"itemType": self.itemType
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerManager = PlayerManager(player)
playerName = player.getName()
playerPageInfo = playerPage.get(playerName, {"page": 1, "itemType": u"boats", "showOwnedOnly": False})
currentPage = playerPageInfo["page"]
showOwnedOnly = playerPageInfo["showOwnedOnly"]
itemType = playerPageInfo["itemType"]
if clickInt == 45 and currentPage > 1:
MenuCollection(player, page=currentPage - 1, itemType=itemType, showOwnedOnly=showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 47:
MenuCollection(player, itemType=u"particles", showOwnedOnly=showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 48:
MenuPreviewing(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
elif clickInt == 49:
MenuCollection(player, itemType=itemType, showOwnedOnly=not showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 50:
MenuCollection(player, itemType=u"boats", showOwnedOnly=showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 51:
MenuCollection(player, itemType=u"titles", showOwnedOnly=showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 53 and currentPage < self.totalPages:
MenuCollection(player, page=currentPage + 1, itemType=itemType, showOwnedOnly=showOwnedOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
if item is not None:
guiValue = getGUIValue(item)
if guiValue:
owned, available, itemType, itemNumber, goToMap = guiValue.split(':')
playerData = PlayerDataManager(playerName)
owned = int(owned)
available = int(available)
if owned == 0: # 如果玩家还没有该收藏品
if available == 1: # 如果该收藏品可用
conditions = collectionManager.getCollectionCondition(itemType, itemNumber)
if conditions[0] == u"cost":
if vaultEconomy is not None:
balance = vaultEconomy.getBalance(player)
price = conditions[1]
if vaultEconomy.has(player, price): #如果玩家有足够的钱
vaultEconomy.withdrawPlayer(player, price) #消耗钱
ownedItems = playerData.getOwnedItems(itemType)
ownedItems.append(itemNumber)
playerData.setOwnedItems(itemType, ownedItems) #更新玩家收藏品列表
player.sendMessage(u"§a购买成功剩余 §e{} §aDC币".format(balance - price))
player.playSound(player, Sound.ENTITY_VILLAGER_YES, 1.0, 1.0)
else: #玩家钱不够则不修改数据
player.sendMessage(u"§c你只有 §e{} §cDC币无法购买".format(balance))
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
else:
player.sendMessage(u"§cVault 经济插件未正确配置,请联系管理员")
return
else: #如果该收藏品不是价格型且可用
ownedItems = playerData.getOwnedItems(itemType)
ownedItems.append(itemNumber)
playerData.setOwnedItems(itemType, ownedItems) #更新玩家收藏品列表
player.sendMessage(u"§a获取成功")
player.playSound(player, Sound.ENTITY_VILLAGER_YES, 1.0, 1.0)
else: #如果该收藏品不可用
if int(goToMap) != 0:
playerManager = PlayerManager(player)
playerManager.joinSingleRoom(goToMap)
else:
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
if itemType == "boats":
playerData.setBoatMaterial(itemNumber)
elif itemType == "particles":
playerData.setParticleType(itemNumber)
elif itemType == "titles":
playerData.setTitle(itemNumber)
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
MenuCollection(player, itemType=itemType, showOwnedOnly=showOwnedOnly).openWithRender()
room = playerManager.inMultiRoom() # 刷新多人盔甲架
if room: #如果房间存在
room.createMapDisplayArmorStands() #更新全息显示
class MenuPreviewing(GUIController):
def __init__(self, player, page=1):
super(MenuPreviewing, self).__init__(player)
self.page = page
self.setInfo("iceboat.previewing", u"§0粒子预览页面 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
items = list(collectionManager.getAllItems("particles"))
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
self.totalPages = (len(items) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = items[startIndex:endIndex]
for index, itemNumber in enumerate(currentPageItems):
display = collectionManager.getDisplay("particles", itemNumber)
material = collectionManager.getMaterial("particles", itemNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(display), u"§6>> §a点击预览 §6<<", data=u"{}".format(itemNumber))
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
self.set(49, Material.CHEST, u"§6>> §b打开收集品菜单 §6<<")
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
# 记录玩家当前页码和筛选设置
playerPage[playerName] = {"page": self.page}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
playerPageInfo = playerPage.get(playerName, {"page": 1})
currentPage = playerPageInfo["page"]
if clickInt == 45 and currentPage > 1:
MenuPreviewing(player, page=currentPage - 1).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 49:
MenuCollection(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
elif clickInt == 53 and currentPage < self.totalPages:
MenuPreviewing(player, page=currentPage + 1).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
if item is not None:
itemNumber = int(float(getGUIValue(item)))
if playerName in playersInParticlePreview: #如果玩家已经在预览粒子
playersInParticlePreview[playerName].stopPreviewing() #停止预览粒子
playerLoc = player.getLocation()
playerLoc.setY(playerLoc.getY() + 0.5)
player.teleport(playerLoc) #将玩家传送到自己的位置
playersInParticlePreview[playerName] = PlayerInParticlePreview(playerName, itemNumber) #创建玩家粒子预览对象
player.closeInventory() #关闭页面
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
class MenuLevel(GUIController):
def __init__(self, player, page=1, showAvailableOnly=True):
super(MenuLevel, self).__init__(player)
self.page = page
self.showAvailableOnly = showAvailableOnly
self.setInfo("iceboat.level", u"§0驾照等级页面 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
playerData = PlayerDataManager(playerName)
playerLevel = playerData.getLevel()
if self.showAvailableOnly:
items = list(levelManager.getAvailableLevels(playerLevel))
else:
items = list(levelManager.getAllLevels())
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
self.totalPages = (len(items) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = items[startIndex:endIndex]
for index, itemNumber in enumerate(currentPageItems):
levelNeed = levelManager.getLevelNeed(itemNumber)
levelTarget = levelManager.getLevelTarget(itemNumber)
levelEnough = True #默认等级足够
finished = False #默认未完成
goToMap = 0 #默认不跳转到地图
if playerLevel >= levelTarget: #超越目标等级
finished = True #无需领取
if playerLevel < levelNeed: #等级要求
levelEnough = False
if self.showAvailableOnly and (not levelEnough or finished):
continue # 跳过不符合条件的项
lore = [
u"§e等级要求 {}{}".format(u"§c" if not levelEnough else u"§f", levelNeed),
u"§e目标等级 {}{}".format(u"§c" if not finished else u"§f", levelTarget),
u"§f"
]
display = levelManager.getLevelDisplay(itemNumber)
material = levelManager.getLevelMaterial(itemNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
lore.append(u"§e单人纪录要求")
recordDict = levelManager.getLevelRecords(itemNumber)
color = u"§c"
available = False #默认不可用
satisfied = True #默认满足条件
for mapNum, record in recordDict.items():
mapManager = MapManager(mapNum)
mapDisplay = mapManager.getDisplay()
playerRecord = playerData.getSingleBestMapRecord(mapNum)
color = u"§c"
if playerRecord > 0:
if playerRecord <= record:
color = u"§f"
else:
satisfied = False
lore.append(u"§7- {}{} {:.2f} <= {:.2f}".format(color, mapDisplay, playerRecord, record))
else:
satisfied = False
playerRecord = u"暂无成绩"
lore.append(u"§7- {}{} {} <= {:.2f}".format(color, mapDisplay, playerRecord, record))
if not satisfied and goToMap == 0:
goToMap = int(mapNum) #设置第一个不满足条件的地图为跳转地图
if satisfied and levelEnough and not finished:
available = True #满足条件,标记可用
lore.append(u"§f")
if not levelEnough:
lore.append(u"§6<< §c未达等级 §6>>")
goToMap = 0 #等级不够无需跳转
elif finished:
lore.append(u"§6>> §a无需升级 §6<<")
goToMap = 0 #已经完成无需跳转
elif available:
lore.append(u"§6>> §e点击升级 §6<<")
goToMap = 0 #满足条件,无需跳转
else:
lore.append(u"§6>> §c未达要求点击挑战 §6<<")
amount = int((levelTarget - levelNeed) * 10)
if amount > 64:
amount = 64
elif amount < 1:
amount = 1
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(display), *lore,
data="{}:{}:{}".format(available, itemNumber, goToMap), amount=amount)
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
buttonText = u"§e仅看可升级" if not self.showAvailableOnly else u"§e显示所有内容"
self.set(49, Material.YELLOW_STAINED_GLASS_PANE, buttonText) # 仅看可以升级的内容
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
# 记录玩家当前页码和筛选设置
playerPage[playerName] = {
"page": self.page,
"showAvailableOnly": self.showAvailableOnly
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
playerPageInfo = playerPage.get(playerName, {"page": 1, "showAvailableOnly": False})
currentPage = playerPageInfo["page"]
showAvailableOnly = playerPageInfo["showAvailableOnly"]
if clickInt == 45 and currentPage > 1:
MenuLevel(player, page=currentPage - 1, showAvailableOnly=showAvailableOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 49:
MenuLevel(player, showAvailableOnly=not showAvailableOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 53 and currentPage < self.totalPages:
MenuLevel(player, page=currentPage + 1, showAvailableOnly=showAvailableOnly).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
if item is not None:
guiValue = getGUIValue(item)
if guiValue:
available, itemNumber, goToMap = guiValue.split(':')
playerData = PlayerDataManager(playerName)
if int(available) == 1: # 如果该收藏品可用
levelTarget = levelManager.getLevelTarget(itemNumber)
playerData.setLevel(levelTarget) #升级
player.sendMessage(u"§a升级成功")
player.playSound(player, Sound.ENTITY_PLAYER_LEVELUP, 1.0, 1.0)
else: #如果不可升级
if int(goToMap) != 0:
playerManager = PlayerManager(player)
playerManager.joinSingleRoom(goToMap)
else:
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
MenuLevel(player, showAvailableOnly=showAvailableOnly).openWithRender()
class MenuRanking(GUIController):
def __init__(self, player, page=1, rankingType=u"seasonScore", mapNum=1):
super(MenuRanking, self).__init__(player)
self.page = page
self.rankingType = rankingType
self.mapNum = mapNum
self.rankManager = RankManager()
display = MapManager(mapNum).getDisplay()
if rankingType == u"seasonScore":
self.setInfo("iceboat.ranking", u"§0排行榜 §1§l当季总分§0 - 第 {}".format(page))
elif rankingType == u"single":
self.setInfo("iceboat.ranking", u"§0排行榜 §1§l{}§0[单人] - 第 {}".format(display, page))
elif rankingType == u"multi":
self.setInfo("iceboat.ranking", u"§0排行榜 §1§l{}§0[多人] - 第 {}".format(display, page))
elif rankingType == u"mapTimes":
self.setInfo("iceboat.ranking", u"§0排行榜 §1§l{}§0[次数] - 第 {}".format(display, page))
else:
self.setInfo("iceboat.ranking", u"§0全服排名 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
mapManager = MapManager(self.mapNum)
mapDisplay = mapManager.getDisplay()
mapMaterial = mapManager.getMaterial()
singleConfig = ps.config.loadConfig("iceboat/ranking/singleMapRecord.yml")
multiConfig = ps.config.loadConfig("iceboat/ranking/multiMapRecord.yml")
seasonScoreConfig = ps.config.loadConfig("iceboat/ranking/seasonScore.yml")
mapTimesConfig = ps.config.loadConfig("iceboat/ranking/mapTimes.yml")
levelConfig = ps.config.loadConfig("iceboat/ranking/level.yml")
totalMapTimesConfig = ps.config.loadConfig("iceboat/ranking/totalMapTimes.yml")
singleMapSection = singleConfig.get(str(self.mapNum))
multiMapSection = multiConfig.get(str(self.mapNum))
mapTimesSection = mapTimesConfig.get(str(self.mapNum))
playersConfig = ps.config.loadConfig("iceboat/players.yml")
players = list(playersConfig.getKeys(False))
players.sort(key=lambda x: str(x))
def getScoreKey(x, config):
playerSection = config.get(x)
if playerSection is not None:
score = playerSection.get('score')
else:
if self.rankingType in [u"seasonScore", u"mapTimes"]:
score = 0
else:
score = 99999 #一天最多86400秒
return score
if self.rankingType == u"seasonScore":
players.sort(key=lambda x: getScoreKey(x, seasonScoreConfig), reverse=True)
if self.rankingType == u"single":
if singleMapSection is not None:
players.sort(key=lambda x: getScoreKey(x, singleMapSection))
elif self.rankingType == u"multi":
if multiMapSection is not None:
players.sort(key=lambda x: getScoreKey(x, multiMapSection))
elif self.rankingType == u"mapTimes":
if mapTimesSection is not None:
players.sort(key=lambda x: getScoreKey(x, mapTimesSection), reverse=True)
else:
if seasonScoreConfig is not None:
players.sort(key=lambda x: getScoreKey(x, seasonScoreConfig), reverse=True)
self.totalPages = (len(players) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPagePlayers = players[startIndex:endIndex]
for index, name in enumerate(currentPagePlayers):
if singleMapSection is None:
singleScore = u"暂无"
else:
singlePlayer = singleMapSection.get(name)
if singlePlayer is None:
singleScore = u"暂无"
else:
singleScore = singlePlayer.get('score')
if multiMapSection is None:
multiScore = u"暂无"
else:
multiPlayer = multiMapSection.get(name)
if multiPlayer is None:
multiScore = u"暂无"
else:
multiScore = multiPlayer.get('score')
playerDataManager = PlayerDataManager(name)
seasonMapScore = playerDataManager.getSeasonBestMapRecord(self.mapNum)
seasonScore = playerDataManager.getSeasonScore()
if seasonMapScore == 0:
seasonMapScore = u"暂无"
if mapTimesSection is None:
mapTimesScore = 0
else:
mapTimesPlayer = mapTimesSection.get(name)
if mapTimesPlayer is None:
mapTimesScore = 0
else:
mapTimesScore = mapTimesPlayer.get('score')
if levelConfig is None:
levelScore = 0
else:
levelPlayer = levelConfig.get(name)
if levelPlayer is None:
levelScore = 0
else:
levelScore = levelPlayer.get('score')
if totalMapTimesConfig is None:
totalMapTimesScore = 0
else:
totalMapTimesPlayer = totalMapTimesConfig.get(name)
if totalMapTimesPlayer is None:
totalMapTimesScore = 0
else:
totalMapTimesScore = totalMapTimesPlayer.get('score')
rank = startIndex + index + 1
lore = []
noRank = False
if self.rankingType == u'single':
if singleScore == u"暂无":
noRank = True
elif self.rankingType == u'multi':
if multiScore == u"暂无":
noRank = True
elif self.rankingType == u'mapTimes':
if mapTimesScore == 0:
noRank = True
elif seasonScore == 0:
noRank = True
if noRank:
lore.append(u"§7---- §c暂无排名 §7----")
else:
lore.append(u"§7---- §a第 §e{} §a名 §7----".format(rank))
lore.append(u"§e当季总分§f{:.2f}".format(seasonScore))
lore.append(u"§e驾照等级§f{}".format(levelScore))
lore.append(u"§e总完成次数§f{}".format(totalMapTimesScore))
if self.rankingType != u"seasonScore":
lore.append(u"§f")
if singleScore == u"暂无":
lore.append(u"§e单人纪录§f{}".format(singleScore))
else:
lore.append(u"§e单人纪录§f{:.2f}".format(singleScore))
if multiScore == u"暂无":
lore.append(u"§e多人纪录§f{}".format(multiScore))
else:
lore.append(u"§e多人纪录§f{:.2f}".format(multiScore))
if seasonMapScore == u"暂无":
lore.append(u"§e当季纪录§f{}".format(seasonMapScore))
else:
lore.append(u"§e当季纪录§f{:.2f}".format(seasonMapScore))
lore.append(u"§e完成次数§f{}".format(mapTimesScore))
try:
material = playerHeadsAPI.getPlayerHead(name)
except Exception:
material = Material.PLAYER_HEAD
self.set(index, material, u"§b§l【§e§l{}§b§l】".format(name), *lore)
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
playerSeasonScore = rankManager.getSeasonScoreRank(playerName)
playerSingleMapRecord = rankManager.getSingleMapRecordRank(playerName, self.mapNum)
playerMultiMapRecord = rankManager.getMultiMapRecordRank(playerName, self.mapNum)
playerMapTimes = rankManager.getMapTimesRank(playerName, self.mapNum)
try:
mapMaterial = getattr(Material, mapMaterial)
except AttributeError:
mapMaterial = Material.MAP
typeButtons = {
47: (u"按当季总分排序", Material.SKULL_BANNER_PATTERN, u"seasonScore",[u"{}".format(u"§7您的当季总分排在第 §f{} §7名".format(playerSeasonScore) if playerSeasonScore != 0 else u'§7您暂无当季总分的排名')]),
48: (u"按单人纪录排序", Material.NETHERITE_INGOT, u"single", [u"{}".format(u"§7您在{}§7{}".format(mapDisplay, u"的单人纪录排在第 §f{} §7名".format(playerSingleMapRecord) if playerSingleMapRecord != 0 else u'暂无单人纪录的排名') if self.rankingType != u"seasonScore" else u"§7当前排序方法无需设置地图")]),
49: (u"按多人纪录排序", Material.NETHERITE_SCRAP, u"multi", [u"{}".format(u"§7您在{}§7{}".format(mapDisplay, u"的多人纪录排在第 §f{} §7名".format(playerMultiMapRecord) if playerMultiMapRecord != 0 else u'暂无多人纪录的排名') if self.rankingType != u"seasonScore" else u"§7当前排序方法无需设置地图")]),
50: (u"按地图完成次数排序", Material.CLOCK, u"mapTimes", [u"{}".format(u"§7您在{}§7{}".format(mapDisplay, u"的完成次数排在第 §f{} §7名".format(playerMapTimes) if playerMapTimes != 0 else u'暂无完成次数的排名') if self.rankingType != u"seasonScore" else u"§7当前排序方法无需设置地图")]),
51: (u"§f点击打开地图切换页面", mapMaterial, None, [u"{}".format(u"§7修改需要查询排名的地图" if self.rankingType == u"seasonScore" else u"§7当前选中的地图为{}".format(mapDisplay))]),
}
for slot, (text, material, type, lore) in typeButtons.items():
current = u"§e§l当前页面" if type == self.rankingType else u"§f"
self.set(slot, material, u"§6>> {}{} §6<<".format(current, text), *lore)
# 记录玩家当前页码和筛选设置
playerPage[playerName] = {
"page": self.page,
"rankingType": self.rankingType,
"mapNum": self.mapNum
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
playerPageInfo = playerPage.get(playerName, {"page": 1, "rankingType": u"single", "mapNum": 1})
currentPage = playerPageInfo["page"]
rankingType = playerPageInfo["rankingType"]
mapNum = playerPageInfo["mapNum"]
if clickInt == 45 and currentPage > 1:
MenuRanking(player, page=currentPage - 1, rankingType=rankingType, mapNum=mapNum).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt in [47, 48, 49, 50]:
typeMapping = {47:u"seasonScore", 48: u"single", 49: u"multi", 50: u"mapTimes"}
newType = typeMapping[clickInt]
MenuRanking(player, rankingType=newType, mapNum=mapNum).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 51:
MenuRankingMapSelect(player).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 53 and currentPage < self.totalPages:
MenuRanking(player, page=currentPage + 1, rankingType=rankingType, mapNum=mapNum).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
class MenuRankingMapSelect(GUIController):# 单人房间菜单
def __init__(self, player, page=1, sortMode="default", isSeason=False):
super(MenuRankingMapSelect, self).__init__(player)
self.page = page
self.sortMode = sortMode
self.isSeason = isSeason
self.setInfo("iceboat.ranking", u"§0排行查询地图选择 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
mapNumbers = []
if self.isSeason:
mapNumbers = seasonManager.getSeasonMaps()
else:
mapNumbers = mapsManager.getMapsNum()
# 根据排序模式排序地图
if self.sortMode == "descDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)), reverse=True)
elif self.sortMode == "ascDifficulty":
mapNumbers.sort(key=lambda num: float(ps.config.loadConfig(u"iceboat/maps/{}.yml".format(num)).get("difficulty", 0)))
elif self.sortMode == "descNumber":
mapNumbers.sort(key=lambda num: int(num), reverse=True)
elif self.sortMode == "ascNumber":
mapNumbers.sort(key=lambda num: int(num))
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
self.totalPages = (len(mapNumbers) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = mapNumbers[startIndex:endIndex]
#创建物品
for index, mapNumber in enumerate(currentPageItems): # 为每个地图创建一个物品
mapFilePath = u"iceboat/maps/{}.yml".format(mapNumber)
mapConfig = ps.config.loadConfig(mapFilePath)
displayName = mapConfig.get("display", u"§b§l【§e§l地图 {}§b§l】§f".format(mapNumber))
material = mapConfig.get("material", "PAPER")
lap = mapConfig.get("lap", "未知")
mapManager = MapManager(mapNumber)
difficulty = mapManager.getDifficulty()
difficultySuffix = mapManager.getDifficultySuffix()
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(displayName),
u"§e序号 §f{}".format(mapNumber),
u"§e难度 §f{} {}".format(difficulty, difficultySuffix),
u"§e圈数 §f{}".format(lap),
"",
u"§6>> §a点击选择 §6<<",
data=str(mapNumber)
)
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
self.set(47, Material.LIME_STAINED_GLASS_PANE, u"§a按序号升序") # 序号升序按钮
self.set(48, Material.ORANGE_STAINED_GLASS_PANE, u"§c按序号降序") # 序号降序按钮
buttonText = u"§e仅看当季图池" if not self.isSeason else u"§e显示所有地图"
self.set(49, Material.YELLOW_STAINED_GLASS_PANE, buttonText) # 当季图池按钮
self.set(50, Material.GREEN_STAINED_GLASS_PANE, u"§a按难度升序")# 难度升序按钮
self.set(51, Material.RED_STAINED_GLASS_PANE, u"§c按难度降序") # 难度降序按钮
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
# 记录玩家当前页码和排序模式
playerPage[playerName] = {
"page": self.page,
"sortMode": self.sortMode,
"isSeason": self.isSeason
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
if clickInt == 45 and self.page > 1:
MenuRankingMapSelect(player, page=self.page - 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 47:
MenuRankingMapSelect(player, sortMode="ascNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 48:
MenuRankingMapSelect(player, sortMode="descNumber", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 49:
MenuRankingMapSelect(player, sortMode=self.sortMode, isSeason=not self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 50:
MenuRankingMapSelect(player, sortMode="ascDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 51:
MenuRankingMapSelect(player, sortMode="descDifficulty", isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, 1.0, 1.0)
elif clickInt == 53 and self.page < self.totalPages:
MenuRankingMapSelect(player, page=self.page + 1, sortMode=self.sortMode, isSeason=self.isSeason).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
mapNumber = getGUIValue(item)
if mapNumber is not None:
MenuRanking(player, mapNum=int(mapNumber), rankingType=u"single").openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
class MenuSpectate(GUIController): # 旁观菜单
def __init__(self, player, page=1, sortMode=u"default"):
super(MenuSpectate, self).__init__(player)
self.page = page
self.sortMode = sortMode
self.setInfo("iceboat.spectate", u"§0旁观玩家选择 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
itemsPerPage = 45 # 每页显示 45 个物品,最后一行用于控制
singlePlayers = []
for playerName in singleGames:
if singleGames[playerName].isPreparing:
continue
singlePlayers.append(playerName)
multiPlayers = []
for roomId, room in multiGames.items():
if room.isRunning:
if room.isPreparing:
continue
for playerName in room.playerList:
multiPlayers.append(playerName)
if self.sortMode == "single":
players = singlePlayers
elif self.sortMode == "multi":
players = multiPlayers
else:
players = list(singlePlayers + multiPlayers)
players.sort(key=lambda x: str(x))
self.totalPages = (len(players) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPagePlayers = players[startIndex:endIndex]
for index, name in enumerate(currentPagePlayers):
lores = []
if name in singlePlayers:
mapDisplay = singleGames[name].map.display
lores.append(u"§e单人模式 §f{}".format(mapDisplay))
elif name in multiPlayers:
for roomId, room in multiGames.items():
if name in room.playerList:
mapDisplay = room.map.display
lores.append(u"§e多人模式[{}] §f{}".format(roomId, mapDisplay))
break
try:
material = playerHeadsAPI.getPlayerHead(name)
except Exception:
material = Material.PLAYER_HEAD
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(name), *lores, data=str(name))
self.spawnSeparators(45, 53) # 填充最后一行默认黑色染色玻璃板
if self.page > 1:
self.set(45, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页") # 上一页按钮
if self.page < self.totalPages:
self.set(53, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页") # 下一页按钮
self.set(48, Material.LIME_STAINED_GLASS_PANE, u"§a查看单人游戏中玩家")
self.set(49, Material.ORANGE_STAINED_GLASS_PANE, u"§a查看多人游戏中玩家")
self.set(50, Material.YELLOW_STAINED_GLASS_PANE, u"§e查看所有游戏中玩家")
playerPage[playerName] = {
"page": self.page,
"sortMode": self.sortMode
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
playerPageInfo = playerPage.get(playerName, {"page": 1, "sortMode": u"default"})
currentPage = playerPageInfo["page"] if hasattr(playerPageInfo, "page") else 1
sortMode = playerPageInfo["sortMode"] if hasattr(playerPageInfo, "sortMode") else u"default"
if clickInt == 45 and currentPage > 1:
MenuSpectate(player, page=currentPage - 1, sortMode=sortMode).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 53 and currentPage < self.totalPages:
MenuSpectate(player, page=currentPage + 1, sortMode=sortMode).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 48:
MenuSpectate(player, sortMode=u"single").openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 49:
MenuSpectate(player, sortMode=u"multi").openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 50:
MenuSpectate(player, sortMode=u"default").openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
else:
item = e.getCurrentItem()
name = getGUIValue(item)
if name is not None:
target = getServer().getPlayer(name)
playersInSpectate[playerName] = PlayerInSpectate(player)
result = playersInSpectate[playerName].attachBoat(target)
if result is True:
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
player.closeInventory()
else:
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
MenuSpectate(player, page=currentPage, sortMode=sortMode).openWithRender()
class MenuCompetition(GUIController):
def __init__(self, player, page=1, setTargetScore=False):
super(MenuCompetition, self).__init__(player)
self.page = page
self.setTargetScore = setTargetScore
if self.setTargetScore:
self.setInfo("iceboat.competition", u"§0比赛抢分设置 - 第 {}".format(page))
else:
self.setInfo("iceboat.competition", u"§0比赛房间地图选择 - 第 {}".format(page))
self.setGUIManager(guiManager)
def render(self):
player = self.getPlayer()
playerName = player.getName()
if self.setTargetScore:
mapNumbers = [10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200]
else:
if competitionGame.mapsList:
mapNumbers = list(competitionGame.mapsList)
mapNumbers.sort(key=lambda num: int(num))
else:
mapNumbers = []
itemsPerPage = 18
self.totalPages = (len(mapNumbers) + itemsPerPage - 1) // itemsPerPage
startIndex = (self.page - 1) * itemsPerPage
endIndex = startIndex + itemsPerPage
currentPageItems = mapNumbers[startIndex:endIndex]
if self.setTargetScore:
for index, targetScore in enumerate(currentPageItems):
material = Material.GOLD_INGOT
amount = int(targetScore/10)
displayName = u"§6>> §e{}分 §6<<".format(targetScore)
lores = [
u"§7点击设置抢分目标"
]
self.set(index, material, displayName, *lores, data=str(targetScore), amount=amount)
else:
playerDataManager = PlayerDataManager(playerName)
for index, mapNumber in enumerate(currentPageItems):
mapFilePath = u"iceboat/maps/{}.yml".format(mapNumber)
mapConfig = ps.config.loadConfig(mapFilePath)
displayName = mapConfig.get("display", u"§b§l【§e§l地图 {}§b§l】§f".format(mapNumber))
material = mapConfig.get("material", "PAPER")
lap = mapConfig.get("lap", "未知")
mapManager = MapManager(mapNumber)
difficulty = mapManager.getDifficulty()
difficultySuffix = mapManager.getDifficultySuffix()
available = roomsManager.getAvailableRoomCount(mapNumber)
total = roomsManager.getTotalRoomCount(mapNumber)
seasonMapBestRecord = playerDataManager.getSeasonBestMapRecord(mapNumber)
try:
material = getattr(Material, material)
except AttributeError:
material = Material.PAPER
lores = [
u"§e序号 §f{}".format(mapNumber),
u"§e难度 §f{} {}".format(difficulty, difficultySuffix),
u"§e圈数 §f{}".format(lap),
"",
u"§e剩余空房 §f{}/{}".format(available, total),
u"§e当季纪录 §f{:.2f}".format(seasonMapBestRecord) if seasonMapBestRecord else u"§e当季纪录 §f暂无",
u"§e当季标准 §f{:.2f}".format(seasonManager.getSeasonStandard(mapNumber)),
u"§e当季分数 §f{:.2f}".format(playerDataManager.getSeasonMapScore(mapNumber))
]
self.set(index, material, u"§b§l【§e§l{}§b§l】§f".format(displayName),
*lores,
data=str(mapNumber)
)
self.spawnSeparators(18, 26)
if self.page > 1:
self.set(18, Material.WHITE_STAINED_GLASS_PANE, u"§a上一页")
buttonText = u"§e设置抢分目标" if self.setTargetScore else u"§e设置比赛地图"
self.set(22, Material.YELLOW_STAINED_GLASS_PANE, buttonText)
if self.page < self.totalPages:
self.set(26, Material.WHITE_STAINED_GLASS_PANE, u"§a下一页")
playerPage[playerName] = {
"page": self.page,
"setTargetScore": self.setTargetScore
}
def onClick(self, e):
e.setCancelled(True)
clickInt = e.getSlot()
player = self.getPlayer()
playerName = player.getName()
if not competitionGame.isPlayerInRoom(playerName):
player.closeInventory()
return
if clickInt == 18 and self.page > 1:
MenuCompetition(player, page=self.page - 1, setTargetScore=self.setTargetScore).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 22:
MenuCompetition(player, page=self.page, setTargetScore=not self.setTargetScore).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt == 26 and self.page < self.totalPages:
MenuCompetition(player, page=self.page + 1, setTargetScore=self.setTargetScore).openWithRender()
player.playSound(player, Sound.ITEM_BOOK_PAGE_TURN, 1.0, 1.0)
elif clickInt < 45:
item = e.getCurrentItem()
if self.setTargetScore:
targetScore = getGUIValue(item)
if targetScore is not None:
result = competitionGame.setTargetScore(targetScore)
if result:
competitionGame.broadcastTitle(u"§f", u"§e{} §a将比赛目标设置为 §e{}".format(player.getName(), targetScore))
competitionGame.broadcastSound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
competitionGame.createHologram() #更新全息显示
player.closeInventory()
else:
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
player.sendTitle(u"§f", u"§c已有达到该分数的玩家设置失败", 5, 60, 10)
player.closeInventory()
else:
mapNumber = getGUIValue(item)
if mapNumber is not None:
if competitionGame.round == 1:
player.sendTitle(u"§f", u"§c首局地图不可更改", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return False
available = roomsManager.getAvailableRoomCount(mapNumber)
if available > 0:
mapNum = str(mapNumber)
roomNum = roomsManager.getAvailableRoom(mapNum)
if roomNum is None:
player.sendTitle(u"§f", u"§c暂无空房", 5, 60, 10)
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
competitionGame.setIceboatMap(mapNum, roomNum)
competitionGame.createHologram() #更新全息显示
mapManager = MapManager(mapNum)
competitionGame.broadcastTitle(u"§f", u"§e{} §a将地图设置为 §e{}".format(player.getName(), mapManager.getDisplay()))
competitionGame.broadcastSound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
player.sendMessage(u"§c该地图暂无空房请稍后再试")
MenuCompetition(player, self.page, self.setTargetScore).openWithRender()
def onInventoryClick(e): # 菜单点击
player = e.getWhoClicked()
worldName = player.getWorld().getName()
if worldName != WORLD_NAME: # 仅在冰船世界启用
return
inv = e.getClickedInventory()
if inv is None:
return
invHolder = inv.getHolder()
if isGUI(invHolder):
guiManager.get(e.getWhoClicked(), e.getClickedInventory().getHolder().getName()).onClick(e) # 菜单控制
return
cancelShiftClickFromPlayerInventory(e)
if inv.getType().name() == 'PLAYER': #处理玩家背包点击事件
if player.getGameMode() == GameMode.CREATIVE: #不影响创造模式玩家
return
e.setCancelled(True) # 阻止玩家调整背包物品
clickedItem = e.getCurrentItem()
if clickedItem:
itemType = clickedItem.getType()
if itemType == Material.AIR: #不处理点击空气
return
playerManager = PlayerManager(player)
if itemType == Material.MAGMA_CREAM: #无论何时都可以尝试退出
iceboatCommand(player, "ib", ["leave", playerManager.playerName])
return
elif itemType == Material.MUSIC_DISC_RELIC: #无论何时都可以关闭音乐
player.stopSound(SoundCategory.RECORDS)
player.sendMessage(u"§a当前音乐已关闭")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif itemType == Material.ENDER_EYE and (playerManager.inSpectateRoom() or playerManager.inLobby()): #旁观菜单
MenuSpectate(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif playerManager.inAnyRoom() and not playerManager.inLobby(): #房间内禁止其它操作
player.sendMessage(u"§c游戏内禁止该操作")
player.playSound(player, Sound.ENTITY_VILLAGER_NO, 1.0, 1.0)
return
else:
if itemType == Material.SLIME_BALL: #打开单人模式菜单
iceboatMenuCommand(player, "", ["single"])
return
elif itemType == Material.NETHER_STAR: #快速进入上次游玩地图的单人模式
mapNumber = int(getGUIValue(clickedItem))
playerManager.joinSingleRoom(mapNumber)
elif itemType == Material.CHEST: #打开收集品菜单
MenuCollection(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif itemType == Material.PLAYER_HEAD: #玩家信息显示
playerManager.updateInventoryPlayerInfo()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif itemType == Material.SPYGLASS: #粒子预览页面
MenuPreviewing(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif itemType == Material.PAPER: #驾照等级页面
MenuLevel(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
elif itemType == Material.SKULL_BANNER_PATTERN: #排名查询页面
MenuRanking(player).openWithRender()
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
return
playerData = PlayerDataManager(playerManager.playerName) #玩家设置修改
if itemType == Material.FIREWORK_STAR: #粒子模式
result = playerData.toggleModeBoolean("particleMode")
playerManager.updateInventoryMode(mode=u"particle")
if result:
player.sendMessage(u"§a自定义粒子模式已开启")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.sendMessage(u"§6自定义粒子模式已关闭")
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
elif itemType == Material.OAK_HANGING_SIGN: #详细播报模式
result = playerData.toggleModeBoolean("detailMode")
playerManager.updateInventoryMode(mode=u"detail")
if result:
player.sendMessage(u"§a详细播报模式已开启")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.sendMessage(u"§6详细播报模式已关闭")
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
elif itemType == Material.BEACON: #广播模式
result = playerData.toggleModeBoolean("broadcastMode")
playerManager.updateInventoryMode(mode=u"broadcast")
if result:
player.sendMessage(u"§a成绩广播模式已开启")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.sendMessage(u"§6成绩广播模式已关闭")
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
elif itemType == Material.NOTE_BLOCK: #音乐模式
result = playerData.toggleModeBoolean("musicMode")
playerManager.updateInventoryMode(mode=u"music")
if result:
player.sendMessage(u"§a音乐模式已开启")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
player.sendMessage(u"§6音乐模式已关闭")
player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BASS, 1.0, 1.0)
def onInventoryClose(e): # 菜单关闭
inv = e.getInventory()
if isGUI(inv.getHolder()):
guiManager.remove(e.getPlayer()) # 关闭菜单时清空存储的控制器
def stop(): # 程序卸载时
closeGuiForAll() # 关菜单
ps.listener.registerListener(onInventoryClick, InventoryClickEvent)
ps.listener.registerListener(onInventoryClose, InventoryCloseEvent)
ps.listener.registerListener(denyAllInventoryDrag, InventoryDragEvent)
# 机制修改 -----------------------------------------------------------------------------------------
def onPlayerDropItem(event): # 阻止玩家丢弃物品
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME and player.getGameMode() != GameMode.CREATIVE:
event.setCancelled(True)
def preventItemFrameDamage(event): # 阻止玩家打掉展示框
if event.getEntityType() == EntityType.ITEM_FRAME:
damager = event.getDamager()
worldName = damager.getWorld().getName()
if worldName == WORLD_NAME:
if isinstance(damager, Player):
if damager.getGameMode() != GameMode.CREATIVE:
event.setCancelled(True)
def preventItemFrameRotation(event): # 阻止玩家旋转展示框
if event.getRightClicked().getType() == EntityType.ITEM_FRAME:
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME:
if player.getGameMode() != GameMode.CREATIVE:
event.setCancelled(True)
def onPlayerSwapHandItems(event): # 阻止切换主副手
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME:
if player.getGameMode() != GameMode.CREATIVE:
event.setCancelled(True)
def playerExitVehicle(event): # 下船机制变革
exited = event.getExited() #获取离开的实体
if isinstance(exited, Player): # 只处理玩家退出情况
worldName = exited.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
boat = event.getVehicle() #获取船只
if isinstance(boat, Boat): # 只处理玩家退出船只的情况
playerManager = PlayerManager(exited)
if playerManager.delayExit():
event.setCancelled(True)
def onPlayerInteract(event): # 玩家交互处理:禁止修改告示牌、物品栏使用
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME and event.getHand() == HAND: #仅冰船世界生效、不重复触发副手
action = event.getAction()
if action in [Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK]: # 检查是否为右键点击告示牌
clickedBlock = event.getClickedBlock()
if action == Action.RIGHT_CLICK_BLOCK: #右键方块
blockType = clickedBlock.getType()
if blockType in SIGNS:
if not (player.isOp() and player.isSneaking()): # 检查玩家是否拥有 OP 权限且蹲下
event.setCancelled(True)
return #点击告示牌后不触发物品栏检测
elif blockType == Material.LECTERN: #不影响阅读讲台
return
item = player.getInventory().getItemInMainHand()
itemType = item.getType()
if itemType == Material.AIR: #不处理点击空气
return
#处理物品栏右键
playerManager = PlayerManager(player)
if itemType == Material.SLIME_BALL: #打开单人模式菜单
if playerManager.actionCooldown():
return
iceboatMenuCommand(playerManager.player, "", ["single"])
elif itemType == Material.NETHER_STAR: #快速进入上次游玩地图的单人模式
if (playerManager.inAnyRoom()) or (not playerManager.inLobby()): #房间内禁止其它操作
return
mapNumber = int(getGUIValue(item))
if mapNumber is not None:
playerManager.joinSingleRoom(mapNumber)
elif itemType == Material.MAGMA_CREAM: #离开房间
if playerManager.actionCooldown():
return
iceboatCommand(playerManager.player, "ib", ["leave", playerManager.playerName])
elif itemType == Material.MUSIC_DISC_RELIC:
player.stopSound(SoundCategory.RECORDS)
player.sendMessage(u"§a当前音乐已关闭")
player.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0, 1.0)
else:
return
event.setCancelled(True)
def onEntityLoad(event): # 加载时删除空船
for entity in event.getEntities():
if isinstance(entity, Boat):
worldName = entity.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
if entity.isEmpty(): # 检测空船
entity.remove()
def cancelPlayerDamageInIceboatWorld(event): # 取消玩家受伤事件
entity = event.getEntity()
worldName = entity.getWorld().getName()
if worldName == WORLD_NAME:
if isinstance(entity, Player):
if event.getCause() == EntityDamageEvent.DamageCause.VOID:
playerManager = PlayerManager(entity)
playerManager.teleportToLobby()
event.setCancelled(True)
def onPlayerQuit(event): # 玩家退出处理
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
playerManager = PlayerManager(player)
playerManager.removeBoat()
playerManager.outGame()
playerManager.removePlayerFromRoom()
def onPlayerJoin(event): # 玩家加入处理
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
playerManager = PlayerManager(player)
playerManager.removeBoat()
playerManager.outGame()
playersManager.addPlayerAsync(playerManager.playerName)
ps.scheduler.runTaskLater(lambda pm=playerManager : pm.updateInventory(), 1) #延迟设置玩家背包
def onPlayerTeleportEvent(event): # 玩家进入冰船世界处理
toWorldName = event.getTo().getWorld().getName()
fromWorldName = event.getFrom().getWorld().getName()
if toWorldName == WORLD_NAME: #进入冰船世界
if fromWorldName == WORLD_NAME: #仅处理玩家从其他世界进入冰船世界的情况
return
player = event.getPlayer()
playerManager = PlayerManager(player)
playersManager.addPlayerAsync(playerManager.playerName)
ps.scheduler.runTaskLater(lambda pm=playerManager : pm.updateInventory(), 1) #延迟设置玩家背包
if fromWorldName == WORLD_NAME: #离开冰船世界
if toWorldName == WORLD_NAME: #仅处理玩家从冰船世界离开冰船世界的情况
return
player = event.getPlayer()
playerManager = PlayerManager(player)
playerManager.removeBoat()
playerManager.outGame()
playerManager.removePlayerFromRoom()
def onArmorStandManipulate(event): #取消盔甲架交互
armorStand = event.getRightClicked()
worldName = armorStand.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
event.setCancelled(True)
def onPlayerStopSPectating(event): #玩家离开观察者附身
player = event.getPlayer()
worldName = player.getWorld().getName()
if worldName == WORLD_NAME: #仅作用于冰船世界
playerName = player.getName()
if playerName in playersInSpectate:
playerManager = PlayerManager(player)
if playerManager.inSpectateRoom():
if not playersInSpectate[playerName].leaving:
event.setCancelled(True)
ps.listener.registerListener(onPlayerInteract, PlayerInteractEvent)
ps.listener.registerListener(onPlayerDropItem, PlayerDropItemEvent)
ps.listener.registerListener(preventItemFrameDamage, EntityDamageByEntityEvent)
ps.listener.registerListener(preventItemFrameRotation, PlayerInteractEntityEvent)
ps.listener.registerListener(onPlayerSwapHandItems, PlayerSwapHandItemsEvent)
ps.listener.registerListener(playerExitVehicle, VehicleExitEvent)
ps.listener.registerListener(onEntityLoad, EntitiesLoadEvent)
ps.listener.registerListener(cancelPlayerDamageInIceboatWorld, EntityDamageEvent)
ps.listener.registerListener(onPlayerJoin, PlayerJoinEvent)
ps.listener.registerListener(onPlayerQuit, PlayerQuitEvent)
ps.listener.registerListener(onPlayerTeleportEvent, PlayerTeleportEvent)
ps.listener.registerListener(onArmorStandManipulate, PlayerArmorStandManipulateEvent)
ps.listener.registerListener(onPlayerStopSPectating, PlayerStopSpectatingEntityEvent) # 取消所有物品栏拖动
# 重载处理 -----------------------------------------------------------------------------------------
def reloadAll():
removeScoreboardObjective(u"IBPlayerRank")
removeScoreboardObjective(u"IBScoreList")
removeScoreboardTeam(u"Competition")
world = worldManager.getWorld() #仅修改冰船世界
roomsManager.resetAvailableRooms() #重置可用房间
players = world.getPlayers()
for player in players: #检测所有玩家
if player.getGameMode() == GameMode.CREATIVE: #不影响创造模式玩家
continue
player.removePotionEffect(PotionEffectType.INVISIBILITY) #取消可能的隐身
player.closeInventory() #关闭正在打开的菜单
playerLoc = player.getLocation()
playerX = playerLoc.getX()
playerZ = playerLoc.getZ()
playerManager = PlayerManager(player)
playerManager.outGame() #重置玩家状态
playerManager.updateInventory() #重置玩家背包
if not (-LOBBY_RADIUS < playerX < LOBBY_RADIUS) and not (-LOBBY_RADIUS < playerZ < LOBBY_RADIUS): #如果玩家在大厅内
playerManager.removeBoat()
playerManager.teleportToLobby()
entities = world.getEntities()
for entity in entities: #检测大厅实体
if isinstance(entity, ArmorStand) or isinstance(entity, EntityType.ITEM_FRAME.getEntityClass()) or isinstance(entity, Boat):
entityLoc = entity.getLocation()
entityLocX = entityLoc.getX()
entityLocZ = entityLoc.getZ()
if -LOBBY_RADIUS < entityLocX < LOBBY_RADIUS and -LOBBY_RADIUS < entityLocZ < LOBBY_RADIUS: #如果实体在大厅内
isValid = False #标记无效
if isinstance(entity, ArmorStand):
for room in multiGames.values(): # 遍历所有多人游戏房间,检查盔甲架是否有效
if entity in room.mapDisplayArmorStands:
isValid = True
break
elif isinstance(entity, EntityType.ITEM_FRAME.getEntityClass()):
for room in multiGames.values():
if entity == room.mapDisplayItemFrame: # 遍历所有多人游戏房间,检查展示框是否有效
isValid = True
break
elif isinstance(entity, Boat):
for preview in playersInParticlePreview.values(): # 遍历所有单人游戏房间,检查船是否有效
if entity == preview.boat:
isValid = True
break
if not isValid:
entity.remove()
reloadAll()
hallOfFameManager.updateHallOfFame() # 更新名人堂