5477 lines
275 KiB
Python
5477 lines
275 KiB
Python
# 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() # 更新名人堂 |