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
|
import pygame
import os
ROTATION_ENABLED = os.getenv("DEVELOPMENT", "") in ["", "0"]
class Input:
def __init__(self, debug=False):
pygame.joystick.init()
self.controller = None
self.instance = os.getenv("INSTANCE", "Unknown")
if pygame.joystick.get_count() > 0:
self.controller = pygame.joystick.Joystick(0)
self.controller.init()
self.button_states = {} # Current states
self.prev_button_states = {} # Previous states
self.prev_axis_states = {"x": 0, "y": 0}
self.prev_hat_states = (0, 0) # D-pad
def get_input(self, event, rotation_angle=0, is_joint_mode=False):
input_action = None
if self.controller:
input_action = self._get_controller_input(event)
if input_action:
return self._apply_rotation(input_action, rotation_angle, is_joint_mode)
input_action = self._get_keyboard_input(event)
return self._apply_rotation(input_action, rotation_angle, is_joint_mode)
def _get_keyboard_input(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
return "UP"
if event.key == pygame.K_DOWN:
return "DOWN"
if event.key == pygame.K_LEFT:
return "LEFT"
if event.key == pygame.K_RIGHT:
return "RIGHT"
if event.key == pygame.K_RETURN:
return "SELECT"
if event.key == pygame.K_ESCAPE:
return "QUIT"
return None
def _get_analog_state(self):
if self.controller:
x = self.controller.get_axis(0) # X-axis
y = self.controller.get_axis(1) # Y-axis
if x <= -0.1:
return "LEFT"
if x >= 0.1:
return "RIGHT"
if y <= -0.1:
return "UP"
if y >= 0.1:
return "DOWN"
return None
def _get_controller_input(self, event):
# Analog stick
if event.type == pygame.JOYAXISMOTION:
return self._get_analog_state()
# D-pad
elif event.type == pygame.JOYHATMOTION:
prev_hat = self.prev_hat_states
curr_hat = event.value
self.prev_hat_states = curr_hat
if prev_hat[0] == 0 and curr_hat[0] == -1:
return "LEFT"
if prev_hat[0] == 0 and curr_hat[0] == 1:
return "RIGHT"
if prev_hat[1] == 0 and curr_hat[1] == 1:
return "UP"
if prev_hat[1] == 0 and curr_hat[1] == -1:
return "DOWN"
# Buttons
elif event.type == pygame.JOYBUTTONDOWN:
button = event.button
self.button_states[button] = True
# Debounced
if (
button not in self.prev_button_states
or not self.prev_button_states[button]
):
if button == 0:
return "DOWN"
if button == 1:
return "LEFT"
if button == 2:
return "UP"
if button == 3:
return "RIGHT"
if button == 4:
return "START"
if button == 5:
return "SELECT"
elif event.type == pygame.JOYBUTTONUP:
button = event.button
self.button_states[button] = False
self.prev_button_states = self.button_states.copy()
return None
def _apply_rotation(self, action, rotation_angle, is_joint_mode):
if not action or not ROTATION_ENABLED or is_joint_mode or rotation_angle == 0:
return action
# Primary: 90 degrees, right is down
if self.instance == "primary" and rotation_angle == 90:
if action == "RIGHT":
return "DOWN"
elif action == "LEFT":
return "UP"
elif action == "UP":
return "LEFT"
elif action == "DOWN":
return "RIGHT"
# Secondary, -90 degrees, left is down
elif self.instance == "secondary" and rotation_angle == -90:
if action == "RIGHT":
return "UP"
elif action == "LEFT":
return "DOWN"
elif action == "UP":
return "RIGHT"
elif action == "DOWN":
return "LEFT"
return action
|