From 0f60e951f87a0532438fdd86e1b42ec6c7ed8d9e Mon Sep 17 00:00:00 2001
From: annika <annika.oesterdiekhoff@uni-bielefeld.de>
Date: Thu, 1 Feb 2024 12:00:25 +0100
Subject: [PATCH] first draft of controller support, missing movement

---
 .../gui_2d_vis/overcooked_gui.py              | 99 +++++++++++++++++--
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
index 7922bc2f..1f5dc39c 100644
--- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py
+++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
@@ -68,12 +68,12 @@ class PyGameGUI:
     """Visualisation of the overcooked environment and reading keyboard inputs using pygame."""
 
     def __init__(
-        self,
-        player_names: list[str | int],
-        player_keys: list[pygame.key],
-        url: str,
-        port: int,
-        manager_ids: list[str],
+            self,
+            player_names: list[str | int],
+            player_keys: list[pygame.key],
+            url: str,
+            port: int,
+            manager_ids: list[str],
     ):
         self.game_screen: pygame.Surface = None
         self.FPS = 60
@@ -182,11 +182,41 @@ class PyGameGUI:
                 if np.linalg.norm(move_vec) != 0:
                     move_vec = move_vec / np.linalg.norm(move_vec)
 
+                print("move_vec keys", move_vec)
                 action = Action(
                     key_set.name, ActionType.MOVEMENT, move_vec, duration=1 / self.FPS
                 )
                 self.send_action(action)
 
+    def handle_joy_stick_input(self, joysticks):
+        """MISSING
+        """
+        # see control stuff here (at the end of the page): https://www.pygame.org/docs/ref/joystick.html
+        for joystick in joysticks.values():
+            # Usually axis run in pairs, up/down for one, and left/right for
+            # the other. Triggers count as axes.
+            axes = joystick.get_numaxes()
+            move_vec = np.zeros(2)
+            for i in range(axes):
+                axis = joystick.get_axis(i)
+                print("axis key", axis)
+                # joy stick left --> left & right
+                if axes == 0:
+                    move_vec[0] += axis
+                # joy stick right --> up & down
+                elif axes == 1:
+                    move_vec[1] += axis
+
+            print("move_vec joystick before ", move_vec)
+            if np.linalg.norm(move_vec) != 0:
+                move_vec = move_vec / np.linalg.norm(move_vec)
+
+            print("move_vec joystick", move_vec)
+            action = Action(
+                "0", ActionType.MOVEMENT, move_vec, duration=1 / self.FPS
+            )
+            self.send_action(action)
+
     def handle_key_event(self, event):
         """Handles key events for the pickup and interaction keys. Pickup is a single action,
         for interaction keydown and keyup is necessary, because the player has to be able to hold
@@ -212,6 +242,37 @@ class PyGameGUI:
                     )
                     self.send_action(action)
 
+    def handle_joy_stick_event(self, event):
+        """Handles joy stick events for the pickup and interaction keys. Pickup is a single action,
+        for interaction buttondown and buttonup is necessary, because the player has to be able to hold
+        the button down.
+
+        Args:
+            event: Pygame event for extracting the button action.
+        """
+
+        if event.type == pygame.JOYBUTTONDOWN:
+            # pickup = Button A <-> 0
+            if event.button == 0:
+                # FIXME: Name missing
+                action = Action("0", ActionType.PUT, "pickup")
+                self.send_action(action)
+
+        # interact = Button X <-> 2
+        if event.button == 2:
+            if event.type == pygame.JOYBUTTONDOWN:
+                # FIXME: Name missing
+                action = Action(
+                    "0", ActionType.INTERACT, InterActionData.START
+                )
+                self.send_action(action)
+            elif event.type == pygame.JOYBUTTONUP:
+                # FIXME: Name missing
+                action = Action(
+                    "0", ActionType.INTERACT, InterActionData.STOP
+                )
+                self.send_action(action)
+
     def init_ui_elements(self):
         self.manager = pygame_gui.UIManager((self.window_width, self.window_height))
         self.manager.get_theme().load_theme(ROOT_DIR / "gui_2d_vis" / "gui_theme.json")
@@ -608,6 +669,11 @@ class PyGameGUI:
 
         # Game loop
         self.running = True
+        # This dict can be left as-is, since pygame will generate a
+        # pygame.JOYDEVICEADDED event for every joystick connected
+        # at the start of the program.
+        joysticks = {}
+
         while self.running:
             try:
                 time_delta = clock.tick(self.FPS) / 1000.0
@@ -616,6 +682,14 @@ class PyGameGUI:
                     if event.type == pygame.QUIT:
                         self.running = False
 
+                    # connect joystick
+                    if pygame.joystick.get_count() > 0 and event.type == pygame.JOYDEVICEADDED:
+                        # This event will be generated when the program starts for every
+                        # joystick, filling up the list without needing to create them manually.
+                        joy = pygame.joystick.Joystick(event.device_index)
+                        joysticks[joy.get_instance_id()] = joy
+                        print(f"Joystick {joy.get_instance_id()} connencted")
+
                         # UI Buttons:
                     if event.type == pygame_gui.UI_BUTTON_PRESSED:
                         match event.ui_element:
@@ -639,12 +713,14 @@ class PyGameGUI:
                         self.manage_button_visibility()
 
                     if (
-                        event.type in [pygame.KEYDOWN, pygame.KEYUP]
-                        and self.menu_state == MenuStates.Game
+                            event.type in [pygame.KEYDOWN, pygame.KEYUP]
+                            and self.menu_state == MenuStates.Game
                     ):
-                        pass
                         self.handle_key_event(event)
 
+                    if event.type in [pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP] and self.menu_state == MenuStates.Game:
+                        self.handle_joy_stick_event(event)
+
                     self.manager.process_events(event)
 
                     # drawing:
@@ -661,6 +737,7 @@ class PyGameGUI:
                         state = self.request_state()
 
                         self.handle_keys()
+                        self.handle_joy_stick_input(joysticks=joysticks)
 
                         if state["ended"]:
                             self.finished_button_press()
@@ -694,6 +771,9 @@ class PyGameGUI:
 
 def main(url: str, port: int, manager_ids: list[str]):
     # TODO maybe read the player names and keyboard keys from config file?
+    # First four keys are for movement. Order: Down, Up, Left, Right.
+    # 5th key is for interacting with counters.
+    # 6th key ist for picking up things or dropping them.
     keys1 = [
         pygame.K_LEFT,
         pygame.K_RIGHT,
@@ -703,6 +783,7 @@ def main(url: str, port: int, manager_ids: list[str]):
         pygame.K_i,
     ]
     keys2 = [pygame.K_a, pygame.K_d, pygame.K_w, pygame.K_s, pygame.K_f, pygame.K_e]
+    # FIXME: add buttons of joystick here
 
     number_players = 2
     gui = PyGameGUI(
-- 
GitLab