From 97c8eea8218f06aae28640bc235587e3d3f2e6a4 Mon Sep 17 00:00:00 2001 From: omg-xtao <100690902+omg-xtao@users.noreply.github.com> Date: Tue, 24 Oct 2023 01:00:32 +0800 Subject: [PATCH] feat: support edit damage rule --- GenshinDamageRule.json | 270 +++++++++++----------- appveyor.yml | 14 +- main.py | 5 +- requirements.txt | 3 + src/choose.py | 11 +- src/damage/__init__.py | 0 src/damage/artifact.py | 30 +++ src/damage/assets.py | 4 + src/damage/character.py | 56 +++++ src/damage/data.py | 232 +++++++++++++++++++ src/damage/models.py | 157 +++++++++++++ src/damage/page.py | 496 ++++++++++++++++++++++++++++++++++++++++ src/damage/weapon.py | 28 +++ src/data.py | 4 +- 14 files changed, 1167 insertions(+), 143 deletions(-) create mode 100644 src/damage/__init__.py create mode 100644 src/damage/artifact.py create mode 100644 src/damage/assets.py create mode 100644 src/damage/character.py create mode 100644 src/damage/data.py create mode 100644 src/damage/models.py create mode 100644 src/damage/page.py create mode 100644 src/damage/weapon.py diff --git a/GenshinDamageRule.json b/GenshinDamageRule.json index b318445..cb229f8 100644 --- a/GenshinDamageRule.json +++ b/GenshinDamageRule.json @@ -1,137 +1,141 @@ { - "胡桃": { - "skills": [ - { - "name": "普通攻击·往生秘传枪法-重击蒸发伤害", - "index": "7", - "damage_key": "vaporize" - }, - { - "name": "安神秘法-蒸发伤害", - "index": "12", - "damage_key": "vaporize" - }, - { - "name": "安神秘法-低血量蒸发伤害", - "index": "13", - "damage_key": "vaporize" - } - ], - "config_skill": { - "after_e": true + "胡桃": { + "skills": [ + { + "name": "普通攻击·往生秘传枪法-重击蒸发伤害", + "index": 7, + "damage_key": "vaporize" + }, + { + "name": "安神秘法-蒸发伤害", + "index": 12, + "damage_key": "vaporize" + }, + { + "name": "安神秘法-低血量蒸发伤害", + "index": 13, + "damage_key": "vaporize" + } + ], + "config": { + "le_50": true + }, + "config_skill": { + "after_e": true + }, + "config_weapon": { + "StaffOfHoma": { + "be50_rate": 1 + } + }, + "artifact_config": { + "config_crimson_witch_of_flames": { + "level": 1 + } + } }, - "config": { - "le_50": true + "夜兰": { + "skills": [ + { + "name": "普通攻击·潜形隐曜弓-破局矢伤害", + "index": 6, + "damage_key": "normal" + }, + { + "name": "萦络纵命索-技能伤害", + "index": 11, + "damage_key": "normal" + }, + { + "name": "渊图玲珑骰-技能伤害", + "index": 12, + "damage_key": "normal" + }, + { + "name": "渊图玲珑骰-玄掷玲珑伤害", + "index": 13, + "damage_key": "normal" + } + ], + "config": { + "team_element_count": 4 + }, + "config_skill": null, + "config_weapon": { + "AquaSimulacra": { + "is_enemy_around": true + } + }, + "artifact_config": null }, - "config_weapon": { - "StaffOfHoma": { - "be50_rate": 1 - } - }, - "artifact_config": { - "config_crimson_witch_of_flames": { - "level": 1 - } + "雷电将军": { + "skills": [ + { + "name": "神变•恶曜开眼-技能伤害", + "index": 10, + "damage_key": "normal" + }, + { + "name": "神变•恶曜开眼协-同攻击伤害", + "index": 11, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-梦想一刀基础伤害", + "index": 12, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-一段伤害", + "index": 13, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-二段伤害", + "index": 14, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-三段伤害", + "index": 15, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-四段伤害-1", + "index": 16, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-四段伤害-2", + "index": 17, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-五段伤害", + "index": 18, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-重击伤害-1", + "index": 19, + "damage_key": "normal" + }, + { + "name": "奥义•梦想真说-重击伤害-2", + "index": 20, + "damage_key": "normal" + } + ], + "config": null, + "config_skill": { + "under_e": true, + "resolve_stack": 60 + }, + "config_weapon": { + "EngulfingLightning": { + "rate": 0 + } + }, + "artifact_config": null } - }, - "夜兰": { - "skills": [ - { - "name": "普通攻击·潜形隐曜弓-破局矢伤害", - "index": "6", - "damage_key": "normal" - }, - { - "name": "萦络纵命索-技能伤害", - "index": "11", - "damage_key": "normal" - }, - { - "name": "渊图玲珑骰-技能伤害", - "index": "12", - "damage_key": "normal" - }, - { - "name": "渊图玲珑骰-玄掷玲珑伤害", - "index": "13", - "damage_key": "normal" - } - ], - "config": { - "team_element_count": 4 - }, - "config_weapon": { - "AquaSimulacra": { - "is_enemy_around": true - } - } - }, - "雷电将军": { - "skills": [ - { - "name": "神变•恶曜开眼-技能伤害", - "index": "10", - "damage_key": "normal" - }, - { - "name": "神变•恶曜开眼协-同攻击伤害", - "index": "11", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-梦想一刀基础伤害", - "index": "12", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-一段伤害", - "index": "13", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-二段伤害", - "index": "14", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-三段伤害", - "index": "15", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-四段伤害-1", - "index": "16", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-四段伤害-2", - "index": "17", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-五段伤害", - "index": "18", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-重击伤害-1", - "index": "19", - "damage_key": "normal" - }, - { - "name": "奥义•梦想真说-重击伤害-2", - "index": "20", - "damage_key": "normal" - } - ], - "config_skill": { - "under_e": true, - "resolve_stack": 60 - }, - "config_weapon": { - "EngulfingLightning": { - "rate": 0 - } - } - } -} +} \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 6b53f58..8796ac3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ install: - pip install -r requirements.txt build_script: -- flet pack main.py --name FightPropRuleEditor --product-name FightPropRuleEditor +- flet pack main.py --name GramBotMetadataEditor --product-name GramBotMetadataEditor test: off @@ -40,10 +40,10 @@ for: - image: Visual Studio 2019 after_build: - - 7z a FightPropRuleEditor-windows.zip %CD%\dist\*.exe + - 7z a GramBotMetadataEditor-windows.zip %CD%\dist\*.exe artifacts: - - path: FightPropRuleEditor-windows.zip + - path: GramBotMetadataEditor-windows.zip # # macOS package @@ -54,10 +54,10 @@ for: - image: macOS after_build: - - tar -czvf FightPropRuleEditor-macos.tar.gz -C dist FightPropRuleEditor.app + - tar -czvf GramBotMetadataEditor-macos.tar.gz -C dist GramBotMetadataEditor.app artifacts: - - path: FightPropRuleEditor-macos.tar.gz + - path: GramBotMetadataEditor-macos.tar.gz # # Linux package @@ -68,7 +68,7 @@ for: - image: Ubuntu after_build: - - tar -czvf FightPropRuleEditor-linux.tar.gz -C dist FightPropRuleEditor + - tar -czvf GramBotMetadataEditor-linux.tar.gz -C dist GramBotMetadataEditor artifacts: - - path: FightPropRuleEditor-linux.tar.gz + - path: GramBotMetadataEditor-linux.tar.gz diff --git a/main.py b/main.py index ba78964..27cf8f3 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import flet as ft from src.choose import choose_view +from src.damage.page import edit_damage_view from src.data import Page from src.edit import edit_view @@ -11,6 +12,8 @@ def main(page: Page): choose_view(page) if e.route == "/edit": edit_view(page) + elif e.route == "/edit_damage": + edit_damage_view(page) page.update() def view_pop(): @@ -18,7 +21,7 @@ def main(page: Page): top_view = page.views[-1] page.go(top_view.route) - page.title = "FightPropRuleEditor" + page.title = "GramBotMetadataEditor" page.vertical_alignment = "center" page.horizontal_alignment = "center" page.on_route_change = on_route_change diff --git a/requirements.txt b/requirements.txt index 273b482..bebd443 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,6 @@ flet PyInstaller httpx +Python-Genshin-Artifact +genshin-artifact-core +pydantic diff --git a/src/choose.py b/src/choose.py index f19f8f1..9904755 100644 --- a/src/choose.py +++ b/src/choose.py @@ -13,6 +13,10 @@ def choose_view(page: Page): page.go("/edit") show_snack_bar(page, "开始编辑原神圣遗物有效词条", ft.colors.GREEN) + def genshin_damage(_e): + page.go("/edit_damage") + show_snack_bar(page, "开始编辑原神伤害计算规则", ft.colors.GREEN) + def starrail(_e): page.core = Core(starrail_path, Starrail()) page.go("/edit") @@ -32,7 +36,7 @@ def choose_view(page: Page): [ ft.Container( content=ft.Text( - "FightPropRuleEditor", + "GramBotMetadataEditor", size=50, ), ), @@ -46,6 +50,11 @@ def choose_view(page: Page): icon=ft.icons.LOGIN, on_click=starrail, ), + ft.FilledButton( + "GenshinDamage", + icon=ft.icons.LOGIN, + on_click=genshin_damage, + ), ft.FilledButton( "Refresh avatars", icon=ft.icons.LOGIN, diff --git a/src/damage/__init__.py b/src/damage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/damage/artifact.py b/src/damage/artifact.py new file mode 100644 index 0000000..5eb19b8 --- /dev/null +++ b/src/damage/artifact.py @@ -0,0 +1,30 @@ +import json +from typing import Dict, List + +from .assets import assets, locale +from .models import Artifact as ArtifactModel, WeaponConfig + + +class Artifact: + def __init__(self): + artifacts: List[ArtifactModel] = [] + artifact_config_map: Dict[str, ArtifactModel] = {} + for value in assets.artifact.values(): + if not value["config4"]: + continue + cn_name = locale[value["name_locale"]] + config_key = ArtifactModel.get_config_name(value["name"]) + config = [] + for i in value["config4"]: + temp = json.loads(i) + temp["title"] = locale[temp["title"]] + temp["parent"] = config_key + config.append(WeaponConfig(**temp)) + artifact_ = ArtifactModel(**value, cn_name=cn_name, config=config) + artifacts.append(artifact_) + artifact_config_map[config_key] = artifact_ + self.artifacts = artifacts + self.artifact_config_map = artifact_config_map + + +artifact = Artifact() diff --git a/src/damage/assets.py b/src/damage/assets.py new file mode 100644 index 0000000..dc81cdd --- /dev/null +++ b/src/damage/assets.py @@ -0,0 +1,4 @@ +from python_genshin_artifact.assets import Assets + +assets = Assets() +locale = assets.locale["zh-cn"] diff --git a/src/damage/character.py b/src/damage/character.py new file mode 100644 index 0000000..a26a2aa --- /dev/null +++ b/src/damage/character.py @@ -0,0 +1,56 @@ +import json +from typing import List, Dict + +from .assets import assets, locale +from .models import CharacterConfig, CharacterSkill + + +class Character: + def __init__(self): + self.current_name = "" + self.characters_map = {} + self.skills_map = {} + self.config_map = {} + self.config_skill_map = {} + for value in assets.character.values(): + character_name = locale[value["name_locale"]] + self.characters_map[character_name] = value + self.skills_map[character_name] = self.gen_skills_model(value) + self.config_map[character_name] = self.gen_config_model(value["config"]) + self.config_skill_map[character_name] = self.gen_config_model( + value["config_skill"] + ) + self.characters_name = list(self.characters_map.keys()) + + @staticmethod + def gen_skills_model(character_: Dict) -> List[CharacterSkill]: + skills = [] + skill1_name = locale[character_.get("skill1_name_index")] + skill2_name = locale[character_.get("skill2_name_index")] + skill3_name = locale[character_.get("skill3_name_index")] + for skill_name, skill in [ + (skill1_name, character_["skill_map1"]), + (skill2_name, character_["skill_map2"]), + (skill3_name, character_["skill_map3"]), + ]: + for i in skill: + skills.append( + CharacterSkill( + name=locale[i["locale_index"]], + index=i["index"], + skill_name=skill_name, + ) + ) + return skills + + @staticmethod + def gen_config_model(config: List[str]) -> List[CharacterConfig]: + data = [] + for i in config: + temp = json.loads(i) + temp["title"] = locale[temp["title"]] + data.append(CharacterConfig(**temp)) + return data + + +character = Character() diff --git a/src/damage/data.py b/src/damage/data.py new file mode 100644 index 0000000..ffc9d4e --- /dev/null +++ b/src/damage/data.py @@ -0,0 +1,232 @@ +import json +from typing import Dict, Any + +from .character import character +from .models import CharacterDamage, CharacterSkill, CharacterConfig, WeaponConfig + + +class Data: + def __init__(self): + self.file_name = "GenshinDamageRule.json" + self.file_data: Dict[str, CharacterDamage] = self.load() + self.patch_character() + + def load(self) -> Dict[str, CharacterDamage]: + with open(self.file_name, "r", encoding="utf-8") as f: + _data = json.load(f) + new_data = {} + for k, v in _data.items(): + new_data[k] = CharacterDamage(**v) + return new_data + + def patch_character(self): + for c_name, v in self.file_data.items(): + custom_name_map = {} + damage_key_map = {} + for skill in v.skills: + custom_name_map[skill.index] = skill.name + damage_key_map[skill.index] = skill.damage_key + for skill in character.skills_map[c_name]: + if skill.index in custom_name_map: + skill.custom_name = custom_name_map[skill.index] + skill.damage_key = damage_key_map[skill.index] + + def dump(self) -> Dict[str, Dict]: + new_data = {} + for k, v in self.file_data.items(): + new_data[k] = v.dict() + return new_data + + def save(self): + with open(self.file_name, "w", encoding="utf-8") as f: + json.dump(self.dump(), f, ensure_ascii=False, indent=4) + + def first_init(self, character_name: str): + if character_name in self.file_data: + return + self.file_data[character_name] = CharacterDamage(skills=[]) + + def last_close(self, character_name: str): + if character_name not in self.file_data: + return + data_ = self.file_data[character_name] + + def last_close_weapon_or_artifact(): + if data_.config_weapon: + for k, v in data_.config_weapon.copy().items(): + if not v: + del data_.config_weapon[k] + if data_.artifact_config: + for k, v in data_.artifact_config.copy().items(): + if not v: + del data_.artifact_config[k] + + last_close_weapon_or_artifact() + if len(data_.skills) != 0: + return + if data_.config is not None and data_.config: + return + if data_.config_skill is not None and data_.config_skill: + return + if data_.config_weapon is not None and data_.config_weapon: + return + if data_.artifact_config is not None and data_.artifact_config: + return + del self.file_data[character_name] + + def get_skill_value(self, character_name: str, skill: CharacterSkill) -> bool: + if not self.file_data.get(character_name): + return False + skill_id = skill.index + for i in self.file_data[character_name].skills: + if i.index == skill_id: + return True + return False + + def set_skill_value(self, character_name: str, skill: CharacterSkill, enable: bool): + self.first_init(character_name) + in_data = None + for i in self.file_data[character_name].skills: + if i.index == skill.index: + in_data = i + break + if enable: + if not in_data: + self.file_data[character_name].skills.append(skill.to_data()) + else: + in_data.name = skill.custom_name or skill.show_name + in_data.damage_key = skill.damage_key + else: + if in_data: + self.file_data[character_name].skills.remove(in_data) + self.file_data[character_name].skills.sort(key=lambda x: x.index) + self.last_close(character_name) + + def get_character_config_value( + self, character_name: str, config_: CharacterConfig + ) -> Any: + if not self.file_data.get(character_name): + return None + if not self.file_data[character_name].config: + return None + return self.file_data[character_name].config.get(config_.name, None) + + def set_character_config_value( + self, character_name: str, config: CharacterConfig, value: Any + ): + self.first_init(character_name) + if not self.file_data[character_name].config: + self.file_data[character_name].config = {} + if isinstance(config.default, bool): + new_value = value == "true" + else: + value_class = type(config.default) + new_value = value_class(value) + self.file_data[character_name].config[config.name] = new_value + self.last_close(character_name) + + def get_character_skill_config_value( + self, character_name: str, config_: CharacterConfig + ) -> Any: + if not self.file_data.get(character_name): + return None + if not self.file_data[character_name].config_skill: + return None + return self.file_data[character_name].config_skill.get(config_.name, None) + + def set_character_skill_config_value( + self, character_name: str, config: CharacterConfig, value: Any + ): + self.first_init(character_name) + if not self.file_data[character_name].config_skill: + self.file_data[character_name].config_skill = {} + if isinstance(config.default, bool): + new_value = value == "true" + else: + value_class = type(config.default) + new_value = value_class(value) + self.file_data[character_name].config_skill[config.name] = new_value + self.last_close(character_name) + + def get_character_weapon_config_value( + self, character_name: str, config_: WeaponConfig + ) -> Any: + if not self.file_data.get(character_name): + return None + if not self.file_data[character_name].config_weapon: + return None + data_ = self.file_data[character_name].config_weapon.get(config_.parent, None) + if not data_: + return None + return data_.get(config_.name, None) + + def set_character_weapon_config_value( + self, character_name: str, config: WeaponConfig, value: Any + ): + self.first_init(character_name) + if not self.file_data[character_name].config_weapon: + self.file_data[character_name].config_weapon = {} + data_ = self.file_data[character_name].config_weapon.get(config.parent, {}) + if value == "": + if config.name in data_: + del data_[config.name] + else: + if isinstance(config.default, bool): + new_value = value == "true" + else: + value_class = type(config.default) + new_value = value_class(value) + data_[config.name] = new_value + self.file_data[character_name].config_weapon[config.parent] = data_ + self.last_close(character_name) + + def get_weapon_config_enable(self, character_name: str, weapon_key: str) -> bool: + if not self.file_data.get(character_name): + return False + if not self.file_data[character_name].config_weapon: + return False + return weapon_key in self.file_data[character_name].config_weapon + + def get_character_artifact_config_value( + self, character_name: str, config_: WeaponConfig + ) -> Any: + if not self.file_data.get(character_name): + return None + if not self.file_data[character_name].artifact_config: + return None + data_ = self.file_data[character_name].artifact_config.get(config_.parent, None) + if not data_: + return None + return data_.get(config_.name, None) + + def set_character_artifact_config_value( + self, character_name: str, config: WeaponConfig, value: Any + ): + self.first_init(character_name) + if not self.file_data[character_name].artifact_config: + self.file_data[character_name].artifact_config = {} + data_ = self.file_data[character_name].artifact_config.get(config.parent, {}) + if value == "": + if config.name in data_: + del data_[config.name] + else: + if isinstance(config.default, bool): + new_value = value == "true" + else: + value_class = type(config.default) + new_value = value_class(value) + data_[config.name] = new_value + self.file_data[character_name].artifact_config[config.parent] = data_ + self.last_close(character_name) + + def get_artifact_config_enable( + self, character_name: str, artifact_key: str + ) -> bool: + if not self.file_data.get(character_name): + return False + if not self.file_data[character_name].artifact_config: + return False + return artifact_key in self.file_data[character_name].artifact_config + + +data = Data() diff --git a/src/damage/models.py b/src/damage/models.py new file mode 100644 index 0000000..f968f56 --- /dev/null +++ b/src/damage/models.py @@ -0,0 +1,157 @@ +import flet +from enum import Enum +from typing import List, Dict, Any, Optional +from pydantic import BaseModel + +Element4OP = [ + flet.dropdown.Option(key="Cyro", text="冰元素"), + flet.dropdown.Option(key="Electro", text="雷元素"), + flet.dropdown.Option(key="Hydro", text="水元素"), + flet.dropdown.Option(key="Pyro", text="火元素"), +] +Element8OP = Element4OP + [ + flet.dropdown.Option(key="Anemo", text="风元素"), + flet.dropdown.Option(key="Dendro", text="草元素"), + flet.dropdown.Option(key="Geo", text="岩元素"), + flet.dropdown.Option(key="Physical", text="物理伤害"), +] + + +class Element4(str, Enum): + Cyro = "Cyro" + """冰元素""" + Electro = "Electro" + """雷元素""" + Hydro = "Hydro" + """水元素""" + Pyro = "Pyro" + """火元素""" + + +class Element8(str, Enum): + Cyro = "Cyro" + """冰元素""" + Electro = "Electro" + """雷元素""" + Hydro = "Hydro" + """水元素""" + Pyro = "Pyro" + """火元素""" + Anemo = "Anemo" + """风元素""" + Dendro = "Dendro" + """草元素""" + Geo = "Geo" + """岩元素""" + Physical = "Physical" + """物理伤害""" + + +class CharacterDamageSkillDamageKey(str, Enum): + normal = "normal" + """普通伤害结果""" + melt = "melt" + """融化伤害结果""" + vaporize = "vaporize" + """蒸发伤害结果""" + + @property + def data_map(self) -> Dict[str, str]: + return {"普通伤害结果": "normal", "融化伤害结果": "melt", "蒸发伤害结果": "vaporize"} + + +class CharacterDamageSkill(BaseModel): + name: str + index: int + damage_key: CharacterDamageSkillDamageKey + + class Config: + frozen = False + + +class CharacterDamage(BaseModel): + skills: List[CharacterDamageSkill] + config: Optional[Dict[str, Any]] = None + config_skill: Optional[Dict[str, Any]] = None + config_weapon: Optional[Dict[str, Dict[str, Any]]] = None + artifact_config: Optional[Dict[str, Dict[str, Any]]] = None + + class Config: + frozen = False + + +class CharacterConfig(BaseModel): + default: Any + name: str + title: str + type: str + + class Config: + frozen = False + + +class CharacterSkill(BaseModel): + name: str + index: int + skill_name: str + custom_name: str = "" + damage_key: CharacterDamageSkillDamageKey = CharacterDamageSkillDamageKey.normal + + @property + def show_name(self) -> str: + return f"{self.name} - {self.skill_name}" + + def to_data(self) -> CharacterDamageSkill: + return CharacterDamageSkill( + name=self.custom_name or self.show_name, + index=self.index, + damage_key=self.damage_key, + ) + + class Config: + frozen = False + + +class WeaponConfig(CharacterConfig): + max: float = 0 + min: float = 0 + parent: str = "" + + class Config: + frozen = False + + +class Weapon(BaseModel): + name: str + cn_name: str + star: int + t: str + config: List[WeaponConfig] + + class Config: + frozen = False + + +class Artifact(BaseModel): + name: str + cn_name: str + min_star: int + max_star: int + config: List[WeaponConfig] + + @property + def config_name(self) -> str: + return self.get_config_name(self.name) + + @staticmethod + def get_config_name(name: str) -> str: + start = "config_" + # 将大写转换为下划线加小写 + # 例如: MaxHp -> max_hp + convert_name = "".join( + [f"_{i.lower()}" if i.isupper() else i for i in name] + ).lstrip("_") + return f"{start}{convert_name}" + + class Config: + frozen = False diff --git a/src/damage/page.py b/src/damage/page.py new file mode 100644 index 0000000..faadc24 --- /dev/null +++ b/src/damage/page.py @@ -0,0 +1,496 @@ +from typing import List + +from flet_core import MainAxisAlignment + +import flet as ft + +from src.data import Page +from .artifact import artifact +from .character import character +from .data import data +from .models import ( + CharacterSkill, + CharacterDamageSkillDamageKey, + CharacterConfig, + Weapon as WeaponModel, + Element4OP, + Element8OP, +) +from .weapon import weapon + + +def edit_damage_view(page: "Page"): + name_list = ft.ListView(width=230) + skill_list = ft.ListView(expand=True) + weapon_list = ft.ListView(expand=True) + artifact_list = ft.ListView(expand=True) + character_control_list = ft.ListView(expand=True) + top_title = ft.Ref[ft.Text]() + + def update_top_title(title: str): + """更新当前页面标题""" + top_title.current.value = title + page.update() + + def back_choose(_): + page.go("/") + page.update() + + def save(_): + def close_alert(_): + success_dialog.open = False + top_title.current.value = "" + choose_character() + page.update() + + data.save() + success_dialog = ft.AlertDialog( + modal=True, + title=ft.Text("保存成功"), + content=ft.Text("保存成功"), + actions=[ + ft.TextButton("好", on_click=close_alert), + ], + actions_alignment=MainAxisAlignment.END, + ) + page.dialog = success_dialog + success_dialog.open = True + page.update() + + def update_skill_component(): + ch_name = character.current_name + skill_list.controls.clear() + + def search_skills(e: ft.ControlEvent = None): + temp = skill_list.controls[0] + skill_list.controls.clear() + skill_list.controls.append(temp) + for con in skill_list_data: + con: "ft.Container" + cb: "ft.Checkbox" = con.content # noqa + if e is None or e.data in cb.label: + skill_list.controls.append(con) + page.update() + + skill_list.controls.append( + ft.Container( + content=ft.TextField( + label="技能配置", + on_change=search_skills, + ), + ) + ) + for i in character.skills_map[ch_name]: + container = ft.Container( + content=ft.Checkbox( + label=i.show_name, + value=data.get_skill_value(ch_name, i), + disabled=True, + data=i, + ), + on_click=choose_skill, + ) + skill_list.controls.append(container) + skill_list_data = skill_list.controls[1:] + + def update_weapon_component(): + ch_name = character.current_name + character_ = character.characters_map[ch_name] + weapon_list.controls.clear() + + def search_weapons(e: ft.ControlEvent = None): + temp = weapon_list.controls[0] + weapon_list.controls.clear() + weapon_list.controls.append(temp) + for con in weapon_list_data: + con: "ft.Container" + cb: "ft.Checkbox" = con.content # noqa + if e is None or e.data in cb.label: + weapon_list.controls.append(con) + page.update() + + weapon_list.controls.append( + ft.Container( + content=ft.TextField( + label="武器配置", + on_change=search_weapons, + ), + ) + ) + for i in weapon.weapon_map.get(character_.get("weapon", ""), []): + i: "WeaponModel" + container = ft.Container( + content=ft.Checkbox( + label=i.cn_name, + value=data.get_weapon_config_enable(ch_name, i.name), + disabled=True, + data=i, + ), + on_click=choose_weapon, + ) + weapon_list.controls.append(container) + weapon_list_data = weapon_list.controls[1:] + + def update_artifact_component(): + ch_name = character.current_name + artifact_list.controls.clear() + + def search_artifacts(e: ft.ControlEvent = None): + temp = artifact_list.controls[0] + artifact_list.controls.clear() + artifact_list.controls.append(temp) + for con in artifact_list_data: + con: "ft.Container" + cb: "ft.Checkbox" = con.content # noqa + if e is None or e.data in cb.label: + artifact_list.controls.append(con) + page.update() + + artifact_list.controls.append( + ft.Container( + content=ft.TextField( + label="圣遗物配置", + on_change=search_artifacts, + ), + ) + ) + for i in artifact.artifacts: + container = ft.Container( + content=ft.Checkbox( + label=i.cn_name, + value=data.get_artifact_config_enable(ch_name, i.config_name), + disabled=True, + data=i, + ), + on_click=choose_artifact, + ) + artifact_list.controls.append(container) + artifact_list_data = artifact_list.controls[1:] + + def gen_switch_or_text( + ch_name, config: CharacterConfig, get_config_value, on_change + ): + ele = None + if isinstance(config.default, bool): + class_ = ft.Checkbox + elif config.type in ["element4", "element8"]: + class_ = ft.Dropdown + ele = Element4OP if config.type == "element4" else Element8OP + else: + class_ = ft.TextField + ins = class_( + label=config.title, + value=get_config_value(ch_name, config), + data=config, + on_change=on_change, + ) + if class_ == ft.Dropdown: + ins.options = ele + return ins + + def update_config_component( + com: List, + name: str, + config_map, + set_character_config_value, + get_character_config_value, + ): + ch_name = character.current_name + if not character.config_map[ch_name]: + return + com.append( + ft.Container( + content=ft.Text( + name, + size=20, + ), + ), + ) + + def on_config_value_change(e: ft.ControlEvent = None): + set_character_config_value(ch_name, e.control.data, e.data) + + def reset_all_config_value(_): + for config in configs: + config.value = config.data.default + set_character_config_value(ch_name, config.data, config.data.default) + page.update() + + configs = [] + for i in config_map[ch_name]: + content = gen_switch_or_text( + ch_name, i, get_character_config_value, on_config_value_change + ) + configs.append(content) + com.append( + ft.Container( + content=content, + ), + ) + if len(configs) != 0: + com.append( + ft.Container( + content=ft.ElevatedButton( + "恢复到默认值", on_click=reset_all_config_value + ), + ) + ) + return com + + def choose_character(e: ft.ControlEvent = None): + if e is not None: + ch_name = e.control.data[0] + character.current_name = ch_name + else: + ch_name = character.current_name + page.update() + update_top_title(ch_name) + update_skill_component() + update_weapon_component() + update_artifact_component() + character_control_list.controls.clear() + com = [] + character_control_list.controls.append(ft.Column(com)) + update_config_component( + com, + " 角色配置", + character.config_map, + data.set_character_config_value, + data.get_character_config_value, + ) + update_config_component( + com, + " 技能配置", + character.config_skill_map, + data.set_character_skill_config_value, + data.get_character_skill_config_value, + ) + page.update() + + def bs_dismissed(_: ft.ControlEvent = None): + choose_character() + + def choose_skill(e: ft.ControlEvent = None): + checkbox: ft.Checkbox = e.control.content + skill: CharacterSkill = checkbox.data + page.overlay.clear() + + def close_bs(_: ft.ControlEvent = None): + bs.open = False + bs.update() + + def skill_status_change(e_: ft.ControlEvent = None): + value = e_.data == "true" + data.set_skill_value(character.current_name, skill, value) + + def skill_custom_name_change(e_: ft.ControlEvent = None): + skill.custom_name = e_.data + data.set_skill_value(character.current_name, skill, True) + + def skill_key_change(e_: ft.ControlEvent = None): + skill.damage_key = CharacterDamageSkillDamageKey(e_.data) + data.set_skill_value(character.current_name, skill, True) + + bs = ft.BottomSheet( + ft.Container( + ft.Column( + [ + ft.Text(skill.show_name), + ft.Switch( + label="显示此数值", + value=checkbox.value, + on_change=skill_status_change, + ), + ft.TextField( + label="自定义显示名称", + value=skill.custom_name or skill.show_name, + on_change=skill_custom_name_change, + ), + ft.Dropdown( + label="输出数据", + options=[ + ft.dropdown.Option(key=v, text=k) + for k, v in CharacterDamageSkillDamageKey.normal.data_map.items() + ], + value=skill.damage_key.value, + on_change=skill_key_change, + ), + ft.ElevatedButton("关闭", on_click=close_bs), + ], + tight=True, + ), + padding=10, + ), + open=True, + on_dismiss=bs_dismissed, + ) + page.overlay.append(bs) + page.update() + bs.update() + + def choose_weapon(e): + choose_weapon_or_artifact( + e, + data.get_character_weapon_config_value, + data.set_character_weapon_config_value, + ) + + def choose_artifact(e): + choose_weapon_or_artifact( + e, + data.get_character_artifact_config_value, + data.set_character_artifact_config_value, + ) + + def choose_weapon_or_artifact( + e: ft.ControlEvent, + get_config_value, + set_config_value, + ): + ch_name = character.current_name + checkbox: ft.Checkbox = e.control.content + model: "WeaponModel" = checkbox.data + page.overlay.clear() + + def close_bs(_: ft.ControlEvent = None): + bs.open = False + bs.update() + + def on_config_value_change(e_: ft.ControlEvent = None): + set_config_value(ch_name, e_.control.data, e_.data) + + def reset_all_config_value(_): + for config in configs: + config.value = config.data.default + set_config_value(ch_name, config.data, config.data.default) + page.update() + + controls = [ + ft.Text(model.cn_name), + ] + configs = [] + for i in model.config: + content = gen_switch_or_text( + ch_name, i, get_config_value, on_config_value_change + ) + configs.append(content) + controls.append(content) + controls.append( + ft.Container( + content=ft.ElevatedButton("恢复到默认值", on_click=reset_all_config_value), + ) + ) + controls.append(ft.ElevatedButton("关闭", on_click=close_bs)) + + bs = ft.BottomSheet( + ft.Container( + ft.Column( + controls, + tight=True, + ), + padding=10, + ), + open=True, + on_dismiss=bs_dismissed, + ) + page.overlay.append(bs) + page.update() + bs.update() + + def load_names(): + def search_names(e: ft.ControlEvent = None): + temp = name_list.controls[0] + name_list.controls.clear() + name_list.controls.append(temp) + for name_ in name_list_data: + if e is None or e.data in name_.text: + name_list.controls.append(name_) + page.update() + + name_list.controls.append( + ft.Container( + content=ft.TextField( + label="搜索", + on_change=search_names, + ), + ) + ) + for index, name in enumerate(character.characters_name): + name_list.controls.append( + ft.TextButton( + text=name, + tooltip=name, + style=ft.ButtonStyle( + shape={ + "hovered": ft.RoundedRectangleBorder(), + "": ft.RoundedRectangleBorder(), + } + ), + data=(name,), + on_click=choose_character, + disabled=False, + ), + ) + name_list_data = name_list.controls[1:] + + load_names() + page.views.append( + ft.View( + "/edit_damage", + [ + ft.Stack( + [ + ft.Container( + content=ft.Row( + [ + ft.Text( + ref=top_title, + value="请选择需要编辑的角色", + size=30, + ), + ft.Row( + [ + ft.ElevatedButton( + "返回", + icon=ft.icons.ARROW_BACK, + on_click=back_choose, + ), + ft.ElevatedButton( + "保存", + icon=ft.icons.DONE, + on_click=save, + ), + ], + alignment=MainAxisAlignment.CENTER, + spacing=50, + ), + ], + alignment=MainAxisAlignment.SPACE_BETWEEN, + ), + padding=10, + ), + ] + ), + ft.Divider( + height=1, + ), + ft.Row( + [ + name_list, + ft.VerticalDivider(width=1), + skill_list, + ft.VerticalDivider(width=1), + weapon_list, + ft.VerticalDivider(width=1), + artifact_list, + ft.VerticalDivider(width=1), + character_control_list, + ], + expand=True, + spacing=0, + ), + ], + padding=0, + spacing=0, + ) + ) diff --git a/src/damage/weapon.py b/src/damage/weapon.py new file mode 100644 index 0000000..0c14d80 --- /dev/null +++ b/src/damage/weapon.py @@ -0,0 +1,28 @@ +import json +from typing import Dict + +from .assets import assets, locale +from .models import Weapon as WeaponModel, WeaponConfig + + +class Weapon: + def __init__(self): + weapon_map: Dict[str, WeaponModel] = {} + for value in assets.weapon.values(): + if not value["configs"]: + continue + cn_name = locale[value["name_index"]] + config = [] + for i in value["configs"]: + temp = json.loads(i) + temp["title"] = locale[temp["title"]] + temp["parent"] = value["name"] + config.append(WeaponConfig(**temp)) + weapon_ = WeaponModel(**value, cn_name=cn_name, config=config) + temp = weapon_map.get(value["t"], []) + temp.append(weapon_) + weapon_map[value["t"]] = temp + self.weapon_map = weapon_map + + +weapon = Weapon() diff --git a/src/data.py b/src/data.py index 91970d4..30c63fc 100644 --- a/src/data.py +++ b/src/data.py @@ -155,7 +155,9 @@ class Core: return if type_name not in self.data[ch_name]: return - self.data[ch_name] = {k: v for k, v in self.data[ch_name].copy().items() if k != type_name} + self.data[ch_name] = { + k: v for k, v in self.data[ch_name].copy().items() if k != type_name + } def save_value(self): self.data = {k: v for k, v in self.data.copy().items() if v}