bcsfe 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bcsfe/__init__.py +12 -0
- bcsfe/__main__.py +64 -0
- bcsfe/cli/__init__.py +21 -0
- bcsfe/cli/color.py +189 -0
- bcsfe/cli/dialog_creator.py +682 -0
- bcsfe/cli/edits/__init__.py +23 -0
- bcsfe/cli/edits/aku_realm.py +11 -0
- bcsfe/cli/edits/basic_items.py +402 -0
- bcsfe/cli/edits/cat_editor.py +715 -0
- bcsfe/cli/edits/clear_tutorial.py +11 -0
- bcsfe/cli/edits/enemy_editor.py +192 -0
- bcsfe/cli/edits/event_tickets.py +147 -0
- bcsfe/cli/edits/fixes.py +43 -0
- bcsfe/cli/edits/map.py +249 -0
- bcsfe/cli/edits/rare_ticket_trade.py +45 -0
- bcsfe/cli/feature_handler.py +294 -0
- bcsfe/cli/file_dialog.py +154 -0
- bcsfe/cli/main.py +327 -0
- bcsfe/cli/save_management.py +529 -0
- bcsfe/cli/server_cli.py +71 -0
- bcsfe/core/__init__.py +361 -0
- bcsfe/core/country_code.py +104 -0
- bcsfe/core/crypto.py +164 -0
- bcsfe/core/game/__init__.py +3 -0
- bcsfe/core/game/battle/__init__.py +3 -0
- bcsfe/core/game/battle/battle_items.py +127 -0
- bcsfe/core/game/battle/cleared_slots.py +306 -0
- bcsfe/core/game/battle/enemy.py +132 -0
- bcsfe/core/game/battle/slots.py +200 -0
- bcsfe/core/game/catbase/__init__.py +49 -0
- bcsfe/core/game/catbase/beacon_base.py +67 -0
- bcsfe/core/game/catbase/cat.py +1243 -0
- bcsfe/core/game/catbase/drop_chara.py +74 -0
- bcsfe/core/game/catbase/gatya.py +454 -0
- bcsfe/core/game/catbase/gatya_item.py +155 -0
- bcsfe/core/game/catbase/item_pack.py +190 -0
- bcsfe/core/game/catbase/login_bonuses.py +184 -0
- bcsfe/core/game/catbase/matatabi.py +69 -0
- bcsfe/core/game/catbase/medals.py +171 -0
- bcsfe/core/game/catbase/mission.py +321 -0
- bcsfe/core/game/catbase/my_sale.py +58 -0
- bcsfe/core/game/catbase/nyanko_club.py +254 -0
- bcsfe/core/game/catbase/officer_pass.py +79 -0
- bcsfe/core/game/catbase/playtime.py +97 -0
- bcsfe/core/game/catbase/powerup.py +179 -0
- bcsfe/core/game/catbase/scheme_items.py +195 -0
- bcsfe/core/game/catbase/special_skill.py +331 -0
- bcsfe/core/game/catbase/stamp.py +58 -0
- bcsfe/core/game/catbase/talent_orbs.py +710 -0
- bcsfe/core/game/catbase/unlock_popups.py +71 -0
- bcsfe/core/game/catbase/upgrade.py +190 -0
- bcsfe/core/game/catbase/user_rank_rewards.py +276 -0
- bcsfe/core/game/gamoto/__init__.py +9 -0
- bcsfe/core/game/gamoto/base_materials.py +90 -0
- bcsfe/core/game/gamoto/cat_shrine.py +207 -0
- bcsfe/core/game/gamoto/catamins.py +59 -0
- bcsfe/core/game/gamoto/gamatoto.py +496 -0
- bcsfe/core/game/gamoto/ototo.py +660 -0
- bcsfe/core/game/localizable.py +31 -0
- bcsfe/core/game/map/__init__.py +41 -0
- bcsfe/core/game/map/aku.py +229 -0
- bcsfe/core/game/map/challenge.py +73 -0
- bcsfe/core/game/map/chapters.py +362 -0
- bcsfe/core/game/map/dojo.py +360 -0
- bcsfe/core/game/map/enigma.py +219 -0
- bcsfe/core/game/map/event.py +898 -0
- bcsfe/core/game/map/ex_stage.py +102 -0
- bcsfe/core/game/map/gauntlets.py +351 -0
- bcsfe/core/game/map/item_reward_stage.py +416 -0
- bcsfe/core/game/map/legend_quest.py +381 -0
- bcsfe/core/game/map/map_names.py +100 -0
- bcsfe/core/game/map/map_reset.py +116 -0
- bcsfe/core/game/map/outbreaks.py +264 -0
- bcsfe/core/game/map/story.py +1166 -0
- bcsfe/core/game/map/timed_score.py +193 -0
- bcsfe/core/game/map/tower.py +68 -0
- bcsfe/core/game/map/uncanny.py +47 -0
- bcsfe/core/game/map/zero_legends.py +295 -0
- bcsfe/core/game_version.py +244 -0
- bcsfe/core/io/__init__.py +31 -0
- bcsfe/core/io/adb_handler.py +181 -0
- bcsfe/core/io/bc_csv.py +263 -0
- bcsfe/core/io/command.py +50 -0
- bcsfe/core/io/config.py +449 -0
- bcsfe/core/io/data.py +647 -0
- bcsfe/core/io/git_handler.py +92 -0
- bcsfe/core/io/json_file.py +45 -0
- bcsfe/core/io/path.py +269 -0
- bcsfe/core/io/root_handler.py +105 -0
- bcsfe/core/io/save.py +3730 -0
- bcsfe/core/io/thread_helper.py +76 -0
- bcsfe/core/io/waydroid.py +120 -0
- bcsfe/core/io/yaml.py +61 -0
- bcsfe/core/locale_handler.py +713 -0
- bcsfe/core/log.py +133 -0
- bcsfe/core/max_value_helper.py +35 -0
- bcsfe/core/server/__init__.py +21 -0
- bcsfe/core/server/client_info.py +34 -0
- bcsfe/core/server/event_data.py +377 -0
- bcsfe/core/server/game_data_getter.py +217 -0
- bcsfe/core/server/headers.py +29 -0
- bcsfe/core/server/managed_item.py +203 -0
- bcsfe/core/server/request.py +64 -0
- bcsfe/core/server/server_handler.py +668 -0
- bcsfe/core/server/updater.py +86 -0
- bcsfe/core/theme_handler.py +308 -0
- bcsfe/files/locales/en/core/config.properties +94 -0
- bcsfe/files/locales/en/core/files.properties +9 -0
- bcsfe/files/locales/en/core/input.properties +29 -0
- bcsfe/files/locales/en/core/locale.properties +33 -0
- bcsfe/files/locales/en/core/main.properties +108 -0
- bcsfe/files/locales/en/core/save.properties +97 -0
- bcsfe/files/locales/en/core/server.properties +47 -0
- bcsfe/files/locales/en/core/theme.properties +24 -0
- bcsfe/files/locales/en/core/updater.properties +19 -0
- bcsfe/files/locales/en/edits/bannable_items.properties +30 -0
- bcsfe/files/locales/en/edits/cats.properties +118 -0
- bcsfe/files/locales/en/edits/enemy.properties +17 -0
- bcsfe/files/locales/en/edits/fixes.properties +13 -0
- bcsfe/files/locales/en/edits/gamototo.properties +54 -0
- bcsfe/files/locales/en/edits/gatya.properties +5 -0
- bcsfe/files/locales/en/edits/gold_pass.properties +6 -0
- bcsfe/files/locales/en/edits/items.properties +52 -0
- bcsfe/files/locales/en/edits/map.properties +122 -0
- bcsfe/files/locales/en/edits/medals.properties +8 -0
- bcsfe/files/locales/en/edits/missions.properties +7 -0
- bcsfe/files/locales/en/edits/playtime.properties +7 -0
- bcsfe/files/locales/en/edits/scheme_items.properties +6 -0
- bcsfe/files/locales/en/edits/special_skills.properties +10 -0
- bcsfe/files/locales/en/edits/talent_orbs.properties +26 -0
- bcsfe/files/locales/en/edits/treasures.properties +24 -0
- bcsfe/files/locales/en/edits/user_rank.properties +9 -0
- bcsfe/files/locales/vi/core/config.properties +84 -0
- bcsfe/files/locales/vi/core/files.properties +9 -0
- bcsfe/files/locales/vi/core/input.properties +28 -0
- bcsfe/files/locales/vi/core/locale.properties +29 -0
- bcsfe/files/locales/vi/core/main.properties +108 -0
- bcsfe/files/locales/vi/core/save.properties +99 -0
- bcsfe/files/locales/vi/core/server.properties +67 -0
- bcsfe/files/locales/vi/core/theme.properties +23 -0
- bcsfe/files/locales/vi/core/updater.properties +21 -0
- bcsfe/files/locales/vi/edits/bannable_items.properties +30 -0
- bcsfe/files/locales/vi/edits/cats.properties +125 -0
- bcsfe/files/locales/vi/edits/enemy.properties +17 -0
- bcsfe/files/locales/vi/edits/fixes.properties +13 -0
- bcsfe/files/locales/vi/edits/gamototo.properties +54 -0
- bcsfe/files/locales/vi/edits/gatya.properties +4 -0
- bcsfe/files/locales/vi/edits/gold_pass.properties +6 -0
- bcsfe/files/locales/vi/edits/items.properties +51 -0
- bcsfe/files/locales/vi/edits/map.properties +118 -0
- bcsfe/files/locales/vi/edits/medals.properties +8 -0
- bcsfe/files/locales/vi/edits/missions.properties +7 -0
- bcsfe/files/locales/vi/edits/playtime.properties +7 -0
- bcsfe/files/locales/vi/edits/scheme_items.properties +6 -0
- bcsfe/files/locales/vi/edits/special_skills.properties +10 -0
- bcsfe/files/locales/vi/edits/talent_orbs.properties +25 -0
- bcsfe/files/locales/vi/edits/treasures.properties +24 -0
- bcsfe/files/locales/vi/edits/user_rank.properties +9 -0
- bcsfe/files/locales/vi/metadata.json +4 -0
- bcsfe/files/max_values.json +26 -0
- bcsfe/files/themes/default.json +16 -0
- bcsfe/py.typed +0 -0
- bcsfe-3.0.0.dist-info/METADATA +393 -0
- bcsfe-3.0.0.dist-info/RECORD +168 -0
- bcsfe-3.0.0.dist-info/WHEEL +5 -0
- bcsfe-3.0.0.dist-info/entry_points.txt +2 -0
- bcsfe-3.0.0.dist-info/licenses/LICENSE +675 -0
- bcsfe-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any
|
|
3
|
+
from bcsfe import core
|
|
4
|
+
from bcsfe.cli import color
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RangeInput:
|
|
8
|
+
def __init__(self, max: int | None = None, min: int = 0):
|
|
9
|
+
self.max = max
|
|
10
|
+
self.min = min
|
|
11
|
+
|
|
12
|
+
def clamp_value(self, value: int) -> int:
|
|
13
|
+
if self.max is None:
|
|
14
|
+
return max(value, self.min)
|
|
15
|
+
return max(min(value, self.max), self.min)
|
|
16
|
+
|
|
17
|
+
def get_input_locale(
|
|
18
|
+
self,
|
|
19
|
+
dialog: str,
|
|
20
|
+
perameters: dict[str, int | str],
|
|
21
|
+
escape: bool = True,
|
|
22
|
+
) -> list[int] | None:
|
|
23
|
+
user_input = color.ColoredInput(end="").localize(
|
|
24
|
+
dialog, escape=escape, **perameters
|
|
25
|
+
)
|
|
26
|
+
return self.parse(user_input)
|
|
27
|
+
|
|
28
|
+
def parse(self, user_input: str) -> list[int] | None:
|
|
29
|
+
if user_input == "":
|
|
30
|
+
return []
|
|
31
|
+
if user_input == core.core_data.local_manager.get_key("quit_key"):
|
|
32
|
+
return None
|
|
33
|
+
parts = user_input.split(" ")
|
|
34
|
+
ids: list[int] = []
|
|
35
|
+
all_text = core.core_data.local_manager.get_key("all")
|
|
36
|
+
for part in parts:
|
|
37
|
+
if "-" in part and len(part.split("-")) == 2:
|
|
38
|
+
lower, upper = part.split("-")
|
|
39
|
+
try:
|
|
40
|
+
lower = int(lower)
|
|
41
|
+
upper = int(upper)
|
|
42
|
+
except ValueError:
|
|
43
|
+
continue
|
|
44
|
+
if lower > upper:
|
|
45
|
+
lower, upper = upper, lower
|
|
46
|
+
if self.max is not None:
|
|
47
|
+
lower = max(lower, self.min)
|
|
48
|
+
upper = min(upper, self.max)
|
|
49
|
+
else:
|
|
50
|
+
lower = max(lower, self.min)
|
|
51
|
+
ids.extend(range(lower, upper + 1))
|
|
52
|
+
elif part.lower() == all_text.lower() and self.max is not None:
|
|
53
|
+
ids.extend(range(self.min, self.max + 1))
|
|
54
|
+
else:
|
|
55
|
+
try:
|
|
56
|
+
part = int(part)
|
|
57
|
+
except ValueError:
|
|
58
|
+
continue
|
|
59
|
+
if self.max is not None:
|
|
60
|
+
part = max(part, self.min)
|
|
61
|
+
part = min(part, self.max)
|
|
62
|
+
else:
|
|
63
|
+
part = max(part, self.min)
|
|
64
|
+
ids.append(part)
|
|
65
|
+
return ids
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class IntInput:
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
max: int | None = None,
|
|
72
|
+
min: int = 0,
|
|
73
|
+
default: int | None = None,
|
|
74
|
+
signed: bool = True,
|
|
75
|
+
):
|
|
76
|
+
self.signed = signed
|
|
77
|
+
self.max = self.get_max_value(max, signed)
|
|
78
|
+
self.min = min
|
|
79
|
+
self.default = default
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def get_max_value(max: int | None, signed: bool = True) -> int:
|
|
83
|
+
disable_maxes = core.core_data.config.get_bool(core.ConfigKey.DISABLE_MAXES)
|
|
84
|
+
if signed:
|
|
85
|
+
max_int = 2**31 - 1
|
|
86
|
+
else:
|
|
87
|
+
max_int = 2**32 - 1
|
|
88
|
+
if disable_maxes or max is None:
|
|
89
|
+
return max_int
|
|
90
|
+
return min(max, max_int)
|
|
91
|
+
|
|
92
|
+
def clamp_value(self, value: int) -> int:
|
|
93
|
+
return max(min(value, self.max), self.min)
|
|
94
|
+
|
|
95
|
+
def get_input(
|
|
96
|
+
self,
|
|
97
|
+
localization_key: str,
|
|
98
|
+
perameters: dict[str, int | str],
|
|
99
|
+
escape: bool = True,
|
|
100
|
+
) -> tuple[int | None, str]:
|
|
101
|
+
user_input = color.ColoredInput(end="").localize(
|
|
102
|
+
localization_key, escape=escape, **perameters
|
|
103
|
+
)
|
|
104
|
+
if user_input == "" and self.default is not None:
|
|
105
|
+
return self.default, user_input
|
|
106
|
+
try:
|
|
107
|
+
user_input_i = int(user_input)
|
|
108
|
+
except ValueError:
|
|
109
|
+
return None, user_input
|
|
110
|
+
|
|
111
|
+
return self.clamp_value(user_input_i), user_input
|
|
112
|
+
|
|
113
|
+
def get_input_locale_while(
|
|
114
|
+
self, dialog: str, perameters: dict[str, int | str], escape: bool = True
|
|
115
|
+
) -> int | None:
|
|
116
|
+
while True:
|
|
117
|
+
int_val, user_input = self.get_input(dialog, perameters, escape=escape)
|
|
118
|
+
if int_val is not None:
|
|
119
|
+
return int_val
|
|
120
|
+
if user_input == core.core_data.local_manager.get_key("quit_key"):
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def get_input_locale(
|
|
124
|
+
self, localization_key: str | None, perameters: dict[str, int | str]
|
|
125
|
+
) -> tuple[int | None, str]:
|
|
126
|
+
if localization_key is None:
|
|
127
|
+
if self.default is not None:
|
|
128
|
+
perameters = {
|
|
129
|
+
"min": self.min,
|
|
130
|
+
"max": self.max,
|
|
131
|
+
"default": self.default,
|
|
132
|
+
}
|
|
133
|
+
localization_key = "input_int_default"
|
|
134
|
+
else:
|
|
135
|
+
perameters = {"min": self.min, "max": self.max}
|
|
136
|
+
localization_key = "input_int"
|
|
137
|
+
return self.get_input(localization_key, perameters)
|
|
138
|
+
|
|
139
|
+
def get_basic_input_locale(self, localization_key: str, perameters: dict[str, Any]):
|
|
140
|
+
try:
|
|
141
|
+
user_input = int(
|
|
142
|
+
color.ColoredInput(end="").localize(localization_key, **perameters)
|
|
143
|
+
)
|
|
144
|
+
except ValueError:
|
|
145
|
+
return None
|
|
146
|
+
return user_input
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ListOutput:
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
strings: list[str],
|
|
153
|
+
ints: list[int],
|
|
154
|
+
dialog: str | None = None,
|
|
155
|
+
perameters: dict[str, Any] | None = None,
|
|
156
|
+
start_index: int = 1,
|
|
157
|
+
localize_elements: bool = True,
|
|
158
|
+
):
|
|
159
|
+
self.strings = strings
|
|
160
|
+
self.ints = ints
|
|
161
|
+
self.dialog = dialog
|
|
162
|
+
if perameters is None:
|
|
163
|
+
perameters = {}
|
|
164
|
+
self.perameters = perameters
|
|
165
|
+
self.start_index = start_index
|
|
166
|
+
self.localize_elements = localize_elements
|
|
167
|
+
|
|
168
|
+
def get_output(self, dialog: str | None, strings: list[str]) -> str:
|
|
169
|
+
end_string = ""
|
|
170
|
+
if dialog is not None:
|
|
171
|
+
end_string = core.core_data.local_manager.get_key(dialog, **self.perameters)
|
|
172
|
+
end_string += "\n"
|
|
173
|
+
for i, string in enumerate(strings):
|
|
174
|
+
try:
|
|
175
|
+
int_string = str(self.ints[i])
|
|
176
|
+
except IndexError:
|
|
177
|
+
int_string = ""
|
|
178
|
+
|
|
179
|
+
string = string.replace("{int}", int_string)
|
|
180
|
+
end_string += f" <@s>{i + self.start_index}.</> <@t>{string}</>\n"
|
|
181
|
+
end_string = end_string.strip("\n")
|
|
182
|
+
return end_string
|
|
183
|
+
|
|
184
|
+
def display(self, dialog: str | None, strings: list[str]) -> None:
|
|
185
|
+
output = self.get_output(dialog, strings)
|
|
186
|
+
color.ColoredText(output)
|
|
187
|
+
|
|
188
|
+
def display_locale(self, remove_alias: bool = False) -> None:
|
|
189
|
+
dialog = ""
|
|
190
|
+
if self.dialog is not None:
|
|
191
|
+
dialog = core.core_data.local_manager.get_key(self.dialog)
|
|
192
|
+
new_strings: list[str] = []
|
|
193
|
+
for string in self.strings:
|
|
194
|
+
if self.localize_elements:
|
|
195
|
+
string_ = core.core_data.local_manager.get_key(string)
|
|
196
|
+
else:
|
|
197
|
+
string_ = string
|
|
198
|
+
if remove_alias:
|
|
199
|
+
string_ = core.core_data.local_manager.get_all_aliases(string_)[0]
|
|
200
|
+
new_strings.append(string_)
|
|
201
|
+
|
|
202
|
+
self.display(dialog, new_strings)
|
|
203
|
+
|
|
204
|
+
def display_non_locale(self) -> None:
|
|
205
|
+
self.display(self.dialog, self.strings)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ChoiceInput:
|
|
209
|
+
def __init__(
|
|
210
|
+
self,
|
|
211
|
+
items: list[str],
|
|
212
|
+
strings: list[str],
|
|
213
|
+
ints: list[int],
|
|
214
|
+
perameters: dict[str, int | str],
|
|
215
|
+
dialog: str,
|
|
216
|
+
single_choice: bool = False,
|
|
217
|
+
remove_alias: bool = False,
|
|
218
|
+
display_all_at_once: bool = True,
|
|
219
|
+
start_index: int = 1,
|
|
220
|
+
localize_options: bool = True,
|
|
221
|
+
):
|
|
222
|
+
self.items = items
|
|
223
|
+
self.strings = strings
|
|
224
|
+
self.ints = ints
|
|
225
|
+
self.perameters = perameters
|
|
226
|
+
self.dialog = dialog
|
|
227
|
+
self.is_single_choice = single_choice
|
|
228
|
+
self.remove_alias = remove_alias
|
|
229
|
+
self.display_all_at_once = display_all_at_once
|
|
230
|
+
self.start_index = start_index
|
|
231
|
+
self.localize_options = localize_options
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def from_reduced(
|
|
235
|
+
items: list[str],
|
|
236
|
+
ints: list[int] | None = None,
|
|
237
|
+
perameters: dict[str, int | str] | None = None,
|
|
238
|
+
dialog: str | None = None,
|
|
239
|
+
single_choice: bool = False,
|
|
240
|
+
remove_alias: bool = False,
|
|
241
|
+
display_all_at_once: bool = True,
|
|
242
|
+
start_index: int = 1,
|
|
243
|
+
localize_options: bool = True,
|
|
244
|
+
) -> ChoiceInput:
|
|
245
|
+
if perameters is None:
|
|
246
|
+
perameters = {}
|
|
247
|
+
if ints is None:
|
|
248
|
+
ints = []
|
|
249
|
+
if dialog is None:
|
|
250
|
+
dialog = ""
|
|
251
|
+
return ChoiceInput(
|
|
252
|
+
items.copy(),
|
|
253
|
+
items.copy(),
|
|
254
|
+
ints.copy(),
|
|
255
|
+
perameters.copy(),
|
|
256
|
+
dialog,
|
|
257
|
+
single_choice,
|
|
258
|
+
remove_alias,
|
|
259
|
+
display_all_at_once,
|
|
260
|
+
start_index,
|
|
261
|
+
localize_options,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def get_input(self) -> tuple[int | None, str]:
|
|
265
|
+
if len(self.strings) == 0:
|
|
266
|
+
return None, ""
|
|
267
|
+
if len(self.strings) == 1:
|
|
268
|
+
return self.get_min_value(), ""
|
|
269
|
+
ListOutput(
|
|
270
|
+
self.strings,
|
|
271
|
+
self.ints,
|
|
272
|
+
start_index=self.start_index,
|
|
273
|
+
localize_elements=self.localize_options,
|
|
274
|
+
).display_locale(self.remove_alias)
|
|
275
|
+
return IntInput(self.get_max_value(), self.get_min_value()).get_input_locale(
|
|
276
|
+
self.dialog, self.perameters
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def get_input_while(self) -> int | None:
|
|
280
|
+
if len(self.strings) == 0:
|
|
281
|
+
return None
|
|
282
|
+
while True:
|
|
283
|
+
int_val, user_input = self.get_input()
|
|
284
|
+
if int_val is not None:
|
|
285
|
+
return int_val
|
|
286
|
+
if user_input == core.core_data.local_manager.get_key("quit_key"):
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def get_max_value(self) -> int:
|
|
290
|
+
return len(self.strings) + self.start_index - 1
|
|
291
|
+
|
|
292
|
+
def get_min_value(self) -> int:
|
|
293
|
+
return self.start_index
|
|
294
|
+
|
|
295
|
+
def get_input_locale(self, localized: bool = True) -> tuple[list[int] | None, bool]:
|
|
296
|
+
if len(self.strings) == 0:
|
|
297
|
+
return [], False
|
|
298
|
+
if len(self.strings) == 1:
|
|
299
|
+
return [self.get_min_value()], False
|
|
300
|
+
if not self.is_single_choice and self.display_all_at_once:
|
|
301
|
+
if localized:
|
|
302
|
+
self.strings.append("all_at_once")
|
|
303
|
+
else:
|
|
304
|
+
self.strings.append(core.core_data.local_manager.get_key("all_at_once"))
|
|
305
|
+
if localized:
|
|
306
|
+
ListOutput(
|
|
307
|
+
self.strings,
|
|
308
|
+
self.ints,
|
|
309
|
+
start_index=self.start_index,
|
|
310
|
+
localize_elements=self.localize_options,
|
|
311
|
+
).display_locale()
|
|
312
|
+
else:
|
|
313
|
+
ListOutput(
|
|
314
|
+
self.strings,
|
|
315
|
+
self.ints,
|
|
316
|
+
start_index=self.start_index,
|
|
317
|
+
localize_elements=self.localize_options,
|
|
318
|
+
).display_non_locale()
|
|
319
|
+
key = "input_many"
|
|
320
|
+
if self.is_single_choice:
|
|
321
|
+
key = "input_single"
|
|
322
|
+
dialog = core.core_data.local_manager.get_key(key).format(
|
|
323
|
+
min=self.get_min_value(), max=self.get_max_value()
|
|
324
|
+
)
|
|
325
|
+
usr_input = color.ColoredInput().get(dialog).split(" ")
|
|
326
|
+
int_vals: list[int] = []
|
|
327
|
+
for inp in usr_input:
|
|
328
|
+
try:
|
|
329
|
+
value = int(inp)
|
|
330
|
+
if value > self.get_max_value() or value < self.get_min_value():
|
|
331
|
+
raise ValueError
|
|
332
|
+
int_vals.append(value)
|
|
333
|
+
except ValueError:
|
|
334
|
+
if inp == core.core_data.local_manager.get_key("quit_key"):
|
|
335
|
+
return None, False
|
|
336
|
+
if inp in self.strings:
|
|
337
|
+
int_vals.append(self.strings.index(inp) + self.start_index)
|
|
338
|
+
continue
|
|
339
|
+
color.ColoredText.localize(
|
|
340
|
+
"invalid_input_int",
|
|
341
|
+
min=self.get_min_value(),
|
|
342
|
+
max=self.get_max_value(),
|
|
343
|
+
)
|
|
344
|
+
if (
|
|
345
|
+
self.get_max_value() in int_vals
|
|
346
|
+
and not self.is_single_choice
|
|
347
|
+
and self.display_all_at_once
|
|
348
|
+
):
|
|
349
|
+
return list(range(self.get_min_value(), self.get_max_value())), True
|
|
350
|
+
|
|
351
|
+
if self.is_single_choice and len(int_vals) > 1:
|
|
352
|
+
int_vals = [int_vals[0]]
|
|
353
|
+
|
|
354
|
+
return int_vals, False
|
|
355
|
+
|
|
356
|
+
def get_input_locale_while(self) -> list[int] | None:
|
|
357
|
+
if len(self.strings) == 0:
|
|
358
|
+
return []
|
|
359
|
+
if len(self.strings) == 1:
|
|
360
|
+
return [self.get_min_value()]
|
|
361
|
+
while True:
|
|
362
|
+
int_vals, all_at_once = self.get_input_locale()
|
|
363
|
+
if int_vals is None:
|
|
364
|
+
return None
|
|
365
|
+
if all_at_once:
|
|
366
|
+
return int_vals
|
|
367
|
+
if len(int_vals) == 0:
|
|
368
|
+
continue
|
|
369
|
+
if len(int_vals) == 1 and int_vals[0] == 0:
|
|
370
|
+
return []
|
|
371
|
+
return int_vals
|
|
372
|
+
|
|
373
|
+
def multiple_choice(
|
|
374
|
+
self, localized_options: bool = True
|
|
375
|
+
) -> tuple[list[int] | None, bool]:
|
|
376
|
+
color.ColoredText.localize(self.dialog, True, **self.perameters)
|
|
377
|
+
user_input, all_at_once = self.get_input_locale(localized_options)
|
|
378
|
+
if user_input is None:
|
|
379
|
+
return None, all_at_once
|
|
380
|
+
return [i - self.start_index for i in user_input], all_at_once
|
|
381
|
+
|
|
382
|
+
def single_choice(self) -> int | None:
|
|
383
|
+
return self.get_input_while()
|
|
384
|
+
|
|
385
|
+
def get(self) -> tuple[int | None | list[int], bool]:
|
|
386
|
+
if self.is_single_choice:
|
|
387
|
+
return self.single_choice(), False
|
|
388
|
+
return self.multiple_choice()
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class MultiEditor:
|
|
392
|
+
def __init__(
|
|
393
|
+
self,
|
|
394
|
+
group_name: str,
|
|
395
|
+
items: list[str],
|
|
396
|
+
strings: list[str],
|
|
397
|
+
ints: list[int] | None,
|
|
398
|
+
max_values: list[int] | int | None,
|
|
399
|
+
perameters: dict[str, int | str] | None,
|
|
400
|
+
dialog: str,
|
|
401
|
+
single_choice: bool = False,
|
|
402
|
+
signed: bool = True,
|
|
403
|
+
group_name_localized: bool = False,
|
|
404
|
+
cumulative_max: bool = False,
|
|
405
|
+
):
|
|
406
|
+
self.items = items
|
|
407
|
+
self.strings = strings
|
|
408
|
+
self.ints = ints
|
|
409
|
+
if self.ints is not None:
|
|
410
|
+
total_ints = len(self.ints)
|
|
411
|
+
else:
|
|
412
|
+
total_ints = len(self.strings)
|
|
413
|
+
if max_values is None:
|
|
414
|
+
max_values_ = [None] * total_ints
|
|
415
|
+
elif isinstance(max_values, int):
|
|
416
|
+
max_values_ = [max_values] * total_ints
|
|
417
|
+
else:
|
|
418
|
+
max_values_ = max_values
|
|
419
|
+
self.max_values = max_values_
|
|
420
|
+
if perameters is None:
|
|
421
|
+
perameters = {}
|
|
422
|
+
self.perameters = perameters
|
|
423
|
+
if group_name_localized:
|
|
424
|
+
self.perameters["group_name"] = core.core_data.local_manager.get_key(
|
|
425
|
+
group_name
|
|
426
|
+
)
|
|
427
|
+
else:
|
|
428
|
+
self.perameters["group_name"] = group_name
|
|
429
|
+
self.dialog = dialog
|
|
430
|
+
self.is_single_choice = single_choice
|
|
431
|
+
self.signed = signed
|
|
432
|
+
self.cumulative_max = cumulative_max
|
|
433
|
+
|
|
434
|
+
@staticmethod
|
|
435
|
+
def from_reduced(
|
|
436
|
+
group_name: str,
|
|
437
|
+
items: list[str],
|
|
438
|
+
ints: list[int] | None,
|
|
439
|
+
max_values: list[int] | int | None,
|
|
440
|
+
group_name_localized: bool = False,
|
|
441
|
+
dialog: str = "input",
|
|
442
|
+
cumulative_max: bool = False,
|
|
443
|
+
items_localized: bool = False,
|
|
444
|
+
):
|
|
445
|
+
if items_localized:
|
|
446
|
+
for i, item in enumerate(items):
|
|
447
|
+
items[i] = core.core_data.local_manager.get_key(item)
|
|
448
|
+
text: list[str] = []
|
|
449
|
+
for item_name in items:
|
|
450
|
+
if ints is not None:
|
|
451
|
+
text.append(f"{item_name} <@q>: {{int}}</>")
|
|
452
|
+
else:
|
|
453
|
+
text.append(f"{item_name}")
|
|
454
|
+
return MultiEditor(
|
|
455
|
+
group_name,
|
|
456
|
+
items,
|
|
457
|
+
text,
|
|
458
|
+
ints,
|
|
459
|
+
max_values,
|
|
460
|
+
None,
|
|
461
|
+
dialog,
|
|
462
|
+
group_name_localized=group_name_localized,
|
|
463
|
+
cumulative_max=cumulative_max,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
def edit(self) -> list[int]:
|
|
467
|
+
choices, all_at_once = ChoiceInput(
|
|
468
|
+
self.items,
|
|
469
|
+
self.strings,
|
|
470
|
+
self.ints or [],
|
|
471
|
+
self.perameters,
|
|
472
|
+
"select_edit",
|
|
473
|
+
).get()
|
|
474
|
+
if choices is None:
|
|
475
|
+
return self.ints or []
|
|
476
|
+
if isinstance(choices, int):
|
|
477
|
+
choices = [choices]
|
|
478
|
+
if all_at_once:
|
|
479
|
+
return self.edit_all(choices)
|
|
480
|
+
return self.edit_one(choices)
|
|
481
|
+
|
|
482
|
+
def edit_all(self, choices: list[int]) -> list[int]:
|
|
483
|
+
max_max_value = 0
|
|
484
|
+
for choice in choices:
|
|
485
|
+
if choice >= len(self.max_values):
|
|
486
|
+
max_value = None
|
|
487
|
+
else:
|
|
488
|
+
max_value = self.max_values[choice]
|
|
489
|
+
if max_value is None:
|
|
490
|
+
max_value = IntInput.get_max_value(max_value, self.signed)
|
|
491
|
+
max_max_value = max(max_max_value, max_value)
|
|
492
|
+
if self.cumulative_max:
|
|
493
|
+
max_max_value = max_max_value // len(choices)
|
|
494
|
+
usr_input = IntInput(max_max_value, default=None).get_input_locale_while(
|
|
495
|
+
self.dialog + "_all",
|
|
496
|
+
{
|
|
497
|
+
"name": self.perameters["group_name"],
|
|
498
|
+
"max": max_max_value,
|
|
499
|
+
},
|
|
500
|
+
)
|
|
501
|
+
if usr_input is None:
|
|
502
|
+
return self.ints or []
|
|
503
|
+
ints = self.ints or [0] * len(self.strings)
|
|
504
|
+
|
|
505
|
+
for choice in choices:
|
|
506
|
+
if choice >= len(self.max_values):
|
|
507
|
+
max_value = None
|
|
508
|
+
else:
|
|
509
|
+
max_value = self.max_values[choice]
|
|
510
|
+
max_value = IntInput.get_max_value(max_value, self.signed)
|
|
511
|
+
value = min(usr_input, max_value)
|
|
512
|
+
ints[choice] = value
|
|
513
|
+
if self.ints is not None:
|
|
514
|
+
color.ColoredText.localize(
|
|
515
|
+
"value_changed",
|
|
516
|
+
name=self.items[choice],
|
|
517
|
+
value=value,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
return ints
|
|
521
|
+
|
|
522
|
+
def edit_one(self, choices: list[int]) -> list[int]:
|
|
523
|
+
ints = self.ints or [0] * len(self.strings)
|
|
524
|
+
|
|
525
|
+
for choice in choices:
|
|
526
|
+
if choice >= len(self.max_values):
|
|
527
|
+
max_value = None
|
|
528
|
+
else:
|
|
529
|
+
max_value = self.max_values[choice]
|
|
530
|
+
if max_value is None:
|
|
531
|
+
max_value = IntInput.get_max_value(max_value, self.signed)
|
|
532
|
+
|
|
533
|
+
if self.cumulative_max:
|
|
534
|
+
max_value -= sum(ints) - ints[choice]
|
|
535
|
+
max_value = max(max_value, 0)
|
|
536
|
+
|
|
537
|
+
item = self.items[choice]
|
|
538
|
+
usr_input = IntInput(
|
|
539
|
+
max_value, default=ints[choice]
|
|
540
|
+
).get_input_locale_while(
|
|
541
|
+
self.dialog,
|
|
542
|
+
{"name": item, "value": ints[choice], "max": max_value},
|
|
543
|
+
escape=False,
|
|
544
|
+
)
|
|
545
|
+
if usr_input is None:
|
|
546
|
+
continue
|
|
547
|
+
ints[choice] = usr_input
|
|
548
|
+
color.ColoredText.localize(
|
|
549
|
+
"value_changed", name=item, value=ints[choice], escape=False
|
|
550
|
+
)
|
|
551
|
+
return ints
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
class SingleEditor:
|
|
555
|
+
def __init__(
|
|
556
|
+
self,
|
|
557
|
+
item: str,
|
|
558
|
+
value: int,
|
|
559
|
+
max_value: int | None = None,
|
|
560
|
+
min_value: int = 0,
|
|
561
|
+
signed: bool = True,
|
|
562
|
+
localized_item: bool = False,
|
|
563
|
+
remove_alias: bool = False,
|
|
564
|
+
):
|
|
565
|
+
if localized_item:
|
|
566
|
+
item = core.core_data.local_manager.get_key(item)
|
|
567
|
+
if remove_alias:
|
|
568
|
+
item = core.core_data.local_manager.get_all_aliases(item)[0]
|
|
569
|
+
self.item = item
|
|
570
|
+
self.value = value
|
|
571
|
+
self.max_value = max_value
|
|
572
|
+
self.min_value = min_value
|
|
573
|
+
self.signed = signed
|
|
574
|
+
|
|
575
|
+
def edit(self, escape_text: bool = True) -> int:
|
|
576
|
+
max_value = self.max_value
|
|
577
|
+
if max_value is None:
|
|
578
|
+
max_value = IntInput.get_max_value(max_value, self.signed)
|
|
579
|
+
if self.max_value is None:
|
|
580
|
+
dialog = "input_non_max"
|
|
581
|
+
elif self.min_value != 0:
|
|
582
|
+
dialog = "input_min"
|
|
583
|
+
else:
|
|
584
|
+
dialog = "input"
|
|
585
|
+
usr_input = IntInput(
|
|
586
|
+
max_value, self.min_value, default=self.value, signed=self.signed
|
|
587
|
+
).get_input_locale_while(
|
|
588
|
+
dialog,
|
|
589
|
+
{
|
|
590
|
+
"name": self.item,
|
|
591
|
+
"value": self.value,
|
|
592
|
+
"max": max_value,
|
|
593
|
+
"min": self.min_value,
|
|
594
|
+
},
|
|
595
|
+
escape=escape_text,
|
|
596
|
+
)
|
|
597
|
+
if usr_input is None:
|
|
598
|
+
return self.value
|
|
599
|
+
print()
|
|
600
|
+
color.ColoredText.localize(
|
|
601
|
+
"value_changed", name=self.item, value=usr_input, escape=escape_text
|
|
602
|
+
)
|
|
603
|
+
return usr_input
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class StringInput:
|
|
607
|
+
def __init__(self, default: str = ""):
|
|
608
|
+
self.default = default
|
|
609
|
+
|
|
610
|
+
def get_input_locale_while(
|
|
611
|
+
self, key: str, perameters: dict[str, Any]
|
|
612
|
+
) -> str | None:
|
|
613
|
+
while True:
|
|
614
|
+
usr_input = self.get_input_locale(key, perameters)
|
|
615
|
+
if usr_input is None:
|
|
616
|
+
return None
|
|
617
|
+
if usr_input == "":
|
|
618
|
+
return self.default
|
|
619
|
+
if usr_input == " ":
|
|
620
|
+
continue
|
|
621
|
+
return usr_input
|
|
622
|
+
|
|
623
|
+
def get_input_locale(
|
|
624
|
+
self,
|
|
625
|
+
key: str,
|
|
626
|
+
perameters: dict[str, Any] | None = None,
|
|
627
|
+
escape: bool = True,
|
|
628
|
+
) -> str | None:
|
|
629
|
+
if perameters is None:
|
|
630
|
+
perameters = {}
|
|
631
|
+
usr_input = color.ColoredInput().localize(key, escape, **perameters)
|
|
632
|
+
quit_key = core.core_data.local_manager.get_key("quit_key")
|
|
633
|
+
if usr_input == "" or usr_input == quit_key:
|
|
634
|
+
return None
|
|
635
|
+
if usr_input == f"\\{quit_key}":
|
|
636
|
+
return quit_key
|
|
637
|
+
return usr_input
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
class StringEditor:
|
|
641
|
+
def __init__(self, item: str, value: str, item_localized: bool = False):
|
|
642
|
+
if item_localized:
|
|
643
|
+
item = core.core_data.local_manager.get_key(item)
|
|
644
|
+
self.item = item
|
|
645
|
+
self.value = value
|
|
646
|
+
|
|
647
|
+
def edit(self) -> str:
|
|
648
|
+
usr_input = StringInput(default=self.value).get_input_locale_while(
|
|
649
|
+
"input_non_max",
|
|
650
|
+
{"name": self.item, "value": self.value},
|
|
651
|
+
)
|
|
652
|
+
if usr_input is None:
|
|
653
|
+
return self.value
|
|
654
|
+
color.ColoredText.localize(
|
|
655
|
+
"value_changed",
|
|
656
|
+
name=self.item,
|
|
657
|
+
value=usr_input,
|
|
658
|
+
)
|
|
659
|
+
return usr_input
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
class YesNoInput:
|
|
663
|
+
def __init__(self, default: bool = False):
|
|
664
|
+
self.default = default
|
|
665
|
+
|
|
666
|
+
def get_input_once(
|
|
667
|
+
self, key: str, perameters: dict[str, Any] | None = None
|
|
668
|
+
) -> bool | None:
|
|
669
|
+
if perameters is None:
|
|
670
|
+
perameters = {}
|
|
671
|
+
usr_input = color.ColoredInput().localize(key, **perameters)
|
|
672
|
+
if usr_input == "":
|
|
673
|
+
return self.default
|
|
674
|
+
|
|
675
|
+
if usr_input == core.core_data.local_manager.get_key("quit_key"):
|
|
676
|
+
return None
|
|
677
|
+
return usr_input == core.core_data.local_manager.get_key("yes_key")
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class DialogBuilder:
|
|
681
|
+
def __init__(self, dialog_structure: dict[Any, Any]):
|
|
682
|
+
self.dialog_structure = dialog_structure
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from bcsfe.cli.edits import (
|
|
2
|
+
basic_items,
|
|
3
|
+
cat_editor,
|
|
4
|
+
clear_tutorial,
|
|
5
|
+
rare_ticket_trade,
|
|
6
|
+
fixes,
|
|
7
|
+
enemy_editor,
|
|
8
|
+
aku_realm,
|
|
9
|
+
map,
|
|
10
|
+
event_tickets,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"basic_items",
|
|
15
|
+
"cat_editor",
|
|
16
|
+
"clear_tutorial",
|
|
17
|
+
"rare_ticket_trade",
|
|
18
|
+
"fixes",
|
|
19
|
+
"enemy_editor",
|
|
20
|
+
"aku_realm",
|
|
21
|
+
"map",
|
|
22
|
+
"event_tickets"
|
|
23
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from bcsfe import core
|
|
3
|
+
from bcsfe.cli import color
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def unlock_aku_realm(save_file: core.SaveFile):
|
|
7
|
+
stage_ids = [255, 256, 257, 258, 265, 266, 268]
|
|
8
|
+
for stage_id in stage_ids:
|
|
9
|
+
save_file.event_stages.clear_map(1, stage_id, 0, False)
|
|
10
|
+
|
|
11
|
+
color.ColoredText.localize("aku_realm_unlocked")
|