mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2026-08-15 13:04:50 +00:00
575 lines
20 KiB
Python
575 lines
20 KiB
Python
# Released under the MIT License. See LICENSE for details.
|
|
#
|
|
"""UI functionality related to advanced gamepad configuring."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
import ba
|
|
|
|
if TYPE_CHECKING:
|
|
from typing import Any
|
|
from bastd.ui.settings import gamepad as gpsui
|
|
|
|
|
|
class GamepadAdvancedSettingsWindow(ba.Window):
|
|
"""Window for advanced gamepad configuration."""
|
|
|
|
def __init__(self, parent_window: gpsui.GamepadSettingsWindow):
|
|
# pylint: disable=too-many-statements
|
|
# pylint: disable=too-many-locals
|
|
self._parent_window = parent_window
|
|
|
|
app = ba.app
|
|
|
|
self._r = parent_window.get_r()
|
|
uiscale = ba.app.ui.uiscale
|
|
self._width = 900 if uiscale is ba.UIScale.SMALL else 700
|
|
self._x_inset = x_inset = 100 if uiscale is ba.UIScale.SMALL else 0
|
|
self._height = 402 if uiscale is ba.UIScale.SMALL else 512
|
|
self._textwidgets: dict[str, ba.Widget] = {}
|
|
advb = parent_window.get_advanced_button()
|
|
super().__init__(
|
|
root_widget=ba.containerwidget(
|
|
transition='in_scale',
|
|
size=(self._width, self._height),
|
|
scale=1.06
|
|
* (
|
|
1.85
|
|
if uiscale is ba.UIScale.SMALL
|
|
else 1.35
|
|
if uiscale is ba.UIScale.MEDIUM
|
|
else 1.0
|
|
),
|
|
stack_offset=(0, -25)
|
|
if uiscale is ba.UIScale.SMALL
|
|
else (0, 0),
|
|
scale_origin_stack_offset=(advb.get_screen_space_center()),
|
|
)
|
|
)
|
|
|
|
ba.textwidget(
|
|
parent=self._root_widget,
|
|
position=(
|
|
self._width * 0.5,
|
|
self._height - (40 if uiscale is ba.UIScale.SMALL else 34),
|
|
),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.advancedTitleText'),
|
|
maxwidth=320,
|
|
color=ba.app.ui.title_color,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
back_button = btn = ba.buttonwidget(
|
|
parent=self._root_widget,
|
|
autoselect=True,
|
|
position=(
|
|
self._width - (176 + x_inset),
|
|
self._height - (60 if uiscale is ba.UIScale.SMALL else 55),
|
|
),
|
|
size=(120, 48),
|
|
text_scale=0.8,
|
|
label=ba.Lstr(resource='doneText'),
|
|
on_activate_call=self._done,
|
|
)
|
|
ba.containerwidget(
|
|
edit=self._root_widget,
|
|
start_button=btn,
|
|
on_cancel_call=btn.activate,
|
|
)
|
|
|
|
self._scroll_width = self._width - (100 + 2 * x_inset)
|
|
self._scroll_height = self._height - 110
|
|
self._sub_width = self._scroll_width - 20
|
|
self._sub_height = (
|
|
940 if self._parent_window.get_is_secondary() else 1040
|
|
)
|
|
if app.vr_mode:
|
|
self._sub_height += 50
|
|
self._scrollwidget = ba.scrollwidget(
|
|
parent=self._root_widget,
|
|
position=(
|
|
(self._width - self._scroll_width) * 0.5,
|
|
self._height - 65 - self._scroll_height,
|
|
),
|
|
size=(self._scroll_width, self._scroll_height),
|
|
claims_left_right=True,
|
|
claims_tab=True,
|
|
selection_loops_to_parent=True,
|
|
)
|
|
self._subcontainer = ba.containerwidget(
|
|
parent=self._scrollwidget,
|
|
size=(self._sub_width, self._sub_height),
|
|
background=False,
|
|
claims_left_right=True,
|
|
claims_tab=True,
|
|
selection_loops_to_parent=True,
|
|
)
|
|
ba.containerwidget(
|
|
edit=self._root_widget, selected_child=self._scrollwidget
|
|
)
|
|
|
|
h = 30
|
|
v = self._sub_height - 10
|
|
|
|
h2 = h + 12
|
|
|
|
# don't allow secondary joysticks to handle unassigned buttons
|
|
if not self._parent_window.get_is_secondary():
|
|
v -= 40
|
|
cb1 = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
position=(h + 70, v),
|
|
size=(500, 30),
|
|
text=ba.Lstr(resource=self._r + '.unassignedButtonsRunText'),
|
|
textcolor=(0.8, 0.8, 0.8),
|
|
maxwidth=330,
|
|
scale=1.0,
|
|
on_value_change_call=(
|
|
self._parent_window.set_unassigned_buttons_run_value
|
|
),
|
|
autoselect=True,
|
|
value=self._parent_window.get_unassigned_buttons_run_value(),
|
|
)
|
|
ba.widget(edit=cb1, up_widget=back_button)
|
|
v -= 60
|
|
capb = self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.runButton1Text'),
|
|
control='buttonRun1' + self._parent_window.get_ext(),
|
|
)
|
|
if self._parent_window.get_is_secondary():
|
|
for widget in capb:
|
|
ba.widget(edit=widget, up_widget=back_button)
|
|
v -= 42
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.runButton2Text'),
|
|
control='buttonRun2' + self._parent_window.get_ext(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 24),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.runTriggerDescriptionText'),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
v -= 85
|
|
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.runTrigger1Text'),
|
|
control='triggerRun1' + self._parent_window.get_ext(),
|
|
message=ba.Lstr(resource=self._r + '.pressAnyAnalogTriggerText'),
|
|
)
|
|
v -= 42
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.runTrigger2Text'),
|
|
control='triggerRun2' + self._parent_window.get_ext(),
|
|
message=ba.Lstr(resource=self._r + '.pressAnyAnalogTriggerText'),
|
|
)
|
|
|
|
# in vr mode, allow assigning a reset-view button
|
|
if app.vr_mode:
|
|
v -= 50
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.vrReorientButtonText'),
|
|
control='buttonVRReorient' + self._parent_window.get_ext(),
|
|
)
|
|
|
|
v -= 60
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.extraStartButtonText'),
|
|
control='buttonStart2' + self._parent_window.get_ext(),
|
|
)
|
|
v -= 60
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.ignoredButton1Text'),
|
|
control='buttonIgnored' + self._parent_window.get_ext(),
|
|
)
|
|
v -= 42
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.ignoredButton2Text'),
|
|
control='buttonIgnored2' + self._parent_window.get_ext(),
|
|
)
|
|
v -= 42
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.ignoredButton3Text'),
|
|
control='buttonIgnored3' + self._parent_window.get_ext(),
|
|
)
|
|
v -= 42
|
|
self._capture_button(
|
|
pos=(h2, v),
|
|
name=ba.Lstr(resource=self._r + '.ignoredButton4Text'),
|
|
control='buttonIgnored4' + self._parent_window.get_ext(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 14),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.ignoredButtonDescriptionText'),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
scale=0.8,
|
|
maxwidth=self._sub_width * 0.8,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
v -= 80
|
|
pwin = self._parent_window
|
|
ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(h + 50, v),
|
|
size=(400, 30),
|
|
text=ba.Lstr(resource=self._r + '.startButtonActivatesDefaultText'),
|
|
textcolor=(0.8, 0.8, 0.8),
|
|
maxwidth=450,
|
|
scale=0.9,
|
|
on_value_change_call=(
|
|
pwin.set_start_button_activates_default_widget_value
|
|
),
|
|
value=pwin.get_start_button_activates_default_widget_value(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 12),
|
|
size=(0, 0),
|
|
text=ba.Lstr(
|
|
resource=self._r + '.startButtonActivatesDefaultDescriptionText'
|
|
),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
v -= 80
|
|
ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(h + 50, v),
|
|
size=(400, 30),
|
|
text=ba.Lstr(resource=self._r + '.uiOnlyText'),
|
|
textcolor=(0.8, 0.8, 0.8),
|
|
maxwidth=450,
|
|
scale=0.9,
|
|
on_value_change_call=self._parent_window.set_ui_only_value,
|
|
value=self._parent_window.get_ui_only_value(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 12),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.uiOnlyDescriptionText'),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
v -= 80
|
|
ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(h + 50, v),
|
|
size=(400, 30),
|
|
text=ba.Lstr(resource=self._r + '.ignoreCompletelyText'),
|
|
textcolor=(0.8, 0.8, 0.8),
|
|
maxwidth=450,
|
|
scale=0.9,
|
|
on_value_change_call=pwin.set_ignore_completely_value,
|
|
value=self._parent_window.get_ignore_completely_value(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 12),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.ignoreCompletelyDescriptionText'),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
|
|
v -= 80
|
|
|
|
cb1 = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(h + 50, v),
|
|
size=(400, 30),
|
|
text=ba.Lstr(resource=self._r + '.autoRecalibrateText'),
|
|
textcolor=(0.8, 0.8, 0.8),
|
|
maxwidth=450,
|
|
scale=0.9,
|
|
on_value_change_call=pwin.set_auto_recalibrate_analog_stick_value,
|
|
value=self._parent_window.get_auto_recalibrate_analog_stick_value(),
|
|
)
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 12),
|
|
size=(0, 0),
|
|
text=ba.Lstr(resource=self._r + '.autoRecalibrateDescriptionText'),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
v -= 80
|
|
|
|
buttons = self._config_value_editor(
|
|
ba.Lstr(resource=self._r + '.analogStickDeadZoneText'),
|
|
control=('analogStickDeadZone' + self._parent_window.get_ext()),
|
|
position=(h + 40, v),
|
|
min_val=0,
|
|
max_val=10.0,
|
|
increment=0.1,
|
|
x_offset=100,
|
|
)
|
|
ba.widget(edit=buttons[0], left_widget=cb1, up_widget=cb1)
|
|
ba.widget(edit=cb1, right_widget=buttons[0], down_widget=buttons[0])
|
|
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(self._sub_width * 0.5, v - 12),
|
|
size=(0, 0),
|
|
text=ba.Lstr(
|
|
resource=self._r + '.analogStickDeadZoneDescriptionText'
|
|
),
|
|
color=(0.7, 1, 0.7, 0.6),
|
|
maxwidth=self._sub_width * 0.8,
|
|
scale=0.7,
|
|
h_align='center',
|
|
v_align='center',
|
|
)
|
|
v -= 100
|
|
|
|
# child joysticks cant have child joysticks.. that's just
|
|
# crazy talk
|
|
if not self._parent_window.get_is_secondary():
|
|
ba.buttonwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
label=ba.Lstr(resource=self._r + '.twoInOneSetupText'),
|
|
position=(40, v),
|
|
size=(self._sub_width - 80, 50),
|
|
on_activate_call=self._parent_window.show_secondary_editor,
|
|
up_widget=buttons[0],
|
|
)
|
|
|
|
# set a bigger bottom show-buffer for the widgets we just made
|
|
# so we can see the text below them when navigating with
|
|
# a gamepad
|
|
for child in self._subcontainer.get_children():
|
|
ba.widget(edit=child, show_buffer_bottom=30, show_buffer_top=30)
|
|
|
|
def _capture_button(
|
|
self,
|
|
pos: tuple[float, float],
|
|
name: ba.Lstr,
|
|
control: str,
|
|
message: ba.Lstr | None = None,
|
|
) -> tuple[ba.Widget, ba.Widget]:
|
|
if message is None:
|
|
message = ba.Lstr(
|
|
resource=self._parent_window.get_r() + '.pressAnyButtonText'
|
|
)
|
|
btn = ba.buttonwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(pos[0], pos[1]),
|
|
label=name,
|
|
size=(250, 60),
|
|
scale=0.7,
|
|
)
|
|
btn2 = ba.buttonwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(pos[0] + 400, pos[1] + 2),
|
|
left_widget=btn,
|
|
color=(0.45, 0.4, 0.5),
|
|
textcolor=(0.65, 0.6, 0.7),
|
|
label=ba.Lstr(resource=self._r + '.clearText'),
|
|
size=(110, 50),
|
|
scale=0.7,
|
|
on_activate_call=ba.Call(self._clear_control, control),
|
|
)
|
|
ba.widget(edit=btn, right_widget=btn2)
|
|
|
|
# make this in a timer so that it shows up on top of all
|
|
# other buttons
|
|
|
|
def doit() -> None:
|
|
from bastd.ui.settings import gamepad
|
|
|
|
txt = ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(pos[0] + 285, pos[1] + 20),
|
|
color=(1, 1, 1, 0.3),
|
|
size=(0, 0),
|
|
h_align='center',
|
|
v_align='center',
|
|
scale=0.7,
|
|
text=self._parent_window.get_control_value_name(control),
|
|
maxwidth=200,
|
|
)
|
|
self._textwidgets[control] = txt
|
|
ba.buttonwidget(
|
|
edit=btn,
|
|
on_activate_call=ba.Call(
|
|
gamepad.AwaitGamepadInputWindow,
|
|
self._parent_window.get_input(),
|
|
control,
|
|
self._gamepad_event,
|
|
message,
|
|
),
|
|
)
|
|
|
|
ba.timer(0, doit, timetype=ba.TimeType.REAL)
|
|
return btn, btn2
|
|
|
|
def _inc(
|
|
self, control: str, min_val: float, max_val: float, inc: float
|
|
) -> None:
|
|
val = self._parent_window.get_settings().get(control, 1.0)
|
|
val = min(max_val, max(min_val, val + inc))
|
|
if abs(1.0 - val) < 0.001:
|
|
if control in self._parent_window.get_settings():
|
|
del self._parent_window.get_settings()[control]
|
|
else:
|
|
self._parent_window.get_settings()[control] = round(val, 1)
|
|
ba.textwidget(
|
|
edit=self._textwidgets[control],
|
|
text=self._parent_window.get_control_value_name(control),
|
|
)
|
|
|
|
def _config_value_editor(
|
|
self,
|
|
name: ba.Lstr,
|
|
control: str,
|
|
position: tuple[float, float],
|
|
min_val: float = 0.0,
|
|
max_val: float = 100.0,
|
|
increment: float = 1.0,
|
|
change_sound: bool = True,
|
|
x_offset: float = 0.0,
|
|
displayname: ba.Lstr | None = None,
|
|
) -> tuple[ba.Widget, ba.Widget]:
|
|
|
|
if displayname is None:
|
|
displayname = name
|
|
ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=position,
|
|
size=(100, 30),
|
|
text=displayname,
|
|
color=(0.8, 0.8, 0.8, 1.0),
|
|
h_align='left',
|
|
v_align='center',
|
|
scale=1.0,
|
|
maxwidth=280,
|
|
)
|
|
self._textwidgets[control] = ba.textwidget(
|
|
parent=self._subcontainer,
|
|
position=(246.0 + x_offset, position[1]),
|
|
size=(60, 28),
|
|
editable=False,
|
|
color=(0.3, 1.0, 0.3, 1.0),
|
|
h_align='right',
|
|
v_align='center',
|
|
text=self._parent_window.get_control_value_name(control),
|
|
padding=2,
|
|
)
|
|
btn = ba.buttonwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(330 + x_offset, position[1] + 4),
|
|
size=(28, 28),
|
|
label='-',
|
|
on_activate_call=ba.Call(
|
|
self._inc, control, min_val, max_val, -increment
|
|
),
|
|
repeat=True,
|
|
enable_sound=(change_sound is True),
|
|
)
|
|
btn2 = ba.buttonwidget(
|
|
parent=self._subcontainer,
|
|
autoselect=True,
|
|
position=(380 + x_offset, position[1] + 4),
|
|
size=(28, 28),
|
|
label='+',
|
|
on_activate_call=ba.Call(
|
|
self._inc, control, min_val, max_val, increment
|
|
),
|
|
repeat=True,
|
|
enable_sound=(change_sound is True),
|
|
)
|
|
return btn, btn2
|
|
|
|
def _clear_control(self, control: str) -> None:
|
|
if control in self._parent_window.get_settings():
|
|
del self._parent_window.get_settings()[control]
|
|
ba.textwidget(
|
|
edit=self._textwidgets[control],
|
|
text=self._parent_window.get_control_value_name(control),
|
|
)
|
|
|
|
def _gamepad_event(
|
|
self,
|
|
control: str,
|
|
event: dict[str, Any],
|
|
dialog: gpsui.AwaitGamepadInputWindow,
|
|
) -> None:
|
|
ext = self._parent_window.get_ext()
|
|
if control in ['triggerRun1' + ext, 'triggerRun2' + ext]:
|
|
if event['type'] == 'AXISMOTION':
|
|
# ignore small values or else we might get triggered
|
|
# by noise
|
|
if abs(event['value']) > 0.5:
|
|
self._parent_window.get_settings()[control] = event['axis']
|
|
# update the button's text widget
|
|
if self._textwidgets[control]:
|
|
ba.textwidget(
|
|
edit=self._textwidgets[control],
|
|
text=self._parent_window.get_control_value_name(
|
|
control
|
|
),
|
|
)
|
|
ba.playsound(ba.getsound('gunCocking'))
|
|
dialog.die()
|
|
else:
|
|
if event['type'] == 'BUTTONDOWN':
|
|
value = event['button']
|
|
self._parent_window.get_settings()[control] = value
|
|
# update the button's text widget
|
|
if self._textwidgets[control]:
|
|
ba.textwidget(
|
|
edit=self._textwidgets[control],
|
|
text=self._parent_window.get_control_value_name(
|
|
control
|
|
),
|
|
)
|
|
ba.playsound(ba.getsound('gunCocking'))
|
|
dialog.die()
|
|
|
|
def _done(self) -> None:
|
|
ba.containerwidget(edit=self._root_widget, transition='out_scale')
|