From e6abdcc0f860608a35a631e00fcecd3b814c3f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A5=BF=E7=93=9C=C2=B0?= Date: Mon, 10 Aug 2026 02:10:51 +0800 Subject: [PATCH] =?UTF-8?q?new:=E6=B7=BB=E5=8A=A0=E7=BC=94=E9=80=A0?= =?UTF-8?q?=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/core/basic/character/info.py | 1 + backend/core/character/GF/extra/creator.py | 95 ++++-- .../core/character/GF/extra/dark_knight.py | 81 ++--- backend/core/character/adventure.py | 2 +- frontend/src/api/info/type.ts | 1 + .../SkillTree/img/extra_awake_85.png | Bin 0 -> 2723 bytes .../Skill/components/SkillTree/index.tsx | 278 ++++++++++-------- .../Skill/components/SkillTree/style.scss | 5 + frontend/src/pages/Character/Skill/index.vue | 1 + 9 files changed, 272 insertions(+), 192 deletions(-) create mode 100644 frontend/src/pages/Character/Skill/components/SkillTree/img/extra_awake_85.png diff --git a/backend/core/basic/character/info.py b/backend/core/basic/character/info.py index fa00572f..49f5e446 100644 --- a/backend/core/basic/character/info.py +++ b/backend/core/basic/character/info.py @@ -38,6 +38,7 @@ class CharacterInfoMixin(CharacterBase): 'maxLearnLv': skill.calculate_lv(), 'maxLv': skill.maxLv, 'position': skill.position, + 'cellPosition': skill.cellPos, 'line': skill.line, 'bind': skill.bind, 'hasVP': skill.hasVP, diff --git a/backend/core/character/GF/extra/creator.py b/backend/core/character/GF/extra/creator.py index b3ae03f9..00ab057a 100644 --- a/backend/core/character/GF/extra/creator.py +++ b/backend/core/character/GF/extra/creator.py @@ -5,10 +5,20 @@ from core.basic.character import Character prefix = "extra/creator/cn/skillDetail" +class CreatorActiveSkill(ActiveSkill): + """缔造者技能按对应系列的满能量计算伤害。""" + + energyCost = 100 + totalEnergy = 100 + + def getSkillData(self, lv: int = 0): + return super().getSkillData(lv) * self.totalEnergy / self.energyCost + + # 烈火燎原 # extra/creator/527cdc3ecca985e18ef819d456532b26 # b522a95d819a5559b775deb9a490e49a/527cdc3ecca985e18ef819d456532b26 -class Skill0(PassiveSkill): +class Skill0(CreatorActiveSkill): """ 从地表生成火焰攻击敌人。\n 按住鼠标左键的状态下拖动鼠标,即可根据鼠标移动轨迹生成火墙。\n @@ -31,6 +41,8 @@ class Skill0(PassiveSkill): # 火焰攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + energyCost = 4 + totalEnergy = 140 # 能量消耗值 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) # [范围信息] @@ -40,7 +52,7 @@ class Skill0(PassiveSkill): # 炽炎星陨 # extra/creator/902a016f6978f13740f237e4740a5646 # b522a95d819a5559b775deb9a490e49a/902a016f6978f13740f237e4740a5646 -class Skill1(PassiveSkill): +class Skill1(CreatorActiveSkill): """ 召唤燃烧烈焰的陨石攻击敌人。\n 陨石将掉落在鼠标点击的位置。\n @@ -63,6 +75,8 @@ class Skill1(PassiveSkill): # 陨石攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + energyCost = 20 + totalEnergy = 140 # 能量消耗值 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) # [范围信息] @@ -201,7 +215,7 @@ class Skill6(PassiveSkill): # 冰霜之球 # extra/creator/370586038cf40378f20d338d507a780c # b522a95d819a5559b775deb9a490e49a/370586038cf40378f20d338d507a780c -class Skill7(PassiveSkill): +class Skill7(CreatorActiveSkill): """ 生成冰霜之球,按角色面对方向滚向敌人。\n 冰球在鼠标点击的位置生成。\n @@ -224,6 +238,8 @@ class Skill7(PassiveSkill): # 冰球攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 6 + energyCost = 20 + totalEnergy = 140 # 冰球多段攻击次数 : {value1}次 data1 = get_data(f'{prefix}/{uuid}', 1) # 能量消耗值 : {value2} @@ -235,7 +251,7 @@ class Skill7(PassiveSkill): # 冰天震地 # extra/creator/fa3ee243b36699e9d3fc34328adf6417 # b522a95d819a5559b775deb9a490e49a/fa3ee243b36699e9d3fc34328adf6417 -class Skill8(PassiveSkill): +class Skill8(CreatorActiveSkill): """ 生成巨大的冰盘砸向敌人。\n 冰盘将砸落在鼠标点击的位置。\n @@ -258,6 +274,8 @@ class Skill8(PassiveSkill): # 冰盘攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + energyCost = 40 + totalEnergy = 140 # 能量消耗值 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) # [范围信息] @@ -523,7 +541,7 @@ class Skill17(PassiveSkill): # 烈焰飓风 # extra/creator/e788de1a4498c99fcc790302c4d41fed # b522a95d819a5559b775deb9a490e49a/e788de1a4498c99fcc790302c4d41fed -class Skill18(PassiveSkill): +class Skill18(CreatorActiveSkill): """ 以自身为中心,生成烈焰飓风。\n 施放过程中可以自由移动。 按跳跃键时,可以强制中断该技能。\n @@ -548,6 +566,8 @@ class Skill18(PassiveSkill): # 烈焰飓风攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 12 + energyCost = 60 + totalEnergy = 140 # 烈焰飓风多段攻击次数 : {value1}次 data1 = get_data(f'{prefix}/{uuid}', 1) # 能量消耗值: {value2} @@ -578,7 +598,7 @@ class Skill18(PassiveSkill): # 极冰护盾 # extra/creator/dd32a9825ec1af42a91f2223be6658e5 # b522a95d819a5559b775deb9a490e49a/dd32a9825ec1af42a91f2223be6658e5 -class Skill19(PassiveSkill): +class Skill19(CreatorActiveSkill): """ 生成极冰护盾。\n 生成时,攻击周围敌人并击退敌人。\n @@ -608,6 +628,8 @@ class Skill19(PassiveSkill): # 护盾攻击力 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) hit2 = 1 + energyCost = 60 + totalEnergy = 140 # 推开距离 : {value3}px data3 = get_data(f'{prefix}/{uuid}', 3) # 能量消耗值 : {value4} @@ -626,7 +648,7 @@ class Skill19(PassiveSkill): 总攻击力 +133%\n 护盾大小 +40%\n """ - ... + self.energyCost = 140 def vp_2(self): """ @@ -694,7 +716,7 @@ class Skill21(ActiveSkill): # 超能旋风波 # extra/creator/5197b141332a65a89d452adf227c2f30 # b522a95d819a5559b775deb9a490e49a/5197b141332a65a89d452adf227c2f30 -class Skill22(PassiveSkill): +class Skill22(CreatorActiveSkill): """ 聚集超能旋风的力量,向前方快速发射旋风波。\n 按住左键的状态下拖动鼠标,即可向指定方向发射旋风波。\n @@ -719,6 +741,8 @@ class Skill22(PassiveSkill): # 旋风波攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + energyCost = 4 + totalEnergy = 140 # 能量消耗值 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) # [范围信息] @@ -742,12 +766,12 @@ class Skill22(PassiveSkill): - 总攻击力相同\n - 不适用[创世]效果\n """ - ... + self.energyCost = 35 # 风暴漩涡 # extra/creator/75747c67e14eec6088ce67f63ea41628 # b522a95d819a5559b775deb9a490e49a/75747c67e14eec6088ce67f63ea41628 -class Skill23(PassiveSkill): +class Skill23(CreatorActiveSkill): """ 生成风暴。\n 可将敌人吸附到风暴中心。\n @@ -773,6 +797,8 @@ class Skill23(PassiveSkill): # 风暴攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + energyCost = 20 + totalEnergy = 140 # 能量消耗值 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) # [范围信息] @@ -798,7 +824,7 @@ class Skill23(PassiveSkill): - 总攻击力相同\n - 不适用[创世]效果\n """ - ... + self.energyCost = 70 # 风暴 # extra/creator/d204ec03a0aed2f20211fb6ccc48d46d @@ -915,7 +941,7 @@ class Skill27(ActiveSkill): # 末日虫洞 # extra/creator/dec8961c485edb02036ba00c789010f0 # b522a95d819a5559b775deb9a490e49a/dec8961c485edb02036ba00c789010f0 -class Skill28(PassiveSkill): +class Skill28(CreatorActiveSkill): """ 生成虫洞,连接阿拉德最后的时空。\n 在鼠标点击位置生成虫洞。\n @@ -947,6 +973,8 @@ class Skill28(PassiveSkill): # 虫洞大爆炸攻击力 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) hit2 = 1 + energyCost = 100 + totalEnergy = 100 # 能量消耗值 : {value3} data3 = get_data(f'{prefix}/{uuid}', 3) # [范围信息] @@ -960,7 +988,7 @@ class Skill28(PassiveSkill): 单次攻击力 -50%\n 施放时强力吸附范围内敌人\n """ - ... + self.energyCost = 50 def vp_2(self): """ @@ -1002,7 +1030,7 @@ class Skill29(ActiveSkill): # 冰雪降临 # extra/creator/d8ff976e2aaa4720272a5175d1eb9126 # b522a95d819a5559b775deb9a490e49a/d8ff976e2aaa4720272a5175d1eb9126 -class Skill30(PassiveSkill): +class Skill30(CreatorActiveSkill): """ 重现冰河时代的酷寒地带。\n 使领域内的敌人进行减速状态。\n @@ -1030,6 +1058,8 @@ class Skill30(PassiveSkill): # 冰河地带攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 21 + energyCost = 50 + totalEnergy = 100 # 冰河地带多段攻击次数 : {value1}次 data1 = get_data(f'{prefix}/{uuid}', 1) # 不同范围的攻击力增加率 : 0%~{value2}% @@ -1059,7 +1089,8 @@ class Skill30(PassiveSkill): - 总攻击力相同\n 不适用不同范围的攻击力增加率效果\n """ - ... + self.hit0 = 1 + self.energyCost = 2 def vp_2(self): """ @@ -1099,7 +1130,7 @@ class Skill31(ActiveSkill): # 时空链接 # extra/creator/394e2f09c1f37ae61d831d8567707170 # b522a95d819a5559b775deb9a490e49a/394e2f09c1f37ae61d831d8567707170 -class Skill32(PassiveSkill): +class Skill32(CreatorActiveSkill): """ 在空地点击鼠标左键可以召唤时间妖精“卡巴拉”实现连接。\n 点击右键会跟被连接的角色以红色实线连接。\n @@ -1126,6 +1157,8 @@ class Skill32(PassiveSkill): # 红色实线攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 35 + energyCost = 50 + totalEnergy = 100 # 红色实线多段攻击次数 : {value1}次 data1 = get_data(f'{prefix}/{uuid}', 1) # [时空链接]连接线持续时间 : {value2}秒 @@ -1159,7 +1192,8 @@ class Skill32(PassiveSkill): - 连接2名队友时,所受伤害 -10%\n - 连接3名队友时,所受伤害 -15%\n """ - ... + self.hit0 = 70 + self.energyCost = 100 # 创世之力 # extra/creator/9376d04c476cd41d60ed1974ca69ab95 @@ -1217,7 +1251,7 @@ class Skill34(ActiveSkill): # 时空禁制 # extra/creator/bae12a6dc7d22a5cf149673e88ddda28 # b522a95d819a5559b775deb9a490e49a/bae12a6dc7d22a5cf149673e88ddda28 -class Skill35(PassiveSkill): +class Skill35(CreatorActiveSkill): """ 通过时间妖精“卡巴拉”之力,使时间加速,限制周围目标行动。\n 在鼠标左键点击的位置召唤“卡巴拉”,在持续时间内强制控制敌人,最后随着爆炸消失。\n @@ -1247,6 +1281,8 @@ class Skill35(PassiveSkill): # 卡巴拉爆炸攻击力 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) hit2 = 1 + energyCost = 100 + totalEnergy = 100 # 卡巴拉召唤持续时间 : {value3}秒 data3 = get_data(f'{prefix}/{uuid}', 3) # 能量消耗值 : {value4} @@ -1366,7 +1402,7 @@ class Skill37(PassiveSkill): # 生命之树 # extra/creator/66a9967e677651d0def34c475795ccde # b522a95d819a5559b775deb9a490e49a/66a9967e677651d0def34c475795ccde -class Skill38(PassiveSkill): +class Skill38(CreatorActiveSkill): """ 召唤蕴含创世之力的卡巴拉,创造出长有烈炎、 寒冰、 风暴之果实的生命之树并使其爆炸。\n 可以通过按住鼠标左键拖拽或右键点击,使元素果实向指定的位置投掷或爆炸。\n @@ -1392,10 +1428,13 @@ class Skill38(PassiveSkill): # 每个元素果实爆炸攻击力 : {value0} data0 = get_data(f'{prefix}/{uuid}', 0) - hit0 = 0 + hit0 = 6 + power0 = 1 / 1.07 #TODO:CHECK # 元素果实同时爆炸攻击力 : {value1} X 剩余果实数 data1 = get_data(f'{prefix}/{uuid}', 1) - hit1 = 6 + hit1 = 0 + energyCost = 100 + totalEnergy = 100 # 能量消耗值 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) @@ -1403,11 +1442,13 @@ class Skill38(PassiveSkill): def setMode(self, mode): if mode == '秒炸': - self.hit0 = 0 - self.hit1 = 6 + self.hit0 = 6 + self.hit1 = 0 + self.power0 = 0.9 #TODO:CHECK elif mode == '常规': self.hit0 = 6 self.hit1 = 0 + self.power0 = 1 / 1.07 #TODO:CHECK # 天工开物 # extra/creator/8eeca98333667959c403104f6d24da34 @@ -1472,7 +1513,7 @@ class Skill40(ActiveSkill): # 创世之光 # extra/creator/bcbaa4338139bc9342c8a8415dfbe39a # b522a95d819a5559b775deb9a490e49a/bcbaa4338139bc9342c8a8415dfbe39a -class Skill41(PassiveSkill): +class Skill41(CreatorActiveSkill): """ 发动创世神器的力量,生成与太初时间相连的魔法门。\n 魔法门持续期间内输入创生系列技能时,注入创世能量,立即引发爆炸。\n @@ -1500,6 +1541,8 @@ class Skill41(PassiveSkill): # 太初之光爆炸攻击力 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) hit1 = 1 + energyCost = 100 + totalEnergy = 100 # 能量消耗量 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) @@ -1557,7 +1600,7 @@ class Skill43(ActiveSkill): # 时之终末 : 归烬 # extra/creator/03bd172c9c50cfdcf9d7e83a3ef82842 # b522a95d819a5559b775deb9a490e49a/03bd172c9c50cfdcf9d7e83a3ef82842 -class Skill44(PassiveSkill): +class Skill44(CreatorActiveSkill): """ 展开创世魔法书,对时间进行干涉,移动到时之终末。\n 卡巴拉变成巨型钟表指向12点时,次元爆炸,湮灭敌人。\n @@ -1589,6 +1632,8 @@ class Skill44(PassiveSkill): # 次元爆炸攻击力 : {value2} data2 = get_data(f'{prefix}/{uuid}', 2) hit2 = 1 + energyCost = 100 + totalEnergy = 100 # 能量消耗量 : {value3} data3 = get_data(f'{prefix}/{uuid}', 3) diff --git a/backend/core/character/GF/extra/dark_knight.py b/backend/core/character/GF/extra/dark_knight.py index 9feb06e6..293722bd 100644 --- a/backend/core/character/GF/extra/dark_knight.py +++ b/backend/core/character/GF/extra/dark_knight.py @@ -5,6 +5,19 @@ from core.basic.character import Character prefix = "extra/dark_knight/cn/skillDetail" +def apply_weapon_mastery(skill: PassiveSkill, old: int, new: int, weapon_type: str): + weapon = skill.char.GetWeaponType() + if weapon[0] != weapon_type: + return None + + physical = skill.char.输出类型.startswith('物理') + inherited = '传世武器' in weapon[1] + data_index = (3 if physical else 4) if inherited else (0 if physical else 1) + attack_type = '$*PAtkP' if physical else '$*PAtkM' + skill.associate = [{'type': attack_type, 'data': getattr(skill, f'data{data_index}')}] + return PassiveSkill.effect(skill, old, new) + + # 暗影斩 # extra/dark_knight/eb71e1d82d92c7e1d40500a0dcd77aa6 # 17e417b31686389eebff6d754c3401ea/eb71e1d82d92c7e1d40500a0dcd77aa6 @@ -174,6 +187,8 @@ class Skill5(PassiveSkill): # 跳跃攻击力变化率 : {value2}% data2 = get_data(f'{prefix}/{uuid}', 2) + associate = [{'type': '*skillDamage', 'data': [i - 100 if i > 0 else 0 for i in data0], 'skills': ['挑击']}] + # 受身蹲伏 # extra/dark_knight/ce26c6b69d02a440a81b552bec94f03b # 17e417b31686389eebff6d754c3401ea/ce26c6b69d02a440a81b552bec94f03b @@ -630,14 +645,7 @@ class Skill19(PassiveSkill): data5 = get_data(f'{prefix}/{uuid}', 5) def effect(self, old, new): - weapon = self.char.GetWeaponType() - if weapon[0] != '光剑': - return None - data = self.data0 - if '传世武器' in weapon[1]: - data = self.data3 - self.associate = [{'type': '$*PAtkP', 'data': data}] - return super().effect(old, new) + return apply_weapon_mastery(self, old, new, '光剑') # 暗 · 短剑精通 # extra/dark_knight/01c3a2fb793d293a25ed8dc7a0d70c1a @@ -676,14 +684,7 @@ class Skill20(PassiveSkill): data5 = get_data(f'{prefix}/{uuid}', 5) def effect(self, old, new): - weapon = self.char.GetWeaponType() - if weapon[0] != '短剑': - return None - data = self.data0 - if '传世武器' in weapon[1]: - data = self.data3 - self.associate = [{'type': '$*PAtkP', 'data': data}] - return super().effect(old, new) + return apply_weapon_mastery(self, old, new, '短剑') # 暗 · 太刀精通 # extra/dark_knight/8f73f243041c2d27739fe7696f02bf9b @@ -722,14 +723,7 @@ class Skill21(PassiveSkill): data5 = get_data(f'{prefix}/{uuid}', 5) def effect(self, old, new): - weapon = self.char.GetWeaponType() - if weapon[0] != '太刀': - return None - data = self.data0 - if '传世武器' in weapon[1]: - data = self.data3 - self.associate = [{'type': '$*PAtkP', 'data': data}] - return super().effect(old, new) + return apply_weapon_mastery(self, old, new, '太刀') # 暗 · 巨剑精通 # extra/dark_knight/8c2379737c5acc935c1731f67f607655 @@ -768,14 +762,7 @@ class Skill22(PassiveSkill): data5 = get_data(f'{prefix}/{uuid}', 5) def effect(self, old, new): - weapon = self.char.GetWeaponType() - if weapon[0] != '巨剑': - return None - data = self.data0 - if '传世武器' in weapon[1]: - data = self.data3 - self.associate = [{'type': '$*PAtkP', 'data': data}] - return super().effect(old, new) + return apply_weapon_mastery(self, old, new, '巨剑') # 暗 · 钝器精通 # extra/dark_knight/9cb6f9ed646fa87f9b7680a42ce83d1a @@ -814,14 +801,7 @@ class Skill23(PassiveSkill): data5 = get_data(f'{prefix}/{uuid}', 5) def effect(self, old, new): - weapon = self.char.GetWeaponType() - if weapon[0] != '钝器': - return None - data = self.data0 - if '传世武器' in weapon[1]: - data = self.data3 - self.associate = [{'type': '$*PAtkP', 'data': data}] - return super().effect(old, new) + return apply_weapon_mastery(self, old, new, '钝器') # 暗劲爆发 # extra/dark_knight/506e7ed77d517419a6e1c437a2cedb17 @@ -1535,7 +1515,8 @@ class Skill41(ActiveSkill): 斩击命中后,将瞬移到命中敌人中最强敌人的后方发动下一次斩击\n 施放动作赋予无敌状态\n """ - ... + self.hit0 = 8 + self.power0 = 13 / 8 #TODO:CHECK def vp_2(self): """ @@ -1592,7 +1573,7 @@ class Skill42(ActiveSkill): - 中断时,适用[时间停滞]能量5阶段的攻击力,且不消耗[时间停滞]能量\n - 暗黑波动旋转速度 +100%\n """ - ... + self.skillDamage *= 2 def vp_2(self): """ @@ -1721,9 +1702,9 @@ class Skill45(ActiveSkill): data0 = get_data(f'{prefix}/{uuid}', 0) # 多段攻击次数 : {value1} data1 = get_data(f'{prefix}/{uuid}', 1) - hit1 = 33 # 多段攻击攻击力 : {value2}% data2 = get_data(f'{prefix}/{uuid}', 2) + hit2 = 33 # 最后一击爆炸攻击力 : {value3}% data3 = get_data(f'{prefix}/{uuid}', 3) hit3 = 1 @@ -1738,7 +1719,8 @@ class Skill45(ActiveSkill): - 总攻击力相同\n 幽魂剑范围 +30%\n """ - ... + self.hit2 = 6 + self.power2 = 33 / 6 #TODO:CHECK def vp_2(self): """ @@ -1966,7 +1948,7 @@ class Skill50(ActiveSkill): - 适用最高连击阶段的攻击力\n - 不恢复[时间停滞]连击能量\n """ - ... + self.skillDamage *= 2 # 超时空碎裂 # extra/dark_knight/d204ec03a0aed2f20211fb6ccc48d46d @@ -2130,6 +2112,7 @@ class Skill55(ActiveSkill): # 次元斩击攻击力 : {value0}% data0 = get_data(f'{prefix}/{uuid}', 0) hit0 = 1 + skillDamage = 2 class classChange(Character): def __init__(self, equVersion): @@ -2143,10 +2126,10 @@ class classChange(Character): self.jobGrowId = '0a49d9c8b5e1358efff324e5cb11d41e' self.武器选项 = ['光剑', '太刀', '钝器', '巨剑', '短剑'] # TODO - self.输出类型选项 = [] # TODO - self.输出类型 = '' # TODO - self.防具精通属性 = [''] # TODO - self.防具类型 = '' + self.输出类型选项 = ['物理百分比', '魔法百分比'] + self.输出类型 = '物理百分比' + self.防具精通属性 = ['力量', '智力'] + self.防具类型 = '轻甲' self.buff = 1.598 # TODO super().__init__(equVersion, __name__) diff --git a/backend/core/character/adventure.py b/backend/core/character/adventure.py index 2cc5f70f..0e89ed21 100644 --- a/backend/core/character/adventure.py +++ b/backend/core/character/adventure.py @@ -220,7 +220,7 @@ adv = [ 'name': 'extra', 'title': '外传', 'children': [ - {'id': 0, 'name': 'creator', 'title': '知源·缔造者', 'open': False}, + {'id': 0, 'name': 'creator', 'title': '知源·缔造者', 'open': True, 'class': 'GF.extra.creator'}, {'id': 1, 'name': 'dark_knight', 'title': '极诣·黑暗武士', 'open': False}, {'id': 2, 'name': 'empty', 'title': ''}, {'id': 3, 'name': 'empty', 'title': ''}, diff --git a/frontend/src/api/info/type.ts b/frontend/src/api/info/type.ts index 0659c95b..a0962a81 100644 --- a/frontend/src/api/info/type.ts +++ b/frontend/src/api/info/type.ts @@ -108,6 +108,7 @@ export interface ISkill { name: string desc: string }[] + cellPosition: number[] uuid: string upType: 'damage' | 'heal' } diff --git a/frontend/src/pages/Character/Skill/components/SkillTree/img/extra_awake_85.png b/frontend/src/pages/Character/Skill/components/SkillTree/img/extra_awake_85.png new file mode 100644 index 0000000000000000000000000000000000000000..8aafcaf62330879947f811030b069e9319a1fb15 GIT binary patch literal 2723 zcmY*b3pkT)AAdx2FjLNlg7XSbt2YYLb zV2u=*@eWbJ`^U@Sc)=nRfw4OZRQD-Q3j&b0GOK7sdz##iKgmD3J44})0%I2tfd-A zG}Af`N9&`hRwQzu{h4qQ_Kb@=;Y=_ALDVuwfp(eFkpct?iH29DQ$j)`kaRP(zk!hg ze=7}DLz#vX{gD`J+ut36$V`n)qfwDiXmoV6UNlTEEIa^efIuLi`i4+LLtR0KZbVEd z4Nun%jX3Zdz?u|62oI#v0>eU8w*c{eVUaX5waCaoBGMo45BDPy;ktf!7*W^2fQZ+{ z6JZ9rL>P>C7;bRH(BHsM?KeFw(Eq>up%K4j5C{U@sz43&^tVRd@{z9Lfh55cTa+jR z)4%KgmotTK4e~#Oq5hK*e5=6wN0}{|{!y0{Dv&f7U^Ant< z15}!Y`_maPK#O!XC(KBxJ4H~unekElDt&6^A{ARpf zy)@gAeZm=Ag=QA*yqC(9ii;pyv7NO=M?E9Lmn&l)* zgxs7bgf6IeDp_7}+AmyOxrQv;;KV0kzOgR;P%C8k+I{luBy-1S_ZaZ}{fX}t{#Ba~*$4jFHXYprcR`%^> zmlJi7C7nrAz|>m@$>6-8OrwmhQrD8bI_Nlya3N))JnwbeEuHSZsMtHhTaJ9P8PTw) z%g?epo^${&jg1gehCqEhV;Vl3U&Ki8^Ww_NW-V+eV!XrrW4_&ALK?*YovWc$EHvnn zTc;9x)NA&s=kdQ7&5;VElRmlqBQ0Fe$%@%oH`3WJzk9Yn%t9R*r{XdwY-3KMt$(9Db3g zzA03bAx;a`!r3+#x4%YpAO_%y+@oKTL%={6W>2|WW?7+Xcy;+ z&#u9E^=taCr=L{nqHt}}`K_V=s!(nsOPBG}=7%z`6}KY*y}#R0`SVRDqiM|yo*hFw zs1hI1|7OtU;_V13`2uC7s#MM2#6<6TLEqwL$E^)4OG(D#6BKMIsS2^MW8w7r?e3bP zoPFbJahpR{=ZACLys_K&x`_;zW~IL-OHLcBG}efo0$tnYue}(W01l4dMf+8x+_^mp zu)rs#em(}6CehWjL^2L;%W!aiX1l}dw4z* zCwFR@Cgs%z;8B_4w8P=-lEFN&){%&%f!e_*ZRum*!lIXb6ZmsC zYvtyd>1MIeNG_+PyyVA*T+$&vROI;HB(U+L;gk-Ua&n;8#SR&#Op3JJI{|-5ld9C* zBa~zkcpW<0`|U9m7G~WPqO?a(4hQn#P~kMv(^{-$4Q_agM=D?rH^9BV>jc4;gnpi{+h9d580by7jwIeV`A}ZVO})`|FmR$!$&zLLC!9o-$M-N z55qCI)q@HX3vb@3k0};D0}D3w&5M0qwOZ!y;qeI|gzHD8v_W37OPNt*fh4eq9tQy< zLajolR)2j>gvneze=K-&p_M4}ShudklVjM-gpy6D6rN}%bmND%G*ey=SyIg zh{*C7L!ZGH^5CwCGx-uX3zFFD@c~Cw^bb41ltPf+rAAT7rw0)?*5h4m`0>-1ay!M* zpXPCL7du7VUe!pI>eOVbGHps*lRNEMn%7eqirI5)&62+l`7Af4%6)H7*~ORHhqxPynLQaOL>q3_T=WT*GH@z^bym7W+;Tg^=8-16Zj zonaJ2^w$~E7XoWc3`EKM@USKr{=&PWZa?*cre2vZD7Hdy^T2C@N&kX)a5R2+?*Sfl zSK=r3u{3gwe4~c!Z+*@OZVN7npL-{_Py9S}2i!h6hpB$JggaM+JXc?Q#-tHj*kG}| z!GhGR9N73aEH%0xy#8Xxjr+XSM=!~@wtw8OH8nD_X^?~~2+;<9%DL;t+8T&$qPPAO N9c)~ztB;;a`Zq^!=5hc4 literal 0 HcmV?d00001 diff --git a/frontend/src/pages/Character/Skill/components/SkillTree/index.tsx b/frontend/src/pages/Character/Skill/components/SkillTree/index.tsx index 16696ad1..dfb0a842 100644 --- a/frontend/src/pages/Character/Skill/components/SkillTree/index.tsx +++ b/frontend/src/pages/Character/Skill/components/SkillTree/index.tsx @@ -1,9 +1,13 @@ -import { getImageURL, getLocalImageURL } from '@/utils/images' +import { getImageURL } from '@/utils/images' import type { ISkill } from '@/api/info/type' import { useVModel } from '@vueuse/core' import CalcButton from '@/components/calc/button/index.vue' import './style.scss' +const rowPrefixes = ['通用', 1, '', 10, '', 20, '', 30, '', 40, 50, 60, 70, 80, 90, 100] +const defaultColumnCount = 11 +const altersWithoutRowPrefix = new Set(['creator', 'dark_knight']) + export default defineComponent({ name: 'SkillTree', props: { @@ -19,34 +23,73 @@ export default defineComponent({ type: Number, default: () => 50, }, + alter: { + type: String, + default: '', + }, }, emits: ['chooseSkill'], - setup(props,{ emit }) { + setup(props, { emit }) { const skills = computed(() => props.skills) const lvInfo = useVModel(props, 'lvInfo') const bindAwake = useVModel(props, 'bindAwake') // const emit = defineEmits(['chooseSkill']) - const getSkill = (lvStart: number, lvEnd: number, position: number) => { - if (position >= 0) - return skills.value.find( - (item) => - item.line > lvStart - 5 && - item.line <= Math.max(lvEnd - 5, 1) && - item.position == position, - ) - else - return skills.value.filter( - (item) => item.line > lvStart - 5 && item.line <= Math.max(lvEnd - 5, 1), - ) + const skillGrid = computed(() => { + const grid = new Map() + const occupiedRows = new Set() + let maxColumn = defaultColumnCount - 1 + + skills.value.forEach((skill) => { + const [column, row] = skill.cellPosition ?? [] + if (!Number.isInteger(column) || !Number.isInteger(row) || column < 0 || row < 0) return + + grid.set(`${row}:${column}`, skill) + occupiedRows.add(row) + maxColumn = Math.max(maxColumn, column) + }) + + return { + grid, + columns: Array.from({ length: maxColumn + 1 }, (_, index) => index), + rows: Array.from(occupiedRows).sort((left, right) => left - right), + } + }) + + const showRowPrefix = computed(() => !altersWithoutRowPrefix.has(props.alter)) + const isExtraAlter = computed(() => altersWithoutRowPrefix.has(props.alter)) + + const getRowPrefix = (row: number) => { + const lastRow = skillGrid.value.rows.at(-1) ?? -1 + if (lastRow >= rowPrefixes.length && row >= rowPrefixes.length - 1) + return row === lastRow ? rowPrefixes.at(-1) : '' + + return rowPrefixes[row] } - const lvList = [15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100] - - const columns = [...new Array(11).keys()] - const activeSkill = ref(-1) + const getLastPositionedSkill = (learnLv: number) => + skills.value + .filter((skill) => skill.learnLv == learnLv && skill.type == 'active' && skill.bind) + .sort((left, right) => { + const [leftColumn = -1, leftRow = -1] = left.cellPosition ?? [] + const [rightColumn = -1, rightRow = -1] = right.cellPosition ?? [] + return leftRow - rightRow || leftColumn - rightColumn + }) + .at(-1) + + const extraAwakeSkill_2 = computed(() => + props.alter == 'creator' + ? skills.value.find((skill) => skill.learnLv == 85 && skill.name == '生命之树') + : getLastPositionedSkill(85), + ) + const extraAwakeSkill_3 = computed(() => getLastPositionedSkill(100)) + + watchEffect(() => { + if (isExtraAlter.value && bindAwake.value != 85) bindAwake.value = 85 + }) + const chooseSkill = (skill?: ISkill, operation: string = 'add') => { if (!skill) { activeSkill.value = -1 @@ -66,19 +109,25 @@ export default defineComponent({ const actionSkillLv = (skill: ISkill, operation: string) => { if (operation == 'add') { - lvInfo.value[skill.id.toString()].lv = Math.min(lvInfo.value[skill.id.toString()]?.lv + 1, skill.maxLv) + lvInfo.value[skill.id.toString()].lv = Math.min( + lvInfo.value[skill.id.toString()]?.lv + 1, + skill.maxLv, + ) return } if (operation == 'addMax') { - lvInfo.value[skill.id.toString()].lv = skill.maxLearnLv + lvInfo.value[skill.id.toString()].lv = skill.maxLearnLv return } if (operation == 'sub') { - lvInfo.value[skill.id.toString()].lv = Math.max(lvInfo.value[skill.id.toString()]?.lv - 1, 0) + lvInfo.value[skill.id.toString()].lv = Math.max( + lvInfo.value[skill.id.toString()]?.lv - 1, + 0, + ) return } if (operation == 'subMax') { - lvInfo.value[skill.id.toString()] = { lv: 0 , vp: 0, up: 0 } + lvInfo.value[skill.id.toString()] = { lv: 0, vp: 0, up: 0 } return } emit('chooseSkill', skill.id) @@ -88,78 +137,86 @@ export default defineComponent({ bindAwake.value = lv } - const renderSkill = (skill: ISkill, index: number) => { + const hasAwakeBinding = (skill: ISkill) => + isExtraAlter.value + ? skill.id == extraAwakeSkill_3.value?.id + : skill.learnLv == 100 && skill.bind + + const renderExtraAwakeBinding = () => ( +
+
+ +
2次
+
+
+ ) + + const renderSkill = (skill: ISkill) => { const info = lvInfo.value?.[skill.id.toString()] let color = 'white' if (info?.lv < skill.maxLearnLv) color = '#32E432' if (info?.lv > skill.maxLearnLv) color = '#58E0FC' return ( - <> -
-
- {info?.vp > 0 && (
)} - {info?.up > 0 && (
)} -
- {activeSkill.value == skill.id && renderSkillAction(skill)} -
{ - e.stopPropagation() - chooseSkill(skill) - }} - onContextmenu={(e) => { - e.stopPropagation() - e.preventDefault() - chooseSkill(skill, 'sub') - }} - key={index} - > - {skill.name} -
- {info?.lv ?? 0} -
-
- {skill.learnLv == 100 && skill.bind && ( - <> -
-
chooseBindAwake(50)} - > - -
- 1次 -
-
-
chooseBindAwake(85)} - > - -
- 2次 -
-
-
- - )} +
+
+ {info?.vp > 0 &&
} + {info?.up > 0 &&
}
- + {activeSkill.value == skill.id && renderSkillAction(skill)} +
{ + e.stopPropagation() + chooseSkill(skill) + }} + onContextmenu={(e) => { + e.stopPropagation() + e.preventDefault() + chooseSkill(skill, 'sub') + }} + > + {skill.name} +
+ {info?.lv ?? 0} +
+
+ {hasAwakeBinding(skill) && + (isExtraAlter.value ? ( + renderExtraAwakeBinding() + ) : ( +
+
chooseBindAwake(50)} + > + +
1次
+
+
chooseBindAwake(85)} + > + +
2次
+
+
+ ))} +
) } @@ -236,37 +293,24 @@ export default defineComponent({ return () => ( <>
(activeSkill.value = -1)}> - {((getSkill(0, 15, -1) ?? []) as ISkill[]).length > 0 && ( - <> -
-
- 其
余 + {skillGrid.value.rows.map((row) => ( +
+ {showRowPrefix.value && ( +
+ {getRowPrefix(row)}
- {((getSkill(0, 15, -1) ?? []) as ISkill[])?.map((skill, index) => - renderSkill(skill, index), - )} -
- - )} - - {lvList.map((lv, index) => - ((getSkill(lv, lvList[index + 1] ?? 150, -1) as ISkill[])?.length ?? 0) > 0 ? ( -
- {lv == 1 || lv % 10 == 0 ? ( -
{lv}
+ )} + {skillGrid.value.columns.map((column) => { + const skill = skillGrid.value.grid.get(`${row}:${column}`) + return skill ? ( + renderSkill(skill) ) : ( -
- )} - {columns.map((column, i) => { - const skill = getSkill(lv, lvList[index + 1] ?? 150, column) as ISkill - return skill ? renderSkill(skill, index) :
- })} -
(activeSkill.value = -1)}>
-
- ) : ( - <> - ), - )} +
+ ) + })} +
(activeSkill.value = -1)}>
+
+ ))}
) diff --git a/frontend/src/pages/Character/Skill/components/SkillTree/style.scss b/frontend/src/pages/Character/Skill/components/SkillTree/style.scss index 907a8284..644fa3a7 100644 --- a/frontend/src/pages/Character/Skill/components/SkillTree/style.scss +++ b/frontend/src/pages/Character/Skill/components/SkillTree/style.scss @@ -34,6 +34,11 @@ background-size: 100% 100%; } +.extra-awake-85 { + background-image: url('./img/extra_awake_85.png'); + background-size: 100% 100%; +} + .vp_1{ width: 18px; height: 18px; diff --git a/frontend/src/pages/Character/Skill/index.vue b/frontend/src/pages/Character/Skill/index.vue index 68802ec6..0b174f0c 100644 --- a/frontend/src/pages/Character/Skill/index.vue +++ b/frontend/src/pages/Character/Skill/index.vue @@ -22,6 +22,7 @@