mirror of
https://github.com/PaiGramTeam/PaiGramMetadata.git
synced 2024-11-21 14:38:22 +00:00
feat: support edit damage rule
This commit is contained in:
parent
eb65dae993
commit
97c8eea821
@ -3,26 +3,26 @@
|
||||
"skills": [
|
||||
{
|
||||
"name": "普通攻击·往生秘传枪法-重击蒸发伤害",
|
||||
"index": "7",
|
||||
"index": 7,
|
||||
"damage_key": "vaporize"
|
||||
},
|
||||
{
|
||||
"name": "安神秘法-蒸发伤害",
|
||||
"index": "12",
|
||||
"index": 12,
|
||||
"damage_key": "vaporize"
|
||||
},
|
||||
{
|
||||
"name": "安神秘法-低血量蒸发伤害",
|
||||
"index": "13",
|
||||
"index": 13,
|
||||
"damage_key": "vaporize"
|
||||
}
|
||||
],
|
||||
"config_skill": {
|
||||
"after_e": true
|
||||
},
|
||||
"config": {
|
||||
"le_50": true
|
||||
},
|
||||
"config_skill": {
|
||||
"after_e": true
|
||||
},
|
||||
"config_weapon": {
|
||||
"StaffOfHoma": {
|
||||
"be50_rate": 1
|
||||
@ -38,92 +38,95 @@
|
||||
"skills": [
|
||||
{
|
||||
"name": "普通攻击·潜形隐曜弓-破局矢伤害",
|
||||
"index": "6",
|
||||
"index": 6,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "萦络纵命索-技能伤害",
|
||||
"index": "11",
|
||||
"index": 11,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "渊图玲珑骰-技能伤害",
|
||||
"index": "12",
|
||||
"index": 12,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "渊图玲珑骰-玄掷玲珑伤害",
|
||||
"index": "13",
|
||||
"index": 13,
|
||||
"damage_key": "normal"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"team_element_count": 4
|
||||
},
|
||||
"config_skill": null,
|
||||
"config_weapon": {
|
||||
"AquaSimulacra": {
|
||||
"is_enemy_around": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifact_config": null
|
||||
},
|
||||
"雷电将军": {
|
||||
"skills": [
|
||||
{
|
||||
"name": "神变•恶曜开眼-技能伤害",
|
||||
"index": "10",
|
||||
"index": 10,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "神变•恶曜开眼协-同攻击伤害",
|
||||
"index": "11",
|
||||
"index": 11,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-梦想一刀基础伤害",
|
||||
"index": "12",
|
||||
"index": 12,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-一段伤害",
|
||||
"index": "13",
|
||||
"index": 13,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-二段伤害",
|
||||
"index": "14",
|
||||
"index": 14,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-三段伤害",
|
||||
"index": "15",
|
||||
"index": 15,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-四段伤害-1",
|
||||
"index": "16",
|
||||
"index": 16,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-四段伤害-2",
|
||||
"index": "17",
|
||||
"index": 17,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-五段伤害",
|
||||
"index": "18",
|
||||
"index": 18,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-重击伤害-1",
|
||||
"index": "19",
|
||||
"index": 19,
|
||||
"damage_key": "normal"
|
||||
},
|
||||
{
|
||||
"name": "奥义•梦想真说-重击伤害-2",
|
||||
"index": "20",
|
||||
"index": 20,
|
||||
"damage_key": "normal"
|
||||
}
|
||||
],
|
||||
"config": null,
|
||||
"config_skill": {
|
||||
"under_e": true,
|
||||
"resolve_stack": 60
|
||||
@ -132,6 +135,7 @@
|
||||
"EngulfingLightning": {
|
||||
"rate": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifact_config": null
|
||||
}
|
||||
}
|
14
appveyor.yml
14
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
|
||||
|
5
main.py
5
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
|
||||
|
@ -1,3 +1,6 @@
|
||||
flet
|
||||
PyInstaller
|
||||
httpx
|
||||
Python-Genshin-Artifact
|
||||
genshin-artifact-core
|
||||
pydantic
|
||||
|
@ -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,
|
||||
|
0
src/damage/__init__.py
Normal file
0
src/damage/__init__.py
Normal file
30
src/damage/artifact.py
Normal file
30
src/damage/artifact.py
Normal file
@ -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()
|
4
src/damage/assets.py
Normal file
4
src/damage/assets.py
Normal file
@ -0,0 +1,4 @@
|
||||
from python_genshin_artifact.assets import Assets
|
||||
|
||||
assets = Assets()
|
||||
locale = assets.locale["zh-cn"]
|
56
src/damage/character.py
Normal file
56
src/damage/character.py
Normal file
@ -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()
|
232
src/damage/data.py
Normal file
232
src/damage/data.py
Normal file
@ -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()
|
157
src/damage/models.py
Normal file
157
src/damage/models.py
Normal file
@ -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
|
496
src/damage/page.py
Normal file
496
src/damage/page.py
Normal file
@ -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,
|
||||
)
|
||||
)
|
28
src/damage/weapon.py
Normal file
28
src/damage/weapon.py
Normal file
@ -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()
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user