-
Notifications
You must be signed in to change notification settings - Fork 33
/
colorscheme.py
428 lines (359 loc) · 14.9 KB
/
colorscheme.py
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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# This plugin for Bombsquad allows players to create any custom RGB
# colorscheme to override the game's default colorscheme.
# Inspired by Smoothy's pink and dark colorscheme plugins!
# Enable the plugin and enter "colorscheme" without quotes in
# Settings -> Advanced -> Enter Code
# to bring up the colorscheme UI.
# ba_meta require api 8
import _babase
import babase
import bauiv1 as bui
from bauiv1lib.colorpicker import ColorPicker
original_buttonwidget = bui.buttonwidget
original_containerwidget = bui.containerwidget
original_checkboxwidget = bui.checkboxwidget
# We set this later so we store the overridden method in case the
# player is using pro-unlocker plugins that override the
# `bui.app.classic.accounts.have_pro` method.
original_have_pro = None
original_add_transaction = bui.app.plus.add_v1_account_transaction
class ColorScheme:
"""
Apply a colorscheme to the game. Can also be invoked directly
through the BombSquad's in-game console. See examples for more
details.
Parameters
----------
color: `tuple`
A tuple consisting of (R,G,B) channel values where each channel
is a float ranging between 0 to 1.
highlight: `tuple`
A tuple consisting of (R,G,B) channel values where each channel
is a float ranging between 0 to 1.
Examples
--------
+ Apply dark colorscheme:
>>> import _babase
>>> dark = _babase.ColorScheme((0.2,0.2,0.2), (0.8,0.8,0.8))
>>> dark.apply()
# Reset back to game's default colorscheme
>>> dark.disable()
+ Colorscheme that modifies only the main colors:
>>> import _babase
>>> bluey = _babase.ColorScheme(color=(0.1,0.3,0.6))
>>> bluey.apply()
# Reset back to game's default colorscheme
>>> bluey.disable()
+ Colorscheme that modifies only the highlight colors:
>>> import _babase
>>> reddish = _babase.ColorScheme(highlight=(0.8,0.35,0.35))
>>> reddish.apply()
# Reset back to game's default colorscheme
>>> reddish.disable()
+ Revert back to game's default colorscheme irrespective of
whatever colorscheme is active at the moment:
>>> import _babase
>>> _babase.ColorScheme.disable()
"""
def __init__(self, color=None, highlight=None):
self.color = color
self.highlight = highlight
def _custom_buttonwidget(self, *args, **kwargs):
assert self.highlight is not None
kwargs["color"] = self.highlight
return original_buttonwidget(*args, **kwargs)
def _custom_containerwidget(self, *args, **kwargs):
assert self.color is not None
kwargs["color"] = self.color
return original_containerwidget(*args, **kwargs)
def _custom_checkboxwidget(self, *args, **kwargs):
assert self.highlight is not None
kwargs["color"] = self.highlight
return original_checkboxwidget(*args, **kwargs)
def _apply_color(self):
if self.color is None:
raise TypeError("Expected color to be an (R,G,B) tuple.")
bui.containerwidget = self._custom_containerwidget
def _apply_highlight(self):
if self.highlight is None:
raise TypeError("Expected highlight to be an (R,G,B) tuple.")
bui.buttonwidget = self._custom_buttonwidget
bui.checkboxwidget = self._custom_checkboxwidget
def apply(self):
if self.color:
self._apply_color()
if self.highlight:
self._apply_highlight()
@staticmethod
def _disable_color():
bui.buttonwidget = original_buttonwidget
bui.checkboxwidget = original_checkboxwidget
@staticmethod
def _disable_highlight():
bui.containerwidget = original_containerwidget
@classmethod
def disable(cls):
cls._disable_color()
cls._disable_highlight()
class ColorSchemeWindow(bui.Window):
def __init__(self, default_colors=((0.41, 0.39, 0.5), (0.5, 0.7, 0.25))):
self._default_colors = default_colors
self._color, self._highlight = babase.app.config.get("ColorScheme", (None, None))
self._last_color = self._color
self._last_highlight = self._highlight
# Let's set the game's default colorscheme before opening the Window.
# Otherwise the colors in the Window are tinted as per the already
# applied custom colorscheme thereby making it impossible to visually
# differentiate between different colors.
ColorScheme.disable()
# A hack to let players select any RGB color value through the UI,
# otherwise this is limited only to pro accounts.
bui.app.classic.accounts.have_pro = lambda: True
self.draw_ui()
def draw_ui(self):
# NOTE: Most of the stuff here for drawing the UI is referred from the
# legacy (1.6 < version <= 1.7.19) game's bastd/ui/profile/edit.py, and
# so there could be some cruft here due to my oversight.
uiscale = bui.app.ui_v1.uiscale
self._width = width = 480.0 if uiscale is babase.UIScale.SMALL else 380.0
self._x_inset = x_inset = 40.0 if uiscale is babase.UIScale.SMALL else 0.0
self._height = height = (
275.0
if uiscale is babase.UIScale.SMALL
else 288.0
if uiscale is babase.UIScale.MEDIUM
else 300.0
)
spacing = 40
self._base_scale = (
2.05
if uiscale is babase.UIScale.SMALL
else 1.5
if uiscale is babase.UIScale.MEDIUM
else 1.0
)
top_extra = 15
super().__init__(
root_widget=bui.containerwidget(
size=(width, height + top_extra),
on_outside_click_call=self.cancel_on_outside_click,
transition="in_right",
scale=self._base_scale,
stack_offset=(0, 15) if uiscale is babase.UIScale.SMALL else (0, 0),
)
)
cancel_button = bui.buttonwidget(
parent=self._root_widget,
position=(52 + x_inset, height - 60),
size=(155, 60),
scale=0.8,
autoselect=True,
label=babase.Lstr(resource="cancelText"),
on_activate_call=self._cancel,
)
bui.containerwidget(edit=self._root_widget, cancel_button=cancel_button)
save_button = bui.buttonwidget(
parent=self._root_widget,
position=(width - (177 + x_inset), height - 110),
size=(155, 60),
autoselect=True,
scale=0.8,
label=babase.Lstr(resource="saveText"),
)
bui.widget(edit=save_button, left_widget=cancel_button)
bui.buttonwidget(edit=save_button, on_activate_call=self.save)
bui.widget(edit=cancel_button, right_widget=save_button)
bui.containerwidget(edit=self._root_widget, start_button=save_button)
reset_button = bui.buttonwidget(
parent=self._root_widget,
position=(width - (177 + x_inset), height - 60),
size=(155, 60),
color=(0.2, 0.5, 0.6),
autoselect=True,
scale=0.8,
label=babase.Lstr(resource="settingsWindowAdvanced.resetText"),
)
bui.widget(edit=reset_button, left_widget=reset_button)
bui.buttonwidget(edit=reset_button, on_activate_call=self.reset)
bui.widget(edit=cancel_button, right_widget=reset_button)
bui.containerwidget(edit=self._root_widget, start_button=reset_button)
v = height - 65.0
v -= spacing * 3.0
b_size = 80
b_offs = 75
self._color_button = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50),
size=(b_size, b_size),
color=self._last_color or self._default_colors[0],
label="",
button_type="square",
)
bui.buttonwidget(
edit=self._color_button, on_activate_call=babase.Call(self._pick_color, "color")
)
bui.textwidget(
parent=self._root_widget,
h_align="center",
v_align="center",
position=(self._width * 0.5 - b_offs, v - 65),
size=(0, 0),
draw_controller=self._color_button,
text=babase.Lstr(resource="editProfileWindow.colorText"),
scale=0.7,
color=bui.app.ui_v1.title_color,
maxwidth=120,
)
self._highlight_button = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50),
size=(b_size, b_size),
color=self._last_highlight or self._default_colors[1],
label="",
button_type="square",
)
bui.buttonwidget(
edit=self._highlight_button,
on_activate_call=babase.Call(self._pick_color, "highlight"),
)
bui.textwidget(
parent=self._root_widget,
h_align="center",
v_align="center",
position=(self._width * 0.5 + b_offs, v - 65),
size=(0, 0),
draw_controller=self._highlight_button,
text=babase.Lstr(resource="editProfileWindow.highlightText"),
scale=0.7,
color=bui.app.ui_v1.title_color,
maxwidth=120,
)
def _pick_color(self, tag):
if tag == "color":
initial_color = self._color or self._default_colors[0]
elif tag == "highlight":
initial_color = self._highlight or self._default_colors[1]
else:
raise ValueError("Unexpected color picker tag: {}".format(tag))
ColorPicker(
parent=None,
position=(0, 0),
initial_color=initial_color,
delegate=self,
tag=tag,
)
def cancel_on_outside_click(self):
bui.getsound("swish").play()
self._cancel()
def _cancel(self):
if self._last_color and self._last_highlight:
colorscheme = ColorScheme(self._last_color, self._last_highlight)
colorscheme.apply()
# Good idea to revert this back now so we do not break anything else.
bui.app.classic.accounts.have_pro = original_have_pro
bui.containerwidget(edit=self._root_widget, transition="out_right")
def reset(self, transition_out=True):
if transition_out:
bui.getsound("gunCocking").play()
babase.app.config["ColorScheme"] = (None, None)
# Good idea to revert this back now so we do not break anything else.
bui.app.classic.accounts.have_pro = original_have_pro
babase.app.config.commit()
bui.containerwidget(edit=self._root_widget, transition="out_right")
def save(self, transition_out=True):
if transition_out:
bui.getsound("gunCocking").play()
colorscheme = ColorScheme(
self._color or self._default_colors[0],
self._highlight or self._default_colors[1],
)
colorscheme.apply()
# Good idea to revert this back now so we do not break anything else.
bui.app.classic.accounts.have_pro = original_have_pro
babase.app.config["ColorScheme"] = (
self._color or self._default_colors[0],
self._highlight or self._default_colors[1],
)
babase.app.config.commit()
bui.containerwidget(edit=self._root_widget, transition="out_right")
def _set_color(self, color):
self._color = color
if self._color_button:
bui.buttonwidget(edit=self._color_button, color=color)
def _set_highlight(self, color):
self._highlight = color
if self._highlight_button:
bui.buttonwidget(edit=self._highlight_button, color=color)
def color_picker_selected_color(self, picker, color):
# The `ColorPicker` calls this method in the delegate once a color
# is selected from the `ColorPicker` Window.
if not self._root_widget:
return
tag = picker.get_tag()
if tag == "color":
self._set_color(color)
elif tag == "highlight":
self._set_highlight(color)
else:
raise ValueError("Unexpected color picker tag: {}".format(tag))
def color_picker_closing(self, picker):
# The `ColorPicker` expects this method to exist in the delegate,
# so here it is!
pass
class CustomTransactions:
def __init__(self):
self.custom_transactions = {}
def _handle(self, transaction, *args, **kwargs):
transaction_code = transaction.get("code")
transaction_fn = self.custom_transactions.get(transaction_code)
if transaction_fn is not None:
return transaction_fn(transaction, *args, **kwargs)
return original_add_transaction(transaction, *args, **kwargs)
def add(self, transaction_code, transaction_fn):
self.custom_transactions[transaction_code] = transaction_fn
def enable(self):
bui.app.plus.add_v1_account_transaction = self._handle
def launch_colorscheme_selection_window():
# We store whether the player is a pro account or not since we
# temporarily attempt to bypass the limitation where only pro
# accounts can set custom RGB color values, and we restore this
# value back later on.
# Also, we attempt to store this value here (and not in the
# beginning) since the player might be using other dedicated
# pro-unlocker plugins which may attempt to override the game
# method early on in the beginning, therefore, leading us to a
# race-condition and we may not correctly store whether the player
# has pro-unlocked or not if our plugin runs before the dedicated
# pro-unlocker plugin has been applied.
global original_have_pro
original_have_pro = bui.app.classic.accounts.have_pro
ColorSchemeWindow()
def colorscheme_transaction(transaction, *args, **kwargs):
launch_colorscheme_selection_window()
def load_colorscheme():
color, highlight = babase.app.config.get("ColorScheme", (None, None))
if color and highlight:
colorscheme = ColorScheme(color, highlight)
colorscheme.apply()
def load_plugin():
# Allow access to changing colorschemes manually through the in-game
# console.
_babase.ColorScheme = ColorScheme
# Adds a new advanced code entry named "colorscheme" which can be
# entered through Settings -> Advanced -> Enter Code, allowing
# colorscheme modification through a friendly UI.
custom_transactions = CustomTransactions()
custom_transactions.add("colorscheme", colorscheme_transaction)
custom_transactions.enable()
# Load any previously saved colorscheme.
load_colorscheme()
# ba_meta export plugin
class Main(babase.Plugin):
def on_app_running(self):
load_plugin()
def has_settings_ui(self):
return True
def show_settings_ui(self, source_widget):
launch_colorscheme_selection_window()