File size: 6,715 Bytes
c145eab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26e0de
c145eab
d26e0de
 
c145eab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26e0de
 
 
 
 
 
 
c145eab
 
d26e0de
 
 
c145eab
 
 
 
 
 
 
 
d26e0de
c145eab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26e0de
 
 
 
 
 
 
 
 
 
 
 
c145eab
 
 
 
 
 
d26e0de
 
c145eab
d26e0de
 
 
 
c145eab
 
d26e0de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from typing import Dict

from gr.components.combat import CombatComponent
from assets.constant import ATTR_TYPE_TRANSLATE
# from gr.scripts.bonuses import Bonuses
# from gr.scripts.consumables import Consumables
from gr.scripts.top import Parser
from gr.scripts.equipments import Equipments
from gr.scripts.recipes import Recipes
from gr.scripts.talents import Talents
from utils.analyzer import analyze_details, Detail

import gradio as gr

FULL_SPACE = "\u3000"


def attribute_content(display_attrs, attribute):
    content = []
    for attr, name in display_attrs.items():
        value = getattr(attribute, attr)
        if isinstance(value, int):
            content.append(name.ljust(10, FULL_SPACE) + str(value))
        else:
            content.append(name.ljust(10, FULL_SPACE) + f"{round(value * 100, 2)}%")
    return "\n".join(content)


def summary_content(summary: Dict[str, Detail], total_damage):
    content = []
    for skill in sorted(summary, key=lambda x: summary[x].expected_damage, reverse=True):
        detail = summary[skill]
        critical = round(detail.critical_count, 2)
        critical_rate = round(detail.critical_count / detail.count * 100, 2)
        hit = round(detail.count - critical, 2)
        hit_rate = round(100 - critical_rate, 2)
        damage = round(detail.expected_damage, 2)
        damage_rate = round(damage / total_damage * 100, 2)
        content.append(
            [f"{skill}/{detail.count}",
             f"{hit}/{hit_rate}%", f"{critical}/{critical_rate}%", f"{damage}/{damage_rate}%"]
        )
    return content


def gradient_content(gradients, total_damage):
    return "\n".join(
        ATTR_TYPE_TRANSLATE[k].ljust(10, FULL_SPACE) + f"{round(v / total_damage * 100, 2)}%"
        for k, v in gradients.items()
    )


def detail_content(detail: Detail):
    damage_detail = "\n".join([
        "命中伤害".ljust(10) + f"{round(detail.damage)}",
        "会心伤害".ljust(10) + f"{round(detail.critical_damage)}",
        "期望伤害".ljust(10) + f"{round(detail.expected_damage)}",
        "期望会心".ljust(10) + f"{round(detail.critical_strike * 100, 2)}%",
        "实际会心".ljust(10) + f"{round(detail.actual_critical_strike * 100, 2)}%",
        "统计数量".ljust(10) + f"{detail.count}"
    ])

    damage_gradient = gradient_content(detail.gradients, detail.expected_damage)

    return damage_detail, damage_gradient


def combat_script(
        parser: Parser,
        talents: Talents, recipes: Recipes, equipments: Equipments,
        # consumables: Consumables, bonuses: Bonuses
        combat_component: CombatComponent,
):
    def formulate(target_level, duration, skill, status):
        combat_update = {}
        record = parser.current_records
        school = parser.current_school

        attribute = school.attribute()
        attribute.target_level = target_level
        for attr, value in equipments.attrs.items():
            setattr(attribute, attr, getattr(attribute, attr) + value)
        combat_update[combat_component.init_attribute] = gr.update(
            value=attribute_content(school.display_attrs, attribute)
        )
        # for attr, value in consumables.attrs.items():
        #     setattr(attribute, attr, getattr(attribute, attr) + value)

        equipment_gains = [school.gains[gain] for gain in equipments.gains]
        talent_gains = [school.talent_gains[school.talent_encoder[talent]] for talent in talents.gains]
        recipe_gains = [
            school.recipe_gains[skill][recipe]
            for i, skill in enumerate(school.recipe_gains)
            for recipe in recipes.gains[i]
        ]
        gains = equipment_gains + talent_gains + recipe_gains  # + bonuses.gains

        for gain in gains:
            gain.add(attribute, school.skills, school.buffs)

        combat_update[combat_component.final_attribute] = gr.update(
            value=attribute_content(school.display_attrs, attribute)
        )
        total, summary, details = analyze_details(record, duration, attribute, school)

        for gain in gains:
            gain.sub(attribute, school.skills, school.buffs)

        combat_update[combat_component.dps] = gr.update(value=round(total.expected_damage / duration))

        combat_update[combat_component.gradient] = gradient_content(total.gradients, total.expected_damage)

        combat_update[combat_component.details] = details
        combat_update[combat_component.skill_select] = gr.update(choices=list(details))
        if skill_detail := details.get(skill, {}):
            combat_update[combat_component.status_select] = gr.update(choices=[""] + list(skill_detail))
        if detail := skill_detail.get(status):
            damage_detail, damage_gradient = detail_content(detail)
            combat_update[combat_component.damage_detail] = gr.update(value=damage_detail)
            combat_update[combat_component.damage_gradient] = gr.update(value=damage_gradient)
        else:
            combat_update[combat_component.damage_detail] = gr.update(value="")
            combat_update[combat_component.damage_gradient] = gr.update(value="")

        combat_update[combat_component.summary] = summary_content(summary, total.expected_damage)
        return combat_update

    combat_component.formulate.click(
        formulate,
        [combat_component.target_level, combat_component.combat_duration,
         combat_component.skill_select, combat_component.status_select],
        [combat_component.init_attribute, combat_component.final_attribute,
         combat_component.dps, combat_component.gradient, combat_component.summary,
         combat_component.details, combat_component.skill_select, combat_component.status_select,
         combat_component.damage_detail, combat_component.damage_gradient
         ]
    )

    def skill_changed(skill, details):
        if skill not in details:
            return None
        return gr.update(choices=[""] + list(details[skill]))

    combat_component.skill_select.change(
        skill_changed, [combat_component.skill_select, combat_component.details], combat_component.status_select
    )

    def status_changed(skill, status, details):
        if skill not in details:
            return None, None
        if status not in details[skill]:
            return None, None
        damage_detail, damage_gradient = detail_content(details[skill][status])
        return gr.update(value=damage_detail), gr.update(value=damage_gradient)

    combat_component.status_select.change(
        status_changed, [combat_component.skill_select, combat_component.status_select, combat_component.details],
        [combat_component.damage_detail, combat_component.damage_gradient]
    )