人工代码初始化,entities文件夹保留
This commit is contained in:
1 parent
e3efeb74ad
commit
98e9903f73
48 files changed
+6545
No files matched your search
@@ -0,0 +1,323 @@
|
||||
# coding=utf-8
|
||||
import time
|
||||
|
||||
from FastComp import *
|
||||
from ToolsConfig import *
|
||||
|
||||
CdTags = {}
|
||||
|
||||
|
||||
class BlueFoxFilterEnum(object):
|
||||
"""
|
||||
事件过滤器枚举值
|
||||
"""
|
||||
|
||||
class ServerEvent(object):
|
||||
class ProjectileDoHitEffectEvent(object):
|
||||
@staticmethod
|
||||
def IsEntityInList(entityStrType):
|
||||
# type:(List) -> Callable
|
||||
|
||||
"""
|
||||
是否为规定的生物
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return serverCompFactory.CreateEngineType(args['id']).GetEngineTypeStr() in entityStrType
|
||||
|
||||
return _
|
||||
|
||||
class NormalEvent(object):
|
||||
@staticmethod
|
||||
def NotInCd(cdTag, cdSize):
|
||||
# type:(str,float) -> Callable
|
||||
"""
|
||||
是否在CdTag所设置的Cd之外,注意cdTag是所有事件通用的
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
lastTime = CdTags.get(MOD_NAME + cdTag, 0)
|
||||
if time.time() - lastTime >= cdSize:
|
||||
CdTags[MOD_NAME + cdTag] = time.time()
|
||||
return True
|
||||
return False
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsNight():
|
||||
"""
|
||||
是否为黑夜
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
_time = serverCompFactory.CreateTime(LevelId).GetTime()
|
||||
_time %= 24000
|
||||
return 12000 <= _time < 24000
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsDay():
|
||||
"""
|
||||
是否为白天
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
_time = serverCompFactory.CreateTime(LevelId).GetTime()
|
||||
_time %= 24000
|
||||
return 0 <= _time < 12000
|
||||
|
||||
return _
|
||||
|
||||
class DestroyBlockEvent(object):
|
||||
@staticmethod
|
||||
def BlockNameInList(blockNameList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
被破坏的方块是否在规定的方块列表内
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['fullName'] in blockNameList
|
||||
|
||||
return _
|
||||
|
||||
class ServerPlayerTryDestroyBlockEvent(object):
|
||||
@staticmethod
|
||||
def BlockNameInList(blockNameList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
被破坏的方块是否在规定的方块列表内
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['fullName'] in blockNameList
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def NotCancel():
|
||||
"""
|
||||
事件未被取消
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return not args['cancel']
|
||||
|
||||
return _
|
||||
|
||||
class ServerBlockUseEvent(object):
|
||||
|
||||
@staticmethod
|
||||
def BlockNameInList(blockNameList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
被使用方块是否在规定的方块列表内
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['blockName'] in blockNameList
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def NoCarriedItem():
|
||||
"""
|
||||
当没有手持物品时
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return not bool(serverCompFactory.CreateItem(args['playerId']).GetPlayerItem(2, 0))
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def CarriedItem():
|
||||
"""
|
||||
当有手持物品时
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return bool(serverCompFactory.CreateItem(args['playerId']).GetPlayerItem(2, 0))
|
||||
|
||||
return _
|
||||
|
||||
class ItemUseOnAfterServerEvent(object):
|
||||
|
||||
@staticmethod
|
||||
def NotInCd(cdTag, cdSize):
|
||||
# type:(str,float) -> Callable
|
||||
"""
|
||||
是否在CdTag所设置的Cd之外
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
lastTime = CdTags.get(MOD_NAME + cdTag, 0)
|
||||
if time.time() - lastTime >= cdSize:
|
||||
CdTags[MOD_NAME + cdTag] = time.time()
|
||||
return True
|
||||
return False
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsBlock(blockName):
|
||||
# type:(str) -> Callable
|
||||
"""
|
||||
是否是规定的被使用方块
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['blockName'] == blockName
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def BlockNameInList(blockNameList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
被使用方块是否在规定的方块列表内
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['blockName'] in blockNameList
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsItemDict(itemDict):
|
||||
# type:(Dict[str]) -> Callable
|
||||
"""
|
||||
被使用的物品是否为规定的物品
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return IsSameItem(args['itemDict'], itemDict)
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def ItemNameInList(itemNameList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
被使用的物品是否在规定的物品列表内
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['itemDict']['itemName'] in itemNameList
|
||||
|
||||
return _
|
||||
|
||||
class DamageEvent(object):
|
||||
@staticmethod
|
||||
def DamageFilter(damage=0):
|
||||
"""
|
||||
伤害数值过滤器, 当伤害大于等于输入的伤害时将会触发
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['damage_f'] >= damage
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def CauseInList(causeList):
|
||||
# type:(List[str]) -> Callable
|
||||
"""
|
||||
伤害类型过滤器, 当于输入的类型相同时触发
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['cause'] in causeList
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IgniteFilter():
|
||||
"""
|
||||
忽视过滤器, 未被忽视时触发
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return not args['ignite']
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def AttackerIsPlayer():
|
||||
"""
|
||||
攻击者是否为玩家
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return IsPlayer(args['srcId'])
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def InjuredIsPlayer():
|
||||
"""
|
||||
受伤者者是否为玩家
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return IsPlayer(args['entityId'])
|
||||
|
||||
return _
|
||||
|
||||
class ServerBlockEntityTickEvent(object):
|
||||
@staticmethod
|
||||
def BlockNameInList(blockNameList):
|
||||
"""
|
||||
方块名称过滤器
|
||||
"""
|
||||
|
||||
def _(args):
|
||||
return args['blockName'] in blockNameList
|
||||
|
||||
return _
|
||||
|
||||
class ClientEvent(object):
|
||||
class NormalEvent(object):
|
||||
@staticmethod
|
||||
def NotInCd(cdTag, cdSize):
|
||||
# type:(str,float) -> Callable
|
||||
"""
|
||||
是否在CdTag所设置的Cd之外,注意cdTag是所有事件通用的
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
lastTime = CdTags.get(MOD_NAME + cdTag, 0)
|
||||
if time.time() - lastTime >= cdSize:
|
||||
CdTags[MOD_NAME + cdTag] = time.time()
|
||||
return True
|
||||
return False
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsNight():
|
||||
"""
|
||||
是否为黑夜
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
_time = serverCompFactory.CreateTime(LevelId).GetTime()
|
||||
_time %= 24000
|
||||
return 12000 <= _time < 24000
|
||||
|
||||
return _
|
||||
|
||||
@staticmethod
|
||||
def IsDay():
|
||||
"""
|
||||
是否为白天
|
||||
"""
|
||||
|
||||
def _(*args):
|
||||
_time = serverCompFactory.CreateTime(LevelId).GetTime()
|
||||
_time %= 24000
|
||||
return 0 <= _time < 12000
|
||||
|
||||
return _
|
||||
@@ -0,0 +1,88 @@
|
||||
# coding=utf-8
|
||||
from ItemTools import IsSameItem
|
||||
from ToolsConfig import serverCompFactory, clientCompFactory, serverApi, LevelId
|
||||
|
||||
|
||||
def DictFromBlockEntityData(blockEntityData, posDim):
|
||||
"""
|
||||
从方块实体中获取全部数据
|
||||
:param blockEntityData:方块实体自定义数据
|
||||
:param posDim:坐标维度
|
||||
:return:dict
|
||||
"""
|
||||
data = getattr(getattr(blockEntityData, '_BlockEntityData__exDataMgr'), 'mBlockEntityData')
|
||||
return data[posDim[3]][posDim[:3]]
|
||||
|
||||
|
||||
def SetBlockAir(posDim):
|
||||
"""
|
||||
设置某位置的方块为空,仅服务端可用
|
||||
"""
|
||||
return serverCompFactory.CreateBlockInfo(LevelId).SetBlockNew(
|
||||
posDim[:3], {'name': 'minecraft:air'}, dimensionId=posDim[3]
|
||||
)
|
||||
|
||||
|
||||
def GetEntityDim(entityId):
|
||||
"""
|
||||
获取实体维度,仅服务端可用
|
||||
"""
|
||||
return serverCompFactory.CreateDimension(entityId).GetEntityDimensionId()
|
||||
|
||||
|
||||
def DeclarePlayerInvItem(playerId, itemDict1, count=1):
|
||||
"""
|
||||
减少玩家背包内特定物品
|
||||
:param playerId:玩家ID
|
||||
:param count:减少数量
|
||||
:param itemDict1:需要检测的物品字典,必须包含newItemName字段
|
||||
:return:是否成功减少
|
||||
"""
|
||||
itemComp = serverCompFactory.CreateItem(playerId)
|
||||
itemDicts = itemComp.GetPlayerAllItems(0)
|
||||
for index in range(itemDicts.__len__()):
|
||||
itemDict = itemDicts[index]
|
||||
if itemDict and IsSameItem(itemDict, itemDict1):
|
||||
itemComp.SetInvItemNum(index, itemDict['count'] - count)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def GetEntityCarriedItem(entityId):
|
||||
"""
|
||||
获取玩家右手物品
|
||||
"""
|
||||
return serverCompFactory.CreateItem(entityId).GetEntityItem(2, 0, True)
|
||||
|
||||
|
||||
def GetEntityTypeComp(entityId):
|
||||
"""
|
||||
获取实体类型组件
|
||||
"""
|
||||
return serverCompFactory.CreateEngineType(entityId) or clientCompFactory.CreateEngineType(entityId)
|
||||
|
||||
|
||||
def IsPlayer(entityId):
|
||||
"""
|
||||
判断一个实体是否为玩家
|
||||
"""
|
||||
player = serverApi.GetMinecraftEnum().EntityType.Player
|
||||
return GetEntityTypeComp(entityId).GetEngineType() & player == player
|
||||
|
||||
|
||||
def GetEntityItemComp(entityId):
|
||||
"""
|
||||
获取实体类型组件
|
||||
"""
|
||||
return serverCompFactory.CreateItem(entityId) or clientCompFactory.CreateItem(entityId)
|
||||
|
||||
|
||||
def DeclarePlayerCarriedItem(playerId):
|
||||
"""
|
||||
减少玩家右手物品一个,仅服务端有效
|
||||
"""
|
||||
itemComp = GetEntityItemComp(playerId)
|
||||
itemDict = itemComp.GetPlayerItem(2, 0, True)
|
||||
if itemDict:
|
||||
itemDict['count'] -= 1
|
||||
return itemComp.SpawnItemToPlayerCarried(itemDict, playerId)
|
||||
@@ -0,0 +1,171 @@
|
||||
# coding=utf-8
|
||||
|
||||
from ToolsConfig import *
|
||||
|
||||
|
||||
def IsItemDict(itemDict):
|
||||
"""
|
||||
是否为有效的物品字典
|
||||
:param itemDict:
|
||||
:return:
|
||||
"""
|
||||
if itemDict and (
|
||||
not isinstance(itemDict, dict) or itemDict.get('newItemName') is None or
|
||||
itemDict.get('newAuxValue') is None or itemDict['newItemName'] == 'minecraft:air'
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def GetItemUserData(itemDict, *args):
|
||||
"""
|
||||
获取物品的userData,若不含有相应的键,则创建
|
||||
:param itemDict: 物品字典
|
||||
:param args: 键值key
|
||||
:return:
|
||||
"""
|
||||
itemDict['userData'] = itemDict.get('userData', {})
|
||||
if itemDict.get('userData') is None:
|
||||
itemDict['userData'] = {}
|
||||
ver = itemDict['userData']
|
||||
for key in args:
|
||||
ver[key] = ver.get(key, {})
|
||||
ver = ver[key]
|
||||
return ver
|
||||
|
||||
|
||||
def CreateItemDict(itemName, aux=0, count=1):
|
||||
"""
|
||||
快速生成最基本的物品字典
|
||||
"""
|
||||
itemInfo = GetItemBaseInfoSafely({'newItemName': itemName, 'newAuxValue': aux})
|
||||
return {
|
||||
'newItemName': itemName, 'newAuxValue': aux, 'count': count, 'durability': itemInfo.get('maxDurability', 0)
|
||||
}
|
||||
|
||||
|
||||
def GetItemBaseInfoSafely(itemDict):
|
||||
"""
|
||||
安全获取物品基本信息,网易接口对于不存在的物品ID_AUX获取为None
|
||||
:param itemDict:
|
||||
:return:
|
||||
"""
|
||||
default = None
|
||||
try:
|
||||
if itemDict:
|
||||
default = clientCompFactory.CreateItem(LevelId).GetItemBasicInfo(
|
||||
itemDict['newItemName'], itemDict['newAuxValue'],
|
||||
(itemDict.get('enchantData', []) + itemDict.get('modEnchantData', [])).__len__() > 0
|
||||
) or serverCompFactory.CreateItem(LevelId).GetItemBasicInfo(
|
||||
itemDict['newItemName'], itemDict['newAuxValue'],
|
||||
(itemDict.get('enchantData', []) + itemDict.get('modEnchantData', [])).__len__() > 0
|
||||
)
|
||||
except:
|
||||
pass
|
||||
default = default if default else {
|
||||
'foodNutrition': 0, 'maxDurability': 0, 'armorDefense': 0, 'tierDict': None,
|
||||
'itemType': '', 'fuelDuration': 0.0, 'foodSaturation': 0.0,
|
||||
'armorKnockbackResistance': 0.0, 'customItemType': '', 'itemTierLevel': -1,
|
||||
'maxStackSize': 64, 'weaponDamage': 0, 'itemName': '',
|
||||
'itemCategory': 'construction', 'id_aux': 0, 'armorToughness': 0
|
||||
}
|
||||
return default
|
||||
|
||||
|
||||
def DeclareItem(itemDict):
|
||||
# type:(Dict) -> Tuple[Dict,Dict]
|
||||
"""
|
||||
减少物品一个,如果个数为0则会返回空dict
|
||||
:return:减少后的物品字典,独立出来的物品
|
||||
"""
|
||||
if not itemDict:
|
||||
return itemDict, {}
|
||||
itemDict['count'] -= 1
|
||||
newItem = copy.deepcopy(itemDict)
|
||||
newItem['count'] = 1
|
||||
if itemDict['count'] > 0:
|
||||
return itemDict, newItem
|
||||
else:
|
||||
return {}, newItem
|
||||
|
||||
|
||||
def DeclareItemAnyNum(itemDict, num):
|
||||
# type:(Dict,int) -> Tuple[Dict,Dict]
|
||||
"""
|
||||
减少物品多个,如果个数为0则会返回空dict
|
||||
:return:减少后的物品字典,独立出来的物品
|
||||
"""
|
||||
if not isinstance(itemDict, dict): return {}, {}
|
||||
if not IsItemDict(itemDict): return {}, {}
|
||||
if not itemDict: return {}, itemDict
|
||||
if itemDict['count'] < num: return {}, itemDict
|
||||
itemDict['count'] -= num
|
||||
newItem = copy.deepcopy(itemDict)
|
||||
newItem['count'] = num
|
||||
if itemDict['count'] > 0:
|
||||
return itemDict, newItem
|
||||
else:
|
||||
return {}, newItem
|
||||
|
||||
|
||||
def StackItems(item1, item2):
|
||||
# type:(Dict,Dict) -> Dict
|
||||
"""
|
||||
使item1与item2堆叠在一起,返回多余的物品
|
||||
"""
|
||||
if not item1 or not item2: return {}
|
||||
if not IsSameItemAndCanStack(item1, item2): return {}
|
||||
itemInfo = GetItemBaseInfoSafely(item1)
|
||||
maxCount = itemInfo['maxStackSize']
|
||||
count1 = item1['count']
|
||||
count2 = item2['count']
|
||||
t_count = count1 + count2 - maxCount
|
||||
t_item = {}
|
||||
if t_count > 0:
|
||||
t_item = copy.deepcopy(item1)
|
||||
t_item['count'] = t_count
|
||||
item1['count'] = maxCount
|
||||
else:
|
||||
item1['count'] += item2['count']
|
||||
return t_item
|
||||
|
||||
|
||||
def IsSameItemAndCanStack(item1, item2):
|
||||
# type:(Dict,Dict) -> bool
|
||||
"""
|
||||
判断item1是否能与item2堆叠在一起
|
||||
"""
|
||||
if not item1 or not item2:
|
||||
return True
|
||||
if IsSameItem(item1, item2):
|
||||
itemInfo = GetItemBaseInfoSafely(item1)
|
||||
return item1['count'] + item2['count'] <= itemInfo['maxStackSize']
|
||||
return False
|
||||
|
||||
|
||||
def IsSameItem(item1, item2, args=('newItemName', 'newAuxValue', 'durability')):
|
||||
"""
|
||||
判断item1是否与item2相同.目前判断物品ID,AUX,耐久度
|
||||
"""
|
||||
if item1 and item2:
|
||||
same = True
|
||||
for key in args: same = same and item1.get(key, 0) == item2.get(key, 0)
|
||||
return same
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def GetItemDurabilityPercent(itemDict, itemInfo=None):
|
||||
"""
|
||||
获取耐久值百分比与总值
|
||||
:param itemDict:物品信息字典
|
||||
:param itemInfo:物品基础信息字典
|
||||
:return:
|
||||
"""
|
||||
if itemInfo is None:
|
||||
itemInfo = GetItemBaseInfoSafely(itemDict)
|
||||
return (
|
||||
(itemDict.get('durability', 0.0) / float(itemInfo['maxDurability']))
|
||||
if itemInfo['maxDurability'] != 0 else 0.0,
|
||||
1.0 if itemInfo['maxDurability'] != 0 else 0.0
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
# coding=utf-8
|
||||
|
||||
MOD_NAME = 'rtdd'
|
||||
MOD_VERSION = '0.0.1'
|
||||
|
||||
Time = 1754134400.0
|
||||
@@ -0,0 +1,39 @@
|
||||
# coding=utf-8
|
||||
from mod.common.mod import Mod
|
||||
from OtherTools import *
|
||||
from SpecialFunciton.BlueFoxSystem import *
|
||||
from SystemTools import *
|
||||
from ToolsConfig import *
|
||||
|
||||
|
||||
def LoadMod():
|
||||
"""
|
||||
一键加载脚本
|
||||
:return:
|
||||
"""
|
||||
print """
|
||||
---->>>加载脚本<<<----
|
||||
模组名称:{}
|
||||
模组版本:{}
|
||||
""".format(MOD_NAME, MOD_VERSION)
|
||||
Mod.Binding(MOD_NAME, MOD_VERSION)(BlueFoxModLoader)
|
||||
|
||||
|
||||
class BlueFoxModLoader:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@Mod.InitClient()
|
||||
def InitClient(self):
|
||||
RegisterSystemFastly(clientApi, BlueFoxClientSystem)
|
||||
lastSubclasses = GetLastSubclass(BlueFoxClientSystem)
|
||||
for lastSubclass in lastSubclasses:
|
||||
RegisterSystemFastly(clientApi, lastSubclass)
|
||||
|
||||
@Mod.InitServer()
|
||||
def InitServer(self):
|
||||
RegisterSystemFastly(serverApi, BlueFoxServerSystem)
|
||||
lastSubclasses = GetLastSubclass(BlueFoxServerSystem)
|
||||
for lastSubclass in lastSubclasses:
|
||||
RegisterSystemFastly(serverApi, lastSubclass)
|
||||
@@ -0,0 +1,60 @@
|
||||
# coding=utf-8
|
||||
from ToolsConfig import *
|
||||
|
||||
if 1 > 2:
|
||||
from SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem
|
||||
|
||||
blueFoxServerSystem = BlueFoxServerSystem
|
||||
|
||||
molangNames = {}
|
||||
|
||||
|
||||
def ChangeBlockEntityMolangFromServerToAllClient(posDim, molangName, value):
|
||||
# type:(Tuple[int,int,int,int],str,float) -> None
|
||||
"""
|
||||
从服务端修改所有客户端某一位置的方块实体里的molang值
|
||||
:param posDim: 方块实体的坐标与维度
|
||||
:param molangName: molang名称
|
||||
:param value: molang值
|
||||
:return:
|
||||
"""
|
||||
blueFoxServerSystem.BroadcastToAllClient('ChangeBlockEntityMolang', {
|
||||
'molangName': molangName, 'value': value, 'posDim': posDim
|
||||
})
|
||||
|
||||
|
||||
def ChangeBlockEntityMolangFromServerToClient(playerId, posDim, molangName, value):
|
||||
# type:(Union[int,str],Tuple[int,int,int,int],str,float) -> None
|
||||
"""
|
||||
从服务端修改某一客户端某一位置的方块实体里的molang值
|
||||
:param posDim: 方块实体的坐标与维度
|
||||
:param molangName: molang名称
|
||||
:param value: molang值
|
||||
:param playerId: 玩家id
|
||||
:return:
|
||||
"""
|
||||
blueFoxServerSystem.NotifyToClient(playerId, 'ChangeBlockEntityMolang', {
|
||||
'molangName': molangName, 'value': value, 'posDim': posDim
|
||||
})
|
||||
|
||||
|
||||
def RegisterMolang(molangName, defaultValue=0.0):
|
||||
"""
|
||||
注册并获取molang查询字段
|
||||
:param molangName: molang名称,无需query.mod.
|
||||
:param defaultValue: 默认值
|
||||
:return:
|
||||
"""
|
||||
name = 'query.mod.' + molangName
|
||||
molangNames[name] = defaultValue
|
||||
return name
|
||||
|
||||
|
||||
def RegisterMolangWithNameSpace(molangName, defaultValue=0.0):
|
||||
"""
|
||||
注册并获取molang查询字段,自带命名空间,使用时需要在前部加入MOD_NAME_molangName
|
||||
:param molangName: molang名称
|
||||
:param defaultValue: 默认值
|
||||
:return:
|
||||
"""
|
||||
return RegisterMolang('{}_{}'.format(MOD_NAME, molangName), defaultValue)
|
||||
@@ -0,0 +1,219 @@
|
||||
# coding=utf-8
|
||||
from ToolsConfig import *
|
||||
import copy
|
||||
|
||||
def GetItemUserData(itemDict, *args):
|
||||
"""
|
||||
获取物品的userData,若不含有相应的键,则创建
|
||||
:param itemDict: 物品字典
|
||||
:param args: 键值key
|
||||
:return:
|
||||
"""
|
||||
itemDict['userData'] = itemDict.get('userData', {})
|
||||
if itemDict.get('userData') is None:
|
||||
itemDict['userData'] = {}
|
||||
ver = itemDict['userData']
|
||||
for key in args:
|
||||
ver[key] = ver.get(key, {})
|
||||
ver = ver[key]
|
||||
return ver
|
||||
|
||||
|
||||
def FixList(_list, _len, defaultValue):
|
||||
"""
|
||||
修正列表到目标长度
|
||||
:param _list:列表
|
||||
:param _len:目标长度
|
||||
:param defaultValue:默认值
|
||||
:return:
|
||||
"""
|
||||
if _list.__class__ is not list: _list = []
|
||||
if len(_list) == _len: return _list
|
||||
_list = _list if isinstance(_list, list) else [copy.deepcopy(defaultValue) for _ in range(_len)]
|
||||
del _list[_len:]
|
||||
_list.extend(copy.deepcopy(defaultValue) for _ in range(len(_list), _len))
|
||||
return _list
|
||||
|
||||
|
||||
def GetPlayerHasAut(playerId):
|
||||
"""
|
||||
判断玩家是否拥有创造和管理员权限
|
||||
:param playerId:
|
||||
:return:
|
||||
"""
|
||||
return serverCompFactory.CreatePlayer(playerId).GetPlayerOperation() == 2 and \
|
||||
serverCompFactory.CreateGame(LevelId).GetPlayerGameType(playerId) == 1
|
||||
|
||||
|
||||
def NbtTranslator(nbtDict):
|
||||
"""
|
||||
将nbt格式的数据转换为python数据
|
||||
:param nbtDict:
|
||||
:return:
|
||||
"""
|
||||
if nbtDict is None:
|
||||
return {}
|
||||
if type(nbtDict) is list:
|
||||
return NbtList(nbtDict)
|
||||
elif type(nbtDict) is dict and nbtDict.get('__type__', None) is None:
|
||||
return NbtDict(nbtDict)
|
||||
else:
|
||||
return NbtValue(nbtDict)
|
||||
|
||||
|
||||
def NbtValue(nbtDict):
|
||||
"""
|
||||
处理数Nbt数据
|
||||
:param nbtDict:
|
||||
:return:
|
||||
"""
|
||||
if type(nbtDict) is dict:
|
||||
valueType = nbtDict.get('__type__', 0)
|
||||
return NbtTranslatorFunc[valueType - 1](nbtDict['__value__']) if valueType else 0
|
||||
else:
|
||||
return nbtDict
|
||||
|
||||
|
||||
def NbtDict(nbtDict):
|
||||
"""
|
||||
处理字典型Nbt数据
|
||||
:param nbtDict:
|
||||
:return:
|
||||
"""
|
||||
value = {}
|
||||
for _ in nbtDict.items():
|
||||
value[_[0]] = NbtTranslator(_[1])
|
||||
return value
|
||||
|
||||
|
||||
def NbtList(nbtList):
|
||||
"""
|
||||
处理列表型Nbt数据
|
||||
:param nbtList:
|
||||
:return:
|
||||
"""
|
||||
value = []
|
||||
for _ in nbtList:
|
||||
value.append(NbtTranslator(_))
|
||||
return value
|
||||
|
||||
|
||||
def TransBool(nbtValue):
|
||||
"""
|
||||
处理bool数据
|
||||
"""
|
||||
if nbtValue == 2:
|
||||
return None
|
||||
return bool(nbtValue)
|
||||
|
||||
|
||||
NbtTranslatorFunc = [
|
||||
TransBool, int, int, int, float, int, bytearray, str, list, NbtDict, list
|
||||
]
|
||||
|
||||
|
||||
def UpdateFunction(old_func, new_func):
|
||||
"""
|
||||
热更新函数
|
||||
"""
|
||||
old_func.__doc__ = new_func.__doc__
|
||||
old_func.__dict__ = new_func.__dict__
|
||||
old_func.__defaults__ = new_func.__defaults__
|
||||
old_func.__code__ = new_func.__code__
|
||||
|
||||
|
||||
mounts = {'s': [], 'c': []}
|
||||
|
||||
|
||||
def Mount(system):
|
||||
# type:(Union[clientBaseSystem,serverBaseSystem]) -> Any
|
||||
"""
|
||||
挂载系统,为非系统类添加属性blueFoxSystem
|
||||
服务端只挂载BlueFoxServerSystem,客户端只挂载BlueFoxClientSystem
|
||||
"""
|
||||
|
||||
def _mount(cls):
|
||||
if issubclass(system, serverBaseSystem):
|
||||
mounts['s'].append(cls)
|
||||
elif issubclass(system, clientBaseSystem):
|
||||
mounts['c'].append(cls)
|
||||
return cls
|
||||
|
||||
return _mount
|
||||
|
||||
|
||||
def GetLastSubclass(cls, basicClass=None):
|
||||
"""
|
||||
获取类的最终继承类
|
||||
:param cls:
|
||||
:param basicClass:
|
||||
:return:
|
||||
"""
|
||||
if basicClass is None:
|
||||
basicClass = []
|
||||
subclasses = cls.__subclasses__()
|
||||
for subclass in subclasses:
|
||||
if subclass.__subclasses__():
|
||||
GetLastSubclass(subclass, basicClass)
|
||||
else:
|
||||
basicClass.append(subclass)
|
||||
return basicClass
|
||||
|
||||
|
||||
def AddTupleContext(list1, list2):
|
||||
# type:(Union[Tuple,List],Union[Tuple,List]) -> Union[Tuple[Any,...],Tuple[int,int,int,int]]
|
||||
"""
|
||||
将两个相同长度的tuple or list内容彼此相加,并返回新的tuple
|
||||
:return:list1 + list2
|
||||
"""
|
||||
if list2.__len__() == list1.__len__():
|
||||
newList = [list1[n] + list2[n] for n in range(list1.__len__())]
|
||||
return tuple(newList)
|
||||
return tuple([])
|
||||
|
||||
|
||||
def AddTupleContextReList(list1, list2):
|
||||
"""
|
||||
将两个相同长度的tuple or list内容彼此相加,并返回新的list
|
||||
:return:list1 + list2
|
||||
"""
|
||||
if list2.__len__() == list1.__len__():
|
||||
newList = [list1[n] + list2[n] for n in range(list1.__len__())]
|
||||
return newList
|
||||
return []
|
||||
|
||||
|
||||
def GetDistance(pos1, pos2):
|
||||
# type:(Union[List,Tuple],Union[List, Tuple]) -> float
|
||||
"""
|
||||
计算三维上两点距离
|
||||
:return:
|
||||
"""
|
||||
_sum = 0
|
||||
for _ in range(min(len(pos1), len(pos2))): _sum += (pos1[_] - pos2[_]) ** 2
|
||||
return _sum ** 0.5
|
||||
|
||||
|
||||
def VecMod(vec):
|
||||
# type:(Union[List,Tuple]) -> float
|
||||
"""
|
||||
计算向量的模
|
||||
:param vec:
|
||||
:return:
|
||||
"""
|
||||
_sum = 0
|
||||
for _ in vec:
|
||||
_sum += _ ** 2
|
||||
return _sum ** 0.5
|
||||
|
||||
|
||||
def ReduceTupleContext(list1, list2):
|
||||
# type:(Union[Tuple,List],Union[Tuple,List]) -> Union[Tuple[Any,...],Tuple[int,int,int,int]]
|
||||
"""
|
||||
将两个相同长度的tuple or list内容彼此相减,并返回新的tuple
|
||||
:return:list1 - list2
|
||||
"""
|
||||
if list2.__len__() == list1.__len__():
|
||||
newList = [list1[n] - list2[n] for n in range(list1.__len__())]
|
||||
return tuple(newList)
|
||||
return tuple([])
|
||||
@@ -0,0 +1,65 @@
|
||||
# coding=utf-8
|
||||
from ToolsConfig import *
|
||||
|
||||
|
||||
def InvisiblePlayerModel(playerId):
|
||||
"""
|
||||
隐藏玩家模型
|
||||
"""
|
||||
comp = clientCompFactory.CreateActorRender(playerId)
|
||||
comp.RemovePlayerRenderController('controller.render.player.first_person')
|
||||
comp.RemovePlayerRenderController('controller.render.player.third_person')
|
||||
comp.RemovePlayerRenderController('controller.render.player.map')
|
||||
comp.RebuildPlayerRender()
|
||||
|
||||
|
||||
def RestorePlayerModel(playerId):
|
||||
"""
|
||||
显示玩家模型
|
||||
"""
|
||||
comp = clientCompFactory.CreateActorRender(playerId)
|
||||
comp.AddPlayerRenderController('controller.render.player.first_person', 'variable.is_first_person')
|
||||
comp.AddPlayerRenderController(
|
||||
'controller.render.player.third_person', '!variable.is_first_person && !variable.map_face_icon'
|
||||
)
|
||||
comp.AddPlayerRenderController('controller.render.player.map', 'variable.map_face_icon')
|
||||
comp.RebuildPlayerRender()
|
||||
|
||||
|
||||
class PlayerVanillaAnimation(object):
|
||||
Attack = 'first_person_attack_controller'
|
||||
MoveArms = 'move.arms'
|
||||
MoveLegs = 'move.legs'
|
||||
Holding = 'holding'
|
||||
EmptyHand = 'first_person_empty_hand'
|
||||
|
||||
|
||||
__d = {
|
||||
PlayerVanillaAnimation.Attack: 'controller.animation.player.first_person_attack',
|
||||
PlayerVanillaAnimation.MoveArms: 'animation.player.move.arms',
|
||||
PlayerVanillaAnimation.MoveLegs: 'animation.player.move.legs',
|
||||
PlayerVanillaAnimation.Holding: 'animation.player.holding',
|
||||
PlayerVanillaAnimation.EmptyHand: 'animation.player.first_person.empty_hand'
|
||||
}
|
||||
|
||||
|
||||
def InvisibleVanillaAnimation(playerId, *args):
|
||||
"""
|
||||
屏蔽原版动画
|
||||
"""
|
||||
comp = clientCompFactory.CreateActorRender(playerId)
|
||||
for _ in args:
|
||||
comp.AddPlayerAnimation(_, 'animation.unvanilla.all') if __d[_].startswith(
|
||||
'animation') else comp.AddPlayerAnimationController(_, 'controller.animation.unvanilla.all')
|
||||
return comp
|
||||
|
||||
|
||||
def RestoreVanillaAnimation(playerId, *args):
|
||||
"""
|
||||
还原原版动画
|
||||
"""
|
||||
comp = clientCompFactory.CreateActorRender(playerId)
|
||||
for _ in args:
|
||||
comp.AddPlayerAnimation(_, __d[_]) if __d[_].startswith(
|
||||
'animation') else comp.AddPlayerAnimationController(_, __d[_])
|
||||
return comp
|
||||
@@ -0,0 +1,137 @@
|
||||
# coding=utf-8
|
||||
from ToolsConfig import *
|
||||
|
||||
CreatedTools = {}
|
||||
ButtonTouchMap = {
|
||||
ButtonTouchTypeEnum.TouchDown: 'SetButtonTouchDownCallback',
|
||||
ButtonTouchTypeEnum.TouchUp: 'SetButtonTouchUpCallback',
|
||||
ButtonTouchTypeEnum.MoveOut: 'SetButtonTouchMoveOutCallback',
|
||||
ButtonTouchTypeEnum.TouchCancel: 'SetButtonTouchCancelCallback'
|
||||
}
|
||||
|
||||
|
||||
def RegisterScreenFastly(cls, UiScreenDef):
|
||||
"""
|
||||
快速注册Ui界面,其中:
|
||||
--namespace为ToolsConfig.MOD_NAME
|
||||
--uiKey为类名
|
||||
"""
|
||||
clientApi.RegisterUI(
|
||||
MOD_NAME, cls.__name__,
|
||||
'{}.{}'.format(cls.__module__, cls.__name__),
|
||||
UiScreenDef
|
||||
)
|
||||
|
||||
|
||||
class ScreenTools(object):
|
||||
"""
|
||||
界面工具
|
||||
"""
|
||||
autoRegister = set()
|
||||
screens = {}
|
||||
|
||||
@classmethod
|
||||
def AutoRegister(cls, uiScreenDef):
|
||||
def register(screenCls):
|
||||
ScreenTools.autoRegister.add((uiScreenDef, screenCls))
|
||||
return screenCls
|
||||
|
||||
return register
|
||||
|
||||
@classmethod
|
||||
def GetTools(cls, tag):
|
||||
"""
|
||||
创建获取工具
|
||||
:param tag:界面定义路径
|
||||
:return:
|
||||
"""
|
||||
tag = tag + MOD_NAME + MOD_VERSION
|
||||
instance = CreatedTools.get(tag)
|
||||
if instance is None:
|
||||
instance = cls()
|
||||
CreatedTools[tag] = instance
|
||||
return instance
|
||||
|
||||
def ExtendTools(self, tag):
|
||||
"""
|
||||
继承已经存在的工具
|
||||
:param tag:界面定义路径
|
||||
:return:
|
||||
"""
|
||||
tag = tag + MOD_NAME + MOD_VERSION
|
||||
instance = CreatedTools.get(tag)
|
||||
if instance is None:
|
||||
instance = type(self)()
|
||||
instance.buttonTouchBackFunc = copy.deepcopy(self.buttonTouchBackFunc)
|
||||
CreatedTools[tag] = instance
|
||||
return instance
|
||||
|
||||
def __init__(self):
|
||||
self.buttonTouchBackFunc = {}
|
||||
self.bindingInstance = None
|
||||
|
||||
def Binding(self, screenInstance):
|
||||
self.bindingInstance = screenInstance
|
||||
self.FirstAddTouchBackFunc()
|
||||
self.screens[screenInstance.__class__.__name__] = screenInstance
|
||||
|
||||
@classmethod
|
||||
def GetScreenByClsName(cls, clsName):
|
||||
return cls.screens.get(clsName)
|
||||
|
||||
def ButtonCallFuncBackBinder(self, touchType, buttonPath):
|
||||
"""
|
||||
绑定按钮回调函数
|
||||
:param buttonPath:按钮路径
|
||||
:param touchType:接触类型,位于ToolsConfig.ButtonTouchTypeEnum
|
||||
"""
|
||||
|
||||
def _binding(func):
|
||||
items = self.buttonTouchBackFunc.get(touchType, set())
|
||||
if not items: self.buttonTouchBackFunc[touchType] = items
|
||||
items.add((buttonPath, func.__name__))
|
||||
if self.bindingInstance:
|
||||
button = self.bindingInstance.GetBaseUIControl(buttonPath)
|
||||
if button:
|
||||
button = button.asButton()
|
||||
button.AddTouchEventParams()
|
||||
getattr(button, ButtonTouchMap[touchType])(getattr(self.bindingInstance, func.__name__))
|
||||
return func
|
||||
|
||||
return _binding
|
||||
|
||||
def FirstAddTouchBackFunc(self):
|
||||
if self.bindingInstance:
|
||||
for touchType, backFuncs in self.buttonTouchBackFunc.items():
|
||||
for buttonPath, backFuncName in backFuncs:
|
||||
button = self.bindingInstance.GetBaseUIControl(buttonPath)
|
||||
if button:
|
||||
button = button.asButton()
|
||||
button.AddTouchEventParams()
|
||||
getattr(button, ButtonTouchMap[touchType])(getattr(self.bindingInstance, backFuncName))
|
||||
|
||||
@staticmethod
|
||||
def CollectionViewBinder(bindFlag, collectionName, bindingName, func):
|
||||
"""
|
||||
绑定数据函数,为执行函数添加一个新的参数:collectionName.此collectionName与形参collectionName一致
|
||||
|
||||
这是一个逆天工具,改造网易屎山
|
||||
|
||||
若不传入cls,确保在binding之后调用
|
||||
|
||||
:param func:必须是bound函数,即self.func
|
||||
:param bindFlag:
|
||||
:param collectionName:
|
||||
:param bindingName:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def __(*args):
|
||||
return func(collectionName, args[1])
|
||||
|
||||
cls = func.im_class
|
||||
funcBindingName = collectionName + bindingName
|
||||
setattr(cls, funcBindingName, __)
|
||||
newFunc = getattr(cls, funcBindingName).im_func
|
||||
newFunc.__name__ = funcBindingName
|
||||
viewBinder.binding_collection(bindFlag, collectionName, bindingName)(newFunc)
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
# coding=utf-8
|
||||
import copy
|
||||
|
||||
from BlueFoxSystem import BlueFoxServerSystem
|
||||
from LocalCoord import FixPosition, OriginCoord, RotCoord, RotAxes, BlockFaceRot
|
||||
from ..OtherTools import Mount
|
||||
from ..ToolsConfig import *
|
||||
|
||||
blockBlocks = {}
|
||||
blockEntityDataComp = serverCompFactory.CreateBlockEntityData(LevelId)
|
||||
|
||||
|
||||
def GetLoadedBlockBlockByPosDim(posDim):
|
||||
# type:(Tuple[int,int,int,int]) -> BlockBlock
|
||||
"""
|
||||
通过维度坐标获取已经加载的方块块
|
||||
"""
|
||||
return blockBlocks.get(posDim)
|
||||
|
||||
|
||||
def GetLoadedBlockBlock():
|
||||
# type:() -> List[BlockBlock]
|
||||
"""
|
||||
获取所有已加载的方块块
|
||||
"""
|
||||
return list(set(blockBlocks.values()))
|
||||
|
||||
|
||||
@Mount(BlueFoxServerSystem)
|
||||
class BlockBlock(object):
|
||||
"""
|
||||
方块块,用于解决模型体积过大导致的模型消失的问题
|
||||
blockList = {
|
||||
(0, 0, 0): {'name': 'rtdd:carrotandbeef', 'aux': 0}
|
||||
(相对坐标位置): {'name': 方块ID, 'aux': 方块AUX}
|
||||
}
|
||||
"""
|
||||
blueFoxSystem = BlueFoxServerSystem if 0 else None
|
||||
blockList = {}
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, posDim):
|
||||
instance = blockBlocks.get(posDim)
|
||||
if instance: return instance
|
||||
data = blockEntityDataComp.GetBlockEntityData(posDim[3], posDim[:3])
|
||||
if data and data['isCenter']:
|
||||
posDims = [tuple(_) for _ in data['posDims']]
|
||||
return cls(tuple(data['centerPosDim']), data['aux'], posDims)
|
||||
blockData = serverCompFactory.CreateBlockInfo(LevelId).GetBlockNew(posDim[:3], posDim[3])
|
||||
return cls(posDim, blockData['aux'])
|
||||
|
||||
def __init__(self, posDim, aux, posDims=None):
|
||||
# type:(Tuple,int,List[Tuple]) -> None
|
||||
self.blockPosDims = posDims # 方块块控制坐标
|
||||
self.centerPosDim = posDim # 控制中心
|
||||
self.aux = aux
|
||||
|
||||
if self.blockPosDims is None:
|
||||
self.blockPosDims = self.CreatePosDims(posDim, aux)
|
||||
self.CreateBlocks()
|
||||
self.dataComp = blockEntityDataComp.GetBlockEntityData(self.centerPosDim[3], self.centerPosDim[:3])
|
||||
self.dataComp['posDims'] = self.blockPosDims
|
||||
self.dataComp['aux'] = self.aux
|
||||
|
||||
for posDim in self.blockPosDims:
|
||||
blockBlocks[posDim] = self
|
||||
|
||||
def ReplaceBlocks(self, newBlockList, otherData=None, drop=0):
|
||||
"""
|
||||
替换方块,新方块依然具有方块实体的要求
|
||||
"""
|
||||
oldData = {
|
||||
'posDims': self.blockPosDims,
|
||||
'aux': self.aux
|
||||
}
|
||||
if otherData is None: otherData = {}
|
||||
oldData.update(otherData)
|
||||
self.DestroyBlocks(drop)
|
||||
self.CreateBlocks(newBlockList)
|
||||
|
||||
def DestroyFunction(self):
|
||||
"""
|
||||
销毁处理逻辑不清除方块
|
||||
"""
|
||||
for posDim in self.blockPosDims:
|
||||
del blockBlocks[posDim]
|
||||
|
||||
def Destroy(self, drop=0):
|
||||
"""
|
||||
销毁方块块
|
||||
:param drop:是否掉落核心方块
|
||||
"""
|
||||
self.DestroyFunction()
|
||||
self.DestroyBlocks(drop)
|
||||
|
||||
def DestroyBlocks(self, drop=0):
|
||||
"""
|
||||
销毁方块块所控制的方块
|
||||
"""
|
||||
blockComp = serverCompFactory.CreateBlockInfo(LevelId)
|
||||
posDim = copy.copy(self.blockPosDims)
|
||||
posDim.remove(self.centerPosDim)
|
||||
for posDim in posDim:
|
||||
blockComp.SetBlockNew(
|
||||
posDim[:3], blockDict={'name': 'minecraft:air'}, dimensionId=posDim[3]
|
||||
)
|
||||
blockComp.SetBlockNew(
|
||||
self.centerPosDim[:3], blockDict={'name': 'minecraft:air'}, dimensionId=self.centerPosDim[3],
|
||||
oldBlockHandling=1 if drop else 0
|
||||
)
|
||||
|
||||
def CreateBlocks(self, blockList=None):
|
||||
"""
|
||||
放置方块块所控制的方块
|
||||
"""
|
||||
if blockList is None:
|
||||
blockList = self.blockList
|
||||
blockComp = serverCompFactory.CreateBlockInfo(LevelId)
|
||||
for pos in self.blockList:
|
||||
blockInfo = blockList[pos]
|
||||
posDim = self.CalculatePosDimFromOffset(pos, self.centerPosDim, self.aux)
|
||||
blockComp.SetBlockNew(posDim[:3], blockDict=blockInfo, dimensionId=posDim[3])
|
||||
dataComp = blockEntityDataComp.GetBlockEntityData(posDim[3], posDim[:3])
|
||||
dataComp['isCenter'] = True if not cmp(pos, (0, 0, 0)) else False
|
||||
dataComp['centerPosDim'] = self.centerPosDim
|
||||
self.dataComp = serverCompFactory.CreateBlockEntityData(LevelId).GetBlockEntityData(
|
||||
self.centerPosDim[3], self.centerPosDim[:3]
|
||||
)
|
||||
|
||||
def IsSelf(self, posDim):
|
||||
"""
|
||||
判断一个维度坐标是否为本方块块
|
||||
"""
|
||||
for posDimSelf in self.blockPosDims:
|
||||
if not cmp(posDimSelf, posDim):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def CreatePosDims(cls, posDim, aux):
|
||||
# type:(Tuple,int) -> List[Tuple]
|
||||
"""
|
||||
创建方块块控制坐标
|
||||
"""
|
||||
return [cls.CalculatePosDimFromOffset(pos, posDim, aux) for pos in cls.blockList]
|
||||
|
||||
@classmethod
|
||||
def CalculatePosDimFromOffset(cls, offset, posDim, aux):
|
||||
"""计算偏移坐标维度"""
|
||||
originCoord = OriginCoord(posDim)
|
||||
_, __, ___ = FixPosition(originCoord.AbsPos(
|
||||
RotCoord(BlockFaceRot[aux], RotAxes.Y).CalculatePos(offset)
|
||||
))
|
||||
return _, __, ___, posDim[3]
|
||||
|
||||
@classmethod
|
||||
def Able(cls, posDim, aux):
|
||||
"""
|
||||
判断是否可以生成块
|
||||
"""
|
||||
blockComp = serverCompFactory.CreateBlockInfo(LevelId)
|
||||
for posDim in cls.CreatePosDims(posDim, aux):
|
||||
block = blockComp.GetBlockNew(posDim[:3], posDim[3])
|
||||
if block is None or (block and block['name'] != 'minecraft:air'):
|
||||
return False
|
||||
return True
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# coding=utf-8
|
||||
import time
|
||||
from math import *
|
||||
|
||||
|
||||
class GraduallySum(object):
|
||||
"""
|
||||
渐变数
|
||||
"""
|
||||
|
||||
def __init__(self, coefficient, target, initValue):
|
||||
self.target = target
|
||||
self.coefficient = coefficient
|
||||
self.value = initValue
|
||||
|
||||
def ResetCoefficient(self, newCoefficient):
|
||||
self.coefficient = newCoefficient
|
||||
|
||||
def Refresh(self):
|
||||
self.value += self.coefficient * (self.target - self.value)
|
||||
|
||||
def ResetTarget(self, newTarget):
|
||||
self.target = newTarget
|
||||
|
||||
|
||||
class GraduallySumTime(GraduallySum):
|
||||
"""
|
||||
时间相关渐变数
|
||||
"""
|
||||
|
||||
def __init__(self, coefficient, target, initSum):
|
||||
self.time = time.time()
|
||||
super(GraduallySumTime, self).__init__(coefficient, target, initSum)
|
||||
|
||||
def Refresh(self):
|
||||
_time = time.time()
|
||||
coe = self.coefficient * (_time - self.time)
|
||||
# 放置系数过大
|
||||
coe = coe if coe < 0.99 else 0.99
|
||||
self.value += coe * (self.target - self.value)
|
||||
|
||||
|
||||
def Clamp(value, _min, _max):
|
||||
# type:(float,float,float) -> float
|
||||
return _max if value > _max else _min if value < _min else value
|
||||
|
||||
|
||||
def ComBin(i, place, result):
|
||||
# type:(int,int,int) -> bool
|
||||
"""
|
||||
提取参数i位于place的结果并于result比较
|
||||
例如输入:0b00101,0b10000,0b01110,则会返回false
|
||||
"""
|
||||
return i & place == result & place
|
||||
|
||||
|
||||
def BinCal(i, place, result):
|
||||
# type:(int,int,bool) -> int
|
||||
"""
|
||||
为参数i设置位于place的result结果
|
||||
例如输入:0b00100,0b00101,True
|
||||
输出将会是:0b00101
|
||||
"""
|
||||
return ((i | place) ^ place) | place * result
|
||||
|
||||
|
||||
def CosOfTwoVec(vec1, vec2):
|
||||
"""
|
||||
获取两个相同维度向量的夹角cos值
|
||||
"""
|
||||
if vec1.__len__() != vec2.__len__():
|
||||
return 0
|
||||
nv1 = Normalize(vec1)
|
||||
nv2 = Normalize(vec2)
|
||||
_ = 0
|
||||
for index in range(nv1.__len__()):
|
||||
_ += nv1[index] * nv2[index]
|
||||
return _
|
||||
|
||||
|
||||
def Normalize(vec):
|
||||
# type:(Union[List,Tuple]) -> Union[List,Tuple]
|
||||
"""
|
||||
将任意维度向量转换为单位向量
|
||||
"""
|
||||
r = 0
|
||||
for _ in vec:
|
||||
r += _ ** 2.0
|
||||
r = r ** 0.5
|
||||
cls = vec.__class__
|
||||
return cls([(_ / r) for _ in vec])
|
||||
|
||||
|
||||
def GetTwoPointVec(pos1, pos2):
|
||||
# type:(Union[List,Tuple],Union[List,Tuple]) -> Tuple
|
||||
"""
|
||||
计算任意两点的方向向量, 非单位向量, pos1 -> pos2
|
||||
"""
|
||||
li = []
|
||||
if pos1.__len__() != pos2.__len__():
|
||||
return tuple(li)
|
||||
for index in range(pos1.__len__()):
|
||||
li.append(pos2[index] - pos1[index])
|
||||
return tuple(li)
|
||||
|
||||
|
||||
def MatProduct(vec, mat):
|
||||
"""
|
||||
计算向量或者坐标与矩阵相乘, 需要i=j
|
||||
"""
|
||||
new = [0.0 for _ in range(vec.__len__())]
|
||||
lengthIndex = range(vec.__len__())
|
||||
for _ in lengthIndex:
|
||||
for __ in lengthIndex:
|
||||
new[_] += vec[__] * mat[_][__]
|
||||
return new
|
||||
+660
@@ -0,0 +1,660 @@
|
||||
# coding=utf-8
|
||||
|
||||
from BlueFoxSystem import BlueFoxClientSystem
|
||||
from ControllableUi.Animation import MoveToPos
|
||||
from ..ItemTools import IsSameItem, GetItemBaseInfoSafely, GetItemDurabilityPercent, DeclareItemAnyNum, IsItemDict
|
||||
from ..OtherTools import Mount, FixList
|
||||
from ..ScreenTools import ScreenTools
|
||||
from ..ToolsConfig import *
|
||||
|
||||
|
||||
class SlotOffset(dict):
|
||||
def __getitem__(self, item):
|
||||
return super(SlotOffset, self).get(item, 0)
|
||||
|
||||
|
||||
@Mount(BlueFoxClientSystem)
|
||||
class RucksackScreen(baseScreen):
|
||||
flyingItemRender = None
|
||||
slotOffset = SlotOffset()
|
||||
flyingMoveControl = None # type:MoveToPos
|
||||
blueFoxClient = BlueFoxClientSystem if 0 else None
|
||||
|
||||
def __init__(self, name_spase, ui_key, param, items=None, customData=None, flyingPanelPath=None):
|
||||
super(RucksackScreen, self).__init__(name_spase, ui_key, param)
|
||||
self._selectSlot = -1
|
||||
self.__itemNum = [] # 物品数量
|
||||
self.__itemD = [] # (目前,总) 耐久度
|
||||
self.__itemId = [] # 物品ID
|
||||
self.__isItemNone = [] # 是否为缺失物品
|
||||
self.flyingItem = {} # 跟随鼠标的物品
|
||||
self.moveControls = []
|
||||
self.exchangeLock = False
|
||||
self.items = items # 所有物品
|
||||
if self.items is None: self.items = [] # type:List[Dict]
|
||||
self.customData = customData # 自定义数据
|
||||
self.gridIndex = {}
|
||||
self.flyingPanelPath = flyingPanelPath
|
||||
self.offsetSlot = {}
|
||||
|
||||
self._selectSlot = -1
|
||||
|
||||
@property
|
||||
def selectSlot(self):
|
||||
return self._selectSlot
|
||||
|
||||
@selectSlot.setter
|
||||
def selectSlot(self, index):
|
||||
self._selectSlot = index
|
||||
|
||||
def GetStartAndFinalIndex(self, collectName):
|
||||
"""
|
||||
获取起止索引段
|
||||
:param collectName:
|
||||
:return:
|
||||
"""
|
||||
offsetKey = self.offsetSlot.keys()
|
||||
offsetKey.sort()
|
||||
offset = self.slotOffset[collectName]
|
||||
if offset == offsetKey[-1]: return offset, len(self.items)
|
||||
for index in range(len(offsetKey) - 1):
|
||||
if offset == offsetKey[index]: return offset, offsetKey[index + 1]
|
||||
|
||||
def RecalculateOffsetSlot(self):
|
||||
"""
|
||||
重新计算OffsetSlot
|
||||
:return:
|
||||
"""
|
||||
self.offsetSlot = {}
|
||||
for _ in self.slotOffset.items(): self.offsetSlot[_[1]] = _[0]
|
||||
|
||||
def PrintSlotName(self):
|
||||
offsetKey = self.offsetSlot.keys()
|
||||
offsetKey.sort()
|
||||
for _ in offsetKey:
|
||||
print (_, self.offsetSlot[_])
|
||||
print '--------------'
|
||||
|
||||
def AddSlot(self, slotName, gridPath, items, dim, refresh=True):
|
||||
"""
|
||||
增加槽位,需要在Create函数执行之前绑定所需的connectionName
|
||||
:param slotName: 槽位名称
|
||||
:param refresh: 是否刷新数据
|
||||
:param gridPath: 网格路径
|
||||
:param items: 物品集
|
||||
:param dim: 网格尺寸
|
||||
:return:
|
||||
"""
|
||||
if slotName in self.slotOffset: return
|
||||
self.InitSlot(slotName, gridPath, items, dim)
|
||||
self.InitItemData() if refresh else 0
|
||||
|
||||
def DelSlot(self, slotName, refresh=True):
|
||||
"""
|
||||
删除槽位,包括items,slotOffset,offsetSlot
|
||||
:param slotName:槽位名称
|
||||
:param refresh: 是否刷新数据
|
||||
:return:
|
||||
"""
|
||||
if not slotName in self.slotOffset: return
|
||||
offsetKey = self.offsetSlot.keys()
|
||||
offsetKey.sort()
|
||||
startAndEndIndex = self.GetStartAndFinalIndex(slotName)
|
||||
oldOffset = startAndEndIndex[0]
|
||||
size = startAndEndIndex[1] - startAndEndIndex[0]
|
||||
del self.items[startAndEndIndex[0]: startAndEndIndex[1]]
|
||||
del self.offsetSlot[oldOffset]
|
||||
del self.slotOffset[slotName]
|
||||
del self.gridIndex[slotName]
|
||||
for index in offsetKey[::-1]:
|
||||
if oldOffset < index: self.slotOffset[self.offsetSlot[index]] = index - size
|
||||
self.RecalculateOffsetSlot()
|
||||
self.InitItemData() if refresh else 0
|
||||
|
||||
# ------------------------------ init ------------------------------
|
||||
def InitItemDataOne(self, slot, slotName, item):
|
||||
"""
|
||||
对某个槽位进行初始化,需要在InitData之后调用
|
||||
"""
|
||||
if item:
|
||||
itemInfo = GetItemBaseInfoSafely(item)
|
||||
self.__itemNum[slot] = item['count']
|
||||
self.__itemId[slot] = itemInfo['id_aux']
|
||||
self.__itemD[slot] = GetItemDurabilityPercent(item, itemInfo)
|
||||
self.__isItemNone[slot] = False
|
||||
else:
|
||||
self.__itemNum[slot] = 0
|
||||
self.__itemD[slot] = (0.0, 0.0)
|
||||
self.__itemId[slot] = 0
|
||||
self.__isItemNone[slot] = False
|
||||
|
||||
def InitItemData(self):
|
||||
"""
|
||||
依据当前items初始化显示所需的物品数据
|
||||
:return:
|
||||
"""
|
||||
self.moveControls = [None for _ in self.items]
|
||||
self.__itemNum = [0 for _ in self.items]
|
||||
self.__itemD = [(0.0, 0.0) for _ in self.items]
|
||||
self.__itemId = [0 for _ in self.items]
|
||||
self.__isItemNone = [False for _ in self.items]
|
||||
offsets = self.offsetSlot.keys()
|
||||
offsets.sort()
|
||||
for offsetIndex in range(len(offsets[:-1])):
|
||||
for index in range(offsets[offsetIndex], offsets[offsetIndex + 1]):
|
||||
if not IsItemDict(self.items[index]): self.items[index] = {}
|
||||
self.InitItemDataOne(index, self.offsetSlot[offsets[offsetIndex]], self.items[index])
|
||||
for index in range(offsets[-1], len(self.items)):
|
||||
if not IsItemDict(self.items[index]): self.items[index] = {}
|
||||
self.InitItemDataOne(index, self.offsetSlot[offsets[-1]], self.items[index])
|
||||
|
||||
def InitFlyingPanel(self, path):
|
||||
self.flyingItemRender = self.GetBaseUIControl(path).asItemRenderer()
|
||||
self.flyingMoveControl = MoveToPos(self.flyingItemRender, 0.1)
|
||||
|
||||
def InitSlot(self, slotName, gridPath, items, dimension):
|
||||
"""
|
||||
初始化物品槽
|
||||
:param dimension: 物品槽网格的尺寸
|
||||
:param items: 物品槽内的物品
|
||||
:param slotName:物品槽名称
|
||||
:param gridPath:物品槽的网格路径
|
||||
:return:
|
||||
"""
|
||||
items = FixList(items, dimension[0] * dimension[1], {})
|
||||
self.GetBaseUIControl(gridPath).asGrid().SetGridDimension(tuple(dimension))
|
||||
self.gridIndex[slotName] = gridPath
|
||||
offset = len(self.items)
|
||||
self.slotOffset[slotName] = offset
|
||||
self.offsetSlot[offset] = slotName
|
||||
self.items += items
|
||||
self.BindingAll(slotName)
|
||||
|
||||
# ------------------------------ init ------------------------------
|
||||
|
||||
def PlayerInvItemChanged(self, args):
|
||||
"""
|
||||
玩家背包服务端主动发生改变时触发
|
||||
"""
|
||||
self.SetUiVisibleInfo(args['item'], args['slot'])
|
||||
|
||||
def Create(self):
|
||||
self.blueFoxClient.ListenForEvent(
|
||||
MOD_NAME, 'BlueFoxServerSystem', 'PlayerInvItemChanged', self, self.PlayerInvItemChanged
|
||||
)
|
||||
|
||||
def Destroy(self):
|
||||
self.blueFoxClient.UnListenForEvent(
|
||||
MOD_NAME, 'BlueFoxServerSystem', 'PlayerInvItemChanged', self, self.PlayerInvItemChanged
|
||||
)
|
||||
|
||||
playerId = clientApi.GetLocalPlayerId()
|
||||
x, y, z = clientCompFactory.CreatePos(playerId).GetFootPos()
|
||||
rot = clientCompFactory.CreateRot(playerId).GetRot()
|
||||
rx, ry, rz = clientApi.GetDirFromRot(rot)
|
||||
self.blueFoxClient.NotifyToServer('RucksackScreenClosed', {
|
||||
'posDim': (
|
||||
(x + rx * 0.3, y + 1.08, z + rz * 0.3), clientCompFactory.CreateGame(LevelId).GetCurrentDimension()
|
||||
), 'playerRot': rot
|
||||
})
|
||||
for moveControl in self.moveControls: moveControl.Finish() if moveControl else 0
|
||||
|
||||
def ItemCellGridInit(self, gridPath, collectionName):
|
||||
"""
|
||||
物品槽网格初始化时触发
|
||||
:param gridPath:网格路径
|
||||
:param collectionName:集合名称
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
def BindingAll(self, collectionName):
|
||||
bindingFunc = ScreenTools.CollectionViewBinder
|
||||
bindingBool = viewBinder.BF_BindBool
|
||||
bindingInt = viewBinder.BF_BindInt
|
||||
bindingFloat = viewBinder.BF_BindFloat
|
||||
bindingString = viewBinder.BF_BindString
|
||||
bindingFunc(bindingBool, collectionName, '#item_num_visible', self.ItemsNumVisible) # 是否显示物品数量文字
|
||||
bindingFunc(bindingString, collectionName, '#item_num', self.ItemsNum) # 显示物品数量
|
||||
bindingFunc(bindingBool, collectionName, '#select_visible', self.SelectVisible) # 是否显示物品槽选择
|
||||
bindingFunc(bindingInt, collectionName, '#item_id_aux', self.RenderItems) # 显示物品
|
||||
bindingFunc(bindingBool, collectionName, '#item_durability_visible', self.ItemsDurabilityVisible) # 是否显示物品耐久度条
|
||||
bindingFunc(
|
||||
bindingFloat, collectionName, '#item_durability_total_amount', self.ItemsDurabilityTotal
|
||||
) # 物品耐久度条总量
|
||||
bindingFunc(
|
||||
bindingFloat, collectionName, '#item_durability_current_amount', self.ItemsDurabilityCurrent
|
||||
) # 物品耐久度条当前量
|
||||
bindingFunc(bindingBool, collectionName, '#item_none_visible', self.ItemsNone) # 是否显示缺失物品背景
|
||||
bindingFunc(bindingBool, collectionName, '#item_cell_init', self.ItemCellInit) # 初始化时触发
|
||||
|
||||
# --------------------- bindings ---------------------
|
||||
|
||||
def GetGirdPath(self, collectionName):
|
||||
return self.gridIndex[collectionName]
|
||||
|
||||
def ItemCellInit(self, collectionName, index):
|
||||
"""
|
||||
初始化时触发,无需任何返回值
|
||||
:param collectionName:集合名称
|
||||
:param index:在网格内的索引值
|
||||
:return:
|
||||
"""
|
||||
if not index: self.ItemCellGridInit(self.GetGirdPath(collectionName), collectionName)
|
||||
|
||||
def ItemsNumVisible(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return self.__itemNum[index] > 1 if index < self.__itemNum.__len__() else False
|
||||
|
||||
def ItemsNum(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return str(self.__itemNum[index]) if index < self.__itemNum.__len__() else ''
|
||||
|
||||
def SelectVisible(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return index == self.selectSlot
|
||||
|
||||
def RenderItems(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return self.__itemId[index] if index < self.__itemId.__len__() else 0
|
||||
|
||||
def ItemsDurabilityVisible(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
if index >= self.__itemD.__len__():
|
||||
return False
|
||||
info = self.__itemD[index]
|
||||
return info[0] != info[1] and info[1] > 0
|
||||
|
||||
def ItemsDurabilityTotal(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return self.__itemD[index][1] if index < self.__itemD.__len__() else 0.0
|
||||
|
||||
def ItemsDurabilityCurrent(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return self.__itemD[index][0] if index < self.__itemD.__len__() else 0.0
|
||||
|
||||
def ItemsNone(self, collectionName, index):
|
||||
index = index + self.slotOffset.get(collectionName, 0)
|
||||
return self.__isItemNone[index] if index < self.__isItemNone.__len__() else False
|
||||
|
||||
# --------------------- bindings ---------------------
|
||||
|
||||
def SetNoneItem(self, slot, itemDict):
|
||||
"""
|
||||
将某个槽位设置为缺少物品
|
||||
:param slot:
|
||||
:param itemDict:
|
||||
:return:
|
||||
"""
|
||||
itemInfo = GetItemBaseInfoSafely(itemDict)
|
||||
self.__itemNum[slot] = itemDict['count']
|
||||
self.__itemD[slot] = GetItemDurabilityPercent(itemDict, itemInfo)
|
||||
self.__itemId[slot] = itemInfo['id_aux']
|
||||
self.__isItemNone[slot] = True
|
||||
self.items[slot] = {}
|
||||
|
||||
def NoticeServerChangeItem(self, slot, itemDict, itemOperate):
|
||||
self.blueFoxClient.NotifyToServer(
|
||||
'ChangeItem', {
|
||||
'slot': slot, 'item': itemDict, 'itemOperate': itemOperate,
|
||||
'posDim': None, 'posType': self.GetOffsetNameByIndex(slot),
|
||||
'startSlot': self.GetOffsetStartIndex(slot)
|
||||
}
|
||||
)
|
||||
|
||||
def NoticeServerSpawnItemEntities(self, itemDict):
|
||||
playerId = clientApi.GetLocalPlayerId()
|
||||
x, y, z = clientCompFactory.CreatePos(playerId).GetFootPos()
|
||||
self.blueFoxClient.NotifyToServer("SpawnItemEntity", {
|
||||
'posDim': (
|
||||
(x, y + 1.6, z), clientCompFactory.CreateGame(LevelId).GetCurrentDimension()
|
||||
), 'item': itemDict,
|
||||
'playerRot': clientCompFactory.CreateRot(playerId).GetRot()
|
||||
})
|
||||
|
||||
def SetItem(self, slot, itemDict=None, noticeServer=True):
|
||||
"""
|
||||
设置某个槽位, 不论是否物品为空, 返回原本物品
|
||||
:param noticeServer:
|
||||
:param itemDict:
|
||||
:param slot:
|
||||
:return:
|
||||
"""
|
||||
return self.SetUiItem(slot, itemDict, noticeServer) if itemDict else self.SetUiItemNone(slot, noticeServer)
|
||||
|
||||
def SetItemAbs(self, slot, itemDict=None, noticeServer=False):
|
||||
"""
|
||||
设置某个槽位, 不论是否物品为空
|
||||
:param noticeServer:
|
||||
:param itemDict:
|
||||
:param slot:
|
||||
:return:
|
||||
"""
|
||||
self.SetUiItemAbs(slot, itemDict, noticeServer) if itemDict else self.SetUiItemNone(slot, noticeServer)
|
||||
|
||||
def SetUiItemNone(self, slot, noticeServer=None):
|
||||
"""
|
||||
将某个槽位设置为空,返回原本物品
|
||||
:param noticeServer:
|
||||
:param slot:
|
||||
:return:
|
||||
"""
|
||||
originItem = self.items[slot]
|
||||
self.SetUiItemAbs(slot, {}, noticeServer)
|
||||
return originItem
|
||||
|
||||
def SetUiItemAbs(self, slot, destinationItem, noticeServer=True, itemOperate=None):
|
||||
"""
|
||||
设置某一个槽位的物品, 不返回任何物品及合并
|
||||
:param itemOperate: 物品操作,用于服务端操作
|
||||
:param noticeServer: 是否通知服务端,默认发送
|
||||
:param slot:设置槽位
|
||||
:param destinationItem:目标物品字典
|
||||
:return:
|
||||
"""
|
||||
destinationItemInfo = GetItemBaseInfoSafely(destinationItem)
|
||||
self.__itemNum[slot] = destinationItem.get('count', 0)
|
||||
self.__itemD[slot] = GetItemDurabilityPercent(destinationItem, destinationItemInfo)
|
||||
self.__itemId[slot] = destinationItemInfo.get('id_aux', 0)
|
||||
self.__isItemNone[slot] = False
|
||||
|
||||
self.items[slot] = destinationItem
|
||||
self.NoticeServerChangeItem(slot, destinationItem, itemOperate) if noticeServer else 0
|
||||
|
||||
def SetUiItem(self, slot, destinationItem, noticeServer=True):
|
||||
"""
|
||||
设置某一个槽位的物品,返回原本物品如果不可合并或者合并后多出的物品
|
||||
:param noticeServer: 是否通知服务端
|
||||
:param slot:设置槽位
|
||||
:param destinationItem:目标物品字典
|
||||
:return:
|
||||
"""
|
||||
originItem = self.items[slot]
|
||||
originItemInfo = GetItemBaseInfoSafely(destinationItem)
|
||||
if IsSameItem(originItem, destinationItem):
|
||||
if originItem['count'] + destinationItem['count'] <= originItemInfo['maxStackSize']:
|
||||
destinationItem['count'] += originItem['count']
|
||||
originItem = {}
|
||||
else:
|
||||
originItem['count'] += destinationItem['count'] - originItemInfo['maxStackSize']
|
||||
destinationItem['count'] = originItemInfo['maxStackSize']
|
||||
self.SetUiItemAbs(slot, destinationItem, noticeServer)
|
||||
return originItem
|
||||
|
||||
def SetUiVisibleInfo(self, itemDict, slot):
|
||||
"""
|
||||
设置物品,通常用于服务端主动设置物品,此接口不会发送ChangeItem事件
|
||||
:param itemDict:物品信息字典
|
||||
:param slot:设置槽位
|
||||
:return:
|
||||
"""
|
||||
self.SetUiItemAbs(slot, itemDict, False)
|
||||
|
||||
def GetOffsetNameByIndex(self, index):
|
||||
"""
|
||||
通过index获取偏移名称
|
||||
:param index:
|
||||
:return:
|
||||
"""
|
||||
offsetKey = self.offsetSlot.keys()
|
||||
offsetKey.sort()
|
||||
offset = offsetKey[-1]
|
||||
for _ in range(offsetKey.__len__() - 1):
|
||||
if offsetKey[_] <= index < offsetKey[_ + 1]:
|
||||
return self.offsetSlot[offsetKey[_]]
|
||||
return self.offsetSlot[offset]
|
||||
|
||||
def GetOffsetStartIndex(self, index):
|
||||
"""
|
||||
通过index获取偏移起始index
|
||||
:param index:
|
||||
:return:
|
||||
"""
|
||||
offsetKey = self.offsetSlot.keys()
|
||||
offsetKey.sort()
|
||||
offset = offsetKey[-1]
|
||||
for _ in range(offsetKey.__len__() - 1):
|
||||
if offsetKey[_] <= index < offsetKey[_ + 1]:
|
||||
return offsetKey[_]
|
||||
return offsetKey[-1]
|
||||
|
||||
def GetItemCellByIndex(self, index):
|
||||
"""
|
||||
通过槽位获取itemCell的路径
|
||||
:param index:槽位编号
|
||||
:return:
|
||||
"""
|
||||
offset = self.GetOffsetStartIndex(index)
|
||||
return self.gridIndex[self.offsetSlot[offset]] + '/item_cell{}'.format(index - offset + 1)
|
||||
|
||||
# 以下三个方法是pe背包槽位交互模式
|
||||
def FlyingMoveFinish(self, slot):
|
||||
"""
|
||||
pe下槽位发生物品堆叠时, flyRender动画完成时触发
|
||||
:return:
|
||||
"""
|
||||
self.flyingItemRender.SetVisible(False) if self.flyingItemRender else 0
|
||||
self.NoticeServerChangeItem(
|
||||
slot, self.flyingItem,
|
||||
ItemOperate.ExchangeFlying(
|
||||
slot - self.GetOffsetStartIndex(slot), self.GetOffsetNameByIndex(slot)
|
||||
)
|
||||
)
|
||||
self.flyingItem = {}
|
||||
|
||||
def ExchangeFinish(self, slot, destinationSlot):
|
||||
"""
|
||||
pe下槽位发生物品交换时, 动画完成时触发
|
||||
:param destinationSlot: 与其交换的槽位
|
||||
:param slot: 槽位
|
||||
:return:
|
||||
"""
|
||||
deSlotName = self.GetOffsetNameByIndex(destinationSlot)
|
||||
deSlotOffset = destinationSlot - self.GetOffsetStartIndex(destinationSlot)
|
||||
fromSlotName = self.GetOffsetNameByIndex(slot)
|
||||
fromSlotOffset = slot - self.GetOffsetStartIndex(slot)
|
||||
|
||||
originItem = self.items[slot]
|
||||
destinationItem = self.items[destinationSlot]
|
||||
self.SetUiItemAbs(slot, destinationItem, False)
|
||||
deSlot = self.slotOffset[deSlotName] + deSlotOffset
|
||||
if deSlotName not in self.slotOffset or deSlot >= len(self.items):
|
||||
self.flyingItem = originItem
|
||||
else:
|
||||
self.SetUiItemAbs(deSlot, originItem, False)
|
||||
self.NoticeServerChangeItem(slot, {}, ItemOperate.Exchange(
|
||||
fromSlotOffset, fromSlotName,
|
||||
deSlotOffset, deSlotName
|
||||
))
|
||||
self.flyingItem = {}
|
||||
|
||||
def StackFinish(self, slot, destinationSlot):
|
||||
"""
|
||||
pe下槽位发生物品堆叠时, slot动画完成时触发
|
||||
:param slot: 槽位
|
||||
:param destinationSlot: 与其交换的槽位
|
||||
:return:
|
||||
"""
|
||||
orSlotName = self.GetOffsetNameByIndex(slot)
|
||||
orSlotOffset = slot - self.GetOffsetStartIndex(slot)
|
||||
deSlotName = self.GetOffsetNameByIndex(destinationSlot)
|
||||
deSlotOffset = destinationSlot - self.GetOffsetStartIndex(destinationSlot)
|
||||
|
||||
self.flyingItem = self.SetItem(destinationSlot, self.items[slot], False)
|
||||
orSlot = self.slotOffset[orSlotName] + orSlotOffset
|
||||
if orSlotName not in self.slotOffset or orSlot >= len(self.items):
|
||||
self.flyingItem = {}
|
||||
else:
|
||||
self.SetItemAbs(slot, self.flyingItem, False)
|
||||
self.flyingItem = {}
|
||||
self.flyingItemRender.SetVisible(False, False) if self.flyingItemRender else 0
|
||||
self.NoticeServerChangeItem(slot, {}, ItemOperate.Merge(
|
||||
orSlotOffset, orSlotName, deSlotOffset, deSlotName
|
||||
))
|
||||
|
||||
@staticmethod
|
||||
def SetUiItemForItemRender(render, itemDict):
|
||||
"""
|
||||
使用itemDict设置物品渲染
|
||||
:param render:itemRender
|
||||
:param itemDict:需要设置的物品字典
|
||||
:return:
|
||||
"""
|
||||
if not itemDict:
|
||||
render.SetVisible(False)
|
||||
return
|
||||
render.SetUiItem(
|
||||
itemDict.get('newItemName', 'minecraft:air'), itemDict.get('newAuxValue', 0),
|
||||
(itemDict.get('enchantData', []) + itemDict.get('modEnchantData', [])).__len__() > 0,
|
||||
itemDict.get('userData', None)
|
||||
)
|
||||
|
||||
def PcDown(self, selectSlot):
|
||||
"""
|
||||
电脑点击物品槽触发
|
||||
:param selectSlot:槽位
|
||||
:return:
|
||||
"""
|
||||
oldItem = self.items[selectSlot]
|
||||
if self.flyingItem is None: self.flyingItem = {}
|
||||
self.SetUiItemAbs(
|
||||
selectSlot, self.flyingItem, True,
|
||||
ItemOperate.ExchangeFlying(
|
||||
selectSlot - self.GetOffsetStartIndex(selectSlot), self.GetOffsetNameByIndex(selectSlot)
|
||||
)
|
||||
)
|
||||
self.flyingItem = oldItem
|
||||
if self.flyingItemRender:
|
||||
self.SetUiItemForItemRender(
|
||||
self.flyingItemRender, self.flyingItem
|
||||
) if self.flyingItem else self.flyingItemRender.SetVisible(False, False)
|
||||
|
||||
def GetSlotAnimationState(self, originSlot, destinationSlot):
|
||||
"""
|
||||
pe模式下, 交换物品时根据槽位确认动画形式
|
||||
:param originSlot:先前选择槽位
|
||||
:param destinationSlot:后选择槽位
|
||||
:return:先前, 后选择, flyingItem
|
||||
"""
|
||||
originItem = self.items[originSlot]
|
||||
destinationItem = self.items[destinationSlot]
|
||||
itemInfo = GetItemBaseInfoSafely(originItem)
|
||||
if IsSameItem(originItem, destinationItem):
|
||||
if originItem['count'] + destinationItem['count'] < itemInfo['maxStackSize']:
|
||||
return True, False, False
|
||||
elif destinationItem['count'] == itemInfo['maxStackSize']:
|
||||
return True, True, False
|
||||
else:
|
||||
return False, False, True
|
||||
return True, True, False
|
||||
|
||||
def GetPeFlyingItem(self, originSlot, destinationSlot):
|
||||
"""
|
||||
在pe模式下, 获取flyingItem
|
||||
:param originSlot: 现物品槽位
|
||||
:param destinationSlot: 目标物品槽位
|
||||
:return:
|
||||
"""
|
||||
itemInfo = GetItemBaseInfoSafely(self.items[originSlot])
|
||||
maxStack = (itemInfo or {}).get('maxStackSize') or 64
|
||||
itemDict1, itemDict2 = DeclareItemAnyNum(
|
||||
self.items[originSlot], maxStack - self.items[destinationSlot]['count'])
|
||||
self.SetItemAbs(originSlot, itemDict1)
|
||||
return itemDict2
|
||||
|
||||
def DeliverItem(self, originSlot, destinationSlot):
|
||||
"""
|
||||
以动画形式从origin向destination传递物品
|
||||
:param originSlot:起源物品槽位
|
||||
:param destinationSlot:目标物品槽位
|
||||
:return:
|
||||
"""
|
||||
ox, oy = self.GetAbsPos(self.GetItemCellByIndex(originSlot) + '/item_cell/item_renderer')
|
||||
dx, dy = self.GetAbsPos(self.GetItemCellByIndex(destinationSlot) + '/item_cell/item_renderer')
|
||||
_, __, ___ = self.GetSlotAnimationState(originSlot, destinationSlot)
|
||||
if _:
|
||||
self.moveControls[originSlot].StartMove(
|
||||
(dx - ox, dy - oy), self.ExchangeFinish if __ else self.StackFinish, originSlot, destinationSlot
|
||||
)
|
||||
if __:
|
||||
self.moveControls[destinationSlot].StartMove(
|
||||
(-dx + ox, -dy + oy), lambda: None
|
||||
)
|
||||
if ___:
|
||||
self.SetUiItemForItemRender(self.flyingItemRender, self.items[originSlot]) if self.flyingItemRender else 0
|
||||
self.flyingItemRender.SetVisible(True) if self.flyingItemRender else 0
|
||||
self.flyingMoveControl.ResetInitialPos((ox, oy))
|
||||
self.flyingMoveControl.StartMove(
|
||||
(dx, dy), self.StackFinish, originSlot, destinationSlot
|
||||
)
|
||||
|
||||
def PeDown(self, selectSlot):
|
||||
"""
|
||||
手机点击物品槽触发
|
||||
:param selectSlot:槽位
|
||||
:return:
|
||||
"""
|
||||
if self.selectSlot == selectSlot:
|
||||
self.selectSlot = -1
|
||||
return
|
||||
else:
|
||||
if self.selectSlot is not -1:
|
||||
if self.items[self.selectSlot]:
|
||||
self.DeliverItem(self.selectSlot, selectSlot)
|
||||
self.selectSlot = -1
|
||||
return
|
||||
if self.items[selectSlot]:
|
||||
self.selectSlot = selectSlot
|
||||
self.flyingItemRender.SetPosition(
|
||||
self.GetAbsPos(self.GetItemCellByIndex(selectSlot) + '/item_cell/item_renderer')
|
||||
) if self.flyingItemRender else 0
|
||||
self.flyingMoveControl.ResetInitialPos()
|
||||
|
||||
@viewBinder.binding(viewBinder.BF_ButtonClickUp, '#item_cell_touch')
|
||||
def ButtonUp(self, args):
|
||||
"""
|
||||
物品槽被点击会触发无论电脑或者手机
|
||||
:param args:与普通的按钮回调参数一致
|
||||
:return:
|
||||
"""
|
||||
touchIndex = args['#collection_index'] + self.slotOffset.get(args['#collection_name'], 0)
|
||||
self.CreateItemRenderMoveControl(touchIndex)
|
||||
self.PeDown(touchIndex) if Platform else self.PcDown(touchIndex)
|
||||
|
||||
def CreateItemRenderMoveControl(self, slot):
|
||||
"""
|
||||
创建物品渲染的移动动画控制器
|
||||
:param slot:
|
||||
:return:
|
||||
"""
|
||||
if self.moveControls[slot] is None:
|
||||
itemCell = self.GetBaseUIControl(self.GetItemCellByIndex(slot))
|
||||
self.moveControls[slot] = MoveToPos(
|
||||
itemCell.GetChildByName('item_cell').GetChildByName('item_renderer'), 0.1
|
||||
)
|
||||
|
||||
def GetAbsPos(self, path):
|
||||
"""
|
||||
获取元素的绝对坐标
|
||||
:param path:路径
|
||||
:return:绝对坐标, xy
|
||||
"""
|
||||
control = self.GetBaseUIControl(path)
|
||||
return control.GetGlobalPosition() if control else (0, 0)
|
||||
|
||||
@viewBinder.binding(viewBinder.BF_BindInt, '#main.gametick')
|
||||
def GameTick(self):
|
||||
if clientCompFactory and not Platform and self.flyingItemRender and self.flyingItem:
|
||||
x, y = clientCompFactory.CreateActorMotion(clientApi.GetLocalPlayerId()).GetMousePosition()
|
||||
self.flyingItemRender.SetPosition((x - 9, y - 9))
|
||||
self.flyingItemRender.SetVisible(self.flyingItem, False)
|
||||
for moveControl in self.moveControls:
|
||||
moveControl.Refresh() if moveControl else 0
|
||||
self.flyingMoveControl.Refresh() if self.flyingMoveControl else 0
|
||||
|
||||
def CreateItemEntityIfFlyItem(self):
|
||||
"""
|
||||
创建跟随鼠标的物品
|
||||
:return:
|
||||
"""
|
||||
if self.flyingItem: self.NoticeServerSpawnItemEntities(self.flyingItem)
|
||||
+1864
File diff suppressed because it is too large.
Load diff
+117
@@ -0,0 +1,117 @@
|
||||
# coding=utf-8
|
||||
from Grapher import *
|
||||
|
||||
|
||||
class SizeAnim(MovableUi):
|
||||
def __init__(self, base_ui_control, time):
|
||||
# type:(BaseUIControl,float)->None
|
||||
self.initialSize = base_ui_control.GetSize()
|
||||
self.totalTime = float(time)
|
||||
self.backFunc = lambda *args: None
|
||||
self.args = []
|
||||
self.fromSize = (0, 0)
|
||||
self.fatherSize = (0, 0)
|
||||
self.fromPos = (0, 0)
|
||||
self.speed = 0
|
||||
super(SizeAnim, self).__init__(base_ui_control)
|
||||
|
||||
def ResetInitialSize(self, initialSize=None):
|
||||
"""
|
||||
重置原位置
|
||||
:return:
|
||||
"""
|
||||
self.initialSize = self.baseUiControl.GetSize() if initialSize is None else initialSize
|
||||
|
||||
def Reset(self):
|
||||
super(SizeAnim, self).Reset()
|
||||
self.baseUiControl.SetSize(self.initialSize)
|
||||
self.backFunc = lambda: None
|
||||
self.args = tuple()
|
||||
|
||||
def Finish(self):
|
||||
super(SizeAnim, self).Finish()
|
||||
self.backFunc(*self.args)
|
||||
self.Reset()
|
||||
|
||||
def StartAnim(self, fromSize, fatherSize, fromPos, backFunc, *backFuncArgs):
|
||||
self.fromSize = fromSize
|
||||
self.fatherSize = fatherSize
|
||||
self.fromPos = fromPos
|
||||
|
||||
self.speedX = (fromSize[0] - self.initialSize[0]) / self.totalTime
|
||||
self.speedY = (fromSize[1] - self.initialSize[1]) / self.totalTime
|
||||
self.args = backFuncArgs
|
||||
self.backFunc = backFunc
|
||||
super(SizeAnim, self).Start()
|
||||
|
||||
def Refresh(self):
|
||||
if self and self.state != ControlState.Run:
|
||||
return
|
||||
runTime = self.GetWorkTime()
|
||||
if runTime < self.totalTime:
|
||||
size = (
|
||||
self.fromSize[0] - self.speedX * runTime, self.fromSize[1] - self.speedY * runTime
|
||||
)
|
||||
self.baseUiControl.SetSize(size)
|
||||
position = ((self.fatherSize[0] - size[0]) / 2.0, (self.fatherSize[1] - size[1]) / 2.0)
|
||||
self.baseUiControl.SetPosition(position)
|
||||
else:
|
||||
self.baseUiControl.SetPosition(self.fromPos)
|
||||
self.baseUiControl.SetSize(self.initialSize)
|
||||
self.Finish()
|
||||
return runTime
|
||||
|
||||
|
||||
class MoveToPos(MovableUi):
|
||||
def __init__(self, base_ui_control, time):
|
||||
# type:(BaseUIControl,float)->None
|
||||
self.initialPos = base_ui_control.GetPosition()
|
||||
self.totalTime = float(time)
|
||||
self.speedX = 0.0
|
||||
self.speedY = 0.0
|
||||
self.destinationPos = (0, 0)
|
||||
self.backFunc = lambda *args: None
|
||||
self.args = []
|
||||
super(MoveToPos, self).__init__(base_ui_control)
|
||||
|
||||
def ResetInitialPos(self, initialPos=None):
|
||||
"""
|
||||
重置原位置
|
||||
:return:
|
||||
"""
|
||||
self.initialPos = self.baseUiControl.GetPosition() if initialPos is None else initialPos
|
||||
|
||||
def Reset(self):
|
||||
super(MoveToPos, self).Reset()
|
||||
self.baseUiControl.SetPosition(self.initialPos)
|
||||
self.backFunc = lambda: None
|
||||
self.args = tuple()
|
||||
|
||||
def Finish(self):
|
||||
super(MoveToPos, self).Finish()
|
||||
self.backFunc(*self.args)
|
||||
self.Reset()
|
||||
|
||||
def StartMove(self, destinationPos, backFunc, *backFuncArgs):
|
||||
self.destinationPos = destinationPos
|
||||
self.args = backFuncArgs
|
||||
self.backFunc = backFunc
|
||||
dx, dy = destinationPos[0] - self.initialPos[0], destinationPos[1] - self.initialPos[1]
|
||||
self.speedX = dx / self.totalTime
|
||||
self.speedY = dy / self.totalTime
|
||||
super(MoveToPos, self).Start()
|
||||
|
||||
def Refresh(self):
|
||||
if self and self.state != ControlState.Run:
|
||||
return
|
||||
runTime = self.GetWorkTime()
|
||||
if runTime < self.totalTime:
|
||||
self.baseUiControl.SetPosition((
|
||||
self.speedX * runTime + self.initialPos[0], self.speedY * runTime + self.initialPos[1]
|
||||
))
|
||||
else:
|
||||
self.baseUiControl.SetPosition((
|
||||
self.destinationPos[0] + self.initialPos[0], self.destinationPos[1] + self.initialPos[1]
|
||||
))
|
||||
self.Finish()
|
||||
return runTime
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# coding=utf-8
|
||||
from __init__ import *
|
||||
|
||||
|
||||
class MovableUi(Base):
|
||||
"""
|
||||
可控的移动性Ui
|
||||
以当前位置为原点,位置随时间变化
|
||||
"""
|
||||
|
||||
def __init__(self, base_ui_control, moving_route_x=lambda t: 0, moving_route_y=lambda t: 0):
|
||||
# type:(BaseUIControl,Any,Any)->None
|
||||
"""
|
||||
@param base_ui_control: ui控件
|
||||
@param moving_route_x: x轴的运动路径
|
||||
@param moving_route_y: y轴的运动路径
|
||||
"""
|
||||
self.baseUiControl = base_ui_control
|
||||
self.movingRouteX = moving_route_x
|
||||
self.movingRouteY = moving_route_y
|
||||
self.initialPos = base_ui_control.GetPosition()
|
||||
Base.__init__(self)
|
||||
|
||||
def ResetRouteX(self, routeX=lambda t: 0):
|
||||
"""
|
||||
设置为新的X轴运动函数
|
||||
:param routeX: 函数, 传参运动时间
|
||||
:return:
|
||||
"""
|
||||
self.Reset()
|
||||
self.movingRouteX = routeX
|
||||
|
||||
def ResetRouteY(self, routeY=lambda t: 0):
|
||||
"""
|
||||
设置为新的Y轴运动函数
|
||||
:param routeY: 函数, 传参运动时间
|
||||
:return:
|
||||
"""
|
||||
self.Reset()
|
||||
self.movingRouteY = routeY
|
||||
|
||||
def Refresh(self):
|
||||
if self.state != ControlState.Run:
|
||||
return
|
||||
runTime = self.GetWorkTime()
|
||||
self.baseUiControl.SetPosition((
|
||||
self.movingRouteX(runTime) + self.initialPos[0], self.movingRouteY(runTime) + self.initialPos[1]
|
||||
))
|
||||
return runTime
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# coding=utf-8
|
||||
import datetime
|
||||
|
||||
try:
|
||||
from mod.client.ui.controls.baseUIControl import *
|
||||
from typing import *
|
||||
except ImportError:
|
||||
BaseUIControl = None
|
||||
ImageUIControl = None
|
||||
LabelUIControl = None
|
||||
|
||||
|
||||
class ControlState(object):
|
||||
Finish = 0
|
||||
Run = 1
|
||||
Suspend = 2
|
||||
|
||||
|
||||
class Base(object):
|
||||
state = ControlState.Finish
|
||||
suspendTimePeriod = 0.0
|
||||
runTime = datetime.datetime.now()
|
||||
suspendTimePoint = runTime
|
||||
|
||||
def __init__(self):
|
||||
self.runTime = datetime.datetime.now()
|
||||
self.suspendTimePoint = self.runTime
|
||||
self.suspendTimePeriod = 0.0
|
||||
|
||||
def Refresh(self):
|
||||
"""
|
||||
刷新,开始执行移动任务时需要刷新
|
||||
:return:已执行任务的时间
|
||||
"""
|
||||
if not self.state == ControlState.Run:
|
||||
return
|
||||
now = (datetime.datetime.now() - self.runTime).total_seconds() - self.suspendTimePeriod
|
||||
return now
|
||||
|
||||
def GetWorkTime(self):
|
||||
"""
|
||||
获取执行了多少时间
|
||||
"""
|
||||
return (datetime.datetime.now() - self.runTime).total_seconds() - self.suspendTimePeriod
|
||||
|
||||
def Suspend(self):
|
||||
"""暂停任务.与完成不同的是,下一次开始时,继续执行暂停时的状态"""
|
||||
self.state = ControlState.Suspend
|
||||
self.suspendTimePoint = datetime.datetime.now()
|
||||
|
||||
def Reset(self):
|
||||
"""重置已运作时间"""
|
||||
self.runTime = datetime.datetime.now()
|
||||
|
||||
def GetState(self):
|
||||
# type:()->str
|
||||
"""
|
||||
获取状态
|
||||
"""
|
||||
return self.state
|
||||
|
||||
def Start(self):
|
||||
"""开始任务"""
|
||||
if self.state == ControlState.Finish:
|
||||
self.runTime = datetime.datetime.now()
|
||||
self.state = ControlState.Run
|
||||
elif self.state == ControlState.Suspend:
|
||||
self.suspendTimePeriod += (datetime.datetime.now() - self.suspendTimePoint).total_seconds()
|
||||
self.state = ControlState.Run
|
||||
|
||||
def Finish(self):
|
||||
"""完成任务"""
|
||||
self.runTime = datetime.datetime.now()
|
||||
self.suspendTimePoint = self.runTime
|
||||
self.suspendTimePeriod = 0.0
|
||||
self.state = ControlState.Finish
|
||||
# 初始化
|
||||
@@ -0,0 +1,81 @@
|
||||
# coding=utf-8
|
||||
from BlueFoxMath import cos, sin, floor, pi, ceil, MatProduct
|
||||
|
||||
|
||||
def FixPosition(pos):
|
||||
"""
|
||||
修正坐标, 通常用于方块坐标
|
||||
"""
|
||||
pos = list(pos)
|
||||
for index in range(pos.__len__()):
|
||||
pos[index] = int(floor(pos[index]) if ceil(pos[index]) - pos[index] > 0.5 else ceil(pos[index]))
|
||||
return tuple(pos)
|
||||
|
||||
|
||||
class OriginCoord(object):
|
||||
"""
|
||||
对三维坐标进行原点变换
|
||||
"""
|
||||
|
||||
def __init__(self, originPos):
|
||||
self.originPos = originPos
|
||||
|
||||
def CalculatePos(self, pos):
|
||||
"""
|
||||
计算变换
|
||||
"""
|
||||
x, y, z = pos
|
||||
return x - self.originPos[0], y - self.originPos[1], z - self.originPos[2]
|
||||
|
||||
def AbsPos(self, pos):
|
||||
"""
|
||||
逆计算变换
|
||||
"""
|
||||
x, y, z = pos
|
||||
return x + self.originPos[0], y + self.originPos[1], z + self.originPos[2]
|
||||
|
||||
|
||||
class RotAxes(object):
|
||||
X = 0
|
||||
Y = 1
|
||||
Z = 2
|
||||
|
||||
|
||||
BlockFaceRot = [90, 0, 270, 180] # 四面向方块对于不同aux对应不同旋转角度
|
||||
|
||||
|
||||
class RotCoord(object):
|
||||
"""
|
||||
对三维坐标进行旋转变换
|
||||
"""
|
||||
|
||||
def __init__(self, rot, axes):
|
||||
"""
|
||||
:param rot:旋转角度
|
||||
:param axes:旋转轴
|
||||
"""
|
||||
self.rotation = rot / 180.0 * pi
|
||||
if axes == RotAxes.X:
|
||||
self.rotMat = [
|
||||
[1, 0, 0],
|
||||
[0, cos(self.rotation), -sin(self.rotation)],
|
||||
[0, sin(self.rotation), cos(self.rotation)]
|
||||
]
|
||||
elif axes == RotAxes.Y:
|
||||
self.rotMat = [
|
||||
[cos(self.rotation), 0, sin(self.rotation)],
|
||||
[0, 1, 0],
|
||||
[-sin(self.rotation), 0, cos(self.rotation)]
|
||||
]
|
||||
else:
|
||||
self.rotMat = [
|
||||
[0, cos(self.rotation), -sin(self.rotation)],
|
||||
[0, sin(self.rotation), cos(self.rotation)],
|
||||
[0, 0, 1],
|
||||
]
|
||||
|
||||
def CalculatePos(self, pos):
|
||||
"""
|
||||
计算变换
|
||||
"""
|
||||
return MatProduct(pos, self.rotMat)
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
# coding=utf-8
|
||||
import copy
|
||||
|
||||
from ..ToolsConfig import *
|
||||
|
||||
|
||||
class MountDataKeyName(object):
|
||||
Animation = 'animation'
|
||||
AnimationController = 'animation_controller'
|
||||
ScriptAnimation = 'script_animation'
|
||||
Geometry = 'geometry'
|
||||
Texture = 'texture'
|
||||
RenderController = 'render_controller'
|
||||
Material = 'material'
|
||||
|
||||
|
||||
class BasicMountData(dict):
|
||||
"""
|
||||
外挂模型动画基本数据字典
|
||||
"""
|
||||
|
||||
def __init__(self, _data=None):
|
||||
# type:(dict) -> None
|
||||
if _data is None:
|
||||
_data = {}
|
||||
data = {
|
||||
MountDataKeyName.Animation: {},
|
||||
MountDataKeyName.AnimationController: {},
|
||||
MountDataKeyName.ScriptAnimation: {},
|
||||
MountDataKeyName.Geometry: {},
|
||||
MountDataKeyName.Texture: {},
|
||||
MountDataKeyName.RenderController: [],
|
||||
MountDataKeyName.Material: {}
|
||||
}
|
||||
for kv in _data.items():
|
||||
data[kv[0]] = kv[1]
|
||||
super(BasicMountData, self).__init__(data)
|
||||
|
||||
def Copy(self):
|
||||
"""
|
||||
继承本字典的所有信息
|
||||
"""
|
||||
return copy.deepcopy(self)
|
||||
|
||||
def SetAnimationData(self, value):
|
||||
self[MountDataKeyName.Animation] = value
|
||||
return self
|
||||
|
||||
def AddAnimationData(self, key, value):
|
||||
self[MountDataKeyName.Animation][key] = value
|
||||
return self
|
||||
|
||||
def SetAnimationControllerData(self, value):
|
||||
self[MountDataKeyName.AnimationController] = value
|
||||
return self
|
||||
|
||||
def AddAnimationControllerData(self, key, value):
|
||||
self[MountDataKeyName.AnimationController][key] = value
|
||||
return self
|
||||
|
||||
def SetScriptAnimationData(self, value):
|
||||
self[MountDataKeyName.ScriptAnimation] = value
|
||||
return self
|
||||
|
||||
def AddScriptAnimationData(self, key, value):
|
||||
self[MountDataKeyName.ScriptAnimation][key] = value
|
||||
return self
|
||||
|
||||
def SetGeometryData(self, value):
|
||||
self[MountDataKeyName.Geometry] = value
|
||||
return self
|
||||
|
||||
def AddGeometryData(self, key, value):
|
||||
self[MountDataKeyName.Geometry][key] = value
|
||||
return self
|
||||
|
||||
def SetTextureData(self, value):
|
||||
self[MountDataKeyName.Texture] = value
|
||||
return self
|
||||
|
||||
def AddTextureData(self, key, value):
|
||||
self[MountDataKeyName.Texture][key] = value
|
||||
return self
|
||||
|
||||
def SetRenderControllerData(self, value):
|
||||
self[MountDataKeyName.RenderController] = value
|
||||
return self
|
||||
|
||||
def AddRenderControllerData(self, value):
|
||||
self[MountDataKeyName.RenderController].append(value)
|
||||
return self
|
||||
|
||||
def SetMaterialData(self, value):
|
||||
self[MountDataKeyName.Material] = value
|
||||
return self
|
||||
|
||||
def AddMaterialData(self, key, value):
|
||||
self[MountDataKeyName.Material][key] = value
|
||||
return self
|
||||
|
||||
|
||||
def RemoveItem(playerId, data):
|
||||
# type:(Union[str,int],BasicMountData)->None
|
||||
"""
|
||||
删除外挂模型
|
||||
"""
|
||||
playerRenderComp = clientCompFactory.CreateActorRender(playerId)
|
||||
RemoveRenderController(playerRenderComp, data['render_controller'])
|
||||
|
||||
|
||||
def LoadItem(playerId, data):
|
||||
# type:(Union[str,int],BasicMountData)->None
|
||||
"""
|
||||
加载外挂模型贴图动画渲染器等
|
||||
"""
|
||||
playerRenderComp = clientCompFactory.CreateActorRender(playerId)
|
||||
LoadGeometry(playerRenderComp, data['geometry'])
|
||||
LoadTexture(playerRenderComp, data['texture'])
|
||||
LoadRenderController(playerRenderComp, data['render_controller'])
|
||||
LoadAnimation(playerRenderComp, data['animation'])
|
||||
LoadAnimationController(playerRenderComp, data['animation_controller'])
|
||||
LoadScriptAnimation(playerRenderComp, data['script_animation'])
|
||||
LoadMaterial(playerRenderComp, data['material'])
|
||||
|
||||
|
||||
def RemoveGeometry(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.RemovePlayerGeometry(_[0])
|
||||
|
||||
|
||||
def RemoveRenderController(playerRenderComp, data):
|
||||
for _ in data:
|
||||
playerRenderComp.RemovePlayerRenderController(_)
|
||||
|
||||
|
||||
def RemoveAnimationController(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.RemovePlayerAnimationController(_[0])
|
||||
|
||||
|
||||
def LoadGeometry(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerGeometry(*_)
|
||||
|
||||
|
||||
def LoadTexture(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerTexture(*_)
|
||||
|
||||
|
||||
def LoadRenderController(playerRenderComp, data):
|
||||
for _ in data:
|
||||
playerRenderComp.AddPlayerRenderController(_)
|
||||
|
||||
|
||||
def LoadAnimation(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerAnimation(*_)
|
||||
|
||||
|
||||
def LoadAnimationController(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerAnimationController(*_)
|
||||
|
||||
|
||||
def LoadScriptAnimation(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerScriptAnimate(*_)
|
||||
|
||||
|
||||
def LoadMaterial(playerRenderComp, data):
|
||||
for _ in data.items():
|
||||
playerRenderComp.AddPlayerRenderMaterial(*_)
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
# coding=utf-8
|
||||
import random
|
||||
|
||||
from ..ToolsConfig import *
|
||||
|
||||
|
||||
def Probability(odds):
|
||||
"""
|
||||
根据输入概率判断一次结果
|
||||
:param odds:概率float
|
||||
:return:bool
|
||||
"""
|
||||
t = odds / 2.0
|
||||
d = random.uniform(t, 1.0 - t)
|
||||
return d - t < random.random() < d + t
|
||||
|
||||
|
||||
class AddProbability(object):
|
||||
"""
|
||||
累加概率算法,当进行到times次时,期望行动概率为100%,但并不代表本次判定为100%
|
||||
"""
|
||||
|
||||
def __init__(self, times):
|
||||
self.runTime = 0
|
||||
self.times = times
|
||||
self.k = 2.0 / times ** 2.0
|
||||
|
||||
def ChanceOnce(self):
|
||||
"""
|
||||
判断一次结果
|
||||
:return:bool
|
||||
"""
|
||||
self.runTime += 1
|
||||
return Probability(self.runTime * self.k)
|
||||
|
||||
|
||||
class AddProbabilityForMcServerTick(AddProbability):
|
||||
"""
|
||||
针对MC系统的特殊算法
|
||||
status:R运行,S中止,F完成
|
||||
"""
|
||||
|
||||
def __init__(self, maxTime, system, func, *args):
|
||||
self.system = system
|
||||
super(AddProbabilityForMcServerTick, self).__init__(int(maxTime * 30.0))
|
||||
self.system.ListenForEvent(MC_SYSTEM_NAMESPACE, MC_SYSTEM_NAME, 'OnScriptTickServer', self, self.McTick)
|
||||
self.func = func
|
||||
self.funcParm = args
|
||||
self.status = 'R'
|
||||
|
||||
def Suspend(self):
|
||||
"""中止"""
|
||||
print "递增概率函数中止:{}".format(self.func.__name__)
|
||||
self.status = 'S'
|
||||
self.system.UnListenForEvent(MC_SYSTEM_NAMESPACE, MC_SYSTEM_NAME, 'OnScriptTickServer', self, self.McTick)
|
||||
|
||||
def Start(self):
|
||||
"""开始"""
|
||||
print "递增概率函数中止后再次开始:{}".format(self.func.__name__)
|
||||
self.status = 'R'
|
||||
self.system.ListenForEvent(MC_SYSTEM_NAMESPACE, MC_SYSTEM_NAME, 'OnScriptTickServer', self, self.McTick)
|
||||
|
||||
def McTick(self):
|
||||
self.ChanceOnce()
|
||||
|
||||
def ChanceOnce(self):
|
||||
"""
|
||||
判断一次结果
|
||||
:return:bool
|
||||
"""
|
||||
self.runTime += 1
|
||||
if Probability(self.runTime * self.k) or self.runTime >= self.times:
|
||||
self.system.UnListenForEvent(MC_SYSTEM_NAMESPACE, MC_SYSTEM_NAME, 'OnScriptTickServer', self, self.McTick)
|
||||
self.func(*self.funcParm)
|
||||
self.status = 'F'
|
||||
return True
|
||||
return False
|
||||
|
||||
def Destroy(self):
|
||||
self.system.UnListenForEvent(MC_SYSTEM_NAMESPACE, MC_SYSTEM_NAME, 'OnScriptTickServer', self, self.McTick)
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
# coding=utf-8
|
||||
import copy
|
||||
|
||||
from ..OtherTools import GetDistance
|
||||
|
||||
routeControls = {} # 所有路径节点
|
||||
|
||||
|
||||
class ConnectChangedState(object):
|
||||
"""
|
||||
当发生连接变化时的状态
|
||||
"""
|
||||
DisConnect = 0b00
|
||||
Connect = 0b01
|
||||
ConvertConnect = 0b00
|
||||
ReallyConnect = 0b10
|
||||
|
||||
|
||||
def GetRouteByPosDim(posDim):
|
||||
# type:(Union[List,Tuple]) -> RouteControl
|
||||
"""
|
||||
通过维度坐标获取加载的路径节点
|
||||
"""
|
||||
return routeControls.get(posDim)
|
||||
|
||||
|
||||
class RouteControl(object):
|
||||
"""
|
||||
路径节点
|
||||
"""
|
||||
|
||||
def __init__(self, posDim):
|
||||
# type:(Tuple[int,int,int,int]) -> None
|
||||
self.posDim = posDim
|
||||
self.connections = [] # 真连接
|
||||
self.convertConnections = [] # 隐性连接,用于记录哪个路径节点连接与此
|
||||
routeControls[posDim] = self
|
||||
|
||||
def HasConnection(self):
|
||||
"""
|
||||
判断是否有方向:自身 -> routeControl的连接
|
||||
"""
|
||||
return self.connections.__len__() > 0
|
||||
|
||||
def ConnectionChanged(self, routeControl, state):
|
||||
"""
|
||||
当连接情况发生改变完成后触发,包括真连接和隐性连接
|
||||
:param routeControl: 将要变化的路径节点
|
||||
:param state: ConnectChangedState不同位的不同state
|
||||
"""
|
||||
pass
|
||||
|
||||
def Destroy(self):
|
||||
"""
|
||||
销毁本路径节点
|
||||
:return:
|
||||
"""
|
||||
for routeControl in copy.copy(self.convertConnections):
|
||||
# 销毁, 不可触发自身连接改变
|
||||
routeControl.DisConnectBefore(self)
|
||||
routeControl.connections.remove(self)
|
||||
self.convertConnections.remove(routeControl)
|
||||
routeControl.ConnectionChanged(self, ConnectChangedState.ReallyConnect | ConnectChangedState.DisConnect)
|
||||
for routeControl in copy.copy(self.connections):
|
||||
# 销毁, 不可触发自身连接改变
|
||||
self.DisConnectBefore(routeControl)
|
||||
self.connections.remove(routeControl)
|
||||
routeControl.convertConnections.remove(self)
|
||||
routeControl.ConnectionChanged(self, ConnectChangedState.ConvertConnect | ConnectChangedState.DisConnect)
|
||||
del routeControls[self.posDim]
|
||||
|
||||
def DisconnectTo(self, routeControl):
|
||||
"""
|
||||
单向取消与路径节点的单向连接, 方向:自身 -> routeControl
|
||||
|
||||
:param routeControl:将要取消的路径节点
|
||||
:return:
|
||||
"""
|
||||
if self.IsConnection(routeControl):
|
||||
self.DisConnectBefore(routeControl)
|
||||
self.connections.remove(routeControl)
|
||||
routeControl.convertConnections.remove(self)
|
||||
self.ConnectionChanged(routeControl, ConnectChangedState.ReallyConnect | ConnectChangedState.DisConnect)
|
||||
routeControl.ConnectionChanged(self, ConnectChangedState.ConvertConnect | ConnectChangedState.DisConnect)
|
||||
|
||||
@staticmethod
|
||||
def DisconnectBy(routeControl):
|
||||
"""
|
||||
单向取消与路径节点的单向连接, 方向: routeControl -> 自身
|
||||
|
||||
:param routeControl:将要取消的路径节点
|
||||
:return:
|
||||
"""
|
||||
routeControl.DisconnectTo(routeControl)
|
||||
|
||||
def InterDisconnect(self, routeControl):
|
||||
"""
|
||||
双向取消与路径节点的连接
|
||||
|
||||
:param routeControl:将要取消的路径节点
|
||||
:return:
|
||||
"""
|
||||
self.DisconnectTo(routeControl)
|
||||
self.DisconnectBy(routeControl)
|
||||
|
||||
def IsConnection(self, routeControl):
|
||||
"""
|
||||
判断路径节点是否与本路径节点连接
|
||||
|
||||
:param routeControl:路径节点
|
||||
:return:
|
||||
"""
|
||||
return routeControl in self.connections
|
||||
|
||||
def Connectable(self, routeControl):
|
||||
"""
|
||||
在尝试与对方进行连接时触发,方向:自身 -> routeControl
|
||||
|
||||
:param routeControl:将要连接的路径节点
|
||||
:return:是否与对方连接
|
||||
"""
|
||||
if abs(GetDistance(self.posDim[:3], routeControl.posDim[:3]) - 1) < 10e-6:
|
||||
return True
|
||||
|
||||
def DisConnectBefore(self, routeControl):
|
||||
"""
|
||||
当断开方向为:routeControl -> 自身 连接之前触发
|
||||
"""
|
||||
pass
|
||||
|
||||
def ConnectBefore(self, routeControl):
|
||||
"""
|
||||
当发生方向为:routeControl -> 自身 连接之前触发
|
||||
"""
|
||||
pass
|
||||
|
||||
def ConnectTo(self, routeControl):
|
||||
"""
|
||||
与另外一个路径节点进行单向连接,方向:自身 -> routeControl
|
||||
如果routeControl不可连接与此, 将无法建立连接
|
||||
"""
|
||||
routeControl.ConnectBefore(self)
|
||||
if self.Connectable(routeControl) and routeControl not in self.connections:
|
||||
self.connections.append(routeControl)
|
||||
routeControl.convertConnections.append(self)
|
||||
self.ConnectionChanged(routeControl, ConnectChangedState.ReallyConnect | ConnectChangedState.Connect)
|
||||
routeControl.ConnectionChanged(self, ConnectChangedState.ConvertConnect | ConnectChangedState.Connect)
|
||||
|
||||
def ConnectBy(self, routeControl):
|
||||
"""
|
||||
与另外一个路径节点进行单向连接,方向:routeControl -> 自身
|
||||
"""
|
||||
routeControl.ConnectTo(self)
|
||||
|
||||
def InterConnect(self, routeControl):
|
||||
"""
|
||||
与另外一个路径节点进行双向连接
|
||||
"""
|
||||
self.ConnectTo(routeControl)
|
||||
self.ConnectBy(routeControl)
|
||||
|
||||
def ConnectBeside(self):
|
||||
"""
|
||||
与周围的可连接的路径节点进行连接
|
||||
"""
|
||||
myPosDim = list(self.posDim)
|
||||
dr = [-1, 1]
|
||||
posDimBeside = [(myPosDim[0] + x, myPosDim[1], myPosDim[2], myPosDim[3]) for x in dr] + \
|
||||
[(myPosDim[0], myPosDim[1] + y, myPosDim[2], myPosDim[3]) for y in dr] + \
|
||||
[(myPosDim[0], myPosDim[1], myPosDim[2] + z, myPosDim[3]) for z in dr]
|
||||
for posDim in posDimBeside:
|
||||
routeControl = routeControls.get(posDim)
|
||||
self.InterConnect(routeControl) if routeControl else 0
|
||||
|
||||
def IsFinal(self, *args, **kwargs):
|
||||
"""
|
||||
是否为终点
|
||||
:param args:判断时可能传入的参数
|
||||
:param kwargs:判断时可能传入的参数
|
||||
:return:
|
||||
"""
|
||||
return self.connections.__len__() == 1
|
||||
|
||||
|
||||
def Finder(routeControl, maxRuntime=10e5):
|
||||
# type:(RouteControl,int)->List
|
||||
"""
|
||||
寻找器,不可跨维度寻找
|
||||
|
||||
:param routeControl:起点路径节点
|
||||
:param maxRuntime:最大运行次数
|
||||
:return:终点集
|
||||
"""
|
||||
destination = [] # 目标集
|
||||
x = {} # 储存已经寻找过的节点
|
||||
runTime = 0 # 寻找次数
|
||||
hand = routeControl.posDim # 运行指针
|
||||
subRoute = [] # 下一节点
|
||||
while runTime < maxRuntime:
|
||||
routeControl = routeControls.get(hand) # type:RouteControl
|
||||
y = x.get(hand[0], {})
|
||||
z = y.get(hand[1], {})
|
||||
# 如果获取错误,或者此节点已被寻找,则尝试下一个分流节点
|
||||
if routeControl is None or z.get(hand[2], False):
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
continue
|
||||
# 储存节点
|
||||
z[hand[2]] = routeControl
|
||||
y[hand[1]] = z
|
||||
x[hand[0]] = y
|
||||
|
||||
if routeControl.IsFinal():
|
||||
destination.append(routeControl)
|
||||
|
||||
for connection in routeControl.connections:
|
||||
posDim = connection.posDim
|
||||
# 判断下一节点是否被储存过
|
||||
if not x.get(posDim[0], {}).get(posDim[1], {}).get(posDim[2], False):
|
||||
subRoute.append(connection.posDim)
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
runTime += 1
|
||||
return destination
|
||||
|
||||
|
||||
def FinderStopWhenFindDestination(routeControl, maxRuntime=10e5):
|
||||
# type:(RouteControl,int)->List
|
||||
"""
|
||||
寻找器,不可跨维度寻找,每个分路遇到终点会停止寻路
|
||||
|
||||
:param routeControl:起点路径节点
|
||||
:param maxRuntime:最大运行次数
|
||||
:return:终点集
|
||||
"""
|
||||
destination = []
|
||||
x = {}
|
||||
runTime = 0
|
||||
hand = routeControl.posDim
|
||||
subRoute = []
|
||||
while runTime < maxRuntime:
|
||||
routeControl = routeControls.get(hand) # type:RouteControl
|
||||
y = x.get(hand[0], {})
|
||||
z = y.get(hand[1], {})
|
||||
if routeControl is None or z.get(hand[2], False):
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
continue
|
||||
z[hand[2]] = True
|
||||
y[hand[1]] = z
|
||||
x[hand[0]] = y
|
||||
if routeControl.IsFinal():
|
||||
destination.append(routeControl)
|
||||
else:
|
||||
for connection in routeControl.connections:
|
||||
posDim = connection.posDim
|
||||
if not x.get(posDim[0], {}).get(posDim[1], {}).get(posDim[2], False):
|
||||
subRoute.append(connection.posDim)
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
runTime += 1
|
||||
return destination
|
||||
|
||||
|
||||
def FinderStopSampleNum(routeControl, sampleNum, maxRuntime=10e5):
|
||||
# type:(RouteControl,int,int)->Dict
|
||||
"""
|
||||
寻找器,不可跨维度寻找,每个分路到达sampleNumber时停止寻路
|
||||
|
||||
:param routeControl:起点路径节点
|
||||
:param maxRuntime:最大运行次数
|
||||
:param sampleNum:距离起点相隔的节点个数
|
||||
:return:结果集无论是否为终点
|
||||
"""
|
||||
destination = {} # 目标集
|
||||
x = {} # 储存已经寻找过的节点
|
||||
runTime = 0 # 寻找次数
|
||||
hand = routeControl.posDim # 运行指针
|
||||
subRoute = [] # 下一节点
|
||||
subRouteNum = {} # 下一节点计数
|
||||
presentNum = 0 # 当前计数
|
||||
while runTime < maxRuntime:
|
||||
routeControl = routeControls.get(hand) # type:RouteControl
|
||||
y = x.get(hand[0], {})
|
||||
z = y.get(hand[1], {})
|
||||
# 如果获取错误,或者此节点已被寻找,再或者此时计数大于sampleNum则尝试下一个分流节点
|
||||
if presentNum >= sampleNum or presentNum == -1 or z.get(hand[2], False) or routeControl is None:
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
continue
|
||||
|
||||
# 储存节点
|
||||
z[hand[2]] = routeControl
|
||||
y[hand[1]] = z
|
||||
x[hand[0]] = y
|
||||
|
||||
if presentNum > 0:
|
||||
destination[presentNum] = destination.get(presentNum, [])
|
||||
destination[presentNum].append(routeControl)
|
||||
|
||||
presentNum += 1
|
||||
|
||||
for connection in routeControl.connections:
|
||||
posDim = connection.posDim
|
||||
# 判断下一节点是否被储存过
|
||||
if not x.get(posDim[0], {}).get(posDim[1], {}).get(posDim[2], False):
|
||||
subRoute.append(connection.posDim)
|
||||
a = subRouteNum.get(posDim[0], {})
|
||||
subRouteNum[posDim[0]] = a
|
||||
|
||||
b = a.get(posDim[1], {})
|
||||
a[posDim[1]] = b
|
||||
|
||||
b[posDim[2]] = presentNum
|
||||
if subRoute.__len__() == 0:
|
||||
break
|
||||
hand = subRoute.pop()
|
||||
presentNum = subRouteNum.get(hand[0], {}).get(hand[1], {}).get(hand[2], False)
|
||||
runTime += 1
|
||||
return destination
|
||||
Whitespace-only changes.
+668
@@ -0,0 +1,668 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from mod.common.utils.mcmath import Matrix
|
||||
|
||||
from config.PlayConfig import PlayConfig
|
||||
from exception.NotClientSystemException import NotClientSystemException
|
||||
from exception.NotPlayConfigException import NotPlayConfigException
|
||||
from utils.SafeEval import SafeEval
|
||||
from .utils.CatmullRom import getCatmullromLerpFromKeyframe
|
||||
|
||||
compFactory = clientApi.GetEngineCompFactory()
|
||||
levelId = clientApi.GetLevelId()
|
||||
|
||||
|
||||
class Camera(object):
|
||||
# 全局摄像机对象(用来判断当前客户端是否有摄像机正在播放)
|
||||
globalCameraObj = None
|
||||
|
||||
def __init__(self, client, animationName, path='modconfigs/spring_camera_animations.json', cameraName='camera',
|
||||
animationDict=None, speed=1.0):
|
||||
"""
|
||||
构造方法
|
||||
|
||||
Args:
|
||||
client (ClientSystem): 客户端系统对象
|
||||
animationName (str): 动画在文件中的名称
|
||||
path (str, optional): 动画文件路径,需要包含 ".json"。默认值 'modconfigs/spring_camera_animations.json'
|
||||
cameraName (str, optional): 动画 JSON 中的摄像机名称。默认值 'camera'
|
||||
animationDict (dict, optional): 动画关键帧字典。默认值 None
|
||||
speed (float, optional): 动画播放速度倍数。默认值 1.0
|
||||
"""
|
||||
if not isinstance(client, clientApi.GetClientSystemCls()):
|
||||
raise NotClientSystemException("传入 Camera 内的 client 不是一个 ClientSystem")
|
||||
|
||||
# region 初始化变量
|
||||
self.client = client
|
||||
self.eval = SafeEval()
|
||||
# 动画总时长
|
||||
self.animationLength = None
|
||||
# 动画旋转关键帧数组
|
||||
self.animationRotationArray = None
|
||||
# 动画位置关键帧数组
|
||||
self.animationPositionArray = None
|
||||
# 动画旋转关键帧数组的索引
|
||||
self.rotationIndex = None
|
||||
# 动画位置关键帧数组的索引
|
||||
self.positionIndex = None
|
||||
# 动画开始时间(单位:秒)
|
||||
self.startAnimationTime = None
|
||||
# 上一个帧的时间(单位:秒)
|
||||
self.preAnimationTime = None
|
||||
# 上一帧的旋转关键帧参数,形如:(0.0, [0, 180, 0], 'catmullrom')
|
||||
self.preAnimationRotArgs = None
|
||||
# 上一帧的位置关键帧参数,形如:(0.0, [0, 180, 0], 'catmullrom')
|
||||
self.preAnimationPosArgs = None
|
||||
# 动画开始前的摄像机旋转参数
|
||||
self.rotationOrigin = None
|
||||
# 动画开始前的摄像机锚点参数
|
||||
self.positionOrigin = None
|
||||
# play 配置对象
|
||||
self.playConfig = PlayConfig()
|
||||
# 是否正在播放摄像机动画
|
||||
self.isPlaying = False
|
||||
# 摄像机接口对象
|
||||
self.cameraCompFactory = compFactory.CreateCamera(levelId)
|
||||
# 绕 y 轴旋转的旋转矩阵
|
||||
self.leftRotYMat = None
|
||||
# 是否覆盖当前的位置和旋转(json 配置)
|
||||
self.overridePreviousAnimation = False
|
||||
# 动画循环状态:播放一次、停止于最后一帧、循环播放
|
||||
self.loop = None
|
||||
# 防止停不住
|
||||
self.playTimer = None
|
||||
# 前一帧设置的摄像机旋转角度(目前仅用于将当前帧设置的旋转角度传递给后续的 GameRenderTickEvent 事件)
|
||||
self.beforeRot = None
|
||||
# 是否正在暂停
|
||||
self.isPausing = False
|
||||
# 动画暂停开始时间
|
||||
self.pauseStartTime = 0.0
|
||||
# endregion
|
||||
|
||||
# 动画关键帧字典
|
||||
if not animationDict:
|
||||
animationDict = clientApi.GetModConfigJson(path)['animations'][animationName]
|
||||
self.animationLength = animationDict.get('animation_length')
|
||||
if not self.animationLength:
|
||||
# 无时间的动画,默认动画时长 24 小时
|
||||
self.animationLength = 86400.0
|
||||
|
||||
# 应用速度倍数
|
||||
speed = 1.0 / speed
|
||||
self.animationLength *= speed
|
||||
|
||||
self.overridePreviousAnimation = animationDict['override_previous_animation'] if animationDict.__contains__(
|
||||
'override_previous_animation') else False
|
||||
self.loop = animationDict['loop'] if animationDict.__contains__('loop') else False
|
||||
|
||||
if animationDict['bones'][cameraName].__contains__('rotation'):
|
||||
animationRotationDict = animationDict['bones'][cameraName]['rotation']
|
||||
# 解析只有单个关键帧的动画
|
||||
if isinstance(animationRotationDict, list):
|
||||
animationRotationDict = {'0.0': animationRotationDict}
|
||||
# 动画旋转关键帧数组
|
||||
self.animationRotationArray = sorted(animationRotationDict.items(), key=lambda item: float(item[0]))
|
||||
self.animationRotationArray = [
|
||||
(float(item[0]) * speed,
|
||||
item[1]['post'] if isinstance(item[1], dict) else item[1],
|
||||
item[1].get('lerp_mode') if isinstance(item[1], dict) else None)
|
||||
for item in self.animationRotationArray
|
||||
]
|
||||
self._parseRotKeyframe(self.animationRotationArray)
|
||||
|
||||
if animationDict['bones'][cameraName].__contains__('position'):
|
||||
animationPositionDict = animationDict['bones'][cameraName]['position']
|
||||
# 解析只有单个关键帧的动画
|
||||
if isinstance(animationPositionDict, list):
|
||||
animationPositionDict = {'0.0': animationPositionDict}
|
||||
# 动画位置关键帧数组
|
||||
self.animationPositionArray = sorted(animationPositionDict.items(), key=lambda item: float(item[0]))
|
||||
self.animationPositionArray = [
|
||||
(float(item[0]) * speed,
|
||||
item[1]['post'] if isinstance(item[1], dict) else item[1],
|
||||
item[1].get('lerp_mode') if isinstance(item[1], dict) else None)
|
||||
for item in self.animationPositionArray
|
||||
]
|
||||
self._parsePosKeyframe(self.animationPositionArray)
|
||||
|
||||
def _parseRotKeyframe(self, animationArray):
|
||||
"""
|
||||
转换旋转关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 旋转关键帧数组
|
||||
"""
|
||||
rot = compFactory.CreateCamera(levelId).GetCameraRotation()
|
||||
self._parseKeyframe(animationArray, rot)
|
||||
|
||||
def _parsePosKeyframe(self, animationArray):
|
||||
"""
|
||||
转换位置关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 位置关键帧数组
|
||||
"""
|
||||
pos = compFactory.CreatePos(clientApi.GetLocalPlayerId()).GetFootPos()
|
||||
self._parseKeyframe(animationArray, (pos[0] * 16.0, pos[1] * 16.0, pos[2] * 16.0))
|
||||
|
||||
def _parseKeyframe(self, animationArray, replaceThis):
|
||||
"""
|
||||
转换关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 关键帧数组
|
||||
replaceThis (tuple): 替换 this 的元组
|
||||
"""
|
||||
for i in xrange(0, len(animationArray)):
|
||||
for j in xrange(0, len(animationArray[i][1])):
|
||||
if not isinstance(animationArray[i][1][j], str):
|
||||
continue
|
||||
if 'this' in animationArray[i][1][j]:
|
||||
animationArray[i][1][j] = animationArray[i][1][j].replace('this', format(replaceThis[j], '.16f'))
|
||||
animationArray[i][1][j] = self.eval.evaluate(animationArray[i][1][j])
|
||||
elif 'math.random' in animationArray[i][1][j]:
|
||||
match = re.search(r'(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)', animationArray[i][1][j])
|
||||
if match:
|
||||
animationArray[i][1][j] = random.uniform(float(match.group(1)), float(match.group(3)))
|
||||
|
||||
def _indexOutEnd(self):
|
||||
"""
|
||||
防止下标越界
|
||||
"""
|
||||
# 如果数组下标越界,设置下标为最后一个元素
|
||||
if self.animationRotationArray and self.rotationIndex >= len(self.animationRotationArray):
|
||||
self.rotationIndex = len(self.animationRotationArray) - 1
|
||||
if self.animationPositionArray and self.positionIndex >= len(self.animationPositionArray):
|
||||
self.positionIndex = len(self.animationPositionArray) - 1
|
||||
|
||||
def _leftRotationMatrixY(self, angleDegrees):
|
||||
"""
|
||||
生成绕 Y 轴旋转指定角度的 3x3 旋转矩阵(左手坐标系)
|
||||
|
||||
Args:
|
||||
angleDegrees (float): 旋转角度
|
||||
|
||||
Returns:
|
||||
Matrix: 3x3 旋转矩阵
|
||||
"""
|
||||
# 将角度转换为弧度
|
||||
angleRad = math.radians(angleDegrees)
|
||||
|
||||
# 计算余弦和正弦值
|
||||
cosAngle = math.cos(angleRad)
|
||||
sinAngle = math.sin(angleRad)
|
||||
|
||||
# 构建绕 Y 轴旋转矩阵(左手坐标系)
|
||||
rotationMatrix = Matrix.Create([
|
||||
[cosAngle, 0, -sinAngle],
|
||||
[0, 1, 0, ],
|
||||
[sinAngle, 0, cosAngle]
|
||||
])
|
||||
|
||||
return rotationMatrix
|
||||
|
||||
def _leftRotationMatrixX(self, angleDegrees):
|
||||
"""
|
||||
生成绕 X 轴旋转指定角度的 3x3 旋转矩阵(左手坐标系)
|
||||
|
||||
Args:
|
||||
angleDegrees (float): 旋转角度
|
||||
|
||||
Returns:
|
||||
Matrix: 3x3 旋转矩阵
|
||||
"""
|
||||
# 将角度转换为弧度
|
||||
angleRad = math.radians(angleDegrees)
|
||||
|
||||
# 计算余弦和正弦值
|
||||
cosAngle = math.cos(angleRad)
|
||||
sinAngle = math.sin(angleRad)
|
||||
|
||||
# 构建绕 X 轴旋转矩阵(左手坐标系)
|
||||
rotationMatrix = Matrix.Create([
|
||||
[1, 0, 0],
|
||||
[0, cosAngle, sinAngle],
|
||||
[0, -sinAngle, cosAngle]
|
||||
])
|
||||
|
||||
return rotationMatrix
|
||||
|
||||
def _getNowTime(self):
|
||||
"""
|
||||
获取当前时间
|
||||
"""
|
||||
if self.isPausing and self.preAnimationTime is not None:
|
||||
return self.preAnimationTime
|
||||
return time.time()
|
||||
|
||||
def playFirstPerson(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(允许拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, True, True, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(允许拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, True, False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableDrag(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(禁止拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, False, True, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableDragWithMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(禁止拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, False, False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPerson(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(允许拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, True, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(允许拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, True, False, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableDrag(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(禁止拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableDragWithMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(禁止拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, False, False, playAfterFunc=playAfterFunc)
|
||||
|
||||
def play(self, isFirstPerson=True, isAllowDrag=True, isAllowMove=True, isPosFollowXRot=True, isPosFollowYRot=True,
|
||||
isZRotForceReturn=True, isZRotStartOrigin=False,
|
||||
isDelayOneFrame=False, isLockPerspective=False, tickPosFunc=None, tickRotFunc=None, playAfterFunc=None):
|
||||
"""
|
||||
播放摄像机动画
|
||||
|
||||
Args:
|
||||
isFirstPerson (bool, optional): 是否是第一人称动画。如果是,那么 Y 轴旋转会减 180 度。默认值 False
|
||||
isAllowDrag (bool, optional): 是否允许玩家拖拽视角。默认值 False
|
||||
isAllowMove (bool, optional): 是否允许玩家移动。默认值 False
|
||||
isPosFollowXRot (bool, optional): 摄像机空间坐标是否受 X 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isPosFollowYRot (bool, optional): 摄像机空间坐标是否受 Y 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isZRotForceReturn (bool, optional): 是否在动画播放完成后强制归零 Z 轴的旋转值。默认值 True
|
||||
isZRotStartOrigin (bool, optional): 是否将动画播放前的 Z 轴旋转值作为初始的 Z 轴旋转值。置为 False 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常(json 配置 override_previous_animation 值为 True 时无效)。默认值 False
|
||||
isDelayOneFrame (bool, optional): 是否延迟一帧再播放摄像机动画。默认值 True
|
||||
isLockPerspective (bool, optional): 是否锁定并强制切换玩家视角。默认值 True
|
||||
tickPosFunc (function, optional): 摄像机动画播放时,在每次设置摄像机位置前执行的函数。默认值 None
|
||||
tickRotFunc (function, optional): 摄像机动画播放时,在每次设置摄像机旋转前执行的函数。默认值 None
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.playWithConfig(
|
||||
PlayConfig(isFirstPerson, isAllowDrag, isAllowMove, isPosFollowXRot, isPosFollowYRot, isZRotForceReturn,
|
||||
isZRotStartOrigin, isDelayOneFrame, isLockPerspective,
|
||||
tickPosFunc, tickRotFunc, playAfterFunc))
|
||||
|
||||
def playWithConfig(self, config):
|
||||
"""
|
||||
使用播放配置对象播放摄像机动画
|
||||
|
||||
Args:
|
||||
config (PlayConfig): 播放配置对象
|
||||
"""
|
||||
if not isinstance(config, PlayConfig):
|
||||
raise NotPlayConfigException("传入 playWithConfig 内的 config 不是一个 PlayConfig")
|
||||
|
||||
if Camera.globalCameraObj:
|
||||
# 强制停止正在播放的摄像机动画(全局)
|
||||
Camera.globalCameraObj.stop(True)
|
||||
|
||||
# 开始监听
|
||||
from .CameraListen import _enable
|
||||
if not _enable:
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.GameRenderTickEvent, 10)
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"HealthChangeClientEvent", self, self.HealthChangeClientEvent)
|
||||
|
||||
def realPlay():
|
||||
# 初始化相关变量
|
||||
self.playConfig = config
|
||||
self.rotationIndex = 0
|
||||
self.positionIndex = 0
|
||||
operationComp = compFactory.CreateOperation(levelId)
|
||||
# 不允许玩家移动
|
||||
if not self.playConfig.isAllowMove:
|
||||
operationComp.SetCanMove(False)
|
||||
operationComp.SetCanJump(False)
|
||||
operationComp.SetCanInair(False)
|
||||
# 不允许玩家拖拽
|
||||
if not self.playConfig.isAllowDrag:
|
||||
operationComp.SetCanDrag(False)
|
||||
# 不允许玩家切换视角
|
||||
if self.playConfig.isLockPerspective:
|
||||
operationComp.SetCanPerspective(False)
|
||||
# 第三人称
|
||||
if not self.playConfig.isFirstPerson:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(1)
|
||||
self.cameraCompFactory.SetCameraDistanceFixed(True)
|
||||
self.cameraCompFactory.DepartCamera()
|
||||
self.cameraCompFactory.SetCameraOffset((0, 0.01, -4))
|
||||
else:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(0)
|
||||
if self.animationRotationArray:
|
||||
self.preAnimationRotArgs = (float(self.animationRotationArray[self.rotationIndex][0]),
|
||||
self.animationRotationArray[self.rotationIndex][1])
|
||||
if self.animationPositionArray:
|
||||
self.preAnimationPosArgs = (float(self.animationPositionArray[self.positionIndex][0]),
|
||||
self.animationPositionArray[self.positionIndex][1])
|
||||
self.preAnimationTime = self.startAnimationTime = time.time()
|
||||
# 是否覆盖
|
||||
if self.overridePreviousAnimation:
|
||||
self.rotationOrigin = (0, 0, 0)
|
||||
self.positionOrigin = (0, 0, 0)
|
||||
else:
|
||||
self.rotationOrigin = self.cameraCompFactory.GetCameraRotation()
|
||||
self.positionOrigin = self.cameraCompFactory.GetCameraAnchor()
|
||||
# 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常
|
||||
if not self.playConfig.isZRotStartOrigin:
|
||||
self.rotationOrigin = (self.rotationOrigin[0], self.rotationOrigin[1], 0.0)
|
||||
# 绕 y 轴的旋转矩阵
|
||||
self.leftRotYMat = self._leftRotationMatrixY(self.rotationOrigin[1])
|
||||
# 一定要在最后给 isPlaying 赋值
|
||||
Camera.globalCameraObj = self
|
||||
self.isPlaying = True
|
||||
|
||||
if config.isDelayOneFrame:
|
||||
# 延时一帧再播放(为了确保上一个动画完全停止,使得下一次获取初始数据时不受上次动画影响)
|
||||
compGame = compFactory.CreateGame(levelId)
|
||||
self.playTimer = compGame.AddTimer(0.0, realPlay)
|
||||
else:
|
||||
realPlay()
|
||||
|
||||
def destroyListen(self):
|
||||
"""
|
||||
销毁内置监听
|
||||
"""
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.GameRenderTickEvent)
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"HealthChangeClientEvent", self, self.HealthChangeClientEvent)
|
||||
|
||||
def stop(self, isStopLoop=False):
|
||||
"""
|
||||
停止摄像机动画
|
||||
|
||||
Args:
|
||||
isStopLoop (bool, optional): 是否停止循环动画(仅对 json 中 loop 配置为 True 的动画有效)。默认值 False
|
||||
"""
|
||||
if self.playTimer: compFactory.CreateGame(levelId).CancelTimer(self.playTimer)
|
||||
|
||||
# 取消监听
|
||||
from .CameraListen import _enable
|
||||
if not _enable:
|
||||
self.destroyListen()
|
||||
|
||||
self.isPlaying = False
|
||||
self.isPausing = False
|
||||
Camera.globalCameraObj = None
|
||||
|
||||
# region 摄像机还原
|
||||
# 第三人称
|
||||
if not self.playConfig.isFirstPerson:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(0)
|
||||
self.cameraCompFactory.SetCameraDistanceFixed(False)
|
||||
self.cameraCompFactory.UnDepartCamera()
|
||||
self.cameraCompFactory.SetCameraOffset((0, 0, 0))
|
||||
|
||||
# 根据 loop 的值进行设置
|
||||
nowCameraRotation = self.rotationOrigin
|
||||
nowCameraPosition = self.positionOrigin
|
||||
if self.loop == 'hold_on_last_frame' or self.loop == True or self.playConfig.isAllowDrag:
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
if not self.playConfig.isAllowDrag: nowCameraPosition = self.cameraCompFactory.GetCameraAnchor()
|
||||
|
||||
# 是否强制回正 Z 轴
|
||||
if self.playConfig.isZRotForceReturn:
|
||||
self.cameraCompFactory.SetCameraRotation((nowCameraRotation[0], nowCameraRotation[1], 0))
|
||||
else:
|
||||
self.cameraCompFactory.SetCameraRotation(nowCameraRotation)
|
||||
self.cameraCompFactory.SetCameraAnchor(nowCameraPosition)
|
||||
# endregion
|
||||
|
||||
# 判断是否循环播放
|
||||
if self.loop == True and not isStopLoop:
|
||||
self.playWithConfig(self.playConfig)
|
||||
return
|
||||
|
||||
operationComp = compFactory.CreateOperation(levelId)
|
||||
# 恢复玩家移动
|
||||
operationComp.SetCanMove(True)
|
||||
operationComp.SetCanJump(True)
|
||||
operationComp.SetCanInair(True)
|
||||
operationComp.SetCanDrag(True)
|
||||
# 允许玩家切换视角
|
||||
operationComp.SetCanPerspective(True)
|
||||
|
||||
# 动画停止播放后执行传入的回调函数
|
||||
if (self.loop == True and isStopLoop) or (self.loop != True):
|
||||
if callable(self.playConfig.playAfterFunc):
|
||||
self.playConfig.playAfterFunc()
|
||||
|
||||
def pause(self, resumeTime=0):
|
||||
# type: (float) -> None
|
||||
"""
|
||||
暂停摄像机动画
|
||||
"""
|
||||
if self.isPausing:
|
||||
return
|
||||
self.isPausing = True
|
||||
self.pauseStartTime = time.time()
|
||||
|
||||
# 多少秒后恢复动画播放
|
||||
if resumeTime > 0:
|
||||
compFactory.CreateGame(levelId).AddTimer(resumeTime, self.resume)
|
||||
|
||||
def resume(self):
|
||||
"""
|
||||
继续播放摄像机动画
|
||||
"""
|
||||
if not self.isPausing:
|
||||
return
|
||||
self.isPausing = False
|
||||
pauseTime = time.time() - self.pauseStartTime
|
||||
self.startAnimationTime += pauseTime
|
||||
self.preAnimationTime += pauseTime
|
||||
|
||||
def GameRenderTickEvent(self, event):
|
||||
"""
|
||||
每 Tick 触发
|
||||
"""
|
||||
if not self.isPlaying:
|
||||
return
|
||||
nowAnimationTime = self._getNowTime()
|
||||
startToNowTime = nowAnimationTime - self.startAnimationTime # 当前时间距离动画开始的时间
|
||||
preToNowTime = nowAnimationTime - self.preAnimationTime # 当前时间距离上一帧的时间
|
||||
self.preAnimationTime = nowAnimationTime # 更新上一帧的时间为当前时间,为下一次计算做准备
|
||||
|
||||
# 如果自动画开始以来经过的时间,大于动画总时长,则动画结束
|
||||
if startToNowTime > self.animationLength:
|
||||
self.stop()
|
||||
return
|
||||
|
||||
self._indexOutEnd()
|
||||
# 如果自动画开始以来经过的时间,大于当前关键帧时间,说明关键帧索引应该增加
|
||||
while self.animationRotationArray and self.rotationIndex < len(
|
||||
self.animationRotationArray) and startToNowTime > float(
|
||||
self.animationRotationArray[self.rotationIndex][0]):
|
||||
# 1.0.3 修复上个关键帧没执行到结尾就计算下个关键帧所带来的问题
|
||||
self.preAnimationRotArgs = (self.preAnimationRotArgs[0], self.animationRotationArray[self.rotationIndex][1])
|
||||
self.rotationIndex += 1
|
||||
while self.animationPositionArray and self.positionIndex < len(
|
||||
self.animationPositionArray) and startToNowTime > float(
|
||||
self.animationPositionArray[self.positionIndex][0]):
|
||||
# 1.0.3 修复上个关键帧没执行到结尾就计算下个关键帧所带来的问题
|
||||
self.preAnimationPosArgs = (self.preAnimationPosArgs[0], self.animationPositionArray[self.positionIndex][1])
|
||||
self.positionIndex += 1
|
||||
self._indexOutEnd()
|
||||
|
||||
# 计算位置
|
||||
if self.animationPositionArray:
|
||||
keyframePosTime = float(self.animationPositionArray[self.positionIndex][0]) - self.preAnimationPosArgs[
|
||||
0] # 位置关键帧下个关键帧与上个关键帧之间的时间
|
||||
# 只有时间间隔大于 0,才计算当前这一刻的值
|
||||
if keyframePosTime > 0.0:
|
||||
positionX = 0
|
||||
positionY = 0
|
||||
positionZ = 0
|
||||
if self.animationPositionArray[self.positionIndex][2] == 'catmullrom':
|
||||
positionX = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 0)
|
||||
positionY = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 1)
|
||||
positionZ = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 2)
|
||||
else:
|
||||
positionX = ((float(self.animationPositionArray[self.positionIndex][1][0]) -
|
||||
self.preAnimationPosArgs[1][0]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][0]
|
||||
positionY = ((float(self.animationPositionArray[self.positionIndex][1][1]) -
|
||||
self.preAnimationPosArgs[1][1]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][1]
|
||||
positionZ = ((float(self.animationPositionArray[self.positionIndex][1][2]) -
|
||||
self.preAnimationPosArgs[1][2]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][2]
|
||||
pos = (
|
||||
(1 if self.playConfig.isFirstPerson else -1) * positionX / 16.0,
|
||||
positionY / 16.0 - 1.62,
|
||||
(-1 if self.playConfig.isFirstPerson else 1) * positionZ / 16.0
|
||||
)
|
||||
posMat = Matrix.Create([
|
||||
[pos[0]],
|
||||
[pos[1]],
|
||||
[pos[2]]
|
||||
])
|
||||
if self.playConfig.isFirstPerson:
|
||||
# 第一人称
|
||||
rotMax = Matrix.CreateEye(3)
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
deltX = (nowCameraRotation[0] - self.preAnimationRotArgs[1][0])
|
||||
deltY = (nowCameraRotation[1] - self.preAnimationRotArgs[1][1])
|
||||
# 注意矩阵相乘顺序
|
||||
if self.playConfig.isPosFollowXRot:
|
||||
rotMax = self._leftRotationMatrixX(deltX)
|
||||
if self.playConfig.isPosFollowYRot:
|
||||
rotMax = self._leftRotationMatrixY(deltY - 180) * rotMax
|
||||
posMat = rotMax * posMat
|
||||
else:
|
||||
# 第三人称位置只受初始绕 y 轴旋转的影响
|
||||
posMat = self.leftRotYMat * posMat
|
||||
pos = (
|
||||
posMat[0, 0] + self.positionOrigin[0],
|
||||
posMat[1, 0] + self.positionOrigin[1],
|
||||
posMat[2, 0] + self.positionOrigin[2]
|
||||
)
|
||||
# 是否不设置
|
||||
isNotSet = False
|
||||
if callable(self.playConfig.tickPosFunc):
|
||||
isNotSet = self.playConfig.tickPosFunc(startToNowTime, pos)
|
||||
if not isNotSet:
|
||||
self.cameraCompFactory.SetCameraAnchor(pos)
|
||||
self.preAnimationPosArgs = (startToNowTime, [positionX, positionY, positionZ])
|
||||
|
||||
# 计算旋转
|
||||
if self.animationRotationArray:
|
||||
keyframeRotTime = float(self.animationRotationArray[self.rotationIndex][0]) - self.preAnimationRotArgs[
|
||||
0] # 旋转关键帧下个关键帧与上个关键帧之间的时间
|
||||
# 只有时间间隔大于 0,才计算当前这一刻的值
|
||||
if keyframeRotTime > 0.0:
|
||||
rotationX = 0
|
||||
rotationY = 0
|
||||
rotationZ = 0
|
||||
if self.animationRotationArray[self.rotationIndex][2] == 'catmullrom':
|
||||
rotationX = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 0)
|
||||
rotationY = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 1)
|
||||
rotationZ = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 2)
|
||||
else:
|
||||
rotationX = ((float(self.animationRotationArray[self.rotationIndex][1][0]) -
|
||||
self.preAnimationRotArgs[1][0]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][0]
|
||||
rotationY = ((float(self.animationRotationArray[self.rotationIndex][1][1]) -
|
||||
self.preAnimationRotArgs[1][1]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][1]
|
||||
rotationZ = ((float(self.animationRotationArray[self.rotationIndex][1][2]) -
|
||||
self.preAnimationRotArgs[1][2]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][2]
|
||||
deltX = self.rotationOrigin[0] if self.playConfig.isFirstPerson else 0
|
||||
deltY = self.rotationOrigin[1] - (180 if self.playConfig.isFirstPerson else 0)
|
||||
if self.playConfig.isAllowDrag:
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
deltX = (nowCameraRotation[0] - self.preAnimationRotArgs[1][0])
|
||||
deltY = (nowCameraRotation[1] - self.preAnimationRotArgs[1][1])
|
||||
deltX += rotationX
|
||||
deltY += rotationY
|
||||
rot = (
|
||||
deltX % (-360 if deltX < 0 else 360),
|
||||
deltY % (-360 if deltY < 0 else 360),
|
||||
# 军械库版本,因为摄像机动画直接使用 Java 版动画,所以 y 轴没有旋转 180 度,因此 z 轴旋转从 - 改为 +,以匹配 Blockbench 里的效果
|
||||
self.rotationOrigin[2] + rotationZ
|
||||
)
|
||||
# 是否不设置
|
||||
isNotSet = False
|
||||
if callable(self.playConfig.tickRotFunc):
|
||||
isNotSet = self.playConfig.tickRotFunc(startToNowTime, rot)
|
||||
if not isNotSet:
|
||||
self.cameraCompFactory.SetCameraRotation(rot)
|
||||
self.beforeRot = rot
|
||||
self.preAnimationRotArgs = (
|
||||
startToNowTime, [rotationX, rotationY, rotationZ]) # 更新上一帧的参数为当前参数,为下一次计算做准备
|
||||
|
||||
def HealthChangeClientEvent(self, event):
|
||||
"""
|
||||
生物生命值发生变化时触发
|
||||
"""
|
||||
# 自己死亡时,停止当前摄像机动画
|
||||
playerType = clientApi.GetMinecraftEnum().EntityType.Player
|
||||
if event['entityId'] == clientApi.GetLocalPlayerId() and compFactory.CreateEngineType(
|
||||
event['entityId']).GetEngineType() & playerType == playerType and event['to'] <= 0.0:
|
||||
self.stop(True)
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
from .Camera import Camera, compFactory
|
||||
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
class CameraListen(object):
|
||||
"""
|
||||
摄像机监听类,创建此类的对象,以手动控制摄像机的监听与取消监听
|
||||
"""
|
||||
|
||||
cameraListenObj = None # type: CameraListen
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GameRenderTickEvent(self, event):
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.GameRenderTickEvent(event)
|
||||
|
||||
def HealthChangeClientEvent(self, event):
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.HealthChangeClientEvent(event)
|
||||
|
||||
|
||||
# 单个实例
|
||||
CameraListen.cameraListenObj = CameraListen()
|
||||
|
||||
# 取消监听定时器
|
||||
_timer = None
|
||||
|
||||
# 是否开启了内置监听
|
||||
_enable = False
|
||||
|
||||
|
||||
def startListen(client):
|
||||
"""
|
||||
开始监听
|
||||
"""
|
||||
global _enable
|
||||
cameraListenObj = CameraListen.cameraListenObj
|
||||
|
||||
# 销毁自身已有监听
|
||||
stopListen(client)
|
||||
|
||||
# 销毁摄像机对象内置监听
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.destroyListen()
|
||||
|
||||
_enable = True
|
||||
client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent",
|
||||
cameraListenObj, cameraListenObj.GameRenderTickEvent, 10)
|
||||
client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "HealthChangeClientEvent",
|
||||
cameraListenObj, cameraListenObj.HealthChangeClientEvent)
|
||||
|
||||
|
||||
def stopListen(client, delayTime=0):
|
||||
"""
|
||||
取消监听
|
||||
"""
|
||||
global _timer
|
||||
cameraListenObj = CameraListen.cameraListenObj
|
||||
comp = compFactory.CreateGame(clientApi.GetLevelId())
|
||||
|
||||
def _scheduleCancel():
|
||||
global _timer, _enable
|
||||
|
||||
# 如果取消监听时,发现还存在未停止的摄像机动画,不取消监听,再次创建一个定时器
|
||||
if Camera.globalCameraObj and delayTime > 0:
|
||||
if _timer:
|
||||
_timer = comp.CancelTimer(_timer)
|
||||
_timer = comp.AddTimer(delayTime, _scheduleCancel)
|
||||
return
|
||||
|
||||
_enable = False
|
||||
client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent",
|
||||
cameraListenObj, cameraListenObj.GameRenderTickEvent)
|
||||
client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"HealthChangeClientEvent", cameraListenObj,
|
||||
cameraListenObj.HealthChangeClientEvent)
|
||||
|
||||
if delayTime > 0:
|
||||
_timer = comp.AddTimer(delayTime, _scheduleCancel)
|
||||
else:
|
||||
if _timer:
|
||||
_timer = comp.CancelTimer(_timer)
|
||||
_scheduleCancel()
|
||||
|
||||
|
||||
def stopListenKeepLastAnim(client):
|
||||
"""
|
||||
取消监听,并且自动保留取消监听前最后一个摄像机动画的完整播放
|
||||
"""
|
||||
if Camera.globalCameraObj and Camera.globalCameraObj.animationLength is not None and Camera.globalCameraObj.startAnimationTime is not None:
|
||||
t = Camera.globalCameraObj.animationLength - (time.time() - Camera.globalCameraObj.startAnimationTime)
|
||||
stopListen(client, t)
|
||||
else:
|
||||
stopListen(client)
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
import time
|
||||
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
from .Camera import Camera, levelId, compFactory
|
||||
from .exception.NotClientSystemException import NotClientSystemException
|
||||
from .utils.CatmullRom import getCatmullromLerpFromKeyframe
|
||||
|
||||
|
||||
class Interpolation(object):
|
||||
# 全局对象
|
||||
globalObj = None
|
||||
|
||||
def __init__(self, client, keyframes, isReturn=False):
|
||||
if not isinstance(client, clientApi.GetClientSystemCls()):
|
||||
raise NotClientSystemException("传入 Interpolation 内的 client 不是一个 ClientSystem")
|
||||
|
||||
self.client = client
|
||||
# 开始时间
|
||||
self.startTime = time.time()
|
||||
# 更新时间(再次调用 setInterpolation 时的时间)
|
||||
self.updateTime = self.startTime
|
||||
# 关键帧数组
|
||||
self.keyframes = keyframes
|
||||
# 关键帧数组索引
|
||||
self.index = 0
|
||||
# 前一帧索引,用于关键帧循环
|
||||
self.preIndex = 0
|
||||
# 前一帧计算出的旋转插值
|
||||
self.preRot = (0, 0)
|
||||
# 用于回正插值的缓存
|
||||
self.tempRot = [0, 0, 0]
|
||||
# 当前是否是回正插值
|
||||
self.isReturn = isReturn
|
||||
# 前一帧的摄像机角度
|
||||
self.beforeRot = None
|
||||
# 是否取消回正插值
|
||||
self.cancelReturn = False
|
||||
# 摄像机接口对象
|
||||
self.cameraComponentFactory = compFactory.CreateCamera(levelId)
|
||||
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.onGameRenderTick, 0)
|
||||
|
||||
def onGameRenderTick(self, event):
|
||||
"""
|
||||
每 Tick 触发
|
||||
"""
|
||||
nowTime = time.time()
|
||||
# 回正超时后自动停止
|
||||
if self.isReturn and nowTime - self.startTime > self.keyframes[-1][0]:
|
||||
self.stop()
|
||||
|
||||
# 当发现关键帧循环时,清空特定变量
|
||||
if self.index < self.preIndex:
|
||||
self.preRot = (0, 0)
|
||||
|
||||
startToNowTime = min(
|
||||
nowTime - self.updateTime + self.keyframes[min(max(self.index - 1, 0), len(self.keyframes) - 1)][0],
|
||||
self.keyframes[self.index][0]
|
||||
)
|
||||
rotX = getCatmullromLerpFromKeyframe(self.keyframes, self.index, startToNowTime, 0)
|
||||
rotY = getCatmullromLerpFromKeyframe(self.keyframes, self.index, startToNowTime, 1)
|
||||
nowRot = self.cameraComponentFactory.GetCameraRotation()
|
||||
|
||||
# 比较摄像机前后帧是否有较大的偏差,如果有,认为是玩家拖动屏幕造成的,取消回正
|
||||
if self.beforeRot:
|
||||
tolerance = 0.3
|
||||
# y 轴角度是 -180 到 180 的循环,所以在计算角度差时要特殊处理
|
||||
y = abs(nowRot[1] - self.beforeRot[1])
|
||||
if abs(nowRot[0] - self.beforeRot[0]) > tolerance or min(y, 360 - y) > tolerance:
|
||||
self.cancelReturn = True
|
||||
|
||||
# 混合之前的事件中设置的摄像机旋转
|
||||
beforeEventRot = (0, 0, 0)
|
||||
if Camera.globalCameraObj and Camera.globalCameraObj.beforeRot:
|
||||
beforeEventRot = Camera.globalCameraObj.beforeRot
|
||||
|
||||
tempRot = (
|
||||
rotX - self.preRot[0],
|
||||
rotY - self.preRot[1],
|
||||
0
|
||||
)
|
||||
deltX = nowRot[0] + tempRot[0]
|
||||
deltY = nowRot[1] + tempRot[1]
|
||||
rot = (
|
||||
deltX % (-360 if deltX < 0 else 360),
|
||||
deltY % (-360 if deltY < 0 else 360),
|
||||
tempRot[2] + beforeEventRot[2]
|
||||
)
|
||||
self.cameraComponentFactory.SetCameraRotation(rot)
|
||||
self.beforeRot = rot
|
||||
self.preRot = (rotX, rotY)
|
||||
self.preIndex = self.index
|
||||
self.tempRot[0] -= tempRot[0]
|
||||
self.tempRot[1] -= tempRot[1]
|
||||
self.tempRot[2] -= tempRot[2]
|
||||
|
||||
def stop(self):
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.onGameRenderTick)
|
||||
Interpolation.globalObj = None
|
||||
if not self.isReturn and not self.cancelReturn:
|
||||
# 启动回正
|
||||
endTime = math.sqrt(
|
||||
self.tempRot[0] * self.tempRot[0] + self.tempRot[1] * self.tempRot[1] + self.tempRot[2] * self.tempRot[
|
||||
2])
|
||||
endTime *= 0.3
|
||||
keyframes = [
|
||||
(0.0, [0, 0, 0]),
|
||||
(endTime, self.tempRot)
|
||||
]
|
||||
setInterpolation(self.client, keyframes, 1, isReturn=True)
|
||||
|
||||
|
||||
def setInterpolation(client, keyframes, index, speed=10.0, isReturn=False):
|
||||
"""
|
||||
设置插值
|
||||
|
||||
Args:
|
||||
client (ClientSystem): 客户端对象
|
||||
keyframes (list): 关键帧数组,格式:[(0.0, [1.0, 2.5, 3.3]),(1.0, [-3.0, 2.5, 3.3])]
|
||||
index (int): 关键帧数组索引
|
||||
speed (float, optional): 速度。默认值 10.0
|
||||
isReturn (bool, optional): 是否是回正插值。默认值 False
|
||||
"""
|
||||
if not Interpolation.globalObj:
|
||||
keyframes = [(t / speed, i) for t, i in keyframes]
|
||||
Interpolation.globalObj = Interpolation(client, keyframes, isReturn)
|
||||
elif Interpolation.globalObj.isReturn:
|
||||
# 正在回正中,强制停止
|
||||
Interpolation.globalObj.stop()
|
||||
keyframes = [(t / speed, i) for t, i in keyframes]
|
||||
Interpolation.globalObj = Interpolation(client, keyframes, isReturn)
|
||||
Interpolation.globalObj.updateTime = time.time()
|
||||
Interpolation.globalObj.index = index
|
||||
|
||||
|
||||
def stopInterpolation():
|
||||
"""
|
||||
停止插值
|
||||
"""
|
||||
if Interpolation.globalObj:
|
||||
Interpolation.globalObj.stop()
|
||||
Whitespace-only changes.
+36
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class PlayConfig(object):
|
||||
|
||||
def __init__(self, isFirstPerson=False, isAllowDrag=False, isAllowMove=False, isPosFollowXRot=True,
|
||||
isPosFollowYRot=True, isZRotForceReturn=True, isZRotStartOrigin=False, isDelayOneFrame=True,
|
||||
isLockPerspective=True, tickPosFunc=None, tickRotFunc=None, playAfterFunc=None):
|
||||
"""
|
||||
play 方法的参数对象
|
||||
|
||||
Args:
|
||||
isFirstPerson (bool, optional): 是否是第一人称动画。如果是,那么 Y 轴旋转会减 180 度。默认值 False
|
||||
isAllowDrag (bool, optional): 是否允许玩家拖拽视角。默认值 False
|
||||
isAllowMove (bool, optional): 是否允许玩家移动。默认值 False
|
||||
isPosFollowXRot (bool, optional): 摄像机空间位置坐标是否受 X 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isPosFollowYRot (bool, optional): 摄像机空间位置坐标是否受 Y 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isZRotForceReturn (bool, optional): 是否在动画播放完成后强制归零 Z 轴的旋转值。默认值 True
|
||||
isZRotStartOrigin (bool, optional): 是否将动画播放前的 Z 轴旋转值作为初始的 Z 轴旋转值。置为 False 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常(json 配置 override_previous_animation 值为 True 时无效)。默认值 False
|
||||
isDelayOneFrame (bool, optional): 是否延迟一帧再播放摄像机动画。默认值 True
|
||||
isLockPerspective (bool, optional): 是否锁定并强制切换玩家视角。默认值 True
|
||||
tickPosFunc (function, optional): 摄像机动画播放时,在每次设置摄像机位置前执行的函数。默认值 None
|
||||
tickRotFunc (function, optional): 摄像机动画播放时,在每次设置摄像机旋转前执行的函数。默认值 None
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.isFirstPerson = isFirstPerson
|
||||
self.isAllowDrag = isAllowDrag
|
||||
self.isAllowMove = isAllowMove
|
||||
self.isPosFollowXRot = isPosFollowXRot
|
||||
self.isPosFollowYRot = isPosFollowYRot
|
||||
self.isZRotForceReturn = isZRotForceReturn
|
||||
self.isZRotStartOrigin = isZRotStartOrigin
|
||||
self.isDelayOneFrame = isDelayOneFrame
|
||||
self.isLockPerspective = isLockPerspective
|
||||
self.tickPosFunc = tickPosFunc
|
||||
self.tickRotFunc = tickRotFunc
|
||||
self.playAfterFunc = playAfterFunc
|
||||
Whitespace-only changes.
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class NotClientSystemException(Exception):
|
||||
"""
|
||||
不是客户端系统异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(NotClientSystemException, self).__init__(errorMessage)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class NotPlayConfigException(Exception):
|
||||
"""
|
||||
不是播放配置对象异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(NotPlayConfigException, self).__init__(errorMessage)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class SafeEvalException(Exception):
|
||||
"""
|
||||
安全计算异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(SafeEvalException, self).__init__(errorMessage)
|
||||
Whitespace-only changes.
+110
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class SplineCurve:
|
||||
"""
|
||||
Catmull-Rom 样条曲线类
|
||||
"""
|
||||
|
||||
def __init__(self, points):
|
||||
"""
|
||||
初始化样条曲线
|
||||
points: 值列表
|
||||
"""
|
||||
self.points = points
|
||||
|
||||
def getPoint(self, t):
|
||||
"""根据参数 t 获取曲线上的点"""
|
||||
if len(self.points) < 2:
|
||||
return 0
|
||||
|
||||
# 处理边界情况
|
||||
if t <= 0:
|
||||
return self.points[0]
|
||||
if t >= 1:
|
||||
return self.points[-1]
|
||||
|
||||
# 找到包含 t 的线段
|
||||
numSegments = len(self.points) - 1
|
||||
segmentIndex = int(t * numSegments)
|
||||
segmentIndex = max(0, min(segmentIndex, numSegments - 1))
|
||||
|
||||
# 计算线段内的局部参数
|
||||
localT = t * numSegments - segmentIndex
|
||||
|
||||
# 获取四个控制点用于 Catmull-Rom 插值
|
||||
p0Index = max(0, segmentIndex - 1)
|
||||
p1Index = segmentIndex
|
||||
p2Index = segmentIndex + 1
|
||||
p3Index = min(len(self.points) - 1, segmentIndex + 2)
|
||||
|
||||
p0 = self.points[p0Index]
|
||||
p1 = self.points[p1Index]
|
||||
p2 = self.points[p2Index]
|
||||
p3 = self.points[p3Index]
|
||||
|
||||
# Catmull-Rom 插值公式
|
||||
t2 = localT * localT
|
||||
t3 = t2 * localT
|
||||
|
||||
# 计算插值点的 y 坐标
|
||||
y = 0.5 * (
|
||||
(2 * p1) +
|
||||
(-p0 + p2) * localT +
|
||||
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
|
||||
(-p0 + 3 * p1 - 3 * p2 + p3) * t3
|
||||
)
|
||||
return y
|
||||
|
||||
|
||||
def getCatmullromLerp(beforePlus, before, after, afterPlus, axis, alpha):
|
||||
"""
|
||||
Catmull-Rom 插值函数
|
||||
|
||||
Args:
|
||||
beforePlus: 前一个关键帧的前一个关键帧
|
||||
before: 前一个关键帧
|
||||
after: 后一个关键帧
|
||||
afterPlus: 后一个关键帧的后一个关键帧
|
||||
axis: 坐标轴 (x: 0, y: 1, z: 2)
|
||||
alpha: 插值参数 (0-1)
|
||||
|
||||
Returns:
|
||||
插值结果
|
||||
"""
|
||||
vectors = []
|
||||
|
||||
# 构建控制点向量
|
||||
if beforePlus:
|
||||
vectors.append(beforePlus[1][axis])
|
||||
if before:
|
||||
vectors.append(before[1][axis])
|
||||
if after:
|
||||
vectors.append(after[1][axis])
|
||||
if afterPlus:
|
||||
vectors.append(afterPlus[1][axis])
|
||||
|
||||
# 创建样条曲线
|
||||
curve = SplineCurve(vectors)
|
||||
|
||||
# 计算时间参数
|
||||
timeParam = (alpha + (1 if beforePlus else 0)) / (len(vectors) - 1) if len(vectors) > 1 else 0
|
||||
|
||||
# 获取插值点并返回 y 坐标
|
||||
point = curve.getPoint(timeParam)
|
||||
return point
|
||||
|
||||
|
||||
def getCatmullromLerpFromKeyframe(keyframeArray, index, nowTime, axis):
|
||||
"""
|
||||
通过关键帧数组获取当前时间点的平滑插值
|
||||
"""
|
||||
if not keyframeArray:
|
||||
return 0
|
||||
endIndex = len(keyframeArray) - 1
|
||||
kf0 = keyframeArray[max(index - 2, 0)]
|
||||
kf1 = keyframeArray[max(index - 1, 0)]
|
||||
kf2 = keyframeArray[min(index, endIndex)]
|
||||
kf3 = keyframeArray[min(index + 1, endIndex)]
|
||||
den = (kf2[0] - kf1[0])
|
||||
alpha = (nowTime - kf1[0]) / den if den != 0 else 0
|
||||
return getCatmullromLerp(kf0, kf1, kf2, kf3, axis, alpha)
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..exception.SafeEvalException import SafeEvalException
|
||||
|
||||
|
||||
class SafeEval:
|
||||
def __init__(self):
|
||||
self.operators = {
|
||||
'+': lambda x, y: x + y,
|
||||
'-': lambda x, y: x - y,
|
||||
'*': lambda x, y: x * y,
|
||||
'/': lambda x, y: x / y if y != 0 else self.raiseException("发生了除以 0 错误")
|
||||
}
|
||||
|
||||
def tokenize(self, expression):
|
||||
"""将表达式分割成数字和运算符"""
|
||||
tokens = []
|
||||
number = ''
|
||||
negative = False # 用于标记负号
|
||||
for char in expression:
|
||||
if char.isspace():
|
||||
continue
|
||||
elif char.isdigit() or char == '.':
|
||||
number += char
|
||||
elif char == '-':
|
||||
if not number: # 如果前面没有数字,则表示负号
|
||||
negative = True
|
||||
else:
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
number = ''
|
||||
negative = False
|
||||
tokens.append(char)
|
||||
else:
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
number = ''
|
||||
negative = False
|
||||
tokens.append(char)
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
return tokens
|
||||
|
||||
def evaluate(self, expression):
|
||||
"""计算表达式的值"""
|
||||
tokens = self.tokenize(expression)
|
||||
return self.parse_expression(tokens)
|
||||
|
||||
def parse_expression(self, tokens):
|
||||
"""解析表达式"""
|
||||
return self.parse_additive(tokens)
|
||||
|
||||
def parse_additive(self, tokens):
|
||||
"""解析加法和减法"""
|
||||
result = self.parse_multiplicative(tokens)
|
||||
while tokens and tokens[0] in ('+', '-'):
|
||||
op = tokens.pop(0)
|
||||
if op == '+':
|
||||
right = self.parse_multiplicative(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
elif op == '-':
|
||||
right = self.parse_multiplicative(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
return result
|
||||
|
||||
def parse_multiplicative(self, tokens):
|
||||
"""解析乘法和除法"""
|
||||
result = self.parse_primary(tokens)
|
||||
while tokens and tokens[0] in ('*', '/'):
|
||||
op = tokens.pop(0)
|
||||
if op == '*':
|
||||
right = self.parse_primary(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
elif op == '/':
|
||||
right = self.parse_primary(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
return result
|
||||
|
||||
def parse_primary(self, tokens):
|
||||
"""解析基本表达式"""
|
||||
if not tokens:
|
||||
return None
|
||||
token = tokens.pop(0)
|
||||
if isinstance(token, float):
|
||||
return token
|
||||
elif token == '(':
|
||||
result = self.parse_expression(tokens)
|
||||
if not tokens or tokens[0] != ')':
|
||||
return self.raiseException("括号不匹配")
|
||||
tokens.pop(0) # Pop the closing parenthesis
|
||||
return result
|
||||
else:
|
||||
return self.raiseException("无效的语法")
|
||||
|
||||
def raiseException(self, message):
|
||||
"""抛出异常"""
|
||||
SafeEvalException(message)
|
||||
Whitespace-only changes.
@@ -0,0 +1,68 @@
|
||||
# coding=utf-8
|
||||
from ..ToolsConfig import *
|
||||
|
||||
|
||||
class ResultTimer(object):
|
||||
"""
|
||||
结果定时器, 在返回True时结束循环
|
||||
"""
|
||||
|
||||
def __init__(self, func, delay=0.0, maxTimes=10, *args, **kwargs):
|
||||
"""
|
||||
:param func: 执行函数
|
||||
:param delay: 执行延迟
|
||||
:param maxTimes: 最大执行次数
|
||||
"""
|
||||
self.func = func
|
||||
self.delay = delay
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.maxTimes = maxTimes
|
||||
self.runTimes = 0
|
||||
self.gameComp = serverCompFactory.CreateGame(LevelId) or clientCompFactory.CreateGame(LevelId)
|
||||
|
||||
def __timerFunc__(self):
|
||||
result = self.func(*self.args, **self.kwargs)
|
||||
if not result and self.runTimes < self.maxTimes: self.Start()
|
||||
|
||||
def Start(self):
|
||||
"""
|
||||
开始执行定时器
|
||||
:return:
|
||||
"""
|
||||
self.gameComp.AddTimer(self.delay, self.__timerFunc__)
|
||||
self.runTimes += 1
|
||||
|
||||
|
||||
class TimesTimer(object):
|
||||
"""
|
||||
次数定时器
|
||||
"""
|
||||
|
||||
def __init__(self, times, func, delay=0.0, *args, **kwargs):
|
||||
"""
|
||||
:param times: 循环次数
|
||||
:param func: 执行函数
|
||||
:param delay: 执行延迟
|
||||
"""
|
||||
self.func = func
|
||||
self.delay = delay
|
||||
self.times = times
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.gameComp = serverCompFactory.CreateGame(LevelId) or clientCompFactory.CreateGame(LevelId)
|
||||
|
||||
def __func(self, *args, **kwargs):
|
||||
self.times -= 1
|
||||
self.func(*args, **kwargs)
|
||||
if self.times >= 0:
|
||||
self.gameComp.AddTimer(self.delay, self.__func, *self.args, **self.kwargs)
|
||||
|
||||
def Start(self):
|
||||
"""
|
||||
开始执行定时器
|
||||
:return:
|
||||
"""
|
||||
if self.times > 0:
|
||||
self.times -= 1
|
||||
self.gameComp.AddTimer(self.delay, self.__func, *self.args, **self.kwargs)
|
||||
Whitespace-only changes.
@@ -0,0 +1,155 @@
|
||||
# coding=utf-8
|
||||
from ToolsConfig import *
|
||||
|
||||
CreatedTools = {}
|
||||
|
||||
|
||||
def RegisterSystemFastly(api, cls):
|
||||
"""
|
||||
快速注册系统
|
||||
:param api:serverApi or clientApi
|
||||
:param cls:系统类
|
||||
:return:
|
||||
"""
|
||||
api.RegisterSystem(
|
||||
MOD_NAME, cls.__name__,
|
||||
'{}.{}'.format(cls.__module__, cls.__name__)
|
||||
)
|
||||
|
||||
|
||||
class EventFilter(object):
|
||||
def __init__(self, func, systemInstance, *args):
|
||||
self.fiterArgs = args
|
||||
self.systemFuncName = func.__name__
|
||||
self.systemInstance = systemInstance
|
||||
|
||||
def Filter(self, *args):
|
||||
key = True
|
||||
for _ in self.fiterArgs:
|
||||
key = key and _(*args)
|
||||
if not key: break
|
||||
return getattr(self.systemInstance, self.systemFuncName)(*args) if key else None
|
||||
|
||||
|
||||
class SystemTools(object):
|
||||
"""
|
||||
系统工具箱,以不同类进行分辨
|
||||
"""
|
||||
Systems = {}
|
||||
|
||||
def __init__(self):
|
||||
self.bindingInstance = None
|
||||
self.systemListenEvent = set()
|
||||
self.filterEvent = set()
|
||||
self.filter = list()
|
||||
self.lastListened = set()
|
||||
|
||||
@classmethod
|
||||
def GetTools(cls, tag):
|
||||
"""
|
||||
创建系统工具
|
||||
:param tag:工具标签
|
||||
:return:SystemTools
|
||||
"""
|
||||
tag = tag + MOD_NAME + MOD_VERSION
|
||||
instance = CreatedTools.get(tag)
|
||||
if instance is None:
|
||||
instance = cls()
|
||||
CreatedTools[tag] = instance
|
||||
else:
|
||||
CreatedTools[tag].Relisten()
|
||||
return instance
|
||||
|
||||
def GetBlueFoxServerListener(self):
|
||||
"""
|
||||
获取BlueFoxServer系统的监听器
|
||||
"""
|
||||
return self.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxServerSystem')
|
||||
|
||||
def GetBlueFoxClientListener(self):
|
||||
"""
|
||||
获取BlueFoxClient系统的监听器
|
||||
"""
|
||||
return self.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxClientSystem')
|
||||
|
||||
def ListenForEvent(self, namespace=MC_SYSTEM_NAMESPACE, systemName=MC_SYSTEM_NAME, eventName=None, priority=0):
|
||||
"""
|
||||
监听事件,binding的服务端可监听服务端事件,binding的客户端则可监听客户端事件
|
||||
:param eventName: 可选:事件名 or 不传入参数.若不传入参数,则将函数名称视为事件名称
|
||||
:param namespace:如果需要监听其他系统内的事件,需要传入系统的namespace
|
||||
:param systemName:如果需要监听其他系统内的事件,需要传入系统的systemName
|
||||
:param priority:执行优先级
|
||||
:return:
|
||||
"""
|
||||
|
||||
def Listen(func):
|
||||
EventName = (
|
||||
namespace, systemName, eventName if eventName else func.__name__,
|
||||
self.bindingInstance if self.bindingInstance else 0, func, priority
|
||||
)
|
||||
self.systemListenEvent.add(EventName)
|
||||
# 热更新重监听
|
||||
self.__listen(EventName)
|
||||
return func
|
||||
|
||||
return Listen
|
||||
|
||||
def __listen(self, eventName):
|
||||
if self.bindingInstance:
|
||||
self.bindingInstance.ListenForEvent(*eventName)
|
||||
self.lastListened.add(eventName)
|
||||
|
||||
def FilterListener(self, namespace=MC_SYSTEM_NAMESPACE, systemName=MC_SYSTEM_NAME, eventName=None, priority=0):
|
||||
"""
|
||||
滤过监听器, 通常用于优化代码可读性
|
||||
"""
|
||||
|
||||
def Filter(*args):
|
||||
# type:(Callable) -> Callable
|
||||
|
||||
def Listener(func):
|
||||
_filter = EventFilter(func, self.bindingInstance, *args)
|
||||
EventName = (namespace, systemName, eventName, _filter, _filter.Filter, priority)
|
||||
self.filterEvent.add(EventName)
|
||||
self.__listen(EventName)
|
||||
return func
|
||||
|
||||
return Listener
|
||||
|
||||
return Filter
|
||||
|
||||
def Binding(self, systemInstance):
|
||||
"""
|
||||
绑定系统类
|
||||
:param systemInstance:系统实例
|
||||
:return:
|
||||
"""
|
||||
self.bindingInstance = systemInstance
|
||||
self.FirstListen()
|
||||
self.Systems[self.bindingInstance.__class__.__name__] = self.bindingInstance
|
||||
|
||||
@classmethod
|
||||
def GetSystemByClsName(cls, systemName):
|
||||
# type:(str) -> Union[clientBaseSystem,serverBaseSystem]
|
||||
return cls.Systems.get(systemName, None)
|
||||
|
||||
def Relisten(self):
|
||||
# 重监听之前需要取消监听监听过的事件
|
||||
for args in self.lastListened:
|
||||
self.bindingInstance.UnListenForEvent(*args)
|
||||
self.lastListened = set()
|
||||
self.filterEvent = set()
|
||||
|
||||
def FirstListen(self):
|
||||
# 初次监听
|
||||
for args in self.systemListenEvent:
|
||||
args = list(args)
|
||||
args[3] = self.bindingInstance
|
||||
if hasattr(self.bindingInstance, args[4].__name__):
|
||||
self.bindingInstance.ListenForEvent(*args)
|
||||
self.lastListened.add(tuple(args))
|
||||
for args in self.filterEvent:
|
||||
args = list(args)
|
||||
args[3].systemInstance = self.bindingInstance
|
||||
self.bindingInstance.ListenForEvent(*args)
|
||||
self.lastListened.add(tuple(args))
|
||||
@@ -0,0 +1,162 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
import mod.client.extraClientApi as clientApi
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
if False:
|
||||
from typing import *
|
||||
IsDebug = 1
|
||||
|
||||
|
||||
def modDebug(msg):
|
||||
"""输出debug信息"""
|
||||
if IsDebug: logging.debug('[DN-MJ] {}'.format(msg))
|
||||
|
||||
|
||||
def modInfo(msg):
|
||||
"""输出信息Log"""
|
||||
logging.info('[DN-MJ] {}'.format(msg))
|
||||
|
||||
|
||||
def modWarning(msg):
|
||||
"""输出警告Log"""
|
||||
logging.warning('[DN-MJ] {}'.format(msg))
|
||||
|
||||
|
||||
class ButtonTouchTypeEnum(object):
|
||||
TouchDown = 0
|
||||
TouchUp = 1
|
||||
TouchCancel = 2
|
||||
MoveOut = 3
|
||||
|
||||
|
||||
class ItemOperate(object):
|
||||
"""服务端操作用数据集"""
|
||||
EXCHANGE_FLYING = 0
|
||||
EXCHANGE = 1
|
||||
MERGE = 2
|
||||
DROP = 3
|
||||
DROP_FLYING = 4
|
||||
|
||||
@staticmethod
|
||||
def DropFlying():
|
||||
"""
|
||||
丢弃飞行物品
|
||||
:return:
|
||||
"""
|
||||
return {'type': ItemOperate.DROP_FLYING}
|
||||
|
||||
@staticmethod
|
||||
def ExchangeFlying(slot, slotName):
|
||||
"""
|
||||
和飞行物交换物品
|
||||
:param slot: 槽位索引
|
||||
:param slotName: 槽位名称
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
'slot': slot, 'slotName': slotName, 'type': ItemOperate.EXCHANGE_FLYING
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def Exchange(fromSlot, fromSlotName, toSlot, toSlotName):
|
||||
"""
|
||||
交换槽位物品
|
||||
:param fromSlot: 交换起点槽位相对索引
|
||||
:param fromSlotName: 交换起点槽位名称
|
||||
:param toSlot:交换重点槽位相对索引
|
||||
:param toSlotName:交换终点槽位名称
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
'fromSlot': fromSlot, 'fromSlotName': fromSlotName, 'toSlot': toSlot, 'toSlotName': toSlotName,
|
||||
'type': ItemOperate.EXCHANGE
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def Drop(slot, slotName):
|
||||
"""
|
||||
丢弃槽位物品
|
||||
:param slot:槽位相对索引
|
||||
:param slotName:槽位名称
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
'slot': slot, 'slotName': slotName, 'type': ItemOperate.DROP
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def Merge(fromSlot, fromSlotName, toSlot, toSlotName):
|
||||
"""
|
||||
合并槽位物品
|
||||
:param fromSlot: 合并起点槽位相对索引
|
||||
:param fromSlotName: 合并起点槽位名称
|
||||
:param toSlot:合并重点槽位相对索引
|
||||
:param toSlotName:合并终点槽位名称
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
'fromSlot': fromSlot, 'fromSlotName': fromSlotName, 'toSlot': toSlot, 'toSlotName': toSlotName,
|
||||
'type': ItemOperate.MERGE
|
||||
}
|
||||
|
||||
|
||||
class PlayerBehaviorTree(object):
|
||||
Sleep = 0b00000001
|
||||
Sprinting = 0b00000010
|
||||
Sneaking = 0b00000100
|
||||
Gliding = 0b00001000
|
||||
Swimming = 0b00010000
|
||||
Crawling = 0b00100000
|
||||
Flying = 0b01000000
|
||||
SpinAttack = 0b10000000
|
||||
__pat = clientApi.GetMinecraftEnum().PlayerActionType
|
||||
PlayerBehaviors = {
|
||||
__pat.StartSleeping: (Sleep, True),
|
||||
__pat.StopSleeping: (Sleep, False),
|
||||
__pat.StartSprinting: (Sprinting, True),
|
||||
__pat.StopSprinting: (Sprinting, False),
|
||||
__pat.StartSneaking: (Sneaking, True),
|
||||
__pat.StopSneaking: (Sneaking, False),
|
||||
__pat.StartGliding: (Gliding, True),
|
||||
__pat.StopGliding: (Gliding, False),
|
||||
__pat.StartSwimming: (Swimming, True),
|
||||
__pat.StopSwimming: (Swimming, False),
|
||||
__pat.StartCrawling: (Crawling, True),
|
||||
__pat.StopCrawling: (Crawling, False),
|
||||
__pat.StartFlying: (Flying, True),
|
||||
__pat.StopFlying: (Flying, False),
|
||||
__pat.StartSpinAttack: (SpinAttack, True),
|
||||
__pat.StopSpinAttack: (SpinAttack, False)
|
||||
}
|
||||
|
||||
|
||||
from .ModConfig import MOD_NAME, MOD_VERSION
|
||||
|
||||
LevelId = serverApi.GetLevelId() or clientApi.GetLevelId()
|
||||
Platform = serverApi.GetPlatform() or clientApi.GetPlatform()
|
||||
# Platform = 1 # 测试用
|
||||
|
||||
MC_SYSTEM_NAME = serverApi.GetEngineSystemName()
|
||||
MC_SYSTEM_NAMESPACE = serverApi.GetEngineNamespace()
|
||||
# ToolsConfig
|
||||
CREATED_TOOLS = {}
|
||||
CREATED_SCREEN_TOOLS = {}
|
||||
# CompFactory
|
||||
clientCompFactory = clientApi.GetEngineCompFactory()
|
||||
serverCompFactory = serverApi.GetEngineCompFactory()
|
||||
# System
|
||||
serverBaseSystem = serverApi.GetServerSystemCls()
|
||||
clientBaseSystem = clientApi.GetClientSystemCls()
|
||||
# UI
|
||||
if not serverApi.IsInServer():
|
||||
bookManager = clientApi.GetBookManager()
|
||||
nativeScreenManager = clientApi.GetNativeScreenManagerCls().instance() # type:clientApi.GetNativeScreenManagerCls()
|
||||
viewBinder = clientApi.GetViewBinderCls()
|
||||
baseScreen = clientApi.GetScreenNodeCls()
|
||||
customUIScreenProxy = clientApi.GetUIScreenProxyCls()
|
||||
RootScreenPathHead = (
|
||||
'variables_button_mappings_and_controls/safezone_screen_matrix/'
|
||||
'inner_matrix/safezone_screen_panel/root_screen_panel'
|
||||
)
|
||||
@@ -0,0 +1,113 @@
|
||||
# coding=utf-8
|
||||
|
||||
from ..ToolsConfig import *
|
||||
|
||||
|
||||
def RegisterSystemFastly(api, cls):
|
||||
"""
|
||||
快速注册系统
|
||||
:param api:serverApi or clientApi
|
||||
:param cls:系统类
|
||||
:return:
|
||||
"""
|
||||
api.RegisterSystem(
|
||||
MOD_NAME, cls.__name__,
|
||||
'{}.{}'.format(cls.__module__, cls.__name__)
|
||||
)
|
||||
|
||||
|
||||
class SystemTools(object):
|
||||
"""
|
||||
系统工具箱,以不同类进行分辨
|
||||
"""
|
||||
classFunc = []
|
||||
|
||||
@classmethod
|
||||
def Binding(cls, ModName='', ModVersion=''):
|
||||
"""
|
||||
绑定系统类
|
||||
:param ModName:模组名称
|
||||
:param ModVersion:模组版本
|
||||
:return:
|
||||
"""
|
||||
|
||||
def binding(systemCls):
|
||||
tag = systemCls.__name__ + ModName + ModVersion
|
||||
if tag not in CREATED_TOOLS:
|
||||
CREATED_TOOLS[tag] = cls(systemCls)
|
||||
else:
|
||||
CREATED_TOOLS[tag].Relisten()
|
||||
return systemCls
|
||||
|
||||
return binding
|
||||
|
||||
@staticmethod
|
||||
def CreateListener(namespace, systemName):
|
||||
"""
|
||||
创建固定系统的监听函数.拿到函数后使用方法与ListenForEvent相同但不需要传入namespace和systemName参数
|
||||
:param namespace:需要监听的系统namespace
|
||||
:param systemName:需要监听的系统systemName
|
||||
:return:
|
||||
"""
|
||||
|
||||
def Listener(eventName=None, priority=0):
|
||||
def Listen(func):
|
||||
funcName = func.__name__
|
||||
EventName = [namespace, systemName, eventName if eventName else funcName, 0, func, priority]
|
||||
if EventName not in SystemTools.classFunc:
|
||||
SystemTools.classFunc.append(EventName)
|
||||
return func
|
||||
|
||||
return Listen
|
||||
|
||||
return Listener
|
||||
|
||||
@staticmethod
|
||||
def ListenForEvent(eventName=None, namespace=MC_SYSTEM_NAMESPACE, systemName=MC_SYSTEM_NAME, priority=0):
|
||||
"""
|
||||
监听事件,binding的服务端可监听服务端事件,binding的客户端则可监听客户端事件
|
||||
:param eventName: 可选:事件名 or 不传入参数.若不传入参数,则将函数名称视为事件名称
|
||||
:param namespace:如果需要监听其他系统内的事件,需要传入系统的namespace
|
||||
:param systemName:如果需要监听其他系统内的事件,需要传入系统的systemName
|
||||
:param priority:执行优先级
|
||||
:return:
|
||||
"""
|
||||
|
||||
def Listen(func):
|
||||
funcName = func.__name__
|
||||
EventName = [namespace, systemName, eventName if eventName else funcName, 0, func, priority]
|
||||
if EventName not in SystemTools.classFunc:
|
||||
SystemTools.classFunc.append(EventName)
|
||||
return func
|
||||
|
||||
return Listen
|
||||
|
||||
def __init__(self, cls):
|
||||
self.systemCls = cls
|
||||
self.InitSystemOriginal = self.systemCls.__init__
|
||||
setattr(self.systemCls, '__init__', self.InitInstead())
|
||||
self.bindingInstance = None
|
||||
|
||||
def Relisten(self):
|
||||
for args in SystemTools.classFunc:
|
||||
args[3] = self.bindingInstance
|
||||
self.bindingInstance.UnListenForEvent(*args)
|
||||
self.bindingInstance.ListenForEvent(*args)
|
||||
args[3] = 0
|
||||
|
||||
def __Listen(self):
|
||||
if self.bindingInstance:
|
||||
for args in SystemTools.classFunc:
|
||||
args[3] = self.bindingInstance
|
||||
if hasattr(self.bindingInstance, args[4].__name__):
|
||||
self.bindingInstance.ListenForEvent(*args)
|
||||
args[3] = 0
|
||||
|
||||
def InitInstead(self):
|
||||
def __init__(systemInstance, namespace, systemName, self=self):
|
||||
self.InitSystemOriginal(systemInstance, namespace, systemName)
|
||||
if systemInstance.__class__ is self.systemCls:
|
||||
self.bindingInstance = systemInstance
|
||||
self.__Listen()
|
||||
|
||||
return __init__
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,39 @@
|
||||
# Blue Fox Tools Plus
|
||||
|
||||
### 使用前须知
|
||||
|
||||
1. 在使用工具之前,需要修改以下变量:*BlueFoxTools.ToolsConfig.MOD_NAME*以及*BlueFoxTools.ToolsConfig.MOD_VERSION*
|
||||
.变量分别为模组名称,模组版本
|
||||
|
||||
### 自动注册
|
||||
|
||||
1. 使用Load函数自动注册系统时则需要`from BlueFoxTools.ModLoader import *`
|
||||
2. 自动注册系统时,需要在LoadMod之前声明所需要注册的类
|
||||
3. 自动注册系统时,仅注册BlueFox系统及其最终继承子类
|
||||
4. 自动注册的系统,nameSpace为MOD_NAME,systemName为类名
|
||||
|
||||
### SystemTools
|
||||
|
||||
1. 使用时,需要在类__init__函数下调用`SystemTools.Binding`方法
|
||||
2. 使用时,需要在类内生成SystemTools,使用`SystemTools.GetTools`方法获取
|
||||
|
||||
### 背包引擎
|
||||
|
||||
1. 物品数据,确保空物品为{}
|
||||
2. 使用时需要将gird节点名称,collection_name与slotDict内的槽位偏移键同名
|
||||
3. 需要结合背包引擎Json包使用
|
||||
4. 创建界面时,Parm需要传入两个参数:items-物品,posDim-工作方块坐标及维度
|
||||
5. 如需物品跟手功能,需要在界面添加`"bindings":[{"binding_name":"#main.gametick","binding_condition":"always"}],`字段
|
||||
|
||||
### ScreenTools
|
||||
|
||||
1. 如果自动注册界面,会在类内添加属性`cls.blueFoxClient = BlueFoxClientSystem`
|
||||
2. 使用自动注册界面时则需要声明类的位置
|
||||
3. 使用时,需要在类Create函数下调用`ScreenTools.Binding`方法
|
||||
4. 使用时,需要在类内生成ScreenTools,使用`ScreenTools.GetTools`方法获取
|
||||
|
||||
### 方块块
|
||||
|
||||
1. 模型具有一定的制作规则:界面朝北,以模型左下角为原点,移动到建模软件原点
|
||||
2. 方块具有一定规则:必须为四面向方块,必须为方块实体
|
||||
3. 创建之前应使用Able方法确认是否可以创建
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,8 @@
|
||||
# coding=utf-8
|
||||
from BlueFoxTools.ModLoader import *
|
||||
|
||||
if not serverApi.IsInServer():
|
||||
import System.Client
|
||||
import System.Server
|
||||
|
||||
LoadMod()
|
||||
Whitespace-only changes.
Reference in new issue
Block a user