From 98e9903f7342fcfe3785a65525f0a91f14bd0c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E7=AB=8B=E9=82=A6?= <1290742618@qq.com> Date: Sat, 10 Oct 2026 09:36:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=BA=E5=B7=A5=E4=BB=A3=E7=A0=81=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=EF=BC=8Centities=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E4=BF=9D=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BlueFoxTools/EventFilter.py | 323 +++ .../BlueFoxTools/FastComp.py | 88 + .../BlueFoxTools/ItemTools.py | 171 ++ .../BlueFoxTools/ModConfig.py | 6 + .../BlueFoxTools/ModLoader.py | 39 + .../BlueFoxTools/MolangTools.py | 60 + .../BlueFoxTools/OtherTools.py | 219 ++ .../BlueFoxTools/RenderTools.py | 65 + .../BlueFoxTools/ScreenTools.py | 137 ++ .../SpecialFunciton/BlockBlock.py | 166 ++ .../SpecialFunciton/BlueFoxMath.py | 116 + .../SpecialFunciton/BlueFoxRuckSackEngine.py | 660 ++++++ .../SpecialFunciton/BlueFoxSystem.py | 1864 +++++++++++++++++ .../ControllableUi/Animation.py | 117 ++ .../SpecialFunciton/ControllableUi/Grapher.py | 49 + .../ControllableUi/__init__.py | 77 + .../SpecialFunciton/LocalCoord.py | 81 + .../SpecialFunciton/MountModel.py | 173 ++ .../SpecialFunciton/Probability.py | 80 + .../SpecialFunciton/RouteFinder.py | 325 +++ .../SpecialFunciton/SpringCamera/__init__.py | 0 .../SpringCamera/core/Camera.py | 668 ++++++ .../SpringCamera/core/CameraListen.py | 101 + .../SpringCamera/core/Interpolation.py | 146 ++ .../SpringCamera/core/__init__.py | 0 .../SpringCamera/core/config/PlayConfig.py | 36 + .../SpringCamera/core/config/__init__.py | 0 .../exception/NotClientSystemException.py | 9 + .../core/exception/NotPlayConfigException.py | 9 + .../core/exception/SafeEvalException.py | 9 + .../SpringCamera/core/exception/__init__.py | 0 .../SpringCamera/core/utils/CatmullRom.py | 110 + .../SpringCamera/core/utils/SafeEval.py | 96 + .../SpringCamera/core/utils/__init__.py | 0 .../BlueFoxTools/SpecialFunciton/Timer.py | 68 + .../BlueFoxTools/SpecialFunciton/__init__.py | 0 .../BlueFoxTools/SystemTools.py | 155 ++ .../BlueFoxTools/ToolsConfig.py | 162 ++ .../BlueFoxTools/ToolsOld/SystemTools.py | 113 + .../BlueFoxTools/ToolsOld/__init__.py | 0 .../BlueFoxTools/__init__.py | 0 .../BlueFoxTools/readme.md | 39 + .../System/Client/__init__.py | 0 .../System/Server/__init__.py | 0 .../ScriptDnSa_ren_gong/System/__init__.py | 0 .../ScriptDnSa_ren_gong/__init__.py | 0 .../ScriptDnSa_ren_gong/modMain.py | 8 + behavior_pack_kcH4M5u0/entities/.keep | 0 48 files changed, 6545 insertions(+) create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/EventFilter.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/FastComp.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ItemTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModConfig.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModLoader.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/MolangTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/OtherTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/RenderTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ScreenTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlockBlock.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxMath.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxRuckSackEngine.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxSystem.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Animation.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Grapher.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/LocalCoord.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/MountModel.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Probability.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/RouteFinder.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Camera.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/CameraListen.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Interpolation.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/PlayConfig.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotClientSystemException.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotPlayConfigException.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/SafeEvalException.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/CatmullRom.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/SafeEval.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Timer.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SystemTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsConfig.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/SystemTools.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/readme.md create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Client/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Server/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/__init__.py create mode 100644 behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/modMain.py create mode 100644 behavior_pack_kcH4M5u0/entities/.keep diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/EventFilter.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/EventFilter.py new file mode 100644 index 0000000..1c3cae8 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/EventFilter.py @@ -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 _ diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/FastComp.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/FastComp.py new file mode 100644 index 0000000..b0c4d44 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/FastComp.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ItemTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ItemTools.py new file mode 100644 index 0000000..1e0375f --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ItemTools.py @@ -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 + ) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModConfig.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModConfig.py new file mode 100644 index 0000000..a5fcfc3 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModConfig.py @@ -0,0 +1,6 @@ +# coding=utf-8 + +MOD_NAME = 'rtdd' +MOD_VERSION = '0.0.1' + +Time = 1754134400.0 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModLoader.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModLoader.py new file mode 100644 index 0000000..5ff7d0f --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ModLoader.py @@ -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) \ No newline at end of file diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/MolangTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/MolangTools.py new file mode 100644 index 0000000..38f81ea --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/MolangTools.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/OtherTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/OtherTools.py new file mode 100644 index 0000000..ccbe88a --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/OtherTools.py @@ -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([]) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/RenderTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/RenderTools.py new file mode 100644 index 0000000..c222c26 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/RenderTools.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ScreenTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ScreenTools.py new file mode 100644 index 0000000..02d345c --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ScreenTools.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlockBlock.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlockBlock.py new file mode 100644 index 0000000..3d5e34b --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlockBlock.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxMath.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxMath.py new file mode 100644 index 0000000..a6d6597 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxMath.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxRuckSackEngine.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxRuckSackEngine.py new file mode 100644 index 0000000..792fd02 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxRuckSackEngine.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxSystem.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxSystem.py new file mode 100644 index 0000000..ceb6a04 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/BlueFoxSystem.py @@ -0,0 +1,1864 @@ +# coding=utf-8 +from ..MolangTools import molangNames +from ..OtherTools import mounts +from ..ScreenTools import ScreenTools, RegisterScreenFastly +from ..SystemTools import SystemTools +from ..ToolsConfig import MOD_NAME, serverCompFactory, serverApi +from ..ToolsConfig import clientBaseSystem, serverBaseSystem, clientCompFactory, LevelId + + +class BlueFoxClientSystem(clientBaseSystem): + SystemTool = SystemTools.GetTools('BlueFoxClientSystem') + BlueFoxServerListener = SystemTool.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxServerSystem') + + def __init__(self, namespace, systemName): + super(BlueFoxClientSystem, self).__init__(namespace, systemName) + molangComp = clientCompFactory.CreateQueryVariable(LevelId) + self.blockMolangComp = clientCompFactory.CreateBlockInfo(LevelId) + if self.__class__ == BlueFoxClientSystem: + self.SystemTool.Binding(self) + for mountCls in mounts['c']: + mountCls.blueFoxSystem = self + print "{}类已挂载{}!".format(mountCls.__name__, systemName) + for molangNV in molangNames.items(): + molangComp.Register(*molangNV) + print "Molang:{}已注册, 默认值:{}".format(*molangNV) + print "{}系统已注册!".format(systemName) + + @BlueFoxServerListener + def ChangeBlockEntityMolang(self, args): + if args['posDim'][3] == clientCompFactory.CreateGame(LevelId).GetCurrentDimension(): + self.blockMolangComp.SetBlockEntityMolangValue( + args['posDim'][:3], args['molangName'], args['value'] + ) + + @SystemTool.ListenForEvent() + def UiInitFinished(self, args): + """ + UI引擎加载完毕时触发,本函数继承重写需要执行父类函数 + """ + if self.__class__ == BlueFoxClientSystem: + for screenDef, cls in ScreenTools.autoRegister: + cls.blueFoxClient = self + RegisterScreenFastly(cls, screenDef) + print "{}界面已注册!".format(screenDef) + self.NotifyToServer('SetBlockBlockOffset', {}) + + def _setBlockBlockMobelOffset(self, blockPos, originalPos): + """ + 方块块尝试偏移模型时触发,不建议修改 + """ + offsets = [0, 0, 0] + for _ in range(3): + offsets[_] = (blockPos[_] - originalPos[_]) * -1 + if not clientCompFactory.CreateBlockInfo(LevelId).SetBlockEntityModelPosOffset( + blockPos, tuple(offsets) + ): + clientCompFactory.CreateGame(LevelId).AddTimer( + 0.0, self._setBlockBlockMobelOffset, blockPos, originalPos + ) + + @BlueFoxServerListener + def SetBlockBlockMobelOffset(self, args): + """ + 方块块尝试偏移模型时触发,不建议修改 + """ + clientCompFactory.CreateGame(LevelId).AddTimer( + 0.0, self._setBlockBlockMobelOffset, args['SetPos'][:3], args['originalPos'][:3] + ) + + def AddEntityClientEvent(self, args): + """ + 客户端侧创建新实体时触发 + """ + pass + + def AddPlayerAOIClientEvent(self, args): + """ + 玩家加入游戏或者其余玩家进入当前玩家视野时触发的事件,替换AddPlayerEvent + """ + pass + + def AddPlayerCreatedClientEvent(self, args): + """ + 玩家进入当前玩家所在的区块AOI后,玩家皮肤数据异步加载完成后触发的事件 + """ + pass + + def ChunkAcquireDiscardedClientEvent(self, args): + """ + 触发时机:客户端区块即将被卸载时 + """ + pass + + def ChunkLoadedClientEvent(self, args): + """ + 触发时机:客户端区块加载完成时 + """ + pass + + def GameRenderTickEvent(self, args): + """ + 客户端渲染帧开始时触发该事件,一秒触发次数为当前的帧数 + """ + pass + + def LoadClientAddonScriptsAfter(self, args): + """ + 客户端加载mod完成事件 + """ + pass + + def OnCommandOutputClientEvent(self, args): + """ + 当command命令有成功消息输出时触发 + """ + pass + + def OnLocalPlayerStopLoading(self, args): + """ + 触发时机:玩家进入存档,出生点地形加载完成时触发。该事件触发时可以进行切换维度的操作。 + """ + pass + + def OnScriptTickClient(self, args): + """ + 客户端tick事件,1秒30次 + """ + pass + + def RemoveEntityClientEvent(self, args): + """ + 客户端侧实体被移除时触发 + """ + pass + + def RemovePlayerAOIClientEvent(self, args): + """ + 玩家离开当前玩家视野时触发的事件 + """ + pass + + def UnLoadClientAddonScriptsBefore(self, args): + """ + 客户端卸载mod之前触发 + """ + pass + + def ApproachEntityClientEvent(self, args): + """ + 玩家靠近生物时触发 + """ + pass + + def EntityModelChangedClientEvent(self, args): + """ + 触发时机:实体模型切换时触发 + """ + pass + + def EntityStopRidingEvent(self, args): + """ + 触发时机:当实体停止骑乘时 + """ + pass + + def HealthChangeClientEvent(self, args): + """ + 生物生命值发生变化时触发 + """ + pass + + def LeaveEntityClientEvent(self, args): + """ + 玩家远离生物时触发 + """ + pass + + def OnGroundClientEvent(self, args): + """ + 实体着地事件。玩家,沙子,铁砧,掉落的物品,点燃的TNT掉落地面时触发,其余实体着地不触发。 + """ + pass + + def OnMobHitMobClientEvent(self, args): + """ + 触发时机:通过OpenPlayerHitMobDetection打开生物碰撞检测后,当生物间(包含玩家)碰撞时触发该事件。注:客户端和服务端分别作碰撞检测,可能两个事件返回的略有差异。 + """ + pass + + def StartRidingClientEvent(self, args): + """ + 触发时机:一个实体即将骑乘另外一个实体 + """ + pass + + def CameraMotionStartClientEvent(self, args): + """ + 相机运动器开始事件。相机添加运动器后,运动器开始运行时触发 + """ + pass + + def CameraMotionStopClientEvent(self, args): + """ + 相机运动器停止事件。相机添加运动器并开始运行后,运动器自动停止时触发 + """ + pass + + def DimensionChangeClientEvent(self, args): + """ + 玩家维度改变时客户端抛出 + """ + pass + + def DimensionChangeFinishClientEvent(self, args): + """ + 玩家维度改变完成后客户端抛出 + """ + pass + + def ExtinguishFireClientEvent(self, args): + """ + 玩家扑灭火焰时触发。下雨,倒水等方式熄灭火焰不会触发。 + """ + pass + + def GameTypeChangedClientEvent(self, args): + """ + 个人游戏模式发生变化时客户端触发。 + """ + pass + + def OnLocalPlayerActionClientEvent(self, args): + """ + 玩家动作事件,当本地玩家开始/停止某些动作时触发该事件 + """ + pass + + def OnLocalPlayerStartJumpClientEvent(self, args): + """ + 本地玩家开始跳跃时触发 + """ + pass + + def OnPlayerHitBlockClientEvent(self, args): + """ + 触发时机:通过OpenPlayerHitBlockDetection打开方块碰撞检测后,当玩家碰撞到方块时触发该事件。玩家着地时会触发OnGroundClientEvent,而不是该事件。客户端和服务端分别作碰撞检测,可能两个事件返回的结果略有差异。 + """ + pass + + def PerspChangeClientEvent(self, args): + """ + 视角切换时会触发的事件 + """ + pass + + def PlayerAttackEntityEvent(self, args): + """ + 触发时机:当本地玩家攻击时触发该事件。 + """ + pass + + def BlockAnimateRandomTickEvent(self, args): + """ + 触发时机:以摄像机为中心,随机选取周围的方块触发Tick,触发的数量取决于设备性能。只有添加了netease:block_animate_random_tick的自定义方块才会触发此事件 + """ + pass + + def ClientBlockUseEvent(self, args): + """ + 触发时机:玩家右键点击新版自定义方块(或者通过接口AddBlockItemListenForUseEvent增加监听的MC原生游戏方块)时客户端抛出该事件(该事件tick执行,需要注意效率问题)。 + """ + pass + + def FallingBlockCauseDamageBeforeClientEvent(self, args): + """ + 触发时机:当下落的方块开始计算砸到实体的伤害时,客户端触发该事件 + """ + pass + + def ModBlockEntityLoadedClientEvent(self, args): + """ + 客户端自定义方块实体初始化完成时触发 + """ + pass + + def ModBlockEntityRemoveClientEvent(self, args): + """ + 客户端自定义方块实体卸载时触发 + """ + pass + + def ModBlockEntityTickClientEvent(self, args): + """ + 客户端自定义方块实体tick事件 + """ + pass + + def OnAfterFallOnBlockClientEvent(self, args): + """ + 触发时机:当实体降落到方块后客户端触发,主要用于力的计算 + """ + pass + + def OnEntityInsideBlockClientEvent(self, args): + """ + 触发时机:当实体碰撞盒所在区域有方块时,客户端持续触发 + """ + pass + + def OnModBlockNeteaseEffectCreatedClientEvent(self, args): + """ + 自定义方块实体绑定的特效创建成功事件,在自定义方块实体中绑定的特效创建成功时触发以及使用接口CreateFrameEffectForBlockEntity或CreateParticleEffectForBlockEntity为自定义方块实体添加特效成功时触发。 + """ + pass + + def OnStandOnBlockClientEvent(self, args): + """ + 触发时机:当实体站立到方块上时客户端持续触发 + """ + pass + + def PlayerTryDestroyBlockClientEvent(self, args): + """ + 当玩家即将破坏方块时,客户端线程触发该事件。主要用于床,旗帜,箱子这些根据方块实体数据进行渲染的方块,一般情况下请使用ServerPlayerTryDestroyBlockEvent + """ + pass + + def ShearsDestoryBlockBeforeClientEvent(self, args): + """ + 触发时机:玩家手持剪刀破坏方块时,有剪刀特殊效果的方块会在客户端线程触发该事件 + """ + pass + + def StartDestroyBlockClientEvent(self, args): + """ + 玩家开始挖方块时触发。创造模式下不触发。 + """ + pass + + def StepOffBlockClientEvent(self, args): + """ + 触发时机:实体移动离开一个实心方块时触发 + """ + pass + + def StepOnBlockClientEvent(self, args): + """ + 触发时机:实体刚移动至一个新实心方块时触发。 + """ + pass + + def ActorAcquiredItemClientEvent(self, args): + """ + 触发时机:玩家获得物品时客户端抛出的事件(有些获取物品方式只会触发客户端事件,有些获取物品方式只会触发服务端事件,在使用时注意一点。) + """ + pass + + def ActorUseItemClientEvent(self, args): + """ + 触发时机:玩家使用物品时客户端抛出的事件(比较特殊不走该事件的例子:1)染料对有水的炼药锅使用;2)盔甲架装备盔甲);3)喝牛奶 + """ + pass + + def AnvilCreateResultItemAfterClientEvent(self, args): + """ + 玩家点击铁砧合成得到的物品时抛出的事件。 + """ + pass + + def ClientItemTryUseEvent(self, args): + """ + 玩家点击右键尝试使用物品时客户端抛出的事件,可以通过设置cancel为True取消使用物品。注:如果需要取消物品的使用需要同时在ClientItemTryUseEvent和ServerItemTryUseEvent中将cancel设置为True才能正确取消。 + """ + pass + + def ClientItemUseOnEvent(self, args): + """ + 玩家在对方块使用物品时客户端抛出的事件。注:如果需要取消物品的使用需要同时在ClientItemUseOnEvent和ServerItemUseOnEvent中将ret设置为True才能正确取消。 + """ + pass + + def ClientShapedRecipeTriggeredEvent(self, args): + """ + 玩家合成物品时触发 + """ + pass + + def CraftUpdateResultItemClientEvent(self, args): + """ + 玩家工作台界面更新显示可合成物品时抛出的事件。 + """ + pass + + def GrindStoneRemovedEnchantClientEvent(self, args): + """ + 玩家点击砂轮合成得到的物品时抛出的事件 + """ + pass + + def InventoryItemChangedClientEvent(self, args): + """ + 玩家背包物品变化时客户端抛出的事件。 + """ + pass + + def ItemReleaseUsingClientEvent(self, args): + """ + 触发时机:释放正在使用的物品 + """ + pass + + def OnCarriedNewItemChangedClientEvent(self, args): + """ + 手持物品发生变化时,触发该事件;数量改变不会通知 + """ + pass + + def PlayerTryDropItemClientEvent(self, args): + """ + 触发时机:玩家丢弃物品时触发 + """ + pass + + def StartUsingItemClientEvent(self, args): + """ + 玩家使用物品(目前仅支持Bucket(奶桶)、Trident(三叉戟)、RangedWeapon(弓)、Food(食物)、Potion(药水)、Crossbow(弩))时抛出 + """ + pass + + def StopUsingItemClientEvent(self, args): + """ + 玩家停止使用物品(目前仅支持Bucket(奶桶)、Trident(三叉戟)、RangedWeapon(弓)、Food(食物)、Potion(药水)、Crossbow(弩))时抛出 + """ + pass + + def AttackAnimBeginClientEvent(self, args): + """ + 攻击动作开始时触发 + """ + pass + + def AttackAnimEndClientEvent(self, args): + """ + 攻击动作结束时触发 + """ + pass + + def WalkAnimBeginClientEvent(self, args): + """ + 走路动作开始时触发 + """ + pass + + def WalkAnimEndClientEvent(self, args): + """ + 走路动作结束时触发 + """ + pass + + def AchievementButtonMovedClientEvent(self, args): + """ + 触发时机:使用自定义成就系统的时,拖动成就入口结束时触发 + """ + pass + + def ClientChestCloseEvent(self, args): + """ + 关闭箱子界面时触发,包括小箱子,合并后大箱子和末影箱(实体容器不属于方块,不会触发该事件) + """ + pass + + def ClientChestOpenEvent(self, args): + """ + 打开箱子界面时触发,包括小箱子,合并后大箱子和末影箱(实体容器不属于方块,不会触发该事件) + """ + pass + + def ClientPlayerInventoryCloseEvent(self, args): + """ + 关闭物品背包界面时触发 + """ + pass + + def ClientPlayerInventoryOpenEvent(self, args): + """ + 打开物品背包界面时触发 + """ + pass + + def CloseNeteaseShopEvent(self, args): + """ + 关闭商城界面时触发,包括脚本商城和Apollo插件商城 + """ + pass + + def GridComponentSizeChangedClientEvent(self, args): + """ + 触发时机:UI grid组件里格子数目发生变化时触发 + """ + pass + + def HudButtonChangedClientEvent(self, args): + """ + 当原生HUD按钮位置或大小发生改变时触发,例如玩家使用了自定义控件功能会触发,可在该事件中修改mod按钮的位置防止重叠 + """ + pass + + def OnItemSlotButtonClickedEvent(self, args): + """ + 点击快捷栏、背包栏、盔甲栏、副手栏的物品槽时触发 + """ + pass + + def PlayerChatButtonClickClientEvent(self, args): + """ + 玩家点击聊天按钮或回车键触发呼出聊天窗口时客户端抛出的事件 + """ + pass + + def PopScreenAfterClientEvent(self, args): + """ + screen移除触发 + """ + pass + + def PopScreenEvent(self, args): + """ + screen移除触发 + """ + pass + + def PushScreenEvent(self, args): + """ + screen创建触发 + """ + pass + + def ScreenSizeChangedClientEvent(self, args): + """ + 改变屏幕大小时会触发的事件 + """ + pass + + def OnMusicStopClientEvent(self, args): + """ + 音乐停止时,当玩家调用StopCustomMusic来停止自定义背景音乐时,会触发该事件 + """ + pass + + def PlayMusicClientEvent(self, args): + """ + 播放背景音乐时触发 + """ + pass + + def PlaySoundClientEvent(self, args): + """ + 播放场景音效或UI音效时触发 + """ + pass + + def ClientJumpButtonPressDownEvent(self, args): + """ + 跳跃按钮按下事件,返回值设置参数只对当次按下事件起作用 + """ + pass + + def ClientJumpButtonReleaseEvent(self, args): + """ + 跳跃按钮按下释放事件 + """ + pass + + def GetEntityByCoordEvent(self, args): + """ + 玩家点击屏幕时触发,多个手指点在屏幕上时,只有第一个会触发。 + """ + pass + + def GetEntityByCoordReleaseClientEvent(self, args): + """ + 玩家点击屏幕后松开时触发,多个手指点在屏幕上时,只有最后一个手指松开时触发。 + """ + pass + + def GyroSensorChangedClientEvent(self, args): + """ + 触发时机:陀螺仪传感器姿态发生变化时 + """ + pass + + def HoldBeforeClientEvent(self, args): + """ + 玩家长按屏幕,即将响应到游戏内时触发。仅在移动端或pc的F11模式下触发。pc的非F11模式可以监听RightClickBeforeClientEvent事件 + """ + pass + + def LeftClickBeforeClientEvent(self, args): + """ + 玩家按下攻击/摧毁键时触发,例如鼠标左键、新触控攻击键、手柄右扳机键。鼠标左键仅在pc的普通控制模式(即非F11模式)下触发 + """ + pass + + def LeftClickReleaseClientEvent(self, args): + """ + 玩家松开攻击/摧毁键时触发,例如鼠标左键、新触控攻击键、手柄右扳机键。鼠标左键仅在pc的普通控制模式(即非F11模式)下触发 + """ + pass + + def MouseWheelClientEvent(self, args): + """ + 鼠标滚轮滚动时触发 + """ + pass + + def OnBackButtonReleaseClientEvent(self, args): + """ + 返回按钮(目前特指安卓系统导航中的返回按钮)松开时触发 + """ + pass + + def OnClientPlayerStartMove(self, args): + """ + 移动按钮按下触发事件,在按住一个方向键的同时,去按另外一个方向键,不会触发第二次 + """ + pass + + def OnClientPlayerStopMove(self, args): + """ + 移动按钮按下释放时触发事件,同时按下多个方向键,需要释放所有的方向键才会触发事件 + """ + pass + + def OnGamepadControllerLayoutChangeClientEvent(self, args): + """ + 游戏手柄按键映射改变事件 + """ + pass + + def OnGamepadKeyPressClientEvent(self, args): + """ + 游戏手柄按键事件 + """ + pass + + def OnGamepadStickClientEvent(self, args): + """ + 游戏手柄摇杆事件 + """ + pass + + def OnGamepadTriggerClientEvent(self, args): + """ + 游戏手柄扳机事件 + """ + pass + + def OnKeyPressInGame(self, args): + """ + 按键按下或按键释放时触发 + """ + pass + + def OnKeyboardControllerLayoutChangeClientEvent(self, args): + """ + 键盘按键映射改变事件 + """ + pass + + def OnMouseMiddleDownClientEvent(self, args): + """ + 鼠标按下中键时触发 + """ + pass + + def RightClickBeforeClientEvent(self, args): + """ + 玩家按下使用物品/放置方块键时触发,例如鼠标右键、新触控交互键、手柄左扳机键 + """ + pass + + def RightClickReleaseClientEvent(self, args): + """ + 玩家松开使用物品/放置方块键时触发,例如鼠标右键、新触控交互键、手柄左扳机键 + """ + pass + + def TapBeforeClientEvent(self, args): + """ + 玩家点击屏幕并松手,即将响应到游戏内时触发。仅在移动端或pc的F11模式下触发。pc的非F11模式可以监听LeftClickBeforeClientEvent事件 + """ + pass + + def TapOrHoldReleaseClientEvent(self, args): + """ + 玩家点击屏幕后松手时触发。仅在移动端或pc的F11模式下触发。pc的非F11模式可以使用LeftClickReleaseClientEvent与RightClickReleaseClientEvent事件监听鼠标松开 + """ + pass + + def Destroy(self): + """ + 销毁系统实例时触发 + """ + pass + + +class BlueFoxServerSystem(serverBaseSystem): + SystemTool = SystemTools.GetTools('BlueFoxServerSystem') + BlueFoxClientListener = SystemTool.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxClientSystem') + + def __init__(self, namespace, systemName): + from .. import MolangTools + super(BlueFoxServerSystem, self).__init__(namespace, systemName) + if self.__class__ == BlueFoxServerSystem: + self.SystemTool.Binding(self) + MolangTools.blueFoxServerSystem = self + for mountCls in mounts['s']: + mountCls.blueFoxSystem = self + print "{}类已挂载{}!".format(mountCls.__name__, systemName) + print "{}系统已注册!".format(systemName) + + self.ChangedSlot = set() + + def InventoryItemChangedServerEvent(self, args): + """ + 玩家背包物品变化时服务端抛出的事件 + """ + pass + + @BlueFoxClientListener + def SetBlockBlockOffset(self, args): + """ + 客户端UI加载完成,尝试请求设置方块块模型偏移时触发,已编辑好逻辑 + """ + pass + + @BlueFoxClientListener + def ChangeItem(self, args): + """ + 背包引擎界面尝试修改背包物品时触发 + 已编辑好玩家背包物品栏的处理逻辑,即slot<36 + """ + pass + + @BlueFoxClientListener + def SpawnItemEntity(self, args): + """ + 背包引擎界面尝试关闭时,若具有跟手物品,需要生成物品实体时触发 + """ + self.SpawnItemEntityByPlayer(args['__id__'], args['item']) + + def SpawnItemEntityByPlayer(self, playerId, itemDict): + """ + 背包引擎界面尝试关闭时,若具有跟手物品,需要生成物品实体时触发 + """ + x, y, z = serverCompFactory.CreatePos(playerId).GetFootPos() + d = serverCompFactory.CreateDimension(playerId).GetEntityDimensionId() + posDim = (x, y + 1.2, z, d) + rot = serverCompFactory.CreateRot(playerId).GetRot() + dropItemEntityId = self.CreateEngineItemEntity( + itemDict, posDim[3], posDim[:3] + ) + x, y, z = serverApi.GetDirFromRot(rot) + serverCompFactory.CreateActorMotion(dropItemEntityId).SetMotion((x * 0.3, y * 0.5, z * 0.3)) + return dropItemEntityId + + def getCarriedItem(self, playerId): + """获取玩家主手物品(统一实现)。子服务可覆写, 缺省走这里""" + itemComp = serverCompFactory.CreateItem(playerId) + return itemComp.GetPlayerItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0, True) + + # ============================================================================ + # 统一"主动获得物品"接口(任务一): + # 所有给玩家的物品一律按优先级分发, 而非直接掉落: + # 1) 背包(0~35格)存在同类且未堆满(按物品真实堆叠上限) -> 数量 +count 合并 + # (上限<=1 的物品如水桶/工具不合并, 由 2/3/4 步处理) + # 2) 主手为空 -> 放到主手 + # 3) 主手有物 -> 放入背包(同类叠加/空位) + # 4) 背包满且主手有物 -> 掉落物形式(在 pos 上方生成) + # pos/dim 缺省(只写 giveItemToPlayer(p, name, n) 的老调用点)时, 第4步按玩家脚下掉落, + # 而不是把物品吞掉 —— 保证"背包满时一定可见"。 + # 死亡生物屠宰 / 稻谷 需要保持"纯掉落"时请勿调用本方法(仍用 CreateEngineItemEntity)。 + # ============================================================================ + def giveItemToPlayer(self, playerId, itemName, count=1, pos=None, dim=None, aux=0, userData=None): + """按统一优先级把物品给玩家。pos/dim 只在最终"掉落物"兜底时用(可为空, + 为空时自动取玩家脚下位置与所在维度)。 + 返回 True = 已成功放入(合并/主手/背包); False = 最终只能掉落(或连玩家位置都取不到)。""" + if not itemName: + return False + itemDict = {"itemName": itemName, "count": count, "auxValue": aux} + if userData: + itemDict["userData"] = userData + try: + # 1) 背包同类合并(带 userData 的物品不合并, 避免附魔/药水数据被并错) + if not userData: + remain = self._mergeSameStackInv(playerId, itemName, count, aux) + if remain <= 0: + return True + # 只把"没能并进已有堆叠"的余量继续走 2/3/4 + itemDict["count"] = remain + # 2) 主手为空 -> 放主手 + cur = self.getCarriedItem(playerId) + hand = (cur or {}).get("newItemName") + if not hand or hand == "minecraft:air": + try: + if serverCompFactory.CreateItem(playerId).SpawnItemToPlayerCarried( + itemDict, playerId): + return True + except Exception: + pass + # 3) 放入背包(同类叠加/空位) + try: + if serverCompFactory.CreateItem(playerId).SpawnItemToPlayerInv( + itemDict, playerId): + return True + except Exception: + pass + except Exception: + pass + # 4) 掉落物兜底(没传 pos/dim 就按玩家脚下, 避免物品凭空消失) + if not (pos and dim): + try: + pos = serverCompFactory.CreatePos(playerId).GetFootPos() + dim = serverCompFactory.CreateDimension(playerId).GetEntityDimensionId() + except Exception: + pos, dim = None, None + if pos and dim is not None: + try: + self.CreateEngineItemEntity( + itemDict, dim, (pos[0], pos[1] + 0.5, pos[2])) + except Exception: + pass + return False + + def _getItemMaxStack(self, itemName, aux=0): + """取物品真实的堆叠上限。取不到时回退 64(与原行为一致, 保证未知物品仍可合并)。""" + try: + info = serverCompFactory.CreateItem(LevelId).GetItemBasicInfo( + itemName, aux, False) + size = (info or {}).get("maxStackSize") + if size: + return int(size) + except Exception: + pass + return 64 + + def _mergeSameStackInv(self, playerId, itemName, count=1, aux=0): + """把 count 个 (itemName, aux) 合并进背包(0~35格)里已有的同类堆叠(未满真实上限)。 + 返回"剩余没能合并的数量"(0 = 全部并进了已有堆叠)。 + 上限取物品真实 maxStackSize, 上限<=1 的物品(水桶/工具等)一律不合并, + 交由后续主手/背包(引擎接口)/掉落处理, 避免出现"一格被叠成多个"。 + 读写用本工程已验证可用的 GetPlayerAllItems(0) + SetInvItemNum + (参考 FastComp.DeclarePlayerInvItem); 逐格独立 try 保护, 单格异常不影响其它格。""" + remain = count + if remain <= 0: + return 0 + maxStack = self._getItemMaxStack(itemName, aux) + if maxStack <= 1: + return remain + try: + itemComp = serverCompFactory.CreateItem(playerId) + allItems = itemComp.GetPlayerAllItems(0) # 0 = ItemPosType.INVENTORY(0~35) + except Exception: + return count + for index in range(0, len(allItems or [])): + if remain <= 0: + break + d = allItems[index] + if not d: + continue + try: + if d.get("newItemName") != itemName: + continue + if (d.get("newAuxValue") or 0) != aux: + continue + cur = d.get("count") or 0 + if cur <= 0 or cur >= maxStack: + continue + add = min(maxStack - cur, remain) + if itemComp.SetInvItemNum(index, cur + add): + remain -= add + except Exception: + continue + return remain + + @BlueFoxClientListener + def RucksackScreenClosed(self, args): + """ + 背包引擎界面尝试关闭时触发 + """ + pass + + def AchievementCompleteEvent(self, args): + """ + 玩家完成自定义成就时触发该事件 + """ + pass + + def AddEntityServerEvent(self, args): + """ + 服务端侧创建新实体,或实体从存档加载时触发 + """ + pass + + def AddServerPlayerEvent(self, args): + """ + 触发时机:玩家加入时触发该事件。 + """ + pass + + def ChunkAcquireDiscardedServerEvent(self, args): + """ + 服务端区块即将被卸载时触发 + """ + pass + + def ChunkGeneratedServerEvent(self, args): + """ + 触发时机:区块创建完成时触发 + """ + pass + + def ChunkLoadedServerEvent(self, args): + """ + 触发时机:服务端区块加载完成时 + """ + pass + + def ClientLoadAddonsFinishServerEvent(self, args): + """ + 触发时机:客户端mod加载完成时,服务端触发此事件。服务器可以使用此事件,往客户端发送数据给其初始化。 + """ + pass + + def CommandEvent(self, args): + """ + 玩家请求执行指令时触发 + """ + pass + + def CustomCommandTriggerServerEvent(self, args): + """ + 自定义命令触发事件 + """ + pass + + def DelServerPlayerEvent(self, args): + """ + 触发时机:删除玩家时触发该事件。 + """ + pass + + def EntityRemoveEvent(self, args): + """ + 实体被删除时触发 + """ + pass + + def ExplosionServerEvent(self, args): + """ + 当发生爆炸时触发。 + """ + pass + + def GlobalCommandServerEvent(self, args): + """ + 服务端全局命令事件,包括聊天栏发送、SetCommand接口、命令方块(矿车)、行为包动画执行命令 + """ + pass + + def LoadServerAddonScriptsAfter(self, args): + """ + 服务器加载完mod时触发 + """ + pass + + def NewOnEntityAreaEvent(self, args): + """ + 触发时机:通过RegisterEntityAOIEvent注册过AOI事件后,当有实体进入或离开注册感应区域时触发该事件。 + """ + pass + + def OnCommandOutputServerEvent(self, args): + """ + Command命令执行成功事件 + """ + pass + + def OnContainerFillLoottableServerEvent(self, args): + """ + 触发时机:随机奖励箱第一次打开根据loottable生成物品时 + """ + pass + + def OnLightningLevelChangeServerEvent(self, args): + """ + 打雷强度发生改变 + """ + pass + + def OnLocalLightningLevelChangeServerEvent(self, args): + """ + 独立维度天气打雷强度发生改变时触发 + """ + pass + + def OnLocalRainLevelChangeServerEvent(self, args): + """ + 独立维度天气下雨强度发生改变时触发 + """ + pass + + def OnRainLevelChangeServerEvent(self, args): + """ + 下雨强度发生改变 + """ + pass + + def OnScriptTickServer(self, args): + """ + 服务器tick时触发,1秒有30个tick + """ + pass + + def PlaceNeteaseLargeFeatureServerEvent(self, args): + """ + 触发时机:网易版大型结构即将生成时服务端抛出该事件。 + """ + pass + + def PlaceNeteaseStructureFeatureEvent(self, args): + """ + 触发时机:首次生成地形时,结构特征即将生成时服务端抛出该事件。 + """ + pass + + def PlayerIntendLeaveServerEvent(self, args): + """ + 触发时机:即将删除玩家时触发该事件,此时可以通过各种API获取玩家的当前状态。 + """ + pass + + def PlayerJoinMessageEvent(self, args): + """ + 触发时机:准备显示“xxx加入游戏”的玩家登录提示文字时服务端抛出的事件。 + """ + pass + + def PlayerLeftMessageServerEvent(self, args): + """ + 触发时机:准备显示“xxx离开游戏”的玩家离开提示文字时服务端抛出的事件。 + """ + pass + + def ServerChatEvent(self, args): + """ + 玩家发送聊天信息时触发 + """ + pass + + def ServerPostBlockPatternEvent(self, args): + """ + 触发时机:用方块组合生成生物,生成生物之后触发该事件。 + """ + pass + + def ServerPreBlockPatternEvent(self, args): + """ + 触发时机:用方块组合生成生物,在放置最后一个组成方块时触发该事件。 + """ + pass + + def ServerSpawnMobEvent(self, args): + """ + 游戏内自动生成生物,以及使用api生成生物时触发 + """ + pass + + def ActorHurtServerEvent(self, args): + """ + 触发时机:生物(包括玩家)受伤时触发 + """ + pass + + def ActuallyHurtServerEvent(self, args): + """ + 实体实际受到伤害时触发,相比于DamageEvent,该伤害为经过护甲及buff计算后,实际的扣血量 + """ + pass + + def AddEffectServerEvent(self, args): + """ + 触发时机:实体获得状态效果时 + """ + pass + + def ChangeSwimStateServerEvent(self, args): + """ + 触发时机:实体开始或者结束游泳时 + """ + pass + + def DamageEvent(self, args): + """ + 实体受到伤害时触发 + """ + pass + + def EntityChangeDimensionServerEvent(self, args): + """ + 实体维度改变时服务端抛出 + """ + pass + + def EntityDefinitionsEventServerEvent(self, args): + """ + 触发时机:生物定义json文件中设置的event触发时同时触发。生物行为变更事件 + """ + pass + + def EntityDieLoottableAfterServerEvent(self, args): + """ + 触发时机:生物死亡掉落物品之后 + """ + pass + + def EntityDieLoottableServerEvent(self, args): + """ + 触发时机:生物死亡掉落物品时 + """ + pass + + def EntityDroppedItemServerEvent(self, args): + """ + 触发时机:生物扔出物品时触发 + """ + pass + + def EntityEffectDamageServerEvent(self, args): + """ + 生物受到状态伤害/回复事件。 + """ + pass + + def EntityLoadScriptEvent(self, args): + """ + 数据库加载实体自定义数据时触发 + """ + pass + + def EntityMotionStartServerEvent(self, args): + """ + 实体运动器开始事件。实体(包含玩家)添加运动器后,运动器开始运行时触发 + """ + pass + + def EntityMotionStopServerEvent(self, args): + """ + 实体运动器停止事件。实体(包含玩家)添加运动器并开始运行后,运动器自动停止时触发 + """ + pass + + def EntityPickupItemServerEvent(self, args): + """ + 有minecraft:behavior.pickup_items行为的生物拾取物品时触发该事件,例如村民拾取面包、猪灵拾取金锭 + """ + pass + + def EntityStartRidingEvent(self, args): + """ + 当实体骑乘上另一个实体时触发 + """ + pass + + def EntityStopRidingEvent(self, args): + """ + 触发时机:当实体停止骑乘时 + """ + pass + + def EntityTickServerEvent(self, args): + """ + 实体tick时触发。该事件为20帧每秒。需要使用AddEntityTickEventWhiteList添加触发该事件的实体类型白名单。 + """ + pass + + def HealthChangeBeforeServerEvent(self, args): + """ + 生物生命值发生变化之前触发 + """ + pass + + def HealthChangeServerEvent(self, args): + """ + 生物生命值发生变化时触发 + """ + pass + + def MobDieEvent(self, args): + """ + 实体死亡时触发 + """ + pass + + def MobGriefingBlockServerEvent(self, args): + """ + 环境生物改变方块时触发,触发的时机与mobgriefing游戏规则影响的行为相同 + """ + pass + + def OnFireHurtEvent(self, args): + """ + 生物受到火焰伤害时触发 + """ + pass + + def OnGroundServerEvent(self, args): + """ + 实体着地事件。实体,掉落的物品,点燃的TNT掉落地面时触发 + """ + pass + + def OnKnockBackServerEvent(self, args): + """ + 实体被击退时触发 + """ + pass + + def OnMobHitBlockServerEvent(self, args): + """ + 触发时机:通过OpenMobHitBlockDetection打开方块碰撞检测后,当生物(不包括玩家)碰撞到方块时触发该事件。 + """ + pass + + def OnMobHitMobServerEvent(self, args): + """ + 触发时机:通过OpenPlayerHitMobDetection打开生物碰撞检测后,当生物间(包含玩家)碰撞时触发该事件。注:客户端和服务端分别作碰撞检测,可能两个事件返回的略有差异。 + """ + pass + + def ProjectileCritHitEvent(self, args): + """ + 触发时机:当抛射物与头部碰撞时触发该事件。注:需调用OpenPlayerCritBox开启玩家爆头后才能触发。 + """ + pass + + def ProjectileDoHitEffectEvent(self, args): + """ + 触发时机:当抛射物碰撞时触发该事件 + """ + pass + + def RefreshEffectServerEvent(self, args): + """ + 触发时机:实体身上状态效果更新时触发,更新条件1、新增状态等级较高,更新状态等级及时间;2、新增状态等级不变,时间较长,更新状态持续时间 + """ + pass + + def RemoveEffectServerEvent(self, args): + """ + 触发时机:实体身上状态效果被移除时 + """ + pass + + def SpawnProjectileServerEvent(self, args): + """ + 触发时机:抛射物生成时触发 + """ + pass + + def StartRidingServerEvent(self, args): + """ + 触发时机:一个实体即将骑乘另外一个实体 + """ + pass + + def WillAddEffectServerEvent(self, args): + """ + 触发时机:实体即将获得状态效果前 + """ + pass + + def WillTeleportToServerEvent(self, args): + """ + 实体即将传送或切换维度 + """ + pass + + def AddExpEvent(self, args): + """ + 触发时机:当玩家增加经验时触发该事件。 + """ + pass + + def AddLevelEvent(self, args): + """ + 触发时机:当玩家升级时触发该事件。 + """ + pass + + def ChangeLevelUpCostServerEvent(self, args): + """ + 触发时机:获取玩家下一个等级升级经验时,用于重载玩家的升级经验,每个等级在重置之前都只会触发一次 + """ + pass + + def DimensionChangeFinishServerEvent(self, args): + """ + 玩家维度改变完成后服务端抛出 + """ + pass + + def DimensionChangeServerEvent(self, args): + """ + 玩家维度改变时服务端抛出 + """ + pass + + def ExtinguishFireServerEvent(self, args): + """ + 玩家扑灭火焰时触发。下雨,倒水等方式熄灭火焰不会触发。 + """ + pass + + def GameTypeChangedServerEvent(self, args): + """ + 当默认游戏模式或个人游戏模式发生变化时服务端触发,如果个人游戏模式不为默认时,修改默认游戏模式也会同时修改个人游戏模式,此时会触发两次该事件 + """ + pass + + def MountTamingEvent(self, args): + """ + 玩家通过骑乘驯服生物后触发该事件 + """ + pass + + def OnPlayerActionServerEvent(self, args): + """ + 玩家动作事件,当玩家开始/停止某些动作时触发该事件 + """ + pass + + def OnPlayerHitBlockServerEvent(self, args): + """ + 触发时机:通过OpenPlayerHitBlockDetection打开方块碰撞检测后,当玩家碰撞到方块时触发该事件。监听玩家着地请使用客户端的OnGroundClientEvent。客户端和服务端分别作碰撞检测,可能两个事件返回的略有差异。 + """ + pass + + def PlayerAttackEntityEvent(self, args): + """ + 触发时机:当玩家攻击时触发该事件。 + """ + pass + + def PlayerDieEvent(self, args): + """ + 触发时机:当玩家死亡时触发该事件。 + """ + pass + + def PlayerDoInteractServerEvent(self, args): + """ + 玩家与有minecraft:interact组件的生物交互时触发该事件,例如玩家手持空桶对牛挤奶、玩家手持打火石点燃苦力怕 + """ + pass + + def PlayerEatFoodServerEvent(self, args): + """ + 触发时机:玩家吃下食物时触发 + """ + pass + + def PlayerFeedEntityServerEvent(self, args): + """ + 玩家喂养生物时触发,例如玩家手持小麦喂养牛、玩家手持胡萝卜喂养幼年猪。 + """ + pass + + def PlayerHungerChangeServerEvent(self, args): + """ + 玩家饥饿度变化时触发该事件 + """ + pass + + def PlayerHurtEvent(self, args): + """ + 触发时机:当玩家受伤害前触发该事件。 + """ + pass + + def PlayerInteractServerEvent(self, args): + """ + 触发时机:玩家可以与实体交互时。如果是鼠标控制模式,则当准心对着实体时触发。如果是触屏模式,则触发时机与屏幕下方的交互按钮显示的时机相同。玩家真正与实体发生交互的事件见PlayerDoInteractServerEvent + """ + pass + + def PlayerNamedEntityServerEvent(self, args): + """ + 玩家用命名牌重命名实体时触发,例如玩家手持命名牌对羊修改名字、玩家手持命名牌对盔甲架修改名字。 + """ + pass + + def PlayerRespawnEvent(self, args): + """ + 触发时机:玩家复活时触发该事件。 + """ + pass + + def PlayerRespawnFinishServerEvent(self, args): + """ + 触发时机:玩家复活完毕时触发 + """ + pass + + def PlayerSleepServerEvent(self, args): + """ + 玩家使用床睡觉成功 + """ + pass + + def PlayerStopSleepServerEvent(self, args): + """ + 玩家停止睡觉 + """ + pass + + def PlayerTeleportEvent(self, args): + """ + 触发时机:当玩家传送时触发该事件,如:玩家使用末影珍珠或tp指令时。 + """ + pass + + def PlayerTrySleepServerEvent(self, args): + """ + 玩家尝试使用床睡觉 + """ + pass + + def ServerPlayerGetExperienceOrbEvent(self, args): + """ + 触发时机:玩家获取经验球时触发的事件 + """ + pass + + def StoreBuySuccServerEvent(self, args): + """ + 触发时机:玩家游戏内购买商品时服务端抛出的事件 + """ + pass + + def BlockDestroyByLiquidServerEvent(self, args): + """ + 触发时机:方块被水流破坏的事件 + """ + pass + + def BlockLiquidStateChangeAfterServerEvent(self, args): + """ + 触发时机:方块转为含水或者脱离含水(流体)后触发 + """ + pass + + def BlockLiquidStateChangeServerEvent(self, args): + """ + 触发时机:方块转为含水或者脱离含水(流体)前触发 + """ + pass + + def BlockNeighborChangedServerEvent(self, args): + """ + 触发时机:自定义方块周围的方块发生变化时,需要配置netease:neighborchanged_sendto_script,详情请查阅《自定义农作物》文档 + """ + pass + + def BlockRandomTickServerEvent(self, args): + """ + 触发时机:自定义方块配置netease:random_tick随机tick时 + """ + pass + + def BlockRemoveServerEvent(self, args): + """ + 触发时机:监听该事件的方块在销毁时触发,可以通过ListenOnBlockRemoveEvent方法进行监听,或者通过json组件netease:listen_block_remove进行配置 + """ + pass + + def BlockSnowStateChangeAfterServerEvent(self, args): + """ + 触发时机:方块转为含雪或者脱离含雪后触发 + """ + pass + + def BlockSnowStateChangeServerEvent(self, args): + """ + 触发时机:方块转为含雪或者脱离含雪前触发 + """ + pass + + def BlockStrengthChangedServerEvent(self, args): + """ + 触发时机:自定义机械元件方块红石信号量发生变化时触发 + """ + pass + + def ChestBlockTryPairWithServerEvent(self, args): + """ + 触发时机:两个并排的小箱子方块准备组合为一个大箱子方块时 + """ + pass + + def CommandBlockContainerOpenEvent(self, args): + """ + 触发时机:玩家点击命令方块,尝试打开命令方块的设置界面 + """ + pass + + def CommandBlockUpdateEvent(self, args): + """ + 触发时机:玩家尝试修改命令方块的内置命令时 + """ + pass + + def DestroyBlockEvent(self, args): + """ + 触发时机:当方块已经被玩家破坏时触发该事件。 + """ + pass + + def DirtBlockToGrassBlockServerEvent(self, args): + """ + 触发时机:泥土方块变成草方块时触发 + """ + pass + + def EntityPlaceBlockAfterServerEvent(self, args): + """ + 触发时机:当生物成功放置方块后触发 + """ + pass + + def FallingBlockBreakServerEvent(self, args): + """ + 触发时机:当下落的方块实体被破坏时,服务端触发该事件 + """ + pass + + def FallingBlockCauseDamageBeforeServerEvent(self, args): + """ + 触发时机:当下落的方块开始计算砸到实体的伤害时,服务端触发该事件 + """ + pass + + def FallingBlockReturnHeavyBlockServerEvent(self, args): + """ + 触发时机:当下落的方块实体变回普通重力方块时,服务端触发该事件 + """ + pass + + def FarmBlockToDirtBlockServerEvent(self, args): + """ + 触发时机:耕地退化为泥土时触发 + """ + pass + + def GrassBlockToDirtBlockServerEvent(self, args): + """ + 触发时机:草方块变成泥土方块时触发 + """ + pass + + def HeavyBlockStartFallingServerEvent(self, args): + """ + 触发时机:当重力方块变为下落的方块实体后,服务端触发该事件 + """ + pass + + def HopperTryPullInServerEvent(self, args): + """ + 触发时机:当漏斗上方连接容器后,容器往漏斗开始输入物品时触发,事件仅触发一次 + """ + pass + + def HopperTryPullOutServerEvent(self, args): + """ + 触发时机:当漏斗以毗邻的方式连接容器时,即从旁边连接容器时,漏斗向容器开始输出物品时触发,事件仅触发一次 + """ + pass + + def OnAfterFallOnBlockServerEvent(self, args): + """ + 触发时机:当实体降落到方块后服务端触发,主要用于力的计算 + """ + pass + + def OnBeforeFallOnBlockServerEvent(self, args): + """ + 触发时机:当实体刚降落到方块上时服务端触发,主要用于伤害计算 + """ + pass + + def OnEntityInsideBlockServerEvent(self, args): + """ + 触发时机:当实体碰撞盒所在区域有方块时,服务端持续触发 + """ + pass + + def OnStandOnBlockServerEvent(self, args): + """ + 触发时机:当实体站立到方块上时服务端持续触发 + """ + pass + + def PistonActionServerEvent(self, args): + """ + 触发时机:活塞或者粘性活塞推送/缩回影响附近方块时 + """ + pass + + def ServerBlockEntityTickEvent(self, args): + """ + 触发时机:自定义方块配置了netease:block_entity组件并设tick为true,方块在玩家的模拟距离(新建存档时可以设置,默认为4个区块)内,或者在tickingarea内的时候触发 + """ + pass + + def ServerBlockUseEvent(self, args): + """ + 触发时机:玩家右键点击新版自定义方块(或者通过接口AddBlockItemListenForUseEvent增加监听的MC原生游戏方块)时服务端抛出该事件(该事件tick执行,需要注意效率问题)。 + """ + pass + + def ServerEntityTryPlaceBlockEvent(self, args): + """ + 触发时机:当生物试图放置方块时触发该事件。 + """ + pass + + def ServerPlaceBlockEntityEvent(self, args): + """ + 触发时机:手动放置或通过接口创建含自定义方块实体的方块时触发,此时可向该方块实体中存放数据 + """ + pass + + def ServerPlayerTryDestroyBlockEvent(self, args): + """ + 当玩家即将破坏方块时,服务端线程触发该事件。 + """ + pass + + def ShearsDestoryBlockBeforeServerEvent(self, args): + """ + 触发时机:玩家手持剪刀破坏方块时,有剪刀特殊效果的方块会在服务端线程触发该事件 + """ + pass + + def StartDestroyBlockServerEvent(self, args): + """ + 玩家开始挖方块时触发。创造模式下不触发。 + """ + pass + + def StepOffBlockServerEvent(self, args): + """ + 触发时机:实体移动离开一个实心方块时触发 + """ + pass + + def StepOnBlockServerEvent(self, args): + """ + 触发时机:实体刚移动至一个新实心方块时触发。 + """ + pass + + def ActorAcquiredItemServerEvent(self, args): + """ + 触发时机:玩家获得物品时服务端抛出的事件(有些获取物品方式只会触发客户端事件,有些获取物品方式只会触发服务端事件,在使用时注意一点。) + """ + pass + + def ActorUseItemServerEvent(self, args): + """ + 触发时机:玩家使用物品生效之前服务端抛出的事件(比较特殊不走该事件的例子:1)染料对有水的炼药锅使用;2)盔甲架装备盔甲) + """ + pass + + def ContainerItemChangedServerEvent(self, args): + """ + 容器物品变化事件 + """ + pass + + def CraftItemOutputChangeServerEvent(self, args): + """ + 玩家从容器拿出生成物品时触发 + """ + pass + + def FurnaceBurnFinishedServerEvent(self, args): + """ + 服务端熔炉烧制触发事件。熔炉, 高炉,烟熏炉烧出物品时触发 + """ + pass + + def ItemDurabilityChangedServerEvent(self, args): + """ + 物品耐久度变化事件 + """ + pass + + def ItemReleaseUsingServerEvent(self, args): + """ + 触发时机:释放正在使用的物品时 + """ + pass + + def ItemUseAfterServerEvent(self, args): + """ + 玩家在使用物品之后服务端抛出的事件。 + """ + pass + + def ItemUseOnAfterServerEvent(self, args): + """ + 玩家在对方块使用物品之后服务端抛出的事件。 + """ + pass + + def OnCarriedNewItemChangedServerEvent(self, args): + """ + 触发时机:玩家切换主手物品时触发该事件 + """ + pass + + def OnItemPutInEnchantingModelServerEvent(self, args): + """ + 触发时机:玩家将可附魔物品放到附魔台上时 + """ + pass + + def OnNewArmorExchangeServerEvent(self, args): + """ + 触发时机:玩家切换盔甲时触发该事件 + """ + pass + + def OnOffhandItemChangedServerEvent(self, args): + """ + 触发时机:玩家切换副手物品时触发该事件 + """ + pass + + def OnPlayerActiveShieldServerEvent(self, args): + """ + 触发时机:玩家激活/取消激活盾牌触发的事件。包括玩家持盾进入潜行状态,以及在潜行状态切换盾牌(切换耐久度不同的相同盾牌不会触发) + """ + pass + + def OnPlayerBlockedByShieldAfterServerEvent(self, args): + """ + 触发时机:玩家使用盾牌抵挡伤害之后触发 + """ + pass + + def OnPlayerBlockedByShieldBeforeServerEvent(self, args): + """ + 触发时机:玩家使用盾牌抵挡伤害之前触发 + """ + pass + + def PlayerDropItemServerEvent(self, args): + """ + 触发时机:玩家丢弃物品时触发 + """ + pass + + def PlayerPickupArrowServerEvent(self, args): + """ + 玩家即将捡起抛射物时触发,包括使用"netease:pick_up"的自定义抛射物 + """ + pass + + def ServerItemTryUseEvent(self, args): + """ + 玩家点击右键尝试使用物品时服务端抛出的事件。注:如果需要取消物品的使用需要同时在ClientItemTryUseEvent和ServerItemTryUseEvent中将cancel设置为True才能正确取消。 + """ + pass + + def ServerItemUseOnEvent(self, args): + """ + 玩家在对方块使用物品之前服务端抛出的事件。注:如果需要取消物品的使用需要同时在ClientItemUseOnEvent和ServerItemUseOnEvent中将ret设置为True才能正确取消。 + """ + pass + + def ServerPlayerTryTouchEvent(self, args): + """ + 玩家即将捡起物品时触发 + """ + pass + + def ShearsUseToBlockBeforeServerEvent(self, args): + """ + 触发时机:实体手持剪刀对方块使用时,有剪刀特殊效果的方块会在服务端线程触发该事件 + """ + pass + + def UIContainerItemChangedServerEvent(self, args): + """ + 合成容器物品发生变化时触发 + """ + pass + + def AttackAnimBeginServerEvent(self, args): + """ + 当攻击动作开始时触发 + """ + pass + + def AttackAnimEndServerEvent(self, args): + """ + 当攻击动作结束时触发 + """ + pass + + def JumpAnimBeginServerEvent(self, args): + """ + 当跳跃动作开始时触发 + """ + pass + + def WalkAnimBeginServerEvent(self, args): + """ + 当走路动作开始时触发 + """ + pass + + def WalkAnimEndServerEvent(self, args): + """ + 当走路动作结束时触发 + """ + pass + + def PlayerInventoryOpenScriptServerEvent(self, args): + """ + 某个客户端打开物品背包界面时触发 + """ + pass + + def UrgeShipEvent(self, args): + """ + 玩家点击商城催促发货按钮时触发该事件 + """ + pass + + def lobbyGoodBuySucServerEvent(self, args): + """ + 玩家登录联机大厅服务器,或者联机大厅游戏内购买商品时触发。如果是玩家登录,触发时玩家客户端已经触发了UiInitFinished事件 + """ + pass + + def Destroy(self): + """ + 销毁系统实例时触发 + """ + pass diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Animation.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Animation.py new file mode 100644 index 0000000..d57c074 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Animation.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Grapher.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Grapher.py new file mode 100644 index 0000000..add1b42 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/Grapher.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/__init__.py new file mode 100644 index 0000000..8a183a9 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/ControllableUi/__init__.py @@ -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 + # 初始化 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/LocalCoord.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/LocalCoord.py new file mode 100644 index 0000000..c0128f7 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/LocalCoord.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/MountModel.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/MountModel.py new file mode 100644 index 0000000..8aa09ea --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/MountModel.py @@ -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(*_) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Probability.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Probability.py new file mode 100644 index 0000000..4ec21d0 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Probability.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/RouteFinder.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/RouteFinder.py new file mode 100644 index 0000000..2e3800b --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/RouteFinder.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Camera.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Camera.py new file mode 100644 index 0000000..eb2d9ff --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Camera.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/CameraListen.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/CameraListen.py new file mode 100644 index 0000000..bc017cb --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/CameraListen.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Interpolation.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Interpolation.py new file mode 100644 index 0000000..6e279d0 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/Interpolation.py @@ -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() diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/PlayConfig.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/PlayConfig.py new file mode 100644 index 0000000..59a0476 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/PlayConfig.py @@ -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 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotClientSystemException.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotClientSystemException.py new file mode 100644 index 0000000..bdddb24 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotClientSystemException.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +class NotClientSystemException(Exception): + """ + 不是客户端系统异常 + """ + + def __init__(self, errorMessage): + super(NotClientSystemException, self).__init__(errorMessage) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotPlayConfigException.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotPlayConfigException.py new file mode 100644 index 0000000..0027862 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/NotPlayConfigException.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +class NotPlayConfigException(Exception): + """ + 不是播放配置对象异常 + """ + + def __init__(self, errorMessage): + super(NotPlayConfigException, self).__init__(errorMessage) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/SafeEvalException.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/SafeEvalException.py new file mode 100644 index 0000000..f1f5ab6 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/SafeEvalException.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +class SafeEvalException(Exception): + """ + 安全计算异常 + """ + + def __init__(self, errorMessage): + super(SafeEvalException, self).__init__(errorMessage) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/exception/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/CatmullRom.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/CatmullRom.py new file mode 100644 index 0000000..0978c26 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/CatmullRom.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/SafeEval.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/SafeEval.py new file mode 100644 index 0000000..002018d --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/SafeEval.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/SpringCamera/core/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Timer.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Timer.py new file mode 100644 index 0000000..fdf1ab5 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/Timer.py @@ -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) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SpecialFunciton/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SystemTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SystemTools.py new file mode 100644 index 0000000..de44d15 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/SystemTools.py @@ -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)) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsConfig.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsConfig.py new file mode 100644 index 0000000..b18dff4 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsConfig.py @@ -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' +) diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/SystemTools.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/SystemTools.py new file mode 100644 index 0000000..cae3a54 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/SystemTools.py @@ -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__ diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/ToolsOld/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/readme.md b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/readme.md new file mode 100644 index 0000000..256022a --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/BlueFoxTools/readme.md @@ -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方法确认是否可以创建 \ No newline at end of file diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Client/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Server/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/Server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/System/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/__init__.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/modMain.py b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/modMain.py new file mode 100644 index 0000000..fc0b070 --- /dev/null +++ b/behavior_pack_kcH4M5u0/ScriptDnSa_ren_gong/modMain.py @@ -0,0 +1,8 @@ +# coding=utf-8 +from BlueFoxTools.ModLoader import * + +if not serverApi.IsInServer(): + import System.Client +import System.Server + +LoadMod() diff --git a/behavior_pack_kcH4M5u0/entities/.keep b/behavior_pack_kcH4M5u0/entities/.keep new file mode 100644 index 0000000..e69de29