File size: 13,607 Bytes
7d13043
 
4393140
7d13043
d200e3d
 
 
 
 
 
 
 
7ee1f08
 
236a96c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d200e3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e6af0f7
820e64b
 
e6af0f7
 
 
1764877
 
 
 
d200e3d
 
 
 
a6c1f07
edc587d
a6c1f07
767d8ff
 
 
d200e3d
a6c1f07
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d200e3d
a2fe95b
731c683
a6c1f07
 
d200e3d
7d13043
89a6022
 
e6af0f7
 
 
89a6022
 
a2fe95b
 
 
 
89a6022
d200e3d
a6c1f07
 
 
 
 
 
e6af0f7
a6c1f07
8da215d
a2fe95b
a6c1f07
 
8da215d
a6c1f07
 
 
8e7bc1d
a6c1f07
 
 
 
 
 
 
8da215d
a6c1f07
 
 
 
8e7bc1d
a6c1f07
820e64b
a6c1f07
 
 
8458525
 
 
a6c1f07
8e7bc1d
 
a6c1f07
66c0494
a6c1f07
66c0494
a6c1f07
 
a2fe95b
a6c1f07
 
a2fe95b
a6c1f07
 
a2fe95b
a6c1f07
 
a2fe95b
a6c1f07
 
66c0494
a6c1f07
 
8da215d
820e64b
 
 
 
a6c1f07
 
 
8da215d
a6c1f07
 
 
8da215d
a6c1f07
 
8458525
 
 
 
 
 
 
 
 
66c0494
a6c1f07
 
a2fe95b
a6c1f07
 
a2fe95b
a6c1f07
 
 
 
 
a2fe95b
e6af0f7
 
 
fae9601
43f75c2
e6af0f7
43f75c2
e6af0f7
43f75c2
e6af0f7
 
 
dbd98f5
 
 
 
 
 
e6af0f7
 
 
fae9601
dbd98f5
e6af0f7
fae9601
dbd98f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e6af0f7
 
 
a6c1f07
e6af0f7
a6c1f07
 
 
 
 
 
 
 
 
 
 
 
 
 
e6af0f7
 
 
d200e3d
a6c1f07
 
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
import pandas as pd
import numpy as np
import streamlit as st

# import glob
# import yaml
from pathlib import Path
from collections import defaultdict

#########################################
# Helpers Functions

display_cols = ['image','name', 'color', 'star', 'class', 'speed', 'power', 'attack', 'defense', 'health', 'types', 'source', 'family']

def filter_by_1col_num(df, col_name, query, oper_flag="eq"):
    ok_flag_list = []
    assert col_name in df.columns, "col_name must be valid"

    for i, val in enumerate(df[col_name]):
        if oper_flag == 'ge':
            flag = True if val >= query else False
        elif oper_flag == 'le':
            flag = True if val <= query else False
        else: # default = eq
            flag = True if val == query else False

        ok_flag_list.append(flag)
    
    assert len(ok_flag_list) == len(df)
    return np.array(ok_flag_list)

def filter_by_1col(df, col_name, query, exact_flag=False):

    def check_valid_value(query, string, exact_flag=False):
        if exact_flag:
            if query.lower() == string.lower():
                return True

        elif query.lower() in string.lower():
            return True
        
        return False

    ok_flag_list = []
    assert col_name in df.columns, "col_name must be valid"

    for i, s in enumerate(df[col_name]):

        if isinstance(s, list):
            for s2 in s:
                flag = check_valid_value(query, s2, exact_flag=exact_flag)
                if flag: break
        else:
            flag = check_valid_value(query, s, exact_flag=exact_flag)

        
        ok_flag_list.append(flag)
    
    assert len(ok_flag_list) == len(df)
    return np.array(ok_flag_list)

def display_image(url, scale=0.5):
    from urllib.request import urlopen
    from PIL import Image

    image = Image.open(urlopen(url))
    st.image(image.resize(( int(image.width * scale), int(image.height * scale))))

def display_heroes_from_df(df,display_cols=display_cols, show_df=True):
    vtob = "is" if len(df)<=1 else "are"
    st.write(f'There {vtob} {len(df)} heroes in the filtered list')

    if show_df:
        st.dataframe(df[display_cols],
                 column_config={
                         "image": st.column_config.ImageColumn("Avatar", help="")},
                 use_container_width=True,
                 hide_index=True)

    for i in range(len(df)):
        url = df['image'].values[i]
        display_image(url)
        st.write(f"***{df['name'].values[i]}*** - {df['speed'].values[i]} - {df['class'].values[i]}")
        st.write(f'Attack:{df["attack"].values[i]} -- Defence:{df["defense"].values[i]} -- Health:{df["health"].values[i]}')
        st.write(f"***{df['skill'].values[i]}***" )
        st.write(df['effects'].values[i])
        # for sp in df['effects'].values[i]:
        #     st.write(sp)

#########################################
## Helper function for LB/CB stat analysis
def return_costume_list(df0, hero_name):
    assert hero_name in df0.name.values

    if hero_name[-2:] == "C2":
        return ['None', 'CB1', 'CB2']
    elif hero_name[-2:] == " C":
        hero_name2 = hero_name + "2"
        if hero_name2 in df0.name.values: # if this hero has C2
            return ['None', 'CB1', 'CB2']
        else:
            return ['None', 'CB1']
    else:
        hero_name1 = hero_name + " C"
        hero_name2 = hero_name + " C2"
        if hero_name2 in df0.name.values: # if this hero has C2
            return ['None', 'CB1', 'CB2']
        elif hero_name1 in df0.name.values: # if this hero has C2
            return ['None', 'CB1']
        else:
            return ['None']

def get_prefix(lb_choice="None", costume_choice="None"):
    prefix_1 = "Max level"

    if lb_choice != 'None':
        prefix_1 = "Limit Break"
    
    prefix_2 = ""
    if costume_choice != "None":
        prefix_2 = f" {costume_choice}" # CB1 or CB2

    prefix_3 = ":"
    if lb_choice == 'LB1':
        prefix_3 = " #1:"
    elif lb_choice == 'LB2':
        prefix_3 = " #2:"

    return prefix_1 + prefix_2 + prefix_3

def return_hero_stat(df0, hero_name, lb_choice="None", costume_choice="None"):
    assert hero_name in df0.name.values
    
    display_cols_0 = ['image', 'name', 'color', 'star', 'class', 'speed',]
    display_cols_1 = [] # ['power', 'attack', 'defense', 'health', ] --> to be select base one LB/Costume choice
    display_cols_2 = ['Aether Power', 'source', 'family', 'types', 'skill', 'effects']

    prefix = get_prefix(lb_choice, costume_choice)

    display_cols_1.append(f'{prefix} Power')
    display_cols_1.append(f'{prefix} Attack')
    display_cols_1.append(f'{prefix} Defense')
    display_cols_1.append(f'{prefix} Health')
    
    display_cols_all = display_cols_0 + display_cols_1 + display_cols_2
    df_ret = df0[df0.name == hero_name][display_cols_all]

    df_ret = df_ret.rename(columns={f'{prefix} Power':'power',
                    f'{prefix} Attack':'attack',
                    f'{prefix} Defense':'defense',
                    f'{prefix} Health':'health'})
    return df_ret

#########################################
## Load the main file (TODO: caching)=
st.set_page_config(layout="wide")
st.header(f'HeroPlan Explorer')
st.write('Powered by Heroplan.io : Thanks E&P community for continually update hero data.')

df = pd.read_csv('heroes_ep.csv')
st.write(f'### Updated: Oct 23, 2023 -- Total heroes in HeroPlan database = {len(df)}')

df_extra = pd.read_csv("heroes_ep_extra.csv")
all_name_extra = sorted(list(df_extra['name'].values))

#########################################

class_values = ['None'] + list(df['class'].unique()) 
star_values = ['None'] + list(df['star'].unique())
color_values = ['None'] + list(df['color'].unique())
speed_values = ['None'] + list(df['speed'].unique())
source_values = ['None'] + list(df['source'].unique()) # Contain lot of typo bugs from HeroPlan

#########################################
## Select Main Program

with st.sidebar:
    genre = st.radio(
    "Choose how to explore heroes",
    [":rainbow[Heroes Explorer]", "Team Simulation","***LB/CB Hero Stat*** :movie_camera:"],
    captions = ["Filter only heroes with certain properties", "Co-powered by Elioty33's DataVault"])

#########################################
## Program 1
if genre == ':rainbow[Heroes Explorer]':
    
    col1, col2, col3 = st.columns(3)
    with col1:
        st.header("Standard Filters:")
        st.write("Tips: filter costume by typing ' C' or 'C2' in the Name box.")
        with st.expander("Filter Options"):
            name_option = st.text_input(label="Name:", value="")
            star_option = st.selectbox(label='Star:', options=star_values, index=0)
            color_option = st.selectbox(label='Color:', options=color_values, index=0)
            speed_option = st.selectbox(label='Speed:', options=speed_values, index=0)
            class_option = st.selectbox(label='Class:', options=class_values, index=0)
            source_option = st.selectbox(label='Origin:', options=source_values, index=0)
    
            special_type_option = st.text_input(label="SpecialSkill Category", value="Hit 3")
            special_text_option = st.text_input(label="SpecialSkill Text", value="Dispel")
    with col2:
        st.header("Stat Filters")
        st.write("Tips: put the **minimum** att/def/hp stat you want to filter heroes")
        with st.expander("Stat Options"):   
            power_option = st.text_input(label="Power:", value="0")
            defense_option = st.text_input(label="Defense:", value="0")
            attack_option = st.text_input(label="Attack:", value="0")
            health_option = st.text_input(label="Health:", value="0")

            total_dot_option = st.text_input(label="Total DoT Damage:", value="0")
            dot_per_turn_option = st.text_input(label="DoT Damage Per Turn:", value="0")
    with col3:
        st.header("Sorted By")
        st.write("Tips: you can also directly click at the column name to sort")
        sort_option = st.selectbox(label='Sort by', options=display_cols[1:], index=5) # default is power
    
    idx_all = []

    if name_option != '':
        idx_all.append(filter_by_1col(df, 'name', name_option, exact_flag=False)) 

    if star_option != 'None':
        idx_all.append(filter_by_1col_num(df, 'star', star_option, oper_flag="eq"))    

    if speed_option != 'None':
        idx_all.append(filter_by_1col(df, 'speed', speed_option, exact_flag=True))    

    if color_option != 'None':
        idx_all.append(filter_by_1col(df, 'color', color_option, exact_flag=False))    

    if class_option != 'None':
        idx_all.append(filter_by_1col(df, 'class', class_option, exact_flag=False))    

    if source_option != 'None':
        idx_all.append(filter_by_1col(df, 'source', source_option, exact_flag=False))    

    if power_option != "0":
        power_option = int(power_option)
        idx_all.append(filter_by_1col_num(df, 'power', power_option, oper_flag="ge"))
    
    if defense_option != "0":
        defense_option = int(defense_option)
        idx_all.append(filter_by_1col_num(df, 'defense', defense_option, oper_flag="ge"))   

    if attack_option != "0":
        attack_option = int(attack_option)
        idx_all.append(filter_by_1col_num(df, 'attack', attack_option, oper_flag="ge"))   

    if health_option != "0":
        health_option = int(health_option)
        idx_all.append(filter_by_1col_num(df, 'health', health_option, oper_flag="ge"))

    if total_dot_option != "0":
        total_dot_option = int(total_dot_option)
        idx_all.append(filter_by_1col_num(df, 'total_dot_damage', total_dot_option, oper_flag="ge"))

    if dot_per_turn_option != "0":
        dot_per_turn_option = int(dot_per_turn_option)
        idx_all.append(filter_by_1col_num(df, 'dot_damage_per_turn', dot_per_turn_option, oper_flag="ge"))

    if special_type_option  != '':
        idx_all.append(filter_by_1col(df, 'types', special_type_option, exact_flag=False))    

    if special_text_option != '':
        idx_all.append(filter_by_1col(df, 'effects', special_text_option, exact_flag=False))    
    
    #########################################

    df2 = df[np.all(idx_all,axis=0)]

    display_heroes_from_df(df2.sort_values(sort_option, ascending=False))
#########################################
## Program 2 "Team Simulation"
elif genre == "Team Simulation":

    def choose_hero(key="Hero1"):
        name_choice = st.selectbox(label='Hero Name:', options=all_name_extra, index=0, key=key+"_name")
        costume_list = return_costume_list(df_extra, name_choice)
        costume_choice = st.selectbox(label='Costume:', options=costume_list, index=0, key=key+"_costume")
        lb_list = ['None', 'LB1', 'LB2']
        lb_choice = st.selectbox(label='Limit Break:', options=lb_list, index=0, key=key+"_lb")
    
        df_ret = return_hero_stat(df_extra, name_choice, lb_choice=lb_choice, costume_choice=costume_choice)
        return df_ret

    def write_short_description(df_hero):
        st.write(f'Power: {df_hero['power'].values[0]}')
        st.write(f'Attack: {df_hero['attack'].values[0]}')
        st.write(f'Defense: {df_hero['defense'].values[0]}')
        st.write(f'Health: {df_hero['health'].values[0]}')
    
    col1, col2, col3, col4, col5 = st.columns(5)
    with col1:
        df_hero1 = choose_hero(key="Hero1") # 'key' in st.selectbox to differentiate widgets
        write_short_description(df_hero1)
    with col2:
        df_hero2 = choose_hero(key="Hero2")
        write_short_description(df_hero2)
    with col3:
        df_hero3 = choose_hero(key="Hero3")
        write_short_description(df_hero3)
    with col4:
        df_hero4 = choose_hero(key="Hero4")
        write_short_description(df_hero4)
    with col5:
        df_hero5 = choose_hero(key="Hero5")
        write_short_description(df_hero5)

    df_hero_all5 = pd.concat([df_hero1, df_hero2, df_hero3, df_hero4, df_hero5])
    total_power = df_hero1['power'].values[0]+ df_hero2['power'].values[0]+ df_hero3['power'].values[0]+ df_hero4['power'].values[0]+ df_hero5['power'].values[0]
    st.write(f'Total power = {total_power}')
    display_heroes_from_df(df_hero_all5, display_cols=df_hero_all5.columns[:-2], show_df=False) # display all except special-skill text
    
#########################################
## Program 3 "Individual Stat"
else:
    
    
    st.header("Analyze Hero LB/CB Stat (without Emblem)")
    st.write("HeroPlan and DataVault are combined here. Thanks ***@Elioty33*** for his DataVault contribution")
    st.write(f"Currently, there are {len(df_extra)} heroes having both data on HeroPlan and DataVault.")
    st.write(f"We don't have emblem calculator here, you can go heroplan.io to do the job.")
    st.write(f"***Heuristically*** Choose Sword-path can increase att 100-150, def 50-100, hp ~100 (reverse att-def for shield path)")
    st.write(f"Choose HP-path can increase att 50-100, def 50-100, hp ~200")
    

    name_values = sorted(list(df_extra['name'].values))
    name_choice = st.selectbox(label='Hero Name:', options=name_values, index=0)

    costume_list = return_costume_list(df_extra, name_choice)
    costume_choice = st.selectbox(label='Costume:', options=costume_list, index=0)
    
    lb_list = ['None', 'LB1', 'LB2']
    lb_choice = st.selectbox(label='Limit Break:', options=lb_list, index=0)

    df_ret = return_hero_stat(df_extra, name_choice, lb_choice=lb_choice, costume_choice=costume_choice)
    display_heroes_from_df(df_ret,display_cols=df_ret.columns[:-2]) # display all except special-skill text