diff --git a/cooperative_cuisine/configs/environment_config.yaml b/cooperative_cuisine/configs/environment_config.yaml
index a6c7c752d93e7e593c1a913ce32291f7495ac0fe..eec7ad500072538f9b44c0fa2b470500410182a5 100644
--- a/cooperative_cuisine/configs/environment_config.yaml
+++ b/cooperative_cuisine/configs/environment_config.yaml
@@ -1,6 +1,6 @@
 plates:
   clean_plates: 2
-  dirty_plates: 0
+  dirty_plates: 1
   plate_delay: [ 5, 10 ]
   # range of seconds until the dirty plate arrives.
 
@@ -53,9 +53,10 @@ orders:
     # if all: false -> only orders for these meals are generated
     # TODO: what if this list is empty?
     list:
-      - TomatoSoup
-      - OnionSoup
-      - Salad
+      #      - TomatoSoup
+      #      - OnionSoup
+      #      - Salad
+      - FriedFish
   order_gen_class: !!python/name:cooperative_cuisine.orders.RandomOrderGeneration ''
   # the class to that receives the kwargs. Should be a child class of OrderGeneration in orders.py
   order_gen_kwargs:
@@ -83,7 +84,7 @@ orders:
 
 player_config:
   radius: 0.4
-  speed_units_per_seconds: 6
+  speed_units_per_seconds: 10
   interaction_range: 1.6
   restricted_view: False
   view_angle: 70
@@ -172,18 +173,15 @@ extra_setup_functions:
       hooks:
         - post_counter_pick_up
         - post_counter_drop_off
-        - dispenser_pick_up
+        - post_dispenser_pick_up
         - cutting_board_100
-        - cutting_board_start_interaction
-        - cutting_board_end_interact
-        - pre_serving
+        - player_start_interaction
+        - player_end_interact
         - post_serving
         - no_serving
         - dirty_plate_arrives
         - trashcan_usage
         - plate_cleaned
-        - sink_start_interact
-        - sink_end_interact
         - added_plate_to_sink
         - drop_on_sink_addon
         - pick_up_from_sink_addon
@@ -197,18 +195,19 @@ extra_setup_functions:
         - fire_spreading
         - drop_off_on_cooking_equipment
         - players_collide
+        - post_plate_dispenser_pick_up
+        - post_plate_dispenser_drop_off
+        - on_item_transition
+        - progress_started
+        - progress_finished
+        - content_ready
+
       callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
       callback_class_kwargs:
         log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
         add_hook_ref: true
 
-  empty_info_msg:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ action_put ]
-      callback_class: !!python/name:cooperative_cuisine.info_msg.InfoMsgManager ''
-      callback_class_kwargs:
-        msg: ""
+
 #  info_msg:
 #    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
 #    kwargs:
diff --git a/cooperative_cuisine/configs/human_readable_print_templates.yaml b/cooperative_cuisine/configs/human_readable_print_templates.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b190f6bccf1f0a3a3c580a8e37dd4c5f05e56365
--- /dev/null
+++ b/cooperative_cuisine/configs/human_readable_print_templates.yaml
@@ -0,0 +1,27 @@
+post_dispenser_pick_up: "Player $player picked up $return_this from the $counter."
+post_counter_pick_up: "Player $player picked $return_this up from $counter."
+post_counter_drop_off: "Player $player dropped $item off on $counter."
+cutting_board_100: "Player $player finished chopping at $counter."
+player_start_interaction: "Player $player started interacting with $counter."
+player_end_interact: "Player $player stopped interacting with $counter."
+post_serving: "Item $item was served at $counter."
+dirty_plate_arrives: "A plate returned to $counter."
+trashcan_usage: "Player $player threw $item in $counter."
+plate_cleaned: "Player $player cleaned a plate at $counter."
+added_plate_to_sink: "Player $player put $item in $counter."
+drop_on_sink_addon: "Player $player put $item on $counter."
+pick_up_from_sink_addon: "Player $player picked up $occupied_by from $counter."
+serve_not_ordered_meal: "Meal $meal was served but it was not ordered."
+completed_order: "Order $order was completed."
+new_orders: "Orders $new_orders were ordered."
+order_expired: "Order $order expired."
+action_on_not_reachable_counter: "Action $action was performed yet nearest counter $counter was not reachable."
+new_fire: "A fire broke out at $target."
+fire_spreading: "A fire spread to target."
+drop_off_on_cooking_equipment: "Player $player put $item in/on $equipment at $counter."
+post_plate_dispenser_pick_up: "Player $player picked up $returned_item from $counter."
+post_plate_dispenser_drop_off: "Player $player dropped $item on $counter."
+on_item_transition: "$item became $result."
+progress_started: "Item $item started progressing."
+progress_finished: "Item $item finished its progress."
+content_ready: "Meal $result was created on $before."
diff --git a/cooperative_cuisine/configs/item_info_debug.yaml b/cooperative_cuisine/configs/item_info_debug.yaml
index fd871d1d5685df542317bf772b67f3a3fd8ed7d8..f7783818b44059e9a004f627c60b433b6ac2e01a 100644
--- a/cooperative_cuisine/configs/item_info_debug.yaml
+++ b/cooperative_cuisine/configs/item_info_debug.yaml
@@ -182,45 +182,45 @@ Pizza:
 
 BurntCookedPatty:
   type: Waste
-  seconds: 5.0
+  seconds: 15.0
   needs: [ CookedPatty ]
   equipment: Pan
 
 BurntChips:
   type: Waste
-  seconds: 1.0
+  seconds: 15.0
   needs: [ Chips ]
   equipment: Basket
 
 BurntFriedFish:
   type: Waste
-  seconds: 5.0
+  seconds: 15.0
   needs: [ FriedFish ]
   equipment: Basket
 
 BurntTomatoSoup:
   type: Waste
   needs: [ TomatoSoup ]
-  seconds: 6.0
+  seconds: 15.0
   equipment: Pot
 
 BurntOnionSoup:
   type: Waste
   needs: [ OnionSoup ]
-  seconds: 6.0
+  seconds: 15.0
   equipment: Pot
 
 BurntPizza:
   type: Waste
   needs: [ Pizza ]
-  seconds: 7.0
+  seconds: 15.0
   equipment: Peel
 
 # --------------------------------------------------------------------------------
 
 Fire:
   type: Effect
-  seconds: 1.0
+  seconds: 20.0
   needs: [ BurntCookedPatty, BurntChips, BurntFriedFish, BurntTomatoSoup, BurntOnionSoup, BurntPizza ]
   manager: FireManager
   effect_type: Unusable
diff --git a/cooperative_cuisine/configs/study/level1/level1_config.yaml b/cooperative_cuisine/configs/study/level1/level1_config.yaml
index a0d0fe8b701b1efb88436089eee643806876c918..94641a0a3e971a8b54115fdd473b7be45f2eb0ef 100644
--- a/cooperative_cuisine/configs/study/level1/level1_config.yaml
+++ b/cooperative_cuisine/configs/study/level1/level1_config.yaml
@@ -142,6 +142,48 @@ extra_setup_functions:
   #      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
   #      callback_class_kwargs:
   #        log_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
+
+  # Game event recording
+  game_events:
+    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
+    kwargs:
+      hooks:
+        - post_counter_pick_up
+        - post_counter_drop_off
+        - post_dispenser_pick_up
+        - cutting_board_100
+        - player_start_interaction
+        - player_end_interact
+        - post_serving
+        - no_serving
+        - dirty_plate_arrives
+        - trashcan_usage
+        - plate_cleaned
+        - added_plate_to_sink
+        - drop_on_sink_addon
+        - pick_up_from_sink_addon
+        - serve_not_ordered_meal
+        - serve_without_plate
+        - completed_order
+        - new_orders
+        - order_expired
+        - action_on_not_reachable_counter
+        - new_fire
+        - fire_spreading
+        - drop_off_on_cooking_equipment
+        - players_collide
+        - post_plate_dispenser_pick_up
+        - post_plate_dispenser_drop_off
+        - on_item_transition
+        - progress_started
+        - progress_finished
+        - content_ready
+
+      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+      callback_class_kwargs:
+        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+        add_hook_ref: true
+
   actions:
     func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
     kwargs:
diff --git a/cooperative_cuisine/configs/study/level2/level2_config.yaml b/cooperative_cuisine/configs/study/level2/level2_config.yaml
index 6ebb7fb3cd2d767dec79ab32a6d214ad95d08ee9..e47528aa0dc770891e930474be3da5d14ad63dd6 100644
--- a/cooperative_cuisine/configs/study/level2/level2_config.yaml
+++ b/cooperative_cuisine/configs/study/level2/level2_config.yaml
@@ -164,6 +164,48 @@ extra_setup_functions:
       callback_class_kwargs:
         log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
         add_hook_ref: true
+
+  # Game event recording
+  game_events:
+    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
+    kwargs:
+      hooks:
+        - post_counter_pick_up
+        - post_counter_drop_off
+        - post_dispenser_pick_up
+        - cutting_board_100
+        - player_start_interaction
+        - player_end_interact
+        - post_serving
+        - no_serving
+        - dirty_plate_arrives
+        - trashcan_usage
+        - plate_cleaned
+        - added_plate_to_sink
+        - drop_on_sink_addon
+        - pick_up_from_sink_addon
+        - serve_not_ordered_meal
+        - serve_without_plate
+        - completed_order
+        - new_orders
+        - order_expired
+        - action_on_not_reachable_counter
+        - new_fire
+        - fire_spreading
+        - drop_off_on_cooking_equipment
+        - players_collide
+        - post_plate_dispenser_pick_up
+        - post_plate_dispenser_drop_off
+        - on_item_transition
+        - progress_started
+        - progress_finished
+        - content_ready
+
+      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+      callback_class_kwargs:
+        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+        add_hook_ref: true
+
 #  info_msg:
 #    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
 #    kwargs:
diff --git a/cooperative_cuisine/configs/study/study_config.yaml b/cooperative_cuisine/configs/study/study_config.yaml
index 38158fdc1c43cd23e4b4b8b2fb548a691903eb96..28c420aa32e0b7b2228b7e16815dd77f3cf35633 100644
--- a/cooperative_cuisine/configs/study/study_config.yaml
+++ b/cooperative_cuisine/configs/study/study_config.yaml
@@ -8,7 +8,7 @@ levels:
   #    layout_path: LAYOUTS_DIR/basic.layout
   #    item_info_path: CONFIGS_DIR/item_info.yaml
   #    name: "Basic"
-  #
+
   - config_path: STUDY_DIR/level2/level2_config.yaml
     layout_path: LAYOUTS_DIR/overcooked-1/1-4-bottleneck.layout
     item_info_path: STUDY_DIR/level2/level2_item_info.yaml
diff --git a/cooperative_cuisine/counter_factory.py b/cooperative_cuisine/counter_factory.py
index d991daecc6149c8cc32ff714c25fce31a57832a6..ba8ce4b9870c43c9a9a67c0de15b176a56c4ea4a 100644
--- a/cooperative_cuisine/counter_factory.py
+++ b/cooperative_cuisine/counter_factory.py
@@ -216,6 +216,7 @@ class CounterFactory:
                                 by_equipment_name=item_info.name,
                                 add_effects=True,
                             ),
+                            hook=self.hook,
                         ),
                         hook=self.hook,
                     )
@@ -246,6 +247,7 @@ class CounterFactory:
                         ),
                         clean=True,
                         item_info=self.item_info[Plate.__name__],
+                        hook=self.hook,
                     ),
                 )
         kwargs = {
diff --git a/cooperative_cuisine/counters.py b/cooperative_cuisine/counters.py
index a4ea2bba8d4681c349a9575af2e69338dffa71c4..991074a33ca8b97c432d6be23cfcbb0db48b1644 100644
--- a/cooperative_cuisine/counters.py
+++ b/cooperative_cuisine/counters.py
@@ -64,6 +64,10 @@ from cooperative_cuisine.hooks import (
     DROP_OFF_ON_COOKING_EQUIPMENT,
     POST_COUNTER_DROP_OFF,
     PRE_COUNTER_DROP_OFF,
+    POST_PLATE_DISPENSER_DROP_OFF,
+    PRE_PLATE_DISPENSER_DROP_OFF,
+    PRE_PLATE_DISPENSER_PICK_UP,
+    POST_PLATE_DISPENSER_PICK_UP,
 )
 from cooperative_cuisine.state_representation import CounterState
 
@@ -224,10 +228,9 @@ class Counter:
             self.occupied_by = item
             self.hook(
                 POST_COUNTER_DROP_OFF,
-                item=item,
-                equipment=self.occupied_by,
                 counter=self,
                 player=player,
+                item=item,
             )
         elif self.occupied_by.can_combine(item):
             self.hook(
@@ -295,13 +298,14 @@ class Counter:
         return False
 
     def do_hand_free_interaction(
-        self, passed_time: timedelta, now: datetime, player_name: str
+        self, passed_time: timedelta, now: datetime, player: str
     ):
         """Called by environment step function for time progression.
 
         Args:
             passed_time: the time passed since the last progress call
             now: the current env time. **Not the same as `datetime.now`**.
+            player: Name of the player doing the interaction.
         """
         ...
 
@@ -348,13 +352,14 @@ class CuttingBoard(Counter):
         super().__init__(**kwargs)
 
     def do_hand_free_interaction(
-        self, passed_time: timedelta, now: datetime, player_name: str
+        self, passed_time: timedelta, now: datetime, player: str
     ):
         """Called by environment step function for time progression.
 
         Args:
             passed_time: the time passed since the last progress call
             now: the current env time. **Not the same as `datetime.now`**.
+            player: Name of the player doing the interaction.
 
         Checks if the item on the board is in the allowed transitions via a Cutting board. Pass the progress call to
         the item on the board. If the progress on the item reaches 100% it changes the name of the item based on the
@@ -390,7 +395,7 @@ class CuttingBoard(Counter):
                 self.occupied_by.name = self.inverted_transition_dict[
                     self.occupied_by.name
                 ].name
-                self.hook(CUTTING_BOARD_100, counter=self, player_name=player_name)
+                self.hook(CUTTING_BOARD_100, counter=self, player=player)
 
 
 class ServingWindow(Counter):
@@ -548,7 +553,8 @@ class PlateDispenser(Counter):
     """At the moment, one and only one plate dispenser must exist in an environment, because only at one place the dirty
     plates should arrive.
 
-    How many plates should exist at the start of the level on the plate dispenser is defined in the `environment_config.yml`:
+    How many plates should exist at the start of the level on the
+    plate dispenser is defined in the `environment_config.yml`:
     ```yaml
     plates:
       clean_plates: 1
@@ -587,18 +593,55 @@ class PlateDispenser(Counter):
         """Random instance."""
         self.setup_plates()
 
+        """   
+        PRE_PLATE_DISPENSER_PICK_UP = "pre_plate_dispenser_pick_up"
+        POST_PLATE_DISPENSER_PICK_UP = "post_plate_dispenser_pick_up"
+        
+        """
+
     def pick_up(self, on_hands: bool = True, player: str = "0") -> Item | None:
+        self.hook(
+            PRE_PLATE_DISPENSER_PICK_UP,
+            counter=self,
+            player=player,
+        )
         if self.occupied_by:
-            return self.occupied_by.pop()
+            returned_item = self.occupied_by.pop()
+            self.hook(
+                POST_PLATE_DISPENSER_PICK_UP,
+                counter=self,
+                player=player,
+                returned_item=returned_item,
+            )
+            return returned_item
 
     def can_drop_off(self, item: Item) -> bool:
         return not self.occupied_by or self.occupied_by[-1].can_combine(item)
 
     def drop_off(self, item: Item, player: str = "0") -> Item | None:
+        self.hook(
+            PRE_PLATE_DISPENSER_DROP_OFF,
+            counter=self,
+            player=player,
+            item=item,
+        )
         if not self.occupied_by:
             self.occupied_by.append(item)
+            self.hook(
+                POST_PLATE_DISPENSER_DROP_OFF,
+                counter=self,
+                player=player,
+                item=item,
+            )
         elif self.occupied_by[-1].can_combine(item):
-            return self.occupied_by[-1].combine(item)
+            returned_item = self.occupied_by[-1].combine(item)
+            self.hook(
+                POST_PLATE_DISPENSER_DROP_OFF,
+                counter=self,
+                player=player,
+                item=returned_item,
+            )
+            return returned_item
         return None
 
     def add_dirty_plate(self):
@@ -656,9 +699,6 @@ class PlateDispenser(Counter):
                 else datetime.max
             )
 
-    def __repr__(self):
-        return "PlateReturn"
-
     def create_item(self, clean: bool = False) -> Plate:
         """Create a plate.
 
@@ -669,6 +709,7 @@ class PlateDispenser(Counter):
             "clean": clean,
             "transitions": self.plate_transitions,
             "item_info": self.dispensing,
+            "hook": self.hook
         }
         return Plate(**kwargs)
 
@@ -693,6 +734,7 @@ class Trashcan(Counter):
         ):
             return item
         if isinstance(item, CookingEquipment):
+            self.hook(TRASHCAN_USAGE, counter=self, item=item, player=player)
             item.reset_content()
             item.reset()
             return item
@@ -797,7 +839,7 @@ class Sink(Counter):
         return len(self.occupied_by) != 0
 
     def do_hand_free_interaction(
-        self, passed_time: timedelta, now: datetime, player_name: str
+        self, passed_time: timedelta, now: datetime, player: str
     ):
         if (
             self.occupied
@@ -829,7 +871,7 @@ class Sink(Counter):
                         equipment=self.__class__.__name__, percent=percent
                     )
                     if self.occupied_by[-1].progress_percentage == 1.0:
-                        self.hook(PLATE_CLEANED, counter=self, player_name=player_name)
+                        self.hook(PLATE_CLEANED, counter=self, player=player)
                         self.occupied_by[-1].reset()
                         self.occupied_by[-1].name = name
                         plate = self.occupied_by.pop()
@@ -880,5 +922,10 @@ class SinkAddon(Counter):
 
     def pick_up(self, on_hands: bool = True, player: str = "0") -> Item | None:
         if self.occupied_by:
-            self.hook(PICK_UP_FROM_SINK_ADDON, player=player)
+            self.hook(
+                PICK_UP_FROM_SINK_ADDON,
+                player=player,
+                occupied_by=self.occupied_by[-1],
+                counter=self,
+            )
             return self.occupied_by.pop()
diff --git a/cooperative_cuisine/environment.py b/cooperative_cuisine/environment.py
index 3b28261852ba9ad268963413cdbe160e47224ad9..57c957ae8b154a59594c3b06a43b1a89e3670988 100644
--- a/cooperative_cuisine/environment.py
+++ b/cooperative_cuisine/environment.py
@@ -418,6 +418,7 @@ class Environment:
                     else {}
                 )
             ),
+            hook=self.hook,
             pos=pos,
         )
         self.players[player.name] = player
@@ -437,7 +438,7 @@ class Environment:
             player.update_facing_point()
 
         self.movement.set_collision_arrays(len(self.players))
-        self.hook(PLAYER_ADDED, player_name=player_name, pos=pos)
+        self.hook(PLAYER_ADDED, player=player, pos=pos)
 
     def step(self, passed_time: timedelta):
         """Performs a step of the environment. Affects time based events such as cooking or cutting things, orders
@@ -543,8 +544,10 @@ class Environment:
         """Registers a callback function for a given hook reference.
 
         Args:
-            hook_ref (str | list[str]): The reference to the hook or hooks for which the callback should be registered. It can be a single string or a list of strings.
-            callback (Callable): The callback function to be registered for the specified hook(s). The function should accept the necessary parameters and perform the desired actions.
+            hook_ref (str | list[str]): The reference to the hook or hooks for which the callback should be registered.
+            It can be a single string or a list of strings.
+            callback (Callable): The callback function to be registered for the specified hook(s).
+            The function should accept the necessary parameters and perform the desired actions.
 
         """
         self.hook.register_callback(hook_ref, callback)
diff --git a/cooperative_cuisine/game_server.py b/cooperative_cuisine/game_server.py
index 9f4271d6f083a6c9170aa9a44386552be16b5ddf..093f30761eff6754c549a311af809e9dbc56f1a2 100644
--- a/cooperative_cuisine/game_server.py
+++ b/cooperative_cuisine/game_server.py
@@ -93,7 +93,7 @@ class EnvironmentStatus(Enum):
     RUNNING = "running"
     """The environment is running."""
     STOPPED = "stopped"
-    """The environement is stopped."""
+    """The environment is stopped."""
 
 
 @dataclasses.dataclass
@@ -322,7 +322,7 @@ class EnvironmentHandler:
         Args:
             manager_id (str): The ID of the manager that manages the environment.
             env_id (str): The ID of the environment.
-            reason (str): The reason for unpausing the environment.
+            reason (str): The reason for un-pausing the environment.
         """
         if (
             manager_id in self.manager_envs
@@ -521,7 +521,8 @@ class EnvironmentHandler:
     def is_known_client_id(self, client_id: str) -> bool:
         """Check if a client ID is known.
 
-        Client IDs are generated by the server for players to connect to a websocket. Therefore, unknown IDs are ignored.
+        Client IDs are generated by the server for players to connect to a websocket.
+        Therefore, unknown IDs are ignored.
 
         Args:
             client_id (str): The client ID to be checked.
@@ -535,7 +536,8 @@ class EnvironmentHandler:
         """Pass an action of a player to the environment.
 
         Args:
-            player_hash (str): The hash that allows access to the player data (should only know the player client and not other players).
+            player_hash (str): The hash that allows access to the player data
+            (should only know the player client and not other players).
             action (Action): The action to be performed.
 
         Returns:
@@ -783,7 +785,7 @@ async def stop_env(manage_env: ManageEnv) -> str:
 
 @app.websocket("/ws/player/{client_id}")
 async def websocket_player_endpoint(websocket: WebSocket, client_id: str):
-    """The method that recives messages from the websocket of a player and sends the results back to the client.
+    """The method that receives messages from the websocket of a player and sends the results back to the client.
 
     Args:
         websocket (WebSocket): The WebSocket connection object.
@@ -826,7 +828,8 @@ if __name__ == "__main__":
     parser = argparse.ArgumentParser(
         prog="Cooperative Cuisine Game Server",
         description="Game Engine Server: Starts overcooked game engine server.",
-        epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/cooperative_cuisine.html",
+        epilog="For further information, see "
+               "https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/cooperative_cuisine.html",
     )
 
     url_and_port_arguments(parser)
diff --git a/cooperative_cuisine/hooks.py b/cooperative_cuisine/hooks.py
index 1e44e08e1abee3e21d38229ab78afdfd9d19efea..2c9f4900b25c67b40b94453291e2589a0c99da34 100644
--- a/cooperative_cuisine/hooks.py
+++ b/cooperative_cuisine/hooks.py
@@ -43,7 +43,7 @@ POST_PERFORM_ACTION = "post_perform_action"
 # TODO Pre and Post Perform Movement
 
 PLAYER_ADDED = "player_added"
-"""Called after a player has been added. Kwargs: `player_name` and `pos`."""
+"""Called after a player has been added. Kwargs: `player` and `pos`."""
 
 GAME_ENDED_STEP = "game_ended_step"
 
@@ -64,71 +64,65 @@ POST_RESET_ENV_TIME = "post_reset_env_time"
 
 PRE_COUNTER_PICK_UP = "pre_counter_pick_up"
 POST_COUNTER_PICK_UP = "post_counter_pick_up"
-
 PRE_COUNTER_DROP_OFF = "pre_counter_drop_off"
 POST_COUNTER_DROP_OFF = "post_counter_drop_off"
 
-PRE_DISPENSER_PICK_UP = "dispenser_pick_up"
-POST_DISPENSER_PICK_UP = "dispenser_pick_up"
+PRE_DISPENSER_PICK_UP = "pre_dispenser_pick_up"
+POST_DISPENSER_PICK_UP = "post_dispenser_pick_up"
+
+DROP_OFF_ON_COOKING_EQUIPMENT = "drop_off_on_cooking_equipment"
+
+PRE_PLATE_DISPENSER_PICK_UP = "pre_plate_dispenser_pick_up"
+POST_PLATE_DISPENSER_PICK_UP = "post_plate_dispenser_pick_up"
+PRE_PLATE_DISPENSER_DROP_OFF = "pre_plate_dispenser_drop_off"
+POST_PLATE_DISPENSER_DROP_OFF = "post_plate_dispenser_drop_off"
 
 CUTTING_BOARD_PROGRESS = "cutting_board_progress"
 CUTTING_BOARD_100 = "cutting_board_100"
 
-CUTTING_BOARD_START_INTERACT = "cutting_board_start_interaction"
-CUTTING_BOARD_END_INTERACT = "cutting_board_end_interact"
+PROGRESS_STARTED = "progress_started"
+PROGRESS_FINISHED = "progress_finished"
 
 PRE_SERVING = "pre_serving"
 POST_SERVING = "post_serving"
 NO_SERVING = "no_serving"
 
-# TODO drop off
-
 PLATE_OUT_OF_KITCHEN_TIME = "plate_out_of_kitchen_time"
-
 DIRTY_PLATE_ARRIVES = "dirty_plate_arrives"
 
 TRASHCAN_USAGE = "trashcan_usage"
 
-PLATE_CLEANED = "plate_cleaned"
-
-SINK_START_INTERACT = "sink_start_interact"
-
-SINK_END_INTERACT = "sink_end_interact"
-
 ADDED_PLATE_TO_SINK = "added_plate_to_sink"
-
 DROP_ON_SINK_ADDON = "drop_on_sink_addon"
-
 PICK_UP_FROM_SINK_ADDON = "pick_up_from_sink_addon"
+PLATE_CLEANED = "plate_cleaned"
 
 SERVE_NOT_ORDERED_MEAL = "serve_not_ordered_meal"
-
 SERVE_WITHOUT_PLATE = "serve_without_plate"
 
 ORDER_DURATION_SAMPLE = "order_duration_sample"
-
 COMPLETED_ORDER = "completed_order"
-
 INIT_ORDERS = "init_orders"
-
 NEW_ORDERS = "new_orders"
-
 ORDER_EXPIRED = "order_expired"
 
 ACTION_ON_NOT_REACHABLE_COUNTER = "action_on_not_reachable_counter"
-
 ACTION_PUT = "action_put"
-
 ACTION_INTERACT_START = "action_interact_start"
 
+ITEM_BURNED = "item_burned"  # MISSING
 NEW_FIRE = "new_fire"
-
 FIRE_SPREADING = "fire_spreading"
 
-DROP_OFF_ON_COOKING_EQUIPMENT = "drop_off_on_cooking_equipment"
-
 PLAYERS_COLLIDE = "players_collide"
 
+PLAYER_START_INTERACT = "player_start_interaction"
+PLAYER_END_INTERACT = "player_end_interact"
+
+ON_ITEM_TRANSITION = "on_item_transition"
+CONTENT_READY = "content_ready"
+
+
 class Hooks:
     """
     Class Hooks
diff --git a/cooperative_cuisine/items.py b/cooperative_cuisine/items.py
index fbfa2dab675b0fddea6b48ba4f5def73f4a349c4..0679d34747e20d9ecd270b952ab688516cc72f76 100644
--- a/cooperative_cuisine/items.py
+++ b/cooperative_cuisine/items.py
@@ -30,6 +30,13 @@ import uuid
 from enum import Enum
 from typing import Optional, TypedDict, TYPE_CHECKING, Literal, Set
 
+from cooperative_cuisine.hooks import (
+    Hooks,
+    ON_ITEM_TRANSITION,
+    PROGRESS_FINISHED,
+    PROGRESS_STARTED,
+    CONTENT_READY,
+)
 from cooperative_cuisine.state_representation import (
     ItemState,
     CookingEquipmentState,
@@ -233,7 +240,7 @@ class CookingEquipment(Item):
 
     item_category = COOKING_EQUIPMENT_ITEM_CATEGORY
 
-    def __init__(self, transitions: dict[str, ItemInfo], *args, **kwargs):
+    def __init__(self, transitions: dict[str, ItemInfo], hook: Hooks, *args, **kwargs):
         """Constructor for CookingEquipment.
 
         Args:
@@ -255,6 +262,8 @@ class CookingEquipment(Item):
         self.content_list: list[Item] = []
         """The items that the equipment holds."""
 
+        self.hook: Hooks = hook
+
         log.debug(f"Initialize {self.name}: {self.transitions}")
 
         for transition in self.transitions.values():
@@ -311,9 +320,17 @@ class CookingEquipment(Item):
 
     def progress(self, passed_time: datetime.timedelta, now: datetime.datetime):
         percent = passed_time.total_seconds() / self.active_transition["seconds"]
+        if self.progress_percentage == 0.0:
+            self.hook(PROGRESS_STARTED, item=self)
         super().progress(equipment=self.name, percent=percent)
         if self.progress_percentage == 1.0:
             if isinstance(self.active_transition["result"], Effect):
+                self.hook(
+                    ON_ITEM_TRANSITION,
+                    item=self,
+                    result=self.active_transition["result"],
+                    seconds=self.active_transition["seconds"],
+                )
                 self.active_transition[
                     "result"
                 ].item_info.manager.register_active_effect(
@@ -321,6 +338,7 @@ class CookingEquipment(Item):
                 )
             else:
                 self.content_list = [self.active_transition["result"]]
+                self.hook(PROGRESS_FINISHED, item=self)
             self.reset()
             self.check_active_transition()
 
@@ -353,7 +371,9 @@ class CookingEquipment(Item):
                     break  # ?
             else:
                 if ingredients == transition.recipe:
+                    # TODO here hook?
                     if transition.seconds == 0:
+                        self.hook(CONTENT_READY, result=result, before=self)
                         self.content_ready = Item(name=result, item_info=transition)
                     else:
                         self.active_transition = {
@@ -405,7 +425,14 @@ class CookingEquipment(Item):
 class Plate(CookingEquipment):
     """The plate can have to states: clean and dirty. In the clean state it can hold content/other items."""
 
-    def __init__(self, transitions: dict[str, ItemInfo], clean: bool, *args, **kwargs):
+    def __init__(
+        self,
+        transitions: dict[str, ItemInfo],
+        clean: bool,
+        hook: Hooks,
+        *args,
+        **kwargs,
+    ):
         """Constructor for Plate.
 
         Args:
@@ -421,6 +448,7 @@ class Plate(CookingEquipment):
         super().__init__(
             name=self.create_name(),
             transitions={k: v for k, v in transitions.items() if not v.equipment},
+            hook=hook,
             *args,
             **kwargs,
         )
diff --git a/cooperative_cuisine/movement.py b/cooperative_cuisine/movement.py
index 320ce0869d76e6dccd506ba03f472a17e888d715..0105065c0d01a6eb35169d45366fe6bd14cb2bb9 100644
--- a/cooperative_cuisine/movement.py
+++ b/cooperative_cuisine/movement.py
@@ -243,4 +243,5 @@ class Movement:
                 else None
             )
             if p.last_interacted_counter != p.current_nearest_counter:
-                p.perform_interact_stop()
+                if p.interacting:
+                    p.perform_interact_stop()
diff --git a/cooperative_cuisine/player.py b/cooperative_cuisine/player.py
index d6242a48809bdd13d2e0c8a2d3a5e7dd80be363e..82133a94af53b206a8b8a404f3c7252323c067f9 100644
--- a/cooperative_cuisine/player.py
+++ b/cooperative_cuisine/player.py
@@ -16,6 +16,11 @@ import numpy as np
 import numpy.typing as npt
 
 from cooperative_cuisine.counters import Counter
+from cooperative_cuisine.hooks import (
+    Hooks,
+    PLAYER_START_INTERACT,
+    PLAYER_END_INTERACT,
+)
 from cooperative_cuisine.items import Item, ItemType
 from cooperative_cuisine.state_representation import PlayerState
 
@@ -52,6 +57,7 @@ class Player:
         self,
         name: str,
         player_config: PlayerConfig,
+        hook: Hooks,
         pos: Optional[npt.NDArray[float]] = None,
     ):
         """Constructor of Player.
@@ -94,12 +100,15 @@ class Player:
         self.interacting: bool = False
         """Is the player currently interacting with a counter."""
 
+        self.hook: Hooks = hook
+
     def set_movement(self, move_vector, move_until):
         """Called by the `perform_action` method. Movements will be performed (pos will be updated) in the `step`
         function of the environment"""
         self.current_movement = move_vector
         self.movement_until = move_until
-        self.perform_interact_stop()
+        if self.interacting:
+            self.perform_interact_stop()
 
     def move_abs(self, new_pos: npt.NDArray[float]):
         """Overwrites the player location by the new_pos 2d-vector. Absolute movement.
@@ -178,6 +187,7 @@ class Player:
         """
         self.interacting = True
         self.last_interacted_counter = counter
+        self.hook(PLAYER_START_INTERACT, player=self.name, counter=counter)
 
     def perform_interact_stop(self):
         """Stops an interaction with the counter. Should be called for a
@@ -187,6 +197,11 @@ class Player:
             counter: The counter to stop the interaction with.
         """
         self.interacting = False
+        self.hook(
+            PLAYER_END_INTERACT,
+            player=self.name,
+            counter=self.last_interacted_counter,
+        )
         self.last_interacted_counter = None
 
     def progress(self, passed_time: timedelta, now: datetime):
diff --git a/cooperative_cuisine/pygame_2d_vis/gui.py b/cooperative_cuisine/pygame_2d_vis/gui.py
index 63f570b3d6b7a4d8de46fd07218a9416f1688f84..ccc28ce9d96b2e485c643fa4f041917a60b6cc85 100644
--- a/cooperative_cuisine/pygame_2d_vis/gui.py
+++ b/cooperative_cuisine/pygame_2d_vis/gui.py
@@ -1929,10 +1929,15 @@ class PyGameGUI:
                 self.disconnect_websockets()
                 if not self.CONNECT_WITH_STUDY_SERVER:
                     self.stop_game_on_server("Program exited.")
+                if self.fullscreen_button:
+                    self.fullscreen_button_press()
 
         self.disconnect_websockets()
         if not self.CONNECT_WITH_STUDY_SERVER:
             self.stop_game_on_server("Program exited.")
+
+        if self.fullscreen:
+            self.fullscreen_button_press()
         pygame.quit()
         sys.exit()
 
diff --git a/cooperative_cuisine/recording.py b/cooperative_cuisine/recording.py
index a30881994a3fe108006f7cdaf8b501fbda508141..596060ad7de9823010b65f6bb78e07b07f99b73c 100644
--- a/cooperative_cuisine/recording.py
+++ b/cooperative_cuisine/recording.py
@@ -45,7 +45,11 @@ import logging
 import os
 import traceback
 from pathlib import Path
+from string import Template
 
+import yaml
+
+from cooperative_cuisine import ROOT_DIR
 from cooperative_cuisine.counters import Counter
 from cooperative_cuisine.environment import Environment
 from cooperative_cuisine.hooks import HookCallbackClass
@@ -89,20 +93,42 @@ class FileRecorder(HookCallbackClass):
 
     def __call__(self, hook_ref: str, env: Environment, **kwargs):
         for key, item in kwargs.items():
-            if isinstance(item, (Counter, Item, Effect, Order)):
-                kwargs[key] = str(item)
+            if isinstance(item, (Counter, Item, Effect)):
+                kwargs[key] = item.to_dict()
+            elif isinstance(item, Order):
+                order_state = {
+                    "id": item.uuid,
+                    "category": "Order",
+                    "meal": item.meal.name,
+                    "start_time": item.start_time.isoformat(),
+                    "max_duration": item.max_duration.total_seconds(),
+                }
+                kwargs[key] = order_state
+
             elif isinstance(item, list):
-                kwargs[key] = [str(i) for i in item]
-        if hook_ref == "new_orders":
-            new_orders = kwargs["new_orders"]
-            kwargs["new_orders"] = [str(order) for order in new_orders]
+                new_list = []
+                for i in item:
+                    if isinstance(i, Order):
+                        new_list.append(
+                            {
+                                "id": i.uuid,
+                                "category": "Order",
+                                "meal": i.meal.name,
+                                "start_time": i.start_time.isoformat(),
+                                "max_duration": i.max_duration.total_seconds(),
+                            }
+                        )
+                    else:
+                        new_list.append(i.to_dict())
+
+                kwargs[key] = new_list
         try:
             record = (
                 json.dumps(
                     {
                         "env_time": env.env_time.isoformat(),
-                        **kwargs,
                         **({"hook_ref": hook_ref} if self.add_hook_ref else {}),
+                        **kwargs,
                     },
                     cls=NumpyAndDataclassEncoder,
                 )
@@ -115,3 +141,90 @@ class FileRecorder(HookCallbackClass):
             log.info(
                 f"Not JSON serializable Record, hook: {hook_ref}, kwargs: {kwargs}"
             )
+
+
+def print_recorded_events_human_readable(jsonl_path: Path):
+    """This function prints a game_event recording in human-readable form.
+
+    Args:
+        jsonl_path: Path to the file with recorded game events.
+
+    """
+
+    def stringify_item(item):
+        if isinstance(item, float):
+            return str(item)
+        if isinstance(item, str):
+            return item
+        if isinstance(item, list) and len(item) == 1:
+            item = item[0]
+        if item:
+            content = None
+            if item and item["type"] in [
+                "Plate",
+                "Pot",
+                "Basket",
+                "Peel",
+                "Pan",
+            ]:
+                if item != "Plate" and item["content_ready"]:
+                    content_ready = stringify_item(item["content_ready"])
+                    return f"{item['type']}{f'({content_ready})'}"
+
+                content = [stringify_item(i) for i in item["content_list"]]
+                if not content:
+                    content = "None"
+            return f"{item['type']}{f'({content})' if content else ''}"
+        else:
+            return None
+
+    with open(ROOT_DIR / "configs" / "human_readable_print_templates.yaml", "r") as f:
+        string_templates = yaml.safe_load(f)
+
+    column_size = 20
+    with open(jsonl_path, "r") as jsonl_file:
+        for line in jsonl_file:
+            record = json.loads(line)
+
+            hook = record["hook_ref"]
+
+            for dict_key in record.keys():
+                match dict_key:
+                    case "hook_ref" | "env_time" | "on_hands":
+                        pass
+                    case "counter":
+                        if not record[dict_key]:
+                            record[dict_key] = "None"
+                        else:
+                            occupied = record[dict_key]["occupied_by"]
+                            if occupied:
+                                if isinstance(occupied, list):
+                                    occupied = [stringify_item(i) for i in occupied]
+                                else:
+                                    occupied = stringify_item(occupied)
+                            record[dict_key] = f"{record[dict_key]['type']}({occupied})"
+
+                    case "order":
+                        order = f"Order({record[dict_key]['meal']})"
+                        record[dict_key] = order
+                    case "new_orders":
+                        orders = [f"Order({o['meal']})" for o in record[dict_key]]
+                        record[dict_key] = orders
+                    case _:
+                        try:
+                            record[dict_key] = stringify_item(record[dict_key])
+                        except (KeyError, TypeError) as e:
+                            print(hook, dict_key, record[dict_key], type(e), e)
+
+            if hook in string_templates.keys():
+                string_template = Template(string_templates[hook])
+                print(string_template.substitute(**dict(record.items())))
+            else:
+                print(hook)
+                for key, item in record.items():
+                    print(f"  - {(key+':').ljust(column_size)}{item}")
+
+
+if __name__ == "__main__":
+    json_lines_path: Path = Path("/home/fabian/.local/state/cooperative_cuisine/log/fcb095915c454446b9ee2905ff534610/game_events.jsonl")
+    print_recorded_events_human_readable(json_lines_path)
diff --git a/tests/test_cooking_equipment.py b/tests/test_cooking_equipment.py
index 03f936f1a1625b318e46905668009ed8fce659db..60377d836cff58accd9ca9d22cebea3c51d8f332 100644
--- a/tests/test_cooking_equipment.py
+++ b/tests/test_cooking_equipment.py
@@ -1,5 +1,6 @@
 import pytest
 
+from cooperative_cuisine.hooks import Hooks
 from cooperative_cuisine.items import ItemInfo, CookingEquipment, Item, ItemType
 
 
@@ -8,7 +9,7 @@ def test_can_combine_single_other_item():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
     other_item = Item("Onion", ItemInfo(type=ItemType.Ingredient, name="Onion"))
 
@@ -20,12 +21,13 @@ def test_can_combine_list_of_other_items():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
     other_item = CookingEquipment(
         name="Pan",
         transitions={},
         item_info=ItemInfo(type=ItemType.Equipment, name="Pan"),
+        hook=Hooks(None)
     )
 
     assert cooking_equipment.can_combine(other_item) == False
@@ -36,7 +38,7 @@ def test_can_combine_without_other_item():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     assert cooking_equipment.can_combine(None) == False
@@ -47,7 +49,7 @@ def test_combine():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
     other_item = Item("Onion", ItemInfo(type=ItemType.Ingredient, name="Onion"))
 
@@ -59,7 +61,7 @@ def test_progress():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
     result = Item(name="TestResult", item_info=None)
     cooking_equipment.active_transition = {
@@ -78,7 +80,7 @@ def test_reset_content():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
     cooking_equipment.content_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     cooking_equipment.reset_content()
@@ -91,7 +93,7 @@ def test_release():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     cooking_equipment.content_list = ["Content1", "Content2"]
@@ -105,7 +107,7 @@ def test_extra_repr_without_content():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     assert cooking_equipment.extra_repr == "[], None"
@@ -116,7 +118,7 @@ def test_extra_repr_with_content():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     item_1 = Item(
@@ -135,7 +137,7 @@ def test_get_potential_meal_without_content():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     assert cooking_equipment.get_potential_meal() is None
@@ -146,7 +148,7 @@ def test_get_potential_meal_with_content():
 
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
     cooking_equipment = CookingEquipment(
-        transitions={}, name="Pot", item_info=item_info
+        transitions={}, name="Pot", item_info=item_info, hook=Hooks(None)
     )
 
     item_1 = Item(
@@ -166,7 +168,7 @@ def test_get_potential_meal_with_content():
 @pytest.fixture
 def cooking_equipment():
     item_info = ItemInfo(type=ItemType.Meal, name="Soup", seconds=5.0)
-    return CookingEquipment(transitions={}, name="Pot", item_info=item_info)
+    return CookingEquipment(transitions={}, name="Pot", item_info=item_info, hook=Hooks(None))
 
 
 def test_reset(cooking_equipment):
diff --git a/tests/test_counter.py b/tests/test_counter.py
index d6860acb3e00eb02a6cfc8703bf3bb9fd8e10f7b..058a9e17ad95f1365e0dc5be6aec76ecdfa80a3e 100644
--- a/tests/test_counter.py
+++ b/tests/test_counter.py
@@ -35,7 +35,7 @@ def test_serving_window():
         plate_dispenser.plate_received
     ), "ServingWindow needs to update plate out of kitchen for ordered meal."
     plate_dispenser.plate_received = False
-    plate = Plate(transitions={}, clean=True, item_info=None)
+    plate = Plate(transitions={}, clean=True, item_info=None, hook=Hooks(None))
     plate.content_list = [Item(name="TestMeal", item_info=None)]
     assert serving_window.can_drop_off(
         item=plate