人工代码初始化,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